跳至主要内容

PyTorch 中的 AdamW 优化器教程

了解 AdamW 优化器如何通过将权重衰减与梯度更新解耦来提升模型表现。本文解释 Adam 与 AdamW 的关键差异及其使用场景,并提供在 PyTorch 中实现 AdamW 的分步指南。
· 12分钟 阅读

使用 AI 探索

优化算法在深度学习中至关重要:它们在训练过程中微调模型权重以最小化损失函数。其中一个这样的算法是Adam 优化器。

Adam 因结合了动量和自适应学习率的优势而在深度学习中大受欢迎。这让它在训练深度神经网络时十分高效。同时,它对超参数的调节需求很少,因此在各种任务中都易于上手且效果良好。

2017 年,Ilya Loshchilov 和 Frank Hutter 在论文“Decoupled Weight Decay Regularization(解耦权重衰减正则化)”中提出了更先进的 Adam 版本。他们将其命名为 AdamW,其突出之处在于将权重衰减与梯度更新过程解耦。与 Adam 相比,这一分离是关键改进,有助于提升模型的泛化能力。

AdamW 在现代深度学习应用中愈发重要,尤其是在处理大规模模型时。其在调控权重更新方面的优越能力,使其在对性能与稳定性要求很高的任务中得到广泛采用。

在本教程中,我们将重点介绍 Adam 与 AdamW 的关键差异及各自的使用场景,并提供在PyTorch 中实现 AdamW 的分步指南。

Adam 与 AdamW

Adam 和 AdamW 都是深度学习中广泛使用的自适应优化器。它们之间的主要区别在于如何处理权重正则化,这会影响它们在不同情境下的效果。

Adam 通过结合动量和自适应学习率实现高效优化,但其引入 L2 正则化的方式可能会阻碍性能。AdamW 通过将权重衰减与学习率更新解耦来解决这一问题,为大型模型提供更有效的方法并提升泛化能力。权重衰减是一种 L2 正则化形式,用于惩罚模型中的大权重。Adam 将权重衰减并入梯度更新过程,而 AdamW 则在梯度更新之后单独应用。

它们的其他差异还包括:

Adam 与 AdamW 的关键差异

尽管这两种优化器都旨在管理动量并动态调整学习率,但在处理权重衰减方面存在根本差异。

在 Adam 中,权重衰减作为梯度更新的一部分被间接应用,这可能会无意中改变学习动态并干扰优化过程。而 AdamW 则将权重衰减与梯度步骤分离,确保正则化直接作用于参数,而不改变自适应学习机制。

这种设计带来了更精确的正则化,帮助模型获得更好的泛化能力,尤其是在涉及大型且复杂数据集的任务中。因此,两者在使用场景上往往差异明显。

Adam 的使用场景

当正则化不那么关键,或当计算效率优先于泛化能力时,Adam 表现更好。示例如下:

  • 较小的神经网络。对于使用小型 CNN(卷积神经网络)在 MNIST 或 CIFAR-10 等数据集上进行基础图像分类的任务,模型复杂度较低,Adam 可在无需大量正则化的情况下高效优化。
  • 简单的回归问题。在特征有限的简单回归任务中,例如用线性回归预测房价,Adam 可在无需高级正则化技巧的情况下快速收敛。
  • 早期原型设计。在模型开发的初期阶段,需要快速试验时,Adam 便于在简单架构上快速迭代,帮助研究者在无需额外调参正则化的负担下定位潜在问题。
  • 噪声较少的数据。在噪声很少的干净数据集上工作时,例如用于情感分析的精心清洗的文本数据,Adam 能有效学习模式,而不易因过拟合而需要更强的正则化。
  • 短训练周期。在有时间约束的场景中,例如面向实时应用的快速部署,Adam 的高效优化能快速给出令人满意的结果,即便在泛化上可能尚未完全最优。

AdamW 的使用场景

当过拟合值得关注且模型规模较大时,AdamW 表现突出。例如:

  • 大规模 Transformer。在自然语言处理任务中,例如在大规模语料上微调 GPT 等模型,AdamW 对权重衰减的有效管理可防止过拟合,确保更好的泛化。
  • 复杂的计算机视觉模型。对于在 ImageNet 等大型数据集上训练的深度 CNN 任务,AdamW 通过解耦权重衰减来帮助维持模型的稳定与性能,这对取得高精度至关重要。
  • 多任务学习。在同时训练多个任务的情境下,AdamW 提供的灵活性有助于处理多样数据集,并避免对单一任务的过拟合。
  • 生成式模型。在训练 GAN 时,生成器与判别器的平衡至关重要,AdamW 改进的正则化有助于稳定训练并提升生成结果质量。
  • 强化学习。在需要适应复杂环境并学习鲁棒策略的强化学习应用中,AdamW 有助于减轻对特定状态或动作的过拟合,提升在多样情境下的整体表现。

AdamW 相较 Adam 的优势

为什么要用 AdamW 而不是 Adam?很简单。AdamW 提供了多项关键优势,尤其在复杂建模场景中能显著提升表现。

它弥补了 Adam 优化器的一些局限,使其在优化上更有效,并有助于改进模型训练与鲁棒性。

以下是一些突出的优势:

  • 解耦的权重衰减。通过将权重衰减与梯度更新分离,AdamW 允许更精确地控制正则化,从而带来更好的模型泛化。
  • 更强的泛化能力。AdamW 降低了过拟合风险,尤其在大规模模型中,使其适用于涉及大数据集与复杂架构的任务。
  • 训练过程更稳定。AdamW 的设计有助于在整个训练过程中保持稳定性,这对需要谨慎调参的模型尤为关键。
  • 可扩展性。AdamW 在模型扩展方面尤为有效,能够在不牺牲性能的前提下应对深层网络的复杂性,适用于最前沿的架构。

AdamW 的工作原理

AdamW 的核心优势在于其处理权重衰减的方式:将其与 Adam 典型的自适应梯度更新解耦。此调整确保正则化直接作用于模型权重,在不负面影响学习率动态的情况下提升泛化能力。

该优化器建立在 Adam 的自适应特性之上,保留动量与按参数调整学习率的优势。独立地应用权重衰减,解决了 Adam 的一个关键缺陷:在正则化时倾向于影响梯度更新。这一分离使 AdamW 即使在复杂与大规模模型中也能保持稳定学习,同时将过拟合控制在合理范围。

在接下来的章节中,我们将探讨权重衰减与正则化背后的理论,以及支撑 AdamW 优化过程的数学原理。

权重衰减与 L2 正则化的理论

L2 正则化是一种用于防止过拟合的技术。其通过在损失函数中添加惩罚项来抑制过大的权重值,从而促使模型更简单,并在新数据上有更好的泛化表现。

在传统优化器(如 Adam)中,权重衰减作为梯度更新的一部分被应用,这会无意间影响学习率,并可能导致次优表现。

AdamW 通过将权重衰减与梯度计算解耦来改进这一点。换言之,AdamW 不在梯度更新期间应用权重衰减,而是将其作为单独步骤,在梯度更新后直接作用于权重。这可避免权重衰减干扰优化过程,从而带来更稳定的训练和更好的泛化。

AdamW 的数学基础

AdamW 通过改变权重衰减的应用方式来修改传统的 Adam 优化器。其核心方程可表示如下:

  1. 动量与自适应学习率:与 Adam 类似,AdamW 使用动量和自适应学习率,基于梯度与平方梯度的滑动平均来计算参数更新。

动量与自适应学习率的公式

  1. 偏差校正估计:对一阶与二阶矩估计进行偏差校正,公式如下:

偏差校正估计的公式

  1. 解耦权重衰减的参数更新:在 AdamW 中,权重衰减在梯度更新之后直接作用于参数。其更新规则为:

解耦权重衰减的参数更新

其中,η 为学习率,λ 为权重衰减系数,θt​ 表示参数。这个解耦的权重衰减项 λθt​ 确保正则化独立于梯度更新进行,这是与 Adam 的关键区别。

在 PyTorch 中实现 AdamW

在PyTorch 中实现 AdamW 十分直观;本节将提供完整的配置指南。请按以下步骤学习如何使用 Adam 优化器高效微调模型。

PyTorch 中 AdamW 的分步指南

注意:本教程假设您已安装 PyTorch。请参考文档以获取指导。

步骤 1:导入必要的库

import torch
import torch.nn as nn
import torch.optim as optim
Import torch.nn.functional as F

步骤 2:定义模型

class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1)
        self.pool = nn.MaxPool2d(kernel_size=2, stride=2, padding=0) 
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1) 
        self.fc1 = nn.Linear(64 * 8 * 8, 128) 
        self.fc2 = nn.Linear(128, 10) 

    def forward(self, x): 
        x = self.pool(F.relu(self.conv1(x))) 
        x = self.pool(F.relu(self.conv2(x))) 
        x = x.view(-1, 64 * 8 * 8) 
        x = F.relu(self.fc1(x)) 
        x = self.fc2(x) 

步骤 3:设置超参数

learning_rate = 1e-4
weight_decay = 1e-2
num_epochs = 10 # number of epochs

步骤 4:初始化 AdamW 优化器并设置损失函数

optimizer = optim.AdamW(model.parameters(), lr=learning_rate, weight_decay=weight_decay) 
criterion = nn.CrossEntropyLoss()

大功告成!

现在,您已经可以开始训练您的 CNN 模型了,下一节我们将正式开练。

实战示例:使用 AdamW 微调模型

上文我们已定义模型、设置超参数、初始化优化器(AdamW),并配置了损失函数。

要训练模型,我们还需要导入几个模块:

from torch.utils.data import DataLoader # provides an iterable of the dataset
import torchvision
import torchvision.transforms as transforms

接下来,定义数据集与数据加载器。本示例使用 CIFAR-10 数据集:

# Define transformations for the training set
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
])
# Load CIFAR-10 dataset
train_dataset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
val_dataset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
# Create data loaders
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)

由于我们已经定义了模型,下一步就是实现训练循环,用 AdamW 来优化模型。

代码如下:

for epoch in range(num_epochs):
    model.train()  # Set the model to training mode
    running_loss = 0.0
    for inputs, labels in train_loader:
        optimizer.zero_grad()  # Clear gradients
        outputs = model(inputs)  # Forward pass
        loss = criterion(outputs, labels)  # Calculate loss
        loss.backward()  # Backward pass
        optimizer.step()  # Update weights
        running_loss += loss.item()
    print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss/len(train_loader):.4f}')

最后一步是在我们先前创建的验证集上评估模型表现。

代码如下:

model.eval()  # Set the model to evaluation mode
correct = 0
total = 0
with torch.no_grad():
    for inputs, labels in val_loader:
        outputs = model(inputs)  # Forward pass
        _, predicted = torch.max(outputs.data, 1)  # Get predicted class
        total += labels.size(0)  # Update total samples
        correct += (predicted == labels).sum().item()  # Update correct predictions
accuracy = 100 * correct / total
print(f'Validation Accuracy: {accuracy:.2f}%')

就是这样。

您现在已经掌握了如何在 PyTorch 中实现 AdamW。

AdamW 的常见使用场景

好的,我们已经说明了 AdamW 因更有效地管理权重衰减而比其前身 Adam 更受欢迎。

但这个优化器有哪些常见用法呢?

本节我们将逐一介绍……

大规模深度学习模型

AdamW 尤其适合训练 BERT、GPT 及其他 Transformer 架构等大型模型。此类模型通常拥有数百万甚至数十亿参数,往往需要能够处理复杂权重更新与泛化挑战的高效优化算法。

计算机视觉与 NLP 任务

AdamW 已成为许多涉及 CNN 的视觉任务与涉及 Transformer 的 NLP 任务中的首选优化器。其防止过拟合的能力使其非常适合包含大型数据集与复杂架构的任务。通过解耦权重衰减,AdamW 避免了 Adam 在过度正则化模型时遇到的问题。

AdamW 的超参数调优

超参数调优是指为那些控制机器学习模型训练、但不从数据中学习的参数选择最佳取值的过程。这些参数直接影响模型如何优化与收敛。

在 AdamW 中,正确调优这些超参数对于实现高效训练、避免过拟合以及确保模型在未见数据上良好泛化至关重要。

本节我们将探讨如何微调 AdamW 的关键超参数以获得最佳性能。

选择学习率与权重衰减的最佳实践

学习率是一个超参数,控制每个训练步中根据损失梯度对模型权重的调整幅度。较高的学习率能加快训练,但可能导致越过最优权重;较低的学习率则允许更精细的调整,但训练可能更慢,或陷入局部最小值。

另一方面,权重衰减是一种用于通过惩罚模型中的大权重来防止过拟合的正则化技术。也就是说,权重衰减在训练期间添加与模型权重大小成比例的小惩罚,有助于降低模型复杂度并提升对新数据的泛化能力。

为 AdamW 选择合适的学习率与权重衰减,可参考:

  1. 从适中的学习率开始——对于 AdamW,约 1e-3 的学习率通常是不错的起点。可根据模型的收敛情况调整:若难以收敛就降低,若训练过慢则提高。
  2. 尝试不同的权重衰减。可从 1e-2 到 1e-4 的范围入手,具体取决于模型规模与数据集。更大的、复杂模型可适当提高权重衰减以防止过拟合,而较小模型可能需要较弱的正则化。
  3. 使用学习率调度。实现学习率调度(如分段衰减或余弦退火),在训练进行中动态降低学习率,帮助模型在临近收敛时更好地微调参数。
  4. 监控性能。持续跟踪模型在验证集上的表现。若观察到过拟合,可考虑提高权重衰减;若训练损失进入平台期,可降低学习率以获得更好的优化。

结语

AdamW 已成为深度学习中最有效的优化器之一,尤其适用于大规模模型。这归功于其将权重衰减与梯度更新解耦的能力。也就是说,AdamW 的设计改进了正则化,并帮助模型在处理复杂架构与大规模数据集时获得更好的泛化。

正如本教程所示,在 PyTorch 中实现 AdamW 十分简便——只需在 Adam 的基础上做少量调整。不过,超参数调优仍然是最大化 AdamW 效能的关键步骤。在学习率与权重衰减之间找到合适的平衡,对于确保优化器高效工作且不使模型过拟合或欠拟合至关重要。

现在您已经足够了解如何在自己的模型中应用 AdamW。若想继续学习,请查看以下资源:

AdamW 优化器常见问答

AdamW 优化器是什么?它与 Adam 有何不同?

AdamW 是对 Adam 的改进版本,它将权重衰减与梯度更新过程解耦,从而带来更好的泛化与更少的过拟合。

我应该在何时使用 AdamW 而非 Adam?

AdamW 适用于如 Transformer 等大规模模型或易于过拟合的任务;而对于更简单的模型与数据集,Adam 往往已足够。

如何在 PyTorch 中实现 AdamW?

您可以在 PyTorch 中使用 torch.optim.AdamW 函数来实现 AdamW,并指定学习率与权重衰减等参数。

为什么权重衰减在 AdamW 中很重要?

权重衰减通过惩罚过大的权重来帮助防止过拟合。在 AdamW 中,它被直接作用于权重,从而在不影响学习率的情况下改进正则化。

AdamW 是否可同时用于 NLP 与计算机视觉任务?

可以。AdamW 对 NLP 和计算机视觉任务都很有效,尤其适用于 BERT 和 CNN 等大型模型,在这些任务中,合理的权重正则化对性能至关重要。

Kurtis Pykes

作者

Kurtis Pykes

主题