From 019dbf7f5f2e6b86b35ae4aa2cbca4501e11984d Mon Sep 17 00:00:00 2001 From: "xiaoli.liu@intel.com" Date: Thu, 20 Dec 2018 00:47:33 +0800 Subject: [PATCH] Add Quantize OP test=develop --- paddle/fluid/operators/quantize_mkldnn_op.cc | 100 ++++++++++++++++++ paddle/fluid/operators/quantize_op.cc | 47 ++++++++ paddle/fluid/operators/quantize_op.h | 46 ++++++++ .../unittests/test_quantize_mkldnn_op.py | 76 +++++++++++++ 4 files changed, 269 insertions(+) create mode 100644 paddle/fluid/operators/quantize_mkldnn_op.cc create mode 100644 paddle/fluid/operators/quantize_op.cc create mode 100644 paddle/fluid/operators/quantize_op.h create mode 100644 python/paddle/fluid/tests/unittests/test_quantize_mkldnn_op.py diff --git a/paddle/fluid/operators/quantize_mkldnn_op.cc b/paddle/fluid/operators/quantize_mkldnn_op.cc new file mode 100644 index 00000000000..400ba383e72 --- /dev/null +++ b/paddle/fluid/operators/quantize_mkldnn_op.cc @@ -0,0 +1,100 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "mkldnn.hpp" +#include "paddle/fluid/framework/tensor.h" +#include "paddle/fluid/operators/quantize_op.h" +#include "paddle/fluid/platform/mkldnn_helper.h" + +namespace paddle { +namespace operators { + +using mkldnn::memory; +using mkldnn::primitive; +using mkldnn::reorder; +using platform::to_void_cast; +using Tensor = framework::Tensor; +using framework::DataLayout; +using mkldnn::stream; +using platform::GetMKLDNNFormat; + +template +class QuantOpKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + auto* input = ctx.Input("Input"); + auto scale_data = ctx.Attr("Scale"); + auto* output = ctx.Output("Output"); + auto& dev_ctx = + ctx.template device_context(); + const auto& engine = dev_ctx.GetEngine(); + + std::vector pipeline; + std::vector src_tz = paddle::framework::vectorize2int(input->dims()); + std::vector dst_tz = paddle::framework::vectorize2int(output->dims()); + + const T* input_data = input->data(); + + mkldnn::primitive_attr attri; + int mask = 0; + attri.set_output_scales(mask, {scale_data}); + + auto src_md = platform::MKLDNNMemDesc({src_tz}, memory::data_type::f32, + input->format()); + auto src_pd = mkldnn::memory::primitive_desc(src_md, engine); + auto src_memory = + std::make_shared(src_pd, to_void_cast(input_data)); + std::shared_ptr src_memory_p = + std::shared_ptr(new primitive::at(*src_memory)); + + bool is_negative = ctx.Attr("is_negative_input"); + mkldnn::memory::primitive_desc dst_pd; + std::shared_ptr dst_memory; + if (is_negative) { + int8_t* output_data = output->mutable_data(ctx.GetPlace()); + auto dst_md = platform::MKLDNNMemDesc({dst_tz}, memory::data_type::s8, + memory::format::nhwc); + dst_pd = mkldnn::memory::primitive_desc(dst_md, engine); + dst_memory.reset( + new mkldnn::memory(dst_pd, to_void_cast(output_data))); + } else { + uint8_t* output_data = output->mutable_data(ctx.GetPlace()); + auto dst_md = platform::MKLDNNMemDesc({dst_tz}, memory::data_type::u8, + memory::format::nhwc); + dst_pd = mkldnn::memory::primitive_desc(dst_md, engine); + dst_memory.reset( + new mkldnn::memory(dst_pd, to_void_cast(output_data))); + } + + auto reorder_pd = std::shared_ptr( + new reorder::primitive_desc(src_pd, dst_pd, attri)); + auto reorder_p = std::shared_ptr( + new reorder(*reorder_pd, *src_memory_p, *dst_memory)); + + pipeline.push_back(*reorder_p); + stream(stream::kind::eager).submit(pipeline).wait(); + + output->set_layout(DataLayout::kMKLDNN); + output->set_format(GetMKLDNNFormat(*dst_memory)); + } +}; + +} // namespace operators +} // namespace paddle +namespace ops = paddle::operators; + +// TODO(Xiaoli) Support FP32->S8 quantization. + +REGISTER_OP_KERNEL(quantize, MKLDNN, ::paddle::platform::CPUPlace, + ops::QuantOpKernel); diff --git a/paddle/fluid/operators/quantize_op.cc b/paddle/fluid/operators/quantize_op.cc new file mode 100644 index 00000000000..bf70c08bdb8 --- /dev/null +++ b/paddle/fluid/operators/quantize_op.cc @@ -0,0 +1,47 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + +#include "paddle/fluid/operators/quantize_op.h" +#ifdef PADDLE_WITH_MKLDNN +#include "paddle/fluid/platform/mkldnn_helper.h" +#endif + +namespace paddle { +namespace operators { + +framework::OpKernelType QuantOp::GetExpectedKernelType( + const framework::ExecutionContext& ctx) const { + framework::LibraryType library_ = framework::LibraryType::kMKLDNN; + framework::DataLayout layout_ = framework::DataLayout::kMKLDNN; + + return framework::OpKernelType(ctx.Input("Input")->type(), + ctx.GetPlace(), layout_, library_); +} + +void QuantOpMaker::Make() { + AddInput("Input", "input data"); + AddOutput("Output", "output data"); + AddAttr("is_negative_input", + "(bool, default false) Only used in mkldnn INT8 kernel") + .SetDefault(false); + AddAttr("Scale", "scale data").SetDefault({1.0f}); + AddComment(R"DOC(This op will quantize data from FP32 to INT8)DOC"); +} + +} // namespace operators +} // namespace paddle +namespace ops = paddle::operators; + +REGISTER_OPERATOR(quantize, ops::QuantOp, ops::QuantOpMaker, + paddle::framework::DefaultGradOpDescMaker); diff --git a/paddle/fluid/operators/quantize_op.h b/paddle/fluid/operators/quantize_op.h new file mode 100644 index 00000000000..091306e4637 --- /dev/null +++ b/paddle/fluid/operators/quantize_op.h @@ -0,0 +1,46 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include +#include +#include "paddle/fluid/framework/op_registry.h" + +namespace paddle { +namespace operators { + +using framework::OpKernelType; +using framework::Tensor; + +class QuantOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + void InferShape(framework::InferShapeContext* ctx) const override { + ctx->SetOutputDim("Output", ctx->GetInputDim("Input")); + ctx->ShareLoD("Input", /*->*/ "Output"); + } + + protected: + framework::OpKernelType GetExpectedKernelType( + const framework::ExecutionContext& ctx) const override; +}; + +class QuantOpMaker : public framework::OpProtoAndCheckerMaker { + public: + void Make() override; +}; +} // namespace operators +} // namespace paddle diff --git a/python/paddle/fluid/tests/unittests/test_quantize_mkldnn_op.py b/python/paddle/fluid/tests/unittests/test_quantize_mkldnn_op.py new file mode 100644 index 00000000000..99607928648 --- /dev/null +++ b/python/paddle/fluid/tests/unittests/test_quantize_mkldnn_op.py @@ -0,0 +1,76 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import print_function + +import unittest +import numpy as np +from op_test import OpTest + + +class TestQuantizeOp(OpTest): + def setUp(self): + self.op_type = 'quantize' + self.scale = 2.0 + self.input_size = [1, 1, 5, 5] #Naive nChw16c + self.is_negative = False + self.set_scale() + self.set_is_negative() + + if self.is_negative: + input = (100 * np.random.random_sample(self.input_size) - 50 + ).astype('float32') + output = np.round(input * self.scale).astype('int8') + else: + input = (100 * + np.random.random_sample(self.input_size)).astype('float32') + output = np.round(input * self.scale).astype('uint8') + + self.inputs = {'Input': OpTest.np_dtype_to_fluid_dtype(input)} + + self.outputs = {'Output': output} + + self.attrs = { + 'Scale': self.scale, + 'is_negative_input': self.is_negative + } + + def test_check_output(self): + self.check_output() + + def set_scale(self): + pass + + def set_is_negative(self): + pass + + +class TestQuantizeOp1(TestQuantizeOp): + def set_scale(self): + self.scale = 1.5 + + def set_is_negative(self): + self.is_nagative = True + + +class TestQuantizeOp2(TestQuantizeOp): + def set_scale(self): + self.scale = 0.1 + + def set_is_negative(self): + self.is_nagative = False + + +if __name__ == '__main__': + unittest.main() -- GitLab