进阶 pytorch.org 2026-10-07 23:07:35 · 6 阅读

第6章 C++ 前端中的 Autograd:PyTorch 自动求导教程

# 第6章 C++ 前端中的 Autograd 创建时间:2020年4月1日 | 最后更新:2025年1月21日 | 最后验证:未验证 autograd 包是构建高度灵活的动态神经网络的核心。PyTorch Python 前端中的大部分 autograd API 在 C++ 前端中同样可用,因此可以轻松地把 Python 的 autograd 代码迁移到 C++。 本教程将通过几个示例演示如何在 PyTorch C++ 前端中使用 autograd。注意,本教程假设你已经对 Python 前端的 autograd 有基本了解。如果没有,请先阅读 Autograd: Automatic Differentiation。 ## 基本 autograd 操作 (改编自这篇教程) 创建一个张量,并设置 torch::requires_grad() 来跟踪其上的计算: ```cpp auto x = torch::ones({2, 2}, torch::requires_grad()); std::cout << x << std::endl; ``` 输出: ``` 1 1 1 1 [ CPUFloatType{2,2} ] ``` 对张量执行一个操作: ```cpp auto y = x + 2; std::cout << y << std::endl; ``` 输出: ``` 3 3 3 3 [ CPUFloatType{2,2} ] ``` y 是某个操作的结果,因此它带有 grad_fn: ```cpp std::cout << y.grad_fn()->name() << std::endl; ``` 输出: ``` AddBackward1 ``` 继续对 y 做更多操作: ```cpp auto z = y * y * 3; auto out = z.mean(); std::cout << z << std::endl; std::cout << z.grad_fn()->name() << std::endl; std::cout << out << std::endl; std::cout << out.grad_fn()->name() << std::endl; ``` 输出: ``` 27 27 27 27 [ CPUFloatType{2,2} ] MulBackward1 27 [ CPUFloatType{} ] MeanBackward0 ``` `.requires_grad_( ... )` 会原地修改已有张量的 requires_grad 标志。 ```cpp auto a = torch::randn({2, 2}); a = ((a * 3) / (a - 1)); std::cout << a.requires_grad() << std::endl; a.requires_grad_(true); std::cout << a.requires_grad() << std::endl; auto b = (a * a).sum(); std::cout << b.grad_fn()->name() << std::endl; ``` 输出: ``` false true SumBackward0 ``` 现在来反向传播。由于 out 只包含一个标量,out.backward() 等价于 out.backward(torch::tensor(1.)): ```cpp out.backward(); ``` 打印梯度 d(out)/dx: ```cpp std::cout << x.grad() << std::endl; ``` 输出: ``` 4.5000 4.5000 4.5000 4.5000 [ CPUFloatType{2,2} ] ``` 你应该会得到一个全为 4.5 的矩阵。关于这个值是如何得出的,请参见这篇教程中的相应章节。 接下来看一个向量-Jacobian 积(vector-Jacobian product)的例子: ```cpp x = torch::randn(3, torch::requires_grad()); y = x * 2; while (y.norm().item() < 1000) { y = y * 2; } std::cout << y << std::endl; std::cout << y.grad_fn()->name() << std::endl; ``` 输出: ``` -1021.4020 314.6695 -613.4944 [ CPUFloatType{3} ] MulBackward1 ``` 如果想计算向量-Jacobian 积,只需把向量作为参数传入 backward: ```cpp auto v = torch::tensor({0.1, 1.0, 0.0001}, torch::kFloat); y.backward(v); std::cout << x.grad() << std::endl; ``` 输出: ``` 102.4000 1024.0000 0.1024 [ CPUFloatType{3} ] ``` 你还可以阻止 autograd 对需要梯度的张量继续跟踪历史:一种方式是把 torch::NoGradGuard 放入代码块中: ```cpp std::cout << x.requires_grad() << std::endl; std::cout << x.pow(2).requires_grad() << std::endl; { torch::NoGradGuard no_grad; std::cout << x.pow(2).requires_grad() << std::endl; } ``` 输出: ``` true true false ``` 另一种方式是使用 .detach() 得到一个内容相同但不再需要梯度的新张量: ```cpp std::cout << x.requires_grad() << std::endl; y = x.detach(); std::cout << y.requires_grad() << std::endl; std::cout << x.eq(y).all().item() << std::endl; ``` 输出: ``` true false true ``` 关于 C++ 张量 autograd API(如 grad / requires_grad / is_leaf / backward / detach / detach_ / register_hook / retain_grad)的更多信息,请查阅相应的 C++ API 文档。 ## 在 C++ 中计算高阶梯度 高阶梯度的一个应用场景是计算梯度惩罚(gradient penalty)。下面用 torch::autograd::grad 来演示一个例子: ```cpp #include auto model = torch::nn::Linear(4, 3); auto input = torch::randn({3, 4}).requires_grad_(true); auto output = model(input); // Calculate loss auto target = torch::randn({3, 3}); auto loss = torch::nn::MSELoss()(output, target); // Use norm of gradients as penalty auto grad_output = torch::ones_like(output); auto gradient = torch::autograd::grad({output}, {input}, /*grad_outputs=*/{grad_output}, /*create_graph=*/true)[0]; auto gradient_penalty = torch::pow((gradient.norm(2, /*dim=*/1) - 1), 2).mean(); // Add gradient penalty to loss auto combined_loss = loss + gradient_penalty; combined_loss.backward(); std::cout << input.grad() << std::endl; ``` 输出: ``` -0.1042 -0.0638 0.0103 0.0723 -0.2543 -0.1222 0.0071 0.0814 -0.1683 -0.1052 0.0355 0.1024 [ CPUFloatType{3,4} ] ``` 关于 torch::autograd::backward 和 torch::autograd::grad 的更多用法,请参阅它们的文档。 ## 在 C++ 中使用自定义 autograd 函数 (改编自这篇教程) 给 torch::autograd 添加一个新的基础操作,需要为每个操作实现一个 torch::autograd::Function 子类。torch::autograd 正是借助 torch::autograd::Function 来计算结果和梯度、并记录操作历史的。每个新函数都需要实现两个方法:forward 和 backward,详细要求请参见这个链接。 下面是 torch::nn 中 Linear 函数的实现代码: ```cpp #include using namespace torch::autograd; // Inherit from Function class LinearFunction : public Function { public: // Note that both forward and backward are static functions // bias is an optional argument static torch::Tensor forward( AutogradContext *ctx, torch::Tensor input, torch::Tensor weight, torch::Tensor bias = torch::Tensor()) { ctx->save_for_backward({input, weight, bias}); auto output = input.mm(weight.t()); if (bias.defined()) { output += bias.unsqueeze(0).expand_as(output); } return output; } static tensor_list backward(AutogradContext *ctx, tensor_list grad_outputs) { auto saved = ctx->get_saved_variables(); auto input = saved[0]; auto weight = saved[1]; auto bias = saved[2]; auto grad_output = grad_outputs[0]; auto grad_input = grad_output.mm(weight); auto grad_weight = grad_output.t().mm(input); auto grad_bias = torch::Tensor(); if (bias.defined()) { grad_bias = grad_output.sum(0); } return {grad_input, grad_weight, grad_bias}; } }; ``` 然后可以这样使用 LinearFunction: ```cpp auto x = torch::randn({2, 3}).requires_grad_(); auto weight = torch::randn({4, 3}).requires_grad_(); auto y = LinearFunction::apply(x, weight); y.sum().backward(); std::cout << x.grad() << std::endl; std::cout << weight.grad() << std::endl; ``` 输出: ``` 0.5314 1.2807 1.4864 0.5314 1.2807 1.4864 [ CPUFloatType{2,3} ] 3.7608 0.9101 0.0073 3.7608 0.9101 0.0073 3.7608 0.9101 0.0073 3.7608 0.9101 0.0073 [ CPUFloatType{4,3} ] ``` 这里再给一个由非张量参数进行参数化的函数示例: ```cpp #include using namespace torch::autograd; class MulConstant : public Function { public: static torch::Tensor forward(AutogradContext *ctx, torch::Tensor tensor, double constant) { // ctx is a context object that can be used to stash information // for backward computation ctx->saved_data["constant"] = constant; return tensor * constant; } static tensor_list backward(AutogradContext *ctx, tensor_list grad_outputs) { // We return as many input gradients as there were arguments. // Gradients of non-tensor arguments to forward must be `torch::Tensor()`. return {grad_outputs[0] * ctx->saved_data["constant"].toDouble(), torch::Tensor()}; } }; ``` 然后可以这样使用 MulConstant: ```cpp auto x = torch::randn({2}).requires_grad_(); auto y = MulConstant::apply(x, 5.5); y.sum().backward(); std::cout << x.grad() << std::endl; ``` 输出: ``` 5.5000 5.5000 [ CPUFloatType{2} ] ``` 关于 torch::autograd::Function 的更多信息,请参阅其文档。 ## 把 autograd 代码从 Python 迁移到 C++ 总体来说,在 C++ 中使用 autograd 最简单的方法是:先在 Python 中写出可运行的 autograd 代码,然后参照下表把它翻译成 C++:
PythonC++
torch.autograd.backwardtorch::autograd::backward(链接)
torch.autograd.gradtorch::autograd::grad(链接)
torch.Tensor.detachtorch::Tensor::detach(链接)
torch.Tensor.detach_torch::Tensor::detach_(链接)
torch.Tensor.backwardtorch::Tensor::backward(链接)
torch.Tensor.register_hooktorch::Tensor::register_hook(链接)
torch.Tensor.requires_gradtorch::Tensor::requires_grad_(链接)
torch.Tensor.retain_gradtorch::Tensor::retain_grad(链接)
torch.Tensor.gradtorch::Tensor::grad(链接)
torch.Tensor.grad_fntorch::Tensor::grad_fn(链接)
torch.Tensor.set_datatorch::Tensor::set_data(链接)
torch.Tensor.datatorch::Tensor::data(链接)
torch.Tensor.output_nrtorch::Tensor::output_nr(链接)
torch.Tensor.is_leaftorch::Tensor::is_leaf(链接)
翻译之后,你的大部分 Python autograd 代码应该可以直接在 C++ 中运行。如果不行,请到 GitHub issues 提交 bug 报告,我们会尽快修复。 ## 总结 现在你应该对 PyTorch 的 C++ autograd API 有了整体了解。本文中的代码示例可以在这里找到。和以往一样,如果你遇到问题或有疑问,可以通过我们的论坛或 GitHub issues 联系我们。

评论 (0)