diff options
author | Chunseok Lee <chunseok.lee@samsung.com> | 2021-11-15 10:54:00 +0900 |
---|---|---|
committer | Chunseok Lee <chunseok.lee@samsung.com> | 2021-11-15 10:54:00 +0900 |
commit | ac6e4dd7b480e83b586ef533d7b29a8a97eb48fe (patch) | |
tree | 70fc4849fbc296ef037f57bf7f678882530cf3de /compiler/luci/pass/src | |
parent | 33ae5d70a1ed85d215c1293ed63afbf3517b07d5 (diff) | |
download | nnfw-ac6e4dd7b480e83b586ef533d7b29a8a97eb48fe.tar.gz nnfw-ac6e4dd7b480e83b586ef533d7b29a8a97eb48fe.tar.bz2 nnfw-ac6e4dd7b480e83b586ef533d7b29a8a97eb48fe.zip |
Imported Upstream version 1.19.0upstream/1.19.0submit/tizen/20211115.015830accepted/tizen/unified/20211116.130520
Diffstat (limited to 'compiler/luci/pass/src')
-rw-r--r-- | compiler/luci/pass/src/CircleOptimizer.cpp | 22 | ||||
-rw-r--r-- | compiler/luci/pass/src/FuseActivationFunctionPass.cpp | 10 | ||||
-rw-r--r-- | compiler/luci/pass/src/FuseActivationFunctionPass.test.cpp | 77 | ||||
-rw-r--r-- | compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp | 157 |
4 files changed, 253 insertions, 13 deletions
diff --git a/compiler/luci/pass/src/CircleOptimizer.cpp b/compiler/luci/pass/src/CircleOptimizer.cpp index 5d0c92625..75f04b3b5 100644 --- a/compiler/luci/pass/src/CircleOptimizer.cpp +++ b/compiler/luci/pass/src/CircleOptimizer.cpp @@ -468,12 +468,20 @@ void CircleOptimizer::quantize(loco::Graph *g) const static const std::vector<std::string> qwmm_supported_input_model_dtype{"float32"}; static const std::vector<std::string> qwmm_supported_output_model_dtype{"uint8", "int16"}; static const std::vector<std::string> qwmm_supported_granularity{"layer", "channel"}; + static const std::vector<std::string> qwmm_supported_input_type{"uint8", "int16"}; + static const std::vector<std::string> qwmm_supported_output_type{"uint8", "int16"}; auto input_model_dtype = _options->param(Options::AlgorithmParameters::Quantize_input_model_dtype); auto output_model_dtype = _options->param(Options::AlgorithmParameters::Quantize_output_model_dtype); auto granularity = _options->param(Options::AlgorithmParameters::Quantize_granularity); + auto input_type = _options->param(Options::AlgorithmParameters::Quantize_input_type); + if (input_type.empty()) + input_type = output_model_dtype; + auto output_type = _options->param(Options::AlgorithmParameters::Quantize_output_type); + if (output_type.empty()) + output_type = output_model_dtype; if (!in_array(to_lower_case(input_model_dtype), qwmm_supported_input_model_dtype)) throw std::runtime_error("Unsupported input type. List of supported input types: " + @@ -487,13 +495,21 @@ void CircleOptimizer::quantize(loco::Graph *g) const throw std::runtime_error("Unsupported granularity. List of supported granularity: " + to_string(qwmm_supported_granularity)); + if (!in_array(to_lower_case(input_type), qwmm_supported_input_type)) + throw std::runtime_error("Unsupported input type. List of supported input types: " + + to_string(qwmm_supported_input_type)); + + if (!in_array(to_lower_case(output_type), qwmm_supported_output_type)) + throw std::runtime_error("Unsupported output type. List of supported output types: " + + to_string(qwmm_supported_output_type)); + if (str_to_granularity(granularity) == QuantizationGranularity::LayerWise && str_to_dtype(output_model_dtype) != loco::DataType::U8) throw std::runtime_error("Layer-wise quantization only supports uint8 dtype."); - luci::QuantizeWithMinMaxPass quantizer(str_to_dtype(input_model_dtype), - str_to_dtype(output_model_dtype), - str_to_granularity(granularity)); + luci::QuantizeWithMinMaxPass quantizer( + str_to_dtype(input_model_dtype), str_to_dtype(output_model_dtype), + str_to_granularity(granularity), str_to_dtype(input_type), str_to_dtype(output_type)); quantizer.run(g); // Post-quantization optimizations diff --git a/compiler/luci/pass/src/FuseActivationFunctionPass.cpp b/compiler/luci/pass/src/FuseActivationFunctionPass.cpp index 66e341518..d83973cd5 100644 --- a/compiler/luci/pass/src/FuseActivationFunctionPass.cpp +++ b/compiler/luci/pass/src/FuseActivationFunctionPass.cpp @@ -72,13 +72,6 @@ bool fuse_activation_function(luci::CircleNode *node) else return false; } - else if (opcode == luci::CircleOpcode::TANH) - { - if (fused_act == luci::FusedActFunc::NONE) - target_func = luci::FusedActFunc::TANH; - else - return false; - } else return false; @@ -98,8 +91,9 @@ bool FuseActivationFunctionPass::run(loco::Graph *g) { auto circle_node = static_cast<luci::CircleNode *>(node); auto opcode = circle_node->opcode(); + // TANH is not supported as CONV fused with TANH is not supported in luci-interpreter if (opcode == luci::CircleOpcode::RELU || opcode == luci::CircleOpcode::RELU6 || - opcode == luci::CircleOpcode::RELU_N1_TO_1 || opcode == luci::CircleOpcode::TANH) + opcode == luci::CircleOpcode::RELU_N1_TO_1) { if (fuse_activation_function(circle_node)) changed = true; diff --git a/compiler/luci/pass/src/FuseActivationFunctionPass.test.cpp b/compiler/luci/pass/src/FuseActivationFunctionPass.test.cpp index 56b414143..9e0a80df1 100644 --- a/compiler/luci/pass/src/FuseActivationFunctionPass.test.cpp +++ b/compiler/luci/pass/src/FuseActivationFunctionPass.test.cpp @@ -86,6 +86,47 @@ protected: luci::CircleConst *_conv2_b = nullptr; }; +class ConvTanhConvGraphlet +{ +public: + ConvTanhConvGraphlet() = default; + + void init(loco::Graph *g) + { + _conv1 = g->nodes()->create<luci::CircleConv2D>(); + _conv2 = g->nodes()->create<luci::CircleConv2D>(); + _tanh = g->nodes()->create<luci::CircleTanh>(); + _conv1_f = g->nodes()->create<luci::CircleConst>(); + _conv1_b = g->nodes()->create<luci::CircleConst>(); + _conv2_f = g->nodes()->create<luci::CircleConst>(); + _conv2_b = g->nodes()->create<luci::CircleConst>(); + + _conv1->fusedActivationFunction(luci::FusedActFunc::NONE); + + _conv1->name("conv1"); + _conv2->name("conv2"); + _tanh->name("tanh"); + _conv1_f->name("conv1f"); + _conv1_b->name("conv1b"); + _conv2_f->name("conv2f"); + _conv2_b->name("conv2b"); + } + +public: + luci::CircleTanh *tanh() { return _tanh; } + luci::CircleConv2D *conv1() { return _conv1; } + luci::CircleConv2D *conv2() { return _conv2; } + +protected: + luci::CircleConv2D *_conv1 = nullptr; + luci::CircleConv2D *_conv2 = nullptr; + luci::CircleTanh *_tanh = nullptr; + luci::CircleConst *_conv1_f = nullptr; + luci::CircleConst *_conv1_b = nullptr; + luci::CircleConst *_conv2_f = nullptr; + luci::CircleConst *_conv2_b = nullptr; +}; + class FuseActTestGraph : public TestIOGraph, public ConvReluConvGraphlet { public: @@ -110,6 +151,30 @@ public: } }; +class FuseTanhActTestGraph : public TestIOGraph, public ConvTanhConvGraphlet +{ +public: + FuseTanhActTestGraph() = default; + + void init(void) + { + TestIOGraph::init({1}, {1}); + ConvTanhConvGraphlet::init(g()); + + _conv1->input(input()); + _conv1->filter(_conv1_f); + _conv1->bias(_conv1_b); + + _tanh->x(_conv1); + + _conv2->input(_tanh); + _conv2->filter(_conv2_f); + _conv2->bias(_conv2_b); + + output()->from(_conv2); + } +}; + class ConvHasMultiSuccGraph : public TestIOGraph, public ConvReluConvGraphlet { public: @@ -190,3 +255,15 @@ TEST(FusePreActivationBatchNorm, fuse_activation_function_tanh_NEG) // Relu input Conv2D already has activation function EXPECT_FALSE(pass.run(g.g())); } + +TEST(FusePreActivationBatchNorm, fuse_tanh_NEG) +{ + FuseTanhActTestGraph g; + luci::FuseActivationFunctionPass pass; + + g.init(); + + // Tanh should not be fused + // This can be changed when CONV+TANH is supported by luci-interpreter + EXPECT_FALSE(pass.run(g.g())); +} diff --git a/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp b/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp index be81732f8..c3552ec52 100644 --- a/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp +++ b/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp @@ -20,6 +20,7 @@ #include <luci/IR/CircleNodes.h> #include <luci/IR/CircleNodeVisitor.h> #include <luci/Service/Nodes/CircleConst.h> +#include <luci/Profile/CircleNodeOrigin.h> #include <luci/Log.h> #include <oops/UserExn.h> @@ -63,6 +64,52 @@ void iterate_per_channel(CircleConst *node, int32_t &channel_dim_index, IterFunc } } +// Create a Quantize Op whose +// dtype is out_type +// shape is the same with node +// qparam is computed using node's min/max +luci::CircleQuantize *create_quantize_op(luci::CircleNode *node, loco::DataType out_type) +{ + auto quantize = node->graph()->nodes()->create<CircleQuantize>(); + quantize->name(node->name() + "_Quantize"); + quantize->dtype(out_type); + quantize->rank(node->rank()); + for (uint32_t i = 0; i < node->rank(); i++) + quantize->dim(i).set(node->dim(i).value()); + + quantize->shape_status(luci::ShapeStatus::VALID); + + auto qparam = node->quantparam(); + assert(qparam); // FIX_CALLER_UNLESS + assert(qparam->min.size() == 1); // FIX_CALLER_UNLESS + assert(qparam->max.size() == 1); // FIX_CALLER_UNLESS + auto min = qparam->min[0]; + auto max = qparam->max[0]; + + float scaling_factor{0}; + int64_t zp{0}; + float nudged_min{0}; + float nudged_max{0}; + + if (out_type == loco::DataType::U8) + { + compute_asym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max); + } + else + { + assert(out_type == loco::DataType::S16); + compute_sym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max); + } + + auto quantparam = std::make_unique<CircleQuantParam>(); + quantparam->scale.push_back(scaling_factor); + quantparam->zerop.push_back(zp); + + quantize->quantparam(std::move(quantparam)); + + return quantize; +} + } // namespace namespace luci @@ -743,8 +790,6 @@ struct QuantizeActivation final : public luci::CircleNodeMutableVisitor<bool> scaling_factor = scaling_factor < 1 ? 1.0f : std::round(scaling_factor); } - circle_node->quantparam()->min.clear(); - circle_node->quantparam()->max.clear(); circle_node->quantparam()->scale.push_back(scaling_factor); circle_node->quantparam()->zerop.push_back(zp); } @@ -1467,6 +1512,97 @@ void propagate_pad_v2_quantparam(luci::CirclePadV2 *pad_v2, loco::DataType quant quant_input(&CirclePadV2::constant_values, 2); } +void QuantizeWithMinMaxPass::set_input_type(loco::Graph *g) const +{ + auto inputs = g->inputs(); + for (auto node : loco::input_nodes(g)) + { + auto input = loco::must_cast<luci::CircleInput *>(node); + if (input->dtype() == _input_type) + continue; + + // Bool type is not quantizable + if (input->dtype() == loco::DataType::BOOL) + continue; + + // Insert Quantize Op + auto quant_op = create_quantize_op(input, input->dtype()); + loco::replace(input).with(quant_op); + quant_op->input(input); + + // TODO Set a proper origin (Quantize should have its own Origin) + { + auto succs = loco::succs(quant_op); + assert(succs.size() > 0); + auto succ = loco::must_cast<luci::CircleNode *>(*succs.begin()); + luci::add_origin(quant_op, luci::get_origin(succ)); + } + + // Requantize input + { + auto quantparam = input->quantparam(); + assert(quantparam); + assert(quantparam->min.size() == 1); // only support layer-wise quant + assert(quantparam->max.size() == 1); // only support layer-wise quant + auto min = quantparam->min[0]; + auto max = quantparam->max[0]; + + float scaling_factor{0}; + int64_t zp{0}; + float nudged_min{0}; + float nudged_max{0}; + + if (_input_type == loco::DataType::U8) + { + compute_asym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max); + } + else + { + assert(_input_type == loco::DataType::S16); + compute_sym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max); + } + input->dtype(_input_type); + input->quantparam()->scale[0] = scaling_factor; + input->quantparam()->zerop[0] = zp; + } + + auto graph_input = inputs->at(input->index()); + graph_input->dtype(_input_type); + } +} + +void QuantizeWithMinMaxPass::set_output_type(loco::Graph *g) const +{ + auto outputs = g->outputs(); + for (auto node : loco::output_nodes(g)) + { + auto output = loco::must_cast<luci::CircleOutput *>(node); + if (output->dtype() == _output_type) + continue; + + // Bool type is not quantizable + if (output->dtype() == loco::DataType::BOOL) + continue; + + auto from = loco::must_cast<luci::CircleNode *>(output->from()); + + // The last Op is not quantizable Op (ex: ArgMax) + if (not from->quantparam()) + continue; + + // Insert Quantize Op + auto quant_op = create_quantize_op(from, _output_type); + loco::replace(from).with(quant_op); + quant_op->input(from); + + // TODO Set a proper origin (Quantize should have its own Origin) + luci::add_origin(quant_op, luci::get_origin(from)); + + auto graph_output = outputs->at(output->index()); + graph_output->dtype(_output_type); + } +} + bool QuantizeWithMinMaxPass::run(loco::Graph *g) { LOGGER(l); @@ -1539,6 +1675,23 @@ bool QuantizeWithMinMaxPass::run(loco::Graph *g) } } + // Set input type + set_input_type(g); + + // Set output type + set_output_type(g); + + // Remove min/max values + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto circle_node = loco::must_cast<luci::CircleNode *>(node); + if (auto qparam = circle_node->quantparam()) + { + qparam->min.clear(); + qparam->max.clear(); + } + } + INFO(l) << "QuantizeWithMinMaxPass End" << std::endl; return false; // one time run } |