Abstract
这是我在19年年初在上海卫宁健康AI Lab实习所做的一个小工作,主要是将一个模型从Pytorch模型下转换成C++可执行进而可以部署在服务器上。当时做的时候参考资料找不到行之有效的解决方案。摸索了很长时间最终完成,这里写出来在Windows上的整个过程。
Linux下没有尝试。
本机环境:
OS: Windows 10 专业版, GPU: GTX 960m
软件版本:CUDA 8.0 + 对应Cudnn、Python 3.7.1、pytorch 1.0.1、Visual Studio 2017、CMake 3.14、OpenCV 4.0.1。
1. LibTorch项目构建
- 在官网下载libtorch,并解压到自己指定的目录下。
![57d53165e0a22e20f4a49c62e7687656.png](https://i-blog.csdnimg.cn/blog_migrate/b5914bb77f591f8202daf1dafa910b12.png)
- 先写一个C++的文件,内容可以是非常简单的。比如 example-app.cpp:
#include<iostream>
void main(){
cout<<”test”;
return 1;
}
- 新建一个CMakeLists.txt,内容:
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(demo)
find_package(OpenCV REQUIRED)
find_package(Torch REQUIRED)
add_executable(demo example-app.cpp)
target_link_libraries(demo ${TORCH_LIBRARIES})
set_property(TARGET demo PROPERTY CXX_STANDARD 11)
(注:这里的倒数第三行有example-app.cpp要和对应的cpp文件名一样)
- 将cpp文件、CMakeLists.txt、libtorch文件夹放在一起,新建一个空文件夹,名为build。文件的组织形式如图1所示。
![897d859b526a9b2302762f5955fc8e50.png](https://i-blog.csdnimg.cn/blog_migrate/27d118c90b31ff1e1a23a5497f6f8742.png)
- 进入build文件夹中,管理员身份执行cmd命令,输入以下命令:
cmake -DCMAKE_PREFIX_PATH=.. -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 15 Win64" -DTorch_DIR=F:NewTestlibtorchsharecmakeTorch DOpenCV_DIR=F:opencvopencvbuild
命令中,DTorch_DIR和DOpenCV_DIR要根据自己的电脑更改为对应电脑上libtorch和opencv存放的地方。然后回车,执行。
如果执行成功的话会输出以下的信息,如图2所示:
![309abcb5e238436107a951863456d471.png](https://i-blog.csdnimg.cn/blog_migrate/a6b88e3ab828ed9af0fc70461ff6efd8.jpeg)
(注:记得将opencv,libtorch,cmake的文件存放路径添加到windows操作系统的环境变量PATH里。)
执行完后,build文件里会生成许多文件。
这步做完,这整个的build文件夹里的文件就是生成好的c++文件,用vs打开demo.sln就可以引入#include<torch/torch.h>了
- 删掉ALL_BUILD和ZERO_CHECK。编写完成时点击本地windows调试器即可运行,demo.sln文件和build文件夹外的cpp文件会同时更新。
2. 生成Libtorch模型
- 利用网上提供的教程,比如 这篇 就可以,用torch.jit.trace将模型转换成libtorch可用的样式。在这里,不能用公司的模型,于是我在网上找了一个图片分类模型把转换成了model.pt用来做讲解。
- 转换完成之后,点开build文件夹下的demo.sln(注意这时候网络输入图片dog.png和model.pt都要放在对应文件夹中),编写如下的代码:
#include <torch/script.h>
#include <torch/torch.h>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <string>
#include <vector>
/* main */
int main() {
std::shared_ptr<torch::jit::script::Module> module = torch::jit::load("model.pt");
assert(module != nullptr);
std::cout << "load model okn";
std::vector<torch::jit::IValue> inputs;
inputs.push_back(torch::rand({ 64, 3, 224, 224 }));
double t = (double)cv::getTickCount();
module->forward(inputs).toTensor();
t = (double)cv::getTickCount() - t;
inputs.pop_back();
//load image with opencv and transform
cv::Mat image;
image = cv::imread("dog.png", 1);
cv::cvtColor(image, image, cv::COLOR_BGR2RGB);
cv::Mat img_float;
image.convertTo(img_float, CV_32F, 1.0 / 255);
cv::resize(img_float, img_float, cv::Size(224, 224));
//std::cout << img_float.at<cv::Vec3f>(56,34)[1] << std::endl;
auto img_tensor = torch::CPU(torch::kFloat32).tensorFromBlob(img_float.data, { 1, 224, 224, 3 });
img_tensor = img_tensor.permute({ 0,3,1,2 });
img_tensor[0][0] = img_tensor[0][0].sub_(0.485).div_(0.229);
img_tensor[0][1] = img_tensor[0][1].sub_(0.456).div_(0.224);
img_tensor[0][2] = img_tensor[0][2].sub_(0.406).div_(0.225);
auto img_var = torch::autograd::make_variable(img_tensor, false);
inputs.push_back(img_var);
// Execute the model and turn its output into a tensor.
torch::Tensor out_tensor = module->forward(inputs).toTensor();
std::cout << out_tensor.slice(/*dim=*/1, /*start=*/0, /*end=*/10) << 'n';
system("pause");
return 0;
}
输入的图片是图3。
![2839697389cc4e8d3c964c72182e4955.png](https://i-blog.csdnimg.cn/blog_migrate/5f1ad1d34188033befee3c38c972630f.jpeg)
此时,整个build文件下的组织形式是:
![7137cde7c5e4eae35368aef81ed2b9fc.png](https://i-blog.csdnimg.cn/blog_migrate/fbb0a34edd138630a53ba2317e3e19ff.jpeg)
然后在VS 2017里面点击Release x64 在本地运行调试器,就会产生如下的结果,如图5所示。输出出来了10个值,模型的运行成功了。
![8df58356a83a5d735d5bd89e284e48c7.png](https://i-blog.csdnimg.cn/blog_migrate/f6869af71aabc3f46703d06f63b8cd3c.png)
(注:因为是在网上随便找的模型和图片做测试,所以这个分类结果不正确。但是从侧面说明了这样的方法可以让程序顺利运行下来。在后来将公司里的模型迁移至Libtorch上时,运行结果没有出现这样的数值错误。)
3. 可能会遇到的Bug
- 出现Torch.dll, caffe.dll和另一个dll的缺失,无法运行程序。
![2acb8fe32dc076b42c4c5f08f78af891.png](https://i-blog.csdnimg.cn/blog_migrate/746d48899d34ea7a16bf7a3809d19b25.png)
这里其实三个动态链接库是没有缺失的,只不过系统没有找到对应位置。
解决方法:找到具体dll所在的地方,把目录添加进Windows的环境变量PATH里。
- Cmake torch运行到CUDA这一步之后无法运行下去
解决方法:重新调整libtorch,检查路径有无问题。
- 没有为 xxx.dll/ pdb 加载的符号文件
解决办法:1.检查自己的程序语句是否有问题,关键是检查,举例:检查imread(“xx.jpg”) 的图片引入路径。
2.手动添加符号文件:
Visual studio 工具栏里: 调试(D) --->选项(O)--->
![165c197b650ffa23656322d7def67642.png](https://i-blog.csdnimg.cn/blog_migrate/aa300824c913fbccfcaae399970532be.jpeg)
安装如上图,勾选 Microsoft符号服务器。
完成之后,在程序第一次执行,会花费一定时间去从服务器上加载符号文件,会比较慢,加载完成后,程序会继续执行。
4. 结尾
谢谢! 在解决整套流程的时候我也遇到了许多的问题,感谢他们无私的帮助。
如有问题,我的邮箱 lizheng1@stu.hznu.edu.cn 欢迎交流!