PyTorch 入门指南
学习 PyTorch
图像和视频
音频
后端
强化学习
在生产环境中部署 PyTorch 模型
Profiling PyTorch
代码变换与FX
前端API
扩展 PyTorch
模型优化
并行和分布式训练
边缘端的 ExecuTorch
推荐系统
多模态

C++ 前端中的 Autograd

autograd 包在 PyTorch 中对于构建高度灵活和动态的神经网络至关重要。PyTorch Python 前端中的大多数 autograd API 在 C++ 前端中同样可用,这使得将 autograd 代码从 Python 迁移到 C++ 变得容易。

在本教程中,我们将探索几个在 PyTorch C++ 前端中执行 autograd 的示例。请注意,本教程假设您已经对 Python 前端中的 autograd 有了基本的了解。如果情况并非如此,请先阅读 Autograd: 自动求导

基本 autograd 操作

(改编自本教程

创建一个张量并设置 torch::requires_grad() 以跟踪其计算过程

autox=torch::ones({2,2},torch::requires_grad());
std::cout<<x<<std::endl;

输出:

11
11
[CPUFloatType{2,2}]

执行张量操作:

autoy=x+2;
std::cout<<y<<std::endl;

Out:

33
33
[CPUFloatType{2,2}]

y 是由某个操作生成的,因此它有一个 grad_fn

std::cout<<y.grad_fn()->name()<<std::endl;

输出:

AddBackward1

y 进行更多操作

autoz=y*y*3;
autoout=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;

输出:

2727
2727
[CPUFloatType{2,2}]
MulBackward1
27
[CPUFloatType{}]
MeanBackward0

.requires_grad_( ... ) 会就地修改现有张量的 requires_grad 标志。

autoa=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;

autob=(a*a).sum();
std::cout<<b.grad_fn()->name()<<std::endl;

Out:

false
true
SumBackward0

现在让我们进行反向传播。由于 out 包含一个标量,out.backward() 等同于 out.backward(torch::tensor(1.))

out.backward();

打印梯度 d(out)/dx

std::cout<<x.grad()<<std::endl;

输出:

4.50004.5000
4.50004.5000
[CPUFloatType{2,2}]

您应该得到了一个 4.5 的矩阵。关于我们如何得出这个值的解释,请参见本教程中的相应章节

现在让我们来看一个向量-雅可比积的例子:

x=torch::randn(3,torch::requires_grad());

y=x*2;
while(y.norm().item<double>()<1000){
y=y*2;
}

std::cout<<y<<std::endl;
std::cout<<y.grad_fn()->name()<<std::endl;

Out:

*1021.4020
314.6695
*613.4944
[CPUFloatType{3}]
MulBackward1

如果我们想要计算向量-雅可比积,将向量作为参数传递给 backward 方法:

autov=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}]

您还可以通过将torch::NoGradGuard放入代码块中,停止autograd跟踪需要梯度的张量的历史记录。

std::cout<<x.requires_grad()<<std::endl;
std::cout<<x.pow(2).requires_grad()<<std::endl;

{
torch::NoGradGuardno_grad;
std::cout<<x.pow(2).requires_grad()<<std::endl;
}

Out:

true
true
false

或者使用 .detach() 方法获取一个具有相同内容但不需梯度的新张量:

std::cout<<x.requires_grad()<<std::endl;
y=x.detach();
std::cout<<y.requires_grad()<<std::endl;
std::cout<<x.eq(y).all().item<bool>()<<std::endl;

输出:

true
false
true

有关 grad / requires_grad / is_leaf / backward / detach / detach_ / register_hook / retain_grad 等 C++ 张量自动求导 API 的更多信息,请参阅 相应的 C++ API 文档

在 C++ 中计算高阶梯度

高阶梯度的一个应用是计算梯度惩罚。让我们通过 torch::autograd::grad 来看一个示例:

#include<torch/torch.h>

automodel=torch::nn::Linear(4,3);

autoinput=torch::randn({3,4}).requires_grad_(true);
autooutput=model(input);

// Calculate loss
autotarget=torch::randn({3,3});
autoloss=torch::nn::MSELoss()(output,target);

// Use norm of gradients as penalty
autograd_output=torch::ones_like(output);
autogradient=torch::autograd::grad({output},{input},/*grad_outputs=*/{grad_output},/*create_graph=*/true)[0];
autogradient_penalty=torch::pow((gradient.norm(2,/*dim=*/1)-1),2).mean();

// Add gradient penalty to loss
autocombined_loss=loss+gradient_penalty;
combined_loss.backward();

std::cout<<input.grad()<<std::endl;

Out:

*0.1042-0.06380.01030.0723
*0.2543-0.12220.00710.0814
*0.1683-0.10520.03550.1024
[CPUFloatType{3,4}]

请参阅 torch::autograd::backward (链接) 和 torch::autograd::grad (链接) 的文档,了解如何使用它们。

在 C++ 中使用自定义自动求导函数

(改编自本教程)

torch::autograd添加新的基本操作需要为每个操作实现一个新的torch::autograd::Function子类。torch::autograd::Functiontorch::autograd用于计算结果和梯度的工具,并编码操作历史。每个新函数都需要实现两个方法:forwardbackward,详细要求请参见此链接

以下您可以找到来自torch::nnLinear函数的代码:

#include<torch/torch.h>

usingnamespacetorch::autograd;

// Inherit from Function
classLinearFunction:publicFunction<LinearFunction>{
public:
// Note that both forward and backward are static functions

// bias is an optional argument
statictorch::Tensorforward(
AutogradContext*ctx,torch::Tensorinput,torch::Tensorweight,torch::Tensorbias=torch::Tensor()){
ctx->save_for_backward({input,weight,bias});
autooutput=input.mm(weight.t());
if(bias.defined()){
output+=bias.unsqueeze(0).expand_as(output);
}
returnoutput;
}

statictensor_listbackward(AutogradContext*ctx,tensor_listgrad_outputs){
autosaved=ctx->get_saved_variables();
autoinput=saved[0];
autoweight=saved[1];
autobias=saved[2];

autograd_output=grad_outputs[0];
autograd_input=grad_output.mm(weight);
autograd_weight=grad_output.t().mm(input);
autograd_bias=torch::Tensor();
if(bias.defined()){
grad_bias=grad_output.sum(0);
}

return{grad_input,grad_weight,grad_bias};
}
};

然后,我们可以按以下方式使用 LinearFunction

autox=torch::randn({2,3}).requires_grad_();
autoweight=torch::randn({4,3}).requires_grad_();
autoy=LinearFunction::apply(x,weight);
y.sum().backward();

std::cout<<x.grad()<<std::endl;
std::cout<<weight.grad()<<std::endl;

Out:

0.53141.28071.4864
0.53141.28071.4864
[CPUFloatType{2,3}]
3.76080.91010.0073
3.76080.91010.0073
3.76080.91010.0073
3.76080.91010.0073
[CPUFloatType{4,3}]

这里,我们提供了一个额外示例,展示了一个由非张量参数化定义的函数:

#include<torch/torch.h>

usingnamespacetorch::autograd;

classMulConstant:publicFunction<MulConstant>{
public:
statictorch::Tensorforward(AutogradContext*ctx,torch::Tensortensor,doubleconstant){
// ctx is a context object that can be used to stash information
// for backward computation
ctx->saved_data["constant"]=constant;
returntensor*constant;
}

statictensor_listbackward(AutogradContext*ctx,tensor_listgrad_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

autox=torch::randn({2}).requires_grad_();
autoy=MulConstant::apply(x,5.5);
y.sum().backward();

std::cout<<x.grad()<<std::endl;

Out:

5.5000
5.5000
[CPUFloatType{2}]

有关 torch::autograd::Function 的更多信息,请参阅其文档

将自动求导代码从 Python 转换为 C++

从高层次来看,在 C++ 中使用 autograd 的最简单方法是先在 Python 中编写可用的 autograd 代码,然后使用下表将您的 autograd 代码从 Python 转换为 C++:

Python C++
torch.autograd.backward torch::autograd::backward(link)
torch.autograd.grad torch::autograd::grad(link)
torch.Tensor.detach torch::Tensor::detach(
```[link](https://pytorch.org/cppdocs/api/classat_1_1_tensor.html#_CPPv4NK2at6Tensor6detachEv))                                                                                                                                                      |
| `torch.Tensor.detach_`       | `torch::Tensor::detach_`([link](https://pytorch.org/cppdocs/api/classat_1_1_tensor.html#_CPPv4NK2at6Tensor7detach_Ev))                                                                                                                                                             |
| `torch.Tensor.backward`      | `torch::Tensor::backward`([link](https://pytorch.org/cppdocs/api/classat_1_1_tensor.html#_CPPv4NK2at6Tensor8backwardERK6Tensorbb))                                                                                                                                                 |
| `torch.Tensor.register_hook` | `torch::Tensor::register_hook`([link](https://pytorch.org/cppdocs/api/classat_1_1_tensor.html#_CPPv4I0ENK2at6Tensor13register_hookE18hook_return_void_tI1TERR1T))                                                                                                                  |
| `torch.Tensor.requires_grad` | `torch::Tensor::requires_grad_``torch::Tensor::requires_grad_`
(
[link](https://pytorch.org/cppdocs/api/classat_1_1_tensor.html#_CPPv4NK2at6Tensor14requires_grad_Eb)[link](https://pytorch.org/cppdocs/api/classat_1_1_tensor.html#_CPPv4NK2at6Tensor14requires_grad_Eb)) |
| `torch.Tensor.retain_grad`   | `torch::Tensor::retain_grad`([link](https://pytorch.org/cppdocs/api/classat_1_1_tensor.html#_CPPv4NK2at6Tensor11retain_gradEv))                                                                                                                                                    |
| `torch.Tensor.grad`          | `torch::Tensor::grad`([link](https://pytorch.org/cppdocs/api/classat_1_1_tensor.html#_CPPv4NK2at6Tensor4gradEv))                                                                                                                                                                   |
| `torch.Tensor.grad_fn`       | `torch::Tensor::grad_fn`([link](https://pytorch.org/cppdocs/api/classat_1_1_tensor.html#_CPPv4NK2at6Tensor7grad_fnEv))                                                                                                                                                             |
| `torch.Tensor.set_data`      | `torch::Tensor::set_data``torch::Tensor::set_data`[link](https://pytorch.org/cppdocs/api/classat_1_1_tensor.html#_CPPv4NK2at6Tensor8set_dataERK6Tensor))                                                                                                                           |
| `torch.Tensor.data`          | `torch::Tensor::data`([link](https://pytorch.org/cppdocs/api/classat_1_1_tensor.html#_CPPv4NK2at6Tensor4dataEv))                                                                                                                                                                   |
| `torch.Tensor.output_nr`     | `torch::Tensor::output_nr`([link](https://pytorch.org/cppdocs/api/classat_1_1_tensor.html#_CPPv4NK2at6Tensor9output_nrEv))                                                                                                                                                         |
| `torch.Tensor.is_leaf`       | `torch::Tensor::is_leaf`([link](https://pytorch.org/cppdocs/api/classat_1_1_tensor.html#_CPPv4NK2at6Tensor7is_leafEv))

(注意:此处是一个闭合的括号,通常用于表示代码块或链接的结束,无需翻译)                                                                                                                       |

翻译后,您的 Python autograd 代码大部分应该可以直接在 C++ 中运行。如果遇到问题,请在 [GitHub issues](https://github.com/pytorch/pytorch/issues) 提交错误报告,我们将尽快修复。

<!--/div--><!--div class='modoc-hide-section'-->

## 结论

您现在应该对 PyTorch 的 C++ 自动求导 API 有了一个全面的了解。您可以在[这里](https://github.com/pytorch/examples/tree/master/cpp/autograd)找到本文中展示的代码示例。一如既往,如果您遇到任何问题或有疑问,可以通过我们的[论坛](https://discuss.pytorch.org/)或 [GitHub issues](https://github.com/pytorch/pytorch/issues)与我们联系。

<!--/div-->

<!--/div-->
本页目录