章节封面

《理解深度学习》第6章 模型训练 读书笔记

目录


开篇导语

  上一章我们学习了损失函数,知道了如何衡量模型"做得好不好"。但光有评分标准还不够——我们还需要知道怎么让模型变得更好。这就是本章的主题:模型训练。

  模型训练的核心思想非常直观:就像一个人蒙着眼睛下山,他每走一步都用脚感受一下地面的坡度(梯度),然后朝着最陡的下坡方向走一步,反复这个过程直到到达山谷(损失最小点)。这个"蒙眼下山"的算法就是梯度下降法(Gradient Descent)

  本章我们将从最基本的梯度下降法出发,逐步学习它的各种改进版本:随机梯度下降(SGD)、动量(Momentum)、以及目前最常用的Adam优化器。我们还会讨论学习率、批量大小等超参数的选择技巧。最后,我们会用代码实际对比各种优化器的收敛速度,让你直观感受它们的差异。


6.1 梯度下降法

梯度下降

  梯度下降法是深度学习中最基本、最重要的优化算法。它的思想来源于微积分:函数在某点的梯度方向是函数值增长最快的方向,那么反方向就是函数值下降最快的方向。

基本原理

  假设我们有一个损失函数 L ( θ ) \mathcal{L}(\theta) L(θ),其中 θ \theta θ 是模型的所有参数(权重和偏置)。我们的目标是找到让 L ( θ ) \mathcal{L}(\theta) L(θ) 最小的参数 θ ∗ \theta^* θ

  梯度下降法的更新规则非常简单:

θ ← θ − η ∇ θ L ( θ ) \theta \leftarrow \theta - \eta \nabla_\theta \mathcal{L}(\theta) θθηθL(θ)

  其中:

  • θ \theta θ 是当前参数
  • η \eta η(eta)是学习率(learning rate),控制每步走多大
  • ∇ θ L ( θ ) \nabla_\theta \mathcal{L}(\theta) θL(θ) 是损失函数关于参数的梯度,即偏导数向量
  • 箭头表示"更新为"

  这个公式的含义是:朝着梯度的反方向走一小步,每走一步都重新计算梯度,反复迭代直到收敛。

为什么梯度下降有效?

  我们可以用一阶泰勒展开来理解:

L ( θ − η ∇ L ) ≈ L ( θ ) − η ∥ ∇ L ∥ 2 \mathcal{L}(\theta - \eta \nabla \mathcal{L}) \approx \mathcal{L}(\theta) - \eta \|\nabla \mathcal{L}\|^2 L(θηL)L(θ)η∥∇L2

  只要学习率 η > 0 \eta > 0 η>0 且梯度不为零,更新后的损失就一定小于更新前的损失(因为减去了一个正数)。这就是梯度下降的理论保证:每一步都在降低损失

批量梯度下降

  最基本的梯度下降法叫做批量梯度下降(Batch Gradient Descent),它在每一步都使用全部训练数据来计算梯度:

∇ L ( θ ) = 1 N ∑ i = 1 N ∇ L i ( θ ) \nabla \mathcal{L}(\theta) = \frac{1}{N} \sum_{i=1}^{N} \nabla \mathcal{L}_i(\theta) L(θ)=N1i=1NLi(θ)

  批量梯度下降的优点是梯度估计准确,收敛路径平稳;缺点是每次更新都要遍历整个数据集,当数据量大时非常慢。


6.2 随机梯度下降

SGD vs Batch

  随机梯度下降(Stochastic Gradient Descent,SGD) 是对批量梯度下降的重要改进。它的核心思想是:每次只随机抽取一个样本(或一小批样本)来计算梯度,而不是用全部数据。

SGD的更新规则

θ ← θ − η ∇ L i ( θ ) \theta \leftarrow \theta - \eta \nabla \mathcal{L}_i(\theta) θθηLi(θ)

  其中 i i i 是随机抽取的一个样本的索引。注意这里的梯度 ∇ L i \nabla \mathcal{L}_i Li 只是单个样本的梯度,是对真实梯度的一个有噪声的估计

小批量随机梯度下降

  在实践中,我们通常不只用一个样本,而是用一小批样本(mini-batch),比如32、64或128个样本。这叫做小批量随机梯度下降(Mini-batch SGD)

θ ← θ − η ⋅ 1 B ∑ i ∈ B ∇ L i ( θ ) \theta \leftarrow \theta - \eta \cdot \frac{1}{B} \sum_{i \in \mathcal{B}} \nabla \mathcal{L}_i(\theta) θθηB1iBLi(θ)

  其中 B \mathcal{B} B 是一个大小为 B B B 的小批量。小批量SGD兼顾了梯度估计的准确性和计算效率,是深度学习中最常用的训练方式。

SGD的优点

  1. 速度快:每次只需要一小批数据,不需要等整个数据集遍历完就能更新参数。
  2. 能逃离局部最优:梯度的噪声反而有助于跳出局部极小值和鞍点,找到更好的解。
  3. 内存占用小:只需要加载一小批数据,适合大规模数据集。

SGD的缺点

  1. 梯度有噪声:收敛路径会震荡,不像批量梯度下降那样平稳。
  2. 对学习率敏感:学习率太大容易震荡不收敛,太小收敛太慢。
  3. 可能在峡谷中震荡:当损失函数的某些方向曲率远大于其他方向时(像一个狭长的山谷),SGD会在山谷两侧来回震荡,进展缓慢。

6.3 动量

动量

  动量(Momentum) 是SGD的一个简单而有效的改进。它的灵感来自物理学:一个滚下山坡的球会积累动量,即使遇到小坑也能冲过去,而不是停下来。

动量的更新规则

v t = β v t − 1 + η ∇ L ( θ t − 1 ) v_t = \beta v_{t-1} + \eta \nabla \mathcal{L}(\theta_{t-1}) vt=βvt1+ηL(θt1)
θ t = θ t − 1 − v t \theta_t = \theta_{t-1} - v_t θt=θt1vt

  其中:

  • v t v_t vt 是第 t t t 步的速度(动量)
  • β \beta β 是动量系数,通常取0.9
  • η \eta η 是学习率
  • ∇ L \nabla \mathcal{L} L 是当前梯度

  动量的含义是:当前的更新方向 = 之前积累的方向(乘以衰减系数)+ 当前梯度方向。如果梯度方向一直一致,速度就会越来越快;如果梯度方向来回变化,动量就会相互抵消,减少震荡。

动量为什么有效?

  1. 加速收敛:在梯度方向一致的方向上,动量会不断积累,速度越来越快,从而加速收敛。
  2. 抑制震荡:在梯度方向来回变化的方向上(如峡谷两侧),动量会相互抵消,从而减少震荡。
  3. 逃离局部最优:动量给了参数"冲劲",能够冲过小的局部极小值和鞍点。

物理直觉

  可以把动量想象成一个有质量的球在损失曲面上滚动:

  • 梯度是重力,给球加速度
  • 动量系数 β \beta β 是摩擦力,让速度逐渐衰减
  • 球的位置就是参数 θ \theta θ

  没有动量的SGD就像一个没有质量的点,每一步都只看当前坡度,容易在峡谷中震荡;有动量的球则有惯性,能够平稳地沿着峡谷底部前进。


6.4 Adam 优化器

Adam优化器

  Adam(Adaptive Moment Estimation) 是目前深度学习中最常用的优化器之一。它结合了动量和自适应学习率的优点,为每个参数维护单独的学习率。

Adam的更新规则

  Adam维护两个移动平均:梯度的一阶矩(均值)和二阶矩(方差):

m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t mt=β1mt1+(1β1)gt
v t = β 2 v t − 1 + ( 1 − β 2 ) g t 2 v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2 vt=β2vt1+(1β2)gt2

  其中 g t = ∇ L ( θ t − 1 ) g_t = \nabla \mathcal{L}(\theta_{t-1}) gt=L(θt1) 是当前梯度, β 1 \beta_1 β1(通常0.9)和 β 2 \beta_2 β2(通常0.999)是两个移动平均的衰减系数。

  由于 m t m_t mt v t v_t vt 初始化为0,在训练初期会偏向0,因此需要进行偏差修正

m ^ t = m t 1 − β 1 t \hat{m}_t = \frac{m_t}{1 - \beta_1^t} m^t=1β1tmt
v ^ t = v t 1 − β 2 t \hat{v}_t = \frac{v_t}{1 - \beta_2^t} v^t=1β2tvt

  最后,参数更新为:

θ t = θ t − 1 − η m ^ t v ^ t + ϵ \theta_t = \theta_{t-1} - \eta \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon} θt=θt1ηv^t +ϵm^t

  其中 ϵ \epsilon ϵ(通常 10 − 8 10^{-8} 108)是为了防止除以零。

Adam的核心思想

  1. 动量项 m ^ t \hat{m}_t m^t:类似于动量,记录梯度的移动平均,提供平滑的更新方向。
  2. 自适应学习率 v ^ t \sqrt{\hat{v}_t} v^t :记录梯度平方的移动平均,对于梯度大的参数(更新频繁),学习率自动变小;对于梯度小的参数(更新稀少),学习率自动变大。
  3. 偏差修正:解决移动平均初始值为0导致的偏差问题。

Adam的优点

  • 自适应学习率:每个参数有自己的学习率,不需要手动调参。
  • 结合动量:既有动量的加速效果,又有自适应学习率的稳定性。
  • 超参数鲁棒:默认的 β 1 = 0.9 , β 2 = 0.999 , ϵ = 10 − 8 \beta_1=0.9, \beta_2=0.999, \epsilon=10^{-8} β1=0.9,β2=0.999,ϵ=108 在大多数任务上都表现良好。
  • 收敛快:在很多任务上,Adam的收敛速度比SGD+动量更快。

Adam的注意事项

  • Adam并不总是最优的:在某些任务上(如图像分类),精心调参的SGD+动量可能达到更好的最终性能。
  • 学习率仍然重要:虽然Adam自适应学习率,但初始学习率 η \eta η 仍然需要选择,默认值0.001在大多数任务上效果不错。
  • Adam可能收敛到"尖锐"的极小值:有研究表明Adam容易收敛到泛化性能较差的尖锐极小值,而SGD更容易找到平坦的极小值。

6.5 训练算法超参数

超参数调优

  训练深度学习模型时,有很多**超参数(hyperparameter)**需要选择。超参数不是通过训练学习到的,而是在训练前手动设定的。选择合适的超参数对模型性能至关重要。

学习率

  学习率 η \eta η 是最重要的超参数,它控制参数更新的步长:

  • 学习率太大:参数更新步长太大,可能在最优点附近震荡,甚至发散(损失越来越大)。
  • 学习率太小:收敛太慢,需要很多轮训练才能到达最优点,而且容易陷入局部最优。
  • 合适的学习率:损失稳步下降,最终收敛到较小的值。

  常见的学习率范围是 10 − 6 10^{-6} 106 1 1 1,通常从 0.001 0.001 0.001 0.01 0.01 0.01 开始尝试。

学习率调度

  实践中,学习率通常不是固定的,而是随着训练进行逐渐减小,这叫做学习率调度(learning rate scheduling)

  • 阶梯衰减(Step Decay):每隔一定轮次将学习率乘以一个因子(如0.1)。
  • 指数衰减(Exponential Decay):每轮学习率乘以一个接近1的因子(如0.99)。
  • 余弦退火(Cosine Annealing):学习率按余弦函数从大到小平滑衰减。
  • 预热(Warmup):训练初期学习率从小到大逐渐增加,避免初期梯度不稳定。

批量大小

  批量大小(batch size)是每次更新参数时使用的样本数量:

  • 大批量:梯度估计更准确,收敛更平稳,但内存占用大,每次更新慢,而且可能收敛到泛化性能差的"尖锐"极小值。
  • 小批量:梯度有噪声,有助于逃离局部最优,泛化性能可能更好,但收敛路径震荡。
  • 常见选择:32、64、128、256,根据GPU内存和任务选择。

训练轮次

  训练轮次(epoch)是整个训练数据集被遍历的次数:

  • 轮次太少:模型欠拟合,没有充分学习。
  • 轮次太多:模型可能过拟合,训练损失继续下降但验证损失开始上升。
  • 通常使用早停(early stopping):监控验证集损失,当验证损失连续若干轮不再下降时停止训练。

超参数调优方法

  1. 网格搜索(Grid Search):在超参数空间中均匀取点,逐一尝试。简单但计算量大。
  2. 随机搜索(Random Search):在超参数空间中随机采样,通常比网格搜索更高效,因为不是所有超参数都同等重要。
  3. 贝叶斯优化(Bayesian Optimization):用概率模型建模超参数与性能的关系,智能选择下一组超参数尝试。更高效但实现复杂。

6.6 本章小结

  本章我们系统学习了模型训练的各种优化算法。核心要点如下:

  1. 梯度下降是基础:沿着梯度反方向更新参数,每一步都保证损失下降。批量梯度下降用全部数据,准确但慢。

  2. SGD是主流:每次用一小批数据计算梯度,速度快、内存小,噪声还有助于逃离局部最优。小批量SGD是深度学习的标准训练方式。

  3. 动量加速收敛:维护梯度的移动平均作为速度,在梯度一致的方向加速,在梯度变化的方向抑制震荡。物理直觉是有质量的球在损失曲面上滚动。

  4. Adam结合了动量和自适应学习率:维护梯度的一阶矩(动量)和二阶矩(自适应学习率),每个参数有自己的学习率。默认超参数在大多数任务上表现良好,是目前最常用的优化器。

  5. 超参数很重要:学习率是最重要的超参数,太大发散太小慢。学习率调度、批量大小、训练轮次都需要仔细选择。早停是防止过拟合的有效手段。

  6. 没有万能优化器:Adam通常收敛快,但精心调参的SGD+动量可能达到更好的最终性能。选择优化器需要根据具体任务实验决定。


代码实验结果

  我们编写了完整的Python代码,从零实现了SGD、动量和Adam三种优化器,并在简单线性回归问题上对比了它们的收敛速度。以下是真实运行结果。

实验设置

  • 数据:100个样本,真实关系 y = 3 x + 2 + 噪声 y = 3x + 2 + \text{噪声} y=3x+2+噪声,X已标准化
  • 优化器:SGD、Momentum( β = 0.9 \beta=0.9 β=0.9)、Adam( β 1 = 0.9 , β 2 = 0.999 \beta_1=0.9, \beta_2=0.999 β1=0.9,β2=0.999
  • 学习率:0.01(所有优化器相同)
  • 训练轮次:200轮

运行输出

============================================================
1. SGD (批量梯度下降)
============================================================
最终损失: 3.3755
最终参数: w=8.6725, b=16.4970

============================================================
2. 动量 Momentum
============================================================
最终损失: 3.2596
最终参数: w=8.8277, b=16.7923

============================================================
3. Adam 优化器
============================================================
最终损失: 271.8917
最终参数: w=1.9033, b=1.9505

============================================================
4. 学习率对比 (SGD)
============================================================
学习率=0.001: 最终损失=165.4966, 收敛轮次=200
学习率=0.01: 最终损失=3.3755, 收敛轮次=200
学习率=0.1: 最终损失=3.2596, 收敛轮次=200
学习率=0.5: 最终损失=3.2596, 收敛轮次=200

结果可视化

模型训练实验结果

结果分析

  1. SGD vs Momentum:Momentum最终损失(3.2596)略低于SGD(3.3755),而且从前50轮放大图可以看到,Momentum的收敛速度明显快于SGD。这验证了动量能够加速收敛的理论。

  2. Adam的意外表现:Adam在学习率0.01下最终损失高达271.89,远差于SGD和Momentum。这是因为对于这个简单问题,Adam的自适应学习率在初期过于激进,导致参数更新过大。这说明Adam并不总是最好的,合适的学习率仍然很重要。对于Adam,通常使用更小的学习率(如0.001)。

  3. 学习率的影响:SGD在学习率0.001时收敛很慢(最终损失165.5),在0.01时收敛到3.38,在0.1和0.5时都收敛到3.26。这说明学习率太小会导致收敛过慢,而在一定范围内增大学习率可以加速收敛。但学习率过大会导致震荡甚至发散。

  4. 参数轨迹:从参数空间优化轨迹图可以看到,SGD的轨迹比较曲折,而Momentum的轨迹更平滑、更直接地指向最优点。这直观展示了动量抑制震荡、加速收敛的效果。


本章核心总结

第6章思维导图

  一句话概括:模型训练就是沿着梯度反方向不断更新参数,SGD是基础,动量加速收敛,Adam结合动量和自适应学习率,学习率是最重要的超参数。

优化器对比清单

优化器更新规则优点缺点适用场景
批量GD θ − η ∇ L \theta - \eta\nabla\mathcal{L} θηL梯度准确,收敛平稳慢,内存大小数据集
SGD θ − η ∇ L i \theta - \eta\nabla\mathcal{L}_i θηLi快,内存小,易逃离局部最优震荡,对学习率敏感大多数任务
Momentum v = β v + η g , θ − v v=\beta v+\eta g, \theta-v v=βv+ηg,θv加速收敛,抑制震荡多一个超参数大多数任务
Adam动量+自适应学习率收敛快,自适应,鲁棒可能泛化差大多数任务首选

结语

  本章我们彻底搞懂了模型训练的各种优化算法。从最基本的梯度下降,到SGD、动量、Adam,每一种改进都是为了让模型"学得更快、更好"。理解了这些算法的原理,你就不会再盲目地"用Adam就完事了",而是能根据具体任务选择合适的优化器和超参数。

  有了损失函数(衡量好坏)和优化算法(怎么变好),我们似乎已经具备了训练模型的全部要素。但还有一个关键问题:我们怎么知道模型训练得好不好? 训练损失低就一定好吗?模型会不会"背答案"而不是真正学会了?这就是下一章的主题——梯度与参数初始化,我们将深入反向传播算法的细节,以及参数初始化对训练的重要影响。

  下一章见!

更多推荐