PyTorch自动求梯度

自动求梯度

前言

神经网络就是寻求一个拟合函数,但是因为参数过多,所以不得不借助每一点的梯度来一点一点的接近最佳的LOSS值,pytorch拥有动态的计算图,存储记忆对向量的每一个函数操作,最后通过反向传播来计算梯度,这可以说是pytorch的核心。

所以深入了解如果利用pytorch进行自动梯度计算非常重要。

深度学习模型的训练就是不断更新权值,权值的更新需要求解梯度。

PyTorch提供的autograd包能够根据输⼊
和前向传播过程自动构建计算图,并执行反向传播。

本节将介绍如何使⽤用autograd包来进行自动求梯度的有关操作。

概念

上一节介绍的 Tensor 是这个包的核心类,如果将其属性 .requires_grad 设置为 True ,它将开始追
踪(track)在其上的所有操作(这样就可以利用链式法则进行梯度传播了)。

完成计算后,可以调用 .backward() 来完成所有梯度计算。此 Tensor 的梯度将累积到 .grad 属性中。

如果不想要被继续追踪,可以调用 .detach() 将其从追踪记录中分离出来,这样就可以防止将来的计算被追踪,这样梯度就传不过去了。

此外,还可以用 with torch.no_grad() 将不想被追踪的操作代码块包裹起来,这种方法在评估模型的时候很常用,因为在评估模型时,我们并不需要计算可训练参数
requires_grad=True )的梯度。

Function 是另外一个很重要的类。 TensorFunction 互相结合就可以构建一个记录有整个计算过程的有向无环图(DAG)。

每个 Tensor 都有一个 .grad_fn 属性,该属性即创建该 TensorFunction , 就是说该 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_fnNone

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 的梯度 d ( o u t ) d x \frac{d(o u t)}{d x} dxd(out):

print(x.grad)

输出:

tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])

我们令 out o o o , 因为
o = 1 4 ∑ i = 1 4 z i = 1 4 ∑ i = 1 4 3 ( x i + 2 ) 2 o=\dfrac{1}{4}\sum_{i=1}^4z_i=\dfrac{1}{4}\sum_{i=1}^43(x_i+2)^2\quad\quad o=41i=14zi=41i=143(xi+2)2
所以
∂ o ∂ x i ∣ x i = 1 = 9 2 = 4.5 \quad\left.\dfrac{\partial o}{\partial x_i}\right|_{x_i=1}=\dfrac{9}{2}=4.5 xio xi=1=29=4.5

所以上面的输出是正确的。

量都为向量的函数 y ⃗ = f ( x ⃗ ) \vec{y}=f(\vec{x}) y =f(x ) , 那么 y ⃗ \vec{y} y 关于 x ⃗ \vec{x} x 的梯度就是⼀个雅可比矩阵(Jacobian matrix):
J = ( ∂ y 1 ∂ x 1 ⋯ ∂ y 1 ∂ x n ⋮ ⋱ ⋮ ∂ y m ∂ x 1 ⋯ ∂ y m ∂ x n ) J=\begin{pmatrix}\frac{\partial y_1}{\partial x_1}&\cdots&\frac{\partial y_1}{\partial x_n}\\ \vdots&\ddots&\vdots\\ \frac{\partial y_{m}}{\partial x_1}&\cdots&\frac{\partial y_{m}}{\partial x_n}\end{pmatrix} J= x1y1x1ymxny1xnym
⽽而 torch.autograd 这个包就是用来计算⼀些雅克比矩阵的乘积的。例如,如果 v v v是⼀个标量函数 l = g ( y ⃗ ) l=g\left(\vec{y}\right) l=g(y )
的梯度:
v = ( ∂ l ∂ y 1 ⋯ ∂ l ∂ y m ) v=\left(\begin{array}{cc}\frac{\partial l}{\partial y_1}&\cdots&\frac{\partial l}{\partial y_m}\end{array}\right)\quad\quad\quad v=(y1lyml)
那么根据链式法则我们有 l l l关于 x ⃗ \vec{x} x 的雅克比矩阵就为:
v J = ( ∂ t ∂ y 1 ⋯ ∂ t ∂ y n ) ( ∂ y 1 ∂ x 1 ⋯ ∂ y n ∂ x n ⋮ ⋱ ⋮ ∂ y n ∂ x 1 ⋯ ∂ y n ∂ x n ) = ( ∂ t ∂ x 1 ⋯ ∂ t ∂ x n ) vJ=\begin{pmatrix}\frac{\partial t}{\partial y_1}&\cdots&\frac{\partial t}{\partial y_n}\end{pmatrix}\begin{pmatrix}\frac{\partial y_1}{\partial x_1}&\cdots&\frac{\partial y_n}{\partial x_n}\\ \vdots&\ddots&\vdots\\ \frac{\partial y_n}{\partial x_1}&\cdots&\frac{\partial y_n}{\partial x_n}\end{pmatrix}=\begin{pmatrix}\frac{\partial t}{\partial x_1}&\cdots&\frac{\partial t}{\partial x_n}\end{pmatrix} vJ=(y1tynt) x1y1x1ynxnynxnyn =(x1txnt)

注意: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.])
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值