“他山之石,可以攻玉”,站在巨人的肩膀才能看得更高,走得更远。在科研的道路上,更需借助东风才能更快前行。为此,我们特别搜集整理了一些实用的代码链接,数据集,软件,编程技巧等,开辟“他山之石”专栏,助你乘风破浪,一路奋勇向前,敬请关注。
作者:知乎—皮特潘
地址:https://www.zhihu.com/people/wu-er-dong
01
前言
libtorch是pytorch推出的C++接口版本,支持CPU端和GPU端的部署和训练。主要是为了满足一些工业场景主体代码是C++实现的。libtorch用于部署官方不会提供太多诸如模型推理时间、模型大小等方面的优化,主要还是为了c++移植。我的理解是:深度学习炼丹是用python,这个毋庸置疑。优化后的模型或者固定的训练流程,如果有需要,可以在c++的libtorch上再实现一遍。本文介绍libtorch的安装和环境搭建,我的环境是ubuntu18.04。
02
安装
直接下载下来解压就好,不需要安装。
03
生成测试用pt
这个非常简单,直接利用python端的接口,将训练好的模型保存成jit.trace的形式。优点是不管python端还是c++端,都不需要重新构建模型代码,移植非常方便。缺点是有些模型的算子无法保存成功,当然这也是部署的“通病”,深度学习部署很大一部分工作就是在模型转换上,另外一大部分的工作就是环境搭建上,苦笑!
def save(net, input, save_path):
net.eval()
traced_script_module = torch.jit.trace(net, input)
traced_script_module.save(save_path)
def load(model_path):
return torch.jit.load(model_path)
04
可以用测试部署
非常简单,可以参照如下代码,性能和时间和python端基本无差别。
int main()
{
torch::DeviceType device_type;
if (torch::cuda::is_available()) {
device_type = torch::kCUDA;
}
else {
device_type = torch::kCPU;
}
torch::Device device(device_type);
std::string model_pb = "unet.pt";
auto module = torch::jit::load(model_pb); // 加载模型
module.to(at::kCUDA);
cv::Mat image = cv::imread("1.jpg");
cv::cvtColor(image, image, cv::COLOR_BGR2RGB);
cv::resize(image, image, cv::Size(320, 160));
image.convertTo(image, CV_32F, 1.0 / 255.0);
// convert to tensort
torch::Tensor img_tensor = ToTensor(image).to(at::kCUDA);
torch::TensorOptions option(torch::kFloat32);
torch::Tensor img_tensor = torch::from_blob(image.data, { 1, image.rows, image.cols, 3 }, option);
img_tensor = img_tensor.permute({ 0, 3, 1, 2 });
img_tensor = img_tensor.toType(torch::kFloat);
img_tensor = img_tensor.div(255);
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 << output << std::endl;
return max_index;
}
05
可用于训练
稍微复杂的是,需要自己利用c++的接口进行构建模型,并且实现优化器等操作,
#include <torch/torch.h>struct Net : torch::nn::Module {
Net() {
conv1 = register_module("conv1", torch::nn::Conv2d(torch::nn::Conv2dOptions(1, 6, /*kernel_size*/{ 5,5 }).padding(/*28->32*/{ 2,2 })));
conv2 = register_module("conv2", torch::nn::Conv2d(torch::nn::Conv2dOptions(6, 16, /*kernel_size*/{ 5,5 })));
conv3 = register_module("conv3", torch::nn::Conv2d(torch::nn::Conv2dOptions(16, 120, /*kernel_size*/{ 5,5 })));
fc1 = register_module("fc1", torch::nn::Linear(torch::nn::LinearOptions(120, 84)));
fc2 = register_module("fc2", torch::nn::Linear(torch::nn::LinearOptions(84, 10)));
}
// forward 方法实现
torch::Tensor forward(torch::Tensor x) {
x = conv1->forward(x);//6@28x28
x = torch::max_pool2d(x, { 2,2 }, { 2,2 });//6@14x14
x = conv2->forward(x);//16@10x10
x = torch::max_pool2d(x, { 2,2 }, { 2,2 });//16@10x10
x = conv3->forward(x);//120@1x1
x = x.view({ x.size(0),-1 });//-1 表示自动推理计算出该值
x = fc1->forward(x);//120->84
x = fc2->forward(x);//84->10
x = torch::log_softmax(x,/*dim=*/1);
return x;
}
torch::nn::Conv2d conv1{ nullptr };
torch::nn::Conv2d conv2{ nullptr };
torch::nn::Conv2d conv3{ nullptr };
torch::nn::Linear fc1{ nullptr };
torch::nn::Linear fc2{ nullptr };
};
int main() {
// 构建模型
auto net = std::make_shared<Net>();
// 构建dataset
auto train_dataset = torch::data::datasets::MNIST("E:\\data").map(
torch::data::transforms::Stack<>());
// dataloader
auto data_loader = torch::data::make_data_loader(train_dataset,/*batch_size=*/64);
const size_t test_dataset_size = train_dataset.size().value();
// 优化器
torch::optim::SGD optimizer(net->parameters(), /*lr=*/0.01);
for (size_t epoch = 1; epoch <= 100; ++epoch) {
size_t batch_index = 0;
int32_t correct = 0;
int len = 0;
for (auto& batch : *data_loader) {
optimizer.zero_grad(); // 优化器梯度清除
torch::Tensor prediction = net->forward(batch.data); //网络前向
// 计算loss
torch::Tensor loss = torch::nll_loss(prediction, batch.target);
auto pred = prediction.argmax(1);
correct += pred.eq(batch.target).sum().template item<int64_t>();
loss.backward();//反向传播
optimizer.step();//优化器迭代
len += torch::size(batch.data, 0);
if (++batch_index % 100 == 0) {
std::cout << "Epoch: " << epoch << " | Batch: " << batch_index
<< " | Loss: " << loss.item<float>() << " acc:"<<static_cast<double>(correct) / len <<std::endl;
// Serialize your model periodically as a checkpoint.
torch::save(net, "net.pt");//保存权重
}
}
}
}
06
常用api
新建tensor
torch::Tensor x = torch::tensor({ {1,2,3,4},{2,3,4,5} });
torch::Tensor x = torch::rand({ 1,3, 224,224 });
cuda操作
torch::DeviceType device_type;
if (torch::cuda::is_available()) {
std::cout << "CUDA available! Predicting on GPU." << std::endl;
device_type = torch::kCUDA;
}
else {
std::cout << "Predicting on CPU." << std::endl;
device_type = torch::kCPU;
}
torch::Device device(device_type);
module.to(at::kCUDA);
torch::Tensor img_tensor = ToTensor(image).to(at::kCUDA);
转换类型
img_tensor = img_tensor.toType(torch::kFloat);
从图像获取
cv::Mat img = cv::imread(path);
cv::resize(img, img, size);
img.convertTo(img, CV_32F, 1.0 / 255.0);
torch::TensorOptions option(torch::kFloat32);
auto img_tensor = torch::from_blob(img.data, { 1,img.rows,img.cols,img.channels() }, option);// opencv H x W x C torch C x H x W
img_tensor = img_tensor.permute({ 0,3,1,2 });// 调整 opencv 矩阵
模型前向
torch::Tensor output = module.forward({ img_tensor }).toTensor();
模型定义
参考上文
07
CMAKE 举例
libtorch 直接下载下来,不需要加入环境变量。
main.cpp
#include <torch/torch.h>#include <iostream>int main()
{
torch::DeviceType device_type;
if (torch::cuda::is_available()) {
std::cout << "CUDA available! Predicting on GPU." << std::endl;
device_type = torch::kCUDA;
}
else {
std::cout << "Predicting on CPU." << std::endl;
device_type = torch::kCPU;
}
torch::Device device(device_type);
torch::Tensor tensor = torch::eye(3);
tensor = tensor.to(at::kCUDA);
// std:cout<<tensor<<std::endl;
std::cout<<"hello torch"<<std::endl;
std::cout<<tensor<<std::endl;
return 0;
}
cMakeLists.txt
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)project(libtorch_test)find_package(Torch REQUIRED)add_executable(MAIN main.cpp)target_link_libraries(MAIN "${TORCH_LIBRARIES}")set_property(TARGET MAIN PROPERTY CXX_STANDARD 14)
编译测试
这里需要把libtorch的路径传进来,或者在cmakelist.txt加入下面几句
find_package(Torch REQUIRED
NO_MODULE
PATHS /home/Downloads/libtorch
NO_DEFAULT_PATH)
终端执行
cmake -DCMAKE_PREFIX_PATH=/Downloads/libtorch ..
cmake --build . --config Release
测试
./MAIN
输出结果
成功了!
本文目的在于学术交流,并不代表本公众号赞同其观点或对其内容真实性负责,版权归原作者所有,如有侵权请告知删除。