// 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 class MXNetParam; class MXNetNode { public: bool has_attr(const char* key) const; class AttrProxy { MXNetNode const* _n; const char* const _key; public: AttrProxy( MXNetNode const* n, const char* key ) : _n(n), _key(key) {} operator int() const { return _n->attr_i(_key); } operator float() const { return _n->attr_f(_key); } operator std::string() const { return _n->attr_s(_key); } operator std::vector() const { return _n->attr_ai(_key); } }; AttrProxy attr(const char* key) const { return AttrProxy(this, key); } int attr_i(const char* key) const; float attr_f(const char* key) const; std::string attr_s(const char* key) const; std::vector attr_ai(const char* key) const; public: bool is_weight() const; bool has_weight(int i) const; std::vector weight(int i, int init_len = 0) const; std::vector* nodes;// reference std::vector* params;// reference public: std::string op; std::string name; std::map attrs; std::vector inputs; std::vector weights; }; class MXNetParam { public: std::string name; std::vector data; std::string init; }; bool MXNetNode::has_attr(const char* key) const { const std::map::const_iterator it = attrs.find(key); return it != attrs.end(); } int MXNetNode::attr_i(const char* key) const { const std::map::const_iterator it = attrs.find(key); if (it == attrs.end()) return 0; if (it->second == "False") return 0; if (it->second == "True") return 1; int i = 0; int nscan = sscanf(it->second.c_str(), "%d", &i); if (nscan != 1) return 0; return i; } float MXNetNode::attr_f(const char* key) const { const std::map::const_iterator it = attrs.find(key); if (it == attrs.end()) return 0.f; float f = 0; int nscan = sscanf(it->second.c_str(), "%f", &f); if (nscan != 1) return 0.f; return f; } std::string MXNetNode::attr_s(const char* key) const { const std::map::const_iterator it = attrs.find(key); if (it == attrs.end()) return std::string(); return it->second; } std::vector MXNetNode::attr_ai(const char* key) const { const std::map::const_iterator it = attrs.find(key); if (it == attrs.end()) return std::vector(); // (1,2,3) std::vector list; int i = 0; int c = 0; int nconsumed = 0; int nscan = sscanf(it->second.c_str() + c, "%*[(,]%d%n", &i, &nconsumed); while (nscan == 1) { list.push_back(i); // fprintf(stderr, "%d\n", i); i = 0; c += nconsumed; nscan = sscanf(it->second.c_str() + c, "%*[(,]%d%n", &i, &nconsumed); } return list; } bool MXNetNode::is_weight() const { for (int i=0; i<(int)(*params).size(); i++) { const MXNetParam& p = (*params)[i]; if (p.name == name) return true; } return false; } bool MXNetNode::has_weight(int i) const { if (i < 0 || i >= (int)weights.size()) return false; const std::string& name = (*nodes)[ weights[i] ].name; for (int i=0; i<(int)(*params).size(); i++) { const MXNetParam& p = (*params)[i]; if (p.name == name) return true; } return false; } std::vector MXNetNode::weight(int i, int init_len) const { if (i < 0 || i >= (int)weights.size()) return std::vector(); const std::string& name = (*nodes)[ weights[i] ].name; for (int i=0; i<(int)(*params).size(); i++) { const MXNetParam& p = (*params)[i]; if (p.name != name) continue; if (!p.data.empty()) return p.data; std::vector data; if (!p.init.empty() && init_len != 0) { if (p.init == "[\\$zero\\$, {}]") { data.resize(init_len, 0.f); } else if (p.init == "[\\$one\\$, {}]") { data.resize(init_len, 1.f); } } return data; } return std::vector(); } static void replace_backslash_doublequote_dollar(char* s) { char* a = s; char* b = s+1; while (*a && *b) { if (*a == '\\' && *b == '\"') { *b = '$'; } a++; b++; } } static std::vector parse_input_list(const char* s) { std::vector inputs; if (memcmp(s, "[]", 2) == 0) return inputs; int nscan = 0; int nconsumed = 0; int id; int c = 1;// skip leading [ nscan = sscanf(s + c, "[%d, %*[^]]]%n", &id, &nconsumed); while (nscan == 1) { inputs.push_back(id); // fprintf(stderr, "%d\n", id); c += nconsumed; nscan = sscanf(s + c, "%*[^[][%d, %*[^]]]%n", &id, &nconsumed); } return inputs; } static bool read_mxnet_json(const char* jsonpath, std::vector& nodes) { FILE* fp = fopen(jsonpath, "rb"); if (!fp) { fprintf(stderr, "fopen %s failed\n", jsonpath); return false; } int internal_unknown = 0; char line[1024]; //{ fgets(line, 1024, fp); MXNetNode n; bool in_nodes_list = false; bool in_node_block = false; bool in_attr_block = false; while (!feof(fp)) { char* s = fgets(line, 1024, fp); if (!s) break; if (in_attr_block) { // }, if (memcmp(line, " }", 7) == 0) { in_attr_block = false; continue; } // replace \" with \$ replace_backslash_doublequote_dollar(line); // "kernel": "(7,7)", char key[256] = {0}; char value[256] = {0}; int nscan = sscanf(line, " \"%255[^\"]\": \"%255[^\"]\"", key, value); if (nscan == 2) { n.attrs[key] = value; // fprintf(stderr, "# %s = %s\n", key, value); continue; } } if (in_node_block) { // }, if (memcmp(line, " }", 5) == 0) { // new node if (n.name.empty()) { // assign default unknown name char unknownname[256]; sprintf(unknownname, "unknownncnn_%d", internal_unknown); n.name = unknownname; internal_unknown++; } nodes.push_back(n); in_node_block = false; continue; } int nscan; // "op": "Convolution", char op[256] = {0}; nscan = sscanf(line, " \"op\": \"%255[^\"]\",", op); if (nscan == 1) { n.op = op; // fprintf(stderr, "op = %s\n", op); continue; } // "name": "conv0", char name[256] = {0}; nscan = sscanf(line, " \"name\": \"%255[^\"]\",", name); if (nscan == 1) { n.name = name; // fprintf(stderr, "name = %s\n", name); continue; } // "inputs": [] char inputs[256] = {0}; nscan = sscanf(line, " \"inputs\": %255[^\n]", inputs); if (nscan == 1) { n.inputs = parse_input_list(inputs); // fprintf(stderr, "inputs = %s\n", inputs); continue; } // "param": {}, if (memcmp(line, " \"param\": {}", 17) == 0) { continue; } // replace \" with \$ replace_backslash_doublequote_dollar(line); // "attr": {"__init__": "[\"zero\", {}]"}, char key[256] = {0}; char value[256] = {0}; nscan = sscanf(line, " \"attr\": {\"%255[^\"]\": \"%255[^\"]\"}", key, value); if (nscan == 2) { n.attrs[key] = value; // fprintf(stderr, "# %s = %s\n", key, value); continue; } // "param": {"p": "0.5"}, nscan = sscanf(line, " \"param\": {\"%255[^\"]\": \"%255[^\"]\"}", key, value); if (nscan == 2) { n.attrs[key] = value; // fprintf(stderr, "# %s = %s\n", key, value); continue; } // "attr": { if (memcmp(line, " \"attr\": {", 15) == 0) { in_attr_block = true; continue; } // "param": { if (memcmp(line, " \"param\": {", 16) == 0) { in_attr_block = true; continue; } } if (in_nodes_list) { // ], if (memcmp(line, " ],", 4) == 0) { in_nodes_list = false; // all nodes parsed break; } // { if (memcmp(line, " {", 5) == 0) { n = MXNetNode(); in_node_block = true; continue; } } // "nodes": [ if (memcmp(line, " \"nodes\": [", 12) == 0) { in_nodes_list = true; continue; } } fclose(fp); return true; } static bool read_mxnet_param(const char* parampath, std::vector& params) { FILE* fp = fopen(parampath, "rb"); if (!fp) { fprintf(stderr, "fopen %s failed\n", parampath); return false; } uint64_t header; uint64_t reserved; fread(&header, 1, sizeof(uint64_t), fp); fread(&reserved, 1, sizeof(uint64_t), fp); // NDArray vec // each data uint64_t data_count; fread(&data_count, 1, sizeof(uint64_t), fp); // fprintf(stderr, "data count = %d\n", (int)data_count); for (int i = 0; i < (int)data_count; i++) { uint32_t magic;// 0xF993FAC9 fread(&magic, 1, sizeof(uint32_t), fp); // shape uint32_t ndim; std::vector shape; if (magic == 0xF993FAC9) { int32_t stype; fread(&stype, 1, sizeof(int32_t), fp); fread(&ndim, 1, sizeof(uint32_t), fp); shape.resize(ndim); fread(&shape[0], 1, ndim * sizeof(int64_t), fp); } else if (magic == 0xF993FAC8) { fread(&ndim, 1, sizeof(uint32_t), fp); shape.resize(ndim); fread(&shape[0], 1, ndim * sizeof(int64_t), fp); } else { ndim = magic; shape.resize(ndim); std::vector shape32; shape32.resize(ndim); fread(&shape32[0], 1, ndim * sizeof(uint32_t), fp); for (int j=0; j= 5 ? argv[3] : "ncnn.proto"; const char* ncnn_modelbin = argc >= 5 ? argv[4] : "ncnn.bin"; std::vector nodes; std::vector params; read_mxnet_json(jsonpath, nodes); read_mxnet_param(parampath, params); FILE* pp = fopen(ncnn_prototxt, "wb"); FILE* bp = fopen(ncnn_modelbin, "wb"); // magic fprintf(pp, "7767517\n"); int node_count = nodes.size(); // node reference std::map node_reference; // weight node std::vector weight_nodes; // global definition line // [layer count] [blob count] std::set blob_names; for (int i=0; i weights; std::vector inputs; for (int j=0; j<(int)n.inputs.size(); j++) { int input_index = n.inputs[j]; if (nodes[input_index].is_weight()) { weights.push_back(input_index); continue; } inputs.push_back(input_index); } n.inputs = inputs; n.weights = weights; // input for (int j=0; j<(int)n.inputs.size(); j++) { int input_index = n.inputs[j]; const std::string& input_name = nodes[input_index].name; // fprintf(stderr, "input = %s\n", input_name.c_str()); blob_names.insert(input_name); if (node_reference.find(input_index) == node_reference.end()) { node_reference[input_index] = 1; } else { node_reference[input_index] = node_reference[input_index] + 1; } } // output // fprintf(stderr, "output = %s\n", output_name.c_str()); blob_names.insert(output_name); } // remove node_reference entry with reference equals to one int splitncnn_blob_count = 0; std::map::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 + node_reference.size() - weight_nodes.size(), blob_names.size() + splitncnn_blob_count); int internal_split = 0; for (int i=0; i slope_data = n.weight(0); std::vector bias_data = n.weight(1); int channels = slope_data.size(); std::vector mean_data = n.weight(2, channels); std::vector var_data = n.weight(3, channels); for (int j=0; j<(int)var_data.size(); j++) { var_data[j] += eps; } fprintf(pp, " 0=%d", channels); fwrite(slope_data.data(), sizeof(float), slope_data.size(), bp); fwrite(mean_data.data(), sizeof(float), mean_data.size(), bp); fwrite(var_data.data(), sizeof(float), var_data.size(), bp); fwrite(bias_data.data(), sizeof(float), bias_data.size(), bp); } else if (n.op == "Concat") { int dim = n.attr("dim"); fprintf(pp, " 0=%d", dim-1); } else if (n.op == "Convolution") { int num_filter = n.attr("num_filter"); std::vector kernel = n.attr("kernel"); std::vector dilate = n.attr("dilate"); std::vector stride = n.attr("stride"); std::vector pad = n.attr("pad"); int no_bias = n.attr("no_bias"); int num_group = n.attr("num_group");//TODO depthwise std::vector weight_data = n.weight(0); std::vector bias_data = n.weight(1); fprintf(pp, " 0=%d", num_filter); if (!kernel.empty()) fprintf(pp, " 1=%d", kernel[0]); if (!dilate.empty()) fprintf(pp, " 2=%d", dilate[0]); if (!stride.empty()) fprintf(pp, " 3=%d", stride[0]); if (!pad.empty()) fprintf(pp, " 4=%d", pad[0]); fprintf(pp, " 5=%d", no_bias == 1 ? 0 : 1); fprintf(pp, " 6=%d", weight_data.size()); int quantize_tag = 0; fwrite(&quantize_tag, sizeof(int), 1, bp); fwrite(weight_data.data(), sizeof(float), weight_data.size(), bp); fwrite(bias_data.data(), sizeof(float), bias_data.size(), bp); } else if (n.op == "Dropout") { // float p = n.attr("p"); // fprintf(pp, " 0=%d", p); } else if (n.op == "elemwise_add") { int op_type = 1; fprintf(pp, " 0=%d", op_type); } else if (n.op == "Flatten") { } else if (n.op == "FullyConnected") { int num_hidden = n.attr("num_hidden"); int no_bias = n.attr("no_bias"); int flatten = n.attr("flatten"); // TODO flatten std::vector weight_data = n.weight(0); std::vector bias_data = n.weight(1); fprintf(pp, " 0=%d", num_hidden); fprintf(pp, " 1=%d", no_bias == 1 ? 0 : 1); fprintf(pp, " 2=%d", weight_data.size()); int quantize_tag = 0; fwrite(&quantize_tag, sizeof(int), 1, bp); fwrite(weight_data.data(), sizeof(float), weight_data.size(), bp); fwrite(bias_data.data(), sizeof(float), bias_data.size(), bp); } else if (n.op == "LeakyReLU") { std::string type = n.attr("act_type"); if (type == "elu") { } else if (type == "leaky") { } else if (type == "prelu") { std::vector weight_data = n.weight(0); fprintf(pp, " 0=%d", weight_data.size()); fwrite(weight_data.data(), sizeof(float), weight_data.size(), bp); } } else if (n.op == "Pooling") { std::string pool_type = n.attr("pool_type"); std::vector kernel = n.attr("kernel"); std::vector stride = n.attr("stride"); std::vector pad = n.attr("pad"); std::string pooling_convention = n.attr("pooling_convention"); int global_pool = n.attr("global_pool"); int pool = 0; if (pool_type == "max") { pool = 0; } else if (pool_type == "avg") { pool = 1; } if (pooling_convention == "valid") { // TODO valid and full mode } fprintf(pp, " 0=%d", pool); if (!kernel.empty()) fprintf(pp, " 1=%d", kernel[0]); if (!stride.empty()) fprintf(pp, " 2=%d", stride[0]); if (!pad.empty()) fprintf(pp, " 3=%d", pad[0]); fprintf(pp, " 4=%d", global_pool); } else if (n.op == "SoftmaxOutput") { } else { // TODO op specific params std::map::const_iterator it = n.attrs.begin(); for (; it != n.attrs.end(); it++) { fprintf(stderr, "# %s=%s\n", it->first.c_str(), it->second.c_str()); // fprintf(pp, " %s=%s", it->first.c_str(), it->second.c_str()); } } fprintf(pp, "\n"); if (node_reference.find(i) != node_reference.end()) { int refcount = node_reference[i]; if (refcount > 1) { std::string output_name = n.name; char splitname[256]; sprintf(splitname, "splitncnn_%d", internal_split); fprintf(pp, "%-16s %-32s %d %d", "Split", splitname, 1, refcount); fprintf(pp, " %s", output_name.c_str()); for (int j=0; j