pytorch基础【4】梯度计算、链式法则、梯度清零 您所在的位置:网站首页 pytorch显示梯度 pytorch基础【4】梯度计算、链式法则、梯度清零

pytorch基础【4】梯度计算、链式法则、梯度清零

2024-07-14 07:55| 来源: 网络整理| 查看: 265

文章目录 梯度计算计算图(Computational Graph)梯度求导(Gradient Computation)函数与概念 示例代码更多细节梯度求导的过程梯度求导的基本步骤示例代码注意事项总结 链式法则是什么?链式法则的数学定义链式法则在深度学习中的应用反向传播中的链式法则具体示例反向传播过程 总结 为什么需要梯度清零如何实现梯度清零进一步说明总结

梯度计算

在PyTorch中,计算图和梯度求导是核心功能之一,特别是在深度学习模型的训练过程中。以下是对这两个概念的详细解释:

计算图(Computational Graph)

计算图是一种有向无环图(Directed Acyclic Graph, DAG),其中节点表示操作(operation)或变量(variable),边表示操作的输入输出关系。PyTorch 使用计算图来记录和管理变量之间的依赖关系,以便在反向传播时计算梯度。在这里插入图片描述

动态计算图(Dynamic Computational Graph):PyTorch 采用动态计算图(Dynamic Computational Graph),即每次进行前向传播(forward pass)时,都会动态构建一个新的计算图。这样做的好处是可以更灵活地处理各种复杂的模型结构,尤其是那些在每个前向传播中都会变化的模型。 梯度求导(Gradient Computation)

梯度求导是深度学习中优化模型参数的关键步骤。梯度描述了损失函数对每个参数的变化率,用于指导参数的更新方向。

自动求导(Autograd):PyTorch 提供了一个强大的自动求导库,称为 Autograd。通过 Autograd,PyTorch 可以自动计算标量值(通常是损失函数)的梯度。 函数与概念 torch.Tensor: Tensor 是 PyTorch 中存储数据和定义计算图的基础数据结构。默认情况下,所有的张量(Tensor)都不会自动追踪计算的历史。如果要使张量参与计算图并能够进行自动求导,需要在创建张量时设置 requires_grad=True。 backward(): 调用张量的 backward() 方法,PyTorch 会自动计算该张量的所有依赖张量的梯度,并存储在各自的 .grad 属性中。backward() 只接受标量张量(一个数值),如果不是标量张量,通常会传递一个与张量形状匹配的梯度参数。 torch.no_grad(): 在评估模型或推理时,我们不需要计算梯度,可以使用 torch.no_grad() 以节省内存和计算资源。 示例代码 import torch # 创建张量,并设置 requires_grad=True 以追踪其计算历史 x = torch.tensor(2.0, requires_grad=True) y = x ** 2 # 计算图中 y 的梯度 y.backward() # 计算 y 对 x 的梯度 print(x.grad) # 输出 x 的梯度,dy/dx = 2*x => 4 # 在不需要梯度计算的情况下进行计算 with torch.no_grad(): z = x * 2 print(z) # 输出:tensor(4.0) 更多细节 梯度累积与清零:每次调用 backward(),梯度会累积(即,累加到 .grad 属性中),因此在每次新的梯度计算之前通常需要清零现有的梯度,例如通过 optimizer.zero_grad()。多次反向传播:如果在同一个计算图上进行多次反向传播(例如在 RNN 中),需要设置 retain_graph=True,以防止计算图被释放。

通过这些机制,PyTorch 提供了一个灵活且高效的框架,用于构建和训练复杂的神经网络模型。

梯度求导的过程

在PyTorch中,梯度求导的过程是通过自动微分(Autograd)机制实现的。以下是梯度求导过程的详细步骤:

梯度求导的基本步骤 定义计算图: 每当你对 torch.Tensor 进行操作时,PyTorch 会动态地创建一个计算图来记录操作。如果 Tensor 的 requires_grad 属性设置为 True,那么该张量会开始追踪其上的所有操作,这样你就可以调用 backward() 来自动计算其梯度。 前向传播(Forward Pass): 计算图的构建是在前向传播过程中完成的。在前向传播过程中,输入数据通过神经网络的各层进行计算,最终生成输出。 计算损失(Loss Calculation): 通常情况下,在前向传播结束后会计算损失函数(Loss),这是一个标量值,用于评估模型的输出与目标之间的差距。 反向传播(Backward Pass): 调用损失张量的 backward() 方法。反向传播通过链式法则计算损失函数相对于每个叶子节点(即,所有具有 requires_grad=True 的张量)的梯度。 更新参数(Parameter Update): 使用优化器(如 SGD、Adam 等)通过梯度下降或其他优化算法更新模型的参数。 示例代码

以下是一个简单的示例代码,演示了梯度求导的过程:

import torch import torch.nn as nn import torch.optim as optim # 定义一个简单的线性模型 class LinearModel(nn.Module): def __init__(self): super(LinearModel, self).__init__() self.linear = nn.Linear(1, 1) # 输入维度为1,输出维度为1 def forward(self, x): return self.linear(x) # 创建模型实例 model = LinearModel() # 定义损失函数和优化器 criterion = nn.MSELoss() # 均方误差损失函数 optimizer = optim.SGD(model.parameters(), lr=0.01) # 随机梯度下降优化器 # 创建输入数据和目标数据 inputs = torch.tensor([[1.0], [2.0], [3.0], [4.0]]) targets = torch.tensor([[2.0], [4.0], [6.0], [8.0]]) # 前向传播 outputs = model(inputs) loss = criterion(outputs, targets) # 反向传播 loss.backward() # 查看梯度 for param in model.parameters(): print(param.grad) # 更新参数 optimizer.step()

步骤解析

创建模型和数据: 定义一个简单的线性回归模型,并创建输入数据和目标数据。 前向传播: 将输入数据传递给模型,计算输出。使用损失函数计算输出与目标之间的损失。 反向传播: 调用 loss.backward() 计算损失相对于每个参数的梯度。PyTorch 会通过计算图自动进行反向传播,计算各个参数的梯度并存储在 param.grad 中。 更新参数: 使用优化器的 step() 方法更新参数。这一步通常在每个训练迭代中执行。 注意事项 梯度清零:在每次调用 backward() 之前,通常需要清零现有的梯度,以避免梯度累积。这可以通过 optimizer.zero_grad() 或 model.zero_grad() 来实现。链式法则:反向传播过程中使用链式法则计算梯度,因此在计算图较深时,梯度的计算会逐层进行,直到计算到每个叶子节点。 总结

PyTorch 的自动微分机制使得梯度计算变得简单且高效,通过构建计算图并自动进行反向传播,你可以专注于模型的设计和训练,而不必手动计算复杂的梯度。

链式法则是什么?

链式法则(Chain Rule)是微积分中的一个基本法则,用于求复合函数的导数。在深度学习中,链式法则用于反向传播(backpropagation)算法的核心,帮助计算损失函数相对于每个模型参数的梯度。

链式法则的数学定义

假设有两个函数 u=f(x) 和 y=g(u),那么复合函数 y=g(f(x)) 的导数可以表示为: d y d x = d y d u ⋅ d u d x \frac{dy}{dx} = \frac{dy}{du} \cdot \frac{du}{dx} dxdy​=dudy​⋅dxdu​

链式法则在深度学习中的应用

在深度学习中,神经网络由多个层组成,每一层可以看作是一个函数,这些函数依次连接形成一个复合函数。假设我们有一个三层的神经网络,其前向传播可以表示为:

a=f(x)b=g(a)c=h(b)

损失函数 L可以表示为 L=l©,其中 x 是输入数据,a、b、c 是中间层的输出。

反向传播中的链式法则

在反向传播过程中,我们需要计算损失函数 L对每个参数的梯度。通过链式法则,我们可以逐层计算这些梯度。具体步骤如下:

计算损失函数相对于输出层的梯度: ∂ L ∂ c \frac{\partial L}{\partial c} ∂c∂L​

计算损失函数相对于中间层 b的梯度: ∂ L ∂ b = ∂ L ∂ c ⋅ ∂ c ∂ b \frac{\partial L}{\partial b} = \frac{\partial L}{\partial c} \cdot \frac{\partial c}{\partial b} ∂b∂L​=∂c∂L​⋅∂b∂c​

计算损失函数相对于中间层 a 的梯度: ∂ L ∂ a = ∂ L ∂ b ⋅ ∂ b ∂ a \frac{\partial L}{\partial a} = \frac{\partial L}{\partial b} \cdot \frac{\partial b}{\partial a} ∂a∂L​=∂b∂L​⋅∂a∂b​

计算损失函数相对于输入层 x的梯度: ∂ L ∂ x = ∂ L ∂ a ⋅ ∂ a ∂ x \frac{\partial L}{\partial x} = \frac{\partial L}{\partial a} \cdot \frac{\partial a}{\partial x} ∂x∂L​=∂a∂L​⋅∂x∂a​

通过这种逐层传播梯度的方式,我们可以计算每个参数的梯度,从而使用梯度下降法来更新模型参数。

具体示例

让我们通过一个具体的例子来说明链式法则的应用。假设我们有一个简单的神经网络,其前向传播过程如下:

输入 xxx

第一层: z 1 = W 1 x + b 1 z_1=W_1x+b_1 z1​=W1​x+b1​

,激活函数 a 1 = σ ( z 1 ) a_1 = \sigma(z_1) a1​=σ(z1​)

第二层: z 2 = W 2 a 1 + b 2 z_2 = W_2 a_1 + b_2 z2​=W2​a1​+b2​ ,激活函数 a 2 = σ ( z 2 ) a_2 = \sigma(z_2) a2​=σ(z2​)

输出层: y = W 3 a 2 + b 3 y = W_3 a_2 + b_3 y=W3​a2​+b3​

损失函数 L 是输出 y 和目标 ytarget之间的均方误差。

反向传播过程

计算输出层的梯度: ∂ L ∂ y = 2 ( y − y t a r g e t ) \frac{\partial L}{\partial y} = 2 (y - y_{target}) ∂y∂L​=2(y−ytarget​)

计算第二层的梯度: ∂ L ∂ z 2 = ∂ L ∂ y ⋅ ∂ y ∂ z 2 = ∂ L ∂ y ⋅ W 3 \frac{\partial L}{\partial z_2} = \frac{\partial L}{\partial y} \cdot \frac{\partial y}{\partial z_2} = \frac{\partial L}{\partial y} \cdot W_3 ∂z2​∂L​=∂y∂L​⋅∂z2​∂y​=∂y∂L​⋅W3​

∂ L ∂ a 2 = ∂ L ∂ z 2 ⋅ σ ′ ( z 2 ) ∂ \frac{\partial L}{\partial a_2} = \frac{\partial L}{\partial z_2} \cdot \sigma'(z_2)∂ ∂a2​∂L​=∂z2​∂L​⋅σ′(z2​)∂

计算第一层的梯度: ∂ L ∂ z 1 = ∂ L ∂ a 2 ⋅ ∂ a 2 ∂ z 1 = ∂ L ∂ a 2 ⋅ W 2 \frac{\partial L}{\partial z_1} = \frac{\partial L}{\partial a_2} \cdot \frac{\partial a_2}{\partial z_1} = \frac{\partial L}{\partial a_2} \cdot W_2 ∂z1​∂L​=∂a2​∂L​⋅∂z1​∂a2​​=∂a2​∂L​⋅W2​

∂ L ∂ a 1 = ∂ L ∂ z 1 ⋅ σ ′ ( z 1 ) \frac{\partial L}{\partial a_1} = \frac{\partial L}{\partial z_1} \cdot \sigma'(z_1) ∂a1​∂L​=∂z1​∂L​⋅σ′(z1​)

计算输入层的梯度: ∂ L ∂ x = ∂ L ∂ a 1 ⋅ W 1 \frac{\partial L}{\partial x} = \frac{\partial L}{\partial a_1} \cdot W_1 ∂x∂L​=∂a1​∂L​⋅W1​

通过链式法则,反向传播算法能够有效地计算出每一层参数的梯度,从而更新参数,最小化损失函数。

总结

链式法则是微积分中的一个重要法则,它在深度学习中的反向传播算法中起到了关键作用。通过链式法则,我们可以有效地计算复合函数的导数,从而利用梯度下降等优化方法来训练神经网络模型。

在深度学习中,梯度清零(zeroing gradients)是训练过程中的一个关键步骤,通常在每次参数更新之前进行。这个过程在PyTorch等深度学习框架中尤为重要。以下是关于为什么需要梯度清零以及如何实现梯度清零的详细解释:

为什么需要梯度清零 防止梯度累积: 在每次反向传播计算中,梯度会累积到模型参数的 .grad 属性中。如果不清零,梯度会在每个小批次(mini-batch)训练后继续累积,这将导致错误的梯度更新。举例来说,如果没有清零,当前批次的梯度会与之前批次的梯度相加,导致最终的梯度远大于实际应该的值。这会使参数更新的步长不合理,影响模型训练效果。 正确的参数更新: 每个小批次的梯度计算都应该基于当前的小批次数据,确保每次参数更新都准确反映当前的小批次数据对损失函数的贡献。 如何实现梯度清零

在PyTorch中,梯度清零通常通过调用 optimizer.zero_grad() 来实现。这里有一个完整的例子来说明这一过程:

import torch import torch.nn as nn import torch.optim as optim # 定义一个简单的神经网络 class SimpleNet(nn.Module): def __init__(self): super(SimpleNet, self).__init__() self.fc1 = nn.Linear(10, 5) self.fc2 = nn.Linear(5, 1) def forward(self, x): x = torch.relu(self.fc1(x)) x = self.fc2(x) return x # 实例化模型和优化器 model = SimpleNet() optimizer = optim.SGD(model.parameters(), lr=0.01) # 生成一些假数据 data = torch.randn(10) # 输入数据 target = torch.tensor([1.0]) # 目标标签 # 损失函数 criterion = nn.MSELoss() # 训练过程中的一个小批次 for epoch in range(100): # 假设训练100个epoch optimizer.zero_grad() # 清零梯度 output = model(data) # 前向传播 loss = criterion(output, target) # 计算损失 loss.backward() # 反向传播计算梯度 optimizer.step() # 更新参数 进一步说明 清零位置: optimizer.zero_grad() 通常放在每个训练循环的开头,确保在计算新的梯度之前先将上一次迭代的梯度清零。梯度累积应用场景: 在某些特定情况下,例如梯度累积(Gradient Accumulation)技术中,故意让梯度在多个小批次上累积,然后再更新参数。但这是特定应用场景,不适用于标准的训练过程。 总结

梯度清零是深度学习模型训练中的一个重要步骤,确保每次参数更新时的梯度计算是正确的、独立的。通过 optimizer.zero_grad() 方法,我们可以有效地防止梯度累积问题,从而确保模型训练过程的稳定性和准确性。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有