PyTorch C++ API 全解:ATen、Autograd、C++ Frontend 与 LibTorch 实战指南
本文以 PyTorch 仓库中的 C++ API 文档入口 为主体,系统梳理 PyTorch 公共 C++ API 的五大组成部分(ATen、Autograd、C++ Frontend、TorchScript、C++ Extensions),并结合仓库源码与配套文档(安装指南、Frontend 指南、API 索引、FAQ)给出可复制的 LibTorch 构建流程、MNIST 端到端训练示例,以及常见编译、链接与性能问题的解决方案。读完本文,你将能够独立用 LibTorch 搭建 C++ 工程、定义并训练模型、保存与加载检查点,并在无 Python 的推理环境中部署 PyTorch 模型。
C++ API 的总体结构:五大组成部分
根据 docs/cpp/source/index.md 的官方描述,PyTorch 的公共 C++ API 大致可以分为五个部分:
- ATen:基础张量与数学运算库,是其余一切的地基;
- Autograd:在 ATen 之上提供自动求导能力;
- C++ Frontend:用于机器学习模型训练与评估的高层构建块;
- TorchScript:TorchScript JIT 编译器与解释器的 C++ 接口;
- C++ Extensions:以自定义 C++/CUDA 例程扩展 Python API 的机制。
这五块积木组合起来,构成了一个可用于研究与生产的、强调 GPU 加速同时兼顾 CPU 高性能的张量计算与动态神经网络 C++ 库。文档同时给出了一条重要稳定性提示:
注意:C++ API 目前应视为 "beta" 稳定性级别。为了改进 API 或更好地支撑 Python 接口(PyTorch 最稳定、支持最好的接口),官方可能会在后端做出重大破坏性变更。
ATen:张量库与动态分发
ATen 本质上是一个张量库,PyTorch 的绝大多数 Python 与 C++ 接口都构建在它之上。它提供了核心 Tensor 类,其上定义了数百个运算,其中大多数运算同时具备 CPU 与 GPU 实现,Tensor 类会根据张量类型动态分发到对应后端。一个最小的 ATen 用法示例如下:
#include <ATen/ATen.h>
at::Tensor a = at::ones({2, 2}, at::kInt);
at::Tensor b = at::randn({2, 2});
auto c = a + b.to(at::kInt);
该 Tensor 类及 ATen 中所有其他符号都位于 at:: 命名空间下。从仓库源码可以印证其组织方式:ATen 的总入口头文件位于 aten/src/ATen/ATen.h,它开头即声明了编译要求——
#if !defined(_MSC_VER) && __cplusplus < 202002L
#error C++20 or later compatible compiler is required to use ATen.
#endif
即(除 MSVC 外)要求 C++20 编译器,随后统一聚合了 ATen/Functions.h、ATen/Tensor.h、ATen/Dispatch.h、c10/core/Storage.h、c10/core/TensorOptions.h 等核心头文件。这也解释了为什么面向应用的 C++ 工程应当优先包含更高层的 torch/torch.h(见后文)而不是零散引用 ATen 内部头。
Autograd:为张量增加可微性
Autograd 是 PyTorch C++ API 中为 ATen Tensor 类补充自动求导能力的部分。其工作原理是:autograd 系统记录张量上的操作以构建计算图(autograd graph);对图中的叶节点变量调用 backwards() 即沿整个图执行反向模式微分,最终得到梯度。官方示例:
#include <torch/csrc/autograd/variable.h>
#include <torch/csrc/autograd/function.h>
torch::Tensor a = torch::ones({2, 2}, torch::requires_grad());
torch::Tensor b = torch::randn({2, 2});
auto c = a + b;
c.backward(); // a.grad() 将持有 c 对 a 的梯度
这里有一个关键区别,也是 C++ API 使用者最常踩的坑:ATen 中的 at::Tensor 默认是不可微的。要获得自动求导能力,必须使用 torch:: 命名空间的张量工厂函数而不是 at:: 命名空间的。例如用 at::ones 创建的张量不可微,而用 torch::ones 创建的则可以。仓库中 autograd 的 C++ 核心实现位于 torch/csrc/autograd/ 目录(Variable、Function、GraphTask 等),对应的公开头文件即文档示例中引用的 torch/csrc/autograd/variable.h。
配套的 FAQ 也重申了这一点:如果你在自定义扩展或模型代码中用 at::ones、at::randn 等 at:: 工厂函数创建张量而报错,解决方法就是改用 torch:: 命名空间——"torch:: 函数创建的是带 autograd 信息的张量(variable),你的代码应当始终只与这类张量打交道"。
C++ Frontend:推荐的 C++ 入口
PyTorch C++ Frontend 提供了纯 C++ 的高层建模接口,面向神经网络及通用 ML 研究与生产场景,设计上大量对齐 Python API。它包含以下能力(引自 index.md):
- 通过分层模块系统(类似
torch.nn.Module)定义模型; - 常用建模目的的"标准库"模块(卷积、RNN、批归一化等);
- 优化器 API,内置 SGD、Adam、RMSprop 等流行优化器;
- 数据集与数据管道的表示,支持跨多 CPU 核心并行加载数据;
- 训练会话检查点的序列化保存与加载格式(类似
torch.utils.data.DataLoader生态); - 模型到多 GPU 的自动并行(类似
torch.nn.parallel.DataParallel); - 基于 pybind11 将 C++ 模型绑定到 Python 的辅助代码;
- TorchScript JIT 编译器的入口;
- 便于衔接 ATen 与 Autograd API 的工具。
官方文档明确建议:除非你有特定理由要只用 ATen 或 Autograd API,否则 C++ Frontend 是进入 PyTorch C++ 生态的推荐入口——它比 ATen 与 Autograd API 提供更完善的功能和更好的稳定性保证。
Frontend 指南 进一步给出了组件与 Python 侧的对应关系:
| 组件 | 说明 |
|---|---|
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.h、nn.h、optim.h、data.h、serialize.h、jit.h、python.h 等),其实现分别对应 torch/csrc/api/src/nn/modules/、torch/csrc/api/src/optim/(sgd.cpp、adam.cpp、rmsprop.cpp、lbfgs.cpp 等)、torch/csrc/api/src/data/、torch/csrc/api/src/serialize/ 等目录。
Frontend 的设计哲学值得单独强调:它追求与 Python Frontend 在设计、命名、约定和功能上高度对齐——把 Python 模型移植到 C++ 所需的工作应完全落在语言特性的翻译上,而不必修改功能或行为;同时优先灵活性与易用性,宁可隐藏性能旋钮也不牺牲 API 的简洁。文档还有一段务实的提醒:Python 并不一定比 C++ 慢,因为 Python Frontend 中所有计算密集的操作都会下沉到 C++ 执行。因此,只有在多线程、低延迟或部署要求下必须写 C++ 时,C++ Frontend 才是正确选择——两个 Frontend 服务于不同场景、协同工作,谁也不无条件取代另一方。
端到端示例:MNIST 上的 C++ 训练
下面完整保留 frontend.md 中的端到端示例,展示了 Frontend 的核心要素:继承 torch::nn::Module 定义模型、用 register_module 注册子模块、用 torch::data 构建多线程数据加载器、用 torch::optim::SGD 训练、用 torch::save 定期保存检查点:
#include <torch/torch.h>
// 定义一个新 Module。
struct Net : torch::nn::Module {
Net() {
// 构造并注册三个 Linear 子模块。
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));
}
// 实现 Net 的前向算法。
torch::Tensor forward(torch::Tensor x) {
// 使用众多张量操作函数之一。
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;
}
// 使用"标准库"中的一个模块。
torch::nn::Linear fc1{nullptr}, fc2{nullptr}, fc3{nullptr};
};
int main() {
// 创建一个 Net。
auto net = std::make_shared<Net>();
// 为 MNIST 数据集创建一个多线程数据加载器。
auto data_loader = torch::data::make_data_loader(
torch::data::datasets::MNIST("./data").map(
torch::data::transforms::Stack<>()),
/*batch_size=*/64);
// 实例化 SGD 优化算法来更新 Net 的参数。
torch::optim::SGD optimizer(net->parameters(), /*lr=*/0.01);
for (size_t epoch = 1; epoch <= 10; ++epoch) {
size_t batch_index = 0;
// 迭代数据加载器,从数据集中产出批次。
for (auto& batch : *data_loader) {
// 重置梯度。
optimizer.zero_grad();
// 在输入数据上执行模型。
torch::Tensor prediction = net->forward(batch.data);
// 计算损失来评判模型预测。
torch::Tensor loss = torch::nll_loss(prediction, batch.target);
// 计算损失对模型参数的梯度。
loss.backward();
// 根据梯度更新参数。
optimizer.step();
// 每 100 个 batch 输出一次损失并做检查点。
if (++batch_index % 100 == 0) {
std::cout << "Epoch: " << epoch << " | Batch: " << batch_index
<< " | Loss: " << loss.item<float>() << std::endl;
// 定期把模型序列化为检查点。
torch::save(net, "net.pt");
}
}
}
}
示例中几个值得注意的实现细节,均可在 FAQ 中得到交叉印证:
- 子模块必须在构造函数中通过
register_module()注册。如果直接赋值而不注册,子模块在forward()、保存/加载模型时都不会被识别; - 训练/推理模式切换:Dropout、BatchNorm 在两种模式下行为不同,需要用
model->eval()与model->train()切换(示例中is_training()即依赖该模式状态控制 dropout 开关); - 数据管道:
Dataset通过.map(torch::data::transforms::Stack<>())转换,再由make_data_loader包装为支持多 worker 的加载器。
LibTorch:安装与最小可运行工程
安装指南 说明:PyTorch 提供包含全部头文件、库与 CMake 配置文件的二进制分发,称为 LibTorch,可下载 ZIP 归档(CPU 版或 GPU 版,通过官网链接选择器挑选)。官方同时提供面向 Windows 开发者的 Visual Studio 扩展(LibTorch Project Template),可自动配置 debug/release 链接选项,前置条件同样是从官网下载 LibTorch。
系统要求
文档列出的硬性要求:
- GLIBC:cxx11 ABI 版本需要 GLIBC 2.29 或更新;
- GCC:cxx11 ABI 需要 GCC 9 或更新。
最小 CMake 工程
CMake 不是使用 LibTorch 的硬要求,但它是官方推荐并会长期支持的构建系统。一个最基本的 CMakeLists.txt 如下(完整保留自 installing.md):
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)
# 以下代码块建议在 Windows 上使用。
# 依据上游 issue #25457,需要拷贝 DLL 以避免内存错误。
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 则只依赖 torch/torch.h 这个"总入口"头文件(仓库中对应 torch/csrc/api/include/torch/torch.h)。文档指出虽然也可以包含更细粒度的头文件只访问 API 的一部分,但包含 torch/torch.h 是覆盖大部分功能最稳妥的方式:
#include <torch/torch.h>
#include <iostream>
int main() {
torch::Tensor tensor = torch::rand({2, 3});
std::cout << tensor << std::endl;
}
假设工程目录布局为 example-app/CMakeLists.txt + example-app/example-app.cpp,构建步骤为:
mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH=/absolute/path/to/libtorch ..
cmake --build . --config Release
其中 /absolute/path/to/libtorch 必须是解压后 LibTorch 分发的绝对路径。如果 PyTorch 是通过 pip 安装的,可以用 torch.utils.cmake_prefix_path 自动查询前缀路径:
cmake -DCMAKE_PREFIX_PATH=`python3 -c 'import torch;print(torch.utils.cmake_prefix_path)'` ..
配置成功的输出会显示 -- Found Threads: TRUE、Configuring done 等,随后 cmake --build . 完成编译链接,运行 ./example-app 即打印出一个 2×3 的随机张量。两条平台相关的注意事项:
- Windows 下 debug 与 release 构建 ABI 不兼容:若以 debug 模式构建项目,请使用 debug 版 LibTorch,并在
cmake --build .时指定正确的 configuration; - Windows MSVC 用户:注意上面 CMake 片段中的 DLL 拷贝步骤,用于规避运行时内存错误。
FAQ:构建与链接问题速查
FAQ 文档 覆盖了 LibTorch 工程最常见的两类构建问题,补充如下:
CMake 找不到 Torch:指定 LibTorch 路径的方式有两种——-DCMAKE_PREFIX_PATH=/path/to/libtorch,或直接 -DTorch_DIR=/path/to/libtorch/share/cmake/Torch(指向包含 TorchConfig.cmake 的目录)。仓库中 cmake/TorchConfig.cmake.in 正是该配置文件的模板来源。
链接期出现未定义引用:确认 CMakeLists.txt 中同时满足三点——find_package(Torch REQUIRED)、target_link_libraries(my_app "${TORCH_LIBRARIES}"),以及设置 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")。
TorchScript:C++ 侧的三大能力
TorchScript 是 PyTorch 模型的表示形式,可被 TorchScript 编译器理解、编译与序列化;本质上它是一门独立语言——使用 PyTorch API 的 Python 子集。C++ 侧的 TorchScript 接口包含三块主要功能:
- 加载并执行 Python 中定义、序列化的 TorchScript 模型。如果你希望尽量用 Python 定义模型、再导出到 C++ 生产环境做无 Python 推理,这是最相关的能力(官方提供了对应的
cpp_export教程); - 定义自定义算子的 API:扩展 TorchScript 标准算子库,这些算子同样可被序列化并在 C++ 推理时被调用;
- 从 C++ 对 TorchScript 程序做即时编译:通过
torch::jit::compile直接访问 TorchScript 编译器。
Frontend 组件表中的 torch::jit 即对应第 3 点的纯 C++ 入口,其公开头文件为 torch/csrc/api/include/torch/jit.h,实现在 torch/csrc/api/src/jit.cpp;而 TorchScript 编译器的核心实现位于 torch/csrc/jit/ 目录(frontend、compiler、interpreter 等子模块),本仓库的 test/jit/ 目录(含 13 个 .pt 与 10 个 .ptl 测试模型)则是这套接口的验证依据。
C++ Extensions:面向 Python 用户的扩展机制
C++ Extensions 提供了一种简单而强大的方式,访问上述所有接口,用于扩展常规 Python 场景下的 PyTorch 用法。它最常见的用途是用 C++ 或 CUDA 实现自定义算子,以加速原生 PyTorch 研究工作流。需要明确的是:C++ 扩展 API 并没有给 PyTorch C++ API 增加任何新功能,它提供的是与 Python setuptools 的集成,以及一套 JIT 编译机制,让你能从 Python 访问 ATen、autograd 及其他 C++ API。官方教程(cpp_extension)是进一步学习该主题的主线材料。本仓库中该机制的实现主体在 torch/csrc/ 下的 python 绑定与扩展基础设施(如 torch/csrc/PyInterpreter.cpp、torch/csrc/Exceptions.cpp 等),测试则位于 test/cpp_extensions/ 目录(约 200 个测试文件)。
FAQ 中给出了一条与 C++ Extensions 强相关的经典排错经验:如果你 import 扩展模块时得到来自 PyTorch/ATen 的 ImportError: undefined symbol ...,正确做法是先 import torch 再导入扩展模块——这样扩展所依赖的 PyTorch 动态库符号会先被加载,扩展导入时才能成功解析符号。
性能与运行时 FAQ:C++ 侧模型跑不快、上 GPU、自定义数据集
FAQ 还整理了若干运行时高频问题,均附带可直接使用的代码:
移动模型到 GPU
torch::Device device(torch::kCUDA);
model->to(device);
auto input = torch::randn({1, 3, 224, 224}).to(device);
auto output = model->forward(input);
也可以先探测 CUDA 可用性再选择设备:
torch::Device device(torch::cuda::is_available() ? torch::kCUDA : torch::kCPU);
FAQ 提示需要包含 <torch/script.h> 等 TorchScript 头文件来完成编译。
模型在 C++ 中比 Python 还慢?
常见原因与对策依次为:
- 关闭梯度计算:用
torch::NoGradGuard no_grad;包裹推理代码; - TorchScript 模型推理优化:
module = torch::jit::optimize_for_inference(module);; - 预热:正式计时前跑若干次推理,让 JIT 编译与显存分配完成;
- 检查线程设置:如
at::set_num_threads(4);,按硬件调整。
自定义数据集
继承 torch::data::datasets::Dataset 并实现 get() 与 size() 即可接入标准数据管道:
class CustomDataset : public torch::data::datasets::Dataset<CustomDataset> {
public:
explicit CustomDataset(const std::string& data_path) {
// 在此加载数据
}
torch::data::Example<> get(size_t index) override {
torch::Tensor data = /* 加载 index 处的数据 */;
torch::Tensor label = /* 加载 index 处的标签 */;
return {data, label};
}
torch::optional<size_t> size() const override {
return dataset_size_;
}
private:
size_t dataset_size_;
};
随后接入 DataLoader:
auto dataset = CustomDataset("path/to/data")
.map(torch::data::transforms::Stack<>());
auto dataloader = torch::data::make_data_loader(
std::move(dataset),
torch::data::DataLoaderOptions().batch_size(32).workers(4));
保存与加载模型
整体保存/加载:
torch::save(model, "model.pt"); // 保存
torch::load(model, "model.pt"); // 加载
仅保存/加载权重状态则使用 Archive API:
// 保存
torch::serialize::OutputArchive archive;
model->save(archive);
archive.save_to("model_weights.pt");
// 加载
torch::serialize::InputArchive archive;
archive.load_from("model_weights.pt");
model->load(archive);
对应实现可参考 torch/csrc/api/src/serialize/ 下的 input-archive.cpp 与 output-archive.cpp。
API 参考的结构
C++ API 的完整参考文档按模块组织在 docs/cpp/source/api/index.md,分为三组:
- Core:
aten/index、c10/index、autograd/index、cuda/index、xpu/index; - C++ Frontend:
nn/index、optim/index、data/index、serialize/index; - Extensions:
library/index、stable/index。
这些 md 参考页(如 api/aten/index.md、api/optim/index.md、api/data/index.md、api/library/index.md)与仓库源码形成映射:aten 参考对应 aten/src/ATen/,nn/optim/data/serialize 参考对应 torch/csrc/api/include/torch/ 及 torch/csrc/api/src/,library 参考则对应自定义算子注册机制(torch/csrc/autograd 与 torch/csrc/jit 中的注册基础设施)。C++ API 顶层入口文档 docs/cpp/source/index.md 的 toctree 正是以 installing、frontend、api/index、faq 四页构成完整阅读路径。
小结与进阶路线
综合 docs/cpp/source/index.md 及其子文档,C++ 侧 PyTorch 的使用路径可以概括为:
- 入门:从 LibTorch 二进制分发 + CMake(
find_package(Torch REQUIRED))搭建工程,包含torch/torch.h,遵循 C++20(ATen 入口头对非 MSVC 编译器的要求)与 GLIBC 2.29+/GCC 9+ 的系统前提; - 建模:优先使用 C++ Frontend——
torch::nn::Module+register_module+torch::optim+torch::data+torch::save,与 Python 侧一一对应,迁移成本仅为语言翻译; - 部署:用 TorchScript 把 Python 模型序列化后在 C++ 中加载执行(
torch::jit入口),配合NoGradGuard、optimize_for_inference、预热与线程配置完成推理侧调优; - 扩展:用 C++ Extensions 机制把自定义 C++/CUDA 算子接回 Python 工作流,注意"先
import torch再导入扩展"的符号解析顺序。
需要再次强调的是文档给出的稳定性边界:C++ API 处于 beta 阶段,Python 接口才是最稳定、支持最好的接口。选型时若环境允许,Python Frontend 依然是官方推荐;C++ Frontend 的价值集中在多线程、低延迟与无 Python 部署这三类场景。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00