开个坑,libtorch1.5 + opencv部署

libtorch1.5部署windows平台的深度学习应用

最近部署windows平台的深度学习应用,纯cpu,走了不少弯路,打算开个帖子,好好聊一聊
代码部分,暂时不公开,涉及公司机密。抽空写个简化版本的,分别给出分类、分割、检测(我想大家肯定碰到过很多问题)

libtorch想说production不容易

  1. 基本概念
    我也不说废话了,我们做成应用,怎么都离不开windows。然而,并不是所有电脑都带cuda,所以优先选择的,当然是cpu版本。毕竟产品化的时候,还是要考虑性价比。
    准备工作如下:
    1.1 下载对应的libtorch,我这里用了1.5,这里得注意,release/debug两个版本都要下载,debug包里面包含你需要的pdb文件,很重要,因为你一定会crash的。release速度快了很多。(发现个问题,我用onnxruntime运行模型,速度比libtorch快了好几倍,我想应该是libtorch哪里做的有问题,官方论坛有个帖子,我反正没怎么看懂libtorch does not initialize OpenMP/MKL by default
    1.2 配置vs2017工程,这个我就不说了,没什么好说的。(cuda版本看起来必须使用cmake,不然你总是无法link到cuda的库
    1.3 然后你就开始写代码吧
  2. 应用架构
    2.1 我这里,部署模型设计成class的结构,这样在构造class的时候,就需要传入模型文件。对外的话,提供inference接口,当然,最好的做法,我觉得,可以设计成多个接口,比如classify/segment/detect,每个接口返回值各不相同,输入参数当然都是一致的,比如,就是个cv::Mat& img这样的,我们要做的img归一化之类的,都在这些接口里面统一处理掉,方便应用端调用嘛。
    2.2 封装成dll,这个就是另一回事儿了,我们晚点再说。
  3. 编译与环境准备
    3.1 libtorch 1.5需要c++14,所以,你不得不使用vs2017。当然,你碰到一些老古董的工程还是使用vs2008之类的,那就坑爹了。你只能自己单独用vs2017做好dll,给vs2008用了,辛苦一点。
    3.2 Cpu起码i5以上吧,内存别不舍得,怎么也得16G。(什么?16G都没有?那你做什么人工智能
  4. 部署方案(source code/dll)
    4.1 最直接了当,当然是把源码集成到应用端,这个根据个人喜好,自己决定了。
    4.2 当然,你肯定不希望自己辛辛苦苦写的前后处理就这么送人,对吧,那么,你就要封装成dll了,封装三个基本的接口:init, inference, destroy。init需要返回一个void指针,让应用端代持,inference的时候,需要用户传入刚刚代持的void指针,destroy也要传入void指针,释放你创建的整个模型内存。

python opencv 与 c++ opencv的差异(TMD非常坑人)

  1. 关键问题
    1.1 opencv roi 的内存不连续问题:
    很多时候,我们会对opencv读取的图片,选取其中的一部分,比如你做语义分割,训练的模型一般不会直接输入一整张大图的,对吧,太大了,模型跑不动的。所以我们会裁切,那么我们就会这么做
cv::Mat img = cv::imread("test.jpg");
cv::Mat partImg = img(cv::Rect(20,30,w,h))
//别忘了mat转float,并做归一化,因为你做的模型肯定只认得float型数据

以上就是我们习惯的做法,看起来也没什么问题,我们常年在pytorch中都是类似这种操作,习惯了,紧接着,我们肯定就会这样做

auto inputData = torch::from_blob(partImg.data, {1, h, w, 3}, torch::kFloat32);
std::vector<torch::jit::IValue> inputs;
inputs.push_back(inputData);

好了,你的噩梦从此开始了。你的预测结果怎么都不对,而且还错得很奇怪,感觉找不到规律。你可能从头开始查了输入数据,发现跟python没差别,你查了归一化后的tensor值,发现也是跟python端一致,就是不知道为什么。
此时……你正在奔溃之际,你应该好好想一想,我哪里做的跟python那边不一样?

细想,细想,你会发现,你在Mat中使用了ROI,那么这个到底有什么差异呢?

roi只是在同一个mat中标记了一块区域,这个roi对于opencv一族的函数,都是起作用的。但是,你别忘记了,torch::from_blob访问的,是mat存数据的起始指针,那么,你设置的roi当然就被无视了,所以不管你怎么做,你输入的图片数据,都是从(0,0)开始的一段固定大小**{1, h, w, 3}**

既然知道问题了,我们来改一下,很简单,一句话解决,别眨眼

cv::Mat img = cv::imread("test.jpg");
cv::Mat partImg = img(cv::Rect(20,30,w,h)).clone();

好了,到此,问题全部解决。

P.S batch forward

我发现这个问题,很多人会关心,代码呢,官方确实也没有那么直观地给出来,我这里就贴一段我写好的,希望能给大家有所帮助

std::vector<cv::Mat> imgVec;
std::vector<torch::Tensor> imgTensor;
for (int i = 0; i < imgVec.size(); i++) {
		auto temp = torch::from_blob(imgVec.at(i).data, { 1, height, weight, channel}, torch::kFloat32);
		temp = temp.permute({ 0,3,1,2 });
		imgTensor.push_back(temp);
	}
c10::ArrayRef<torch::Tensor> batchImg(imgTensor);
torch::Tensor outBatchTensor = torch::cat(batchImg, 0);
std::vector<torch::jit::IValue> inputs;
inputs.push_back(outBatchTensor);
//这里,你就可以forward了。

下篇预告

下次,我们来讲讲,我们怎么在cpu端加速我们做的这些模型,没有GPU,能快一点,是一点嘛,see you.

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 23
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值