【one way的pytorch学习笔记】(二)autograd自动求导

本文深入解析PyTorch的autograd模块,探讨如何通过.requires_grad属性追踪张量操作,实现自动求导。示例展示了标量和非标量结果的梯度计算,以及雅可比向量积的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

autograd 是 pytorch 构建神经网络的核心

  1. 当把tensor的属性 .requires_grad置为True时,pytorch则会追踪在这个tensor上的接下来的所有操作。然后当使用.backward()时,将会自动求导。所有分支上的梯度值将会被累计在 .grad中
  2. 可以使用with torch.no_grad(): 来暂停梯度记录(可以用在evaluation中)
  3. 用.backward()求导
tensor创建
例 1
import torch

x = torch.ones(2, 2, requires_grad=True)
print(x)
y = x + 2
print(y)

结果:这里x的requires_grad 设置成为 True,因此pytorch将会记录x的所有相关的计算流程,方便求微分

tensor([[1., 1.],
        [1., 1.]], requires_grad=True)
y = x + 2
print(y)

由于运算过程中x 已经设为requires_grad, 在进行 y的赋值运算时 x执行了加法运算。而在y中的grad_fn不为空,说明已经包含了当执行back propagation 时,y所对应的backward函数,这个函数存在y.grad_fn中。

tensor([[3., 3.],
        [3., 3.]], grad_fn=<AddBackward0>)

yx2相加生成,查看一下y.grad_fn,发现x与2对应的backward 函数被打包成名为next_function的tuple类,其中第一项对应的即为x的backward相关函数,第二项对应的即为2对应的相关函数,由于对实数求导得0,因此不含backward函数。

z = y * y * 3
out = z.mean()

print(z, out)
例 2
a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)

结果:可知在不指定a的时候,默认a的requires_grad 为 False,当声明a后,可以通过in-place操作.requires_grad_( ... )来改变状态

False
True
<SumBackward0 object at 0x7f341c47cd68>
自动求梯度
例 1 结果为标量时
x = torch.ones(2, 2, requires_grad=True)
y = x + 2
z = y * y * 3
out = z.mean()
#由于最终的结果只是单个标量
#out.backward()等于 out.backward(torch.tensor(1.))
out.backward()
print(x.grad)

结果:对out进行backprop,out关于x的导数存在了x的.grad中,所以直接print(x.grad)

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

整个公式:
z i = 3 ( x i + 2 ) 2 o = 1 4 ∑ i z i z_i = 3(x_i+2)^2 \\ o = \frac{1}{4}\sum_i z_i zi=3(xi+2)2o=41izi

z i ∣ x i = 1 = 27 z_i\bigr\rvert_{x_i=1} = 27 zixi=1=27,对 o o o x x 的偏导 ∂ o ∂ x i = 3 2 ( x i + 2 ) \frac{\partial o}{\partial x_i} = \frac{3}{2}(x_i+2) xio=23(xi+2),带入得 ∂ o ∂ x i ∣ x i = 1 = 9 2 = 4.5 \frac{\partial o}{\partial x_i}\bigr\rvert_{x_i=1} = \frac{9}{2} = 4.5 xioxi=1=29=4.5,其实本质上这里的求导是一种对应的关系,标量的结果对应参数x中每个值的相应的梯度.

数学上,若有向量值函数 y ⃗ = f ( x ⃗ ) \vec{y}=f(\vec{x}) y =f(x ),那么 y ⃗ \vec{y} y 相对于 x ⃗ \vec{x} x 的梯度是一个雅可比矩阵:
J = ( ∂ y 1 ∂ x 1 ⋯ ∂ y m ∂ x 1   ⋮ ⋱ ⋮   ∂ y 1 ∂ x n ⋯ ∂ y m ∂ x n ) J=\left(\begin{array}{ccc} \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{1}}\ \vdots & \ddots & \vdots\ \frac{\partial y_{1}}{\partial x_{n}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}} \end{array}\right) J=(x1y1x1ym  xny1xnym)
通常来说,torch.autograd 是计算雅可比向量积的一个“引擎”。也就是说,给定任意向量 v = ( v 1 v 2 ⋯ v m ) T v=\left(\begin{array}{cccc} v_{1} & v_{2} & \cdots & v_{m}\end{array}\right)^{T} v=(v1v2vm)T,计算乘积 v T ⋅ J v^{T}\cdot J vTJ如果 v v v 恰好是一个标量函数 l = g ( y ⃗ ) l=g\left(\vec{y}\right) l=g(y ) 的导数,即 v = ( ∂ l ∂ y 1 ⋯ ∂ l ∂ y m ) T v=\left(\begin{array}{ccc}\frac{\partial l}{\partial y_{1}} & \cdots & \frac{\partial l}{\partial y_{m}}\end{array}\right)^{T} v=(y1lyml)T,那么根据链式法则,雅可比向量积应该是 l l l x ⃗ \vec{x} x 的导数:
J T ⋅ v = ( ∂ y 1 ∂ x 1 ⋯ ∂ y m ∂ x 1   ⋮ ⋱ ⋮   ∂ y 1 ∂ x n ⋯ ∂ y m ∂ x n ) ( ∂ l ∂ y 1   ⋮   ∂ l ∂ y m ) = ( ∂ l ∂ x 1   ⋮   ∂ l ∂ x n ) J^{T}\cdot v=\left(\begin{array}{ccc} \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{1}}\ \vdots & \ddots & \vdots\ \frac{\partial y_{1}}{\partial x_{n}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}} \end{array}\right)\left(\begin{array}{c} \frac{\partial l}{\partial y_{1}}\ \vdots\ \frac{\partial l}{\partial y_{m}} \end{array}\right)=\left(\begin{array}{c} \frac{\partial l}{\partial x_{1}}\ \vdots\ \frac{\partial l}{\partial x_{n}} \end{array}\right) JTv=(x1y1x1ym  xny1xnym)(y1l  yml)=(x1l  xnl)
注意:行向量的 v T ⋅ J v^{T}\cdot J vTJ也可以被视作列向量的 J T ⋅ v J^{T}\cdot v JTv
摘自:pytorch中文github

雅可比向量积的这一特性使得将外部梯度输入到具有非标量输出的模型中变得非常方便。

例 2 结果不为标量时
x = torch.randn(3, requires_grad=True)

y = x * 2
while y.data.norm() < 1000:
    y = y * 2

print(y)
tensor([1444.3817, -340.2753, -480.7858], grad_fn=<MulBackward0>)

此时torch.autograd不能直接计算完整的Jacobian矩阵(就是pytorch能力不足).但此时如果想计算vector-Jacobian乘积,只需要把向量加到backward中作为参数即可.

v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)

print(x.grad)
tensor([4.0960e+02, 4.0960e+03, 4.0960e-01])

此处我觉得官方的例子不好,本身大家就不太懂,还非得生成随机的tensor,还加入不确定次数的2的累积.让人完全不知道算出来的x.grad代表着什么

重新举个例子

x = torch.tensor([1.,2.,3.], requires_grad=True)
y = x * 2
print(y)
v = torch.tensor([2,1,1],dtype=torch.float)
y.backward(v)
print(x.grad)
tensor([2., 4., 6.], grad_fn=<MulBackward0>)
tensor([4., 2., 2.])

结果:可见这里y本身可以看成三个x与2相乘的结果的集合而成的向量. 在这里的v相当于给了结果向量一个权重,生成对应的关于x的导数.如果想要正常结果,直接把输入的向量v改成[1,1,1]就好了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值