torch.optim.SGD

torch.optim.SGD 是 PyTorch 中用于实现随机梯度下降(Stochastic Gradient Descent, SGD)优化算法的类。SGD 是一种常用的优化方法,广泛用于训练深度学习模型。

1. 基本语法

1
torch.optim.SGD(params, lr=<required parameter>, momentum=0, dampening=0, weight_decay=0, nesterov=False)
  • params:要优化的模型参数,通常传递 model.parameters()
  • lr:学习率(learning rate),控制每次参数更新的步长。学习率是 SGD 的一个重要超参数。
  • momentum:动量项(Momentum),用于加速 SGD 收敛,尤其是在存在噪声的情况下。默认值为 0,表示不使用动量。
  • dampening:用于控制动量的衰减,默认值为 0,即没有衰减。
  • weight_decay:权重衰减(L2 正则化),用于控制模型复杂度,防止过拟合。默认值为 0,表示不使用权重衰减。
  • nesterov:布尔值,表示是否使用 Nesterov 动量,默认值为 False,即不使用。

2. 随机梯度下降(SGD)

SGD 是一种逐个小批量(mini-batch)更新模型参数的优化方法。每次从数据集中随机抽取一个或多个样本进行梯度计算并更新参数。相比于标准的梯度下降(batch gradient descent)使用整个数据集计算梯度,SGD 能加速训练,但会带来一些噪声。

SGD 通过以下公式更新参数:
$$
\theta = \theta - \eta \nabla_{\theta}J(\theta)
$$

  • $\theta$ 是模型参数。
  • $\eta$ 是学习率。
  • $\nabla_{\theta}J(\theta)$ 是损失函数关于 $\theta$ 的梯度。

3. 带动量的 SGD

动量是 SGD 的一种改进版本,通过在梯度更新中引入动量项,动量可以减少训练过程中的震荡,加速收敛。动量项累积之前的梯度,使得优化方向更稳定。

带动量的 SGD 更新规则为:
$$
v_t = \mu v_{t-1} + \eta \nabla_{\theta}J(\theta)
$$

$$
\theta = \theta - v_t
$$
其中:

  • $v_t$ 是速度(velocity),它是在梯度上累积的动量。
  • $μ$ 是动量系数,表示之前更新的权重。通常取值范围为 [0, 1],如 0.9

4. 带 Nesterov 动量的 SGD

Nesterov 动量是一种动量的改进版本,它在参数更新时使用了提前更新的梯度,这样可以更好地调整学习方向。Nesterov 动量的更新公式为:
$$
v_t = \mu v_{t-1} + \eta \nabla_{\theta}J(\theta - \mu v_{t-1})
$$

$$
\theta = \theta - v_t
$$
在 PyTorch 中,你可以通过设置 nesterov=True 来使用 Nesterov 动量。

5. 参数说明

  • 学习率(lr:学习率是控制优化器步长的重要参数。值太大会导致训练不稳定,太小则会收敛过慢。通常,学习率会随着训练进行而调整(如使用学习率调度器)。

  • 动量(momentum:动量在优化过程中积累之前的梯度,使得参数更新不仅考虑当前的梯度,还考虑之前的梯度。常见值为 0.90.99

  • 权重衰减(weight_decay:权重衰减对应于 L2 正则化项,通常用于防止模型过拟合。它会对每次更新的权重进行一定的衰减。权重衰减公式如下:
    $$
    \theta = \theta - \eta (\nabla_{\theta}J(\theta) + \lambda \theta)
    $$
    其中 λ 是权重衰减因子,也就是 weight_decay

  • dampening:动量的衰减参数,通常与 momentum 一起使用。当 dampening 为非零值时,动量的更新会随着时间逐渐减小。默认 0 不衰减。

6. 示例代码

不带动量的 SGD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import torch
import torch.optim as optim

# 定义模型
model = torch.nn.Linear(2, 1)

# 定义优化器
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 训练循环
for epoch in range(100):
# 前向传播
outputs = model(torch.randn(10, 2))
loss = torch.mean((outputs - torch.randn(10, 1)) ** 2)
# 梯度清零
optimizer.zero_grad()
# 反向传播
loss.backward()
# 更新参数
optimizer.step()

带动量的 SGD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torch
import torch.optim as optim

# 定义模型
model = torch.nn.Linear(2, 1)

# 定义带动量的优化器
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

# 训练循环
for epoch in range(100):
outputs = model(torch.randn(10, 2))
loss = torch.mean((outputs - torch.randn(10, 1)) ** 2)

optimizer.zero_grad()
loss.backward()
optimizer.step()

带 Nesterov 动量的 SGD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torch
import torch.optim as optim

# 定义模型
model = torch.nn.Linear(2, 1)

# 定义带 Nesterov 动量的优化器
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, nesterov=True)

# 训练循环
for epoch in range(100):
outputs = model(torch.randn(10, 2))
loss = torch.mean((outputs - torch.randn(10, 1)) ** 2)

optimizer.zero_grad()
loss.backward()
optimizer.step()

7. 注意事项

  1. 学习率调整:SGD 对学习率敏感,通常需要在训练过程中使用调度器(如 torch.optim.lr_scheduler)来动态调整学习率。
  2. 小批量数据:SGD 通常在小批量(mini-batch)数据上运行,这可以减少内存消耗并提高模型更新频率。
  3. 动量设置:大多数情况下,使用 momentum=0.9 是一种常见的选择,尤其是当数据中有噪声时,动量能够加速收敛并减少震荡。

总结

torch.optim.SGD 是 PyTorch 中实现随机梯度下降(SGD)的优化器类。它可以通过引入动量和权重衰减等增强功能来改善模型的训练效果,特别是在深度学习中的应用。


torch.optim.Adam

torch.optim.Adam 是 PyTorch 中实现 Adam 优化算法的类。Adam(Adaptive Moment Estimation,自适应矩估计)是深度学习中一种非常流行的优化算法,它结合了动量法RMSProp 的优点,能够高效处理大规模的数据并且无需手动调整学习率。

1. 基本语法

1
torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)
  • params:要优化的参数(通常传递 model.parameters())。
  • lr:学习率,控制每次更新的步长。Adam 的默认学习率是 0.001,通常比 SGD 的默认学习率小,因为 Adam 会根据梯度变化自适应调整步长。
  • betas:一对控制一阶和二阶矩估计的系数。默认值为 (0.9, 0.999),分别对应一阶矩(动量项)和二阶矩(梯度平方的指数加权移动平均)。
  • eps:为了防止除以零,Adam 算法在更新时加上的一个非常小的数值。默认值是 1e-08
  • weight_decay:权重衰减,等价于 L2 正则化,用于防止模型过拟合。默认值为 0
  • amsgrad:布尔值,是否启用 AMSGrad 算法的改进版本。默认值为 False

2. Adam 优化算法原理

Adam 算法结合了 SGD 中的动量法和 RMSProp。与标准的梯度下降方法不同,Adam 使用两个动量项:一个是梯度的动量(类似于 SGD + momentum),另一个是梯度平方的动量,用来调整学习率。

一阶和二阶矩估计

Adam 主要通过一阶和二阶矩的指数移动平均值来进行参数更新:

  • 一阶矩估计:对梯度进行指数加权移动平均,类似于动量法。
  • 二阶矩估计:对梯度平方进行指数加权移动平均,用于调节学习率。

更新公式如下:

  1. 一阶矩动量(动量)
    $$
    m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t
    $$
    其中,m_t 是当前时刻的梯度一阶矩动量,g_t 是当前时刻的梯度,β_1 是一阶矩的衰减系数,通常取 0.9

  2. 二阶矩动量
    $$
    v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2
    $$
    其中,v_t 是当前时刻的梯度二阶矩动量,g_t^2 是当前时刻的梯度平方,β_2 是二阶矩的衰减系数,通常取 0.999

  3. 偏差修正:为了修正初始时的偏差,Adam 引入了以下修正公式:
    $$
    \hat{m_t} = \frac{m_t}{1 - \beta_1^t}, \quad \hat{v_t} = \frac{v_t}{1 - \beta_2^t}
    $$

  4. 参数更新
    $$
    \theta_t = \theta_{t-1} - \frac{\eta}{\sqrt{\hat{v_t}} + \epsilon} \hat{m_t}
    $$
    其中,θ_t 是模型的参数,η 是学习率,ε 是为了避免除零的数值(默认值 1e-8)。

3. 参数解释

  • lr(学习率):Adam 的学习率默认是 0.001,通常它不需要像 SGD 那样频繁调整,因为它对不同参数使用自适应学习率。如果要微调学习率,可以根据模型的具体情况调整。

  • betas(动量系数)

    • beta1=0.9:控制一阶矩估计的衰减率。较高的 beta1 值会平滑梯度更新,但可能会导致模型学习速度变慢。
    • beta2=0.999:控制二阶矩估计的衰减率,用于调整学习率。如果 beta2 取值太小,学习率会波动过大。
  • eps(小数值):用于数值稳定性,防止除以零的错误。通常不需要调整。

  • weight_decay(权重衰减):在 Adam 中,权重衰减等价于 L2 正则化,用于防止过拟合。较大的 weight_decay 值会对模型施加更强的正则化。

  • amsgrad:这个参数是 Adam 的一个改进版本。AMSGrad 通过记录历史最大值来保证优化器的收敛性。默认 False 即为标准的 Adam,True 时为 AMSGrad。

4. 示例代码

标准 Adam 优化器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import torch
import torch.optim as optim

# 定义一个简单的模型
model = torch.nn.Linear(2, 1)

# 定义 Adam 优化器
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练循环
for epoch in range(100):
outputs = model(torch.randn(10, 2))
loss = torch.mean((outputs - torch.randn(10, 1)) ** 2)

# 梯度清零
optimizer.zero_grad()

# 反向传播
loss.backward()

# 更新参数
optimizer.step()

带权重衰减和自适应动量的 Adam 优化器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torch
import torch.optim as optim

# 定义模型
model = torch.nn.Linear(2, 1)

# 带有权重衰减的 Adam 优化器
optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-4, betas=(0.9, 0.999))

# 训练循环
for epoch in range(100):
outputs = model(torch.randn(10, 2))
loss = torch.mean((outputs - torch.randn(10, 1)) ** 2)

optimizer.zero_grad()
loss.backward()
optimizer.step()

启用 AMSGrad 的 Adam 优化器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torch
import torch.optim as optim

# 定义模型
model = torch.nn.Linear(2, 1)

# 使用 AMSGrad 的 Adam 优化器
optimizer = optim.Adam(model.parameters(), lr=0.001, amsgrad=True)

# 训练循环
for epoch in range(100):
outputs = model(torch.randn(10, 2))
loss = torch.mean((outputs - torch.randn(10, 1)) ** 2)

optimizer.zero_grad()
loss.backward()
optimizer.step()

5. Adam 优化器的优缺点

优点

  1. 自适应学习率:Adam 的每个参数都拥有自适应的学习率,不同参数的学习率会根据其梯度更新情况进行自动调整。
  2. 动量:Adam 在参数更新中引入了动量项,有效地平滑了更新方向,减少了噪声影响,加快了收敛速度。
  3. 无需手动调参:相比于 SGD,Adam 对学习率的要求相对宽松,训练过程较为稳定。
  4. 高效计算:Adam 的计算效率高,占用内存小,非常适合大规模数据和模型的训练。

缺点

  1. 过拟合风险:由于 Adam 可以快速适应参数变化,可能会导致过拟合,尤其是当模型复杂度较高且训练数据量较少时。
  2. 学习率退化:在一些特定任务中(如自然语言处理任务),Adam 可能会导致学习率在训练后期衰减过快,从而减缓模型进一步优化的速度。
  3. 较差的泛化性能:有时 Adam 优化器训练出的模型在测试集上的表现不如 SGD 优化器,尤其是当数据量较大时。

6. Adam vs. SGD

  • SGD:具有更好的泛化能力,但收敛速度较慢,尤其在梯度较小或不平稳的情况下需要更大的调整。
  • Adam:收敛速度更快且更稳定,适合处理稀疏数据和高维度数据,但在某些情况下泛化能力不足

如 SGD。

7. 总结

torch.optim.Adam 是一种自适应优化算法,它结合了动量法和 RMSProp 的优点,使得优化过程更加高效且稳定。它适用于各种深度学习任务,尤其在处理大规模数据和高维度问题时表现出色。然而,在某些特定任务中,Adam 的泛化性能可能不如 SGD,因此在选择优化器时应根据具体任务进行调试和比较。