介绍
归一化方法已经成为深度学习算法中不可或缺的一个重要模块,并且存在多种不同版本的归一化方法,其本质都是减均值除以方差,进行线性映射后,使其数据满足某个稳定分布。
y
=
γ
(
x
−
μ
(
x
)
σ
(
x
)
)
+
β
y = γ(\frac{x-μ(x)}{σ(x)}){+β}
y=γ(σ(x)x−μ(x))+β
1 为什么要做归一化处理
目的:解决数据分布不稳定带来的问题,从而加速训练、稳定优化过程并提升模型性能。主要解决以下几个问题:
解决内部协变量偏移
- 问题描述:
- 深度网络由多个层叠加而成,每一层的输入都是前一层的输出。在训练过程中,随着网络参数的不断更新,每一层的输入数据的分布会发生剧烈变化(均值、方差偏移)。这种现象就称为内部协变量偏移。
- 例如:某一层输入原本集中在 [-1, 1] 范围内,参数更新后下一批输入可能偏移到 [100, 200]。
- 带来的后果
- 学习目标持续变化: 后层需要不断适应新的数据分布,降低了学习效率。想象打靶时靶心一直在随机移动,射手很难命中。
- 需要极小学习率: 为了保证在剧烈波动的输入下网络仍然能稳定学习,只能使用很小的学习率,导致收敛极慢。
- 训练不稳定: 梯度计算变得不可靠,容易引起震荡或发散。
- 归一化的作用:
- BN/LN/IN/GN 等操作强制性地将每一层的输入(激活值)拉回一个稳定的分布(通常是零均值、单位方差)。后续的缩放平移参数 γ, β 赋予网络调整分布的灵活性。
- 这样后层面对的是一个更稳定、更“标准” 的输入分布,可以把主要精力放在学习“有用特征”上,而不是费力地适应输入的剧烈变化,大大加速训练。
解决梯度问题(梯度消失/爆炸)
- 问题描述:
- 深度网络中梯度需要通过反向传播逐层传递。
- 如果激活函数的输入值过大或过小(例如 Sigmoid、Tanh 的饱和区),或者各层输入的尺度差异巨大,会导致梯度变得极大(爆炸)或极小(消失),使得参数无法有效更新或更新幅度失控。
- 归一化的作用:
- 将激活值归一化到合理的范围(如均值为0,标准差为1),使其通常落在激活函数的线性区或不饱和区附近。
- Sigmoid/Tanh: 其导数值在输入接近0时最大,在两端接近0。归一化使输入集中在线性区,确保梯度具有较大且稳定的值。
- ReLU 族: 虽然不存在“饱和区”,但输入值过大会导致激活值可能过大(尤其在深层),后续层计算的梯度也可能很大。归一化使输入保持在一个相对标准的尺度范围内,有助于稳定梯度的反向传播。
- 从而显著缓解梯度消失和梯度爆炸问题。
允许使用更大的学习率
- 问题描述:
- 如第1点所述,内部协变量偏移的存在迫使我们必须使用很小的学习率,以防网络因输入分布突变而变得不稳定。
- 归一化的作用:
- 由于归一化大大稳定了各层的输入分布,减小了训练过程中的震荡风险。
- 这使得我们可以使用更大的学习率而不用担心训练发散。更大的学习率显著加速了模型在训练初期的收敛速度。
降低对权重初始化的敏感度
- 问题描述:
- 在深度神经网络中,参数的初始值对训练的成败至关重要。不当的初始化容易导致梯度消失、爆炸或训练停滞。为深度网络找到完美的初始值是个难题。
- 归一化的作用:
- 归一化(尤其是 BN、LN、GN)减轻了网络对初始权重的依赖。即使初始权重设置得不是特别理想(例如初始输出值偏离0很多,或者方差过大/过小),归一化操作也能快速地将这些分布的偏移修正过来,使训练能够正常启动并继续下去。
- 这大大降低了设计深度网络架构和训练策略的门槛。
因此,加入归一化操作后,相当于给神经网络安装了:
- 稳定器(Stabilizer): 让每一层的输入都在一个“舒适区”工作。
- 加速器(Accelerator): 可以开足马力(大学习率)训练,大大加快收敛。
- 润滑剂(Lubricant): 让梯度流动得更顺畅,解决深层网络的梯度消失/爆炸问题。
- 防护罩(Shield): 降低对初始化和超参的敏感性,提升训练的鲁棒性。
- 轻微的随机干扰器(Light Regularizer): 特别是 BN,有助于提升泛化能力。
2 归一化算子
下表对比了最常用的几种归一化操作。
-
批归一化(Batch Normalization, BN)
原理: 对每一批数据的每个特征维度进行均值方差归一化,引入可学习的缩放和平移参数。
公式:
x norm = x − μ B σ B 2 + ϵ , y = γ x norm + β x_{\text{norm}} = \frac{x - \mu_{\mathcal{B}}}{\sqrt{\sigma_{\mathcal{B}}^2 + \epsilon}}, \quad y = \gamma x_{\text{norm}} + \beta xnorm=σB2+ϵx−μB,y=γxnorm+β
优点: 缓解内部协变量偏移,允许更大的学习率。
局限: 依赖批量大小,在小批量场景下性能下降。 -
层归一化(Layer Normalization, LN)
原理: 对单个样本的所有特征进行归一化,适用于RNN和Transformer等序列模型。
公式:
x norm = x − μ L σ L 2 + ϵ x_{\text{norm}} = \frac{x - \mu_{\mathcal{L}}}{\sqrt{\sigma_{\mathcal{L}}^2 + \epsilon}} xnorm=σL2+ϵx−μL
优点: 不依赖批量大小,适合动态网络结构。
应用: Transformer中的自注意力机制。 -
实例归一化(Instance Normalization, IN)
原理: 对每个样本的每个通道单独归一化,常见于图像生成任务。
优点: 保留图像风格信息,适合风格迁移。
对比: 与BN和LN的区别在于归一化维度。 -
组归一化(Group Normalization, GN)
原理: 将通道分组后对每组进行归一化,折中BN和LN的优势。
适用场景: 目标检测、小批量训练(如YOLOv3)。 -
其他归一化技术
权重归一化(Weight Normalization): 直接对网络权重参数归一化。
谱归一化(Spectral Normalization): 用于GAN训练,约束判别器 Lipschitz 常数。
2.1 Layernorm算子
对于一个样本(或序列中的一个时间步),将其在该层所有神经元(特征单元)上的输出激活值进行标准化(减均值,除以方差),再应用可学习的缩放和平移。
计算步骤:
- 计算均值和方差:沿特征维度(N)计算均值和方差。
- 归一化:对每个特征值进行归一化。
- 仿射变换:应用可学习参数缩放和平移。
PyTorch中的代码实现:
def layer_norm(x, normalized_shape, gamma, beta, eps=1e-5):
"""
x: 输入张量
normalized_shape: 归一化部分的形状 (如 (hidden_size,) 或 (d_model,))
gamma: 缩放参数 (形状 = normalized_shape)
beta: 偏移参数 (形状 = normalized_shape)
eps: 小常数
"""
# 1. 计算归一化轴上的均值和方差
mean = np.mean(x, axis=-1, keepdims=True) # 在最后一个轴(特征轴)上求均值
var = np.var(x, axis=-1, keepdims=True) # 在最后一个轴上求方差
# 2. 标准化
x_hat = (x - mean) / np.sqrt(var + eps)
# 3. 应用仿射变换
out = gamma * x_hat + beta
return out
2.2.1 归一化轴
在layernorm算子中,有一个比较重要的属性:归一化轴(axis)。
归一化轴的作用就是在,计算均值和方差时,按照指定维度计算,计算后被指定的维度会被压缩(保留为维度1以便于广播)。例如,输入形状(B, S, D)中指定axis=-1,计算结果形状会变为(B, S, 1)。
注:缩放参数gamma和平移参数beta的维度必须与axis指定的归一化维度完全一致。例如指定axis=(-1,)时,gamma/beta形状为(D,);指定axis=(-2,-1)时,gamma/beta形状应为(S,D)。
另外,计算均值和方差时shape发生变化,最终的输出shape是不会变的。
下面是deepseek的解释:
2.2.1 ONNX算子定义
在ONNX算子定义中,layernorm算子只在opset17及以上有op定义。因此,在低版本的opset中,往往是一些小算子的组合形式表示layernorm算子。
如下图所示,详细描述的layernorm算子的组合中各个小算子的作用:
-
属性:
1. axis:类型(int),默认值为 -1。规范化维度,如果 rank(X) 为 r,则 axis 允许的范围为 [-r, r]。负值表示从后面计算维度。 2. epsilon:类型(float),默认值 1e-05。计算方差时的数值稳定性常数。 3. stash_type: 类型(int) ,默认值为1。Mean 和 InvStdDev 的类型,还指定了第一阶段的计算精度。
-
输入:
1. X: 输入tensor 2. Scale: 均值 3. B: 偏移
-
输出:
1. Y: 输出tensor 2. Mean: 在训练期间用于加速梯度计算的保存均值 3. InvStdDev: 在训练期间用于加快梯度计算保存的逆标准差
另外,在ONNX中衍生出一种新的算子,就是addlayernorm算子。
addlayernorm算子结合了残差连接(add)和层归一化(layernorm)两个操作。
AddLayerNorm(x, y) = LayerNorm(x + y)
2.2 groupnorm算子
对于一个shape为[N,C,H,W]的输入,GroupNorm将每个[C,H,W]在C维度上分为groupnum组,然后对每一组进行归一化。最后对归一化后的特征进行缩放和平移。其中缩放参数和平移参数是可训练的。
- 计算公式:
# mean 和 variance 是按每组 channels 的每个实例计算的,并且为每组通道指定 'scale' 和 'bias'。参数 'num_groups' 应当能被通道数整除,以便与每组的通道数相等
y = scale * (x - mean) / sqrt(variance + epsilon) + bias
当组数与通道数相同时,该运算符等效于 InstanceNormalization。当只有一个组时,此运算符等效于 LayerNormalization。
2.3.1 ONNX算子定义
在ONNX算子定义中,groupnorm算子只在opset18及以上有op定义。因此,在低版本的opset中,往往是一些小算子的组合形式表示。
如下图所示,详细描述的groupnorm算子的组合中各个小算子的作用:
-
属性:
1. epsilon: 类型(float),默认值 1e-05。计算方差时的数值稳定性常数。 2. num_groups: 类型(int)必填 ,默认值为1。通道组的数量。它应该是通道数 'C' 的除数。
-
输入:
1. X: 输入数据张量。 2. scale: 缩放因子。 3. Bias: 偏移。
-
输入:
1. Y: 输出数据张量。
总结
以上就是今天要讲的内容,本文简单介绍了深度学习算法中常用的归一化方法,并且对layernorm和groupnorm算子进行详细介绍,加深了我们对归一化方法的理解。