WHCSRL 技术网

C++调pytorch模型的全过程记录

前面已经记录过了,流程就是这么个流程:

这里就来记录一下模型转化和C++调用程序。

关于模型的训练就不多说了,不会训练模型还扯什么调用。

好在之前记录过一点:


========================分割线============================

好。我现在有了一个训练好的模型了,这个模型就是随意跑了一两个epoch,不考虑准确率,这里只考虑通整个流程 。

最初的模型就是 cnn.pth,我直接读这个模型好像不大行,于是还是将其按照文档那样,转成cnn.pt。怎么转的呢?教程就是这样的

  1. import torch
  2. import torchvision
  3. # An instance of your model.
  4. model = torchvision.models.resnet18()
  5. # An example input you would normally provide to your model's forward() method.
  6. example = torch.rand(1, 3, 224, 224)
  7. # Use torch.jit.trace to generate a torch.jit.ScriptModule via tracing.
  8. traced_script_module = torch.jit.trace(model, example)
  9. traced_script_module.save("traced_resnet_model.pt")

他这个例子是加载的已有的模型库,然后随意给了一个输入,让这个输入进到模型里面打个样,观察一下地形(我的理解),然后保存为pt文件。

那我测的话是自己的模型,怎么搞呢。和这个例子差不多,只是模型不一样,获得model后,就一样了

  1. class VGG16(nn.Module):
  2. def __init__(self, num_classes=10):
  3. super(VGG16, self).__init__()
  4. self.features = nn.Sequential(
  5. # 1
  6. nn.Conv2d(3, 64, kernel_size=3, padding=1),
  7. nn.BatchNorm2d(64),
  8. nn.ReLU(True),
  9. # 2
  10. nn.Conv2d(64, 64, kernel_size=3, padding=1),
  11. nn.BatchNorm2d(64),
  12. nn.ReLU(True),
  13. nn.MaxPool2d(kernel_size=2, stride=2),
  14. # 3
  15. nn.Conv2d(64, 128, kernel_size=3, padding=1),
  16. nn.BatchNorm2d(128),
  17. nn.ReLU(True),
  18. # 4
  19. nn.Conv2d(128, 128, kernel_size=3, padding=1),
  20. nn.BatchNorm2d(128),
  21. nn.ReLU(True),
  22. nn.MaxPool2d(kernel_size=2, stride=2),
  23. # 5
  24. nn.Conv2d(128, 256, kernel_size=3, padding=1),
  25. nn.BatchNorm2d(256),
  26. nn.ReLU(True),
  27. # 6
  28. nn.Conv2d(256, 256, kernel_size=3, padding=1),
  29. nn.BatchNorm2d(256),
  30. nn.ReLU(True),
  31. # 7
  32. nn.Conv2d(256, 256, kernel_size=3, padding=1),
  33. nn.BatchNorm2d(256),
  34. nn.ReLU(True),
  35. nn.MaxPool2d(kernel_size=2, stride=2),
  36. # 8
  37. nn.Conv2d(256, 512, kernel_size=3, padding=1),
  38. nn.BatchNorm2d(512),
  39. nn.ReLU(True),
  40. # 9
  41. nn.Conv2d(512, 512, kernel_size=3, padding=1),
  42. nn.BatchNorm2d(512),
  43. nn.ReLU(True),
  44. # 10
  45. nn.Conv2d(512, 512, kernel_size=3, padding=1),
  46. nn.BatchNorm2d(512),
  47. nn.ReLU(True),
  48. nn.MaxPool2d(kernel_size=2, stride=2),
  49. # 11
  50. nn.Conv2d(512, 512, kernel_size=3, padding=1),
  51. nn.BatchNorm2d(512),
  52. nn.ReLU(True),
  53. # 12
  54. nn.Conv2d(512, 512, kernel_size=3, padding=1),
  55. nn.BatchNorm2d(512),
  56. nn.ReLU(True),
  57. # 13
  58. nn.Conv2d(512, 512, kernel_size=3, padding=1),
  59. nn.BatchNorm2d(512),
  60. nn.ReLU(True),
  61. nn.MaxPool2d(kernel_size=2, stride=2),
  62. nn.AvgPool2d(kernel_size=1, stride=1),
  63. )
  64. self.classifier = nn.Sequential(
  65. # 14
  66. nn.Linear(512, 4096),
  67. nn.ReLU(True),
  68. nn.Dropout(),
  69. # 15
  70. nn.Linear(4096, 4096),
  71. nn.ReLU(True),
  72. nn.Dropout(),
  73. # 16
  74. nn.Linear(4096, num_classes),
  75. )
  76. # self.classifier = nn.Linear(512, 10)
  77. def forward(self, x):
  78. out = self.features(x)
  79. # print(out.shape)
  80. out = out.view(out.size(0), -1)
  81. # print(out.shape)
  82. out = self.classifier(out)
  83. # print(out.shape)
  84. return out
  85. '''创建model实例对象,并检测是否支持使用GPU'''
  86. model = VGG16()
  87. use_gpu = torch.cuda.is_available() # 判断是否有GPU加速
  88. if use_gpu:
  89. model = model.cuda()
  90. model.eval()
  91. '''测试'''
  92. classes=('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')
  93. # 转换模型
  94. model.load_state_dict(torch.load("./cnn.pth"))
  95. torch.no_grad()
  96. # An example input you would normally provide to your model's forward() method.
  97. example = torch.rand(1, 3, 32, 32)
  98. if use_gpu:
  99. example = Variable(example).cuda()
  100. # label = Variable(label, volatile=True).cuda()
  101. else:
  102. example = Variable(example)
  103. # label = Variable(label)
  104. # Use torch.jit.trace to generate a torch.jit.ScriptModule via tracing.
  105. traced_script_module = torch.jit.trace(model, example)
  106. traced_script_module.save("cnn.pt")

上面就是我测试的,先把我之前训练好的pth模型读进去,这样就有了model,之后就和例子一样了。不过这里加了个GPU的检测,我是用的GPU。好,到这里就将pth文件转换为了pt文件。

【当然,我相信这肯定是个笨方法,一定有更简单的,比如我训练好之后直接就保存成pt或是其他方法,暂时先不管】

下一步就是编写C++调用代码了

  1. #include "torch/script.h" // One-stop header.
  2. #include <iostream>
  3. #include <opencv2opencv.hpp>
  4. #include <opencv2imgproc ypes_c.h>
  5. using namespace cv;
  6. using namespace std;
  7. int main(int argc, const char* argv[])
  8. {
  9. /*******load*********/
  10. if (argc != 2) {
  11. std::cerr << "usage: example-app <path-to-exported-script-module> ";
  12. return -1;
  13. }
  14. torch::DeviceType device_type;
  15. device_type = torch::kCPU;//这里我没有检测了,直接用CPU做推理
  16. torch::Device device(device_type);
  17. torch::jit::script::Module module;
  18. //std::shared_ptr<torch::jit::script::Module> module = torch::jit::load(argv[1], device);
  19. try {
  20. // Deserialize the scriptmodule from a file using torch::jit::load().
  21. module = torch::jit::load(argv[1], device);//这里一定要加device,不然加载失败
  22. //module = torch::jit::load("cnn.pt", device);
  23. }
  24. catch (const c10::Error& e) {
  25. std::cerr << "error loading the model ";
  26. return -1;
  27. }
  28. vector<string> out_list = { "plane", "ca", "bird", "cat","deer", "dog", "frog", "horse", "ship", "truck" };
  29. auto image = imread("dog3.jpg");
  30. if (!image.data)
  31. {
  32. cout << "image imread failed" << endl;
  33. }
  34. cvtColor(image, image, CV_BGR2RGB);
  35. Mat img_transfomed;
  36. resize(image, img_transfomed, Size(32, 32));
  37. /*cout << img_transfomed.data;*/
  38. //img_transfomed.convertTo(img_transfomed, CV_16FC3, 1.0f / 255.0f);
  39. //Mat to tensor,
  40. torch::Tensor tensor_image = torch::from_blob(img_transfomed.data, { img_transfomed.rows, img_transfomed.cols, img_transfomed.channels() }, torch::kByte);
  41. tensor_image = tensor_image.permute({ 2, 0, 1 });
  42. tensor_image = tensor_image.toType(torch::kFloat);
  43. tensor_image = tensor_image.div(255);
  44. tensor_image = tensor_image.unsqueeze(0);//增加一维,拓展维度,在最前面
  45. std::vector<torch::jit::IValue> inputs;
  46. inputs.push_back(tensor_image);
  47. torch::Tensor output = module.forward(inputs).toTensor();
  48. torch::Tensor output_max = output.argmax(1);
  49. int a = output_max.item().toInt();
  50. cout << "分类预测的结果为:"<< out_list[a] << endl;
  51. return 0;
  52. //下面是输出为图像的例子
  53. tensor to Mat
  54. //output_max = output_max.squeeze();
  55. //output_max = output_max.mul(255).to(torch::kU8);
  56. //output_max = output_max.to(torch::kCPU);
  57. //Mat result_img(Size(480, 320), CV_8UC1);
  58. //memcpy((void*)result_img.data, output_max.data_ptr(), sizeof(torch::kU8) * output_max.numel());
  59. //imshow("result", result_img);
  60. //imwrite("result.bmp", result_img);
  61. //system("pause");
  62. }

 这里把模型名字写到命令行参数里

然后就可以出结果了(这个“然后”。。。其实我踩了挺多坑,后面慢慢再记录吧)

还有一个事情要做,这里的结果和python下跑的结果是一样的吗?

对比一下python下相同图片的结果

C++下和python下的结果一致,但都是错的,因为我用的测试图是小狗的。。。



没测试之前总感觉会比较复杂,搞不定。但是很多事情都是自己唬自己,试一下呢?说不定会发现很简单,或者不过如此之类的。(当然不是说这个简单了,我还是感觉挺复杂的,只是这个小测试还是比较简单的) 

最后贴个搞笑的图片,哈哈哈哈哈哈哈哈笑死了,别人的标题还真学不来

 

推荐阅读