首页
/ PyTorch C++ Frontend 指南:用 LibTorch 定义、训练与序列化神经网络模型

PyTorch C++ Frontend 指南:用 LibTorch 定义、训练与序列化神经网络模型

2026-09-07 15:29:05作者:劳婵绚Shirley

本篇基于 PyTorch 官方 C++ 文档(docs/cpp/source/frontend.md)展开,系统讲解 PyTorch C++ Frontend(LibTorch)的组成结构、端到端 MNIST 训练示例、torch::nn::Module 的设计机制以及模型序列化 API。读完后你将能够:在 C++ 中用与 Python 前端一致的命名约定定义模型、编写训练循环、管理参数与缓冲区,并通过 torch::save/torch::load 完成检查点存取。

一、C++ Frontend 是什么

PyTorch C++ frontend 是一个 C++17 库,面向 CPU 与 GPU 的张量计算,提供自动微分以及构建现代机器学习应用所需的高层构件。官方文档将其定位为"Python 前端的 C++ 版本",它由以下七个核心组件构成(下表完整继承自原文档):

组件 说明
torch::Tensor 支持自动微分、高效的 CPU/GPU 张量
torch::nn 用于神经网络建模的可组合模块集合
torch::optim SGD、Adam、RMSprop 等训练模型的优化算法
torch::data 数据集、数据管道与多线程异步数据加载器
torch::serialize 保存和加载模型检查点的序列化 API
torch::python 将 C++ 模型绑定到 Python 的胶水代码
torch::jit 纯 C++ 访问 TorchScript JIT 编译器

从仓库源码结构看,这些命名空间对应的公共头文件都位于 torch/csrc/api/include/torch/ 下:

其中 torch/torch.htorch/csrc/api/include/torch/torch.h)是"一揽子"头文件——官方安装文档明确指出,包含它是获取 C++ API 大部分功能最稳妥的方式;如果你只需要部分功能,也可以只包含更细粒度的头文件。

二、端到端示例:在 MNIST 上定义并训练一个网络

以下是官方文档给出的完整端到端示例:定义一个三层全连接网络,用多线程数据加载器读取 MNIST,以 SGD 训练 10 个 epoch,并定期打印 loss 与保存检查点。

#include <torch/torch.h>

// Define a new Module.
struct Net : torch::nn::Module {
  Net() {
    // Construct and register two Linear submodules.
    fc1 = register_module("fc1", torch::nn::Linear(784, 64));
    fc2 = register_module("fc2", torch::nn::Linear(64, 32));
    fc3 = register_module("fc3", torch::nn::Linear(32, 10));
  }

  // Implement the Net's algorithm.
  torch::Tensor forward(torch::Tensor x) {
    // Use one of many tensor manipulation functions.
    x = torch::relu(fc1->forward(x.reshape({x.size(0), 784})));
    x = torch::dropout(x, /*p=*/0.5, /*train=*/is_training());
    x = torch::relu(fc2->forward(x));
    x = torch::log_softmax(fc3->forward(x), /*dim=*/1);
    return x;
  }

  // Use one of many "standard library" modules.
  torch::nn::Linear fc1{nullptr}, fc2{nullptr}, fc3{nullptr};
};

int main() {
  // Create a new Net.
  auto net = std::make_shared<Net>();

  // Create a multi-threaded data loader for the MNIST dataset.
  auto data_loader = torch::data::make_data_loader(
      torch::data::datasets::MNIST("./data").map(
          torch::data::transforms::Stack<>()),
      /*batch_size=*/64);

  // Instantiate an SGD optimization algorithm to update our Net's parameters.
  torch::optim::SGD optimizer(net->parameters(), /*lr=*/0.01);

  for (size_t epoch = 1; epoch <= 10; ++epoch) {
    size_t batch_index = 0;
    // Iterate the data loader to yield batches from the dataset.
    for (auto& batch : *data_loader) {
      // Reset gradients.
      optimizer.zero_grad();
      // Execute the model on the input data.
      torch::Tensor prediction = net->forward(batch.data);
      // Compute a loss value to judge the prediction of our model.
      torch::Tensor loss = torch::nll_loss(prediction, batch.target);
      // Compute gradients of the loss w.r.t. the parameters of our model.
      loss.backward();
      // Update the parameters based on the calculated gradients.
      optimizer.step();
      // Output the loss and checkpoint every 100 batches.
      if (++batch_index % 100 == 0) {
        std::cout << "Epoch: " << epoch << " | Batch: " << batch_index
                  << " | Loss: " << loss.item<float>() << std::endl;
        // Serialize your model periodically as a checkpoint.
        torch::save(net, "net.pt");
      }
    }
  }
}

示例中的几个关键设计点值得展开:

  1. 模块注册模式Net 继承 torch::nn::Module,在构造函数里用 register_module("fc1", ...) 把子模块以"名字 + 智能指针"的形式挂到模块树上。成员变量 torch::nn::Linear fc1{nullptr} 初值为空,真正的对象由 register_module 返回的 shared_ptr 持有——这正是 Python 端 self.fc1 = nn.Linear(...) 的 C++ 对应物。
  2. 训练态感知torch::dropout(x, 0.5, is_training()) 的第三个参数读取模块当前是否处于训练模式(train()/eval() 切换),与 Python 端 F.dropout(x, 0.5, self.training) 行为一致。
  3. 数据管道torch::data::datasets::MNIST("./data") 构造数据集,.map(torch::data::transforms::Stack<>()) 把逐样本张量堆叠成批次,make_data_loader(..., 64) 生成 batch_size 为 64 的多线程加载器。
  4. 标准训练循环zero_grad() → forward → nll_loss → loss.backward() → step() 五步曲,与 Python 前端的语义一一对应。
  5. 检查点torch::save(net, "net.pt") 把整个模块树(含参数)序列化到磁盘。

源码印证:Module 的三类持久化状态

阅读 torch/csrc/api/include/torch/nn/module.h 的类注释可以看到,Moduletorch::nn::Module(Python 版)在设计上刻意保持一致,并明确区分了三类持久化数据:

  1. Parameters:记录梯度的张量,典型如 Linearweight,由 register_parameter 注册;
  2. Buffers:不记录梯度、通常在 forward 中更新的张量,典型如 BatchNorm 的运行均值/方差,由 register_buffer 单独注册;
  3. 其他附加状态:模块实现或配置所需的非张量数据。

前两类注册进 Module 系统后可批量访问:parameters() 可迭代整棵子树的所有参数;Module::to() 可一次性改设备或 dtype(如 module->to(torch::kCUDA));clone() 则对整个可克隆模块层次做深拷贝。Module 还通过 enable_shared_from_this 支持 shared_ptr 语义,name() 在禁用 RTTI 时也可由构造函数手工指定——这些细节解释了上面示例中 std::make_shared<Net>()fc1{nullptr} 初始化方式背后的原因。

源码印证:torch::save / torch::load 的实现

示例中的 torch::save(net, "net.pt") 对应 torch/csrc/api/include/torch/serialize.h。从实现看,torch::save 是一个模板函数:内部构造 serialize::OutputArchive(附带一个 jit::CompilationUnit),通过 operator<< 写出值后再调用 save_to(...),因此除文件名外还可以直接传 ostreamtorch::load 则对称地构造 InputArchiveload_from(...) 后用 operator>> 读回。该头文件的文档注释明确列出了当前支持的类型:torch::nn::Moduletorch::optim::Optimizertorch::Tensor 的子类,另外还额外提供了 std::vector<torch::Tensor> 的 save/load 重载以及 pickle_save/pickle_load 用于 IValue 级别的操作。一个值得注意的限制(同样来自该头文件的注释):同一个 stream 不能在多次 torch::save(...) 调用中复用,否则会损坏文件头。

三、设计哲学:为什么还要一个 C++ 前端

原文档的 Philosophy 一节给出了非常明确的定位,值得完整保留:

PyTorch 的 Python 前端很优秀,能用 Python 时应该优先用 Python。但在一些场景下,性能与可移植性要求使得引入 Python 解释器不可行——例如低延迟、高性能或多线程环境(视频游戏、生产服务器)。C++ 前端的目标就是覆盖这些场景,同时不牺牲 Python 前端的用户体验。

由此衍生出两条设计原则:

  • 在结构、命名、约定和功能上紧密对齐 Python 前端。虽然偶有差异(例如砍掉了已弃用的功能、修复了 Python 端的"毛刺"),但官方保证:把一个 Python 模型移植到 C++,工作量应该只体现在翻译语言特性上,而不是修改功能或行为。
  • 优先考虑灵活性与易用性,而非微观优化。C++ 常常能写出极致性能的代码,但代价是糟糕的用户体验;PyTorch 的核心是灵活与动态,C++ 前端在某些场景甚至"藏起"性能旋钮以保持 API 简单,目标是让不写 C++ 为生的研究者也能上手。

文档同时给出一句重要的告诫:Python 未必比 C++ 慢。Python 前端对几乎一切计算密集型操作(尤其是数值运算)都会下沉到 C++ 调用,这些操作占了程序运行时间的大头。因此,如果你"能用 Python 就尽量用 Python";只有当你想写 C++,或因多线程、延迟、部署要求必须写 C++ 时,C++ 前端才发挥价值。两个前端服务于不同用例、相互配合,谁也不无条件取代谁。

四、安装与构建:依赖 LibTorch 的最小工程

C++ 前端的二进制发行版称为 LibTorch,包含依赖 PyTorch 所需的全部头文件、库与 CMake 配置。完整安装说明见仓库内的 docs/cpp/source/installing.md,此处给出可复制的最小工程骨架。

第一步,下载 LibTorch ZIP 并解压(CPU 版示例,GPU 版需在官方发布页选择对应包):

wget https://download.pytorch.org/libtorch/nightly/cpu/libtorch-shared-with-deps-latest.zip
unzip libtorch-shared-with-deps-latest.zip

第二步,编写最小 CMakeLists.txt(CMake 不是硬性要求,但官方推荐且会长期维护支持):

cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
project(example-app)

find_package(Torch REQUIRED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")

add_executable(example-app example-app.cpp)
target_link_libraries(example-app "${TORCH_LIBRARIES}")
set_property(TARGET example-app PROPERTY CXX_STANDARD 20)

# The following code block is suggested to be used on Windows.
# According to https://github.com/pytorch/pytorch/issues/25457,
# the DLLs need to be copied to avoid memory errors.
if (MSVC)
  file(GLOB TORCH_DLLS "${TORCH_INSTALL_PREFIX}/lib/*.dll")
  add_custom_command(TARGET example-app
                     POST_BUILD
                     COMMAND ${CMAKE_COMMAND} -E copy_if_different
                     ${TORCH_DLLS}
                     $<TARGET_FILE_DIR:example-app>)
endif (MSVC)

第三步,编写入口(example-app.cpp):

#include <torch/torch.h>
#include <iostream>

int main() {
  torch::Tensor tensor = torch::rand({2, 3});
  std::cout << tensor << std::endl;
}

第四步,在 example-app/ 目录下构建:

mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH=/absolute/path/to/libtorch ..
cmake --build . --config Release

其中 CMAKE_PREFIX_PATH 必须指向绝对路径的 LibTorch 解压目录。如果 PyTorch 是 pip 安装的,可直接查询前缀路径:

cmake -DCMAKE_PREFIX_PATH=`python3 -c 'import torch;print(torch.utils.cmake_prefix_path)'` ..

运行 build/example-app,应能打印一个 2×3 的随机张量。

系统要求(来自 installing.md):

  1. cxx11 ABI 版本需要 GLIBC ≥ 2.29
  2. GCC ≥ 9(cxx11 ABI)。

Windows 注意事项:Windows 上 debug 与 release 构建 ABI 不兼容,计划用 debug 模式构建项目时请改用 debug 版 LibTorch,并在 cmake --build . 行指定正确的 configuration。

五、延伸入口

适用前提小结:本文所有 API 形态(register_module 返回值、make_data_loader 模板参数、torch::save 的流式重载等)均以当前仓库 torch/csrc/api/include/torch/ 中的头文件为准;不同版本的 LibTorch 发行版在个别签名上可能略有差异,落地前建议以你所下载版本的头文件注释为最终依据。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388