diff --git a/mace/dsp/BUILD b/mace/dsp/BUILD index 814d18744d47da77e28a51c9676acb260d47c23c..ca0183822ff2c6313bf8b1f8faa112becb3ef4b3 100644 --- a/mace/dsp/BUILD +++ b/mace/dsp/BUILD @@ -28,6 +28,7 @@ cc_library( deps = [ "//mace/proto:cc_proto", "//mace/core:core", + "//mace/dsp/util:util", ], ) diff --git a/mace/dsp/test/quantized_maxpool_test.cc b/mace/dsp/test/quantized_maxpool_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..897d1ac432e955ebc3337424e708ad62d28ab2d9 --- /dev/null +++ b/mace/dsp/test/quantized_maxpool_test.cc @@ -0,0 +1,231 @@ +// +// Copyright (c) 2017 XiaoMi All rights reserved. +// + +#include "mace/dsp/hexagon_control_wrapper.h" +#include "mace/dsp/util/quantize.h" +#include "mace/kernels/conv_pool_2d_util.h" +#include "mace/kernels/pooling.h" +#include "gtest/gtest.h" + +using namespace mace; + +static NetDef BuildNetDef(const vector &input_shape, + const vector &output_shape, + const vector &filter_shape, + const vector &stride, + Padding padding, + float input_min, float input_max) { + NetDef net; + net.set_name("quantized_maxpool_test"); + // input op + OperatorDef *input_op = net.add_op(); + input_op->set_name("input_node"); + input_op->set_type("INPUT"); + input_op->set_node_id(0); + input_op->set_padding(0); + input_op->add_out_max_byte_size(1000); + + // maxpool op + OperatorDef *maxpool_op = net.add_op(); + maxpool_op->set_name("maxpool"); + maxpool_op->set_type("QuantizedMaxPool_8"); + maxpool_op->set_node_id(1); + if (padding == Padding::SAME) { + maxpool_op->set_padding(1); + } else { + maxpool_op->set_padding(2); + } + maxpool_op->add_input("input_node"); + maxpool_op->add_input("input_min"); + maxpool_op->add_input("input_max"); + maxpool_op->add_input("ksize"); + maxpool_op->add_input("stride"); + maxpool_op->add_output("maxpool:0"); + maxpool_op->add_output("maxpool:1"); + maxpool_op->add_output("maxpool:2"); + NodeInput *input_node_input = maxpool_op->add_node_input(); + input_node_input->set_node_id(0); + input_node_input->set_output_port(0); + input_node_input = maxpool_op->add_node_input(); + input_node_input->set_node_id(10); + input_node_input->set_output_port(0); + input_node_input = maxpool_op->add_node_input(); + input_node_input->set_node_id(11); + input_node_input = maxpool_op->add_node_input(); + input_node_input->set_node_id(12); + input_node_input->set_output_port(0); + input_node_input = maxpool_op->add_node_input(); + input_node_input->set_node_id(13); + input_node_input->set_output_port(0); + maxpool_op->add_out_max_byte_size(1000); + maxpool_op->add_out_max_byte_size(1000); + maxpool_op->add_out_max_byte_size(1000); + + // output op + OperatorDef *output_op = net.add_op(); + output_op->set_name("__output__"); + output_op->set_type("OUTPUT"); + output_op->set_op_id(2); + input_node_input = output_op->add_node_input(); + input_node_input->set_node_id(1); + input_node_input->set_output_port(0); + + // tensor + TensorProto *input_min_tensor = net.add_tensors(); + input_min_tensor->set_name("input_min"); + input_min_tensor->add_dims(1); + input_min_tensor->set_data_type(DataType::DT_FLOAT); + input_min_tensor->set_node_id(10); + input_min_tensor->add_float_data(input_min); + + TensorProto *input_max_tensor = net.add_tensors(); + input_max_tensor->set_name("input_max"); + input_max_tensor->add_dims(1); + input_max_tensor->set_data_type(DataType::DT_FLOAT); + input_max_tensor->set_node_id(11); + input_max_tensor->add_float_data(input_max); + + TensorProto *ksize_tensor = net.add_tensors(); + ksize_tensor->set_name("ksize"); + ksize_tensor->add_dims(filter_shape[0]); + ksize_tensor->add_dims(filter_shape[1]); + ksize_tensor->add_dims(filter_shape[2]); + ksize_tensor->add_dims(filter_shape[3]); + ksize_tensor->set_data_type(DataType::DT_INT32); + ksize_tensor->set_node_id(12); + + TensorProto *stride_tensor = net.add_tensors(); + stride_tensor->set_name("stride"); + stride_tensor->add_dims(stride[0]); + stride_tensor->add_dims(stride[1]); + stride_tensor->add_dims(stride[2]); + stride_tensor->add_dims(stride[3]); + stride_tensor->set_data_type(DataType::DT_INT32); + stride_tensor->set_node_id(13); + + // input & output info + InputInfo *input_info = net.add_input_info(); + input_info->set_name("input_node"); + input_info->set_node_id(0); + input_info->add_dims(input_shape[0]); + input_info->add_dims(input_shape[1]); + input_info->add_dims(input_shape[2]); + input_info->add_dims(input_shape[3]); + input_info->set_data_type(DataType::DT_UINT8); + input_info->set_max_byte_size(1000); + OutputInfo *output_info = net.add_output_info(); + output_info->set_name("output_node"); + output_info->set_node_id(1); + output_info->add_dims(output_shape[0]); + output_info->add_dims(output_shape[1]); + output_info->add_dims(output_shape[2]); + output_info->add_dims(output_shape[3]); + output_info->set_data_type(DataType::DT_UINT8); + output_info->set_max_byte_size(1000); + + return net; +} + +static void TestQuantizedMaxPool(Padding padding, int kernel_size, int stride_size) { + testing::internal::LogToStderr(); + HexagonControlWrapper wrapper; + wrapper.Init(); + wrapper.SetDebugLevel(3); + wrapper.Config(); + + vector input_shape {1, 10, 10, 3}; + vector filter_shape {1, 3, 3, 1}; + vector stride {1, stride_size, stride_size, 1}; + vector dilation {1, 1, 1, 1}; + vector output_shape {input_shape[0], 0, 0, input_shape[3]}; + vector padding_size(2); + switch (padding) { + case VALID: + output_shape[1] = (input_shape[1] - filter_shape[1]) / stride[1] + 1; + output_shape[2] = (input_shape[2] - filter_shape[2]) / stride[2] + 1; + break; + case SAME: + output_shape[1] = (input_shape[1] - 1) / stride[1] + 1; + output_shape[2] = (input_shape[2] - 1) / stride[2] + 1; + break; + default: + ASSERT_TRUE(0); + } + for (int i = 0; i < 4; ++i) { + VLOG(0) << "! shape = " << output_shape[i]; + } + NetDef net = BuildNetDef(input_shape, output_shape, filter_shape, stride, + padding, -50, 100); + VLOG(0) << wrapper.SetupGraph(net); + + Allocator *cpu_allocator = GetDeviceAllocator(DeviceType::CPU); + Tensor original_tensor(cpu_allocator, DT_FLOAT); + Tensor input_tensor(cpu_allocator, DT_UINT8); + Tensor output_tensor(cpu_allocator, DT_UINT8); + Tensor dequantized_output_tensor(cpu_allocator, DT_FLOAT); + original_tensor.Resize(input_shape); + input_tensor.Resize(input_shape); + output_tensor.Resize(output_shape); + dequantized_output_tensor.Resize(output_shape); + float *original_data = original_tensor.mutable_data(); + uint8_t *input_data = input_tensor.mutable_data(); + const uint8_t *output_data = output_tensor.data(); + float *dequantized_output_data = dequantized_output_tensor.mutable_data(); + + std::random_device rd; + std::mt19937 gen(rd()); + std::normal_distribution nd(-50, 50); + std::generate(original_data, original_data + original_tensor.size(), + [&gen, &nd] { + return nd(gen); + }); + + Quantizer quantizer; + float min_in, min_out; + quantizer.Quantize(original_tensor, &input_tensor, &min_in, &min_out); + VLOG(0) << wrapper.ExecuteGraph(input_tensor, &output_tensor); + quantizer.DeQuantize(output_tensor, min_in, min_out, &dequantized_output_tensor); + + // debug original float input data + for (index_t c = 0; c < input_shape[3]; ++c) { + for (index_t i = 0; i < input_shape[1]; ++i) { + for (index_t j = 0; j < input_shape[2]; ++j) { + std::cout << original_data[i * input_shape[2] * input_shape[3] + j * input_shape[3] + c] << " "; + } + std::cout << std::endl; + } + std::cout << std::endl << std::endl; + } + + // debug dequantized float output data + for (index_t c = 0; c < output_shape[3]; ++c) { + for (index_t i = 0; i < output_shape[1]; ++i) { + for (index_t j = 0; j < output_shape[2]; ++j) { + std::cout << dequantized_output_data[i * output_shape[2] * output_shape[3] + j * output_shape[3] + c] << " "; + } + std::cout << std::endl; + } + std::cout << std::endl << std::endl; + } + + wrapper.PrintLog(); + VLOG(0) << wrapper.TeardownGraph(); + wrapper.Finalize(); +} + +TEST(QuantizedMaxPoolTest, QuantizedMaxPoolValidStride1) { + TestQuantizedMaxPool(Padding::VALID, 3, 1); +} + +TEST(QuantizedMaxPoolTest, QuantizedMaxPoolValidStride2) { + TestQuantizedMaxPool(Padding::VALID, 3, 2); +} + +TEST(QuantizedMaxPoolTest, QuantizedMaxPoolSameStride1) { + TestQuantizedMaxPool(Padding::SAME, 3, 1); +} + +TEST(QuantizedMaxPoolTest, QuantizedMaxPoolSameStride2) { + TestQuantizedMaxPool(Padding::SAME, 3, 2); +} \ No newline at end of file diff --git a/mace/dsp/util/quantize.cc b/mace/dsp/util/quantize.cc index 42063b4f05f8bfac40d000cbd399c7131baa3d60..4d3ccec8b52cf0da0abc622ed78d61e96fee561e 100644 --- a/mace/dsp/util/quantize.cc +++ b/mace/dsp/util/quantize.cc @@ -6,6 +6,21 @@ namespace mace { +void Quantizer::Quantize(const Tensor &in_tensor, + Tensor *out_tensor, + float *min_out, + float *max_out) { + if (in_tensor.size() == 0) return; + const float *in_data = in_tensor.data(); + float min_in = in_data[0]; + float max_in = in_data[0]; + for (index_t i = 0; i < in_tensor.size(); ++i) { + min_in = std::min(min_in, in_data[i]); + max_in = std::max(max_in, in_data[i]); + } + Quantize(in_tensor, min_in, max_in, out_tensor, min_out, max_out); +} + void Quantizer::Quantize(const Tensor &in_tensor, const float min_in, const float max_in, @@ -61,7 +76,7 @@ void Quantizer::DeQuantize(const Tensor &in_tensor, const uint8_t *in = in_tensor.data(); float *out = out_tensor->mutable_data(); - for (int i = 0; i < in_tensor.size(); i++) { + for (int i = 0; i < out_tensor->size(); ++i) { out[i] = (in[i] * stepsize) + min_in; } } diff --git a/mace/dsp/util/quantize.h b/mace/dsp/util/quantize.h index 316fdaed72b216a0cd009fe19ea84facc508fef4..58588ae43840e96d4dbe5309e87ffe8d866587a4 100644 --- a/mace/dsp/util/quantize.h +++ b/mace/dsp/util/quantize.h @@ -15,6 +15,9 @@ class Quantizer { Quantizer() {} ~Quantizer() {} + void Quantize(const Tensor &in_tensor, + Tensor *out_tensor, + float *min_out, float *max_out); void Quantize(const Tensor &in_tensor, const float min_in, const float max_in, Tensor *out_tensor, @@ -30,6 +33,8 @@ class Quantizer { float *max_out, float *stepsize, float *recip_stepsize); + + DISABLE_COPY_AND_ASSIGN(Quantizer); }; } // mace