3. 反向传播与梯度下降:链式法则、梯度下降变体(SGD、Adam)、学习率调度策略

各位同学,欢迎来到第三章。前两章我们聊了神经网络的基础和损失函数,说白了,就是搭好了架子,也知道了怎么才算「好」。但光知道好坏没用,你得让网络自己学会变好。怎么变?靠的就是反向传播和梯度下降。

这俩东西,是深度学习训练的发动机。我当年刚入行时,觉得这玩意儿玄乎得很,不就是求个导嘛?后来在车载摄像头项目里,因为梯度爆炸导致模型直接崩掉,我才真正领教了它的威力。今天咱们就把它彻底讲透。

3.1 链式法则:反向传播的数学根基

反向传播的核心,其实就是链式法则。你想想看,一个神经网络动辄几十层,每一层都有参数。你想知道某个参数对最终损失的影响,怎么办?

一层一层往回推。这就是链式法则干的事。

举个例子,假设我们有这么个简单网络:

输入 x → 线性层 z = wx + b → 激活函数 a = σ(z) → 损失 L = (a - y)²

我想知道 w 对 L 的影响,也就是 ∂L/∂w。根据链式法则:

∂L/∂w = ∂L/∂a · ∂a/∂z · ∂z/∂w

就这么简单。每一层的梯度,都是后面层梯度的「乘积」。我习惯把这个过程想象成一条河流,损失从上游流下来,每经过一个节点,就分出一部分「责任」给对应的参数。

核心要点:反向传播不是新算法,它就是链式法则的工程实现。你只要记住——梯度是沿着计算图从后往前传播的。

在实际项目中,我见过不少新手自己手写反向传播,结果梯度算错了,模型怎么训都不收敛。嗯,这里要注意:千万别手算,用自动微分框架。TensorFlow、PyTorch 都帮你算好了,你只需要定义好前向计算图就行。

3.2 梯度下降变体:SGD 与 Adam

有了梯度,怎么更新参数?最朴素的想法就是:沿着梯度的反方向走一步。这就是梯度下降。

但实际用起来,你会发现朴素梯度下降问题不少。我当年在车载雷达项目里,用 SGD 训一个目标检测模型,收敛慢得像蜗牛爬,而且动不动就在局部最优附近震荡。后来换了 Adam,效果立竿见影。

3.2.1 SGD(随机梯度下降)

SGD 每次只用一个样本(或一个小批量)计算梯度,然后更新参数:

θ = θ - η · ∇L(θ)

其中 η 是学习率。SGD 的优点是简单、省内存。但缺点也很明显:

  • 震荡严重:每个样本的梯度方向不一致,导致参数更新路径很曲折
  • 容易陷入局部最优:尤其在非凸问题中
  • 对学习率敏感:η 设大了不收敛,设小了慢得要命

我曾经在车载语音识别项目里,用 SGD 训了一个星期,损失曲线还在那晃悠。后来加了动量(Momentum),才算是稳住了。

我的经验:SGD + 动量是经典组合。动量项相当于给梯度更新加了「惯性」,能有效抑制震荡。公式很简单:

v = β·v + (1-β)·∇L(θ)
θ = θ - η·v

β 通常取 0.9,相当于看过去 10 步的平均梯度方向。

3.2.2 Adam(自适应矩估计)

Adam 可以说是目前最流行的优化器了。它结合了动量和自适应学习率的思想。说白了,就是给每个参数单独配一个学习率,而且这个学习率还会根据梯度的大小自动调整。

Adam 的更新公式稍微复杂一点:

m = β₁·m + (1-β₁)·∇L(θ)    # 一阶矩估计(动量)
v = β₂·v + (1-β₂)·(∇L(θ))²  # 二阶矩估计(自适应学习率)
m_hat = m / (1 - β₁ᵗ)        # 偏差修正
v_hat = v / (1 - β₂ᵗ)
θ = θ - η · m_hat / (√v_hat + ε)

你看,Adam 既考虑了梯度的方向(m),又考虑了梯度的大小(v)。梯度大的参数,学习率自动变小;梯度小的参数,学习率自动变大。

实际建议:在汽车电子项目中,我一般默认用 Adam。β₁=0.9,β₂=0.999,ε=1e-8。这几个参数基本不用调。但要注意,Adam 虽然收敛快,有时候泛化性能不如 SGD。如果你追求极致精度,可以先用 Adam 快速收敛,再切到 SGD 微调。

3.3 学习率调度策略

学习率是训练过程中最重要的超参数之一。你想想看,学习率太大,参数会在最优解附近来回震荡;学习率太小,训练慢得让人抓狂。

所以,动态调整学习率就成了标配。我常用的几种策略:

策略名称 原理 适用场景
Step Decay 每 N 个 epoch 将学习率乘以一个衰减因子 简单任务,训练曲线平稳
Exponential Decay 学习率按指数衰减:η = η₀ · e^{-kt} 需要平滑衰减的场景
Cosine Annealing 学习率按余弦函数周期变化 避免陷入局部最优,适合大模型
ReduceLROnPlateau 当验证损失不再下降时,降低学习率 不知道何时衰减,让算法自己判断
Warmup 前几个 epoch 从很小的学习率逐渐增加到目标值 训练初期不稳定,尤其适合大 batch size

我个人最常用的是 Cosine Annealing + Warmup 的组合。为什么?

  • Warmup:训练初期模型参数是随机的,梯度方向不靠谱。如果一开始就用大学习率,很容易把参数推到奇怪的地方。先 warmup 一下,让模型「热热身」。
  • Cosine Annealing:学习率按余弦曲线下降,前期下降慢(充分探索),后期下降快(精细收敛)。而且余弦曲线有周期性,偶尔还能跳出局部最优。

避坑指南:我曾经在车载摄像头项目里,用了一个固定的学习率 0.01 训了 200 个 epoch。结果损失曲线在 50 个 epoch 后就基本不动了。后来加了 ReduceLROnPlateau,损失又降了一大截。记住:学习率不调,等于白训

3.4 实战:在汽车电子中如何选择

说了这么多理论,咱们回到实际。在汽车电子项目中,你该怎么选?

我一般遵循这个原则:

  1. 先快速验证:用 Adam + Cosine Annealing,跑 50 个 epoch。看看模型能不能收敛,损失大概能降到多少。
  2. 再精细调优:如果 Adam 效果不错,但泛化性能差,换成 SGD + Momentum + Step Decay。学习率从 0.01 开始,每 30 个 epoch 衰减 0.1 倍。
  3. 最后做 ablation study:对比不同优化器和调度策略的效果,选最优的。

举个例子,我在做车载行人检测时,一开始用 Adam 训了 100 个 epoch,mAP 到了 85%。换成 SGD 后,mAP 涨到了 87%。虽然只涨了 2 个点,但在汽车安全领域,这 2% 可能就意味着少撞一个人。

小技巧:如果你不确定学习率该设多少,可以用「学习率范围测试」(LR Range Test)。从一个很小的学习率开始,每个 batch 增大一点,观察损失变化。损失下降最快的那个学习率,就是你的初始学习率。我每次开新项目,第一件事就是跑这个测试。

好了,这一章的内容就到这里。反向传播和梯度下降是深度学习的基石,你理解得越深,调参就越有底气。下一章我们会聊更高级的训练技巧,比如正则化、批归一化这些。到时候你会发现,今天学的这些东西,全都会用上。

记住:理论是死的,项目是活的。多动手,多踩坑,你才能真正成为专家。