深入理解d2l-ai中的随机梯度下降(SGD)算法
引言
随机梯度下降(Stochastic Gradient Descent, SGD)是深度学习中最基础也最常用的优化算法之一。本文将从理论到实践,全面剖析SGD的工作原理、实现细节以及在实际应用中的各种变体。
什么是随机梯度下降
从梯度下降说起
在传统的梯度下降(Gradient Descent)中,我们计算整个训练集上的平均损失函数:
$$f(\mathbf{x}) = \frac{1}{n} \sum_{i = 1}^n f_i(\mathbf{x})$$
然后沿着梯度的反方向更新参数:
$$\mathbf{x} \leftarrow \mathbf{x} - \eta \nabla f(\mathbf{x})$$
其中η是学习率。这种方法计算成本高,特别是当数据集很大时。
随机梯度下降的核心思想
SGD的核心改进在于每次迭代只随机采样一个样本(或一小批样本)计算梯度:
$$\mathbf{x} \leftarrow \mathbf{x} - \eta \nabla f_i(\mathbf{x})$$
这种方法的优势在于:
- 计算复杂度从O(n)降到了O(1)
- 随机梯度是完整梯度的无偏估计
- 随机性可以帮助跳出局部极小值
SGD的实现与可视化
让我们通过一个简单的二维示例来观察SGD的行为:
def f(x1, x2): # 目标函数
return x1**2 + 2*x2**2
def f_grad(x1, x2): # 梯度函数
return 2*x1, 4*x2
def sgd(x1, x2, s1, s2, f_grad):
g1, g2 = f_grad(x1, x2)
# 添加随机噪声模拟SGD
g1 += np.random.normal(0, 1)
g2 += np.random.normal(0, 1)
eta_t = eta * lr() # 动态学习率
return (x1 - eta_t*g1, x2 - eta_t*g2, 0, 0)
从可视化结果可以看出,SGD的轨迹比标准梯度下降更加"嘈杂",这是因为它使用的是单个样本的梯度而非整个数据集的平均梯度。
学习率调度策略
固定学习率往往不是最优选择,动态调整学习率可以显著改善收敛性。常见的调度策略包括:
- 分段常数:在特定epoch后降低学习率
- 指数衰减:η(t) = η₀·e^(-λt)
- 多项式衰减:η(t) = η₀·(βt + 1)^(-α)
让我们比较不同调度策略的效果:
# 指数衰减学习率
def exponential_lr():
global t
t += 1
return math.exp(-0.1*t)
# 多项式衰减学习率
def polynomial_lr():
global t
t += 1
return (1 + 0.1*t)**(-0.5)
实验表明,多项式衰减通常能取得更好的平衡,既不会过早停止优化,也不会收敛过慢。
收敛性分析(凸函数情形)
对于凸目标函数,我们可以证明SGD的收敛性。假设:
- 随机梯度的L₂范数有界:∥∇fᵢ(x)∥ ≤ L
- 初始参数与最优解的距离:r² = ∥x₁ - x*∥²
经过推导可以得到收敛速度的上界:
$$E[R(\bar{x})] - R^* ≤ \frac{r^2 + L^2∑η_t^2}{2∑η_t}$$
选择η = r/(L√T)时,收敛速度为O(1/√T)。
实际应用中的注意事项
- 采样方式:通常采用不放回抽样而非有放回抽样,以减少方差
- 批量大小:小批量(mini-batch)通常比单样本更稳定
- 学习率选择:需要实验调整,太大导致震荡,太小收敛慢
- 非凸问题:在深度学习中,SGD可能收敛到局部极小值而非全局最优
总结
随机梯度下降是深度学习优化的基石算法。理解其工作原理、收敛性质和各种变体对于有效训练神经网络至关重要。虽然理论分析主要在凸函数框架下进行,但实际应用中的许多洞见仍然适用。在实践中,结合适当的学习率调度和批量策略,SGD能够高效地训练大规模深度学习模型。
思考题
- 尝试不同的学习率调度策略,观察对收敛速度的影响
- 比较有放回抽样和不放回抽样的效果差异
- 思考如何处理梯度分量差异过大的情况
- 对于非凸函数,SGD可能陷入哪些优化困境?如何改进?
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考