#梯度和反向传播
本文学习自莫烦教程
import torch
# 导入autograd包进行梯度运算
from torch.autograd import Variable
tensor = torch.FloatTensor([[1, 2], [3, 4]])
# variable是计算图的一部分,是自动梯度计算的一部分
variable = Variable(tensor, requires_grad=True)
print(tensor)
print(variable)
tensor([[1., 2.],
[3., 4.]])
tensor([[1., 2.],
[3., 4.]], requires_grad=True)
t_out = torch.mean(tensor*tensor)
v_out = torch.mean(variable*variable)
print(t_out)
print(v_out)
tensor(7.5000)
tensor(7.5000, grad_fn=<MeanBackward0>)
# 计算得出的v_out可以进行反向传播
v_out.backward()
下式是
v
o
u
t
v_{out}
vout的计算公式,在此基础上可以进行反向传播并且求出variable的梯度
v
o
u
t
=
1
4
s
u
m
(
v
a
r
i
a
b
l
e
2
)
v_{out} = \frac{1}{4}sum(variable^2)
vout=41sum(variable2)
# 得出variable的梯度
variable.grad
tensor([[0.5000, 1.0000],
[1.5000, 2.0000]])
关于autograd的一些补充:
x = torch.randn((2, 2))
y = torch.randn((2, 2))
var_x = Variable(x, requires_grad=True)
var_y = Variable(y, requires_grad=True)
print(var_x.grad_fn) # None
var_z = var_x + var_y
print(var_z.grad_fn) # <AddBackward0 object at 0x00000166200D1A90>
x = torch.ones(2, 2, requires_grad=True)
print(x)
print(x.grad_fn) # None
y = x + 2
print(y)
print(y.grad_fn) # <AddBackward0 object at 0x000001341DB67E80>
由上面的代码可以知道,var_x是由用户自己指定的,不具有grad_fn属性,而var_z是由var_x和var_y计算得来的,所以具备grad_fn属性。
梯度计算
z = y * y * 3
out = z.mean()
print(z)
print(out) # tensor(27., grad_fn=<MeanBackward0>)
out.backward()
print(x.grad)
# tensor([[4.5000, 4.5000],
#[4.5000, 4.5000]])
out是个标量,所以.backward()方法中不需要参数,可以直接进行反向传播,out的计算公式如下,
o
u
t
=
1
4
∑
i
=
1
4
3
(
x
+
2
)
2
out=\frac{1}{4}\sum_{i=1}^4{3(x+2)^2}
out=41i=1∑43(x+2)2
求out对x的导数,
d
(
o
u
t
)
d
x
i
∣
x
i
=
1
=
1
4
⋅
6
(
x
i
+
2
)
∣
x
i
=
1
=
4.5
\frac{d(out)}{dx_i}|x_{i=1}=\frac{1}{4}\cdot6(x_i+2)|x_{i=1}=4.5
dxid(out)∣xi=1=41⋅6(xi+2)∣xi=1=4.5
中断梯度追踪的例子
x = torch.tensor(1.0, requires_grad=True)
y1 = x ** 2
with torch.no_grad():
y2 = x ** 3
y3 = y1 + y2
print(x.requires_grad)
print(y1, y1.requires_grad) # True
print(y2, y2.requires_grad) # False
print(y3, y3.requires_grad) # True
y3.backward()
print(x.grad) # tensor(2.)
和y2有关的梯度不会回传,所以只计算和y1有关的梯度。
如果我们想要修改tensor的数值,但是又不希望被autograd记录(即不会影响反向传播),那么我么可以对tensor.data进行操作。
x = torch.ones(1,requires_grad=True)
print(x.data) # tensor([1.]),还是一个tensor
print(x.data.requires_grad) # False,证明已经是独立于计算图之外
y = 2 * x
x.data *= 100 # 只改变了值,不会记录在计算图,所以不会影响梯度传播
y.backward()
print(x) # tensor([100.], requires_grad=True),更改data的值也会影响tensor的值
print(x.grad) # tensor([2.]),不会影响梯度传播