// Tencent is pleased to support the open source community by making ncnn available. // // Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. // // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // https://opensource.org/licenses/BSD-3-Clause // // 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 #include #include #include #include #include #include #include #include #include #include #include #include "onnx.pb.h" static bool read_proto_from_binary(const char* filepath, google::protobuf::Message* message) { std::ifstream fs(filepath, std::ifstream::in | std::ifstream::binary); if (!fs.is_open()) { fprintf(stderr, "open failed %s\n", filepath); return false; } google::protobuf::io::IstreamInputStream input(&fs); google::protobuf::io::CodedInputStream codedstr(&input); codedstr.SetTotalBytesLimit(INT_MAX, INT_MAX / 2); bool success = message->ParseFromCodedStream(&codedstr); fs.close(); return success; } static std::vector get_node_attr_ai(const onnx::NodeProto& node, const char* key) { std::vector v; for (int i=0; i get_node_attr_af(const onnx::NodeProto& node, const char* key) { std::vector v; for (int i=0; i= 4 ? argv[2] : "ncnn.param"; const char* ncnn_modelbin = argc >= 4 ? argv[3] : "ncnn.bin"; onnx::ModelProto model; // load bool s1 = read_proto_from_binary(onnxpb, &model); if (!s1) { fprintf(stderr, "read_proto_from_binary failed\n"); return -1; } FILE* pp = fopen(ncnn_prototxt, "wb"); FILE* bp = fopen(ncnn_modelbin, "wb"); // magic fprintf(pp, "7767517\n"); const onnx::GraphProto& graph = model.graph(); onnx::GraphProto* mutable_graph = model.mutable_graph(); int node_count = graph.node_size(); // node reference std::map node_reference; // weight node and weight reshape node std::map weights; // weight node before BinaryOp std::map binaryop_weights; for (int j=0; j blob_names; for (int i=0; i::iterator it = weights.find(input_name); if (it != weights.end()) { // binary op with weight, insert MemoryData layer and const blob binaryop_weights[input_name] = it->second; weights.erase(it); } } } } for (int j=0; j<(int)node.input_size(); j++) { const std::string& input_name = node.input(j); // check weight if (weights.find(input_name) != weights.end()) { continue; } blob_names.insert(input_name); if (node_reference.find(input_name) == node_reference.end()) { node_reference[input_name] = 1; } else { node_reference[input_name] = node_reference[input_name] + 1; } } if (op == "Dropout") { const std::string& output_name = node.output(0); blob_names.insert(output_name); continue; } for (int j=0; j<(int)node.output_size(); j++) { const std::string& output_name = node.output(j); blob_names.insert(output_name); } } // include Input node int input_node_count = 0; for (int j=0; jmutable_node(i); // MatMul <= Transpose(weight) - MatMul if (node->op_type() == "Transpose") { // check weight if (weights.find(node->input(0)) == weights.end()) continue; onnx::TensorProto& B = weights[node->input(0)]; if (B.dims_size() != 2) continue; if (node_reference[node->output(0)] != 1) continue; // perm = (1, 0) std::vector perm = get_node_attr_ai(*node, "perm"); if (perm.size() != 2) continue; if (perm[0] != 1 || perm[1] != 0) continue; if (i+1 >= node_count) continue; onnx::NodeProto* node2 = mutable_graph->mutable_node(i+1); if (node2->op_type() != "MatMul") continue; // reduce node->set_op_type("noop_reducedncnn"); node_reference.erase(node_reference.find(node->output(0))); blob_names.erase(node->output(0)); node2->set_input(1, node->input(0)); // permute weight { const int h = B.dims(0); const int w = B.dims(1); std::vector permuted_data; permuted_data.reserve(h * w); const float* bptr = B.has_raw_data() ? (const float*)B.raw_data().data() : B.float_data().data(); for (int j=0; j::iterator it = node_reference.begin(); while (it != node_reference.end()) { if (it->second == 1) { node_reference.erase(it++); } else { splitncnn_blob_count += it->second; // fprintf(stderr, "%s %d\n", it->first.c_str(), it->second); ++it; } } fprintf(pp, "%lu %lu\n", node_count - reduced_node_count + input_node_count + node_reference.size() + graph.initializer_size() - weights.size(), blob_names.size() + splitncnn_blob_count); int internal_split = 0; // place Input at the beginning for (int j=0; j 1) { fprintf(pp, "%-16s", "ConvolutionDepthWise"); } else { fprintf(pp, "%-16s", "Convolution"); } } else if (op == "ConvTranspose") { int group = get_node_attr_i(node, "group", 1); if (group > 1) { fprintf(pp, "%-16s", "DeconvolutionDepthWise"); } else { fprintf(pp, "%-16s", "Deconvolution"); } } else if (op == "Cos") { fprintf(pp, "%-16s", "UnaryOp"); } else if (op == "Div") { fprintf(pp, "%-16s", "BinaryOp"); } else if (op == "Dropout") { fprintf(pp, "%-16s", "Dropout"); output_size = 1; } else if (op == "Elu") { fprintf(pp, "%-16s", "ELU"); } else if (op == "Exp") { fprintf(pp, "%-16s", "UnaryOp"); } else if (op == "Flatten") { fprintf(pp, "%-16s", "Flatten"); } else if (op == "Floor") { fprintf(pp, "%-16s", "UnaryOp"); } else if (op == "Gemm") { float alpha = get_node_attr_f(node, "alpha", 1.f); float beta = get_node_attr_f(node, "beta", 1.f); int transA = get_node_attr_i(node, "transA", 0); int transB = get_node_attr_i(node, "transB", 0); if (alpha == 1.f && beta == 1.f) { // InnerProduct-like A * B + C if (transA == 0 && transB == 1) { fprintf(pp, "%-16s", "InnerProduct"); } } // TODO } else if (op == "GlobalAveragePool") { fprintf(pp, "%-16s", "Pooling"); } else if (op == "GlobalMaxPool") { fprintf(pp, "%-16s", "Pooling"); } else if (op == "ImageScaler") { fprintf(pp, "%-16s", "Scale"); } else if (op == "InstanceNormalization") { fprintf(pp, "%-16s", "InstanceNorm"); } else if (op == "LeakyRelu") { fprintf(pp, "%-16s", "ReLU"); } else if (op == "Log") { fprintf(pp, "%-16s", "UnaryOp"); } else if (op == "LRN") { fprintf(pp, "%-16s", "LRN"); } else if (op == "MatMul") { fprintf(pp, "%-16s", "InnerProduct"); } else if (op == "Max") { fprintf(pp, "%-16s", "BinaryOp"); } else if (op == "Min") { fprintf(pp, "%-16s", "BinaryOp"); } else if (op == "Mul") { fprintf(pp, "%-16s", "BinaryOp"); } else if (op == "Neg") { fprintf(pp, "%-16s", "UnaryOp"); } else if (op == "Pad") { fprintf(pp, "%-16s", "Padding"); } else if (op == "Pow") { fprintf(pp, "%-16s", "BinaryOp"); } else if (op == "PRelu") { fprintf(pp, "%-16s", "PReLU"); } else if (op == "Reciprocal") { fprintf(pp, "%-16s", "UnaryOp"); } else if (op == "Relu") { fprintf(pp, "%-16s", "ReLU"); } else if (op == "Reshape") { if (node.input_size() == 1 || node.input_size() == 2) { const std::string& input_name = node.input(0); // skip weight reshape if (weights.find(input_name) != weights.end()) { continue; } } fprintf(pp, "%-16s", "Reshape"); } else if (op == "Sigmoid") { fprintf(pp, "%-16s", "Sigmoid"); } else if (op == "Sin") { fprintf(pp, "%-16s", "UnaryOp"); } else if (op == "Slice") { fprintf(pp, "%-16s", "Crop"); } else if (op == "Softmax") { fprintf(pp, "%-16s", "Softmax"); } else if (op == "Sqrt") { fprintf(pp, "%-16s", "UnaryOp"); } else if (op == "Sub") { fprintf(pp, "%-16s", "BinaryOp"); } else if (op == "Sum") { fprintf(pp, "%-16s", "Eltwise"); } else if (op == "Tan") { fprintf(pp, "%-16s", "UnaryOp"); } else if (op == "Transpose") { fprintf(pp, "%-16s", "Permute"); } else if (op == "Upsample") { fprintf(pp, "%-16s", "Interp"); } else { // TODO fprintf(stderr, "%s not supported yet!\n", op.c_str()); fprintf(pp, "%-16s", op.c_str()); } fprintf(pp, " %-24s %d %d", name.c_str(), input_size, output_size); for (int j=0; j kernel_shape = get_node_attr_ai(node, "kernel_shape"); std::vector strides = get_node_attr_ai(node, "strides"); std::vector pads = get_node_attr_ai(node, "pads"); int pool = op == "AveragePool" ? 1 : 0; int pad_mode = 1; if (auto_pad == "SAME_LOWER" || auto_pad == "SAME_UPPER") { // TODO pad_mode = 2; } fprintf(pp, " 0=%d", pool); if (kernel_shape.size() == 1) { fprintf(pp, " 1=%d", kernel_shape[0]); } else if (kernel_shape.size() == 2) { fprintf(pp, " 1=%d", kernel_shape[1]); fprintf(pp, " 11=%d", kernel_shape[0]); } if (strides.size() == 1) { fprintf(pp, " 2=%d", strides[0]); } else if (strides.size() == 2) { fprintf(pp, " 2=%d", strides[1]); fprintf(pp, " 12=%d", strides[0]); } if (pads.size() == 1) { fprintf(pp, " 3=%d", pads[0]); } else if (pads.size() == 2) { fprintf(pp, " 3=%d", pads[1]); fprintf(pp, " 13=%d", pads[0]); } else if (pads.size() == 4) { fprintf(pp, " 3=%d", pads[1]); fprintf(pp, " 13=%d", pads[0]); fprintf(pp, " 14=%d", pads[3]); fprintf(pp, " 15=%d", pads[2]); } fprintf(pp, " 5=%d", pad_mode); } else if (op == "BatchNormalization") { float epsilon = get_node_attr_f(node, "epsilon", 1e-5f); const onnx::TensorProto& scale = weights[node.input(1)]; const onnx::TensorProto& B = weights[node.input(2)]; const onnx::TensorProto& mean = weights[node.input(3)]; const onnx::TensorProto& var = weights[node.input(4)]; int channels = get_tensor_proto_data_size(scale); fprintf(pp, " 0=%d", channels); fwrite_tensor_proto_data(scale, bp); fwrite_tensor_proto_data(mean, bp); // apply epsilon to var { const float* v = var.has_raw_data() ? (const float*)var.raw_data().data() : var.float_data().data(); for (int j=0; j kernel_shape = get_node_attr_ai(node, "kernel_shape"); std::vector dilations = get_node_attr_ai(node, "dilations"); std::vector strides = get_node_attr_ai(node, "strides"); std::vector pads = get_node_attr_ai(node, "pads"); int group = get_node_attr_i(node, "group", 1); fprintf(pp, " 0=%d", num_filter); if (kernel_shape.size() == 1) { fprintf(pp, " 1=%d", kernel_shape[0]); } else if (kernel_shape.size() == 2) { fprintf(pp, " 1=%d", kernel_shape[1]); fprintf(pp, " 11=%d", kernel_shape[0]); } if (dilations.size() == 1) { fprintf(pp, " 2=%d", dilations[0]); } else if (dilations.size() == 2) { fprintf(pp, " 2=%d", dilations[1]); fprintf(pp, " 12=%d", dilations[0]); } if (strides.size() == 1) { fprintf(pp, " 3=%d", strides[0]); } else if (strides.size() == 2) { fprintf(pp, " 3=%d", strides[1]); fprintf(pp, " 13=%d", strides[0]); } if (auto_pad == "SAME_LOWER" || auto_pad == "SAME_UPPER") { // TODO fprintf(pp, " 4=-233"); } else { if (pads.size() == 1) { fprintf(pp, " 4=%d", pads[0]); } else if (pads.size() == 2) { fprintf(pp, " 4=%d", pads[1]); fprintf(pp, " 14=%d", pads[0]); } else if (pads.size() == 4) { fprintf(pp, " 4=%d", pads[1]); fprintf(pp, " 14=%d", pads[0]); // TODO hpad2=pads[2] wpad2=pads[3] } } fprintf(pp, " 5=%d", has_bias); fprintf(pp, " 6=%d", get_tensor_proto_data_size(W)); if (group > 1) { fprintf(pp, " 7=%d", group); } int quantize_tag = 0; fwrite(&quantize_tag, sizeof(int), 1, bp); fwrite_tensor_proto_data(W, bp); if (has_bias) { const onnx::TensorProto& B = weights[node.input(2)]; fwrite_tensor_proto_data(B, bp); } } else if (op == "ConvTranspose") { const onnx::TensorProto& W = weights[node.input(1)]; int num_filter = W.dims(1); int has_bias = node.input_size() == 3 ? 1 : 0; std::string auto_pad = get_node_attr_s(node, "auto_pad");//TODO std::vector kernel_shape = get_node_attr_ai(node, "kernel_shape"); std::vector dilations = get_node_attr_ai(node, "dilations"); std::vector strides = get_node_attr_ai(node, "strides"); std::vector output_padding = get_node_attr_ai(node, "output_padding");//TODO implement adj std::vector output_shape = get_node_attr_ai(node, "output_shape");//TODO std::vector pads = get_node_attr_ai(node, "pads"); int group = get_node_attr_i(node, "group", 1); fprintf(pp, " 0=%d", num_filter); if (kernel_shape.size() == 1) { fprintf(pp, " 1=%d", kernel_shape[0]); } else if (kernel_shape.size() == 2) { fprintf(pp, " 1=%d", kernel_shape[1]); fprintf(pp, " 11=%d", kernel_shape[0]); } if (dilations.size() == 1) { fprintf(pp, " 2=%d", dilations[0]); } else if (dilations.size() == 2) { fprintf(pp, " 2=%d", dilations[1]); fprintf(pp, " 12=%d", dilations[0]); } if (strides.size() == 1) { fprintf(pp, " 3=%d", strides[0]); } else if (strides.size() == 2) { fprintf(pp, " 3=%d", strides[1]); fprintf(pp, " 13=%d", strides[0]); } if (auto_pad == "SAME_LOWER" || auto_pad == "SAME_UPPER") { // TODO fprintf(pp, " 4=-233"); } else { if (pads.size() == 1) { fprintf(pp, " 4=%d", pads[0]); } else if (pads.size() == 2) { fprintf(pp, " 4=%d", pads[1]); fprintf(pp, " 14=%d", pads[0]); } else if (pads.size() == 4) { fprintf(pp, " 4=%d", pads[1]); fprintf(pp, " 14=%d", pads[0]); // TODO hpad2=pads[2] wpad2=pads[3] } } fprintf(pp, " 5=%d", has_bias); fprintf(pp, " 6=%d", get_tensor_proto_data_size(W)); if (group > 1) { fprintf(pp, " 7=%d", group); } int quantize_tag = 0; fwrite(&quantize_tag, sizeof(int), 1, bp); int maxk = 0; if (kernel_shape.size() == 2) { maxk = kernel_shape[1] * kernel_shape[0]; } else { maxk = kernel_shape[0] * kernel_shape[0]; } int weight_data_size = get_tensor_proto_data_size(W); const float* weight_data = 0; if (W.has_raw_data()) { weight_data = (const float*)W.raw_data().data(); } else if (W.data_type() == 1) { weight_data = W.float_data().data(); } for (int g=0; g bias = get_node_attr_af(node, "bias"); float scale = get_node_attr_f(node, "scale", 1.f); int channels = bias.size(); fprintf(pp, " 0=%d", channels); fprintf(pp, " 1=1"); for (int j=0; j pads = get_node_attr_ai(node, "pads"); float value = get_node_attr_f(node, "value", 0.f); int type = 0; if (mode == "constant") { type = 0; } else if (mode == "edge") { type = 1; } else if (mode == "reflect") { // FIXME } int top = pads[0]; int bottom = pads[2]; int left = pads[1]; int right = pads[3]; fprintf(pp, " 0=%d", top); fprintf(pp, " 1=%d", bottom); fprintf(pp, " 2=%d", left); fprintf(pp, " 3=%d", right); fprintf(pp, " 4=%d", type); fprintf(pp, " 5=%f", value); } else if (op == "Pow") { int op_type = 6; fprintf(pp, " 0=%d", op_type); } else if (op == "PRelu") { const onnx::TensorProto& slope = weights[node.input(1)]; int num_slope = get_tensor_proto_data_size(slope); fprintf(pp, " 0=%d", num_slope); fwrite_tensor_proto_data(slope, bp); } else if (op == "Reciprocal") { int op_type = 15; fprintf(pp, " 0=%d", op_type); } else if (op == "Reshape") { std::vector shape; if (node.input_size() == 1) { shape = get_node_attr_ai(node, "shape"); } else { const onnx::TensorProto& shape_tp = weights[node.input(1)]; const int64_t* shape_data = shape_tp.int64_data().data(); for (int j=0; j starts = get_node_attr_ai(node, "starts"); std::vector ends = get_node_attr_ai(node, "ends"); std::vector steps = get_node_attr_ai(node, "steps");// TODO // assert step == 1 for (int i=0; i<(int)steps.size(); i++) { if (steps[i] != 1) fprintf(stderr, "Unsupported slice step !\n"); } int woffset = 0; int hoffset = 0; int coffset = 0; int outw = -233; int outh = -233; int outc = -233; if (starts.size() == 2) { woffset = starts[1]; outw = ends[1] == -1 ? -234 : ends[1] - starts[1]; } else if (starts.size() == 3) { woffset = starts[2]; hoffset = starts[1]; outw = ends[2] == -1 ? -234 : ends[2] - starts[2]; outh = ends[1] == -1 ? -234 : ends[1] - starts[1]; } else if (starts.size() == 4) { woffset = starts[3]; hoffset = starts[2]; coffset = starts[1]; outw = ends[3] == -1 ? -234 : ends[3] - starts[3]; outh = ends[2] == -1 ? -234 : ends[2] - starts[2]; outc = ends[1] == -1 ? -234 : ends[1] - starts[1]; } fprintf(pp, " 0=%d", woffset); fprintf(pp, " 1=%d", hoffset); fprintf(pp, " 2=%d", coffset); fprintf(pp, " 3=%d", outw); fprintf(pp, " 4=%d", outh); fprintf(pp, " 5=%d", outc); } else if (op == "Softmax") { int axis = get_node_attr_i(node, "axis", 1); fprintf(pp, " 0=%d", axis-1); fprintf(pp, " 1=1"); } else if (op == "Sqrt") { int op_type = 5; fprintf(pp, " 0=%d", op_type); } else if (op == "Sub") { int op_type = 1; fprintf(pp, " 0=%d", op_type); } else if (op == "Sum") { int op_type = 1; fprintf(pp, " 0=%d", op_type); } else if (op == "Tan") { int op_type = 11; fprintf(pp, " 0=%d", op_type); } else if (op == "Transpose") { std::vector perm = get_node_attr_ai(node, "perm"); if (perm.size() == 4) { if (perm[1] == 1 && perm[2] == 2 && perm[3] == 3) fprintf(pp, " 0=0");// w h c else if (perm[1] == 1 && perm[2] == 3 && perm[3] == 2) fprintf(pp, " 0=1");// h w c else if (perm[1] == 2 && perm[2] == 1 && perm[3] == 3) fprintf(pp, " 0=2");// w c h else if (perm[1] == 2 && perm[2] == 3 && perm[3] == 1) fprintf(pp, " 0=3");// c w h else if (perm[1] == 3 && perm[2] == 1 && perm[3] == 2) fprintf(pp, " 0=4");// h c w else if (perm[1] == 3 && perm[2] == 2 && perm[3] == 1) fprintf(pp, " 0=5");// c h w } else if (perm.size() == 5) { if (perm[1] == 1 && perm[2] == 2 && perm[3] == 3 && perm[4] == 4) fprintf(pp, " 0=0");// wx h c else if (perm[1] == 1 && perm[2] == 3 && perm[3] == 4 && perm[4] == 2) fprintf(pp, " 0=1");// h wx c else if (perm[1] == 2 && perm[2] == 1 && perm[3] == 3 && perm[4] == 4) fprintf(pp, " 0=2");// wx c h else if (perm[1] == 2 && perm[2] == 3 && perm[3] == 4 && perm[4] == 1) fprintf(pp, " 0=3");// c wx h else if (perm[1] == 3 && perm[2] == 4 && perm[3] == 1 && perm[4] == 2) fprintf(pp, " 0=4");// h c wx else if (perm[1] == 3 && perm[2] == 4 && perm[3] == 2 && perm[4] == 1) fprintf(pp, " 0=5");// c h wx else fprintf(stderr, "Unsupported transpose type !\n"); } } else if (op == "Upsample") { std::string mode = get_node_attr_s(node, "mode"); std::vector scales; if (node.input_size() == 1) { scales = get_node_attr_af(node, "scales"); } else { const onnx::TensorProto& scales_tp = weights[node.input(1)]; const float* shape_data = scales_tp.has_raw_data() ? (const float*)scales_tp.raw_data().data() : scales_tp.float_data().data(); int float_data_size = scales_tp.float_data_size(); //float data is None, use raw data instead if (float_data_size == 0) { float_data_size = scales_tp.dims().Get(0); } for (int j=0; j 1) { char splitname[256]; sprintf(splitname, "splitncnn_%d", internal_split); fprintf(pp, "%-16s %-24s %d %d", "Split", splitname, 1, refcount); fprintf(pp, " %s", output_name.c_str()); for (int k=0; k