// Tencent is pleased to support the open source community by making ncnn available. // // Copyright (C) 2021 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 "ir.h" #include "pass_level2.h" #include "pass_level3.h" #include "pass_level4.h" #include "pass_level5.h" #if BUILD_TORCH2PNNX #include "load_torchscript.h" #endif #if BUILD_ONNX2PNNX #include "load_onnx.h" #endif #include "pass_ncnn.h" #include "save_ncnn.h" #if BUILD_PNNX2ONNX #include "save_onnx.h" #endif static std::string get_basename(const std::string& path) { std::string dirpath; std::string filename; size_t dirpos = path.find_last_of("/\\"); if (dirpos != std::string::npos) { dirpath = path.substr(0, dirpos + 1); filename = path.substr(dirpos + 1); } else { filename = path; } std::string base = filename.substr(0, filename.find_last_of('.')); // sanitize - std::replace(base.begin(), base.end(), '-', '_'); return dirpath + base; } static void parse_string_list(char* s, std::vector& list) { list.clear(); char* pch = strtok(s, ","); while (pch != NULL) { list.push_back(std::string(pch)); pch = strtok(NULL, ","); } } static void print_string_list(const std::vector& list) { for (size_t i = 0; i < list.size(); i++) { fprintf(stderr, "%s", list[i].c_str()); if (i + 1 != list.size()) fprintf(stderr, ","); } } static void parse_shape_list(char* s, std::vector >& shapes, std::vector& types) { shapes.clear(); types.clear(); char* pch = strtok(s, "[]"); while (pch != NULL) { // assign user data type if (!types.empty() && (pch[0] == 'b' || pch[0] == 'f' || pch[0] == 'i' || pch[0] == 'u' || pch[0] == 'c')) { char type[32]; int nscan = sscanf(pch, "%31[^,]", type); if (nscan == 1) { types[types.size() - 1] = std::string(type); } } // parse a,b,c int v; int nconsumed = 0; int nscan = sscanf(pch, "%d%n", &v, &nconsumed); if (nscan == 1) { // ok we get shape pch += nconsumed; std::vector s; s.push_back(v); nscan = sscanf(pch, ",%d%n", &v, &nconsumed); while (nscan == 1) { pch += nconsumed; s.push_back(v); nscan = sscanf(pch, ",%d%n", &v, &nconsumed); } // shape end shapes.push_back(s); types.push_back("f32"); } pch = strtok(NULL, "[]"); } } static void print_shape_list(const std::vector >& shapes, const std::vector& types) { for (size_t i = 0; i < shapes.size(); i++) { const std::vector& s = shapes[i]; const std::string& t = types[i]; fprintf(stderr, "["); for (size_t j = 0; j < s.size(); j++) { fprintf(stderr, "%ld", s[j]); if (j != s.size() - 1) fprintf(stderr, ","); } fprintf(stderr, "]"); fprintf(stderr, "%s", t.c_str()); if (i != shapes.size() - 1) fprintf(stderr, ","); } } static bool model_file_maybe_torchscript(const std::string& path) { FILE* fp = fopen(path.c_str(), "rb"); if (!fp) { fprintf(stderr, "open failed %s\n", path.c_str()); return false; } uint32_t signature = 0; fread((char*)&signature, sizeof(signature), 1, fp); fclose(fp); // torchscript is a zip return signature == 0x04034b50; } static void show_usage() { fprintf(stderr, "Usage: pnnx [model.pt] [(key=value)...]\n"); fprintf(stderr, " pnnxparam=model.pnnx.param\n"); fprintf(stderr, " pnnxbin=model.pnnx.bin\n"); fprintf(stderr, " pnnxpy=model_pnnx.py\n"); fprintf(stderr, " pnnxonnx=model.pnnx.onnx\n"); fprintf(stderr, " ncnnparam=model.ncnn.param\n"); fprintf(stderr, " ncnnbin=model.ncnn.bin\n"); fprintf(stderr, " ncnnpy=model_ncnn.py\n"); fprintf(stderr, " fp16=1\n"); fprintf(stderr, " optlevel=2\n"); fprintf(stderr, " device=cpu/gpu\n"); fprintf(stderr, " inputshape=[1,3,224,224],...\n"); fprintf(stderr, " inputshape2=[1,3,320,320],...\n"); #if _WIN32 fprintf(stderr, " customop=C:\\Users\\nihui\\AppData\\Local\\torch_extensions\\torch_extensions\\Cache\\fused\\fused.dll,...\n"); #else fprintf(stderr, " customop=/home/nihui/.cache/torch_extensions/fused/fused.so,...\n"); #endif fprintf(stderr, " moduleop=models.common.Focus,models.yolo.Detect,...\n"); fprintf(stderr, "Sample usage: pnnx mobilenet_v2.pt inputshape=[1,3,224,224]\n"); fprintf(stderr, " pnnx yolov5s.pt inputshape=[1,3,640,640]f32 inputshape2=[1,3,320,320]f32 device=gpu moduleop=models.common.Focus,models.yolo.Detect\n"); } int main(int argc, char** argv) { if (argc < 2) { show_usage(); return -1; } for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { show_usage(); return -1; } } std::string ptpath = std::string(argv[1]); std::string ptbase = get_basename(ptpath); std::string pnnxparampath = ptbase + ".pnnx.param"; std::string pnnxbinpath = ptbase + ".pnnx.bin"; std::string pnnxpypath = ptbase + "_pnnx.py"; std::string pnnxonnxpath = ptbase + ".pnnx.onnx"; std::string ncnnparampath = ptbase + ".ncnn.param"; std::string ncnnbinpath = ptbase + ".ncnn.bin"; std::string ncnnpypath = ptbase + "_ncnn.py"; int fp16 = 1; int optlevel = 2; std::string device = "cpu"; std::vector > input_shapes; std::vector input_types; std::vector > input_shapes2; std::vector input_types2; std::vector customop_modules; std::vector module_operators; for (int i = 2; i < argc; i++) { // key=value char* kv = argv[i]; char* eqs = strchr(kv, '='); if (eqs == NULL) { fprintf(stderr, "unrecognized arg %s\n", kv); continue; } // split k v eqs[0] = '\0'; const char* key = kv; char* value = eqs + 1; if (strcmp(key, "pnnxparam") == 0) pnnxparampath = std::string(value); if (strcmp(key, "pnnxbin") == 0) pnnxbinpath = std::string(value); if (strcmp(key, "pnnxpy") == 0) pnnxpypath = std::string(value); if (strcmp(key, "pnnxonnx") == 0) pnnxonnxpath = std::string(value); if (strcmp(key, "ncnnparam") == 0) ncnnparampath = std::string(value); if (strcmp(key, "ncnnbin") == 0) ncnnbinpath = std::string(value); if (strcmp(key, "ncnnpy") == 0) ncnnpypath = std::string(value); if (strcmp(key, "fp16") == 0) fp16 = atoi(value); if (strcmp(key, "optlevel") == 0) optlevel = atoi(value); if (strcmp(key, "device") == 0) device = value; if (strcmp(key, "inputshape") == 0) parse_shape_list(value, input_shapes, input_types); if (strcmp(key, "inputshape2") == 0) parse_shape_list(value, input_shapes2, input_types2); if (strcmp(key, "customop") == 0) parse_string_list(value, customop_modules); if (strcmp(key, "moduleop") == 0) parse_string_list(value, module_operators); } // print options { fprintf(stderr, "pnnxparam = %s\n", pnnxparampath.c_str()); fprintf(stderr, "pnnxbin = %s\n", pnnxbinpath.c_str()); fprintf(stderr, "pnnxpy = %s\n", pnnxpypath.c_str()); fprintf(stderr, "pnnxonnx = %s\n", pnnxonnxpath.c_str()); fprintf(stderr, "ncnnparam = %s\n", ncnnparampath.c_str()); fprintf(stderr, "ncnnbin = %s\n", ncnnbinpath.c_str()); fprintf(stderr, "ncnnpy = %s\n", ncnnpypath.c_str()); fprintf(stderr, "fp16 = %d\n", fp16); fprintf(stderr, "optlevel = %d\n", optlevel); fprintf(stderr, "device = %s\n", device.c_str()); fprintf(stderr, "inputshape = "); print_shape_list(input_shapes, input_types); fprintf(stderr, "\n"); fprintf(stderr, "inputshape2 = "); print_shape_list(input_shapes2, input_types2); fprintf(stderr, "\n"); fprintf(stderr, "customop = "); print_string_list(customop_modules); fprintf(stderr, "\n"); fprintf(stderr, "moduleop = "); print_string_list(module_operators); fprintf(stderr, "\n"); } std::set foldable_constants; std::string foldable_constants_zippath = ptbase + ".foldable_constants.zip"; pnnx::Graph pnnx_graph; #if BUILD_ONNX2PNNX if (!model_file_maybe_torchscript(ptpath)) { load_onnx(ptpath.c_str(), pnnx_graph, input_shapes, input_types, input_shapes2, input_types2); } else #endif { load_torchscript(ptpath, pnnx_graph, device, input_shapes, input_types, input_shapes2, input_types2, customop_modules, module_operators, foldable_constants_zippath, foldable_constants); } fprintf(stderr, "############# pass_level2\n"); pnnx::pass_level2(pnnx_graph); pnnx_graph.save("debug.param", "debug.bin"); if (optlevel >= 1) { fprintf(stderr, "############# pass_level3\n"); pnnx::pass_level3(pnnx_graph, foldable_constants, foldable_constants_zippath); fprintf(stderr, "############# pass_level4\n"); pnnx::pass_level4(pnnx_graph); } pnnx_graph.save("debug2.param", "debug2.bin"); if (optlevel >= 2) { fprintf(stderr, "############# pass_level5\n"); pnnx::pass_level5(pnnx_graph, foldable_constants, foldable_constants_zippath); } // delete foldable_constants_zippath remove(foldable_constants_zippath.c_str()); pnnx_graph.save(pnnxparampath, pnnxbinpath); pnnx_graph.python(pnnxpypath, pnnxbinpath); #if BUILD_PNNX2ONNX pnnx::save_onnx(pnnx_graph, pnnxonnxpath.c_str(), fp16); #else fprintf(stderr, "pnnx build without onnx-zero support, skip saving onnx\n"); #endif // if (optlevel >= 2) { fprintf(stderr, "############# pass_ncnn\n"); pnnx::pass_ncnn(pnnx_graph, module_operators); pnnx::save_ncnn(pnnx_graph, ncnnparampath, ncnnbinpath, ncnnpypath, fp16); } // pnnx::Graph pnnx_graph2; // pnnx_graph2.load("pnnx.param", "pnnx.bin"); // pnnx_graph2.save("pnnx2.param", "pnnx2.bin"); return 0; }