2.5 优化器 (Optimizers)


文档摘要

2.5 优化器 (Optimizers) 2.5 优化器 (Optimizers) 2.5.1 优化器概述 深度学习模型的训练本质上是一个优化问题。我们的目标是找到一组模型参数,使得模型在给定任务上的表现最佳。这个“最佳表现”通常通过损失函数来衡量,损失函数值越小,代表模型预测结果与真实值之间的差距越小,模型性能越好。 优化器正是用于更新模型参数以最小化损失函数的算法。它基于梯度下降的思想,通过计算损失函数关于模型参数的梯度,并根据梯度信息调整参数,逐步逼近损失函数的最小值点。 梯度下降与优化器 梯度下降是优化器最核心的理论基础。简单来说,梯度指向损失函数增长最快的方向,而负梯度则指向损失函数下降最快的方向。

2.5 优化器 (Optimizers)

2.5 优化器 (Optimizers)

2.5.1 优化器概述

深度学习模型的训练本质上是一个优化问题。我们的目标是找到一组模型参数,使得模型在给定任务上的表现最佳。这个“最佳表现”通常通过损失函数来衡量,损失函数值越小,代表模型预测结果与真实值之间的差距越小,模型性能越好。

优化器正是用于更新模型参数以最小化损失函数的算法。它基于梯度下降的思想,通过计算损失函数关于模型参数的梯度,并根据梯度信息调整参数,逐步逼近损失函数的最小值点。

梯度下降与优化器

梯度下降是优化器最核心的理论基础。简单来说,梯度指向损失函数增长最快的方向,而负梯度则指向损失函数下降最快的方向。梯度下降算法的核心思想是沿着负梯度方向迭代更新参数,期望最终到达损失函数的局部最小值点或全局最小值点。

可以用下图来简单表示梯度下降的过程:

在上述流程中,优化器负责执行步骤 C 和 D,即计算梯度并更新参数。不同的优化器算法在梯度计算和参数更新策略上有所差异,从而影响模型的训练速度、收敛性以及最终性能。

2.5.2 PyTorch 中的优化器

PyTorch 提供了 torch.optim 模块,其中包含了各种常用的优化器算法,例如:

  • SGD (Stochastic Gradient Descent):随机梯度下降

  • Adam (Adaptive Moment Estimation):自适应矩估计

  • RMSprop (Root Mean Square Propagation):均方根传播

  • Adagrad (Adaptive Gradient Algorithm):自适应梯度算法

  • AdamW (Adam with Weight Decay):带有权重衰减的 Adam

  • ASGD (Averaged Stochastic Gradient Descent):平均随机梯度下降

  • LBFGS (Limited-memory Broyden-Fletcher-Goldfarb-Shanno):有限内存 BFGS

接下来我们将重点介绍 SGD、Adam 和 RMSprop 这三种最常用的优化器,并结合代码示例进行详细讲解。

2.5.2.1 SGD (Stochastic Gradient Descent)

SGD 是最基础也是最经典的优化器之一。它的核心思想是在每次迭代中,随机选取一个或一批样本(mini-batch)来计算梯度,并利用该梯度更新模型参数。

算法原理:

  1. 随机选取 mini-batch 数据 (从训练集中随机抽取一部分数据)

  2. 计算 mini-batch 损失函数 (基于选取的 mini-batch 数据计算损失值)

  3. 计算损失函数关于模型参数的梯度 (计算损失函数对模型参数的偏导数)

  4. 更新模型参数 (根据负梯度方向和学习率更新参数)

公式:

参数更新公式如下:

θ = θ - η * ∇L(θ; batch)

其中:

  • θ:模型参数

  • η:学习率 (learning rate),控制参数更新的步长

  • ∇L(θ; batch):基于 mini-batch 数据计算的损失函数梯度

PyTorch 代码示例:

import torch import torch.nn as nn import torch.optim as optim # 1. 定义模型 class SimpleNet(nn.Module): def __init__(self): super(SimpleNet, self).__init__() self.fc = nn.Linear(10, 2) def forward(self, x): return self.fc(x) model = SimpleNet() # 2. 定义优化器 - SGD optimizer_sgd = optim.SGD(model.parameters(), lr=0.01) # lr 为学习率 # 3. 示例训练循环 (简化版) inputs = torch.randn(64, 10) # 模拟输入数据 targets = torch.randint(0, 2, (64,)) # 模拟目标数据 criterion = nn.CrossEntropyLoss() # 定义损失函数 optimizer_sgd.zero_grad() # 梯度清零 outputs = model(inputs) # 前向传播 loss = criterion(outputs, targets) # 计算损失 loss.backward() # 反向传播,计算梯度 optimizer_sgd.step() # 更新模型参数 print(f"Loss after SGD step: {loss.item():.4f}")

SGD 的参数:

  • lr (learning rate): 学习率,控制参数更新步长,是 SGD 最重要的超参数。

  • momentum (动量): 加速 SGD 在相关方向上的学习,并抑制振荡,有助于更快地收敛。

  • weight_decay (权重衰减): L2 正则化,防止过拟合。

  • dampening (阻尼): 用于动量的阻尼,通常不常用。

  • nesterov (Nesterov 动量): 一种改进的动量版本,可以提高收敛速度。

SGD 的优缺点:

优点:

  • 简单有效: 算法简单,易于理解和实现。

  • 计算效率高: 每次迭代计算量相对较小,尤其是在数据量较大时。

  • 泛化性能好: 在某些情况下,SGD 的泛化性能优于更复杂的优化器。

缺点:

  • 收敛速度慢: 在平坦区域或梯度变化剧烈的区域,收敛速度可能较慢。

  • 容易陷入局部最小值: 对于非凸优化问题,SGD 可能陷入局部最小值而无法找到全局最优解。

  • 对学习率敏感: 学习率的选择对 SGD 的性能影响很大,需要仔细调参。

适用场景:

  • 适用于大规模数据集和计算资源有限的场景。

  • 作为其他优化器的基准对比。

  • 在模型结构较为简单,对收敛速度要求不高的情况下。

Mermaid 图示 - SGD 参数更新:

2.5.2.2 Adam (Adaptive Moment Estimation)

Adam 是一种自适应学习率优化器,它结合了 Momentum 和 RMSprop 的优点,能够根据参数的历史梯度信息动态调整学习率。Adam 广泛应用于各种深度学习任务,被认为是目前最流行的优化器之一。

算法原理:

Adam 算法维护了每个参数的两个移动平均值:

  1. 一阶矩估计 (first moment estimate) m_t: 类似于 Momentum 中的动量,是对梯度均值的估计。

  2. 二阶矩估计 (second moment estimate) v_t: 类似于 RMSprop 中的平方梯度均值,是对梯度方差的估计。

Adam 根据这两个矩估计值来调整每个参数的学习率。对于梯度变化平缓的参数,学习率会增大;对于梯度变化剧烈的参数,学习率会减小。

公式:

参数更新公式如下:

m_t = β_1 * m_{t-1} + (1 - β_1) * g_t v_t = β_2 * v_{t-1} + (1 - β_2) * g_t^2 m̂_t = m_t / (1 - β_1^t) v̂_t = v_t / (1 - β_2^t) θ_{t+1} = θ_t - η * m̂_t / (√v̂_t + ε)

其中:

  • g_t: 当前时刻 t 的梯度

  • m_t, v_t: 一阶矩和二阶矩的移动平均值

  • β_1, β_2: 矩估计的指数衰减率 (通常设置为接近 1 的值,如 0.9 和 0.999)

  • η: 学习率

  • ε: 防止分母为零的小常数 (通常设置为 1e-8)

  • m̂_t, v̂_t: 偏差校正后的一阶矩和二阶矩估计

PyTorch 代码示例:

# 1. 模型已定义 (SimpleNet) # 2. 定义优化器 - Adam optimizer_adam = optim.Adam(model.parameters(), lr=0.001) # lr 为学习率 # 3. 示例训练循环 (简化版) - 与 SGD 示例相同,只需替换优化器 inputs = torch.randn(64, 10) targets = torch.randint(0, 2, (64,)) criterion = nn.CrossEntropyLoss() optimizer_adam.zero_grad() outputs = model(inputs) loss = criterion(outputs, targets) loss.backward() optimizer_adam.step() print(f"Loss after Adam step: {loss.item():.4f}")

Adam 的参数:

  • lr (learning rate): 学习率,通常比 SGD 小,如 0.001 或 0.0001。

  • betas (β_1, β_2): 一阶矩和二阶矩估计的指数衰减率,默认值分别为 (0.9, 0.999)。

  • eps (ε): 防止分母为零的小常数,默认值为 1e-8。

  • weight_decay (权重衰减): L2 正则化,防止过拟合。

  • amsgrad (AMSGrad): 是否使用 AMSGrad 变体,AMSGrad 可以提高 Adam 在某些情况下的收敛性。

Adam 的优缺点:

优点:

  • 自适应学习率: 能够自动调整每个参数的学习率,无需手动精细调整。

  • 收敛速度快: 通常比 SGD 收敛速度更快。

  • 效果稳定: 对超参数不敏感,鲁棒性较好。

  • 适用于各种场景: 在各种深度学习任务中表现良好。

缺点:

  • 可能泛化性稍差: 在某些情况下,Adam 的泛化性能可能不如 SGD。

  • 需要更多内存: 需要维护一阶矩和二阶矩估计,占用更多内存。

适用场景:

  • 几乎适用于所有深度学习任务,尤其是模型结构复杂、数据量较大的场景。

  • 作为默认优化器的首选。

  • 在需要快速收敛和对超参数不敏感的场景。

Mermaid 图示 - Adam 参数更新:

2.5.2.3 RMSprop (Root Mean Square Propagation)

RMSprop 也是一种自适应学习率优化器,与 Adam 类似,它也能够根据参数的历史梯度信息动态调整学习率。RMSprop 主要解决了 Adagrad 优化器学习率下降过快的问题,在实践中也表现出良好的性能。

算法原理:

RMSprop 算法维护了每个参数的平方梯度移动平均值,并用该平均值来调整学习率。

公式:

参数更新公式如下:

s_t = ρ * s_{t-1} + (1 - ρ) * g_t^2 θ_{t+1} = θ_t - η / (√s_t + ε) * g_t

其中:

  • g_t: 当前时刻 t 的梯度

  • s_t: 平方梯度移动平均值

  • ρ: 平方梯度移动平均的指数衰减率 (通常设置为接近 1 的值,如 0.9)

  • η: 学习率

  • ε: 防止分母为零的小常数 (通常设置为 1e-8)

PyTorch 代码示例:

# 1. 模型已定义 (SimpleNet) # 2. 定义优化器 - RMSprop optimizer_rmsprop = optim.RMSprop(model.parameters(), lr=0.001) # lr 为学习率 # 3. 示例训练循环 (简化版) - 与 SGD 和 Adam 示例相同,只需替换优化器 inputs = torch.randn(64, 10) targets = torch.randint(0, 2, (64,)) criterion = nn.CrossEntropyLoss() optimizer_rmsprop.zero_grad() outputs = model(inputs) loss = criterion(outputs, targets) loss.backward() optimizer_rmsprop.step() print(f"Loss after RMSprop step: {loss.item():.4f}")

RMSprop 的参数:

  • lr (learning rate): 学习率,通常与 Adam 类似,如 0.001 或 0.0001。

  • alpha (ρ): 平方梯度移动平均的指数衰减率,默认值为 0.99。

  • eps (ε): 防止分母为零的小常数,默认值为 1e-8。

  • weight_decay (权重衰减): L2 正则化,防止过拟合。

  • momentum (动量): 可以添加动量项,进一步加速收敛。

  • centered (Centered RMSprop): 是否使用 Centered RMSprop 变体,Centered RMSprop 可以提高 RMSprop 在某些情况下的性能。

RMSprop 的优缺点:

优点:

  • 自适应学习率: 能够自动调整每个参数的学习率。

  • 收敛速度快: 通常比 SGD 收敛速度更快。

  • 解决了 Adagrad 学习率下降过快的问题: 通过指数衰减平均平方梯度,避免学习率过早降为零。

  • 效果稳定: 对超参数不敏感,鲁棒性较好。

缺点:

  • 可能泛化性稍差: 在某些情况下,RMSprop 的泛化性能可能不如 SGD。

  • 参数较多: 相比 SGD,RMSprop 的参数更多,需要更多内存。

适用场景:

  • 适用于各种深度学习任务,尤其是在循环神经网络 (RNN) 等模型中表现良好。

  • 作为 Adam 的替代选择。

  • 在需要快速收敛和对超参数不敏感的场景。

Mermaid 图示 - RMSprop 参数更新:

2.5.3 优化器参数详解

不同的优化器有不同的参数,但一些核心参数是通用的,理解这些参数对于有效使用优化器至关重要。

  • 学习率 (Learning Rate, lr): 学习率是最重要的超参数之一,它控制着参数更新的步长。学习率过大可能导致模型震荡,难以收敛;学习率过小则可能导致收敛速度过慢。通常需要根据具体任务和优化器类型进行调整。

  • 动量 (Momentum): 动量用于加速 SGD 在相关方向上的学习,并抑制振荡。它通过累积之前的梯度信息,使得参数更新更加平滑和稳定。动量值通常设置为 0.9 或 0.99。

  • 权重衰减 (Weight Decay): 权重衰减是一种正则化技术,也称为 L2 正则化。它通过在损失函数中添加模型参数的平方和项,惩罚模型参数过大,从而防止过拟合。权重衰减值通常设置为 0.0001 或 0.0005。

  • β_1, β_2 (Adam 参数): Adam 优化器中用于控制一阶矩和二阶矩估计的指数衰减率。默认值分别为 0.9 和 0.999。

  • ρ (RMSprop 参数): RMSprop 优化器中用于控制平方梯度移动平均的指数衰减率。默认值为 0.99。

  • ε (Epsilon): 一个很小的常数,用于防止在除法运算中分母为零,通常设置为 1e-8。

2.5.4 优化器的使用实践

在 PyTorch 中使用优化器非常简单,主要包括以下几个步骤:

  1. 初始化优化器: 根据选择的优化器类型,实例化相应的优化器对象,并将模型参数和优化器超参数传入。

  2. 梯度清零 (optimizer.zero_grad()): 在每次迭代开始前,需要将优化器的梯度缓冲区清零,防止梯度累积。

  3. 反向传播 (loss.backward()): 计算损失函数关于模型参数的梯度。

  4. 参数更新 (optimizer.step()): 根据优化器算法,利用梯度信息更新模型参数。

  5. 学习率调整 (可选): 根据训练过程中的情况,动态调整学习率,例如使用学习率衰减策略。

代码示例 - 完整的训练循环 (包含优化器):

import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, TensorDataset # 1. 定义模型 (SimpleNet - 已定义) # 2. 准备数据 (示例数据) X_train = torch.randn(1000, 10) y_train = torch.randint(0, 2, (1000,)) train_dataset = TensorDataset(X_train, y_train) train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True) # 3. 定义损失函数 criterion = nn.CrossEntropyLoss() # 4. 定义优化器 (Adam) optimizer = optim.Adam(model.parameters(), lr=0.001) # 5. 训练循环 epochs = 10 for epoch in range(epochs): for inputs, targets in train_loader: optimizer.zero_grad() # 梯度清零 outputs = model(inputs) # 前向传播 loss = criterion(outputs, targets) # 计算损失 loss.backward() # 反向传播 optimizer.step() # 参数更新 print(f"Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}") print("Training finished!")

学习率调整 (Learning Rate Scheduling)

学习率调整是一种常用的优化技巧,可以在训练过程中动态调整学习率,以提高模型性能和收敛速度。PyTorch 提供了 torch.optim.lr_scheduler 模块,包含多种学习率调整策略,例如:

  • StepLR: 每隔一定步数将学习率乘以一个衰减系数。

  • MultiStepLR: 在指定的步数将学习率乘以一个衰减系数。

  • ExponentialLR: 每个 epoch 将学习率乘以一个衰减系数。

  • CosineAnnealingLR: 使用余弦函数周期性地调整学习率。

  • ReduceLROnPlateau: 当验证集损失不再下降时,降低学习率。

示例 - 使用 StepLR 学习率衰减:

# ... (模型, 数据, 损失函数, 优化器 定义) ... # 定义学习率调度器 - StepLR (每 3 个 epoch 学习率乘以 0.1) scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.1) # 训练循环 epochs = 10 for epoch in range(epochs): for inputs, targets in train_loader: optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, targets) loss.backward() optimizer.step() scheduler.step() # 在每个 epoch 结束时更新学习率 print(f"Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}, LR: {optimizer.param_groups[0]['lr']:.6f}") print("Training finished!")

保存和加载优化器状态

与模型参数类似,优化器的状态 (例如动量缓冲区、矩估计等) 也需要保存和加载,以便在恢复训练或进行模型部署时使用。PyTorch 提供了 optimizer.state_dict()optimizer.load_state_dict() 方法来实现优化器状态的保存和加载。

# 保存优化器状态 torch.save(optimizer.state_dict(), 'optimizer.pth') # 加载优化器状态 optimizer.load_state_dict(torch.load('optimizer.pth'))

2.5.5 优化器的选择与调优

如何选择合适的优化器:

没有一个优化器能够适用于所有任务。选择合适的优化器需要根据具体任务、模型结构和数据集特点进行尝试和选择。

  • Adam: 通常是首选的默认优化器,适用于大多数深度学习任务,尤其是模型结构复杂、数据量较大的场景。

  • SGD: 在数据量较小或模型结构简单的情况下,SGD 可能表现良好,并且泛化性能可能更优。如果计算资源有限,SGD 也是一个不错的选择。

  • RMSprop: 在循环神经网络 (RNN) 等模型中表现良好,可以作为 Adam 的替代选择。

优化器超参数调优:

优化器的超参数 (例如学习率、动量、权重衰减等) 对模型性能影响很大,需要进行调优。常用的调优方法包括:

  • 手动调参: 根据经验和实验结果,手动调整超参数。

  • 网格搜索 (Grid Search): 在预定义的超参数网格中,遍历所有可能的组合,并选择性能最佳的组合。

  • 随机搜索 (Random Search): 在超参数空间中随机采样一定数量的组合,并选择性能最佳的组合。

  • 贝叶斯优化 (Bayesian Optimization): 使用贝叶斯方法对超参数空间进行建模,并选择下一个最有希望的超参数组合进行尝试。

常见问题与解决方案:

  • 训练不收敛或收敛速度慢: 可能是学习率过大或过小,可以尝试调整学习率。也可以尝试更换优化器,例如从 SGD 切换到 Adam 或 RMSprop。

  • 模型过拟合: 可以尝试增加权重衰减 (weight decay) 值,或者使用更强的正则化方法。

  • 梯度消失或梯度爆炸: 可能是模型结构或激活函数选择不当,可以尝试调整模型结构或使用 ReLU 等激活函数。也可以尝试使用梯度裁剪 (gradient clipping) 技术来缓解梯度爆炸问题。

2.5.6 总结

优化器是深度学习模型训练的核心组件之一。理解不同优化器的原理、特点和适用场景,并掌握优化器的使用方法和调优技巧,对于构建高性能的深度学习模型至关重要。在 PyTorch 中,torch.optim 模块提供了丰富的优化器选择,结合学习率调整和超参数调优等技巧,可以有效地训练各种复杂的深度学习模型。希望本节的详细介绍能够帮助读者更好地理解和应用 PyTorch 中的优化器,提升模型构建能力。


发布者: 作者: 转发
评论区 (0)
U