提交 946afe3f 编写于 作者: H hjchen2

Add quantize/dequantize op unit test

上级 5e4a1781
......@@ -15,7 +15,6 @@ limitations under the License. */
#include "common/types.h"
#include <vector>
namespace paddle_mobile {
const char *G_OP_TYPE_CONV = "conv2d";
......@@ -117,7 +116,6 @@ std::unordered_map<
{G_OP_TYPE_SHAPE, {{"Input"}, {"Out"}}},
{G_OP_TYPE_CONV_TRANSPOSE, {{"Input"}, {"Output"}}},
{G_OP_TYPE_QUANTIZE, {{"X"}, {"Out", "OutScale"}}},
{G_OP_TYPE_DEQUANTIZE, {{"X", "Scale"}, {"Out"}}}
};
{G_OP_TYPE_DEQUANTIZE, {{"X", "Scale"}, {"Out"}}}};
} // namespace paddle_mobile
......@@ -14,8 +14,8 @@ limitations under the License. */
#pragma once
#include "common/enforce.h"
#include <string>
#include "common/enforce.h"
namespace paddle_mobile {
......
此差异已折叠。
此差异已折叠。
......@@ -58,6 +58,15 @@ class Executor {
std::vector<Ptype> Predict(const std::vector<Ptype> &input,
const std::vector<int64_t> &dims);
#ifdef PADDLE_MOBILE_FPGA
void InjectVariable(const framework::Tensor &t, string var_name);
void FeedData(const framework::Tensor &t);
std::shared_ptr<framework::Tensor> FetchResult(int id = -1);
void Predict_From_To(int start = 0, int end = -1);
void Predict_From(int start);
void Predict_To(int end);
#endif
protected:
Executor() = default;
std::shared_ptr<framework::Tensor> Predict(const framework::Tensor &t,
......@@ -83,7 +92,6 @@ class Executor {
uint64_t runEnd = 0UL;
};
#endif
int batch_size_ = 1;
bool use_optimize_ = false;
bool loddable_ = false;
};
......
......@@ -14,31 +14,30 @@ limitations under the License. */
#pragma once
#include <string>
#include "framework/operator.h"
#include "operators/op_param.h"
#include "operators/kernel/dequantize_kernel.h"
#include "operators/op_param.h"
namespace paddle_mobile {
namespace operators {
template <typename DeviceType, typename T>
class DequantizeOp : public framework::OperatorWithKernel<
DeviceType,
DequantizeParam<DeviceType>,
DequantizeKernel<DeviceType, T>> {
class DequantizeOp
: public framework::OperatorWithKernel<DeviceType,
DequantizeParam<DeviceType>,
DequantizeKernel<DeviceType, T>> {
public:
DequantizeOp(const std::string &type,
const VariableNameMap &inputs,
const VariableNameMap &outputs,
const framework::AttributeMap &attrs,
std::shared_ptr<framework::Scope> scope)
: framework::OperatorWithKernel<DeviceType,
DequantizeParam<DeviceType>,
DequantizeOp(const std::string &type, const VariableNameMap &inputs,
const VariableNameMap &outputs,
const framework::AttributeMap &attrs,
std::shared_ptr<framework::Scope> scope)
: framework::OperatorWithKernel<DeviceType, DequantizeParam<DeviceType>,
DequantizeKernel<DeviceType, T>>(
type, inputs, outputs, attrs, scope) {}
// inference output shape
void InferShape() const override;
};
} // namespace paddle_mobile
} // namespace operators
} // namespace paddle_mobile
......@@ -38,7 +38,7 @@ void DequantizeKernel<CPU, float>::Compute(
const int32_t *x = input->data<const int32_t>();
float *y = output->mutable_data<float>();
size_t size = output->numel();
float scale = 1.f / activation_scale / weight_scale;
float scale = 1.f / (activation_scale * weight_scale);
#if defined(__ARM_NEON__) || defined(__ARM_NEON)
size_t loop = size >> 4;
size_t remain = size & 0xF;
......
......@@ -16,7 +16,6 @@ limitations under the License. */
#include "operators/kernel/quantize_kernel.h"
#include <cmath>
#include <limits>
#if defined(__ARM_NEON__) || defined(__ARM_NEON)
#include <arm_neon.h>
......@@ -55,7 +54,7 @@ int32x4_t vrnd_to_even(float32x4_t r) {
if (abs(q) % 2 == 0) {
ret[i] = q;
} else {
ret[i] = q + (q > 0) ? -1 : 1;
ret[i] = q + ((q > 0) ? -1 : 1);
}
}
}
......@@ -131,7 +130,7 @@ static float find_abs_max(const Tensor *input) {
static void quantize_round_to_even(const Tensor *input, const float scale,
Tensor *output) {
const float *x = input->data<const float>();
int8_t *y = output->data<int8_t>();
int8_t *y = output->mutable_data<int8_t>();
size_t size = input->numel();
#if defined(__ARM_NEON__) || defined(__ARM_NEON)
size_t loop = size >> 4;
......@@ -153,8 +152,8 @@ static void quantize_round_to_even(const Tensor *input, const float scale,
int16x4_t d1 = vmovn_s32(q1);
int16x4_t d2 = vmovn_s32(q2);
int16x4_t d3 = vmovn_s32(q3);
int16x8_t q5 = vcombine_s16(d1, d0);
int16x8_t q6 = vcombine_s16(d3, d2);
int16x8_t q5 = vcombine_s16(d0, d1);
int16x8_t q6 = vcombine_s16(d2, d3);
int8x8_t d5 = vmovn_s16(q5);
int8x8_t d6 = vmovn_s16(q6);
vst1_s8(y, d5);
......@@ -174,7 +173,7 @@ static void quantize_round_to_even(const Tensor *input, const float scale,
if (abs(q) % 2 == 0) {
y[i] = q;
} else {
y[i] = q + (q > 0) ? -1 : 1;
y[i] = q + ((q > 0) ? -1 : 1);
}
}
}
......@@ -183,7 +182,7 @@ static void quantize_round_to_even(const Tensor *input, const float scale,
static void quantize_round_to_zero(const Tensor *input, const float scale,
Tensor *output) {
const float *x = input->data<const float>();
int8_t *y = output->data<int8_t>();
int8_t *y = output->mutable_data<int8_t>();
size_t size = input->numel();
#ifdef defined(__ARM_NEON__) || defined(__ARM_NEON)
size_t loop = size >> 4;
......@@ -205,8 +204,8 @@ static void quantize_round_to_zero(const Tensor *input, const float scale,
int16x4_t d1 = vmovn_s32(q1);
int16x4_t d2 = vmovn_s32(q2);
int16x4_t d3 = vmovn_s32(q3);
int16x8_t q5 = vcombine_s16(d1, d0);
int16x8_t q6 = vcombine_s16(d3, d2);
int16x8_t q5 = vcombine_s16(d0, d1);
int16x8_t q6 = vcombine_s16(d2, d3);
int8x8_t d5 = vmovn_s16(q5);
int8x8_t d6 = vmovn_s16(q6);
vst1_s8(y, d5);
......@@ -224,7 +223,7 @@ static void quantize_round_to_zero(const Tensor *input, const float scale,
static void quantize_round_to_nearest(const Tensor *input, const float scale,
Tensor *output) {
const float *x = input->data<const float>();
int8_t *y = output->data<int8_t>();
int8_t *y = output->mutable_data<int8_t>();
size_t size = input->numel();
#ifdef defined(__ARM_NEON__) || defined(__ARM_NEON)
size_t loop = size >> 4;
......@@ -246,8 +245,8 @@ static void quantize_round_to_nearest(const Tensor *input, const float scale,
int16x4_t d1 = vmovn_s32(q1);
int16x4_t d2 = vmovn_s32(q2);
int16x4_t d3 = vmovn_s32(q3);
int16x8_t q5 = vcombine_s16(d1, d0);
int16x8_t q6 = vcombine_s16(d3, d2);
int16x8_t q5 = vcombine_s16(d0, d1);
int16x8_t q6 = vcombine_s16(d2, d3);
int8x8_t d5 = vmovn_s16(q5);
int8x8_t d6 = vmovn_s16(q6);
vst1_s8(y, d5);
......@@ -258,7 +257,7 @@ static void quantize_round_to_nearest(const Tensor *input, const float scale,
size = remain;
#endif
for (size_t i = 0; i < size; ++i) {
y[i] = trunc(x[i] * scale);
y[i] = round(x[i] * scale);
}
}
......@@ -279,9 +278,7 @@ void QuantizeKernel<CPU, float>::Compute(
} else {
max_abs = find_abs_max(input);
}
if (max_abs < std::numeric_limits<float>::min()) {
max_abs = std::numeric_limits<float>::min();
}
max_abs = std::max(max_abs, 1e-6f);
// only support int8 currently
float online_scale = 127 / max_abs;
param.online_scale_->mutable_data<float>()[0] = online_scale;
......
......@@ -20,9 +20,9 @@ limitations under the License. */
namespace paddle_mobile {
namespace operators {
template<typename DeviceType, typename T>
class DequantizeKernel : public
framework::OpKernelBase<DeviceType, DequantizeParam<DeviceType>> {
template <typename DeviceType, typename T>
class DequantizeKernel
: public framework::OpKernelBase<DeviceType, DequantizeParam<DeviceType>> {
public:
void Compute(const DequantizeParam<DeviceType> &param) const;
bool Init(DequantizeParam<DeviceType> *param);
......
......@@ -20,9 +20,9 @@ limitations under the License. */
namespace paddle_mobile {
namespace operators {
template<typename DeviceType, typename T>
class QuantizeKernel : public
framework::OpKernelBase<DeviceType, QuantizeParam<DeviceType>> {
template <typename DeviceType, typename T>
class QuantizeKernel
: public framework::OpKernelBase<DeviceType, QuantizeParam<DeviceType>> {
public:
void Compute(const QuantizeParam<DeviceType> &param) const;
bool Init(QuantizeParam<DeviceType> *param);
......
......@@ -14,30 +14,29 @@ limitations under the License. */
#pragma once
#include <string>
#include "framework/operator.h"
#include "operators/op_param.h"
#include "operators/kernel/quantize_kernel.h"
#include "operators/op_param.h"
namespace paddle_mobile {
namespace operators {
template <typename DeviceType, typename T>
class QuantizeOp : public framework::OperatorWithKernel<
DeviceType, QuantizeParam<DeviceType>,
operators::QuantizeKernel<DeviceType, T>> {
DeviceType, QuantizeParam<DeviceType>,
operators::QuantizeKernel<DeviceType, T>> {
public:
QuantizeOp(const std::string &type,
const VariableNameMap &inputs,
QuantizeOp(const std::string &type, const VariableNameMap &inputs,
const VariableNameMap &outputs,
const framework::AttributeMap &attrs,
std::shared_ptr<framework::Scope> scope)
: framework::OperatorWithKernel<DeviceType,
QuantizeParam<DeviceType>,
: framework::OperatorWithKernel<DeviceType, QuantizeParam<DeviceType>,
operators::QuantizeKernel<DeviceType, T>>(
type, inputs, outputs, attrs, scope) {}
// inference output shape
void InferShape() const override;
};
} // namespace paddle_mobile
} // namespace operators
} // namespace paddle_mobile
......@@ -212,6 +212,14 @@ if (NOT FOUND_MATCH)
ADD_EXECUTABLE(test-fc-op operators/test_fusion_fc_op.cpp test_helper.h test_include.h)
target_link_libraries(test-fc-op paddle-mobile)
# test quantize op
ADD_EXECUTABLE(test-quantize-op operators/test_quantize_op.cpp test_helper.h test_include.h)
target_link_libraries(test-quantize-op paddle-mobile)
# test dequantize op
ADD_EXECUTABLE(test-dequantize-op operators/test_dequantize_op.cpp test_helper.h test_include.h)
target_link_libraries(test-dequantize-op paddle-mobile)
# gen test log
ADD_EXECUTABLE(test-log common/test_log.cpp)
target_link_libraries(test-log paddle-mobile)
......
/* 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. */
#include "../test_helper.h"
#include "../test_include.h"
#include "operators/dequantize_op.h"
namespace paddle_mobile {
void dequantize(const Tensor* input, const float scale, Tensor* output) {
const int32_t* x = input->data<const int32_t>();
float* y = output->mutable_data<float>();
size_t size = output->numel();
for (size_t i = 0; i < size; ++i) {
y[i] = x[i] * scale;
}
}
int TestDequqntizeOp() {
framework::DDim dim = framework::make_ddim({1, 3, 224, 224});
VariableNameMap inputs;
VariableNameMap outputs;
auto scope = std::make_shared<framework::Scope>();
inputs["X"] = std::vector<std::string>({"input"});
inputs["Scale"] = std::vector<std::string>({"scale"});
outputs["Out"] = std::vector<std::string>({"output"});
auto input_var = scope.get()->Var("input");
auto input = input_var->template GetMutable<framework::LoDTensor>();
SetupTensor<int32_t>(input, dim, -1000, 1000);
auto scale_var = scope.get()->Var("scale");
auto scale = scale_var->template GetMutable<framework::LoDTensor>();
scale->Resize(framework::make_ddim({1}));
scale->mutable_data<float>()[0] = 1.27;
auto output_var = scope.get()->Var("output");
framework::AttributeMap attrs;
attrs["weight_scale"].Set<float>(1.74);
auto* op = new operators::DequantizeOp<CPU, float>("dequantize", inputs,
outputs, attrs, scope);
op->InferShape();
op->Run();
auto output = output_var->template Get<framework::LoDTensor>();
const float* output_data = output->data<float>();
framework::Tensor output_cmp;
output_cmp.Resize(dim);
float dequant_scale = 1.f / (1.27 * 1.74);
dequantize(input, dequant_scale, &output_cmp);
const float* output_cmp_data = output_cmp.data<float>();
for (int i = 0; i < output->numel(); ++i) {
PADDLE_MOBILE_ENFORCE(output_data[i] == output_cmp_data[i],
"output[%d] = %.6f, output_cmp[%d] = %.6f", i,
output_data[i], i, output_cmp_data[i]);
}
delete op;
return 0;
}
} // namespace paddle_mobile
int main() { return paddle_mobile::TestDequqntizeOp(); }
/* 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. */
#include "../test_helper.h"
#include "../test_include.h"
#include "operators/quantize_op.h"
namespace paddle_mobile {
// static float g_test_data[50] = {
// -5.55, -5.5, -5.45, -5.0, -4.55, -4.5, -4.45, -4.0, -3.55, -3.5,
// -3.45, -3.01, -2.75, -2.5, -2.501, -2.49, -2.01, -1.75, -1.5, -1.25,
// -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0, 1.25,
// 1.5, 1.75, 2.01, 2.49, 2.501, 2.5, 2.75, 3.01, 3.45, 3.5,
// 3.55, 4.0, 4.45, 4.5, 4.55, 5.0, 5.45, 5.5, 5.55, 6.0,
// };
static float find_abs_max(const Tensor *input) {
float max_abs = 0.f;
const float *x = input->data<const float>();
size_t size = input->numel();
for (size_t i = 0; i < size; ++i) {
float value = std::abs(x[i]);
if (value > max_abs) {
max_abs = value;
}
}
return max_abs;
}
static void quantize_round_to_even(const Tensor *input, const float scale,
Tensor *output) {
const float *x = input->data<const float>();
int8_t *y = output->mutable_data<int8_t>();
size_t size = input->numel();
for (size_t i = 0; i < size; ++i) {
float value = x[i] * scale;
float v = round(value);
int32_t q = (int32_t)v;
if (abs(abs(q - value) - 0.5) > 0) {
y[i] = q;
} else {
if (abs(q) % 2 == 0) {
y[i] = q;
} else {
y[i] = q + ((q > 0) ? -1 : 1);
}
}
}
}
int TestQuqntizeOp() {
framework::DDim dim = framework::make_ddim({1, 3, 224, 224});
VariableNameMap inputs;
VariableNameMap outputs;
auto scope = std::make_shared<framework::Scope>();
inputs["X"] = std::vector<std::string>({"input"});
outputs["Out"] = std::vector<std::string>({"output"});
outputs["OutScale"] = std::vector<std::string>({"output_scale"});
auto input_var = scope.get()->Var("input");
auto input = input_var->template GetMutable<framework::LoDTensor>();
SetupTensor<float>(input, dim, -100.f, 100.f);
auto output_var = scope.get()->Var("output");
auto output_scale_var = scope.get()->Var("output_scale");
framework::AttributeMap attrs;
auto *op = new operators::QuantizeOp<CPU, float>("quantize", inputs, outputs,
attrs, scope);
op->InferShape();
op->Run();
auto output = output_var->template Get<framework::LoDTensor>();
const int8_t *output_data = output->data<int8_t>();
auto output_scale = output_scale_var->template Get<framework::LoDTensor>();
const float *output_scale_data = output_scale->data<float>();
float max_abs = find_abs_max(input);
float output_scale_cmp = 127 / max_abs;
PADDLE_MOBILE_ENFORCE(output_scale_cmp == output_scale_data[0],
"output_scale = %.6f, output_scale_cmp = %.6f",
output_scale_cmp, output_scale_data[0]);
framework::Tensor output_cmp;
output_cmp.Resize(dim);
quantize_round_to_even(input, output_scale_cmp, &output_cmp);
int8_t *output_cmp_data = output_cmp.data<int8_t>();
for (int i = 0; i < output->numel(); ++i) {
PADDLE_MOBILE_ENFORCE(output_data[i] == output_cmp_data[i],
"output[%d] = %d, output_cmp[%d] = %d", i,
static_cast<int>(output_data[i]), i,
static_cast<int>(output_cmp_data[i]));
}
delete op;
return 0;
}
} // namespace paddle_mobile
int main() { return paddle_mobile::TestQuqntizeOp(); }
......@@ -12,8 +12,8 @@ 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 "../../src/operators/kernel/sigmoid_kernel.h"
#include "../../src/operators/kernel/central-arm-func/sigmoid_arm_func.h"
#include "../../src/operators/kernel/sigmoid_kernel.h"
#include "../test_helper.h"
#include "io/executor.h"
......
......@@ -3,7 +3,8 @@
TOTAL_ERRORS=0
# The trick to remove deleted files: https://stackoverflow.com/a/2413151
for file in $(git diff --cached --name-status | awk '$1 != "D" {print $2}' | grep -v ".pb.cpp" | grep -v ".pb.h"); do
for file in $(git diff --cached --name-status | awk '$1 != "D" {print $2}' | \
grep -v ".pb.cpp" | grep -v ".pb.h" | grep -v ".pb-c.h" | grep -v ".pb-c.c"); do
cpplint $file;
TOTAL_ERRORS=$(expr $TOTAL_ERRORS + $?);
done
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册