summaryrefslogtreecommitdiff
path: root/compiler/luci/pass/src
diff options
context:
space:
mode:
authorChunseok Lee <chunseok.lee@samsung.com>2021-11-15 10:54:00 +0900
committerChunseok Lee <chunseok.lee@samsung.com>2021-11-15 10:54:00 +0900
commitac6e4dd7b480e83b586ef533d7b29a8a97eb48fe (patch)
tree70fc4849fbc296ef037f57bf7f678882530cf3de /compiler/luci/pass/src
parent33ae5d70a1ed85d215c1293ed63afbf3517b07d5 (diff)
downloadnnfw-ac6e4dd7b480e83b586ef533d7b29a8a97eb48fe.tar.gz
nnfw-ac6e4dd7b480e83b586ef533d7b29a8a97eb48fe.tar.bz2
nnfw-ac6e4dd7b480e83b586ef533d7b29a8a97eb48fe.zip
Diffstat (limited to 'compiler/luci/pass/src')
-rw-r--r--compiler/luci/pass/src/CircleOptimizer.cpp22
-rw-r--r--compiler/luci/pass/src/FuseActivationFunctionPass.cpp10
-rw-r--r--compiler/luci/pass/src/FuseActivationFunctionPass.test.cpp77
-rw-r--r--compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp157
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
}