各位观众老爷们,大家好!欢迎来到今天的“LibTorch深度历险记”讲座。今天咱们不讲虚的,直接上干货,用C++ LibTorch来玩转深度学习,让你的代码像开了挂一样,嗖嗖的!
开篇:为啥要用LibTorch?Python它不香吗?
Python确实香,简单易上手,生态丰富。但有些时候,Python的性能就有点捉襟见肘了。比如,你需要在嵌入式设备上跑模型,或者对延迟有极致要求,又或者你想把深度学习集成到一个大型的C++项目中。这时候,LibTorch就派上用场了。
LibTorch,简单来说,就是PyTorch的C++ API。它让你能在C++环境中直接加载、推理和训练PyTorch模型,性能杠杠的!
第一章:环境搭建,万事开头难?No!
工欲善其事,必先利其器。咱们先来搭建LibTorch的开发环境。
- 下载LibTorch: 打开PyTorch官网 (pytorch.org),找到LibTorch那一栏,根据你的操作系统和CUDA版本,下载对应版本的LibTorch包。
- 解压: 把下载的包解压到你喜欢的地方,比如
~/libtorch
。 - 配置CMake: 接下来,我们需要用CMake来构建我们的C++项目。CMakeLists.txt 才是关键!
下面是一个简单的CMakeLists.txt示例:
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(LibTorchExample)
# 设置C++标准
set(CMAKE_CXX_STANDARD 14)
# LibTorch的路径,替换成你自己的
set(Torch_DIR "/path/to/libtorch") # 例如: /home/user/libtorch
find_package(Torch REQUIRED)
if(NOT Torch_FOUND)
message(FATAL_ERROR "Could not find Torch")
endif()
add_executable(example main.cpp)
target_link_libraries(example "${TORCH_LIBRARIES}")
set_property(TARGET example PROPERTY CXX_STANDARD 14)
message(STATUS "Torch_VERSION: " ${Torch_VERSION})
message(STATUS "TORCH_LIBRARIES: " ${TORCH_LIBRARIES})
Torch_DIR
: 这里要替换成你解压的LibTorch的路径。find_package(Torch REQUIRED)
: 这句告诉CMake去找到LibTorch。target_link_libraries
: 这句把LibTorch的库链接到你的可执行文件。
第二章:加载模型,让你的C++程序变得聪明
有了环境,接下来咱们要加载一个训练好的PyTorch模型。通常,我们会先用Python训练一个模型,然后保存成.pt
或者.pth
文件。
-
Python模型导出: 假设你已经用Python训练好了一个模型,现在要把它导出。
import torch import torch.nn as nn # 定义一个简单的模型 class MyModel(nn.Module): def __init__(self): super(MyModel, self).__init__() self.linear = nn.Linear(10, 5) def forward(self, x): return self.linear(x) model = MyModel() # 保存模型 torch.save(model.state_dict(), 'model.pth') # 只保存模型参数,更轻量 # 或者保存整个模型 # torch.save(model, 'model.pt')
-
C++加载模型: 在C++中,我们需要用
torch::jit::load()
函数来加载模型。#include <torch/script.h> // One-stop header. #include <iostream> #include <memory> int main() { torch::jit::Module module; try { // 加载模型,替换成你的模型路径 module = torch::jit::load("model.pth"); // 或者 model.pt } catch (const c10::Error& e) { std::cerr << "error loading the modeln"; return -1; } std::cout << "Model loaded successfully!n"; return 0; }
torch::jit::load()
: 加载模型的核心函数。torch::jit::Module
: 加载的模型会变成一个torch::jit::Module
对象,你可以用它来进行推理。
第三章:模型推理,让你的C++程序会思考
模型加载好了,接下来就是让模型跑起来,也就是模型推理。
#include <torch/script.h> // One-stop header.
#include <iostream>
#include <memory>
int main() {
torch::jit::Module module;
try {
// 加载模型
module = torch::jit::load("model.pth");
} catch (const c10::Error& e) {
std::cerr << "error loading the modeln";
return -1;
}
std::cout << "Model loaded successfully!n";
// 创建一个输入张量
torch::Tensor input = torch::randn({1, 10}); // 1个batch,10个特征
// 推理
at::Tensor output = module.forward({input}).toTensor();
// 打印输出
std::cout << "Output:n" << output << "n";
return 0;
}
torch::randn()
: 创建一个随机的输入张量。module.forward()
: 调用模型的前向传播函数,进行推理。toTensor()
: 将IValue
类型转换为Tensor
类型at::Tensor
:at::Tensor
是LibTorch中表示张量的数据类型。
第四章:模型训练,让你的C++程序更聪明
LibTorch不仅可以用来推理,还可以用来训练模型。虽然用C++训练模型不如Python方便,但有些场景下,你可能需要在C++中进行模型的微调或者迁移学习。
#include <torch/torch.h>
#include <iostream>
int main() {
// 定义一个简单的线性模型
struct Net : torch::nn::Module {
Net() : linear(torch::nn::Linear(1, 1)) {
register_module("linear", linear);
}
torch::Tensor forward(torch::Tensor x) {
return linear->forward(x);
}
torch::nn::Linear linear;
};
// 创建模型
auto net = std::make_shared<Net>();
// 定义优化器
torch::optim::SGD optimizer(net->parameters(), 0.01);
// 训练数据
torch::Tensor x = torch::randn({100, 1});
torch::Tensor y = 2 * x + 1 + torch::randn({100, 1}) * 0.1;
// 训练循环
for (size_t i = 0; i < 100; ++i) {
// 前向传播
torch::Tensor output = net->forward(x);
// 计算损失
torch::Tensor loss = torch::mse_loss(output, y);
// 反向传播
optimizer.zero_grad();
loss.backward();
optimizer.step();
// 打印损失
if (i % 10 == 0) {
std::cout << "Epoch " << i << ", Loss: " << loss.item<float>() << std::endl;
}
}
std::cout << "Training finished!n";
return 0;
}
torch::nn::Module
: 所有模型的基类。torch::nn::Linear
: 线性层。torch::optim::SGD
: 随机梯度下降优化器。torch::mse_loss
: 均方误差损失函数。optimizer.zero_grad()
: 清空梯度。loss.backward()
: 计算梯度。optimizer.step()
: 更新模型参数。
第五章:CUDA加速,让你的模型跑得飞起
如果你的电脑有NVIDIA显卡,那么一定要用CUDA来加速你的模型。
- 编译CUDA支持的LibTorch: 下载LibTorch的时候,要选择CUDA版本的。
-
把模型和数据放到GPU上:
// 创建模型 auto net = std::make_shared<Net>(); // 把模型放到GPU上 net->to(torch::kCUDA); // 训练数据 torch::Tensor x = torch::randn({100, 1}); torch::Tensor y = 2 * x + 1 + torch::randn({100, 1}) * 0.1; // 把数据放到GPU上 x = x.to(torch::kCUDA); y = y.to(torch::kCUDA);
torch::kCUDA
: 表示CUDA设备。net->to(torch::kCUDA)
: 把模型放到GPU上。x.to(torch::kCUDA)
: 把数据放到GPU上。
第六章:性能优化,让你的代码更上一层楼
即使用了CUDA,还可以通过一些技巧来进一步优化性能。
- Batch Size: 适当增加Batch Size可以提高GPU的利用率。
- Data Loader: 使用多线程Data Loader可以加速数据加载。
- 模型量化: 把模型参数从float32转换成int8,可以减小模型大小和提高推理速度。
- TorchScript优化: 可以使用
torch::jit::optimize_for_inference
来优化模型。
第七章:实战案例,让你的知识落地
光说不练假把式,咱们来个实战案例:用LibTorch实现一个简单的图像分类器。
-
Python模型训练:
import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms # 定义模型 class SimpleCNN(nn.Module): def __init__(self): super(SimpleCNN, self).__init__() self.conv1 = nn.Conv2d(1, 10, kernel_size=5) self.pool = nn.MaxPool2d(2, 2) self.conv2 = nn.Conv2d(10, 20, kernel_size=5) self.fc1 = nn.Linear(320, 50) self.fc2 = nn.Linear(50, 10) def forward(self, x): x = self.pool(torch.relu(self.conv1(x))) x = self.pool(torch.relu(self.conv2(x))) x = x.view(-1, 320) x = torch.relu(self.fc1(x)) x = self.fc2(x) return x # 加载MNIST数据集 transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform) train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True) # 创建模型 model = SimpleCNN() # 定义优化器 optimizer = optim.Adam(model.parameters(), lr=0.001) # 训练循环 for epoch in range(2): for batch_idx, (data, target) in enumerate(train_loader): optimizer.zero_grad() output = model(data) loss = nn.functional.cross_entropy(output, target) loss.backward() optimizer.step() if batch_idx % 100 == 0: print('Epoch: {} [{}/{} ({:.0f}%)]tLoss: {:.6f}'.format( epoch, batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item())) # 保存模型 torch.jit.save(torch.jit.script(model), "mnist_model.pt") #使用torch.jit.script 进行保存
-
C++模型推理:
#include <torch/script.h> // One-stop header. #include <iostream> #include <memory> #include <opencv2/opencv.hpp> // 需要安装OpenCV int main() { torch::jit::Module module; try { // 加载模型 module = torch::jit::load("mnist_model.pt"); } catch (const c10::Error& e) { std::cerr << "error loading the modeln"; return -1; } std::cout << "Model loaded successfully!n"; // 加载图像 (需要OpenCV) cv::Mat image = cv::imread("test_image.png", cv::IMREAD_GRAYSCALE); //test_image.png 需要自己准备 if (image.empty()) { std::cerr << "Could not open or find the imagen"; return -1; } // 预处理图像 cv::resize(image, image, cv::Size(28, 28)); image.convertTo(image, CV_32F, 1.0 / 255.0); // 归一化到 [0, 1] // 创建输入张量 torch::Tensor input = torch::from_blob(image.data, {1, 1, 28, 28}, torch::kFloat32); // 1个batch,1个通道,28x28 input = input.toType(torch::kFloat); // 转换成float类型 // 推理 at::Tensor output = module.forward({input}).toTensor(); // 获取预测结果 at::Tensor prediction = torch::argmax(output, 1); int predicted_label = prediction.item<int>(); std::cout << "Predicted label: " << predicted_label << "n"; return 0; }
- 需要安装OpenCV,用于图像加载和预处理。
torch::from_blob()
: 从现有的数据创建张量。torch::argmax()
: 获取最大值的索引,也就是预测的类别。
第八章:常见问题,助你避坑
在使用LibTorch的过程中,可能会遇到一些问题,这里列举一些常见的:
问题 | 解决方法 |
---|---|
找不到LibTorch库 | 检查CMakeLists.txt中的 Torch_DIR 是否正确,确保指向LibTorch的根目录。 |
模型加载失败 | 检查模型路径是否正确,模型是否完整。 确保Python模型保存时使用了 torch.save(model.state_dict(), 'model.pth') 或者 torch.jit.save(torch.jit.script(model), "model.pt") |
CUDA错误 | 检查CUDA版本是否正确,显卡驱动是否安装正确。 确保编译的时候使用了CUDA支持的LibTorch版本。 |
张量类型不匹配 | 检查输入张量的类型是否与模型期望的类型一致。可以使用 input.toType(torch::kFloat) 等函数来转换张量类型。 |
内存泄漏 | 注意管理张量的生命周期,避免内存泄漏。 可以使用智能指针来管理张量,或者手动调用 tensor.reset() 来释放内存。 |
模型输入输出不匹配,崩溃或者结果异常 | 1. 确保C++中输入的Tensor的数据类型,大小,形状与Python中模型定义时的输入一致。2. 使用debug模式进行跟踪,查看具体崩溃位置。3. 检查 C++中输入张量的均值方差等预处理参数是否与Python中一致。 4.使用torch::jit::Module::dump 函数可以将导出的模型结构打印出来,方便进行对比,检查。 |
总结:LibTorch,你的深度学习好帮手
LibTorch是一个强大的工具,可以让你在C++环境中进行深度学习模型的推理和训练。虽然学习曲线比Python陡峭一些,但掌握之后,你会发现它的性能和灵活性是Python无法比拟的。
希望今天的讲座能帮助你入门LibTorch,在深度学习的道路上越走越远!
课后作业
- 用LibTorch加载一个自己训练的模型,并进行推理。
- 用LibTorch实现一个简单的图像分类器,并在自己的数据集上进行训练。
- 尝试使用CUDA加速你的LibTorch程序。
祝大家学习愉快! 下课!