前言
填一个之前的坑啊,本篇的姊妹篇——利用Pytorch的C++前端(libtorch)读取预训练权重并进行预测 这篇文章中已经说明了如何在Ubuntu系统中使用libtorch做预测,当初也有朋友问我如何在Windows之下尝试使用libtorch,当时因为时间关系没有去看,后来就给忘了…现在有时间了当然要尝试一下~
当然先说结论哈,其实在Windows环境下的配置也是很简单的,因为官方已经替我们编译好的Windows版本的libtorch,这下就节省了我们编译Pytorch的时间,直接可以拿来使用,只要稍微配置一下就可以在Windows跑起libtorch了,没有想象中那么多的步骤,大可放心。
下文中使用的代码和之前在Ubuntu中使用的完全相同,我们不需要进行修改。
同样,首先,我们在官网下载适合于Windows的libtorch
,因为稳定版出来了,所以我们可以直接拿来使用。有CPU版本的和GPU版本的,这里我都进行了测试,都是可以的直接使用的,大家按照自己的需求进行下载即可。
个人环境:
- win10
- cuda9.0 + cudnn7.0.5
- 1060-6G
正式开始
与之前实现的任务相同,我这里将libtorch和OpenCV一起编译,使用OpenCV的读取摄像头然后识别当前的手势,模型是我自己训练好的,对于大家来说可以自己随便挑一个模型来使用。
下图为在Visual Studio中使用libtorch和OpenCV来实现判断剪刀石头布手势,运行的平台是cpu端。当然GPU端也是可以运行的,之后也会进行详解。
不得不说下,Pytorch的部署端真的很好用啊,虽然说目前仅仅适合一些小型的任务,但是潜力还是很大地,libtorch端配套Pytorch真的是太方便了!
获取libtorch
之前在Ubuntu跑libtorch的时候,因为OpenCV的一些原因,如果需要libtorch和OpenCV一起编译的话,最好自己编译一边libtorch从而保证libtorch和OpenCV混合编译时不会发生冲突。但是在win10端,OpenCV可以直接从官网下载已经编译好的,既然OpenCV从官方直接下载了,那么libtorch当然也从官方直接下载了。
正如前言所说,打开官网点击下载即可,CPU和GPU按照自己来进行选择。
测试GPU端的libtorch
在Windows端,因为我们从官方下载的OpenCV预编译版本是利用MSVS编译的,也就是我们常说的Visual Studio编译工具,所以我们接下来使用的编译器就是Visual Studio自带的编译器,为此我们需要安装:
- Visual Studio 2015或者2017(两个都测试过,都是可以的!),之后简称VS
- windows端的CMake
大概流程就是我们使用cmake构建好libtorch工程,然后使用VS打开根据cmake配置好的信息进行编译,所以在进行之后的步骤前一定要提前安装好上述的两样东西。
好了,那么首先我们要配置CmakeList:
cmake_minimum_required(VERSION 3.12 FATAL_ERROR) project(simnet) find_package(Torch REQUIRED) find_package(OpenCV REQUIRED) if(NOT Torch_FOUND) message(FATAL_ERROR "Pytorch Not Found!") endif(NOT Torch_FOUND) message(STATUS "Pytorch status:") message(STATUS " libraries: ${TORCH_LIBRARIES}") message(STATUS "OpenCV library status:") message(STATUS " version: ${OpenCV_VERSION}") message(STATUS " libraries: ${OpenCV_LIBS}") message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}") add_executable(simnet main.cpp) target_link_libraries(simnet ${TORCH_LIBRARIES} ${OpenCV_LIBS}) set_property(TARGET simnet PROPERTY CXX_STANDARD 11)
和之前的没有什么区别,主要我们需要找到libtorch和opencv库。
接下来使用cmake来进行配置吧,我们首先自己创建一个文件夹,存放我们的主程序main.cpp
还有CMakeLists.txt
,然后我们再创建一个build的空文件夹,之后我们编译好的文件都存放在build文件夹里头。
目录结构大概就是这样,假设这个文件夹存放在D盘:
- example
— build
— main.cpp
— CMakeLists.txt
好了,我们打开Window的命令行界面,进入该目录,再进入build文件夹,然后我们设置我们的Cmake参数:
-DCMAKE_PREFIX_PATH=path\to\opencv\build\x64\vc14\lib;path\to\libtorch -DCMAKE_BUILD_TYPE=Release -G"Visual Studio 14 Win64"
这里需要注意下OpenCV的路径,将上面的位置改成自己的安装位置修改即可,我们需要同时告诉cmake编译工具OpenCV和libtorch的位置信息,然后这里我选择-G"Visual Studio 14 Win64"
执行编译器是VS2015的编译工具,如果是2017的话就是-G"Visual Studio 15 Win64
这里根据自己的需要进行调整(如果使用VS2017别忘了修改OpenCV中的vc14为vc15)。
编译类型(-DCMAKE_BUILD_TYPE=Release)也要加上,要不然会报错:
Unknown cmake build type: Call Stack (most recent call first): D:/libtorch-gpu/share/cmake/Caffe2/Caffe2Config.cmake:88 (include) D:/libtorch-gpu/share/cmake/Torch/TorchConfig.cmake:39 (find_package) CMakeLists.txt:4 (find_package) -- Configuring incomplete, errors occurred! See also "E:/simnet-gpu/build/CMakeFiles/CMakeOutput.log". See also "E:/simnet-gpu/build/CMakeFiles/CMakeError.log".
好了,上述具体步骤如下:
C:\Users\dell>D: D:\>cd example D:\example>cd build D:\example\build>cmake -DCMAKE_PREFIX_PATH=D:\opencv4\opencv\build\x64\vc15\lib;D:\libtorch-gpu -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 14 Win64" ..
如果顺利的话应该是Cmake会输出:
-- The C compiler identification is MSVC 19.0.24215.1 -- The CXX compiler identification is MSVC 19.0.24215.1 -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/x86_amd64/cl.exe -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/x86_amd64/cl.exe -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Detecting C compile features -- Detecting C compile features - done -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/x86_amd64/cl.exe -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/x86_amd64/cl.exe -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Detecting CXX compile features -- Detecting CXX compile features - done -- Looking for pthread.h -- Looking for pthread.h - not found -- Found Threads: TRUE -- Found CUDA: C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v9.0 (found suitable version "9.0", minimum required is "7.0") -- Caffe2: CUDA detected: 9.0 -- Caffe2: CUDA nvcc is: C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v9.0/bin/nvcc.exe -- Caffe2: CUDA toolkit directory: C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v9.0 -- Caffe2: Header version is: 9.0 -- Found cuDNN: v7.0.4 (include: C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v9.0/include, library: C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v9.0/lib/x64/cudnn.lib) -- Autodetected CUDA architecture(s): 6.1 -- Added CUDA NVCC flags for: -gencode;arch=compute_61,code=sm_61 -- Found torch: D:/libtorch-gpu/lib/torch.lib -- Pytorch status: -- libraries: torch;caffe2_library;caffe2_gpu_library;C:/Program Files/NVIDIA Corporation/NvToolsExt/lib/x64/nvToolsExt64_1.lib;C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v9.0/lib/x64/cudart_static.lib -- Found OpenCV: D:/opencv4/opencv/build (found version "4.0.0") -- OpenCV library status: -- version: 4.0.0 -- libraries: opencv_calib3d;opencv_core;opencv_dnn;opencv_features2d;opencv_flann;opencv_gapi;opencv_highgui;opencv_imgcodecs;opencv_imgproc;opencv_ml;opencv_objdetect;opencv_photo;opencv_stitching;opencv_video;opencv_videoio;opencv_world -- include path: D:/opencv4/opencv/build/include -- Configuring done -- Generating done -- Build files have been written to: E:/simnet-gpu/build
这时Cmake会在build文件夹中生成一些列项目信息,可以使用VS打开(打开下面箭头标注的文件)。
如果你的电脑装有两个版本以上的Visual Studio,那么这里要注意如果我们之前使用VS15编译的话那么就要使用VS15去打开这个文件,也就是之前编译使用的工具和之后打开工程的版本应该一致
打开后大概是这样子的:
这里simnet是我的项目名字,我们将该项目设为启动项(可以看到simnet字体比较粗)从而方便调试。
上述的代码在之前的那篇文章中已经提到过,这里简单展示下main函数部分,在这段代码中,我们利用OpenCV读取摄像头数据转化为Tensor,然后投入模型中进行判断:
... cv::VideoCapture stream(0); cv::namedWindow("Gesture Detect", cv::WINDOW_AUTOSIZE); // 下面的 new-mobilenetv2-128_S-win.pt 是我在Window上利用Pytorch导出的trace后的模型 string model_path = "new-mobilenetv2-128_S-win.pt"; std::shared_ptr<torch::jit::script::Module> module = torch::jit::load(model_path); ...
关于模型
这里还有一点需要注意,使用libtorch导入的模型必须是和libtorch相匹配版本的Pytorch导出的模型,也就是说如果我拿我之前在linux端导出的模型(之前我在linux端导出的模型使用的Pytorch版本不是稳定版),在这里使用window下的libtorch读取是会发生错误的,所以我们需要利用正式版1.0版本的Pytorch去导出一个模型。
这里我直接在window下安装稳定版的Pytorch-1.0(官方已经有稳定版的win版的Pytorch了),然后利用以下代码导出traced后的模型。
# 读入之前训练好的.pth模型 state = torch.load('model/new-mobilenetv2-128_S.pth', map_location=device) model.load_state_dict(state['model'], strict=True) # example = torch.rand(1, 3, 128, 128).cuda() # model.to(device) # torch_out = torch.onnx.export(model, # example, # "new-mobilenetv2-128_S.onnx", # verbose=True, # export_params=True # ) example = torch.ones(1, 3, 128, 128) model = model.eval() traced_script_module = torch.jit.trace(model, example) output = traced_script_module(example) print(traced_script_module) # 导出trace后的模型 traced_script_module.save('new-mobilenetv2-128_S-win.pt')
开始编译
然后,我们直接点击VS中的调试器进行调试即可,这里我选择了Release(与之前cmake一致)进行编译。
点击后开始编译,可能会输出一堆警告,这里不用理会:
如果顺利的话,直接开始运行:
在VS中可以运行后,我们可以找到其单独的.exe文件,然后将必要的.dll文件(CPU和GPU的all不同)和模型与simnet.exe
放到一个文件夹中,这时,我们点击simnet.exe
就可以直接运行了:
后记
libtorch在WIndow端的使用也不是很复杂,我们根据运行环境不同下载不同版本的libtorch(CPU和GPU),然后使用cmake配置后,利用VS进行编译就可以直接使用,其中遇到的问题大部分时环境的问题,我们的代码并不需要修改,是可以跨平台的,我也在VS2015和VS2017中进行了测试,都是可以的。
可能会遇到的问题
我在其中遇到了两个小问题,稍微查下就解决了,现在贴出来,如果和我遇到同样的就不用去google了…
在Window下查看指定camke编译器的命令
在window中指定cmake的编译器构架:
cmake -G CMake Error: No generator specified for -G Generators Visual Studio 15 2017 [arch] = Generates Visual Studio 2017 project files. Optional [arch] can be "Win64" or "ARM". Visual Studio 14 2015 [arch] = Generates Visual Studio 2015 project files. Optional [arch] can be "Win64" or "ARM". Visual Studio 12 2013 [arch] = Generates Visual Studio 2013 project files. Optional [arch] can be "Win64" or "ARM". Visual Studio 11 2012 [arch] = Generates Visual Studio 2012 project files. Optional [arch] can be "Win64" or "ARM". Visual Studio 10 2010 [arch] = Generates Visual Studio 2010 project files. Optional [arch] can be "Win64" or "IA64". Visual Studio 9 2008 [arch] = Generates Visual Studio 2008 project files. Optional [arch] can be "Win64" or "IA64". Borland Makefiles = Generates Borland makefiles. NMake Makefiles = Generates NMake makefiles. NMake Makefiles JOM = Generates JOM makefiles. Green Hills MULTI = Generates Green Hills MULTI files (experimental, work-in-progress). MSYS Makefiles = Generates MSYS makefiles. MinGW Makefiles = Generates a make file for use with mingw32-make. Unix Makefiles = Generates standard UNIX makefiles. Ninja = Generates build.ninja files. Watcom WMake = Generates Watcom WMake makefiles. CodeBlocks - MinGW Makefiles = Generates CodeBlocks project files. CodeBlocks - NMake Makefiles = Generates CodeBlocks project files. CodeBlocks - NMake Makefiles JOM = Generates CodeBlocks project files. CodeBlocks - Ninja = Generates CodeBlocks project files. CodeBlocks - Unix Makefiles = Generates CodeBlocks project files. CodeLite - MinGW Makefiles = Generates CodeLite project files. CodeLite - NMake Makefiles = Generates CodeLite project files. CodeLite - Ninja = Generates CodeLite project files. CodeLite - Unix Makefiles = Generates CodeLite project files. Sublime Text 2 - MinGW Makefiles = Generates Sublime Text 2 project files. Sublime Text 2 - NMake Makefiles = Generates Sublime Text 2 project files. Sublime Text 2 - Ninja = Generates Sublime Text 2 project files. Sublime Text 2 - Unix Makefiles = Generates Sublime Text 2 project files. Kate - MinGW Makefiles = Generates Kate project files. Kate - NMake Makefiles = Generates Kate project files. Kate - Ninja = Generates Kate project files. Kate - Unix Makefiles = Generates Kate project files. Eclipse CDT4 - NMake Makefiles = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - MinGW Makefiles = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files.
缺失nvToolsExt64_1.dll
如果在使用GPU版本的libtorch的时候遇到缺少nvToolsExt64_1.dll
,直接从网上下一个放到C:\Windows\System32
目录下即可(win10)。
附带的资源文件:
下方资源文件为上文所述的 new-mobilenetv2-128_S-win.pt
通过tortch.jit.trace导出,以便大家快速使用,在CPU端和GPU端都进行了测试可以正常工作。
链接:https://pan.baidu.com/s/1bSwzH7YNZnRk3bLq3LMF8Q
提取码:2jjd
博主您好,我用unet训练的模型trace之后,在c++中用libtorch调用,但是输出的图片为全黑色,像素都是0,这种情况如何解决呢?
博主我在一台电脑上编的程序移到另一台电脑上,另一台电脑没有libtorch全文件,需要把libtorch下所有的dll都移过去吗?好像有点大的,现在是没有运行成功
我跑GPU下的权重时不能正确的输出,也没有错误显示。但是跑CPU下面的权重时,能够正确的执行prediction.
请问这可能是什么原因?
不能正确的输出是什么意思,结果和cpu的不一致么?
博主你好,libtorch不是官网说已经编译好了的,还是必须经过cmake再编译一遍吗
有编译好的版本,需要提前弄明白和你的编译环境是否匹配。也可以在自己的环境再编译一遍,pytorch编译的时候会顺便编译得到libtorch
博主你好,我想问一下,为啥用cuda进行模型预测,内存会一下子暴涨,并且停掉之后还是没有降下去。大佬知道怎么释放cuda或者内存吗?
停掉之后是指程序运行完毕了吗,释放内存我博客中有一篇文章有写
博主你好,我是纯cpu的电脑,cmake时出现找不到cuda,我又没法装cuda,怎么办?
CUDA_TOOLKIT_ROOT_DIR not found or specified
-- Could NOT find CUDA (missing: CUDA_TOOLKIT_ROOT_DIR CUDA_NVCC_EXECUTABLE CUDA_INCLUDE_DIRS CUDA_CUDART_LIBRARY) (Required is at least version "7.0")
请问博主可以给出.pth转.pth的完整代码吗?
.pth转.pth?我没看明白,C++代码我找下
博主你好,我运行的时候出现错误:错误 LNK1181 无法打开输入文件“torch-NOTFOUND.obj”
不知道为什么?查了很久都没有找到解决方案。
唔...这个看不出来呀,Window下确实坑很多,你是按照我的方式来的吗
是的,我的这些问题解决了,发现libtorch提供的windows版只有debug方式,所以用release方式始终不对,不知道你和好多博主是怎么完成的 ,
对了,博主可以把你的c++代码给我看一下吗?我现在调试的有些懵逼。如果可以的话,可以发我邮箱里,谢谢。
我也遇到同样问题了,尴尬
博主你好,我通过你的方式来在windows上部署,但是我总是在found torch这一步无法完成,找不到torch.lib,
楼主,我编译好在调试的时候遇到“未加载kernelbase.pdb”,请问你遇到过这个问题吗?我使用的是pytorch1.1, VS2017
博主你好,按照你的教程我跑到最后生成.exe文件之后,打开就一闪而过,请问是因为我的libtorch是gpu版本的,而.pt文件是cpu版本的原因吗
应该不是,CPU可以打开GPU版本的,一闪而过让我想知道你有没有加类似于pause类的函数
我加了system("pause"),但是仍然一闪而过,尽管只是输出一个ok
确定加对了么,用getch也可以,或者用opencv库里的wait
加了的,尚未解决。博主我还想请教一下关于libtorch与pytorch的版本匹配问题,我是在ubantu上用pytorch1.1训练的导出的模型,cuda是10.0;然后在windows上libtorch也是1.1,但是显卡不行,不支持cuda10.0就装了9.0,在vs调试的时候就报未经处理的异常: Microsoft C++ 异常: c10::Error,请问这是因为cuda不匹配的原因吗?
应该不是cuda问题,你应该在win上导出模型在win上导入,用ubuntu上导出win导入会出现符号不匹配的情况
确实遇到了这个 问题。。谢谢你的回复
博主你还,像你说的我先在win下利用ubantu训练好的.pth文件导出.pt文件,然后在win下导入,当我设置好命令行参数,调试运行时,报未经处理的异常: Microsoft C++ 异常: c10::Error:未加载kernelbase.pdb,kernelbase.pdb 包含查找模块 KernelBase.dll 的源文件所需的调试信息,问题出在我加载模型那一行,如下:
torch::Device available_device(device_type);
std::cout << "1111111111111" << std::endl;
//反序列化pytorch模型,并加载到module变量
std::shared_ptr model = torch::jit::load(modelName,available_device);
std::cout << "3333333333333" << std::endl;
请问您知道这是什么问题嘛?