5. AUTOGRAD

5. AUTOGRAD

在训练神经网络时,最常用的算法是反向传播。在该算法中,根据损失函数相对于给定参数的梯度来调整参数(模型权重)。
为了计算这些梯度,PyTorch内置torch.argrad,它支持梯度的自动计算。
考虑最简单的一层神经网络,输入x,参数w和b,以及一些损失函数。它可以在PyTorch中以以下方式定义:

import torch

x = 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)

5.1 张量、函数和计算图

上述代码定义的计算图如下:

5. AUTOGRAD_第1张图片

其中 w w w b b b是需要优化的参数,因此,需要能够计算损失函数相对于这些变量的梯度requires_grad

我们应用于张量来构造计算图的函数实际上是函数类的对象。这个对象知道如何计算正向传播的函数,以及如何在反向传播步骤中计算其导数。对反向传播函数的引用存储在张量的grad_fn属性中。

print(f"Gradient function for z = {z.grad_fn}")
print(f"Gradient function for loss = {loss.grad_fn}")

Gradient function for z =
Gradient function for loss =

5.2 计算梯度

为了优化神经网络中的参数权重,我们需要计算损失函数相对于参数的导数,即,我们需要 ∂ l o s s ∂ w \frac{\partial loss}{\partial w} wloss ∂ l o s s ∂ b \frac{\partial loss}{\partial b} bloss在固定的 x x x y y y下的值。为了计算这些导数,我们调用loss.backward(),然后从w.grad和b.grad中检索值:

loss.backward()
print(w.grad)
print(b.grad)

tensor([[0.2727, 0.3044, 0.2571],
[0.2727, 0.3044, 0.2571],
[0.2727, 0.3044, 0.2571],
[0.2727, 0.3044, 0.2571],
[0.2727, 0.3044, 0.2571]])
tensor([0.2727, 0.3044, 0.2571])

5.3 禁用梯度跟踪

默认情况下,requires_grad=True的所有张量都在跟踪其计算历史并支持梯度计算。但在某些情况下,我们不需要这样做,例如,当我们训练了模型,只想通过网络进行前向计算时,我们可以通过用torch.no_grad()包围我们的计算代码来停止梯度跟踪:

z = torch.matmul(x, w)+b
print(z.requires_grad)
# True

with torch.no_grad():
    z = torch.matmul(x, w)+b
print(z.requires_grad)
# False

另一种可以实现相同效果的方法是在张量上使用detach()方法

z = torch.matmul(x, w)+b
z_det = z.detach()
print(z_det.requires_grad)
# False

禁用梯度跟踪的两种可能情况:

  • 将神经网络中的某些参数标记为冻结参数
  • 在只进行前向传播时,加快计算速度

5.4 计算图

autograd在由函数对象组成的有向无环图(DAG)中记录数据以及所有执行的操作。在此DAG中叶子节点是输入张量,根节点是输出张量。从根节点到叶子节点进行跟踪就可使用链式法则自动计算梯度。

在前向传播的过程中,autograd会做以下两件事:

  • 运行请求的操作以计算结果张量
  • 在DAG中保持操作的梯度

在DAG的根节点上调用.backward()时,就会进行反向传播:

  • 在每个.grad_fn上计算梯度
  • 将其累计在各自张量的.grad属性中
  • 使用链式法则,一直传播到叶张量

5.5 张量梯度和雅可比乘积

pytorch允许计算雅可比乘积。

对向量函数 y → = f ( x → ) \overrightarrow{y}=f(\overrightarrow{x}) y =f(x ),当 x → = ⟨ x 1 , . . . , x n ⟩ \overrightarrow{x}=\langle x_1,...,x_n \rangle x =x1,...,xn y → = ⟨ y 1 , . . . , y m ⟩ \overrightarrow{y}=\langle y_1,...,y_m \rangle y =y1,...,ym y → \overrightarrow{y} y 相对于 x → \overrightarrow{x} x 的梯度可由雅可比矩阵得出:

J = ( ∂ y 1 ∂ x 1 … ∂ y 1 ∂ x n ⋮ ⋱ ⋮ ∂ y m ∂ x 1 … ∂ y m ∂ x n ) J=\left( \begin{array}{c} \frac{\partial y_1}{\partial x_1} & \dots & \frac{\partial y_1}{\partial x_n} \\ \vdots & \ddots & \vdots \\ \frac{\partial y_m}{\partial x_1} & \dots & \frac{\partial y_m}{\partial x_n} \end{array} \right) J= x1y1x1ymxny1xnym

PyTorch允许计算给定输入向量 v = ( v 1 , . . . , v m ) v=(v_1, ...,v_m) v=(v1,...,vm)的雅可比乘积 v T ⋅ J v^T\cdot J vTJ,而不是计算雅可比矩阵本身。这是通过以 v v v为自变量反向传播来实现的。 v v v的大小与原始张量的大小相同:

inp = torch.eye(4, 5, requires_grad=True)
out = (inp+1).pow(2).t()
out.backward(torch.ones_like(out), retain_graph=True)
print(f"First call\n{inp.grad}")
out.backward(torch.ones_like(out), retain_graph=True)
print(f"\nSecond call\n{inp.grad}")
inp.grad.zero_()
out.backward(torch.ones_like(out), retain_graph=True)
print(f"\nCall after zeroing gradients\n{inp.grad}")

你可能感兴趣的:(PyTorch学习笔记,python,pytorch)