首页
/ PyTorch C++ API 全解:ATen、Autograd、C++ Frontend 与 LibTorch 实战指南

PyTorch C++ API 全解:ATen、Autograd、C++ Frontend 与 LibTorch 实战指南

2026-09-07 15:35:55作者:咎岭娴Homer

本文以 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.hATen/Tensor.hATen/Dispatch.hc10/core/Storage.hc10/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/ 目录(VariableFunctionGraphTask 等),对应的公开头文件即文档示例中引用的 torch/csrc/autograd/variable.h

配套的 FAQ 也重申了这一点:如果你在自定义扩展或模型代码中用 at::onesat::randnat:: 工厂函数创建张量而报错,解决方法就是改用 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.hnn.hoptim.hdata.hserialize.hjit.hpython.h 等),其实现分别对应 torch/csrc/api/src/nn/modules/torch/csrc/api/src/optim/sgd.cppadam.cpprmsprop.cpplbfgs.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。

系统要求

文档列出的硬性要求:

  1. GLIBC:cxx11 ABI 版本需要 GLIBC 2.29 或更新;
  2. 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: TRUEConfiguring 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 接口包含三块主要功能:

  1. 加载并执行 Python 中定义、序列化的 TorchScript 模型。如果你希望尽量用 Python 定义模型、再导出到 C++ 生产环境做无 Python 推理,这是最相关的能力(官方提供了对应的 cpp_export 教程);
  2. 定义自定义算子的 API:扩展 TorchScript 标准算子库,这些算子同样可被序列化并在 C++ 推理时被调用;
  3. 从 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.cpptorch/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 还慢?

常见原因与对策依次为:

  1. 关闭梯度计算:用 torch::NoGradGuard no_grad; 包裹推理代码;
  2. TorchScript 模型推理优化module = torch::jit::optimize_for_inference(module);
  3. 预热:正式计时前跑若干次推理,让 JIT 编译与显存分配完成;
  4. 检查线程设置:如 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.cppoutput-archive.cpp

API 参考的结构

C++ API 的完整参考文档按模块组织在 docs/cpp/source/api/index.md,分为三组:

  • Coreaten/indexc10/indexautograd/indexcuda/indexxpu/index
  • C++ Frontendnn/indexoptim/indexdata/indexserialize/index
  • Extensionslibrary/indexstable/index

这些 md 参考页(如 api/aten/index.mdapi/optim/index.mdapi/data/index.mdapi/library/index.md)与仓库源码形成映射:aten 参考对应 aten/src/ATen/nn/optim/data/serialize 参考对应 torch/csrc/api/include/torch/torch/csrc/api/src/library 参考则对应自定义算子注册机制(torch/csrc/autogradtorch/csrc/jit 中的注册基础设施)。C++ API 顶层入口文档 docs/cpp/source/index.md 的 toctree 正是以 installingfrontendapi/indexfaq 四页构成完整阅读路径。

小结与进阶路线

综合 docs/cpp/source/index.md 及其子文档,C++ 侧 PyTorch 的使用路径可以概括为:

  1. 入门:从 LibTorch 二进制分发 + CMake(find_package(Torch REQUIRED))搭建工程,包含 torch/torch.h,遵循 C++20(ATen 入口头对非 MSVC 编译器的要求)与 GLIBC 2.29+/GCC 9+ 的系统前提;
  2. 建模:优先使用 C++ Frontend——torch::nn::Module + register_module + torch::optim + torch::data + torch::save,与 Python 侧一一对应,迁移成本仅为语言翻译;
  3. 部署:用 TorchScript 把 Python 模型序列化后在 C++ 中加载执行(torch::jit 入口),配合 NoGradGuardoptimize_for_inference、预热与线程配置完成推理侧调优;
  4. 扩展:用 C++ Extensions 机制把自定义 C++/CUDA 算子接回 Python 工作流,注意"先 import torch 再导入扩展"的符号解析顺序。

需要再次强调的是文档给出的稳定性边界:C++ API 处于 beta 阶段,Python 接口才是最稳定、支持最好的接口。选型时若环境允许,Python Frontend 依然是官方推荐;C++ Frontend 的价值集中在多线程、低延迟与无 Python 部署这三类场景。

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

项目优选

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