1、SGD原理介绍
随机梯度下降(Stochastic Gradient Descent,简称SGD)是基于梯度的一种优化算法,用于寻找损失函数最小化的参数配置。SGD通过计算每个样本的梯度来更新参数,并在每次更新中随机选择一个或一批样本。SGD的原理相对简单,它通过计算损失函数对每个训练样本的梯度来更新参数。具体步骤如下:
1、随机选择一个训练样本;
2、计算该样本的梯度;
3、使用梯度值和学习率来更新参数;
4、重复以上步骤,直至达到收敛条件或达到指定迭代次数。
SGD在深度学习中广泛应用于模型的训练过程,特别是在大规模数据集和复杂模型的情况下。由于SGD的简单性和高效性,它成为了优化算法领域的基准方法。
2、SGD参数介绍
torch.optim.SGD(params, lr=<required parameter>, momentum=0, dampening=0, weight_decay=0, nesterov=False)
params(必须参数): 这是一个包含了需要优化的参数(张量)的迭代器,例如模型的参数 model.parameters()。
lr(必须参数): 学习率(learning rate)。它是一个正数,控制每次参数更新的步长。较小的学习率会导致收敛较慢,较大的学习率可能导致震荡或无法收敛。
momentum(默认值为 0): 动量(momentum)是一个用于加速 SGD 收敛的参数。它引入了上一步梯度的指数加权平均。通常设置在 0 到 1 之间。当 momentum 大于 0 时,算法在更新时会考虑之前的梯度,有助于加速收敛。
dampening(默认值为 0): 阻尼项,用于减缓动量的速度。在某些情况下,为了防止动量项引起的震荡,可以设置一个小的 dampening 值。
weight_decay(默认值为 0): 权重衰减,也称为 L2 正则化项。它用于控制参数的幅度,以防止过拟合。通常设置为一个小的正数。
nesterov(默认值为 False): Nesterov 动量。当设置为 True 时,采用 Nesterov 动量更新规则。Nesterov 动量在梯度更新之前先进行一次预测,然后在计算梯度更新时使用这个预测。
3、SGD应用介绍
import torch
import torch.optim as optim
# 定义模型和损失函数
model = torch.nn.Linear(10, 1)
criterion = torch.nn.MSELoss()
# 定义优化器
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=0.0001)
# 在训练循环中使用优化器
for epoch in range(epochs):
# Forward pass
output = model(input_data)
loss = criterion(output, target)
# Backward pass and optimization
optimizer.zero_grad()
loss.backward()
optimizer.step()
在上述示例中,创建了一个线性模型,使用均方误差损失,并使用 torch.optim.SGD 作为优化器。在训练循环中,通过执行前向传播、反向传播和优化步骤来更新模型参数。
4、SGD手动实现
PyTorch中SGD的源码比较复杂,很多C++底层的优化,我们可以手动实现一个简化版的SGD,从而加深对SGD优化器的理解。
import torch
from .optimizer import Optimizer, required
class SGD(Optimizer):
def __init__(self, params, lr=required, momentum=0, dampening=0,
weight_decay=0, nesterov=False):
if lr is not required and lr < 0.0:
raise ValueError("Invalid learning rate: {}".format(lr))
if momentum < 0.0:
raise ValueError("Invalid momentum value: {}".format(momentum))
if weight_decay < 0.0:
raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
defaults = dict(lr=lr, momentum=momentum, dampening=dampening,
weight_decay=weight_decay, nesterov=nesterov)
if nesterov and (momentum <= 0 or dampening != 0):
raise ValueError("Nesterov momentum requires a momentum and zero dampening")
super(SGD, self).__init__(params, defaults)
def __setstate__(self, state):
super(SGD, self).__setstate__(state)
for group in self.param_groups:
group.setdefault('nesterov', False)
def step(self, closure=None):
"""Performs a single optimization step.
Arguments:
closure (callable, optional): A closure that reevaluates the model
and returns the loss.
"""
loss = None
if closure is not None:
loss = closure()
for group in self.param_groups:
weight_decay = group['weight_decay']
momentum = group['momentum']
dampening = group['dampening']
nesterov = group['nesterov']
for p in group['params']:
if p.grad is None:
continue
d_p = p.grad.data
if weight_decay != 0:
d_p = d_p.add(weight_decay, p.data)
if momentum != 0:
param_state = self.state[p]
if 'momentum_buffer' not in param_state:
buf = param_state['momentum_buffer'] = torch.clone(d_p).detach()
else:
buf = param_state['momentum_buffer']
buf.mul_(momentum).add_(1 - dampening, d_p)
if nesterov:
d_p = d_p.add(momentum, buf)
else:
d_p = buf
p.data.add_(-group['lr'], d_p)
return loss
5、SGD的改进
凡事都有两面性,SGD每次仅使用一个样本或一批样本进行模型参数更新,相比于使用全部样本的批量梯度下降(BGD),计算成本更低。但是,由于SGD每次更新只使用一个样本或一批样本,更新方向可能受噪声的干扰而波动较大。
对于SGD,合适的学习率的选择至关重要。学习率过大可能导致振荡,学习率过小则收敛速度缓慢。为了克服SGD的缺点,研究者们提出了一系列改进的优化方法:
1、动量法(Momentum):引入一个动量项,使更新方向在梯度变化较大的维度上具有一定的惯性,从而加速收敛并减少震荡。
2、学习率衰减(Learning Rate Decay):随着训练的进行,逐渐减小学习率,有助于在接近损失函数最小值时平稳收敛。
3、Adagrad:自适应学习率的方法,根据参数在每次迭代中的梯度大小自适应地调整学习率。
4、RMSprop:对Adagrad进行改进,通过平均梯度的平方来调整学习率,从而缓解训练过程中的震荡。
5、Adam:结合了Momentum和Adagrad的优点,并进行了一定的改进和修正,是目前最流行和常用的优化算法之一。