本文讨论了经典梯度下降法之外的常用优化器,它们都是基于梯度下降法进行了改进的方法,它们往往会比经典梯度下降法取得更好的优化速度和精度。掌握这些优化器的用法,并取得初步实践经验是提高深度学习应用能力的重要内容。读者在学习了基本知识后,要求AI大模型来辅助设置参数,提高效率。本文最后还简要说明了梯度消散和梯度爆炸问题。

本专栏之四讨论了梯度下降法,它是优化机器学习模型参数的基本方法。以它为基础,人们又发展出了许多改进方法,它们有不同的特点和适用场景。了解并实践这些内容,有助于选择合适的优化器及其参数以加快优化速度和精度。本文数学公式较多,读者也可以先跳过理论推导,通过示例掌握应用方法。

以梯度下降法为基础的改进方法,主要是从增加动量和调整步长两方面着手。

1.动量优化算法

在经典力学中,动量(Momentum)表示为物体的质量和速度的乘积,体现为物体运动的惯性。在梯度下降法(见本专栏之四)中,如果使梯度下降的过程具有一定的“动量”,保持原方向运动的一定的 “惯性”,则有可能在下降的过程中“冲过”小的“洼地”,避免陷入极小值点,如图11-1所示。其中,在第3个点处,其梯度负方向为蓝色虚线实箭头所示,而在动量的影响下,仍然保持向左的惯性,从而“冲出”了局部极小点。

图11-1 加入动量的梯度下降过程示意
在这里插入图片描述

加入动量优化,梯度下降法还可以克服前进路线振荡的问题,从而加快收敛速度。

梯度下降的迭代关系式(本专栏之四中的式4-1)为:
xi+1=xi+α⋅(−df(x)dx)x=xi=xi−α⋅df(x)dx∣x=xi x_{i+1} = x_i + \alpha \cdot \left( -\frac{df(x)}{dx} \right)_{x=x_i} = x_i - \alpha \cdot \frac{df(x)}{dx} \bigg|_{x=x_i} xi+1=xi+α(dxdf(x))x=xi=xiαdxdf(x) x=xi
对照上式,加入动量的梯度下降法迭代关系式为:
θi+1=βθi−α⋅df(x)dx∣x=xixi+1=xi+θi+1(式11-1) \theta_{i+1} = \beta \theta_i - \alpha \cdot \left. \dfrac{df(x)}{dx} \right|_{x=x_i} \\[10pt] x_{i+1} = x_i + \theta_{i+1} \tag{式11-1} θi+1=βθiαdxdf(x) x=xixi+1=xi+θi+1(11-1)
可见,加入动量之后的前进量 θi+1\theta_{i+1}θi+1 是由上一步的前进量 θi\theta_iθi 和新梯度值 df(x)dx∣x=xi\frac{df(x)}{dx}\big|_{x=x_i}dxdf(x) x=xi 的加权和,其中 β\betaβ 决定了保留上一步前进量的大小,称为动量系数。初始 θ0=0\theta_0 = 0θ0=0

加入动量的梯度下降的迭代关系式还有一种改进方法,称为 NAG(Nesterov accelerated gradient):

θi+1=βθi−α⋅(df(x)dx)x=xi+βθixi+1=xi+θi+1(式11-2) \theta_{i+1} = \beta\theta_i - \alpha \cdot \left( \frac{df(x)}{dx} \right)_{x=x_i+\beta\theta_i}\\[10pt] x_{i+1} = x_i + \theta_{i+1}\tag{式11-2} θi+1=βθiα(dxdf(x))x=xi+βθixi+1=xi+θi+1(11-2)

该方法计算梯度的点发生了变化,即在 xi+βθix_i + \beta\theta_ixi+βθi 处计算梯度,而不是原 xix_ixi 点处。它可以理解为先按“惯性”前进一小步 βθi\beta\theta_iβθi,再计算梯度。这种方法在每一步都往前多走了一小步,有时可以加快收敛速度。

在 PyTorch 中,动量优化和 NAG 方法主要通过设置torch.optim.SGD的momentum(动量系数)和nesterov两个参数的来实现。

代码11-1.1示例了它们的用法。

代码11-1.1 动量优化算法应用示例

### 1.导入和设置环境
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import datetime
from torchvision import datasets, transforms

# 设置随机种子
torch.manual_seed(0)

### 2.训练样本和验证样本数据预处理
# 数据预处理方式
transform = transforms.Compose([
transforms.ToTensor(), # 转换为 torch.Tensor
])

# 加载MNIST数据集
train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform)
val_dataset = datasets.MNIST('./data', train=False, transform=transform)

# 样本拉平、归一化后
X_train = train_dataset.data.float().view(-1, 784) / 255.0
y_train = train_dataset.targets
X_val = val_dataset.data.float().view(-1, 784) / 255.0
y_val = val_dataset.targets

# 转换为独热编码
y_train = torch.nn.functional.one_hot(y_train, num_classes=10).float()
y_val = torch.nn.functional.one_hot(y_val, num_classes=10).float()

# 创建数据加载器
batch_size = 200
train_loader = DataLoader(TensorDataset(X_train, y_train), batch_size=batch_size, shuffle=True)
val_loader = DataLoader(TensorDataset(X_val, y_val), batch_size=batch_size)

### 3.定义神经网络模型
# relu-relu-softmax
class MNISTModel(nn.Module):
 def __init__(self):
     super(MNISTModel, self).__init__()
     self.fc1 = nn.Linear(784, 784)
     self.fc2 = nn.Linear(784, 784)
     self.fc3 = nn.Linear(784, 10)
     self.relu = nn.ReLU()
     self.softmax = nn.Softmax()

 def forward(self, x):
     x = self.relu(self.fc1(x))
     x = self.relu(self.fc2(x))
     x = self.softmax(self.fc3(x))
     return x
### 4.采用不同的优化器进行对比试验
def train_process():
    # 训练模型,开始计时
    start_time = datetime.datetime.now()
    
    epochs = 10
    for epoch in range(epochs):
        # 每轮中的训练
        model.train()
        train_loss = 0.0
        for batch_X, batch_y in train_loader:
            optimizer.zero_grad()
            outputs = model(batch_X)
            loss = criterion(outputs, batch_y)
            loss.backward()
            optimizer.step()
            train_loss += loss.item()
        # 看一下该轮训练后的效果
        model.eval()
        correct = 0
        total = 0
        with torch.no_grad():
            for batch_X, batch_y in train_loader:
                outputs = model(batch_X)
                _, predicted = torch.max(outputs.data, 1) # 模型预测值的独热编码
                _, labels = torch.max(batch_y.data, 1) # 真实标签值的独热编码
                total += labels.size(0)
                correct += (predicted == labels).sum().item() # 准确率
        print(f'Epoch {epoch+1}/{epochs}, 对训练样本进行预测的准确率(Train Acc): {100*correct/total:.2f}%')
    # 训练结束,终止计时
    end_time = datetime.datetime.now()
    print(f"训练用时: {end_time - start_time}")
    
criterion = nn.MSELoss()
model = MNISTModel() 
optimizer = optim.SGD(model.parameters(), lr=0.15) # 基本梯度下降法优化器
train_process()

输出:

Epoch 1/10, 对训练样本进行预测的准确率(Train Acc): 46.40%
Epoch 2/10, 对训练样本进行预测的准确率(Train Acc): 42.39%
Epoch 3/10, 对训练样本进行预测的准确率(Train Acc): 51.85%
Epoch 4/10, 对训练样本进行预测的准确率(Train Acc): 58.24%
Epoch 5/10, 对训练样本进行预测的准确率(Train Acc): 74.46%
Epoch 6/10, 对训练样本进行预测的准确率(Train Acc): 82.62%
Epoch 7/10, 对训练样本进行预测的准确率(Train Acc): 85.90%
Epoch 8/10, 对训练样本进行预测的准确率(Train Acc): 87.47%
Epoch 9/10, 对训练样本进行预测的准确率(Train Acc): 88.53%
Epoch 10/10, 对训练样本进行预测的准确率(Train Acc): 89.14%
训练用时: 0:01:19.254175

criterion = nn.MSELoss()
model = MNISTModel() 
optimizer = optim.SGD(model.parameters(), lr=0.15, momentum=0.9) # 启用动量优化的梯度下降法优化器
train_process()

输出:

Epoch 1/10, 对训练样本进行预测的准确率(Train Acc): 88.11%
Epoch 2/10, 对训练样本进行预测的准确率(Train Acc): 91.55%
Epoch 3/10, 对训练样本进行预测的准确率(Train Acc): 92.71%
Epoch 4/10, 对训练样本进行预测的准确率(Train Acc): 93.86%
Epoch 5/10, 对训练样本进行预测的准确率(Train Acc): 94.56%
Epoch 6/10, 对训练样本进行预测的准确率(Train Acc): 95.15%
Epoch 7/10, 对训练样本进行预测的准确率(Train Acc): 95.60%
Epoch 8/10, 对训练样本进行预测的准确率(Train Acc): 95.87%
Epoch 9/10, 对训练样本进行预测的准确率(Train Acc): 96.29%
Epoch 10/10, 对训练样本进行预测的准确率(Train Acc): 96.86%
训练用时: 0:01:25.797210

criterion = nn.MSELoss()
model = MNISTModel() 
optimizer = optim.SGD(model.parameters(), lr=0.15, momentum=0.9, nesterov=True) # 启用NAG的梯度下降法优化器
train_process()

输出:

Epoch 1/10, 对训练样本进行预测的准确率(Train Acc): 87.90%
Epoch 2/10, 对训练样本进行预测的准确率(Train Acc): 91.56%
Epoch 3/10, 对训练样本进行预测的准确率(Train Acc): 92.46%
Epoch 4/10, 对训练样本进行预测的准确率(Train Acc): 93.72%
Epoch 5/10, 对训练样本进行预测的准确率(Train Acc): 94.41%
Epoch 6/10, 对训练样本进行预测的准确率(Train Acc): 95.05%
Epoch 7/10, 对训练样本进行预测的准确率(Train Acc): 95.57%
Epoch 8/10, 对训练样本进行预测的准确率(Train Acc): 96.07%
Epoch 9/10, 对训练样本进行预测的准确率(Train Acc): 96.58%
Epoch 10/10, 对训练样本进行预测的准确率(Train Acc): 96.87%
训练用时: 0:01:24.306429

2.步长优化算法

在讨论梯度下降法时,简要说明了步长对梯度下降的影响。针对步长,人们研究了不少的优化算法。

2.1 Adagrad算法

Adagrad(Adaptive Gradient)算法的梯度迭代关系式为:

ri=ri−1+(df(x)dx∣x=xi)2xi=xi−1−lrri+ϵ⋅df(x)dx∣x=xi(式11-3) r_i = r_{i-1} + \left(\frac{df(\boldsymbol{x})}{d\boldsymbol{x}}\bigg|_{\boldsymbol{x}=\boldsymbol{x}_i}\right)^2 \\ x_i = x_{i-1} - \frac{lr}{\sqrt{r_i + \epsilon}} \cdot \frac{df(x)}{dx}\bigg|_{\boldsymbol{x}=\boldsymbol{x}_i} \tag{式11-3} ri=ri1+(dxdf(x) x=xi)2xi=xi1ri+ϵ lrdxdf(x) x=xi(11-3)

其中,rir_iri 称为累积平方梯度,它是到当前为止所有梯度的平方和,初值为 000。这里的(df(x)dx∣x=xi)x=xi2\left( \frac{df(x)}{dx}\bigg|_{\boldsymbol{x}=\boldsymbol{x}_i} \right)_{x=x_i}^2(dxdf(x) x=xi)x=xi2表示向量元素的平方向量:df(x)dx∣x=xi⨀df(x)dx∣x=xi\frac{df(x)}{dx} \bigg|_{x=x_i} \bigodot \frac{df(x)}{dx} \bigg|_{x=x_i}dxdf(x) x=xidxdf(x) x=xi,下同。超参数 lrlrlr 为事先设置的步长,ϵ\epsilonϵ 为很小的常数。初始 r0=0r_0 = 0r0=0

可见,随着迭代次数的增加,rir_iri 越来越大,实际步长 lrri+1+ϵ\frac{lr}{\sqrt{r_{i+1}}+\epsilon}ri+1 +ϵlr 越来越小,可以防止在极小值附近来回振荡。

PyTorch中实现该算法的是torch.optim.Adagrad(),它的lr、eps和initial_accumulator_value三个参数分别设置 lrlrlrϵ\epsilonϵr0r_0r0,另外还设置了步长衰减参数lr_decay和L2正则化参数weight_decay。

该算法的应用示例如代码11-1.2所示。

代码11-1.2 Adagrad优化器应用示例

criterion = nn.MSELoss()
model = MNISTModel() 
optimizer = optim.Adagrad(model.parameters(), lr=0.01) # Adagrad优化器
train_process()

输出:

Epoch 1/10, 对训练样本进行预测的准确率(Train Acc): 96.76%
Epoch 2/10, 对训练样本进行预测的准确率(Train Acc): 98.04%
Epoch 3/10, 对训练样本进行预测的准确率(Train Acc): 98.64%
Epoch 4/10, 对训练样本进行预测的准确率(Train Acc): 98.97%
Epoch 5/10, 对训练样本进行预测的准确率(Train Acc): 99.20%
Epoch 6/10, 对训练样本进行预测的准确率(Train Acc): 99.36%
Epoch 7/10, 对训练样本进行预测的准确率(Train Acc): 99.51%
Epoch 8/10, 对训练样本进行预测的准确率(Train Acc): 99.58%
Epoch 9/10, 对训练样本进行预测的准确率(Train Acc): 99.64%
Epoch 10/10, 对训练样本进行预测的准确率(Train Acc): 99.70%
训练用时: 0:01:19.308178

2.2 RMSProp算法

RMSProp(Root Mean Square Prop)算法的原始形式对Adagrad算法进行了简单改进,增加了一个系数 rhorhorho 来控制历史信息与当前梯度的比例:

ri=rho⋅ri−1+(1−rho)(df(x)dx∣x=xi)2xi=xi−1−lrri+ϵ⋅df(x)dx∣x=xi(式11-4) r_i = rho \cdot r_{i-1} + (1 - rho) \left( \frac{df(\boldsymbol{x})}{d\boldsymbol{x}} \bigg|_{\boldsymbol{x}=\boldsymbol{x}_i} \right)^2 \\ \boldsymbol{x}_i = \boldsymbol{x}_{i-1} - \frac{lr}{\sqrt{r_i + \epsilon}} \cdot \frac{df(\boldsymbol{x})}{d\boldsymbol{x}} \bigg|_{\boldsymbol{x}=\boldsymbol{x}_i} \tag{式11-4} ri=rhori1+(1rho)(dxdf(x) x=xi)2xi=xi1ri+ϵ lrdxdf(x) x=xi(11-4)

对RMSProp的原始形式增加动量因子如下:

ri=rho⋅ri−1+(1−rho)(df(x)dx∣x=xi)2θi=βθi−1+lrri+ϵ⋅df(x)dx∣x=xixi=xi−1−θi(式11-5) r_i = rho \cdot r_{i-1} + (1 - rho) \left( \frac{df(\boldsymbol{x})}{d\boldsymbol{x}} \bigg|_{\boldsymbol{x}=\boldsymbol{x}_i} \right)^2 \\ \boldsymbol{\theta}_i = \beta \boldsymbol{\theta}_{i-1} + \frac{lr}{\sqrt{r_i + \epsilon}} \cdot \frac{df(\boldsymbol{x})}{d\boldsymbol{x}} \bigg|_{\boldsymbol{x}=\boldsymbol{x}_i} \\ \boldsymbol{x}_i = \boldsymbol{x}_{i-1} - \boldsymbol{\theta}_i \tag{式11-5} ri=rhori1+(1rho)(dxdf(x) x=xi)2θi=βθi1+ri+ϵ lrdxdf(x) x=xixi=xi1θi(11-5)

其中,β\betaβ为动量系数。

下式为 RMSProp 的中心化版本:

si=rho⋅si−1+(1−rho)df(x)dx∣x=xiri=rho⋅ri−1+(1−rho)(df(x)dx∣x=xi)2θi=βθi−1+lrri−si2+ϵ⋅df(x)dx∣x=xixi=xi−1−θi(式11-6) \boldsymbol{s}_i = rho \cdot \boldsymbol{s}_{i-1} + (1 - rho) \frac{df(\boldsymbol{x})}{d\boldsymbol{x}} \bigg|_{\boldsymbol{x}=\boldsymbol{x}_i} \\ r_i = rho \cdot r_{i-1} + (1 - rho) \left( \frac{df(\boldsymbol{x})}{d\boldsymbol{x}} \bigg|_{\boldsymbol{x}=\boldsymbol{x}_i} \right)^2\\ \boldsymbol{\theta}_i = \beta \boldsymbol{\theta}_{i-1} + \frac{lr}{\sqrt{r_i - \boldsymbol{s}_i^2 + \epsilon}} \cdot \frac{df(\boldsymbol{x})}{d\boldsymbol{x}} \bigg|_{\boldsymbol{x}=\boldsymbol{x}_i}\\ \boldsymbol{x}_i = \boldsymbol{x}_{i-1} - \boldsymbol{\theta}_i\tag{式11-6} si=rhosi1+(1rho)dxdf(x) x=xiri=rhori1+(1rho)(dxdf(x) x=xi)2θi=βθi1+risi2+ϵ lrdxdf(x) x=xixi=xi1θi(11-6)

它增加了一个累积梯度 si\boldsymbol{s}_isi,在求动量因子 θi\boldsymbol{\theta}_iθi 时,通过 si\boldsymbol{s}_isirir_iri 进行了一个中心化过程:ri−si2初始s0=0,r0=0r_i - \boldsymbol{s}_i^2 \quad 初始 \boldsymbol{s}_0 = \boldsymbol{0}, r_0 = 0risi2初始s0=0,r0=0

RMSProp 在实践中效果较好,得到了较多的应用,在 PyTorch中也提供了实现类,可以通过设置相关参数来实现以上三个版本的 RMSProp 算法。

该算法的应用示例如代码11-1.3所示。

代码11-1.3 RMSProp 优化器应用示例

criterion = nn.MSELoss()
model = MNISTModel() 
optimizer = torch.optim.RMSprop(model.parameters(), lr=0.001, momentum=0, centered=False) # RMSprop优化器原始版本
train_process()

输出:

Epoch 1/10, 对训练样本进行预测的准确率(Train Acc): 95.33%
Epoch 2/10, 对训练样本进行预测的准确率(Train Acc): 98.02%
Epoch 3/10, 对训练样本进行预测的准确率(Train Acc): 98.21%
Epoch 4/10, 对训练样本进行预测的准确率(Train Acc): 98.82%
Epoch 5/10, 对训练样本进行预测的准确率(Train Acc): 98.91%
Epoch 6/10, 对训练样本进行预测的准确率(Train Acc): 99.33%
Epoch 7/10, 对训练样本进行预测的准确率(Train Acc): 99.35%
Epoch 8/10, 对训练样本进行预测的准确率(Train Acc): 99.36%
Epoch 9/10, 对训练样本进行预测的准确率(Train Acc): 99.36%
Epoch 10/10, 对训练样本进行预测的准确率(Train Acc): 99.54%
训练用时: 0:02:55.587366

criterion = nn.MSELoss()
model = MNISTModel() 
optimizer = torch.optim.RMSprop(
    model.parameters(),
    lr=0.001,           
    alpha=0.9,          # 梯度平滑系数
    momentum=0.8,       # 添加动量加速收敛
    weight_decay=1e-5,  # L2正则化
    centered=False      
)
train_process()

输出:

Epoch 1/10, 对训练样本进行预测的准确率(Train Acc): 93.75%
Epoch 2/10, 对训练样本进行预测的准确率(Train Acc): 96.19%
Epoch 3/10, 对训练样本进行预测的准确率(Train Acc): 95.52%
Epoch 4/10, 对训练样本进行预测的准确率(Train Acc): 97.39%
Epoch 5/10, 对训练样本进行预测的准确率(Train Acc): 97.03%
Epoch 6/10, 对训练样本进行预测的准确率(Train Acc): 97.13%
Epoch 7/10, 对训练样本进行预测的准确率(Train Acc): 97.80%
Epoch 8/10, 对训练样本进行预测的准确率(Train Acc): 97.58%
Epoch 9/10, 对训练样本进行预测的准确率(Train Acc): 97.05%
Epoch 10/10, 对训练样本进行预测的准确率(Train Acc): 97.30%
训练用时: 0:07:34.488462

criterion = nn.MSELoss()
model = MNISTModel() 
optimizer = torch.optim.RMSprop(
    model.parameters(),
    lr=0.001,           
    alpha=0.9,          # 梯度平滑系数
    momentum=0.8,       # 添加动量加速收敛
    weight_decay=1e-5,  # L2正则化
    centered=True       # 中心化
)
train_process()

输出:

Epoch 1/10, 对训练样本进行预测的准确率(Train Acc): 91.83%
Epoch 2/10, 对训练样本进行预测的准确率(Train Acc): 95.06%
Epoch 3/10, 对训练样本进行预测的准确率(Train Acc): 96.34%
Epoch 4/10, 对训练样本进行预测的准确率(Train Acc): 95.43%
Epoch 5/10, 对训练样本进行预测的准确率(Train Acc): 96.05%
Epoch 6/10, 对训练样本进行预测的准确率(Train Acc): 97.22%
Epoch 7/10, 对训练样本进行预测的准确率(Train Acc): 97.39%
Epoch 8/10, 对训练样本进行预测的准确率(Train Acc): 97.39%
Epoch 9/10, 对训练样本进行预测的准确率(Train Acc): 97.46%
Epoch 10/10, 对训练样本进行预测的准确率(Train Acc): 96.95%
训练用时: 0:06:58.417171

3.结合动量和步长优化的算法

Adam(Adaptive moment estimation)算法结合了AdaGrad算法和RMSProp算法的优势,它的迭代关系式为:
si=β1⋅si−1+(1−β1)df(x)dx∣x=xiri=β2⋅ri−1+(1−β2)(df(x)dx∣x=xi)2s^i=si1−β1ir^i=ri1−β2ixi=xi−1−lr⋅s^ir^i+ϵ(式11-7) \boldsymbol{s}_i = \beta_1 \cdot \boldsymbol{s}_{i-1} + (1 - \beta_1) \frac{df(\boldsymbol{x})}{d\boldsymbol{x}} \bigg|_{\boldsymbol{x}=\boldsymbol{x}_i}\\ r_i = \beta_2 \cdot r_{i-1} + (1 - \beta_2) \left( \frac{df(\boldsymbol{x})}{d\boldsymbol{x}} \bigg|_{\boldsymbol{x}=\boldsymbol{x}_i} \right)^2\\ \hat{\boldsymbol{s}}_i = \frac{\boldsymbol{s}_i}{1 - \beta_1^i}\\ \hat{r}_i = \frac{r_i}{1 - \beta_2^i}\\ \boldsymbol{x}_i = \boldsymbol{x}_{i-1} - \frac{lr \cdot \hat{\boldsymbol{s}}_i}{\sqrt{\hat{r}_i} + \epsilon}\tag{式11-7} si=β1si1+(1β1)dxdf(x) x=xiri=β2ri1+(1β2)(dxdf(x) x=xi)2s^i=1β1isir^i=1β2irixi=xi1r^i +ϵlrs^i(11-7)

式中,β1,β2∈[0,1]\beta_1, \beta_2 \in [0, 1]β1,β2[0,1] 分别为累积梯度的动量系数和累积平方梯度的动量系数。初始 s0=0,r0=0\boldsymbol{s}_0 = \boldsymbol{0}, r_0 = 0s0=0,r0=0

对比式11-7和式11-6,可知Adam算法是RMSProp中心化版本的进一步改进。

对累积平方梯度 rir_iri 可以取历史最大值用来更新,称为 AMSGrad,此时 Adam 算法为:

si=β1⋅si−1+(1−β1)df(x)dx∣x=xiri=β2⋅ri−1+(1−β2)(df(x)dx∣x=xi)2ri′=max⁡(ri−1′,ri)s^i=si1−β1ir^i=ri′1−β2ixi=xi−1−lr⋅s^ir^i+ϵ(式11-8) \boldsymbol{s}_i = \beta_1 \cdot \boldsymbol{s}_{i-1} + (1 - \beta_1) \frac{df(\boldsymbol{x})}{d\boldsymbol{x}} \bigg|_{\boldsymbol{x}=\boldsymbol{x}_i}\\ r_i = \beta_2 \cdot r_{i-1} + (1 - \beta_2) \left( \frac{df(\boldsymbol{x})}{d\boldsymbol{x}} \bigg|_{\boldsymbol{x}=\boldsymbol{x}_i} \right)^2\\ r_i' = \max(r_{i-1}', r_i)\\ \hat{\boldsymbol{s}}_i = \frac{\boldsymbol{s}_i}{1 - \beta_1^i}\\ \hat{r}_i = \frac{r_i'}{1 - \beta_2^i}\\ \boldsymbol{x}_i = \boldsymbol{x}_{i-1} - \frac{lr \cdot \hat{\boldsymbol{s}}_i}{\sqrt{\hat{r}_i} + \epsilon}\tag{式11-8} si=β1si1+(1β1)dxdf(x) x=xiri=β2ri1+(1β2)(dxdf(x) x=xi)2ri=max(ri1,ri)s^i=1β1isir^i=1β2irixi=xi1r^i +ϵlrs^i(11-8)

初始 r0′=0r_0' = 0r0=0

Adam 算法综合效果较好,在PyTorch 中有相应的实现类,通过设置参数可以实现上述两个算法。

该算法的应用示例如代码11-1.4所示。

代码11-1.4 Adam 优化器应用示例

criterion = nn.MSELoss()
model = MNISTModel() 
optimizer = optim.Adam(model.parameters(), lr=0.001) # Adam优化器
train_process()

输出:

Epoch 1/10, 对训练样本进行预测的准确率(Train Acc): 95.18%
Epoch 2/10, 对训练样本进行预测的准确率(Train Acc): 97.85%
Epoch 3/10, 对训练样本进行预测的准确率(Train Acc): 98.42%
Epoch 4/10, 对训练样本进行预测的准确率(Train Acc): 98.30%
Epoch 5/10, 对训练样本进行预测的准确率(Train Acc): 99.01%
Epoch 6/10, 对训练样本进行预测的准确率(Train Acc): 99.27%
Epoch 7/10, 对训练样本进行预测的准确率(Train Acc): 99.30%
Epoch 8/10, 对训练样本进行预测的准确率(Train Acc): 98.91%
Epoch 9/10, 对训练样本进行预测的准确率(Train Acc): 99.43%
Epoch 10/10, 对训练样本进行预测的准确率(Train Acc): 99.30%
训练用时: 0:01:35.730784

criterion = nn.MSELoss()
model = MNISTModel() 
optimizer = optim.Adam(model.parameters(), lr=0.001, amsgrad=True) # Adam优化器AMSGrad版本
train_process()

输出:

Epoch 1/10, 对训练样本进行预测的准确率(Train Acc): 96.44%
Epoch 2/10, 对训练样本进行预测的准确率(Train Acc): 97.58%
Epoch 3/10, 对训练样本进行预测的准确率(Train Acc): 98.43%
Epoch 4/10, 对训练样本进行预测的准确率(Train Acc): 98.59%
Epoch 5/10, 对训练样本进行预测的准确率(Train Acc): 98.85%
Epoch 6/10, 对训练样本进行预测的准确率(Train Acc): 99.06%
Epoch 7/10, 对训练样本进行预测的准确率(Train Acc): 99.37%
Epoch 8/10, 对训练样本进行预测的准确率(Train Acc): 99.42%
Epoch 9/10, 对训练样本进行预测的准确率(Train Acc): 99.42%
Epoch 10/10, 对训练样本进行预测的准确率(Train Acc): 99.56%
训练用时: 0:01:41.566383

不同的优化算法有不同的特点,读者可通过更多的练习来摸索它们的应用方法和特点。

4.梯度消散与梯度爆炸问题

本专栏中,本文之前的几篇文章讨论了多层神经网络的激活函数、损失函数和优化器等问题。这里简要说明一下与这几个问题都有关的在多层神经网络训练中非常重要的梯度消散和梯度爆炸问题。

在校对误差反向传播的过程中,如果偏导数较小,在多次连乘之后,校对误差会趋近于0,导致梯度也趋近于0,前面层的参数无法得到有效更新,称之为梯度消散。梯度消散会使得增加再多的层也无法提高效果,甚至反而会降低。

相反,如果偏导数较大,则会在反向传播的过程中呈指数级增长,导致溢出,无法计算,网络不稳定,称之为梯度爆炸。

梯度消散和梯度爆炸只在层次较多的网络中出现,常用的解决方法包括尽量使用合适的激活函数(如Relu函数,它在正数部分导数为1)和损失函数;适当的优化器;预训练;合适的网络模型(有些网络模型具有防消散和爆炸能力,将在后文讨论);梯度截断等等。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐