激活函数、归一化、Dropout:模型稳定训练的三件套
激活函数,让模型有表达力。归一化,让数值分布稳定。Dropout,让模型别死记。 1. 这三件事分别解决什么问题 第一步,线性层负责“算”。它把输入乘权重、加偏置。 第二步,激活函数负责“拐弯”。没有它,再多层线性层也只是更大的线性层。 第
激活函数,让模型有表达力。归一化,让数值分布稳定。Dropout,让模型别死记。
1. 这三件事分别解决什么问题
第一步,线性层负责“算”。它把输入乘权重、加偏置。
第二步,激活函数负责“拐弯”。没有它,再多层线性层也只是更大的线性层。
第三步,归一化负责“扶正”。它把中间输出拉回稳定范围,避免训练一路飘。
第四步,Dropout 负责“扰动”。它训练时随机遮挡一部分激活,让模型不要过度依赖某几个特征。
没有激活函数,多层线性可以坍缩成一层线性
2. 激活函数:给模型增加“非线性表达力”
ReLU 是硬开关。小于 0 的值砍掉,大于 0 的值原样通过。它简单、快、稳定,是很多 CNN 和 MLP 的默认选择。
Sigmoid 是软压缩。它把输出压到 0 到 1,适合做概率或门控。但在隐藏层里,大正数和大负数会让梯度变小,训练容易慢。
GELU 是软门控。它不是简单砍掉负数,而是根据输入大小平滑放行。Transformer 结构里经常能看到它。
ReLU、Sigmoid、GELU 的输出曲线
3. 激活函数的源码主线
源码不要从底层 kernel 开始硬啃。先看 Python 层。你会发现,大多数激活层只是薄薄一层包装。
nn.ReLU.forward 调 F.relu。nn.GELU.forward 调 F.gelu。nn.Sigmoid.forward 调 torch.sigmoid。
真正的张量计算继续走 dispatcher,再落到 CPU/CUDA 等后端算子。Python 类负责组织,底层算子负责计算。
这就是 PyTorch nn.Module 的常见设计:上层接口清楚,下层算子高效。
4. 归一化:让训练过程不乱飘
深层网络训练时,每一层输出的分布都在变。上一层稍微动一下,下一层看到的输入范围就可能变。
归一化的目标很朴素:把中间值拉回一个更稳定的尺度。
但不同 Norm 的关键区别是:统计量从哪里来。
BatchNorm、LayerNorm、RMSNorm 的统计维度
5. BatchNorm:看整个 batch 的统计量
BatchNorm 常用于 CNN。以 N,C,H,W 的图像特征为例,它通常按通道 C 归一化,每个通道会从 N,H,W 上统计均值和方差。
它有两类状态。weight 和 bias 是可学习参数。running_mean 和 running_var 是 Buffer,不是参数,但会跟着 state_dict 保存。
训练时,BatchNorm 用当前 batch 的均值和方差,同时更新 running 统计量。推理时,它使用 running_mean 和 running_var,让输出稳定。
BatchNorm 在 train/eval 下的两条路径
6. LayerNorm 和 RMSNorm:大模型为什么更爱它们
LayerNorm 不看整个 batch。它对每个样本自己的最后若干维做归一化。batch 大小变了,它的统计逻辑不变。
这让 LayerNorm 特别适合序列模型和 Transformer。因为文本长度、batch size、分布都可能变化,按样本归一化更稳。
RMSNorm 可以看作更轻量的归一化。它不减均值,而是用均方根控制尺度。许多大模型使用 RMSNorm,是因为它足够稳,也更省。
记住一句话:BatchNorm 依赖 batch,LayerNorm/RMSNorm 更像每个样本自己的稳定器。
7. Dropout:让模型别死记答案
Dropout 的动作很简单:训练时随机把一部分元素置零。
它的目的不是让网络永久少一些神经元,而是训练时制造扰动,逼模型不要把答案押在少数激活值上。
PyTorch 的 Dropout 在训练时会按 1/(1-p) 缩放保留下来的值。这样推理时不需要额外缩放,直接恒等输出。
Dropout 训练时采样 mask,推理时恒等输出
8. train() 和 eval():控制行为的开关
model.train() 不是“开始训练”。model.eval() 也不是“关闭梯度”。
它们本质上是递归设置每个 Module 的 training 标志。Dropout 和 BatchNorm 会读取这个标志,决定走训练逻辑还是推理逻辑。
所以推理时通常要做两件事:model.eval() 切换模块行为,再配合 no_grad() 或 inference_mode() 避免构建梯度图。
train/eval 是递归开关,不等于梯度开关
9. 五个组件看一条线
这章的源码可以总结成一张图。先看 Module 的 forward,再看 functional,再看底层算子。
激活函数:forward 基本只转发到 functional 或 torch 原生算子。
Dropout:forward 会把 self.training 传进去,底层根据 training 决定是否采样 mask。
BatchNorm:forward 会判断当前是训练还是推理,决定用 batch 统计还是 running 统计。
LayerNorm/RMSNorm:forward 直接走对应 functional,按 normalized_shape 指定的维度做归一化。
10. 常见坑:真正出问题的地方
实际项目里,公式通常不是最难的。最容易错的是模式、位置和 batch size。
激活函数、归一化、Dropout 常见坑
11. 总结
激活函数解决表达力。Norm 解决稳定性。Dropout 解决泛化。
它们看起来是小组件,但能直接决定模型能不能收敛、会不会过拟合、推理结果稳不稳。
读源码时不要迷路:先看 forward,再看 functional,再看 training 标志和参数/Buffer。
要点速读
激活函数,让模型有表达力。归一化,让数值分布稳定。Dropout,让模型别死记。 1. 这三件事分别解决什么问题 第一步
- 激活函数,让模型有表达力
- 归一化,让数值分布稳定
- Dropout,让模型别死记