PyTorch 学习笔记(十一)
1 . 如果我们想要修改 tensor 的数值,但是又不希望被 autograd 记录,则使用 tensor.data 进行操作。
Input:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32import torch as t
a = t.ones(3, 4, requires_grad=True)
b = t.ones(3, 4, requires_grad=True)
c = a * b
print(a.data) # 还是同一个 tensor
print(a.data.requires_grad) # 但是已经独立于计算图之外了
d = a.data.sigmoid_() # sigmoid_ 是一个 inplace 操作,会修改 a 自身的值
print(a)
print(d.requires_grad)
print(a.requires_grad)
# 近似于 tensor = a.data,但是如果 tensor 被修改,backward 可能会报错
tensor = a.detach()
print(tensor.requires_grad)
# 统计 tensor 的一些指标,不希望被记录
mean = tensor.mean()
std = tensor.std()
maximum = tensor.max()
print(mean, std, maximum)
tensor[0] = 1
print(a)
# 下面会报错: RuntimeError: one of the variables needed for gradient
# computation has been modified by an inplace operation.
# 因为 c = a * b,b 的梯度取决于 a,现在修改了 tensor,其实也就是修改了 a,梯度不再准确
# c.sum().backward()
Output:
2 . 在反向传播过程中非叶子节点的导数计算完之后即被清空,若想查看这些变量的梯度,有两种方法:
- 使用
autograd.grad
函数; - 使用
hook
。
推荐使用 hook
方法,但是在实际应用中应尽量避免修改 grad 的值。
1 | import torch as t |
1 | import torch as t |
3 . 看看 variable 中的 grad 属性和 backward 函数 grad_variables 参数的含义。
- variable x 的梯度是目标函数 $f(x)$ 对 x 的梯度,$\frac{df(x)}{dx}=(\frac{df(x)}{dx_{0}},\frac{df(x)}{dx_{1}},…,\frac{df(x)}{dx_N})$,形状和 x 一致;
- 对于
y.backward(grad_variables)
中的grad_variables
相当于链式求导法则 $\frac{\partial{z}}{\partial{x}}=\frac{\partial{z}}{\partial{y}}\cdot \frac{\partial{y}}{\partial{x}}$ 中的 $\frac{\partial{z}}{\partial{y}}$,z 是目标函数,一般是一个标量,故而 $\frac{\partial{z}}{\partial{y}}$ 的形状与 variable y 的形状一致,z.backward()
在一定程度上等价于y.backward(grad_y)
。z.backward()
省略了grad_variables
参数,因为 z 是一个标量,而 $\frac{\partial{z}}{\partial{z}}=1$。
1 | import torch as t |
1 | import torch as t |
另外需要注意,只有对 variable 的操作才能使用 autograd,如果对 variable 的 data 直接进行操作,将无法使用反向传播,除了对参数初始化,一般我们不会修改 variable.data 的值。
总结
PyTorch 中计算图的特点可总结如下:
autograd
根据用户对 variable 的操作构建计算图,对变量的操作抽象为Function
;- 对于那些不是任何函数的输出,由用户创建的节点称为叶子节点,叶子节点的
grad_fn
为 None,叶子节点中需要求导的 variable,具有AccumulateGrad
标识,因其梯度是累加的; - variable 默认是不需要求导的,即
requires_grad
属性默认为 False,如果某一个节点requires_grad
被设置为True
,那么所有依赖它的节点requires_grad
都为True
; - variable 的
volatitle
属性默认为False
,如果某一个 variable 的volatitle
属性被设置为True
,那么所有依赖它的节点的volatitle
属性都为True
,volatitle
为True
的节点不会求导,volatitle
的优先级比requires_grad
高; - 多次反向传播时,梯度是累加的,反向传播的中间缓存会被清空,为进行多次反向传播需指定
retian_graph=True
来保存这些缓存; - 非叶子节点的梯度计算完之后即被清空,可以使用
autograd.grad
或hook
技术获取非叶子节点值; - variable 的 grad 与 data 形状一致,应避免直接修改 variable.data,因为对 data 的直接操作无法利用 autograd 进行反向传播;
- 反向传播函数
backward
的参数grad_variables
可以看成链式求导的中间结果,如果是标量,可以省略,默认为 1; - PyTorch 采用动态图设计,可以很方便地查看中间层的输出,动态地设计计算图结构。
4 . 目前绝大多数函数都可以使用 autograd
实现反向求导,但如果需要自己写一个复杂的函数,不支持自动反向求导怎么办?那就需要自己写一个 Function
,实现它的前向传播和反向传播代码。
此外实现了自己的 Function
之后,还可以使用 gradcheck
函数来检测实现是否正确,gradcheck
通过数值逼近来计算梯度,可能具有一定的误差,通过控制 eps
的大小可以控制容忍的误差。
1 | import torch as t |
1 | import torch as t |
笔记来源:《pytorch-book》