# 自动求导机制

1.  [从后向中排除子图](#excluding-subgraphs-from-backward)
    *   [requires_grad](#requires_grad)
    *   [volatile](#volatile)
2.  [自动求导如何编码历史信息](#how-autograd-encodes-the-history)
3.  [Variable 上的 In-place 操作](#in-place-operations-on-variables)
4.  [In-place 正确性检查](#in-place-correctness-checks)

* * *

本说明将概述 Autograd 如何工作并记录操作。没有必要全部了解，但建议您熟悉它，他可以将帮助你编写程序更高效，更清洁；同时还可以帮助您进行调试。

### 向后排除子视图：

每个变量都有一个标记：`requires_grad`允许从梯度计算中细分排除子图，并可以提高效率。

#### requires_grad

如果一个输入变量定义`requires_grad`,那么他的输出也可以使用`requires_grad`；相反，只有当所有的输入变量都不定义`requires_grad`梯度，才不会输出梯度。如果其中所有的变量都不需要计算梯度，在子图中从不执行向后计算。

```py
>>> x = Variable(torch.randn(5, 5))
>>> y = Variable(torch.randn(5, 5))
>>> z = Variable(torch.randn(5, 5), requires_grad=True)
>>> a = x + y
>>> a.requires_grad
False
>>> b = a + z
>>> b.requires_grad
True 
```

当您想要冻结部分模型时，这个标志特别有用;除非您事先知道不会使用到某些参数的梯度。

例如，如果您想调整预训练的`CNN`，只要切换冻结模型中的`requires_grad`标志即可，直到计算到最后一层才会保存中间缓冲区，仿射变换和网络输出都需要使用梯度的权值。

```py
model = torchvision.models.resnet18(pretrained=True)
for param in model.parameters():
    param.requires_grad = False
# Replace the last fully-connected layer
# Parameters of newly constructed modules have requires_grad=True by default
model.fc = nn.Linear(512, 100)

# Optimize only the classifier
optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9) 
```

### autograd 如何编码历史信息：

每个变量都有一个`.creator`属性，它指向把它作为输出的函数。这是一个由`Function`对象作为节点组成的有向无环图（DAG）的入口点，它们之间的引用就是图的边。每次执行一个操作时，一个表示它的新`Function`就被实例化，它的`forward()`方法被调用，并且它输出的`Variable`的创建者被设置为这个`Function`。然后，通过跟踪从任何变量到叶节点的路径，可以重建创建数据的操作序列，并自动计算梯度。

需要注意的一点是，整个图在每次迭代时都是从头开始重新创建的，这就允许使用任意的 Python 控制流语句，这样可以在每次迭代时改变图的整体形状和大小。在启动训练之前不必对所有可能的路径进行编码—— what you run is what you differentiate.

### Variable 上的 In-place 操作：

支持自动归档中的就地操作是一件很困难的事情，我们在大多数情况下都不鼓励使用它们。Autograd 的积极缓冲区释放和重用使其非常高效，并且在现场操作实际上会降低内存使用量的情况下，极少数场合很少。除非您在内存压力很大的情况下运行，否则您可能永远不需要使用它们。

限制现场操作适用性的两个主要原因：

1.  覆盖计算梯度所需的值。这就是为什么变量不支持`log_`。其梯度公式需要原始输入，而通过计算逆运算可以重新创建它，它在数值上是不稳定的，并且需要额外的工作，这往往会失败使用这些功能的目的。
2.  每个`in-place`操作实际上需要实现重写计算图。不合适的版本只需分配新对象，并保留对旧图的引用，而`in-place`操作则需要将所有输入的`creator`更改为`Function`表示此操作。这就比较棘手，特别是如果有许多变量引用相同的存储（例如通过索引或转置创建的），并且如果被修改输入的存储被其他`Variable`引用，则`in-place`函数实际上会抛出错误。

### In-place 正确性检测：

每个变量都保留一个版本计数器`version counter`，当在任何操作中被使用时，它都会递增。当函数保存任何用于后向的`tensor`时，还会保存其包含变量的版本计数器`version counter`。一旦访问，`self.saved_tensors`它被会被检查，如果它大于保存的值，则会引起错误。

### 译者署名

| 用户名 | 头像 | 职能 | 签名 |
| --- | --- | --- | --- |
| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 |