4、深度学习基础(下):反向传播、梯度下降、优化器(SGD、Adam)、正则化与Dropout
上一讲我们把前向传播和损失函数聊透了。这一讲,咱们聊聊网络到底是怎么“学”东西的——说白了,就是怎么调整那些成千上万的权重,让损失降下来。
我刚开始做AI ISP的时候,总觉得训练网络是个黑盒子。后来亲手调了几次梯度消失和爆炸,才真正理解这些基础模块有多重要。你想想看,如果反向传播算出来的梯度是错的,那后面再怎么调优化器都是白搭。
4.1 反向传播:误差从输出层“流”回输入层
反向传播的核心思想,就是链式法则。损失函数对某一层权重的梯度,等于损失对该层输出的导数,乘以该层输出对权重的导数。一层一层往回传。
举个例子,一个简单的三层网络:输入层 → 隐藏层(ReLU) → 输出层(Sigmoid)。前向传播算出一个预测值,和真实标签算交叉熵损失。反向传播时,先算输出层的梯度,再往隐藏层传。
关键公式(链式法则):
∂L/∂w = (∂L/∂a) * (∂a/∂z) * (∂z/∂w)
其中 L 是损失,a 是激活输出,z 是线性加权和,w 是权重。
我在项目中遇到过一个问题:训练一个HDR融合网络,发现前几层的权重几乎不更新。排查了半天,原来是激活函数用了Sigmoid,梯度在深层已经饱和了。换成ReLU之后,问题立刻解决。
注意:反向传播的梯度会随着层数增加而指数级衰减或爆炸。这就是“梯度消失/爆炸”问题。Batch Normalization和残差连接(ResNet)就是用来缓解这个问题的。
4.2 梯度下降:沿着最陡的方向下山
梯度下降,说白了就是“下山”。你站在山顶(随机初始化的权重),环顾四周,找到最陡的下坡方向(负梯度方向),迈一步(更新权重),然后重复。
数学上就是:
w_new = w_old - learning_rate * gradient
学习率(learning rate)决定了每一步迈多大。步子太大,可能直接跨过最低点,甚至发散;步子太小,训练慢得像蜗牛爬。
我建议刚开始调参时,先把学习率设小一点(比如1e-3),观察损失曲线。如果下降太慢,再逐步增大。如果损失震荡或发散,立刻减小。
| 变体 | 更新方式 | 特点 |
|---|---|---|
| Batch GD | 用全部样本算梯度 | 稳定但慢,内存占用大 |
| Stochastic GD | 每次用一个样本 | 快但震荡大,不收敛 |
| Mini-batch GD | 每次用一个小批量 | 折中方案,最常用 |
4.3 优化器:从SGD到Adam
普通的SGD有个问题:所有参数都用同一个学习率。但实际训练中,有些参数可能需要更大的步长,有些需要更小的。这就引出了自适应优化器。
SGD + Momentum
Momentum模拟了物理中的惯性。更新时不仅考虑当前梯度,还考虑上一次的更新方向。这样能加速收敛,同时抑制震荡。
v = momentum * v - learning_rate * gradient
w = w + v
我习惯把momentum设成0.9。这个值在大部分任务上都表现不错。
Adam:自适应学习率 + Momentum
Adam是目前最常用的优化器之一。它结合了Momentum和RMSProp的优点:既考虑了梯度的一阶矩(均值),也考虑了二阶矩(方差)。
Adam的核心思想:
- 对每个参数单独调整学习率
- 梯度大的参数,学习率自动变小
- 梯度小的参数,学习率自动变大
- 加上动量,加速收敛
Adam的默认参数(lr=0.001, betas=(0.9, 0.999))在大多数任务上都能直接工作。我曾经在色彩重建任务中,直接用默认参数就收敛了,省了不少调参时间。
| 优化器 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| SGD | 简单,泛化性好 | 收敛慢,需精细调参 | 大数据集,有调参经验 |
| SGD+Momentum | 加速收敛,抑制震荡 | 仍需要调学习率 | 大部分CV任务 |
| Adam | 自适应,几乎不用调参 | 可能泛化性略差 | 快速实验,新手友好 |
| AdamW | 改进了权重衰减 | 比Adam多一个参数 | Transformer等大模型 |
我的建议:刚开始做实验时,先用Adam快速验证想法。等模型结构稳定了,再换成SGD+Momentum精调,往往能得到更好的泛化性能。
4.4 正则化:防止网络“死记硬背”
网络太深、参数太多,就容易过拟合——在训练集上表现很好,但在测试集上一塌糊涂。正则化就是用来防止这种情况的。
L1和L2正则化
在损失函数后面加一个惩罚项,限制权重的大小。
L2正则化:Loss_new = Loss_old + λ * Σ(w²)
L1正则化:Loss_new = Loss_old + λ * Σ|w|
L2会让权重趋向于小值,但不为零。L1会让部分权重变成0,起到特征选择的作用。
我曾经在ISP的色调映射网络中用L1正则化,发现很多卷积核的权重变成了0,网络变得非常稀疏,推理速度也快了不少。
Dropout:随机“丢弃”神经元
Dropout的原理很简单:训练时,每次前向传播都随机让一部分神经元失活(输出置0)。这样网络就不能依赖某个特定的神经元,必须学会冗余表示。
Dropout的关键参数:
- keep_prob:保留神经元的概率。常用0.5(全连接层)或0.8(卷积层)
- 测试时:所有神经元都参与,但输出要乘以keep_prob(或使用inverted dropout自动缩放)
嗯,这里要注意:Dropout一般只用在全连接层,卷积层后面用得少。因为卷积层的参数共享特性,本身就有一定的正则化效果。
4.5 知识结构图:深度学习训练流程
下面这张图把整个训练流程串起来了。从输入数据到前向传播,算损失,反向传播算梯度,优化器更新权重,再加上正则化防止过拟合——这就是一个完整的训练闭环。
4.6 避坑指南:我踩过的那些坑
最后分享几个实战中容易踩的坑,都是我亲身经历过的。
- 学习率太大导致损失NaN:我曾经把学习率设成0.1,结果训练到第10步损失就变成了NaN。后来改成1e-3,一切正常。
- Dropout在测试时忘了缩放:有一次测试集准确率比训练集还高,我高兴了半天。后来发现是Dropout在测试时没关,输出被随机丢弃了。嗯,尴尬。
- L2正则化系数太大:设成0.1,结果所有权重都趋近于0,网络直接“躺平”了。一般从1e-4开始调比较稳妥。
- Adam + 权重衰减的坑:直接用Adam做L2正则化效果不好,因为Adam的自适应学习率会干扰权重衰减。建议用AdamW,它把权重衰减和自适应学习率分开了。
总结一下:反向传播是训练的基础,梯度下降是优化的手段,优化器决定了收敛速度和稳定性,正则化防止过拟合。这四个模块配合好了,你的网络才能又快又好地收敛。