深入理解torch.optim.Adam的用法及优化流程

深入理解 torch.optim.Adam 的用法及优化流程

在深度学习中,优化器是训练模型的重要组件,用于更新模型参数以最小化损失函数。torch.optim.Adam 是 PyTorch 中常用的优化器之一,它结合了动量法和自适应学习率的优点,被广泛应用于各种神经网络的训练。

本文将结合以下代码,详细讲解 torch.optim.Adam 的使用方法及其背后的逻辑。


代码背景

在代码中,我们实现了一个强化学习 PPO(Proximal Policy Optimization)的优化过程,涉及 Actor 模型(策略)和 Critic 模型(值函数)的更新。以下是完整代码片段:具体可参考笔者的另一篇博客:RLHF (PPO) 流程详解: Proximal Policy Optimization

# Actor Loss: 策略梯度损失(PPO 损失函数)
actor_loss = -torch.min(ratios * advantages, clipped_ratios * advantages).mean()

# Critic Loss: 值函数损失
critic_loss = nn.MSELoss()(critic_values, returns)

# 总损失
total_loss = actor_loss + critic_loss

# 5. 反向传播和更新
optimizer = torch.optim.Adam(list(actor_model.parameters()) + list(critic_model.parameters()))
optimizer.zero_grad()
total_loss.backward()
optimizer.step()

1. torch.optim.Adam 的基础知识

Adam 是一种自适应优化算法,结合了两种方法的优点:

  1. 动量法(Momentum):利用梯度的指数移动平均加速收敛。
  2. RMSProp:利用历史梯度平方的移动平均动态调整学习率。

Adam 的核心更新公式为:
θ t + 1 = θ t − α v ^ t + ϵ m ^ t \theta_{t+1} = \theta_t - \frac{\alpha}{\sqrt{\hat{v}_t} + \epsilon} \hat{m}_t θt+1=θtv^t +ϵαm^t
其中:

  • ( m ^ t \hat{m}_t m^t ) 是梯度的一阶动量估计(类似于动量)。
  • ( v ^ t \hat{v}_t v^t ) 是梯度的二阶动量估计(类似于 RMSProp 的平方梯度)。
  • ( α \alpha α ) 是学习率。
  • ( ϵ \epsilon ϵ ) 是防止除零的小数。

PyTorch 中 torch.optim.Adam 的关键参数:

  • params: 要优化的参数(如模型的 parameters())。
  • lr: 学习率(默认值为 0.001)。
  • betas: 一阶和二阶动量的衰减系数(默认值为 (0.9, 0.999))。
  • eps: 防止除零的微小值(默认值为 1e-8)。
  • weight_decay: 权重衰减系数,用于 L2 正则化。

2. torch.optim.Adam 的使用流程

以下是使用 torch.optim.Adam 的典型步骤:

(1) 初始化优化器
optimizer = torch.optim.Adam(list(actor_model.parameters()) + list(critic_model.parameters()), lr=3e-4)
  • 将需要优化的参数列表传入优化器,如 actor_model.parameters()critic_model.parameters()
  • 可以为不同模块设置不同的学习率。
(2) 清零梯度
optimizer.zero_grad()
  • 每次反向传播前需要清零梯度,避免梯度累加导致错误更新。
(3) 反向传播
total_loss.backward()
  • total_loss 求梯度,梯度会存储在每个参数的 .grad 属性中。
(4) 更新参数
optimizer.step()
  • 根据梯度和优化公式更新参数。

3. PPO 损失函数的优化

在 PPO 中,损失函数包含两部分:

  1. Actor Loss(策略损失):用于更新 Actor 模型,使其生成更优的策略。

    actor_loss = -torch.min(ratios * advantages, clipped_ratios * advantages).mean()
    
    • ratios 是当前策略与参考策略的概率比值。
    • clipped_ratios 是裁剪后的概率比值,避免策略更新过大。
  2. Critic Loss(值函数损失):用于更新 Critic 模型,使其对状态值的估计更准确。

    critic_loss = nn.MSELoss()(critic_values, returns)
    
    • 使用均方误差(MSE)衡量 Critic 模型预测值(critic_values)与目标值(returns)之间的差距。
  3. 总损失:两者相加,用于同时优化 Actor 和 Critic。

    total_loss = actor_loss + critic_loss
    

4. 示例代码分析

以下代码展示了使用 torch.optim.Adam 完成 Actor 和 Critic 模型更新的过程:

# 初始化优化器
optimizer = torch.optim.Adam(
    list(actor_model.parameters()) + list(critic_model.parameters()), lr=3e-4
)

# 清零梯度
optimizer.zero_grad()

# 计算总损失
total_loss = actor_loss + critic_loss

# 反向传播
total_loss.backward()

# 更新参数
optimizer.step()
优化流程
  1. zero_grad 清除上一轮梯度,确保每次更新的独立性。
  2. backward 计算损失函数对所有模型参数的梯度。
  3. step 根据梯度更新模型参数,完成一次优化。

5. 代码运行结果示例

假设:

  • ratios = torch.tensor([1.2, 0.8])clipped_ratios = torch.tensor([1.0, 0.8])
  • advantages = torch.tensor([2.0, 1.5])
  • critic_values = torch.tensor([1.0, 2.0])returns = torch.tensor([1.5, 2.5])

我们可以打印每个步骤的值:

print("Actor Loss:", actor_loss.item())
print("Critic Loss:", critic_loss.item())
print("Total Loss:", total_loss.item())

输出示例:

Actor Loss: -1.35
Critic Loss: 0.25
Total Loss: -1.10

6. 总结

通过以上分析可以看到,torch.optim.Adam 的使用主要包括以下几点:

  1. 初始化优化器:传入需要优化的参数及超参数。
  2. 清零梯度:避免梯度累加。
  3. 反向传播:通过 .backward() 计算梯度。
  4. 更新参数:通过 .step() 完成参数更新。

在 PPO 的优化中,Adam 的动量机制和自适应学习率帮助我们更高效地优化策略和值函数,为模型生成更优质的响应提供了强大支持。

补充内容:为什么传入的是 list(actor_model.parameters())

在 PyTorch 中,model.parameters() 返回的是一个 生成器(generator)对象,它惰性地迭代模型的所有参数。如果需要对参数进行进一步操作(如合并多个模型的参数列表),就需要将其显式地转换为 list 类型。

例如:

# 获取参数生成器
params_gen = actor_model.parameters()
print(type(params_gen))  # 输出:<class 'generator'>

# 转换为列表
params_list = list(params_gen)
print(type(params_list))  # 输出:<class 'list'>

在以下代码中,我们将 actor_modelcritic_model 的参数合并传入优化器:

optimizer = torch.optim.Adam(
    list(actor_model.parameters()) + list(critic_model.parameters()), lr=3e-4
)
  • actor_model.parameters()critic_model.parameters() 分别返回两个生成器。
  • 使用 list() 将生成器转为列表后,可以通过 + 运算符合并两个模型的参数。

这样,Adam 优化器就可以同时更新 actor_modelcritic_model 的参数。


具体的数值模拟:Adam 的计算过程

模拟参数初始化

假设 actor_model 中有两个可训练参数:

  1. ( θ 1 = 0.5 \theta_1 = 0.5 θ1=0.5 )
  2. ( θ 2 = − 0.3 \theta_2 = -0.3 θ2=0.3 )

目标是最小化以下损失函数:
L = θ 1 2 + 2 θ 2 2 \mathcal{L} = \theta_1^2 + 2\theta_2^2 L=θ12+2θ22
对应的梯度为:
∂ L ∂ θ 1 = 2 θ 1 , ∂ L ∂ θ 2 = 4 θ 2 \frac{\partial \mathcal{L}}{\partial \theta_1} = 2\theta_1,\quad \frac{\partial \mathcal{L}}{\partial \theta_2} = 4\theta_2 θ1L=2θ1,θ2L=4θ2


Adam 优化过程

Adam 的核心更新公式为:
θ t + 1 = θ t − α v ^ t + ϵ ⋅ m ^ t \theta_{t+1} = \theta_t - \frac{\alpha}{\sqrt{\hat{v}_t} + \epsilon} \cdot \hat{m}_t θt+1=θtv^t +ϵαm^t

其中:

  1. 梯度 ( g t g_t gt ) 是损失函数对参数的偏导数。
  2. ( m ^ t \hat{m}_t m^t):梯度的一阶动量估计。
    m t = β 1 ⋅ m t − 1 + ( 1 − β 1 ) ⋅ g t m_t = \beta_1 \cdot m_{t-1} + (1 - \beta_1) \cdot g_t mt=β1mt1+(1β1)gt
    m ^ t = m t 1 − β 1 t \hat{m}_t = \frac{m_t}{1 - \beta_1^t} m^t=1β1tmt
  3. ( v ^ t \hat{v}_t v^t ):梯度平方的二阶动量估计。
    v t = β 2 ⋅ v t − 1 + ( 1 − β 2 ) ⋅ g t 2 v_t = \beta_2 \cdot v_{t-1} + (1 - \beta_2) \cdot g_t^2 vt=β2vt1+(1β2)gt2
    v ^ t = v t 1 − β 2 t \hat{v}_t = \frac{v_t}{1 - \beta_2^t} v^t=1β2tvt

超参数取值:

  • 学习率 ( α = 0.1 \alpha = 0.1 α=0.1 )
  • 动量系数 ( β 1 = 0.9 \beta_1 = 0.9 β1=0.9 )
  • 二阶动量系数 ( β 2 = 0.999 \beta_2 = 0.999 β2=0.999 )
  • 防止除零项 ( ϵ = 1 0 − 8 \epsilon = 10^{-8} ϵ=108 )

初始状态
  • 参数:( θ 1 = 0.5 \theta_1 = 0.5 θ1=0.5 ), ( θ 2 = − 0.3 \theta_2 = -0.3 θ2=0.3 )
  • 梯度:
    g θ 1 = 2 ⋅ 0.5 = 1.0 , g θ 2 = 4 ⋅ ( − 0.3 ) = − 1.2 g_{\theta_1} = 2 \cdot 0.5 = 1.0,\quad g_{\theta_2} = 4 \cdot (-0.3) = -1.2 gθ1=20.5=1.0,gθ2=4(0.3)=1.2
  • 初始一阶动量:( m 0 = 0 m_0 = 0 m0=0 )
  • 初始二阶动量:( v 0 = 0 v_0 = 0 v0=0 )

第 1 步迭代
  1. 计算一阶动量:
    m θ 1 ( 1 ) = 0.9 ⋅ 0 + 0.1 ⋅ 1.0 = 0.1 , m θ 2 ( 1 ) = 0.9 ⋅ 0 + 0.1 ⋅ ( − 1.2 ) = − 0.12 m_{\theta_1}^{(1)} = 0.9 \cdot 0 + 0.1 \cdot 1.0 = 0.1,\quad m_{\theta_2}^{(1)} = 0.9 \cdot 0 + 0.1 \cdot (-1.2) = -0.12 mθ1(1)=0.90+0.11.0=0.1,mθ2(1)=0.90+0.1(1.2)=0.12
    偏差修正:
    m ^ θ 1 ( 1 ) = 0.1 1 − 0. 9 1 = 1.0 , m ^ θ 2 ( 1 ) = − 0.12 1 − 0. 9 1 = − 1.2 \hat{m}_{\theta_1}^{(1)} = \frac{0.1}{1 - 0.9^1} = 1.0,\quad \hat{m}_{\theta_2}^{(1)} = \frac{-0.12}{1 - 0.9^1} = -1.2 m^θ1(1)=10.910.1=1.0,m^θ2(1)=10.910.12=1.2

  2. 计算二阶动量:
    v θ 1 ( 1 ) = 0.999 ⋅ 0 + 0.001 ⋅ ( 1. 0 2 ) = 0.001 , v θ 2 ( 1 ) = 0.999 ⋅ 0 + 0.001 ⋅ ( − 1. 2 2 ) = 0.00144 v_{\theta_1}^{(1)} = 0.999 \cdot 0 + 0.001 \cdot (1.0^2) = 0.001,\quad v_{\theta_2}^{(1)} = 0.999 \cdot 0 + 0.001 \cdot (-1.2^2) = 0.00144 vθ1(1)=0.9990+0.001(1.02)=0.001,vθ2(1)=0.9990+0.001(1.22)=0.00144
    偏差修正:
    v ^ θ 1 ( 1 ) = 0.001 1 − 0.99 9 1 = 1.0 , v ^ θ 2 ( 1 ) = 0.00144 1 − 0.99 9 1 = 1.44 \hat{v}_{\theta_1}^{(1)} = \frac{0.001}{1 - 0.999^1} = 1.0,\quad \hat{v}_{\theta_2}^{(1)} = \frac{0.00144}{1 - 0.999^1} = 1.44 v^θ1(1)=10.99910.001=1.0,v^θ2(1)=10.99910.00144=1.44

  3. 更新参数:
    θ 1 ( 1 ) = 0.5 − 0.1 1.0 + 1 0 − 8 ⋅ 1.0 = 0.4 \theta_1^{(1)} = 0.5 - \frac{0.1}{\sqrt{1.0} + 10^{-8}} \cdot 1.0 = 0.4 θ1(1)=0.51.0 +1080.11.0=0.4
    θ 2 ( 1 ) = − 0.3 − 0.1 1.44 + 1 0 − 8 ⋅ ( − 1.2 ) = − 0.2 \theta_2^{(1)} = -0.3 - \frac{0.1}{\sqrt{1.44} + 10^{-8}} \cdot (-1.2) = -0.2 θ2(1)=0.31.44 +1080.1(1.2)=0.2


第 2 步迭代

重复上述步骤,计算新的梯度、一阶动量、二阶动量和参数更新。


总结:Adam 的优点

  1. 动态学习率:针对每个参数自动调整学习率,收敛更稳定。
  2. 动量机制:缓解梯度更新的噪声,提高收敛速度。
  3. 广泛适用性:在稀疏数据、高维数据中表现优异。

通过数值模拟可以更直观地理解 Adam 的核心更新机制,也能帮助更好地理解为什么要使用 list(actor_model.parameters()) 传入优化器以更新多个模型的参数。

后记

2024年12月13日13点00分于上海,在GPT4o大模型辅助下完成。

### 回答1: torch.optim.adamPyTorch框架中的一种优化器,用于优化深度学习模型的参数。它是一种基于梯度的优化方法,能够自适应地调整每个参数的学习率,并且在许多应用中表现出色。 下面是使用torch.optim.adam优化器的一般步骤: 1. 导入PyTorchtorch.optim库: ``` import torch import torch.optim as optim ``` 2. 定义模型: ``` class MyModel(torch.nn.Module): def __init__(self): super(MyModel, self).__init__() self.fc1 = torch.nn.Linear(10, 5) self.fc2 = torch.nn.Linear(5, 1) def forward(self, x): x = torch.relu(self.fc1(x)) x = self.fc2(x) return x ``` 3. 实例化模型和优化器: ``` model = MyModel() optimizer = optim.Adam(model.parameters(), lr=0.001) ``` 4. 计算损失并进行反向传播: ``` criterion = torch.nn.MSELoss() input = torch.randn(1, 10) output = model(input) target = torch.randn(1, 1) loss = criterion(output, target) loss.backward() ``` 5. 更新模型的参数: ``` optimizer.step() ``` 6. 清空梯度: ``` optimizer.zero_grad() ``` 这就是使用torch.optim.adam优化器的基本流程。你可以通过调整lr参数来改变学习率,并且还有其他一些参数可以进行调整,以满足不同的需求。 ### 回答2: torch.optim.Adam 是在深度学习中常用的算法之一,用于优化神经网络的一个参数。对于神经网络中的参数,Adam 算法会自适应地调整每个参数的学习率,从而实现更快地训练和更好的性能。 TensorFlow, Keras, PyTorch等框架中都有Adam的实现。因此,本文将重点介绍PyTorch中该优化算法的使用方法。 在 PyTorch 中,使用 Adam 优化器有以下四个步骤: 1. 导入PyTorch包 首先,需要从 PyTorch 包中导入Adam方法 ```python import torch.optim as optim ``` 2. 定义Optimizer 要使用Adam,需要先通过调用optim.Adam类来实例化一个 Adam 优化器对象: ```python optimizer = optim.Adam(model.parameters(), lr=learning_rate) ``` 其中, - `model.parameters()`:指定需要优化的模型参数,例如权重矩阵和偏置项等。 - `learning_rate`:指定学习率。 3. 计算损失 在这一步骤中,需要定义损失函数(例如交叉熵),并为其提供一些输入。在 PyTorch 中,我们需要创建一个损失函数,并传入输入及其目标(真实标签)。 ```python criterion = nn.CrossEntropyLoss() ... loss = criterion(y_pred, y_true) ``` 4. 反向传播 在这一步骤中,我们需要做两件事: - 首先,将计算出的梯度存储在所有参数的.grad属性中。 ```python loss.backward() ``` - 然后使用第2步骤中定义的优化器更新参数。 ```python optimizer.step() optimizer.zero_grad() ``` 其中, `optimizer.step()`:根据损失计算每个参数的梯度,以及每个参数的学习率 `optimizer.zero_grad()`:需要在优化器对象的step()方法前调用,将所有参数的 grad 属性归零;在反向传播阶段,PyTorch默认将参数的梯度进行累加,这样通常不是我们想要的;因此,需要在每个batch的训练开始时用0将它们清除(否则,会不断地累加)。 这就是使用 Adam 优化器进行 PyTorch 模型训练的基本流程。通过调节和优化 learning rate 值,可以提高模型的收敛速度、泛化能力等。 ### 回答3: torch.optim.adam是一种优化器,用于在深度学习训练过程中更新模型的参数,以便得到更好的效果。Adam优化器是一种基于梯度的优化算法,被广泛应用于深度学习中。 Adam基于随机梯度下降(Stochastic Gradient Descent)算法,使用一种自适应的学习率方法来更新参数。Adam算法在SGD基础上,加入了两个动量项,具体为梯度一阶矩估计(一次动量)和梯度二阶矩估计(二次动量)。 torch.optim.adam的使用步骤如下: 1. 定义模型并选择使用Adam优化器。 2. 设置优化器超参数,主要包括学习率、权重衰减和动量等参数。 3. 在每个batch的训练中,计算loss,并调用optimizer.step()函数更新模型参数。 4. 在每个epoch的末尾,使用验证集对模型进行评估,并根据评估结果进行调整。 代码示例: import torch import torch.nn as nn import torch.optim as optim # 定义模型 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.fc = nn.Linear(10, 5) def forward(self, x): x = self.fc(x) return x # 选择优化器 model = Net() optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=0.001) # 训练模型 for epoch in range(100): for i, (inputs, labels) in enumerate(train_loader): optimizer.zero_grad() outputs = model(inputs) loss = nn.CrossEntropyLoss()(outputs, labels) loss.backward() optimizer.step() # 验证模型 with torch.no_grad(): correct = 0 total = 0 for inputs, labels in test_loader: outputs = model(inputs) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() accuracy = correct / total print('Epoch: %d, Accuracy: %f' % (epoch, accuracy)) 在上述代码中,我们通过定义一个简单的线性模型Net,并选择Adam作为优化器。在每个batch的训练中,我们计算模型输出和真实标签之间的交叉熵损失,并调用optimizer.step()函数更新模型参数。在每个epoch的末尾,我们使用测试集对模型进行评估,并输出模型在测试集上的精度。 总之,torch.optim.adam是一个非常实用且广泛使用的优化器。使用它能够在深度学习训练中非常有效地调整模型参数,提高模型性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值