win10下pytorch转ncnn手把手教程

前言

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


一、环境配置

1.VS2019

点击链接去官网安装。

2.CMake3.21.3

点击链接去官网安装。
win10下pytorch转ncnn手把手教程_第1张图片
用户变量中配置环境变量。右键此电脑->属性->高级系统设置->环境变量->用户变量,选择Path进行添加。(在系统变量中添加也行,二者的区别在于:用户变量中添加的只对当前用户有效,而系统变量中添加的对所有用户都有效)
win10下pytorch转ncnn手把手教程_第2张图片

3.OpenCV3.4.13

点击链接去官网安装。
win10下pytorch转ncnn手把手教程_第3张图片
用户变量中配置环境变量。
win10下pytorch转ncnn手把手教程_第4张图片
我在E盘下新建了一个ncnnby的文件夹表示其所用到的所有依赖。opencv就装在这个文件夹下。

4.protobuf3.4.0

点击链接直接下载。
将其下载到指定文件夹下: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

最终得到以下所需文件:
win10下pytorch转ncnn手把手教程_第5张图片

5.编译ncnn

点击链接去github下载ncnn。
win10下pytorch转ncnn手把手教程_第6张图片
下载得到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进行推理。

最终得到以下所需文件:
win10下pytorch转ncnn手把手教程_第7张图片
至此,环境已经配置结束,且所需要的文件都已编译好。

二、模型转换

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文件
win10下pytorch转ncnn手把手教程_第8张图片win10下pytorch转ncnn手把手教程_第9张图片
测试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是我在网上下载了一只猫咪图片,测试结果如下:
win10下pytorch转ncnn手把手教程_第10张图片
推理结果是对的,预测概率会有些许的偏差。

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
win10下pytorch转ncnn手把手教程_第11张图片

3.onnx转ncnn

将简化后文件移动到E:\ncnnby\ncnn-master\build-vs2019\tools\onnx文件夹下
win10下pytorch转ncnn手把手教程_第12张图片
注意:不是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模型文件
win10下pytorch转ncnn手把手教程_第13张图片
生成了resnet18的param文件和bin文件,其中,param文件保存了模型结构,bin文件保存了模型参数。

4.测试ncnn

打开Visual Studio2019,选择创建新项目
win10下pytorch转ncnn手把手教程_第14张图片
选择空项目,点击下一步
win10下pytorch转ncnn手把手教程_第15张图片
随便起一个名字,我这里叫ncnn-by-test,且把它放在了E:\ncnnby下面,点击创建
win10下pytorch转ncnn手把手教程_第16张图片
现在点击视图->其他窗口->属性管理器,右键Release x64,选择添加新项目属性表
win10下pytorch转ncnn手把手教程_第17张图片
命名属性表为ncnn_opencv_releasex64,点击添加win10下pytorch转ncnn手把手教程_第18张图片
双击打开属性页开始编辑,我们依次需要修改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的根目录。
win10下pytorch转ncnn手把手教程_第19张图片
库目录中添加如下代码:

<opencv-root-dir>/build-vs2019/x64/vc15/lib
<ncnn-root-dir>/build-vs2019/install/lib
<protobuf-root-dir>/build-vs2019/install/lib

win10下pytorch转ncnn手把手教程_第20张图片
点击链接器->输入->附加依赖项,增加如下内容:

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
win10下pytorch转ncnn手把手教程_第21张图片
win10下pytorch转ncnn手把手教程_第22张图片
至此,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!的问题,百度了一些找不到解决办法,只能说不影响检测结果。如果你遇到了并且解决了,请告知我。
win10下pytorch转ncnn手把手教程_第23张图片
至此,全部ok。


总结

有进一步的进展再进行总结吧。

你可能感兴趣的:(深度学习,pytorch,opencv,深度学习)