From df3cc77ba03f2c2dc5c872eeceafdf504efa3447 Mon Sep 17 00:00:00 2001 From: daquexian Date: Thu, 10 Jan 2019 13:18:41 +0800 Subject: [PATCH 1/4] Refactor and support prelu(not tested) --- common/daq.fbs | 26 ++- common/daq_generated.h | 317 ++++++++++++++++++++++++++++++- common/helper.h | 2 + dnnlibrary/src/DaqReader.cpp | 33 ++++ tools/onnx2daq/OnnxConverter.cpp | 139 +++++--------- tools/onnx2daq/OnnxConverter.h | 115 ++++++++++- 6 files changed, 535 insertions(+), 97 deletions(-) diff --git a/common/daq.fbs b/common/daq.fbs index 7984cfd..2689d6d 100644 --- a/common/daq.fbs +++ b/common/daq.fbs @@ -3,7 +3,7 @@ namespace DNN; enum DataType:byte { Float32 = 0, Int8 } enum FuseCode:byte { None = 0, Relu, Relu1, Relu6 } enum LayerType:byte { Conv2D = 0, AvePool, MaxPool, Relu, Softmax, FC, Add, Concat, - DepthwiseConv2D, BatchToSpace, SpaceToBatch, StridedSlice } + DepthwiseConv2D, BatchToSpace, SpaceToBatch, StridedSlice, Mul, AddScalar, MulScalar } table Tensor { data_type:DataType; @@ -112,6 +112,27 @@ table Concat { output:string; } +table Mul { + input1:string; + input2:string; + fuse:FuseCode; + output:string; +} + +table AddScalar { + input1:string; + input2:float; + fuse:FuseCode; + output:string; +} + +table MulScalar { + input1:string; + input2:float; + fuse:FuseCode; + output:string; +} + table Layer { type:LayerType; conv2d_param:Conv2D; @@ -126,6 +147,9 @@ table Layer { batch_to_space_param:BatchToSpace; space_to_batch_param:SpaceToBatch; strided_slice_param:StridedSlice; + mul_param:Mul; + add_scalar_param:AddScalar; + mul_scalar_param:MulScalar; } table Model { diff --git a/common/daq_generated.h b/common/daq_generated.h index b305302..6ae1268 100644 --- a/common/daq_generated.h +++ b/common/daq_generated.h @@ -36,6 +36,12 @@ struct Add; struct Concat; +struct Mul; + +struct AddScalar; + +struct MulScalar; + struct Layer; struct Model; @@ -117,11 +123,14 @@ enum class LayerType : int8_t { BatchToSpace = 9, SpaceToBatch = 10, StridedSlice = 11, + Mul = 12, + AddScalar = 13, + MulScalar = 14, MIN = Conv2D, - MAX = StridedSlice + MAX = MulScalar }; -inline const LayerType (&EnumValuesLayerType())[12] { +inline const LayerType (&EnumValuesLayerType())[15] { static const LayerType values[] = { LayerType::Conv2D, LayerType::AvePool, @@ -134,7 +143,10 @@ inline const LayerType (&EnumValuesLayerType())[12] { LayerType::DepthwiseConv2D, LayerType::BatchToSpace, LayerType::SpaceToBatch, - LayerType::StridedSlice + LayerType::StridedSlice, + LayerType::Mul, + LayerType::AddScalar, + LayerType::MulScalar }; return values; } @@ -153,6 +165,9 @@ inline const char * const *EnumNamesLayerType() { "BatchToSpace", "SpaceToBatch", "StridedSlice", + "Mul", + "AddScalar", + "MulScalar", nullptr }; return names; @@ -1501,6 +1516,265 @@ inline flatbuffers::Offset CreateConcatDirect( output ? _fbb.CreateString(output) : 0); } +struct Mul FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum { + VT_INPUT1 = 4, + VT_INPUT2 = 6, + VT_FUSE = 8, + VT_OUTPUT = 10 + }; + const flatbuffers::String *input1() const { + return GetPointer(VT_INPUT1); + } + const flatbuffers::String *input2() const { + return GetPointer(VT_INPUT2); + } + FuseCode fuse() const { + return static_cast(GetField(VT_FUSE, 0)); + } + const flatbuffers::String *output() const { + return GetPointer(VT_OUTPUT); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_INPUT1) && + verifier.VerifyString(input1()) && + VerifyOffset(verifier, VT_INPUT2) && + verifier.VerifyString(input2()) && + VerifyField(verifier, VT_FUSE) && + VerifyOffset(verifier, VT_OUTPUT) && + verifier.VerifyString(output()) && + verifier.EndTable(); + } +}; + +struct MulBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_input1(flatbuffers::Offset input1) { + fbb_.AddOffset(Mul::VT_INPUT1, input1); + } + void add_input2(flatbuffers::Offset input2) { + fbb_.AddOffset(Mul::VT_INPUT2, input2); + } + void add_fuse(FuseCode fuse) { + fbb_.AddElement(Mul::VT_FUSE, static_cast(fuse), 0); + } + void add_output(flatbuffers::Offset output) { + fbb_.AddOffset(Mul::VT_OUTPUT, output); + } + explicit MulBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + MulBuilder &operator=(const MulBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateMul( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset input1 = 0, + flatbuffers::Offset input2 = 0, + FuseCode fuse = FuseCode::None, + flatbuffers::Offset output = 0) { + MulBuilder builder_(_fbb); + builder_.add_output(output); + builder_.add_input2(input2); + builder_.add_input1(input1); + builder_.add_fuse(fuse); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateMulDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const char *input1 = nullptr, + const char *input2 = nullptr, + FuseCode fuse = FuseCode::None, + const char *output = nullptr) { + return DNN::CreateMul( + _fbb, + input1 ? _fbb.CreateString(input1) : 0, + input2 ? _fbb.CreateString(input2) : 0, + fuse, + output ? _fbb.CreateString(output) : 0); +} + +struct AddScalar FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum { + VT_INPUT1 = 4, + VT_INPUT2 = 6, + VT_FUSE = 8, + VT_OUTPUT = 10 + }; + const flatbuffers::String *input1() const { + return GetPointer(VT_INPUT1); + } + float input2() const { + return GetField(VT_INPUT2, 0.0f); + } + FuseCode fuse() const { + return static_cast(GetField(VT_FUSE, 0)); + } + const flatbuffers::String *output() const { + return GetPointer(VT_OUTPUT); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_INPUT1) && + verifier.VerifyString(input1()) && + VerifyField(verifier, VT_INPUT2) && + VerifyField(verifier, VT_FUSE) && + VerifyOffset(verifier, VT_OUTPUT) && + verifier.VerifyString(output()) && + verifier.EndTable(); + } +}; + +struct AddScalarBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_input1(flatbuffers::Offset input1) { + fbb_.AddOffset(AddScalar::VT_INPUT1, input1); + } + void add_input2(float input2) { + fbb_.AddElement(AddScalar::VT_INPUT2, input2, 0.0f); + } + void add_fuse(FuseCode fuse) { + fbb_.AddElement(AddScalar::VT_FUSE, static_cast(fuse), 0); + } + void add_output(flatbuffers::Offset output) { + fbb_.AddOffset(AddScalar::VT_OUTPUT, output); + } + explicit AddScalarBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + AddScalarBuilder &operator=(const AddScalarBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateAddScalar( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset input1 = 0, + float input2 = 0.0f, + FuseCode fuse = FuseCode::None, + flatbuffers::Offset output = 0) { + AddScalarBuilder builder_(_fbb); + builder_.add_output(output); + builder_.add_input2(input2); + builder_.add_input1(input1); + builder_.add_fuse(fuse); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateAddScalarDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const char *input1 = nullptr, + float input2 = 0.0f, + FuseCode fuse = FuseCode::None, + const char *output = nullptr) { + return DNN::CreateAddScalar( + _fbb, + input1 ? _fbb.CreateString(input1) : 0, + input2, + fuse, + output ? _fbb.CreateString(output) : 0); +} + +struct MulScalar FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum { + VT_INPUT1 = 4, + VT_INPUT2 = 6, + VT_FUSE = 8, + VT_OUTPUT = 10 + }; + const flatbuffers::String *input1() const { + return GetPointer(VT_INPUT1); + } + float input2() const { + return GetField(VT_INPUT2, 0.0f); + } + FuseCode fuse() const { + return static_cast(GetField(VT_FUSE, 0)); + } + const flatbuffers::String *output() const { + return GetPointer(VT_OUTPUT); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_INPUT1) && + verifier.VerifyString(input1()) && + VerifyField(verifier, VT_INPUT2) && + VerifyField(verifier, VT_FUSE) && + VerifyOffset(verifier, VT_OUTPUT) && + verifier.VerifyString(output()) && + verifier.EndTable(); + } +}; + +struct MulScalarBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_input1(flatbuffers::Offset input1) { + fbb_.AddOffset(MulScalar::VT_INPUT1, input1); + } + void add_input2(float input2) { + fbb_.AddElement(MulScalar::VT_INPUT2, input2, 0.0f); + } + void add_fuse(FuseCode fuse) { + fbb_.AddElement(MulScalar::VT_FUSE, static_cast(fuse), 0); + } + void add_output(flatbuffers::Offset output) { + fbb_.AddOffset(MulScalar::VT_OUTPUT, output); + } + explicit MulScalarBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + MulScalarBuilder &operator=(const MulScalarBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateMulScalar( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset input1 = 0, + float input2 = 0.0f, + FuseCode fuse = FuseCode::None, + flatbuffers::Offset output = 0) { + MulScalarBuilder builder_(_fbb); + builder_.add_output(output); + builder_.add_input2(input2); + builder_.add_input1(input1); + builder_.add_fuse(fuse); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateMulScalarDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const char *input1 = nullptr, + float input2 = 0.0f, + FuseCode fuse = FuseCode::None, + const char *output = nullptr) { + return DNN::CreateMulScalar( + _fbb, + input1 ? _fbb.CreateString(input1) : 0, + input2, + fuse, + output ? _fbb.CreateString(output) : 0); +} + struct Layer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_TYPE = 4, @@ -1515,7 +1789,10 @@ struct Layer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_DEPTHWISE_CONV2D_PARAM = 22, VT_BATCH_TO_SPACE_PARAM = 24, VT_SPACE_TO_BATCH_PARAM = 26, - VT_STRIDED_SLICE_PARAM = 28 + VT_STRIDED_SLICE_PARAM = 28, + VT_MUL_PARAM = 30, + VT_ADD_SCALAR_PARAM = 32, + VT_MUL_SCALAR_PARAM = 34 }; LayerType type() const { return static_cast(GetField(VT_TYPE, 0)); @@ -1556,6 +1833,15 @@ struct Layer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const StridedSlice *strided_slice_param() const { return GetPointer(VT_STRIDED_SLICE_PARAM); } + const Mul *mul_param() const { + return GetPointer(VT_MUL_PARAM); + } + const AddScalar *add_scalar_param() const { + return GetPointer(VT_ADD_SCALAR_PARAM); + } + const MulScalar *mul_scalar_param() const { + return GetPointer(VT_MUL_SCALAR_PARAM); + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_TYPE) && @@ -1583,6 +1869,12 @@ struct Layer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { verifier.VerifyTable(space_to_batch_param()) && VerifyOffset(verifier, VT_STRIDED_SLICE_PARAM) && verifier.VerifyTable(strided_slice_param()) && + VerifyOffset(verifier, VT_MUL_PARAM) && + verifier.VerifyTable(mul_param()) && + VerifyOffset(verifier, VT_ADD_SCALAR_PARAM) && + verifier.VerifyTable(add_scalar_param()) && + VerifyOffset(verifier, VT_MUL_SCALAR_PARAM) && + verifier.VerifyTable(mul_scalar_param()) && verifier.EndTable(); } }; @@ -1629,6 +1921,15 @@ struct LayerBuilder { void add_strided_slice_param(flatbuffers::Offset strided_slice_param) { fbb_.AddOffset(Layer::VT_STRIDED_SLICE_PARAM, strided_slice_param); } + void add_mul_param(flatbuffers::Offset mul_param) { + fbb_.AddOffset(Layer::VT_MUL_PARAM, mul_param); + } + void add_add_scalar_param(flatbuffers::Offset add_scalar_param) { + fbb_.AddOffset(Layer::VT_ADD_SCALAR_PARAM, add_scalar_param); + } + void add_mul_scalar_param(flatbuffers::Offset mul_scalar_param) { + fbb_.AddOffset(Layer::VT_MUL_SCALAR_PARAM, mul_scalar_param); + } explicit LayerBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); @@ -1655,8 +1956,14 @@ inline flatbuffers::Offset CreateLayer( flatbuffers::Offset depthwise_conv2d_param = 0, flatbuffers::Offset batch_to_space_param = 0, flatbuffers::Offset space_to_batch_param = 0, - flatbuffers::Offset strided_slice_param = 0) { + flatbuffers::Offset strided_slice_param = 0, + flatbuffers::Offset mul_param = 0, + flatbuffers::Offset add_scalar_param = 0, + flatbuffers::Offset mul_scalar_param = 0) { LayerBuilder builder_(_fbb); + builder_.add_mul_scalar_param(mul_scalar_param); + builder_.add_add_scalar_param(add_scalar_param); + builder_.add_mul_param(mul_param); builder_.add_strided_slice_param(strided_slice_param); builder_.add_space_to_batch_param(space_to_batch_param); builder_.add_batch_to_space_param(batch_to_space_param); diff --git a/common/helper.h b/common/helper.h index f99fa54..f38b2a6 100644 --- a/common/helper.h +++ b/common/helper.h @@ -10,4 +10,6 @@ T Product(const std::vector &v) { return static_cast (accumulate(v.begin(), v.end(), 1, std::multiplies())); } +using css = const std::string; + #endif /* DNNLIBRARY_HELPER_H */ diff --git a/dnnlibrary/src/DaqReader.cpp b/dnnlibrary/src/DaqReader.cpp index ac59937..3301e38 100644 --- a/dnnlibrary/src/DaqReader.cpp +++ b/dnnlibrary/src/DaqReader.cpp @@ -42,6 +42,12 @@ std::string layer_type_to_str(DNN::LayerType type) { return "space2batch"; case DNN::LayerType::StridedSlice: return "stridedslice"; + case DNN::LayerType::Mul: + return "mul"; + case DNN::LayerType::MulScalar: + return "mulscalar"; + case DNN::LayerType::AddScalar: + return "addscalar"; } } @@ -181,6 +187,33 @@ void AddLayers(const DNN::Model &model, ModelBuilder &builder) { builder.AddAddTensor(input1_name, input2_name, output_name); break; } + case DNN::LayerType::AddScalar: { + auto param = layer->add_scalar_param(); + auto input1_name = param->input1()->str(); + auto input2 = param->input2(); + auto output_name = param->output()->str(); + LOG(INFO) << "Add, input1 " << input1_name << ", input2 " << input2 << ", output: " << output_name; + builder.AddAddScalar(input1_name, input2, output_name); + break; + } + case DNN::LayerType::Mul: { + auto param = layer->add_param(); + auto input1_name = param->input1()->str(); + auto input2_name = param->input2()->str(); + auto output_name = param->output()->str(); + LOG(INFO) << "Mul, input1 " << input1_name << ", input2 " << input2_name << ", output: " << output_name; + builder.AddMulTensor(input1_name, input2_name, output_name); + break; + } + case DNN::LayerType::MulScalar: { + auto param = layer->add_scalar_param(); + auto input1_name = param->input1()->str(); + auto input2 = param->input2(); + auto output_name = param->output()->str(); + LOG(INFO) << "Mul, input1 " << input1_name << ", input2 " << input2 << ", output: " << output_name; + builder.AddMulScalar(input1_name, input2, output_name); + break; + } case DNN::LayerType::FC: { auto param = layer->fc_param(); auto fuse = param->fuse(); diff --git a/tools/onnx2daq/OnnxConverter.cpp b/tools/onnx2daq/OnnxConverter.cpp index dde6ef0..228c090 100644 --- a/tools/onnx2daq/OnnxConverter.cpp +++ b/tools/onnx2daq/OnnxConverter.cpp @@ -37,10 +37,10 @@ DNN::FuseCode OnnxConverter::ConvertFuseCodeType(FuseCode fuse_code) { throw std::invalid_argument("Invalid FuseCode"); } -std::pair, OnnxConverter::FuseCode> OnnxConverter::FindActivation(const ONNX_NAMESPACE::ModelProto &model_proto, const ONNX_NAMESPACE::NodeProto &node) { +std::pair, OnnxConverter::FuseCode> OnnxConverter::FindActivation(const ONNX_NAMESPACE::ModelProto &model_proto, css &output_name) { std::pair, FuseCode> activation{{}, FuseCode::FUSED_NONE}; for (const auto &_node : model_proto.graph().node()) { - if (!node.output().empty() && !_node.input().empty() && node.output(0) == _node.input(0) && _node.op_type() == "Relu") { + if (!_node.input().empty() && output_name == _node.input(0) && _node.op_type() == "Relu") { // If there are two branches after a conv/pool and both branches has a relu on the top, we have to add two normal relu layers if (activation.second != FuseCode::FUSED_NONE) { return {{}, FuseCode::FUSED_NONE}; @@ -145,9 +145,9 @@ void OnnxConverter::AddConv(const string &input_name, const std::vector &st void OnnxConverter::Convert(const ONNX_NAMESPACE::ModelProto &model_proto, const std::string &filepath) { GOOGLE_PROTOBUF_VERIFY_VERSION; - auto optimized = ONNX_NAMESPACE::optimization::Optimize(model_proto, vector{"fuse_bn_into_conv"}); + model_proto_ = ONNX_NAMESPACE::optimization::Optimize(model_proto, vector{"fuse_bn_into_conv"}); - for (const auto &tensor : optimized.graph().initializer()) { + for (const auto &tensor : model_proto_.graph().initializer()) { if (tensor.data_type() == ONNX_NAMESPACE::TensorProto_DataType_FLOAT) { const float *ptr = tensor.float_data().empty() ? reinterpret_cast(tensor.raw_data().data()) : tensor.float_data().data(); @@ -163,7 +163,7 @@ void OnnxConverter::Convert(const ONNX_NAMESPACE::ModelProto &model_proto, const } vector> inputs; - for (const auto &input : optimized.graph().input()) { + for (const auto &input : model_proto_.graph().input()) { if (std::find(operands_.begin(), operands_.end(), input.name()) != operands_.end()) { continue; } @@ -182,9 +182,8 @@ void OnnxConverter::Convert(const ONNX_NAMESPACE::ModelProto &model_proto, const inputs.push_back(flat_input); } - vector skipped_act; bool has_reshape = false; - for (const auto &node : optimized.graph().node()) { + for (const auto &node : model_proto_.graph().node()) { if (has_reshape) { throw std::invalid_argument("Reshape can only be the last layer for now"); } @@ -200,9 +199,9 @@ void OnnxConverter::Convert(const ONNX_NAMESPACE::ModelProto &model_proto, const CHECK_EQ(strides.size(), 2ul); CHECK_EQ(dilations.size(), 2ul); auto group = helper.get("group", 1); - auto activation = FindActivation(optimized, node); + auto activation = FindActivation(model_proto_, node.output(0)); if (activation.first.has_value()) { - skipped_act.push_back(activation.first.value()); + skipped_act_.push_back(activation.first.value()); } nonstd::optional bias_name; if (node.input_size() >= 3) { @@ -246,22 +245,7 @@ void OnnxConverter::Convert(const ONNX_NAMESPACE::ModelProto &model_proto, const CHECK_EQ(pads.size(), 4ul); CHECK_EQ(kernel_shape.size(), 2ul); CHECK_EQ(strides.size(), 2ul); - auto activation = FindActivation(optimized, node); - if (activation.first.has_value()) { - skipped_act.push_back(activation.first.value()); - } - shaper_.Pool(input_name, strides[1], strides[0], pads[2], pads[3], pads[0], pads[1], kernel_shape[0], kernel_shape[1], output_name); - flatbuffers::Offset layer; - if (op == "AveragePool" || op == "GlobalAveragePool") { - auto param = DNN::CreateAvePoolDirect(builder_, input_name.c_str(), &kernel_shape, &pads, &strides, - ConvertFuseCodeType(activation.second), output_name.c_str()); - layer = DNN::CreateLayer(builder_, DNN::LayerType::AvePool, 0, param); - } else { - auto param = DNN::CreateMaxPoolDirect(builder_, input_name.c_str(), &kernel_shape, &pads, &strides, - ConvertFuseCodeType(activation.second), output_name.c_str()); - layer = DNN::CreateLayer(builder_, DNN::LayerType::MaxPool, 0, 0, param); - } - layers_.push_back(layer); + addLayerPool(op, input_name, kernel_shape, pads, strides, output_name); // operand_indexes[node.output(0)] = builder_.addPool(operand_indexes.at(node.input(0)), strides[1], strides[0], // pads[2], pads[3], pads[0], pads[1], // kernel_shape[0], kernel_shape[1], activation.second, @@ -272,102 +256,77 @@ void OnnxConverter::Convert(const ONNX_NAMESPACE::ModelProto &model_proto, const LOG(INFO) << "Start converting Relu"; auto input_name = m(node.input(0)); auto output_name = m(node.output(0)); - shaper_.Relu(input_name, output_name); - auto param = DNN::CreateReluDirect(builder_, input_name.c_str(), output_name.c_str()); - auto layer = DNN::CreateLayer(builder_, DNN::LayerType::Relu, 0, 0, 0, param); - layers_.push_back(layer); + addLayerRelu(input_name, output_name); LOG(INFO) << "Converting Relu completed"; // operand_indexes[node.output(0)] = builder_.addReLU(operand_indexes.at(node.input(0))); + + } else if (op == "PRelu") { + LOG(INFO) << "Start converting PRelu"; + const auto input_name = m(node.input(0)); + const auto slope_name = m(node.input(1)); + const auto output_name = m(node.output(0)); + const auto imm1_name = output_name + "_imm1"; + const auto imm2_name = output_name + "_imm2"; + const auto imm3_name = output_name + "_imm3"; + const auto imm4_name = output_name + "_imm4"; + const auto imm5_name = output_name + "_imm5"; + const auto imm6_name = output_name + "_imm6"; + addLayerRelu(input_name, imm1_name); + addLayerMul(input_name, slope_name, imm2_name); + addLayerMul(imm2_name, -1.f, imm3_name); + addLayerRelu(imm3_name, imm4_name); + addLayerMul(imm4_name, -1.f, imm5_name); + addLayerAdd(imm1_name, imm5_name, output_name); + // TODO: + LOG(INFO) << "Converting PRelu completed"; } else if (op == "Add") { LOG(INFO) << "Start converting Add"; auto input1_name = m(node.input(0)); auto input2_name = m(node.input(1)); auto output_name = m(node.output(0)); - shaper_.Eltwise(input1_name, input2_name, output_name); - auto activation = FindActivation(optimized, node); - if (activation.first.has_value()) { - skipped_act.push_back(activation.first.value()); - } - auto param = DNN::CreateAddDirect(builder_, input1_name.c_str(), input2_name.c_str(), - ConvertFuseCodeType(activation.second), output_name.c_str()); - auto layer = DNN::CreateLayer(builder_, DNN::LayerType::Add, 0, 0, 0, 0, 0, 0, param); - layers_.push_back(layer); + addLayerAdd(input1_name, input2_name, output_name); LOG(INFO) << "Converting Add completed"; // auto input1 = operand_indexes.at(node.input(0)); // auto input2 = operand_indexes.at(node.input(1)); // operand_indexes[node.output(1)] = builder.addAddTensor(input1, input2); + + } else if (op == "Mul") { + LOG(INFO) << "Start converting Mul"; + const auto input1_name = m(node.input(0)); + const auto input2_name = m(node.input(1)); + const auto output_name = m(node.output(0)); + addLayerMul(input1_name, input2_name, output_name); + LOG(INFO) << "Converting Mul completed"; } else if (op == "Gemm") { LOG(INFO) << "Start converting Gemm"; + auto input_name = m(node.input(0)); + auto weight_name = m(node.input(1)); + auto output_name = m(node.output(0)); + nonstd::optional bias_name; + if (node.input_size() >= 3) { + bias_name = m(node.input(2)); + } auto transA = helper.get("transA", 0); auto transB = helper.get("transB", 0); auto alpha = helper.get("alpha", 1.0f); auto beta = helper.get("beta", 1.0f); - if (transA == 0 && transB == 1 && alpha == 1.f && beta == 1.f) { - auto input_name = m(node.input(0)); - auto weight_name = m(node.input(1)); - { - nnapi_tensors_[weight_name] = onnx_tensors_.at(weight_name); - const auto &weight_tensor = nnapi_tensors_[weight_name]; - shaper_.AddShape(weight_name, weight_tensor.shape); - auto flat_tensor = DNN::CreateTensorDirect(builder_, DNN::DataType::Float32, nullptr, - &weight_tensor.data, &weight_tensor.shape, - weight_name.c_str()); - tensors_.push_back(flat_tensor); - } - string bias_name; - if (node.input_size() >= 3) { - bias_name = m(node.input(2)); - nnapi_tensors_[bias_name] = onnx_tensors_.at(bias_name); - const auto &bias_tensor = nnapi_tensors_[bias_name]; - auto flat_tensor = DNN::CreateTensorDirect(builder_, DNN::DataType::Float32, nullptr, - &bias_tensor.data, &bias_tensor.shape, bias_name.c_str()); - tensors_.push_back(flat_tensor); - } - auto activation = FindActivation(optimized, node); - if (activation.first.has_value()) { - skipped_act.push_back(activation.first.value()); - } - auto output_name = m(node.output(0)); - shaper_.FC(input_name, weight_name, output_name); - auto param = DNN::CreateFCDirect(builder_, input_name.c_str(), weight_name.c_str(), - node.input_size() >= 3 ? bias_name.c_str() : nullptr, - ConvertFuseCodeType(activation.second), output_name.c_str() - ); - auto layer = DNN::CreateLayer(builder_, DNN::LayerType::FC, 0, 0, 0, 0, 0, param, 0); - layers_.push_back(layer); - // builder.addFC(operand_indexes.at(node.input(0)), activation.second, - // operand_indexes.at(node.input(1)), operand_indexes.at(node.input(2))); - } else { - throw std::invalid_argument( - "Only transA == 0, transB == 1, alpha == 1.0 and beta == 1.0 is supported."); - } + addLayerGemm(input_name, weight_name, bias_name, transA, transB, alpha, beta, output_name); LOG(INFO) << "Converting Gemm completed"; } else if (op == "Softmax") { LOG(INFO) << "Start converting Softmax"; auto input_name = m(node.input(0)); auto output_name = m(node.output(0)); - shaper_.Softmax(input_name, output_name); - // simply ignore attribute "axis", because nnapi softmax didn't has this attr, and we will check the equality of the two ops in DaqReader.cpp - auto param = DNN::CreateSoftmaxDirect(builder_, input_name.c_str(), output_name.c_str()); - auto layer = DNN::CreateLayer(builder_, DNN::LayerType::Softmax, 0, 0, 0, 0, param); - layers_.push_back(layer); + addLayerSoftmax(input_name, output_name); LOG(INFO) << "Converting Softmax completed"; } else if (op == "Concat") { LOG(INFO) << "Start converting Concat"; - vector> concat_inputs; vector concat_inputs_str; for (const auto &onnx_input : node.input()) { concat_inputs_str.push_back(onnx_input); - auto flat_input = builder_.CreateString(m(onnx_input).c_str(), onnx_input.size()); - concat_inputs.push_back(flat_input); } auto axis = helper.get("axis", 1); - uint32_t axis_nchw_to_nhwc[4]{0, 3, 1, 2}; auto output_name = m(node.output(0)); - shaper_.Concat(concat_inputs_str, axis, output_name); - auto param = DNN::CreateConcatDirect(builder_, &concat_inputs, axis_nchw_to_nhwc[axis], output_name.c_str()); - auto layer = DNN::CreateLayer(builder_, DNN::LayerType::Concat, 0, 0, 0, 0, 0, 0, 0, param); - layers_.push_back(layer); + addLayerConcat(concat_inputs_str, output_name, axis); LOG(INFO) << "Converting Concat completed"; } else if (op == "Dropout") { LOG(INFO) << "Start converting Dropout"; diff --git a/tools/onnx2daq/OnnxConverter.h b/tools/onnx2daq/OnnxConverter.h index a4a06d8..bad6b65 100644 --- a/tools/onnx2daq/OnnxConverter.h +++ b/tools/onnx2daq/OnnxConverter.h @@ -29,7 +29,9 @@ class OnnxConverter { std::string m(const std::string &str); + ONNX_NAMESPACE::ModelProto model_proto_; flatbuffers::FlatBufferBuilder builder_; + std::vector skipped_act_; std::vector operands_; StrKeyMap nnapi_tensors_; @@ -39,12 +41,123 @@ class OnnxConverter { std::vector> tensors_; DNN::FuseCode ConvertFuseCodeType(FuseCode fuse_code); - std::pair, FuseCode> FindActivation(const ONNX_NAMESPACE::ModelProto &model_proto, const ONNX_NAMESPACE::NodeProto &node); + std::pair, FuseCode> FindActivation(const ONNX_NAMESPACE::ModelProto &model_proto, css &output_name); void AddConv(const std::string &input_name, const std::vector &strides, const std::vector &pads, const std::vector &dilations, int group, const std::pair, FuseCode>& activation, const std::string &ori_weight_name, const nonstd::optional &bias_name, const std::string &output_name); + inline void addLayerPool(css &op, css &input_name, const std::vector &kernel_shape, const std::vector &pads, const std::vector &strides, css &output_name) { + auto activation = FindActivation(model_proto_, output_name); + if (activation.first.has_value()) { + skipped_act_.push_back(activation.first.value()); + } + shaper_.Pool(input_name, strides[1], strides[0], pads[2], pads[3], pads[0], pads[1], kernel_shape[0], kernel_shape[1], output_name); + flatbuffers::Offset layer; + if (op == "AveragePool" || op == "GlobalAveragePool") { + auto param = DNN::CreateAvePoolDirect(builder_, input_name.c_str(), &kernel_shape, &pads, &strides, + ConvertFuseCodeType(activation.second), output_name.c_str()); + layer = DNN::CreateLayer(builder_, DNN::LayerType::AvePool, 0, param); + } else { + auto param = DNN::CreateMaxPoolDirect(builder_, input_name.c_str(), &kernel_shape, &pads, &strides, + ConvertFuseCodeType(activation.second), output_name.c_str()); + layer = DNN::CreateLayer(builder_, DNN::LayerType::MaxPool, 0, 0, param); + } + layers_.push_back(layer); + } + inline void addLayerRelu(css &input_name, css &output_name) { + shaper_.Relu(input_name, output_name); + auto param = DNN::CreateReluDirect(builder_, input_name.c_str(), output_name.c_str()); + auto layer = DNN::CreateLayer(builder_, DNN::LayerType::Relu, 0, 0, 0, param); + layers_.push_back(layer); + } + inline void addLayerAdd(css &input1_name, css &input2_name, css &output_name) { + shaper_.Eltwise(input1_name, input2_name, output_name); + auto activation = FindActivation(model_proto_, output_name); + if (activation.first.has_value()) { + skipped_act_.push_back(activation.first.value()); + } + auto param = DNN::CreateAddDirect(builder_, input1_name.c_str(), input2_name.c_str(), + ConvertFuseCodeType(activation.second), output_name.c_str()); + auto layer = DNN::CreateLayer(builder_, DNN::LayerType::Add, 0, 0, 0, 0, 0, 0, param); + layers_.push_back(layer); + } + inline void addLayerMul(css &input1_name, css &input2_name, css &output_name) { + shaper_.Eltwise(input1_name, input2_name, output_name); + const auto activation = FindActivation(model_proto_, output_name); + if (activation.first.has_value()) { + skipped_act_.push_back(activation.first.value()); + } + const auto param = DNN::CreateMulDirect(builder_, input1_name.c_str(), input2_name.c_str(), + ConvertFuseCodeType(activation.second), output_name.c_str()); + const auto layer = DNN::CreateLayer(builder_, DNN::LayerType::Mul, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, param); + layers_.push_back(layer); + } + inline void addLayerMul(css &input1_name, float input2, css &output_name) { + shaper_.Eltwise(input1_name, output_name); + const auto activation = FindActivation(model_proto_, output_name); + if (activation.first.has_value()) { + skipped_act_.push_back(activation.first.value()); + } + const auto param = DNN::CreateMulScalarDirect(builder_, input1_name.c_str(), input2, + ConvertFuseCodeType(activation.second), output_name.c_str()); + const auto layer = DNN::CreateLayer(builder_, DNN::LayerType::Mul, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, param); + layers_.push_back(layer); + } + inline void addLayerGemm(css &input_name, css &weight_name, nonstd::optional bias_name, const int transA, const int transB, const float alpha, const float beta, css &output_name) { + if (transA == 0 && transB == 1 && alpha == 1.f && beta == 1.f) { + { + nnapi_tensors_[weight_name] = onnx_tensors_.at(weight_name); + const auto &weight_tensor = nnapi_tensors_[weight_name]; + shaper_.AddShape(weight_name, weight_tensor.shape); + auto flat_tensor = DNN::CreateTensorDirect(builder_, DNN::DataType::Float32, nullptr, + &weight_tensor.data, &weight_tensor.shape, + weight_name.c_str()); + tensors_.push_back(flat_tensor); + } + if (bias_name.has_value()) { + nnapi_tensors_[bias_name.value()] = onnx_tensors_.at(bias_name.value()); + const auto &bias_tensor = nnapi_tensors_[bias_name.value()]; + auto flat_tensor = DNN::CreateTensorDirect(builder_, DNN::DataType::Float32, nullptr, + &bias_tensor.data, &bias_tensor.shape, bias_name.value().c_str()); + tensors_.push_back(flat_tensor); + } + auto activation = FindActivation(model_proto_, output_name); + if (activation.first.has_value()) { + skipped_act_.push_back(activation.first.value()); + } + shaper_.FC(input_name, weight_name, output_name); + auto param = DNN::CreateFCDirect(builder_, input_name.c_str(), weight_name.c_str(), + bias_name.has_value() ? bias_name.value().c_str() : nullptr, + ConvertFuseCodeType(activation.second), output_name.c_str() + ); + auto layer = DNN::CreateLayer(builder_, DNN::LayerType::FC, 0, 0, 0, 0, 0, param, 0); + layers_.push_back(layer); + } else { + throw std::invalid_argument( + "Only transA == 0, transB == 1, alpha == 1.0 and beta == 1.0 is supported."); + } + } + inline void addLayerSoftmax(css &input_name, css &output_name) { + shaper_.Softmax(input_name, output_name); + // simply ignore attribute "axis", because nnapi softmax didn't has this attr, and we will check the equality of the two ops in DaqReader.cpp + auto param = DNN::CreateSoftmaxDirect(builder_, input_name.c_str(), output_name.c_str()); + auto layer = DNN::CreateLayer(builder_, DNN::LayerType::Softmax, 0, 0, 0, 0, param); + layers_.push_back(layer); + } + // axis here is for onnx nchw + inline void addLayerConcat(const std::vector &inputs, css &output_name, const int axis) { + std::vector> concat_inputs; + for (const auto &onnx_input : inputs) { + auto flat_input = builder_.CreateString(m(onnx_input).c_str(), onnx_input.size()); + concat_inputs.push_back(flat_input); + } + uint32_t axis_nchw_to_nhwc[4]{0, 3, 1, 2}; + shaper_.Concat(inputs, axis, output_name); + auto param = DNN::CreateConcatDirect(builder_, &concat_inputs, axis_nchw_to_nhwc[axis], output_name.c_str()); + auto layer = DNN::CreateLayer(builder_, DNN::LayerType::Concat, 0, 0, 0, 0, 0, 0, 0, param); + layers_.push_back(layer); + } /** * onnx: [filter_out_channel, filter_in_channel / group, height, width] From 6e38bd38d731e4ecd8c5e456b08b75d12cbbf08b Mon Sep 17 00:00:00 2001 From: daquexian Date: Thu, 10 Jan 2019 13:30:48 +0800 Subject: [PATCH 2/4] Clear state on convert finishing --- tools/onnx2daq/OnnxConverter.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/onnx2daq/OnnxConverter.cpp b/tools/onnx2daq/OnnxConverter.cpp index 228c090..a99439d 100644 --- a/tools/onnx2daq/OnnxConverter.cpp +++ b/tools/onnx2daq/OnnxConverter.cpp @@ -354,4 +354,13 @@ void OnnxConverter::Convert(const ONNX_NAMESPACE::ModelProto &model_proto, const std::ofstream ofs(filepath); ofs.write(reinterpret_cast(builder_.GetBufferPointer()), builder_.GetSize()); ofs.close(); + + skipped_act_.clear(); + layers_.clear(); + operands_.clear(); + tensors_.clear(); + name_map_.clear(); + nnapi_tensors_.clear(); + onnx_tensors_.clear(); + shaper_.Clear(); } From 27f5b155809cfcd6884ed1069808d7635a5740a2 Mon Sep 17 00:00:00 2001 From: daquexian Date: Thu, 10 Jan 2019 16:49:41 +0800 Subject: [PATCH 3/4] Fix prelu bug, add some operator, add res_shape in validate_onnx.py --- binaries/dnn_infer.cpp | 4 ++-- dnnlibrary/src/DaqReader.cpp | 4 ++-- dnnlibrary/src/ModelBuilder.cpp | 1 + tools/onnx2daq/OnnxConverter.cpp | 6 +++++- tools/onnx2daq/OnnxConverter.h | 17 ++++++++++++++--- validate_onnx.py | 7 +++++++ 6 files changed, 31 insertions(+), 8 deletions(-) diff --git a/binaries/dnn_infer.cpp b/binaries/dnn_infer.cpp index 506a581..05954e7 100644 --- a/binaries/dnn_infer.cpp +++ b/binaries/dnn_infer.cpp @@ -19,8 +19,8 @@ using std::string; using std::cout; using std::endl; using Clock = std::chrono::high_resolution_clock; -#define WARM_UP 5 -#define RUNS 20 +#define WARM_UP 0 +#define RUNS 1 // ./dnn_save_result daqName outputBlob [input] int main(int argc, char **argv) { diff --git a/dnnlibrary/src/DaqReader.cpp b/dnnlibrary/src/DaqReader.cpp index 3301e38..5c767c9 100644 --- a/dnnlibrary/src/DaqReader.cpp +++ b/dnnlibrary/src/DaqReader.cpp @@ -197,7 +197,7 @@ void AddLayers(const DNN::Model &model, ModelBuilder &builder) { break; } case DNN::LayerType::Mul: { - auto param = layer->add_param(); + auto param = layer->mul_param(); auto input1_name = param->input1()->str(); auto input2_name = param->input2()->str(); auto output_name = param->output()->str(); @@ -206,7 +206,7 @@ void AddLayers(const DNN::Model &model, ModelBuilder &builder) { break; } case DNN::LayerType::MulScalar: { - auto param = layer->add_scalar_param(); + auto param = layer->mul_scalar_param(); auto input1_name = param->input1()->str(); auto input2 = param->input2(); auto output_name = param->output()->str(); diff --git a/dnnlibrary/src/ModelBuilder.cpp b/dnnlibrary/src/ModelBuilder.cpp index 1c6c079..38cbc4d 100644 --- a/dnnlibrary/src/ModelBuilder.cpp +++ b/dnnlibrary/src/ModelBuilder.cpp @@ -298,6 +298,7 @@ ModelBuilder::Index ModelBuilder::AddMulTensor(const string &input1_name, const auto input1 = operand_indexes_[input1_name]; auto input2 = operand_indexes_[input2_name]; IndexSeq input_indexes{input1, input2}; + shaper_.Eltwise(input1_name, output_name); AddOperands(input_indexes, ModelBuilder::ACTIVATION_NONE); auto output_idx = AddOperation(ANEURALNETWORKS_MUL, input_indexes, shaper_[output_name])[0]; AppendOperandIndex(output_name, output_idx); diff --git a/tools/onnx2daq/OnnxConverter.cpp b/tools/onnx2daq/OnnxConverter.cpp index a99439d..8bae1cd 100644 --- a/tools/onnx2daq/OnnxConverter.cpp +++ b/tools/onnx2daq/OnnxConverter.cpp @@ -271,8 +271,12 @@ void OnnxConverter::Convert(const ONNX_NAMESPACE::ModelProto &model_proto, const const auto imm4_name = output_name + "_imm4"; const auto imm5_name = output_name + "_imm5"; const auto imm6_name = output_name + "_imm6"; + if (onnx_tensors_[slope_name].shape != Shape{1}) { + // TODO: support it + throw std::invalid_argument("Only support one element slope."); + } addLayerRelu(input_name, imm1_name); - addLayerMul(input_name, slope_name, imm2_name); + addLayerMul(input_name, onnx_tensors_[slope_name].data[0], imm2_name); addLayerMul(imm2_name, -1.f, imm3_name); addLayerRelu(imm3_name, imm4_name); addLayerMul(imm4_name, -1.f, imm5_name); diff --git a/tools/onnx2daq/OnnxConverter.h b/tools/onnx2daq/OnnxConverter.h index bad6b65..4897bbc 100644 --- a/tools/onnx2daq/OnnxConverter.h +++ b/tools/onnx2daq/OnnxConverter.h @@ -72,7 +72,7 @@ class OnnxConverter { layers_.push_back(layer); } inline void addLayerAdd(css &input1_name, css &input2_name, css &output_name) { - shaper_.Eltwise(input1_name, input2_name, output_name); + shaper_.Eltwise(input1_name, output_name); auto activation = FindActivation(model_proto_, output_name); if (activation.first.has_value()) { skipped_act_.push_back(activation.first.value()); @@ -82,8 +82,19 @@ class OnnxConverter { auto layer = DNN::CreateLayer(builder_, DNN::LayerType::Add, 0, 0, 0, 0, 0, 0, param); layers_.push_back(layer); } + inline void addLayerAdd(css &input1_name, float input2, css &output_name) { + shaper_.Eltwise(input1_name, output_name); + const auto activation = FindActivation(model_proto_, output_name); + if (activation.first.has_value()) { + skipped_act_.push_back(activation.first.value()); + } + const auto param = DNN::CreateAddScalarDirect(builder_, input1_name.c_str(), input2, + ConvertFuseCodeType(activation.second), output_name.c_str()); + const auto layer = DNN::CreateLayer(builder_, DNN::LayerType::AddScalar, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, param, 0); + layers_.push_back(layer); + } inline void addLayerMul(css &input1_name, css &input2_name, css &output_name) { - shaper_.Eltwise(input1_name, input2_name, output_name); + shaper_.Eltwise(input1_name, output_name); const auto activation = FindActivation(model_proto_, output_name); if (activation.first.has_value()) { skipped_act_.push_back(activation.first.value()); @@ -101,7 +112,7 @@ class OnnxConverter { } const auto param = DNN::CreateMulScalarDirect(builder_, input1_name.c_str(), input2, ConvertFuseCodeType(activation.second), output_name.c_str()); - const auto layer = DNN::CreateLayer(builder_, DNN::LayerType::Mul, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, param); + const auto layer = DNN::CreateLayer(builder_, DNN::LayerType::MulScalar, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, param); layers_.push_back(layer); } inline void addLayerGemm(css &input_name, css &weight_name, nonstd::optional bias_name, const int transA, const int transB, const float alpha, const float beta, css &output_name) { diff --git a/validate_onnx.py b/validate_onnx.py index 4e1aa35..961fc6b 100644 --- a/validate_onnx.py +++ b/validate_onnx.py @@ -41,8 +41,13 @@ def run(input_arr, onnx, onnx2daq, dnn_infer, output_name): parser.add_argument('dnn_infer', type=str, help='dnn_infer binary file') parser.add_argument('output', type=str, help='Output name of the model') parser.add_argument('test_data_dir', type=str, help='e.g. test_data_set_0') + parser.add_argument('--res_shape', type=str, help='The shape of result in nhwc, such as [1000] or [1,224,224,3]', default='-1') args = parser.parse_args() + import ast + args.res_shape = ast.literal_eval(args.res_shape) + if type(args.res_shape) == int: + args.res_shape = [args.res_shape] # Load inputs inputs = [] @@ -67,6 +72,8 @@ def run(input_arr, onnx, onnx2daq, dnn_infer, output_name): assert inputs_num == ref_outputs_num for i in range(inputs_num): actual = run(inputs[i], args.onnx, args.onnx2daq, args.dnn_infer, args.output) + if len(args.res_shape) == 4: + actual = np.transpose(actual.reshape(args.res_shape), [0, 3, 1, 2]).flatten() expected = ref_outputs[i].flatten() print('====================') From 7e930fa04cdd559cbf5c2d0fefb01e7b7d513eff Mon Sep 17 00:00:00 2001 From: daquexian Date: Thu, 10 Jan 2019 16:51:42 +0800 Subject: [PATCH 4/4] Fuse two mul scalar in prelu --- tools/onnx2daq/OnnxConverter.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tools/onnx2daq/OnnxConverter.cpp b/tools/onnx2daq/OnnxConverter.cpp index 8bae1cd..381957c 100644 --- a/tools/onnx2daq/OnnxConverter.cpp +++ b/tools/onnx2daq/OnnxConverter.cpp @@ -269,18 +269,15 @@ void OnnxConverter::Convert(const ONNX_NAMESPACE::ModelProto &model_proto, const const auto imm2_name = output_name + "_imm2"; const auto imm3_name = output_name + "_imm3"; const auto imm4_name = output_name + "_imm4"; - const auto imm5_name = output_name + "_imm5"; - const auto imm6_name = output_name + "_imm6"; if (onnx_tensors_[slope_name].shape != Shape{1}) { // TODO: support it throw std::invalid_argument("Only support one element slope."); } addLayerRelu(input_name, imm1_name); - addLayerMul(input_name, onnx_tensors_[slope_name].data[0], imm2_name); - addLayerMul(imm2_name, -1.f, imm3_name); - addLayerRelu(imm3_name, imm4_name); - addLayerMul(imm4_name, -1.f, imm5_name); - addLayerAdd(imm1_name, imm5_name, output_name); + addLayerMul(input_name, -onnx_tensors_[slope_name].data[0], imm2_name); + addLayerRelu(imm2_name, imm3_name); + addLayerMul(imm3_name, -1.f, imm4_name); + addLayerAdd(imm1_name, imm4_name, output_name); // TODO: LOG(INFO) << "Converting PRelu completed"; } else if (op == "Add") {