Libtorch(c++)模型部署

一、前言

需要预先准备libtorch(c++)环境,可以看这篇Libtorch(C++)环境配置,如果要读入图片,则建议提前准备OpenCV环境,可以看这篇OpenCV(C++)环境配置(windows)

二、模型部署步骤

1.Python端利用torch.jit.trace将训练完成的模型保存成Libtorch模型

有两种方法,一种是训练过程中直接整个模型保存了,可利用示例一进行转换保存;第二种是训练过程中仅保存了模型权重,需要再次构建模型再进行转换,可利用示例二进行转换保存。
本文采用的示例一

python代码如下(示例一):

# 转换模型,保存成libtorch可读模型
from torch.autograd import Variable

model=torch.load("./model/LeNet.pt") #加载保存的模型

from torchsummary import summary
summary(model, input_size=(30,5,5)) #打印model summary

use_gpu = torch.cuda.is_available()  # 判断是否有GPU加速
if use_gpu:
    model = model.cuda()
 
model.eval()

torch.no_grad()
# An example input you would normally provide to your model's forward() method.
example = torch.rand(1, 30, 5, 5) #根据自己的模型输入进行修改
 
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("./model/libtorch/LeNet.pt")

python代码如下(示例二):

# 转换模型,保存成libtorch可读模型
from torch.autograd import Variable


model = VGG16() #创建model实例对象,VGG16()即是你的模型函数

use_gpu = torch.cuda.is_available()  # 判断是否有GPU加速
if use_gpu:
    model = model.cuda()
 
model.eval() #关闭dropout等

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, 30, 5, 5)
 
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("./model/libtorch/LeNet.pt")

2.C++端加载保存的模型和测试用的my_tensor_test.txt文件

需要提前设置模型路径,项目上右键属性,命令参数中添加模型名称,然后将模型和测试tensor拷贝到.cpp同路径下
在这里插入图片描述

c++代码如下(示例):

#include<iostream>
#include<string>

#include<opencv2/opencv.hpp>
#include<torch/torch.h>
#include<torch/script.h>

#include<direct.h> //输出当前目录

using namespace cv;
using namespace torch;
using namespace std;

//定义读取的tensor(.txt文件,二维)
template <typename T>
std::vector<T> InputData_To_Vector(const std::string& path)
{
	std::vector<T> p;
	std::ifstream infile(path);
	assert(infile.is_open() && "Unable to open txt file. please check if the .txt file path is right!");
	T number;
	std::string s;
	while (getline(infile, s)) {
		std::istringstream is(s);
		T d;
		while (!is.eof()) {
			is >> d;
			p.push_back(d);
		}
		s.clear();
	}
	infile.close();
	return p;
}

// argc代表的是命令参数的数量,至少为1(argv[0]即.exe文件的路径)。argv为指针表示的参数, argv[0]表示第一个参数,argv[1]表示第二个参数,以此类推。
int main(int argc, const char* argv[]) {

	//cout << "argc=" << argc << endl;       //3个参数
	//cout << "argv[0]=" << argv[0] << endl; //E:\2022学习文档\c++\Libtorch_test\x64\Release\Libtorch_test.exe
	//cout << "argv[1]=" << argv[1] << endl; //LeNet.pt
	//cout << "argv[2]=" << argv[2] << endl; //cnn.pt

	//char buff[250];
	//_getcwd(buff, 250);
	//std::string current_working_directory(buff);
	//cout << "Current path is " << current_working_directory << '\n'; //输出当前路径,记得把模型文件放在该路径下

	//查看c++版本,默认为c++98,修改步骤如下:https://blog.csdn.net/chinabinlang/article/details/95597347
	//1.属性--c/c++ 所有选项--c++语言标准修改
	//2.属性–C/C++–命令行–其他选项  输入 /Zc:__cplusplus
	//if (__cplusplus == 201703l)
	//	std::cout << "c++17" << endl;
	//else if (__cplusplus == 201402l)
	//	std::cout << "c++14" << endl;
	//else if (__cplusplus == 201103l)
	//	std::cout << "c++11" << endl;
	//else if (__cplusplus == 199711l)
	//	std::cout << "c++98" << endl;
	//else
	//	std::cout << "pre-standard c++" << endl;

    //cpu形式加载模型
	DeviceType device_type = kCPU;
	Device device(device_type);
	jit::script::Module module;
    
	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);
		cout << "成功加载模型!" << endl;
	}
	catch (const c10::Error& e) {
		std::cerr << "error loading the model\n";
		return -1;
	}

    //加载保存的tensor,并进行处理
	std::vector<float> data = InputData_To_Vector<float>("my_tensor_test.txt");
	auto ten = torch::tensor(data).reshape({ 30,5,5 }).toType(torch::kFloat32); //reshape回正常shape
	cout << ten.sizes() << endl;

	ten = ten.unsqueeze(0);//增加一维,拓展维度,在最前面
	cout << ten.sizes() << endl;

	std::vector<torch::jit::IValue> inputs;
	inputs.push_back(ten);
    
    //前向传播,进行预测
	Tensor output = module.forward(inputs).toTensor();
	torch::Tensor output_max = output.argmax(1);
	int a = output_max.item().toInt();

	vector<string> out_list = { "Alfalfa", "Corn-notill", "Corn-mintill", "Corn"
					,"Grass-pasture", "Grass-trees", "Grass-pasture-mowed",
					"Hay-windrowed", "Oats", "Soybean-notill", "Soybean-mintill",
					"Soybean-clean", "Wheat", "Woods", "Buildings-Grass-Trees-Drives",
					"Stone-Steel-Towers" };
	cout << "分类预测的结果为:" << a << endl;
	cout << "分类预测的结果为:" << out_list[a] << endl;

	return 0;
}

3.测试结果

python端:

真值:10
预测值:10
实际类别:Soybean-mintill

c++端:
在这里插入图片描述

两者结果一致


总结

以上就是今天要讲的内容,本文仅仅简单介绍了Libtorch的模型读入和预测,未来可能进一步研究利用Libtorch进行模型搭建及训练。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐