专题链接:https://blog.csdn.net/qq_33345365/category_12591348.html
本教程翻译自微软教程:https://learn.microsoft.com/en-us/training/paths/pytorch-fundamentals/
初次编辑:2024/3/2;最后编辑:2024/3/3
本教程第一篇:介绍pytorch基础和张量操作
本教程第二篇:介绍了数据集与归一化
本教程第三篇:介绍构建模型层的基本操作。
本教程第四篇:介绍自动微分相关知识,即本博客内容。
另外本人还有pytorch CV相关的教程,见专题:
https://blog.csdn.net/qq_33345365/category_12578430.html
自动微分
使用torch.autograd
自动微分 Automaic differentiation
在训练神经网络时,最常用的算法是反向传播(back propagation)。在这个算法中,参数(模型权重)根据损失函数相对于给定参数的梯度进行调整。损失函数(loss function)计算神经网络产生的预期输出和实际输出之间的差异。目标是使损失函数的结果尽可能接近零。该算法通过神经网络向后遍历以调整权重和偏差来重新训练模型。这就是为什么它被称为反向传播。随着时间的推移,通过反复进行这种回传和前向过程来将损失(loss)减少到0的过程称为梯度下降。
为了计算这些梯度,PyTorch具有一个内置的微分引擎,称为torch.autograd
。它支持对任何计算图进行梯度的自动计算。
考虑最简单的单层神经网络,具有输入x
,参数w
和b
,以及某些损失函数。可以在PyTorch中如下定义:
import torchx = torch.ones(5) # input tensor
y = torch.zeros(3) # expected output
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
z = torch.matmul(x, w)+b # z = x*w +b
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)
张量、函数与计算图(computational graphs)
在这个网络中,w
和b
是参数,他们会被损失函数优化。因此,需要能够计算损失函数相对于这些变量的梯度。为此,我们将这些张量的requires_grad
属性设置为True。
**注意:**您可以在创建张量时设置
requires_grad
的值,也可以稍后使用x.requires_grad_(True)
方法来设置。
我们将应用于张量的函数(function)用于构建计算图,这些函数是Function
类的对象。这个对象知道如何在前向方向上计算函数,还知道在反向传播步骤中如何计算其导数。反向传播函数
的引用存储在张量的grad_fn
属性中。
print('Gradient function for z =',z.grad_fn)
print('Gradient function for loss =', loss.grad_fn)
输出是:
Gradient function for z = <AddBackward0 object at 0x00000280CC630CA0>
Gradient function for loss = <BinaryCrossEntropyWithLogitsBackward object at 0x00000280CC630310>
计算梯度
为了优化神经网络中参数的权重,需要计算损失函数相对于参数的导数,即我们需要在某些固定的x
和y
值下计算 ∂ l o s s ∂ w \frac{\partial loss}{\partial w} ∂w∂loss和 ∂ l o s s ∂ b \frac{\partial loss}{\partial b} ∂b∂loss。为了计算这些导数,我们调用loss.backward()
,然后从w.grad
和b.grad
中获取值。
loss.backward()
print(w.grad)
print(b.grad)
输出是:
tensor([[0.2739, 0.0490, 0.3279],[0.2739, 0.0490, 0.3279],[0.2739, 0.0490, 0.3279],[0.2739, 0.0490, 0.3279],[0.2739, 0.0490, 0.3279]])
tensor([0.2739, 0.0490, 0.3279])
注意: 只能获取计算图中设置了
requires_grad
属性为True
的叶节点的grad
属性。对于计算图中的所有其他节点,梯度将不可用。此外,出于性能原因,我们只能对给定图执行一次backward
调用以进行梯度计算。如果我们需要在同一图上进行多次backward
调用,我们需要在backward
调用中传递retain_graph=True
。
禁用梯度追踪 Disabling gradient tracking
默认情况下,所有requires_grad=True
的张量都在跟踪其计算历史并支持梯度计算。然而,在某些情况下,我们并不需要这样做,例如,当我们已经训练好模型并且只想将其应用于一些输入数据时,也就是说,我们只想通过网络进行前向计算。我们可以通过将我们的计算代码放在一个torch.no_grad()
块中来停止跟踪计算:
z = torch.matmul(x, w)+b
print(z.requires_grad)with torch.no_grad():z = torch.matmul(x, w)+b
print(z.requires_grad)
输出是:
True
False
另外一种产生相同结果的方法是在张量上使用detach
方法:
z = torch.matmul(x, w)+b
z_det = z.detach()
print(z_det.requires_grad)
有一些理由你可能想要禁用梯度跟踪:
- 将神经网络中的某些参数标记为冻结参数(frozen parameters)。这在微调预训练网络的情况下非常常见。
- 当你只进行前向传播时,为了加速计算,因为不跟踪梯度的张量上的计算更有效率。
计算图的更多知识
概念上,autograd 在一个有向无环图 (DAG) 中保留了数据(张量)和所有执行的操作(以及生成的新张量),这些操作由 Function 对象组成。在这个 DAG 中,叶子节点是输入张量,根节点是输出张量。通过从根节点到叶子节点追踪这个图,你可以使用链式法则(chain rule)自动计算梯度。
在前向传播中,autograd 同时执行两件事情:
- 运行所请求的操作以计算结果张量,并且
- 在 DAG 中维护操作的 梯度函数(gradient function)。
当在 DAG 根节点上调用 .backward()
时,反向传播开始。autograd
然后:
- 从每个
.grad_fn
计算梯度, - 将它们累积在相应张量的
.grad
属性中,并且 - 使用链式法则一直传播到叶子张量。
PyTorch 中的 DAG 是动态的
一个重要的事情要注意的是,图是从头开始重新创建的;在每次 .backward()
调用之后,autograd 开始填充一个新的图。这正是允许您在模型中使用控制流语句的原因;如果需要,您可以在每次迭代中更改形状、大小和操作。
代码汇总:
import torchx = torch.ones(5) # input tensor
y = torch.zeros(3) # expected output
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
z = torch.matmul(x, w) + b
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)print('Gradient function for z =', z.grad_fn)
print('Gradient function for loss =', loss.grad_fn)loss.backward()
print(w.grad)
print(b.grad)z = torch.matmul(x, w) + b
print(z.requires_grad)with torch.no_grad():z = torch.matmul(x, w) + b
print(z.requires_grad)z = torch.matmul(x, w) + b
z_det = z.detach()
print(z_det.requires_grad)