You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.cpp 12 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. // Tencent is pleased to support the open source community by making ncnn available.
  2. //
  3. // Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
  4. //
  5. // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
  6. // in compliance with the License. You may obtain a copy of the License at
  7. //
  8. // https://opensource.org/licenses/BSD-3-Clause
  9. //
  10. // Unless required by applicable law or agreed to in writing, software distributed
  11. // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  12. // CONDITIONS OF ANY KIND, either express or implied. See the License for the
  13. // specific language governing permissions and limitations under the License.
  14. #include <stdio.h>
  15. #include <stdint.h>
  16. #include <string.h>
  17. #include <algorithm>
  18. #include <string>
  19. #include <vector>
  20. #include "ir.h"
  21. #include "pass_level2.h"
  22. #include "pass_level3.h"
  23. #include "pass_level4.h"
  24. #include "pass_level5.h"
  25. #if BUILD_TORCH2PNNX
  26. #include "load_torchscript.h"
  27. #endif
  28. #if BUILD_ONNX2PNNX
  29. #include "load_onnx.h"
  30. #endif
  31. #include "pass_ncnn.h"
  32. #include "save_ncnn.h"
  33. #if BUILD_PNNX2ONNX
  34. #include "save_onnx.h"
  35. #endif
  36. static std::string get_basename(const std::string& path)
  37. {
  38. std::string dirpath;
  39. std::string filename;
  40. size_t dirpos = path.find_last_of("/\\");
  41. if (dirpos != std::string::npos)
  42. {
  43. dirpath = path.substr(0, dirpos + 1);
  44. filename = path.substr(dirpos + 1);
  45. }
  46. else
  47. {
  48. filename = path;
  49. }
  50. std::string base = filename.substr(0, filename.find_last_of('.'));
  51. // sanitize -
  52. std::replace(base.begin(), base.end(), '-', '_');
  53. return dirpath + base;
  54. }
  55. static void parse_string_list(char* s, std::vector<std::string>& list)
  56. {
  57. list.clear();
  58. char* pch = strtok(s, ",");
  59. while (pch != NULL)
  60. {
  61. list.push_back(std::string(pch));
  62. pch = strtok(NULL, ",");
  63. }
  64. }
  65. static void print_string_list(const std::vector<std::string>& list)
  66. {
  67. for (size_t i = 0; i < list.size(); i++)
  68. {
  69. fprintf(stderr, "%s", list[i].c_str());
  70. if (i + 1 != list.size())
  71. fprintf(stderr, ",");
  72. }
  73. }
  74. static void parse_shape_list(char* s, std::vector<std::vector<int64_t> >& shapes, std::vector<std::string>& types)
  75. {
  76. shapes.clear();
  77. types.clear();
  78. char* pch = strtok(s, "[]");
  79. while (pch != NULL)
  80. {
  81. // assign user data type
  82. if (!types.empty() && (pch[0] == 'b' || pch[0] == 'f' || pch[0] == 'i' || pch[0] == 'u' || pch[0] == 'c'))
  83. {
  84. char type[32];
  85. int nscan = sscanf(pch, "%31[^,]", type);
  86. if (nscan == 1)
  87. {
  88. types[types.size() - 1] = std::string(type);
  89. }
  90. }
  91. // parse a,b,c
  92. int v;
  93. int nconsumed = 0;
  94. int nscan = sscanf(pch, "%d%n", &v, &nconsumed);
  95. if (nscan == 1)
  96. {
  97. // ok we get shape
  98. pch += nconsumed;
  99. std::vector<int64_t> s;
  100. s.push_back(v);
  101. nscan = sscanf(pch, ",%d%n", &v, &nconsumed);
  102. while (nscan == 1)
  103. {
  104. pch += nconsumed;
  105. s.push_back(v);
  106. nscan = sscanf(pch, ",%d%n", &v, &nconsumed);
  107. }
  108. // shape end
  109. shapes.push_back(s);
  110. types.push_back("f32");
  111. }
  112. pch = strtok(NULL, "[]");
  113. }
  114. }
  115. static void print_shape_list(const std::vector<std::vector<int64_t> >& shapes, const std::vector<std::string>& types)
  116. {
  117. for (size_t i = 0; i < shapes.size(); i++)
  118. {
  119. const std::vector<int64_t>& s = shapes[i];
  120. const std::string& t = types[i];
  121. fprintf(stderr, "[");
  122. for (size_t j = 0; j < s.size(); j++)
  123. {
  124. fprintf(stderr, "%ld", s[j]);
  125. if (j != s.size() - 1)
  126. fprintf(stderr, ",");
  127. }
  128. fprintf(stderr, "]");
  129. fprintf(stderr, "%s", t.c_str());
  130. if (i != shapes.size() - 1)
  131. fprintf(stderr, ",");
  132. }
  133. }
  134. static bool model_file_maybe_torchscript(const std::string& path)
  135. {
  136. FILE* fp = fopen(path.c_str(), "rb");
  137. if (!fp)
  138. {
  139. fprintf(stderr, "open failed %s\n", path.c_str());
  140. return false;
  141. }
  142. uint32_t signature = 0;
  143. fread((char*)&signature, sizeof(signature), 1, fp);
  144. fclose(fp);
  145. // torchscript is a zip
  146. return signature == 0x04034b50;
  147. }
  148. static void show_usage()
  149. {
  150. fprintf(stderr, "Usage: pnnx [model.pt] [(key=value)...]\n");
  151. fprintf(stderr, " pnnxparam=model.pnnx.param\n");
  152. fprintf(stderr, " pnnxbin=model.pnnx.bin\n");
  153. fprintf(stderr, " pnnxpy=model_pnnx.py\n");
  154. fprintf(stderr, " pnnxonnx=model.pnnx.onnx\n");
  155. fprintf(stderr, " ncnnparam=model.ncnn.param\n");
  156. fprintf(stderr, " ncnnbin=model.ncnn.bin\n");
  157. fprintf(stderr, " ncnnpy=model_ncnn.py\n");
  158. fprintf(stderr, " fp16=1\n");
  159. fprintf(stderr, " optlevel=2\n");
  160. fprintf(stderr, " device=cpu/gpu\n");
  161. fprintf(stderr, " inputshape=[1,3,224,224],...\n");
  162. fprintf(stderr, " inputshape2=[1,3,320,320],...\n");
  163. #if _WIN32
  164. fprintf(stderr, " customop=C:\\Users\\nihui\\AppData\\Local\\torch_extensions\\torch_extensions\\Cache\\fused\\fused.dll,...\n");
  165. #else
  166. fprintf(stderr, " customop=/home/nihui/.cache/torch_extensions/fused/fused.so,...\n");
  167. #endif
  168. fprintf(stderr, " moduleop=models.common.Focus,models.yolo.Detect,...\n");
  169. fprintf(stderr, "Sample usage: pnnx mobilenet_v2.pt inputshape=[1,3,224,224]\n");
  170. 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");
  171. }
  172. int main(int argc, char** argv)
  173. {
  174. if (argc < 2)
  175. {
  176. show_usage();
  177. return -1;
  178. }
  179. for (int i = 1; i < argc; i++)
  180. {
  181. if (argv[i][0] == '-')
  182. {
  183. show_usage();
  184. return -1;
  185. }
  186. }
  187. std::string ptpath = std::string(argv[1]);
  188. std::string ptbase = get_basename(ptpath);
  189. std::string pnnxparampath = ptbase + ".pnnx.param";
  190. std::string pnnxbinpath = ptbase + ".pnnx.bin";
  191. std::string pnnxpypath = ptbase + "_pnnx.py";
  192. std::string pnnxonnxpath = ptbase + ".pnnx.onnx";
  193. std::string ncnnparampath = ptbase + ".ncnn.param";
  194. std::string ncnnbinpath = ptbase + ".ncnn.bin";
  195. std::string ncnnpypath = ptbase + "_ncnn.py";
  196. int fp16 = 1;
  197. int optlevel = 2;
  198. std::string device = "cpu";
  199. std::vector<std::vector<int64_t> > input_shapes;
  200. std::vector<std::string> input_types;
  201. std::vector<std::vector<int64_t> > input_shapes2;
  202. std::vector<std::string> input_types2;
  203. std::vector<std::string> customop_modules;
  204. std::vector<std::string> module_operators;
  205. for (int i = 2; i < argc; i++)
  206. {
  207. // key=value
  208. char* kv = argv[i];
  209. char* eqs = strchr(kv, '=');
  210. if (eqs == NULL)
  211. {
  212. fprintf(stderr, "unrecognized arg %s\n", kv);
  213. continue;
  214. }
  215. // split k v
  216. eqs[0] = '\0';
  217. const char* key = kv;
  218. char* value = eqs + 1;
  219. if (strcmp(key, "pnnxparam") == 0)
  220. pnnxparampath = std::string(value);
  221. if (strcmp(key, "pnnxbin") == 0)
  222. pnnxbinpath = std::string(value);
  223. if (strcmp(key, "pnnxpy") == 0)
  224. pnnxpypath = std::string(value);
  225. if (strcmp(key, "pnnxonnx") == 0)
  226. pnnxonnxpath = std::string(value);
  227. if (strcmp(key, "ncnnparam") == 0)
  228. ncnnparampath = std::string(value);
  229. if (strcmp(key, "ncnnbin") == 0)
  230. ncnnbinpath = std::string(value);
  231. if (strcmp(key, "ncnnpy") == 0)
  232. ncnnpypath = std::string(value);
  233. if (strcmp(key, "fp16") == 0)
  234. fp16 = atoi(value);
  235. if (strcmp(key, "optlevel") == 0)
  236. optlevel = atoi(value);
  237. if (strcmp(key, "device") == 0)
  238. device = value;
  239. if (strcmp(key, "inputshape") == 0)
  240. parse_shape_list(value, input_shapes, input_types);
  241. if (strcmp(key, "inputshape2") == 0)
  242. parse_shape_list(value, input_shapes2, input_types2);
  243. if (strcmp(key, "customop") == 0)
  244. parse_string_list(value, customop_modules);
  245. if (strcmp(key, "moduleop") == 0)
  246. parse_string_list(value, module_operators);
  247. }
  248. // print options
  249. {
  250. fprintf(stderr, "pnnxparam = %s\n", pnnxparampath.c_str());
  251. fprintf(stderr, "pnnxbin = %s\n", pnnxbinpath.c_str());
  252. fprintf(stderr, "pnnxpy = %s\n", pnnxpypath.c_str());
  253. fprintf(stderr, "pnnxonnx = %s\n", pnnxonnxpath.c_str());
  254. fprintf(stderr, "ncnnparam = %s\n", ncnnparampath.c_str());
  255. fprintf(stderr, "ncnnbin = %s\n", ncnnbinpath.c_str());
  256. fprintf(stderr, "ncnnpy = %s\n", ncnnpypath.c_str());
  257. fprintf(stderr, "fp16 = %d\n", fp16);
  258. fprintf(stderr, "optlevel = %d\n", optlevel);
  259. fprintf(stderr, "device = %s\n", device.c_str());
  260. fprintf(stderr, "inputshape = ");
  261. print_shape_list(input_shapes, input_types);
  262. fprintf(stderr, "\n");
  263. fprintf(stderr, "inputshape2 = ");
  264. print_shape_list(input_shapes2, input_types2);
  265. fprintf(stderr, "\n");
  266. fprintf(stderr, "customop = ");
  267. print_string_list(customop_modules);
  268. fprintf(stderr, "\n");
  269. fprintf(stderr, "moduleop = ");
  270. print_string_list(module_operators);
  271. fprintf(stderr, "\n");
  272. }
  273. std::set<std::string> foldable_constants;
  274. std::string foldable_constants_zippath = ptbase + ".foldable_constants.zip";
  275. pnnx::Graph pnnx_graph;
  276. #if BUILD_ONNX2PNNX
  277. if (!model_file_maybe_torchscript(ptpath))
  278. {
  279. load_onnx(ptpath.c_str(), pnnx_graph,
  280. input_shapes, input_types,
  281. input_shapes2, input_types2);
  282. }
  283. else
  284. #endif
  285. {
  286. load_torchscript(ptpath, pnnx_graph,
  287. device, input_shapes, input_types,
  288. input_shapes2, input_types2,
  289. customop_modules, module_operators,
  290. foldable_constants_zippath, foldable_constants);
  291. }
  292. fprintf(stderr, "############# pass_level2\n");
  293. pnnx::pass_level2(pnnx_graph);
  294. pnnx_graph.save("debug.param", "debug.bin");
  295. if (optlevel >= 1)
  296. {
  297. fprintf(stderr, "############# pass_level3\n");
  298. pnnx::pass_level3(pnnx_graph, foldable_constants, foldable_constants_zippath);
  299. fprintf(stderr, "############# pass_level4\n");
  300. pnnx::pass_level4(pnnx_graph);
  301. }
  302. pnnx_graph.save("debug2.param", "debug2.bin");
  303. if (optlevel >= 2)
  304. {
  305. fprintf(stderr, "############# pass_level5\n");
  306. pnnx::pass_level5(pnnx_graph, foldable_constants, foldable_constants_zippath);
  307. }
  308. // delete foldable_constants_zippath
  309. remove(foldable_constants_zippath.c_str());
  310. pnnx_graph.save(pnnxparampath, pnnxbinpath);
  311. pnnx_graph.python(pnnxpypath, pnnxbinpath);
  312. #if BUILD_PNNX2ONNX
  313. pnnx::save_onnx(pnnx_graph, pnnxonnxpath.c_str(), fp16);
  314. #else
  315. fprintf(stderr, "pnnx build without onnx-zero support, skip saving onnx\n");
  316. #endif
  317. // if (optlevel >= 2)
  318. {
  319. fprintf(stderr, "############# pass_ncnn\n");
  320. pnnx::pass_ncnn(pnnx_graph, module_operators);
  321. pnnx::save_ncnn(pnnx_graph, ncnnparampath, ncnnbinpath, ncnnpypath, fp16);
  322. }
  323. // pnnx::Graph pnnx_graph2;
  324. // pnnx_graph2.load("pnnx.param", "pnnx.bin");
  325. // pnnx_graph2.save("pnnx2.param", "pnnx2.bin");
  326. return 0;
  327. }