前面已经记录过了,流程就是这么个流程:
- 配置libtorch --->python训练的模型怎么在C++使用?_己亥谷雨-CSDN博客
- pytorch模型转化
- 编写C++调用程序
这里就来记录一下模型转化和C++调用程序。
关于模型的训练就不多说了,不会训练模型还扯什么调用。
好在之前记录过一点:
========================分割线============================
好。我现在有了一个训练好的模型了,这个模型就是随意跑了一两个epoch,不考虑准确率,这里只考虑通整个流程 。
最初的模型就是 cnn.pth,我直接读这个模型好像不大行,于是还是将其按照文档那样,转成cnn.pt。怎么转的呢?教程就是这样的
import torch
import torchvision
# An instance of your model.
model = torchvision.models.resnet18()
# An example input you would normally provide to your model's forward() method.
example = torch.rand(1, 3, 224, 224)
# Use torch.jit.trace to generate a torch.jit.ScriptModule via tracing.
traced_script_module = torch.jit.trace(model, example)
traced_script_module.save("traced_resnet_model.pt")
他这个例子是加载的已有的模型库,然后随意给了一个输入,让这个输入进到模型里面打个样,观察一下地形(我的理解),然后保存为pt文件。
那我测的话是自己的模型,怎么搞呢。和这个例子差不多,只是模型不一样,获得model后,就一样了
class VGG16(nn.Module):
def __init__(self, num_classes=10):
super(VGG16, self).__init__()
self.features = nn.Sequential(
# 1
nn.Conv2d(3, 64, kernel_size=3, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(True),
# 2
nn.Conv2d(64, 64, kernel_size=3, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(True),
nn.MaxPool2d(kernel_size=2, stride=2),
# 3
nn.Conv2d(64, 128, kernel_size=3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(True),
# 4
nn.Conv2d(128, 128, kernel_size=3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(True),
nn.MaxPool2d(kernel_size=2, stride=2),
# 5
nn.Conv2d(128, 256, kernel_size=3, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(True),
# 6
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(True),
# 7
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(True),
nn.MaxPool2d(kernel_size=2, stride=2),
# 8
nn.Conv2d(256, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(True),
# 9
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(True),
# 10
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(True),
nn.MaxPool2d(kernel_size=2, stride=2),
# 11
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(True),
# 12
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(True),
# 13
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(True),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.AvgPool2d(kernel_size=1, stride=1),
)
self.classifier = nn.Sequential(
# 14
nn.Linear(512, 4096),
nn.ReLU(True),
nn.Dropout(),
# 15
nn.Linear(4096, 4096),
nn.ReLU(True),
nn.Dropout(),
# 16
nn.Linear(4096, num_classes),
)
# self.classifier = nn.Linear(512, 10)
def forward(self, x):
out = self.features(x)
# print(out.shape)
out = out.view(out.size(0), -1)
# print(out.shape)
out = self.classifier(out)
# print(out.shape)
return out
'''创建model实例对象,并检测是否支持使用GPU'''
model = VGG16()
use_gpu = torch.cuda.is_available() # 判断是否有GPU加速
if use_gpu:
model = model.cuda()
model.eval()
'''测试'''
classes=('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')
# 转换模型
model.load_state_dict(torch.load("./cnn.pth"))
torch.no_grad()
# An example input you would normally provide to your model's forward() method.
example = torch.rand(1, 3, 32, 32)
if use_gpu:
example = Variable(example).cuda()
# label = Variable(label, volatile=True).cuda()
else:
example = Variable(example)
# label = Variable(label)
# Use torch.jit.trace to generate a torch.jit.ScriptModule via tracing.
traced_script_module = torch.jit.trace(model, example)
traced_script_module.save("cnn.pt")
上面就是我测试的,先把我之前训练好的pth模型读进去,这样就有了model,之后就和例子一样了。不过这里加了个GPU的检测,我是用的GPU。好,到这里就将pth文件转换为了pt文件。
【当然,我相信这肯定是个笨方法,一定有更简单的,比如我训练好之后直接就保存成pt或是其他方法,暂时先不管】
下一步就是编写C++调用代码了
#include "torch/script.h" // One-stop header.
#include <iostream>
#include <opencv2\opencv.hpp>
#include <opencv2\imgproc\types_c.h>
using namespace cv;
using namespace std;
int main(int argc, const char* argv[])
{
/*******load*********/
if (argc != 2) {
std::cerr << "usage: example-app <path-to-exported-script-module>\n";
return -1;
}
torch::DeviceType device_type;
device_type = torch::kCPU;//这里我没有检测了,直接用CPU做推理
torch::Device device(device_type);
torch::jit::script::Module module;
//std::shared_ptr<torch::jit::script::Module> module = torch::jit::load(argv[1], device);
try {
// Deserialize the scriptmodule from a file using torch::jit::load().
module = torch::jit::load(argv[1], device);//这里一定要加device,不然加载失败
//module = torch::jit::load("cnn.pt", device);
}
catch (const c10::Error& e) {
std::cerr << "error loading the model\n";
return -1;
}
vector<string> out_list = { "plane", "ca", "bird", "cat","deer", "dog", "frog", "horse", "ship", "truck" };
auto image = imread("dog3.jpg");
if (!image.data)
{
cout << "image imread failed" << endl;
}
cvtColor(image, image, CV_BGR2RGB);
Mat img_transfomed;
resize(image, img_transfomed, Size(32, 32));
/*cout << img_transfomed.data;*/
//img_transfomed.convertTo(img_transfomed, CV_16FC3, 1.0f / 255.0f);
//Mat to tensor,
torch::Tensor tensor_image = torch::from_blob(img_transfomed.data, { img_transfomed.rows, img_transfomed.cols, img_transfomed.channels() }, torch::kByte);
tensor_image = tensor_image.permute({ 2, 0, 1 });
tensor_image = tensor_image.toType(torch::kFloat);
tensor_image = tensor_image.div(255);
tensor_image = tensor_image.unsqueeze(0);//增加一维,拓展维度,在最前面
std::vector<torch::jit::IValue> inputs;
inputs.push_back(tensor_image);
torch::Tensor output = module.forward(inputs).toTensor();
torch::Tensor output_max = output.argmax(1);
int a = output_max.item().toInt();
cout << "分类预测的结果为:"<< out_list[a] << endl;
return 0;
//下面是输出为图像的例子
tensor to Mat
//output_max = output_max.squeeze();
//output_max = output_max.mul(255).to(torch::kU8);
//output_max = output_max.to(torch::kCPU);
//Mat result_img(Size(480, 320), CV_8UC1);
//memcpy((void*)result_img.data, output_max.data_ptr(), sizeof(torch::kU8) * output_max.numel());
//imshow("result", result_img);
//imwrite("result.bmp", result_img);
//system("pause");
}
这里把模型名字写到命令行参数里
然后就可以出结果了(这个“然后”。。。其实我踩了挺多坑,后面慢慢再记录吧)
还有一个事情要做,这里的结果和python下跑的结果是一样的吗?
对比一下python下相同图片的结果
C++下和python下的结果一致,但都是错的,因为我用的测试图是小狗的。。。
没测试之前总感觉会比较复杂,搞不定。但是很多事情都是自己唬自己,试一下呢?说不定会发现很简单,或者不过如此之类的。(当然不是说这个简单了,我还是感觉挺复杂的,只是这个小测试还是比较简单的)
最后贴个搞笑的图片,哈哈哈哈哈哈哈哈笑死了,别人的标题还真学不来