一、优化器的核心目标与基础问题
1. 核心目标
优化器的本质是高效更新模型参数(如权重 w、偏置 b),通过最小化损失函数(如均方误差),让模型预测结果更接近真实值。
2. 基础问题:梯度下降的 "数据量困境"
传统梯度下降需计算所有样本的梯度均值更新参数,但实际场景中样本量常达数万至亿级,会导致两大问题:
- 内存 / 显存不足(OOM):海量数据的存储与运算超出硬件承载能力;
- 计算效率极低:全量数据迭代一次耗时极久,无法快速调整参数。
为解决此问题,++梯度下降衍生出++ 3 种核心变体,也是后续优化器的 "基础框架":
|---------------|----------------------------------------------|--------------------------|-------------------------------------|--------------------|
| 梯度下降变体 | 核心逻辑 | 优点 | 缺点 | 适用场景 |
| 批量梯度下降(BGD) | 每次迭代用全部样本算梯度更新 | 梯度准确,易收敛到全局最优解 | 大规模数据内存超限,计算开销大 | 小数据集(样本数 < 1 万) |
| 随机梯度下降(SGD) | 每次迭代用1 个样本算梯度更新 | 数据量极小,计算开销极低 | 梯度随机性大,训练震荡明显,迭代次数激增 | 超大规模数据(快速近似优化) |
| 小批量梯度下降(MBGD) | 每次迭代用固定大小的 mini-batch(如 32/64/128) 算梯度更新 | 兼顾效率与内存,可按硬件调 batch size | 需手动调 batch size,震荡程度介于 BGD 与 SGD 之间 | 深度学习主流场景(90% 以上模型) |
关键辨析:实际应用中常将 "MBGD" 误称为 "SGD",但严格来说二者不同 ------SGD 仅用 1 个样本,MBGD 用多个样本(mini-batch)。
3. 批量大小(batch_size)与更新次数的关系
1 个 "epoch" 指遍历全部训练样本的过程,更新次数由 batch_size 决定:
- 例:总样本数 = 10
-
- batch_size=10(BGD):1 个 epoch 仅需 1 次更新;
-
- batch_size=2(MBGD):1 个 epoch 需 5 次更新(10÷2=5 个 mini-batch);
-
- batch_size=1(SGD):1 个 epoch 需 10 次更新。
二、经典优化器详解(从基础到进阶)
1. 随机梯度下降(SGD):优化器的 "基石"
(1)核心原理
基于 MBGD/SGD 框架,参数更新公式为:
w = w - lr × dw
b = b - lr × db
- lr(学习率):控制每次更新的 "步长",过大易震荡不收敛,过小收敛过慢(通常取 0.001/0.01);
- dw/db:当前 mini-batch 样本对 w/b 的梯度(损失函数对参数的偏导数)。
(2)代码实现(含核心逻辑)
python
import numpy as np
import matplotlib.pyplot as plt
# 1. 输入数据(散点样本,模拟线性回归任务)
points = np.array([[-0.5, 7.7], [1.8, 98.5], [0.9, 57.8], [0.4, 39.2], [-1.4, -15.7],
[-1.4, -37.3], [-1.8, -49.1], [1.5, 75.6], [0.4, 34.0], [0.8, 62.3]])
X = points[:, 0] # 特征(自变量)
Y = points[:, 1] # 标签(因变量)
# 2. 参数初始化(w初始为0,b初始为-1,学习率lr=0.001)
w = 0
b = -1
lr = 0.001
# 3. 损失函数(均方误差MSE):衡量预测值与真实值的差距
def loss_func(X, w, b):
pre_y = np.dot(X, w) + b # 前向传播:预测值=w×X + b
loss = np.mean((Y - pre_y) ** 2) # 均方误差
return loss
# 4. SGD优化器核心:按mini-batch更新参数
def SGD(points, w, b, lr, batch_size=1):
np.random.shuffle(points) # 每次迭代前打乱样本(避免顺序影响)
# 遍历所有mini-batch:从0到样本数,步长=batch_size
for num_batch in range(0, len(points), batch_size):
batch_points = points[num_batch:num_batch + batch_size, :] # 取当前mini-batch
batch_X = batch_points[:, 0]
batch_Y = batch_points[:, 1]
# 计算当前batch的梯度(dw:w的梯度,db:b的梯度)
pre_y = np.dot(batch_X, w) + b
dw = np.mean((pre_y - batch_Y) * batch_X) # 损失对w的偏导数
db = np.mean(pre_y - batch_Y) # 损失对b的偏导数
# 更新参数(核心公式)
w = w - lr * dw
b = b - lr * db
return w, b
# 5. 训练过程(迭代1000次,观察损失下降)
loss_history = []
for epoch in range(1000):
w, b = SGD(points, w, b, lr, batch_size=2) # 用MBGD(batch_size=2)
loss = loss_func(X, w, b)
loss_history.append(loss)
# 6. 可视化损失下降(验证优化效果)
plt.plot(range(1000), loss_history)
plt.xlabel("Epoch")
plt.ylabel("Loss (MSE)")
plt.title("SGD Training Loss Curve")
plt.show()
(3)SGD 的缺陷
- 震荡明显:每次更新仅依赖少量样本,梯度方向随机,参数更新路径 "忽左忽右";
- 收敛慢:步长(学习率)固定,后期接近最优解时仍可能因步长问题反复震荡,无法快速收敛。
2. SGD+Momentum(动量法):解决 SGD 的 "震荡与慢收敛"
(1)核心思路
引入 "动量(Momentum)" 概念 ------ 模拟物理中的 "惯性",让参数更新时 "记住历史梯度方向",平滑当前梯度的随机性,减少震荡、加速收敛。
(2)数学公式(分框架差异)
|------------|-------------------------|--------------------------|
| 框架 | 动量计算(v_t:t 时刻动量) | 参数更新公式(P_t:t 时刻参数,如 w/b) |
| TensorFlow | v_t = η·v_{t-1} + α·g_t | P_{t+1} = P_t - v_t |
| PyTorch | v_t = η·v_{t-1} + g_t | P_{t+1} = P_t - α·v_t |
- 符号说明:
-
- η(动量系数):控制历史梯度的影响程度,默认 0.9(越大惯性越强,历史影响越大);
-
- α(学习率):步长,同 SGD;
-
- g_t:t 时刻参数的梯度(dw/db);
-
- v_{t-1}:t-1 时刻的动量,初始值为 0(TensorFlow)或初始梯度(PyTorch)。
(3)核心优势:"平滑震荡,加速收敛"
- 平滑震荡:当当前梯度方向与历史动量方向一致时,动量会 "叠加" 当前梯度,让更新步长变大;当方向相反时,动量会 "抵消" 部分当前梯度,减少震荡;
- 加速收敛:在梯度方向稳定的区域(如损失函数的 "斜坡" 区域),动量会持续累积,让参数快速向最优解移动。
(4)直观理解(如图示)
- A 点:记录当前梯度方向(水平向右),动量 v_{A} = η・0 + α・g_A(初始 v=0);
- B 点:当前梯度方向为 "斜向左下",但 A 点的动量(水平向右)会与当前梯度叠加,最终更新方向指向 C 点(更平滑、更接近最优解);
- 本质:"记着之前的路,别被当前梯度带偏"。
3. AdaGrad(自适应梯度):解决 "学习率固定" 问题
(1)核心思路
SGD 和 Momentum 用统一的学习率更新所有参数,但模型中不同参数的 "更新频率" 不同:
- 高频更新参数(如高频特征对应的 w):需小学习率,避免被单次样本影响过大;
- 低频更新参数(如低频特征对应的 w):需大学习率,从少量样本中多学信息。
AdaGrad 通过为每个参数适配独立的学习率,解决学习率固定的问题 ------ 引入 "二阶动量"(梯度平方累积和),动态调整学习率。
(2)数学公式
- 二阶动量计算(S_t:t 时刻参数的梯度平方累积和):
S_t = S_{t-1} + g_t · g_t
- 参数更新公式:
P_{t+1} = P_t - (α / √(S_t + ε)) · g_t
- 符号说明:
-
- ε:防止分母为 0 的微小值(通常取 1e-7);
-
- √(S_t + ε):随迭代次数增大,分母变大,学习率自动衰减。
(3)优势与缺陷
|---------------------------------------------------------------|--------------------------------------------------|
| 优势 | 缺陷 |
| 1. 前期学习率高,更新快;后期自动衰减,稳定训练;. 为不同参数适配独立学习率,适合稀疏数据(如 NLP 中的低频词)。 | 历史梯度平方持续累积,分母越来越大,学习率衰减过快,可能导致训练 "过早停滞"(参数不再更新)。 |
4. RMSProp:改进 AdaGrad 的 "学习率衰减过快"
(1)核心思路
针对 AdaGrad "二阶动量持续累积" 的缺陷,RMSProp 引入指数加权移动平均(EMA),让二阶动量仅 "关注近期梯度",减缓学习率衰减速度。
(2)数学公式
- 二阶动量计算(用 EMA 替代直接累积):
S_t = η·S_{t-1} + (1 - η)·g_t · g_t
- 参数更新公式(同 AdaGrad):
P_{t+1} = P_t - (α / √(S_t + ε)) · g_t
- 符号说明:
-
- η(衰减系数):控制历史梯度的遗忘程度,默认 0.9(仅保留近期 10% 的梯度信息);
-
- 例:t=100 时,S_100 = 0.9・S_99 + 0.1・g_100²,仅保留 S_99 的 90% 和 g_100² 的 10%,避免累积过大。
(3)核心优势
- 克服 AdaGrad 的 "学习率衰减过快" 问题,让训练后期仍能保持合理的学习率,持续更新参数;
- 比 AdaGrad 更稳定,调参难度更低,适合中大型模型训练。
5. Adam(自适应矩估计):当前主流优化器
(1)核心思路
融合Momentum(一阶动量,关注梯度方向) 和RMSProp(二阶动量,关注梯度大小) 的优势,同时跟踪梯度的 "均值(方向)" 和 "方差(大小)",实现更智能的参数更新 ------ 既解决震荡问题,又适配独立学习率,且无学习率衰减过快问题。
(2)数学公式(三步核心)
第一步:计算一阶矩和二阶矩(分别对应方向和大小)
- 一阶矩(m_t:梯度均值,模拟 Momentum 的惯性):
m_t = β₁·m_{t-1} + (1 - β₁)·g_t
- 二阶矩(v_t:梯度方差,模拟 RMSProp 的自适应学习率):
v_t = β₂·v_{t-1} + (1 - β₂)·g_t · g_t
- 默认参数:β₁=0.9(一阶矩衰减系数),β₂=0.999(二阶矩衰减系数),ε=1e-7。
第二步:偏差修正(解决 "初始矩估计不准确" 问题)
初始时 m_0=0、v_0=0,前几次迭代的 m_t 和 v_t 会 "低估" 真实梯度的均值和方差,需修正:
- 一阶矩修正:m_t^hat = m_t / (1 - β₁^t)
- 二阶矩修正:v_t^hat = v_t / (1 - β₂^t)
- 说明:t 为迭代次数,当 t 增大时,β₁^t 和 β₂^t 趋近于 0,修正项趋近于 1,无需额外调整。
第三步:参数更新
P_{t+1} = P_t - (α / √(v_t^hat + ε)) · m_t^hat
(3)核心优势(为何成为主流?)
- 收敛快:融合一阶矩的惯性和平滑性,加速参数向最优解移动;
- 鲁棒性强:二阶矩的自适应学习率适配不同参数,避免震荡和衰减过快;
- 调参简单:默认参数(β₁=0.9、β₂=0.999、lr=0.001)在 90% 以上的任务中表现优异,无需频繁调整。
三、知识点串联:优化器的 "演进逻辑"
从基础到进阶,优化器的发展始终围绕 "解决前一代的缺陷" 展开,形成清晰的演进链:
各优化器核心差异对比
|--------------|----------------------|-----------------------------|----------------------|
| 优化器 | 核心改进点 | 适用场景 | 缺点 |
| SGD | 基础框架,按 mini-batch 更新 | 简单模型、小数据集 | 震荡明显,收敛慢 |
| SGD+Momentum | 加惯性(一阶矩),平滑震荡 | 中大型模型,需加速收敛 | 仍用统一学习率,不适应稀疏数据 |
| AdaGrad | 自适应学习率(二阶矩) | 稀疏数据(如 NLP 低频词) | 学习率衰减过快,易停滞 |
| RMSProp | EMA 优化二阶矩,减缓衰减 | 中大型模型,避免训练停滞 | 未利用梯度方向,部分场景适配性差 |
| Adam | 融合一阶矩 + 二阶矩 + 偏差修正 | 几乎所有场景(CNN、RNN、Transformer) | 极端稀疏数据下,AdaGrad 可能更优 |
四、实验实操:从虚拟仿真到代码运行
1. 实验流程(基于 "华清远见人工智能虚拟仿真平台")
- 启动服务:打开 "虚拟仿真本地服务管理平台",启动后无需重复启动;
- 进入实验:点击 "优化器与优化方法实验",若已在其他实验页面,点击右上角 "返回" 回到主页;
- 组件连线:根据优化器原理(如 SGD 需 "数据输入→参数初始化→损失计算→SGD 更新"),拖出组件并按逻辑连线;
- 验证逻辑:点击 "验证" 按钮,显示 "校验成功" 说明组件连线无误;
- 运行实验:点击 "运行",观察参数更新过程与损失下降曲线;
- 生成代码:点击左下角 "生成代码",复制代码到 Jupyter 中运行(需先通过 CMD 启动 Jupyter:python -m jupyterlab --notebook-dir ./learn)。
2. 实验关键观察点
- 损失曲线:优质优化器(如 Adam)的损失曲线应 "快速下降并趋于平稳",无明显震荡;
- 参数更新速度:对比 SGD 与 Adam 的迭代次数,Adam 通常能在更少迭代次数内达到较低损失;
- 学习率影响:当 lr 过小时,所有优化器收敛都会变慢;当 lr 过大时,SGD 会剧烈震荡,Adam 则更稳定(因二阶矩修正步长)。