win10下pytorch转ncnn手把手教程

foreword

最近需要把pytorch转为onnx再转为ncnn,整体流程大概为:
1、pytorch转为onnx;
2、使用onnx-simplifier工具简化onnx模型;
3、onnx转化为ncnn。

一、环境配置

1.VS2019

点击链接进入官网安装。[0]

2.CMake3.21.3

点击链接进入官网安装。
CMake官网
用户变量中配置环境变量。右键此电脑->属性->高级系统设置->环境变量->用户变量,选择Path进行添加。(在系统变量中添加也行,二者的区别在于:用户变量中添加的只对当前用户有效,而系统变量中添加的对所有用户都有效)
[0]

3.OpenCV3.4.13

点击链接进入官网安装。

在用户变量中配置环境变量。

我在E盘下新建了一个ncnnby的文件夹表示其所用到的所有依赖。opencv就装在这个文件夹下。[0]

4.protobuf3.4.0

点击链接直接下载。
将其下载到指定文件夹下:E:\ncnnby,打开x64 Native Tools Command Prompt for VS 2019,依次输入以下指令[0]

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

最终得到以下所需文件:

5.编译ncnn

点击链接去github下载ncnn。

下载得到ncnn-master.zip,解压到E:\ncnnby下。
打开x64 Native Tools Command Prompt for VS 2019,依次输入以下指令:[0]

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进行推理。

最终得到以下所需文件:

至此,环境已经配置好,需要的文件也已经编译好了。

2.模型转换

1.pytorch转onnx

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是我在网上下载了一只猫咪图片,测试结果如下:

推理结果正确,预测的概率会略有偏差。

2.简化onnx

对于我这个用例,因为很简单,所以其实不用简化也能成功将onnx转成ncnn,但对于复杂的模型必须先简化再转换,否则会失败。这里我简化一下。
在命令行安装简化工具onnx-simplifier,指令如下:

pip install onnx-simplifier

cd到onnx模型所在目录,我这里是F:\Pycharm\pythonProject,在命令行输入如下指令:

python -m onnxsim resnet18.onnx resnet18-sim.onnx

注意:如果以python开头不行的话就改成python3,哪个行用哪个。

可以得到简化模型resnet18-sim.onnx

3.onnx转ncnn

将简化后文件移动到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文件保存了模型参数。

4.测试ncnn

打开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 <opencv2/highgui/highgui.hpp> //这些东西在我们刚才添加依赖的时候已经添加进来了
#include <vector>
#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。

Summarize

让我们总结进一步的进展。

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2022年5月7日
下一篇 2022年5月7日

相关推荐