LibTorch 部署 PyTorch 模型

模型训练

用 python 来构建模型并用数据集进行训练(略).

需要注意的是,用于训练的 python 版本的 pytorch 需要和 CPP 版本的 libtorch 版本一致!这里统一用 Stable (1.7.0) 版本.

模型保存

用 TorchScript 将训练好的模型保存为 *.pt 文件,以便通过 CPP 调用.

1
2
3
4
5
6
7
import torch
from torchvision.models import resnet18
model = resnet18()
example = torch.rand(1, 3, 224, 224)
traced_script_module = torch.jit.trace(model, example)

traced_script_module.save("./model/model1_resnet18.pt")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import torch
import torchvision.models as models
from PIL import Image
import numpy as np

image = Image.open("./images/demo.jpg")
image = image.resize((224, 224),Image.ANTIALIAS)
image = np.asarray(image)
image = image / 255
image = torch.Tensor(image).unsqueeze_(dim=0)
image = image.permute((0, 3, 1, 2)).float()

model = models.resnet50(pretrained=True)
model = model.eval()
resnet = torch.jit.trace(model, torch.rand(1,3,224,224))
# output=resnet(torch.ones(1,3,224,224))
output = resnet(image)
max_index = torch.max(output, 1)[1].item()
print(max_index) # ImageNet1000 类的类别序

resnet.save("./model/model2_resnet50.pt")

VS2017+libtorch

官网 选择 Stable (1.7.0)+Windows+LibTorch+C++ / Java+10.2, 下载Release 版本和 Debug 版本.

Windows binaries do not support Java. Support is only available for Linux and MacOS. Download here for C++ (Release version):
https://download.pytorch.org/libtorch/cu102/libtorch-win-shared-with-deps-1.7.0.zip
Download here for C++ (Debug version):
https://download.pytorch.org/libtorch/cu102/libtorch-win-shared-with-deps-debug-1.7.0.zip

配置

下载好 libtorch-win-shared-with-deps-1.7.0.ziplibtorch-win-shared-with-deps-debug-1.7.0.zip两个文件之后,分别解压到 releasedebug两个文件夹下. 这里先仅使用 Debug 版本.

libtorch-win-shared-with-deps-debug-1.7.0.zip压缩包解压后得到如下文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- debug
- libtorch
- bin
- cmake
- include
- ...
- lib
- *.lib
- *.pdb
- *.dll
- share
- test
- build-hash
- release
- ...
  • LIB
    解决方案资源管理器 右击项目名 ->属性 -> 链接器 -> 常规 -> 附加库目录 : 将 lib 文件夹路径添加到这里: …libtorch\lib
    解决方案资源管理器 右击项目名 ->属性 -> 链接器 -> 输入 -> 附加依赖项: 将 lib 文件名添加到这里:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    asmjit.lib
    clog.lib
    libprotobuf-lited.lib
    c10.lib
    cpuinfo.lib
    libprotocd.lib
    c10_cuda.lib
    dnnl.lib
    mkldnn.lib
    c10d.lib
    fbgemm.lib
    torch.lib
    caffe2_detectron_ops_gpu.lib
    gloo.lib
    torch_cpu.lib
    caffe2_module_test_dynamic.lib
    gloo_cuda.lib
    torch_cuda.lib
    caffe2_nvrtc.lib
    libprotobufd.lib
  • INCLUDE
    解决方案资源管理器 右击项目名 ->属性 ->C/C++-> 常规 -> 附加包含目录: 将 include 文件夹路径添加到这里:
    …libtorch\include
    …libtorch\include\torch\csrc\api\include

  • 解决提醒 std 冲突问题
    解决方案资源管理器 右击项目名 ->属性 ->C/C++-> 常规 ->SDL 检查-> 否(/sdl-)
    解决方案资源管理器 右击项目名 ->属性 ->C/C++-> 语言 -> 符合模式 ->

  • dll
    将 libtorch\lib 文件夹下的所有 dll 文件都复制到解决方案生成的可执行文件 *.exe 文件所在路径.
    此外,如果是 debug 模式,运行时会报错:找不到 vcruntime140_1d.dll. 其实系统里一定有vcruntime140_1.dll 文件,但是没有用于调试版本的 dll 文件,可以去 <www.dll-files.com> 网站下载: https://www.dll-files.com/vcruntime140_1d.dll.html

dll 文件可以直接放在 exe 文件相同路径下,也可以专门放在一个文件夹下,然后将这个文件夹添加到可搜索环境下:
解决方案资源管理器 右击项目名 ->属性 -> 调试 -> 环境->PATH=.\dll;%PATH%.

测试

测试 libtorch

1
2
3
4
5
6
7
#include<iostream>
#include<torch/script.h>
int main() {
torch::Tensor t1 = torch::tensor({ 10,1,2 });
std::cout << t1[0] << std::endl;
system("pause");
}
1
2
3
4
5
6
7
8
9
10
11
12
//2 个 Tensor 计算矩阵乘法
#include<iostream>
#include<torch/script.h>

int main() {
auto t1 = torch::tensor({ 1,2,3,4,5,6,7,8,9 }).reshape({ 3,3 });
auto t2 = torch::tensor({ 1,0,2,6,1,1,5,3,2 }).reshape({ 3,3 });
auto t3 = t1.mul(t2);
std::cout << t3 << std::endl;

system("pause");
}

模型导入测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <torch/script.h>  // One-stop header.
#include <iostream>
#include <memory>

int main() {
// Deserialize the ScriptModule from a file using torch::jit::load().
using torch::jit::script::Module;
Module module = torch::jit::load("./model/model1_resnet18.pt");
std::cout << "model load success" << std::endl;

// Create a vector of inputs.
std::vector<torch::jit::IValue> inputs;
inputs.push_back(torch::ones({ 1, 3, 224, 224 }));

// Execute the model and turn its output into a tensor.
at::Tensor output = module.forward(inputs).toTensor();

std::cout << output.slice(/*dim=*/1, /*start=*/0, /*end=*/5) << std::endl;
return 0;
}

模型预测测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <torch/script.h>
#include <torch/torch.h>
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <vector>

/****************************************
* 测试模型导入是否成功,并用全零的数据对模型进行测试
****************************************/
void TorchTest() {
torch::jit::script::Module module =
torch::jit::load("./model/model2_resnet50.pt");

std::cout << "Load model successful!" << std::endl;
std::vector<torch::jit::IValue> inputs;
inputs.push_back(torch::zeros({ 1, 3, 224, 224 }));
at::Tensor output = module.forward(inputs).toTensor();
auto max_result = output.max(1, true);
auto max_index = std::get<1>(max_result).item<float>();
std::cout << max_index << std::endl;
}

/****************************************
* 传入图片,对模型进行测试,得到模型预测结果
****************************************/
void Classfier(cv::Mat &image) {
torch::Tensor img_tensor = torch::from_blob(
image.data, { 1, image.rows, image.cols, 3 }, torch::kByte);
img_tensor = img_tensor.permute({ 0, 3, 1, 2 });
img_tensor = img_tensor.toType(torch::kFloat);
img_tensor = img_tensor.div(255);
torch::jit::script::Module module =
torch::jit::load("./model/model2_resnet50.pt");
torch::Tensor output = module.forward({ img_tensor }).toTensor();
auto max_result = output.max(1, true);
auto max_index = std::get<1>(max_result).item<float>();
std::cout << max_index << std::endl;
}

int main() {
// 对导入的模型进行简单测试
TorchTest();

// 通过导入的模型进行预测图像分类结果
cv::Mat image = cv::imread("./images/demo.jpg");
cv::resize(image, image, cv::Size(224, 224));
cv::imshow("image", image);
cv::waitKey(100);
std::cout << image.rows << " " << image.cols << " " << image.channels()
<< std::endl;
Classfier(image);
return 0;
}

Reference

LibTorch 部署 PyTorch 模型

https://luosiyou.cn/blogs/libtorch/

作者

Luo Siyou

发布于

2023-01-09

更新于

2023-01-09

许可协议