PyTorch深度学习60分钟快速上手(二),自动微分。

本文围绕Pytorch中的自动微分展开。介绍了Pytorch是先运行后定义的网络框架,其包为tensor操作提供自动微分功能。阐述了张量可跟踪操作并计算梯度,还介绍了停止追踪的方法。同时讲解了梯度的反向传播计算,以及向量 - 雅可比点积在计算非标量输出梯度时的应用。

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

机器工匠

自动微分

Pytorch中所有神经网络的核心是autograd包,我们先简单的来了解下这个包,然后来训练第一个神经网络。

autograd包为tensor上所有操作提供了自动微分功能。Pytorch是一个先运行后定义(define-by-run)的网络框架,是一种动态网络图结构,因此代码如何运行决定了如何计算反向传播,并且每次迭代,反向传播都可能不同。

张量(Tensor)

torch.Tensorautograd包的核心,如果把.requires_grad设置为True,那么它就会跟踪tensor上的所有操作,计算完之后调用.backward()自动计算梯度,这个tensor的梯度将会累积到.grad属性中。

如果要停止tensor追踪操作历史,可以调用.detach(),这样在以后的计算中阻止计算操作被追踪。

停止追踪操作历史同样还可以将代码块放进with torch.no_grad():中,这种做法在模型评估时候非常有帮助,因为模型可能包含requires_grad=True的可训练参数,但是我们并不需要它们的梯度值。

自动微分实现中还有一个非常重要类—— Function

TensorFunction是互联的,它们组成一个非循环图,记录了完整的计算过程。每个tensor都有一个.grad_fn属性指向了创建这个TensorFunction(用户自己创建的Tensor类型除外,它们的grad_fnNone)。

如果想要计算Tensor的导数,可以调用.backward()。如果tensor是标量(即它包含一个元素),那么不需要为backward()指定任何参数,如果有多个元素,那么就需要指定梯度参数。

现在我们创建一个tensor并设置requires_grad=True来追踪计算。

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

结果:

tensor([[1., 1.],
        [1., 1.]], requires_grad=True)

再来做一个tensor操作把:

y = x + 2
print(y)

结果:

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

y是由一个操作得出来的,因此它有grad_fn属性:

print(y.grad_fn)

结果:

<AddBackward0 object at 0x00000240692EF470>

在y上再做一些操作:

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

结果:

tensor([[27., 27.],
        [27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward1>)

requires_grad属性默认为False

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)

结果:

False
True
<SumBackward0 object at 0x000001D3B315F438>

梯度(Gradients)

现在我们进行反向传播计算梯度,因为out只包含一个标量,out.backward()out.backward(torch.tensor(1.))等价。

out.backward()

现在计算d(out)/d(x)

print(x.grad)

结果:

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

那么4.5这个数值是如何得到的呢,下面是计算过程:

out=14∑izi out=\frac{1}{4} \sum_{i} z_i out=41izi
zi=3yi2=3(xi+2)2 z_i=3y_i^2=3(x_i+2)^2 zi=3yi2=3(xi+2)2
∂out∂xi=32(xi+2) \frac{\partial out}{\partial x_i} = \frac{3}{2} (x_i+2) xiout=23(xi+2)
∂out∂xi∣xi=1=92=4.5 \frac{\partial out}{\partial x_i} |_{x_i=1} = \frac{9}{2} = 4.5 xioutxi=1=29=4.5

从数学上说,对于函数y⃗=f(x⃗)\vec y=f(\vec x)y=f(x), y⃗\vec yy关于x⃗\vec xx的梯度是一个雅可比矩阵(Jacobian matrix):

J=(∂y1∂x1⋅⋅⋅∂y1∂xn⋮⋱⋮∂ym∂x1⋅⋅⋅∂ym∂xn) J = \begin{pmatrix} \frac{\partial y_1}{\partial x_1} &amp; ··· &amp; \frac{\partial y_1}{\partial x_n}\\ ⋮ &amp; ⋱ &amp; ⋮ \\ \frac{\partial y_m}{\partial x_1} &amp; ··· &amp; \frac{\partial y_m}{\partial x_n} \end{pmatrix} J=x1y1x1ymxny1xnym

一般来说torch.autograd是一个计算向量-雅可比点积的计算引擎。给定任意一个向量v=(v1 v2 ⋅⋅⋅ vm)Tv = (v_1 \space v_2 \space ··· \space v_m)^Tv=(v1 v2  vm)T,计算vT⋅Jv^T·JvTJ。如果vvv恰好是一个标量函数l=g(y⃗)l=g(\vec y)l=g(y)的梯度,即v=(∂l∂y1 ⋅⋅⋅ ∂l∂ym)Tv = (\frac{\partial l}{\partial y_1} \space ··· \space \frac{\partial l}{\partial y_m})^Tv=(y1l  yml)T, 那么根据链式法则,向量-雅可比的点积就是lll关于x⃗\vec xx的梯度:

JT⋅v=(∂y1∂x1⋅⋅⋅∂ym∂x1⋮⋱⋮∂y1∂xn⋅⋅⋅∂ym∂xn)(∂l∂y1⋮∂l∂ym)=(∂l∂x1⋮∂l∂xn) J^T·v = \begin{pmatrix} \frac{\partial y_1}{\partial x_1} &amp; ··· &amp; \frac{\partial y_m}{\partial x_1}\\ ⋮ &amp; ⋱ &amp; ⋮ \\ \frac{\partial y_1}{\partial x_n} &amp; ··· &amp; \frac{\partial y_m}{\partial x_n} \end{pmatrix} \begin{pmatrix} \frac{\partial l}{\partial y_1} \\ ⋮ \\ \frac{\partial l}{\partial y_m} \end{pmatrix} = \begin{pmatrix} \frac{\partial l}{\partial x_1} \\ ⋮ \\ \frac{\partial l}{\partial x_n} \end{pmatrix} JTv=x1y1xny1x1ymxnymy1lyml=x1lxnl

注意:vT⋅Jv^T·JvTJ得到的是一个行向量,我们可以通过计算JT⋅vJ^T·vJTv来得到它的列向量。

这种向量-雅可比点积的特性使得计算非标量输出的梯度非常方便。

下面来看一个例子:

x = torch.randn(3, requires_grad=True)

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

print(y)

结果:

tensor([-358.4211, -803.5598,  780.2765], grad_fn=<MulBackward0>)

可以看出y已经不是一个标量了,torch.autograd不能直接计算出雅可比矩阵,不过我们可以在反向传播的时候传入一个向量作为参数来计算向量-雅可比点积:

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

print(x.grad)

结果:

tensor([5.1200e+01, 5.1200e+02, 5.1200e-02])

机器工匠

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值