最近需要把pytorch转为onnx再转为ncnn,整体流程大概为:
1、pytorch转为onnx;
2、使用onnx-simplifier工具简化onnx模型;
3、onnx转化为ncnn。
点击链接去官网安装。
点击链接去官网安装。
用户变量中配置环境变量。右键此电脑->属性->高级系统设置->环境变量->用户变量,选择Path进行添加。(在系统变量中添加也行,二者的区别在于:用户变量中添加的只对当前用户有效,而系统变量中添加的对所有用户都有效)
点击链接去官网安装。
用户变量中配置环境变量。
我在E盘下新建了一个ncnnby的文件夹表示其所用到的所有依赖。opencv就装在这个文件夹下。
点击链接直接下载。
将其下载到指定文件夹下:E:\ncnnby
,打开x64 Native Tools Command Prompt for VS 2019
,依次输入以下指令
cd <protobuf-root-dir>
mkdir build-vs2019
cd build-vs2019
cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%cd%/install -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF ../cmake
nmake
nmake install
点击链接去github下载ncnn。
下载得到ncnn-master.zip
,解压到E:\ncnnby
下。
打开x64 Native Tools Command Prompt for VS 2019
,依次输入以下指令:
cd <ncnn-root-dir>
mkdir build-vs2019
cd build-vs2019
cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%cd%/install -DProtobuf_INCLUDE_DIR=E:/ncnnby/protobuf-3.4.0/build-vs2019/install/include -DProtobuf_LIBRARIES=E:/ncnnby/protobuf-3.4.0/build-vs2019/install/lib/libprotobuf.lib -DProtobuf_PROTOC_EXECUTABLE=E:/ncnnby/protobuf-3.4.0/build-vs2019/install/bin/protoc.exe -DNCNN_VULKAN=off -DOpenCV_DIR=D:/ncnnby/opencv/build ..
nmake
nmake install
注意:把以-DProtobuf开头的路径改成自己protobuf所在路径,把-DOpenCV_DIR开头的命令改成自己的opencv所在路径。因此这里总共需要改动四处。
-DNCNN_VULKAN=off
的意思是不使用GPU,而是用CPU进行推理。
最终得到以下所需文件:
至此,环境已经配置结束,且所需要的文件都已编译好。
Pycharm新建一个项目(名字任意),只需一个main.py
即可, 采用pytorch自带的resnet18作为示例,代码如下:
import torch
import torchvision
#define resnet18 model
model = torchvision.models.resnet18(pretrained=True)
#define input shape
x = torch.rand(1, 3, 224, 224)
#define input and output nodes, can be customized
input_names = ["x"]
output_names = ["y"]
#convert pytorch to onnx
torch_out = torch.onnx.export(model, x, "resnet18.onnx", input_names=input_names, output_names=output_names)
直接运行,会发现项目下面多了一个resnet18.onnx
文件
测试pytorch模型和onnx模型的推理结果是否一致,代码如下:
import torch
import torchvision
import onnxruntime as rt
import numpy as np
import cv2
#test image
img_path = "test.jpg" //测试图片放在当前目录下
img = cv2.imread(img_path)
img = cv2.resize(img, (224, 224))
img = np.transpose(img, (2, 0, 1)).astype(np.float32)
img = torch.from_numpy(img)
img = img.unsqueeze(0)
#pytorch test
model = torchvision.models.resnet18(pretrained=True)
model.eval()
output = model.forward(img)
val, cls = torch.max(output.data, 1)
print("[pytorch]--->predicted class:", cls.item())
print("[pytorch]--->predicted value:", val.item())
#onnx test
sess = rt.InferenceSession("resnet18.onnx")
x = "x"
y = ["y"]
output = sess.run(y, {x : img.numpy()})
cls = np.argmax(output[0][0], axis=0)
val = output[0][0][cls]
print("[onnx]--->predicted class:", cls)
print("[onnx]--->predicted value:", val)
test.jpg
是我在网上下载了一只猫咪图片,测试结果如下:
推理结果是对的,预测概率会有些许的偏差。
对于我这个用例,因为很简单,所以其实不用简化也能成功将onnx转成ncnn,但对于复杂的模型必须先简化再转换,否则会失败。这里我简化一下。
在命令行安装简化工具onnx-simplifier,指令如下:
pip install onnx-simplifier
cd到onnx模型所在目录,我这里是F:\Pycharm\pythonProject
,在命令行输入如下指令:
python -m onnxsim resnet18.onnx resnet18-sim.onnx
注意:如果以python
开头不行的话就改成python3
,哪个行用哪个。
将简化后文件移动到E:\ncnnby\ncnn-master\build-vs2019\tools\onnx
文件夹下
注意:不是E:\ncnnby\ncnn-master\tools\onnx
,而是E:\ncnnby\ncnn-master\build-vs2019\tools\onnx
,我一开始就在这里踩坑了。前者只包含onnx2ncnn.cpp这个文件,缺少头文件和可执行文件等等一系列我们所需要的文件,而后者是我们之前编译ncnn的时候创建的,里面万事俱备。
在当前目录下打开命令行,输入如下指令:
onnx2ncnn resnet18-sim.onnx resnet18.param resnet18.bin
得到ncnn模型文件
生成了resnet18的param文件和bin文件,其中,param文件保存了模型结构,bin文件保存了模型参数。
打开Visual Studio2019,选择创建新项目
选择空项目,点击下一步
随便起一个名字,我这里叫ncnn-by-test
,且把它放在了E:\ncnnby
下面,点击创建
现在点击视图->其他窗口->属性管理器,右键Release x64
,选择添加新项目属性表
命名属性表为ncnn_opencv_releasex64
,点击添加
双击打开属性页开始编辑,我们依次需要修改VC++目录下的包含目录、库目录
,以及链接器->输入下的附加依赖项
。
在包含目录
中添加如下代码:
<opencv-root-dir>/build/include
<opencv-root-dir>/build/include/opencv
<opencv-root-dir>/build/include/opencv2
<ncnn-root-dir>/build-vs2019/install/include/ncnn
<protobuf-root-dir>/build-vs2019/install/include
前面的分别是opencv的根目录,ncnn的根目录和protobuf的根目录。
在库目录
中添加如下代码:
<opencv-root-dir>/build-vs2019/x64/vc15/lib
<ncnn-root-dir>/build-vs2019/install/lib
<protobuf-root-dir>/build-vs2019/install/lib
ncnn.lib
opencv_world3413.lib
libprotobuf.lib
libprotobuf-lite.lib
libprotoc.lib
这里一定要注意opencv_world3413.lib
这个玩意儿,因为opencv的官网是不断更新的,所以你从官网上下载下来的这个文件的版本号不知道已经迭代到多少版了,所以你需要去相应的目录看你下载的到底是多少,我的目录是E:\ncnnby\opencv\build\x64\vc15\lib
,可以看到我的是3413,你的如果是3410你就要改成opencv_world3410.lib
至此,VS2019的相关操作结束,注意测试的时候要选择release
以及x64
,如图所示:
最后,测试ncnn模型的前向推理结果的正确性,在源文件
下新建一个ncnn_test.cpp
的文件,添加如下代码:
#include //这些东西在我们刚才添加依赖的时候已经添加进来了
#include
#include "net.h"
using namespace std;
vector<float> get_output(const ncnn::Mat& m)
{
vector<float> res;
for (int q = 0; q < m.c; q++)
{
const float* ptr = m.channel(q);
for (int y = 0; y < m.h; y++)
{
for (int x = 0; x < m.w; x++)
{
res.push_back(ptr[x]);
}
ptr += m.w;
}
}
return res;
}
int main()
{
cv::Mat img = cv::imread("test.jpg"); //替换为你的图片地址
int w = img.cols;
int h = img.rows;
ncnn::Mat in = ncnn::Mat::from_pixels_resize(img.data, ncnn::Mat::PIXEL_BGR, w, h, 224, 224);
ncnn::Net net;
net.load_param("resnet18.param"); //刚才生成的param文件
net.load_model("resnet18.bin"); //刚才生成的bin文件
ncnn::Extractor ex = net.create_extractor();
ex.set_light_mode(true);
ex.set_num_threads(4);
ex.input("x", in);
ncnn::Mat feat;
ex.extract("y", feat);
vector<float> res = get_output(feat);
vector<float>::iterator max_id = max_element(res.begin(), res.end());
printf("predicted class: %d, predicted value: %f", max_id - res.begin(), res[max_id - res.begin()]);
net.clear();
return 0;
}
预测类别和pytorch/onnx保持一致,由于计算库的不同,预测概率略微偏差。
这里我遇到一个FATAL ERROE!的问题,百度了一些找不到解决办法,只能说不影响检测结果。如果你遇到了并且解决了,请告知我。
至此,全部ok。
有进一步的进展再进行总结吧。