自动求梯度
“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情”
前言
神经网络就是寻求一个拟合函数,但是因为参数过多,所以不得不借助每一点的梯度来一点一点的接近最佳的LOSS值,pytorch拥有动态的计算图,存储记忆对向量的每一个函数操作,最后通过反向传播来计算梯度,这可以说是pytorch的核心。
所以深入了解如果利用pytorch进行自动梯度计算非常重要。
深度学习模型的训练就是不断更新权值,权值的更新需要求解梯度。
PyTorch提供的
autograd包能够根据输⼊ 和前向传播过程自动构建计算图,并执行反向传播。
本节将介绍如何使⽤用autograd包来进行自动求梯度的有关操作。
概念
上一节介绍的 Tensor 是这个包的核心类,如果将其属性 .requires_grad 设置为 True ,它将开始追
踪(track)在其上的所有操作(这样就可以利用链式法则进行梯度传播了)。
完成计算后,可以调用 .backward() 来完成所有梯度计算。此 Tensor 的梯度将累积到 .grad 属性中。
如果不想要被继续追踪,可以调用 .detach() 将其从追踪记录中分离出来,这样就可以防止将来的计算被追踪,这样梯度就传不过去了。
此外,还可以用 with torch.no_grad() 将不想被追踪的操作代码块包裹起来,这种方法在评估模型的时候很常用,因为在评估模型时,我们并不需要计算可训练参数
( requires_grad=True )的梯度。
Function 是另外一个很重要的类。 Tensor 和 Function 互相结合就可以构建一个记录有整个计算过程的有向无环图(DAG)。
每个 Tensor 都有一个 .grad_fn 属性,该属性即创建该 Tensor 的Function , 就是说该 Tensor 是不是通过某些运算得到的,若是,则 grad_fn 返回一个与这些运算相关的对象,否则是None。
下面通过⼀些例子来理解这些概念。
Tensor
创建⼀一个 Tensor 并设置 requires_grad=True :
x = torch.ones(2, 2, requires_grad=True)
print(x)
print(x.grad_fn)
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
None
再做一下运算操作:
y = x + 2
print(y)
print(y.grad_fn)
输出:
tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward>)
<AddBackward object at 0x1100477b8>
注意x是直接创建的,所以它没有
grad_fn, ⽽而y是通过一个加法操作创建的,所以它有一个为<AddBackward>的grad_fn。
像x这种直接创建的称为叶子节点,叶子节点对应的 grad_fn 是 None 。
print(x.is_leaf, y.is_leaf) # True False
关于复杂度运算操作:
z = y * y * 3
out = z.mean()
print(z, out)
输出:
tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward>) tensor(27., grad_fn=
<MeanBackward1>
通过 .requires_grad_() 来用in-place(就地操作)的方式改变 requires_grad 属性:
a = torch.randn(2, 2) # 缺失情况下默认 requires_grad = False
a = ((a * 3) / (a - 1))
print(a.requires_grad) # False
a.requires_grad_(True)
print(a.requires_grad) # True
b = (a * a).sum()
print(b.grad_fn)
输出:
False
True
<SumBackward0 object at 0x118f50cc0>
梯度
因为 out 是一个标量,所以调用 backward() 时不需要指定求导变量:
out.backward() # 等价于 out.backward(torch.tensor(1.))
我们来看看 out 关于 x 的梯度:
print(x.grad)
输出:
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
我们令 out 为 , 因为
所以
所以上面的输出是正确的。
量都为向量的函数 , 那么 关于的梯度就是⼀个雅可比矩阵(Jacobian matrix):
⽽而 torch.autograd 这个包就是用来计算⼀些雅克比矩阵的乘积的。例如,如果是⼀个标量函数
的梯度:
那么根据链式法则我们有关于的雅克比矩阵就为:
注意:grad在反向传播过程中是累加的(accumulated),这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以一般在反向传播之前需把梯度清零。
# 再来反向传播⼀一次,注意grad是累加的
out2 = x.sum()
out2.backward()
print(x.grad)
out3 = x.sum()
x.grad.data.zero_()
out3.backward()
print(x.grad)
输出为:
tensor([[5.5000, 5.5000],
[5.5000, 5.5000]])
tensor([[1., 1.],
[1., 1.]])
此外,如果我们想要修改 tensor 的数值,但是又不希望被 autograd 记录(即不会影响反向传播),
那么我们可以对 tensor.data 进行操作。
x = torch.ones(1,requires_grad=True)
print(x.data) # 还是⼀一个tensor
print(x.data.requires_grad) # 但是已经是独⽴立于计算图之外
y = 2 * x
x.data *= 100 # 只改变了了值,不不会记录在计算图,所以不不会影响梯度传播
y.backward()
print(x) # 更更改data的值也会影响tensor的值
print(x.grad)
输出为:
tensor([1.])
False
tensor([100.], requires_grad=True)
tensor([2.])