批量归一化(Batch Normalization)与层归一化(Layer Normalization)

一句话概述

批量归一化(Batch Normalization, BN)和层归一化(Layer Normalization, LN)是深度学习中最核心的归一化技术,它们通过标准化中间层的特征分布来加速训练、稳定梯度、允许更大的学习率。BN在批次维度上计算均值和方差——对每个特征通道,在batch内所有样本上标准化;LN在特征维度上计算均值和方差——对每个样本,在其所有特征通道上标准化。BN是CNN的标配,能够显著加速图像分类模型的训练;LN是Transformer的标配,不依赖batch size,适合NLP和序列任务。近年来还出现了Instance Norm(风格迁移)、Group Norm(小batch场景)、RMSNorm(大模型简化版LN)等变体。

💡 核心要点:①归一化通过标准化中间层分布来加速训练,是深层网络训练的基石 ②BN在batch维度归一化(依赖batch size),适合CNN ③LN在feature维度归一化(不依赖batch size),适合Transformer/RNN ④BN使用训练时累积的移动平均均值和方差进行推理 ⑤归一化还起到轻微正则化作用

教学与演示

一、Batch Normalization:批次维度归一化

是什么(定义):Batch Normalization由Ioffe和Szegedy于2015年提出。对于卷积层输出x∈R^{B×C×H×W}(B=批次大小,C=通道数),BN沿B、H、W三个维度计算均值和方差,对每个通道独立标准化:x̂_c = (x_c - μ_c)/√(σ_c²+ε),然后通过可学习的缩放γ_c和偏移β_c恢复表达能力:y_c = γ_c·x̂_c + β_c。训练时使用当前batch的统计量;推理时使用训练过程中累积的移动平均(running mean和running var)。

大白话 BN就是"给每一层的输出做体检"。就像体检时量血压,如果"收缩压"偏离正常范围,医生会预警。BN在每个训练步骤检查每层输出的"均值和方差"是否正常——太偏了就把数据"拉回"均值为0、方差为1的标准状态。医生(BN)还会根据情况"微调"(γ和β),让正常范围符合当前层的需求。BN还有一个"记忆"功能——在推理时用训练时累积的"过往体检记录"(移动平均),而不是当前这一批的数据。

为什么(原理):BN解决了"内部协变量偏移"(Internal Covariate Shift)问题——训练过程中,由于前面层参数更新,后续层的输入分布不断变化,迫使后续层不断适应新分布。BN将每层输入标准化,减少了分布漂移。此外,BN还有以下效果:①允许更大的学习率(梯度缩放更稳定);②减少对权重初始化的敏感性;③轻微正则化(batch统计中的噪声类似于dropout效果);④缓解梯度消失/爆炸(激活值控制在合理范围)。

import numpy as np

# Batch Normalization的完整实现
# 展示训练和推理时的差异

class BatchNorm:
    def __init__(self, num_features, momentum=0.9, eps=1e-5):
        np.random.seed(42)
        self.num_features = num_features
        self.momentum = momentum
        self.eps = eps

        # 可学习参数
        self.gamma = np.ones(num_features)  # 缩放
        self.beta = np.zeros(num_features)  # 偏移

        # 移动平均(用于推理)
        self.running_mean = np.zeros(num_features)
        self.running_var = np.ones(num_features)

    def forward_train(self, x):
        """训练时的BN前向传播"""
        # x shape: (B, C),B=批次大小,C=特征数
        # 步骤1:计算当前batch的统计量
        batch_mean = np.mean(x, axis=0)  # 每个特征在batch上的均值
        batch_var = np.var(x, axis=0)  # 每个特征在batch上的方差

        # 步骤2:标准化
        x_norm = (x - batch_mean) / np.sqrt(batch_var + self.eps)

        # 步骤3:缩放和偏移(恢复表达能力)
        output = self.gamma * x_norm + self.beta

        # 步骤4:更新移动平均(用于推理)
        self.running_mean = self.momentum * self.running_mean + (1 - self.momentum) * batch_mean
        self.running_var = self.momentum * self.running_var + (1 - self.momentum) * batch_var

        return output, batch_mean, batch_var

    def forward_test(self, x):
        """推理时的BN前向传播"""
        # 使用训练时累积的移动平均
        x_norm = (x - self.running_mean) / np.sqrt(self.running_var + self.eps)
        output = self.gamma * x_norm + self.beta
        return output


# 创建示例数据
np.random.seed(1)
X = np.random.randn(16, 8) * 2 + 3  # batch=16, features=8, 均值≈3, 方差≈4
print(f"输入统计: 均值={np.mean(X):.2f}, 方差={np.var(X):.2f}")

bn = BatchNorm(num_features=8)

print("\n=== Batch Normalization ===\n")

# 训练模式
out_train, batch_mean, batch_var = bn.forward_train(X)
print("【训练模式】")
print(f"Batch均值: {np.round(batch_mean, 3)}")
print(f"Batch方差: {np.round(batch_var, 3)}")
print(f"输出均值: {np.mean(out_train):.2f}, 方差: {np.var(out_train):.2f}")
print("→ 输出被标准化(通过γ=1, β=0),但gamma/beta可学习调整")

# 更新多次移动平均
for _ in range(5):
    X2 = np.random.randn(16, 8) * 2 + 3
    bn.forward_train(X2)

print(f"\n运行均值: {np.round(bn.running_mean[:4], 3)}")
print(f"运行方差: {np.round(bn.running_var[:4], 3)}")

# 推理模式
X_test = np.array([[5.0, 3.0, 7.0, 2.0, 4.0, 6.0, 1.0, 8.0]])
out_test = bn.forward_test(X_test)
print(f"\n【推理模式】")
print(f"测试输入: {X_test.flatten()}")
print(f"标准化输出: {np.round(out_test.flatten(), 3)}")
print("→ 使用运行均值/方差而非batch统计量")
Batch Normalization的公式\(\hat{x}_i = \frac{x_i - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}}, \quad y_i = \gamma \hat{x}_i + \beta\)
大白话 BN就是把数据"规整"一下:先把当前这批数据的每个特征"掐头去尾"归到标准范围(均值0方差1),然后乘以一个"可学习的幅度"γ,加上"可学习的基线"β。γ和β让网络自己决定"我到底需要什么样的分布"——如果网络发现"不归一化更好",它会学得γ=σ, β=μ,相当于把归一化"抵消"掉。推理时用训练期间积累的"平均均值"和"平均方差",因为推理可能batch_size=1,没法算batch统计量。

什么用(应用):BN是CNN训练的标准配置。ResNet、DenseNet、EfficientNet等所有经典CNN都使用BN。BN允许使用大学习率(如0.1),减少了对权重初始化的敏感度,使得上百层的CNN训练成为可能。

哪些坑(缺点):BN依赖于batch size——batch太小时(如<8),batch统计量不稳定。RNN和Transformer中每个时间步/位置的特征分布不同,BN不适合。推理时的移动平均统计量需要保存,增加了模型大小。BN打破了batch内样本的独立性(一个样本的BN输出依赖同batch的其他样本),在某些任务(如对比学习)中有负面影响。

二、Layer Normalization:特征维度归一化

是什么(定义):Layer Normalization由Ba等人于2016年提出。与BN在batch维度上标准化不同,LN在特征维度上标准化——对每个样本,计算其所有特征通道的均值和方差,然后标准化。LN不依赖batch size,每个样本独立标准化。公式为:μ = (1/d)Σ_i x_i, σ² = (1/d)Σ_i (x_i - μ)², x̂_i=(x_i-μ)/√(σ²+ε), y_i=γx̂_i+β。

大白话 LN就是"每个人自己量血压"。BN是"一批人体检,算出这批人的平均血压,然后每个人的血压按这批人的平均值来校准"——依赖一群人。LN是"每个人自己量血压,按自己的历史血压来校准"——不依赖别人。这种独立性让LN在"单打独斗"的场景(如翻译、对话生成)中很稳定——即使只有一句话(batch=1),LN也能正常工作。

为什么(原理):LN适合Transformer和RNN的原因:①序列长度可变——BN在可变长度序列上维度不对齐;②RNN的时序依赖——不同时间步使用相同的BN统计量不合理;③推理时batch可能为1——BN在batch=1时统计量退化。LN在特征维度上标准化的方式也与NLP任务中词的表示特性更契合——每个词的嵌入向量的"平均值"和"方差"反映的是该词在当前上下文中的"激活强度",标准化后使不同词的表示更具可比性。

import numpy as np

# Layer Normalization的完整实现
# 对比BN和LN在不同场景下的适用性

class LayerNorm:
    def __init__(self, num_features, eps=1e-5):
        self.gamma = np.ones(num_features)
        self.beta = np.zeros(num_features)
        self.eps = eps

    def forward(self, x):
        """LN前向传播:沿特征维度标准化"""
        # x shape: (N, d),N=序列长度,d=特征数
        # 关键:沿axis=-1计算(特征维度)
        mean = np.mean(x, axis=-1, keepdims=True)  # 每个样本的均值
        var = np.var(x, axis=-1, keepdims=True)  # 每个样本的方差
        x_norm = (x - mean) / np.sqrt(var + self.eps)
        return self.gamma * x_norm + self.beta


# 对比BN和LN的适用场景
print("=== Layer Normalization vs Batch Normalization ===\n")

# 场景1:NLP——变长序列,batch_size=1
X_nlp = np.random.randn(1, 5, 8)  # 1个句子,5个词,8维特征
print("【NLP场景:1个句子,5个词】")
print(f"输入形状: {X_nlp.shape}")

# BN:batch=1时统计量退化
# LN:正常工作
ln = LayerNorm(num_features=8)
out_ln = ln.forward(X_nlp[0])  # (5, 8)
print(f"LN:每个词独立标准化 → 输出形状: {out_ln.shape}")

# 场景2:CNN——大batch,固定尺寸
X_cnn = np.random.randn(32, 16, 7, 7)  # 32张图,16通道,7×7
print(f"\n【CNN场景:32张图,16通道】")
print(f"输入形状: {X_cnn.shape}")
print("BN:使用batch统计量 → 适合CNN")
print("LN:每张图独立标准化 → 计算量大,不适合CNN")

print("\n总结:")
print("┌──────────┬─────────┬─────────┬──────────┐")
print("│   特性   │   BN    │   LN    │ 适用场景 │")
print("├──────────┼─────────┼─────────┼──────────┤")
print("│ 标准化轴 │ batch   │ feature │          │")
print("│ 依赖batch│   是    │   否    │          │")
print("│ 适用架构 │  CNN    │Transformer│        │")
print("│ batch=1  │ 不稳定  │  正常   │          │")
print("│ 变长序列 │ 不适合  │  适合   │          │")
print("└──────────┴─────────┴─────────┴──────────┘")
Layer Normalization的公式\(\mu = \frac{1}{d}\sum_{i=1}^{d} x_i, \quad \sigma^2 = \frac{1}{d}\sum_{i=1}^{d} (x_i - \mu)^2, \quad \hat{x}_i = \frac{x_i - \mu}{\sqrt{\sigma^2 + \epsilon}}, \quad y_i = \gamma \hat{x}_i + \beta\)
大白话 LN就是"自己的特征自己管"。不管你是batch里的第1个样本还是第100个样本,LN只看你自己的d个特征值——算出这d个值的均值和方差,然后标准化。这种方式使得LN在推理时完全不需要"记忆"训练时的统计量(BN需要),代码简洁,行为一致。Transformer里的每个词都独立做LN,无论batch里有多少句子。

什么用(应用):LN是Transformer(BERT、GPT、T5等)的标配。每个注意力子层和前馈网络子层后都有LN。LN也是RNN训练的标准归一化方式。近年来,RMSNorm(只除σ不做中心化)作为LN的简化版,在LLaMA等大模型中使用,计算更高效。

哪些坑(缺点):LN在CNN上效果不如BN——图像的空间维度上使用LN会破坏空间信息(每个空间位置的归一化是独立的)。LN不提供BN的"轻微正则化"效果(因为没有batch统计的噪声)。大模型中LN的计算开销虽小但可积累,RMSNorm通过去掉减均值操作来加速。

三、其他归一化变体

是什么(定义):除BN和LN外,还有Instance Normalization(IN)、Group Normalization(GN)和RMSNorm。IN沿空间维度(H,W)标准化——每个样本的每个通道独立标准化,用于风格迁移。GN将通道分组——在每组内标准化,是BN(组数=1)和IN(组数=通道数)的推广,适合小batch_size。RMSNorm只除以均方根(RMS),不做减均值操作:x̂=x/RMS(x)·γ。

大白话 不同归一化就是"在哪个维度上算均值和方差"的区别。BN算"同一通道上所有样本"的均值和方差;LN算"同一样本上所有通道"的均值和方差;IN算"同一样本同一通道上所有空间位置"的均值和方差;GN算"同一样本同一组通道上所有空间位置"的均值和方差。维度选择的不同,决定了归一化适合不同的任务和架构。RMSNorm是LN的"偷懒版"——不减均值只除标准差,但大模型上效果差不多,计算还更快。

概念关系图谱

概念核心含义与AI的关系关联概念
归一化(Normalization)标准化中间层特征分布深层网络训练的基石标准化、白化
Batch Normalizationbatch维度标准化CNN训练的标配内部协变量偏移
Layer Normalizationfeature维度标准化Transformer/RNN的标配序列建模
Instance Normalization空间维度标准化风格迁移的核心技术风格解耦
Group Normalization分组标准化小batch场景替代BNBN/IN的一般化
RMSNorm仅除RMS不去均值大模型LN的高效替代计算效率

重点答疑

Q1: BN为什么不适合RNN和Transformer?

三个原因:①序列长度可变——BN需要固定的维度来维护移动平均,RNN/Transformer处理变长序列时维度不一致;②RNN的时序共享权重——每个时间步使用相同的RNN单元,但BN在每个时间步需要不同的统计量(因为时间步的分布不同);③推理时batch_size=1——BN在batch=1时batch统计量极不稳定,而NLP推理经常batch=1。

Q2: LN中减均值(center)操作有什么用?为什么RMSNorm可以去掉?

减均值(center)让数据以0为中心,有助于梯度的对称流动。但大模型实验发现,去掉减均值(只保留除RMS)对效果影响不大,却能节省约7-8%的计算量。这是因为在大模型中,后续的网络层(特别是注意力机制)本身已经提供了足够的灵活性来处理非零均值的数据。

Q3: BN的移动平均为什么需要momentum参数?

momentum控制训练统计量向推理统计量的"平滑过渡"。如果momentum太小(如0.5),running_mean/var受近期batch影响大,波动大;如果momentum太大(如0.999),running_mean/var更新太慢,反映不出训练早期到后期的分布变化。通常momentum=0.9或0.99是一个较好的平衡。

章节单词汇总

英文音标术语/释义
Batch Normalization/bætʃ ˌnɔːrməlaɪˈzeɪʃən/批量归一化,batch维度标准化
Layer Normalization/ˈleɪər ˌnɔːrməlaɪˈzeɪʃən/层归一化,feature维度标准化
Running Mean/Var/ˈrʌnɪŋ miːn/移动均值/方差,训练中累积的统计值
Internal Covariate Shift/ɪnˈtɜːrnəl koʊˈveriət ʃɪft/内部协变量偏移,层输入分布变化
RMSNorm/ɑːr em es nɔːrm/均方根归一化,仅除RMS不去均值
Instance Normalization/ˈɪnstəns/实例归一化,空间维度标准化
Group Normalization/ɡruːp/组归一化,分组标准化

面试练习

Q1 [单选] Batch Normalization在哪个维度上计算均值和方差?

  • A. 特征维度
  • B. 批次(batch)维度
  • C. 空间维度
  • D. 时间维度
解答:BN在batch维度上计算——对于每个特征通道,计算该通道在当前batch中所有样本的均值和方差。

Q2 [单选] Layer Normalization为什么适合Transformer?

  • A. 不依赖batch size,每个样本独立标准化
  • B. 比BN速度更快
  • C. 比BN参数更少
  • D. 比BN对CNN更有效
解答:LN不依赖batch size——即使batch=1(NLP推理常见场景)也能正常工作。每个词(位置)的表示独立标准化,适合变长序列。

Q3 [单选] BN推理时使用什么统计量?

  • A. 当前batch的统计量
  • B. 训练时累积的移动平均(running mean/var)
  • C. 全零
  • D. 随机统计量
解答:BN推理时使用训练过程中累积的running_mean和running_var。这是因为推理时batch_size可能很小(甚至为1),batch统计量不可靠。

Q4 [多选] 关于BN和LN,以下哪些是正确的?

  • A. BN在batch维度标准化,LN在feature维度标准化
  • B. BN依赖batch size,LN不依赖
  • C. BN是CNN的标配,LN是Transformer的标配
  • D. LN推理时也需要移动平均统计量
  • E. 两者都有可学习的γ和β参数
解答:BN batch维度标准化、依赖batch size、CNN标配;LN feature维度标准化、不依赖batch、Transformer标配。LN训练推理行为一致,不需要移动平均。两者都有γ和β。

Q5 [单选] 大语言模型(如LLaMA)中常使用哪种归一化?

  • A. Batch Normalization
  • B. Layer Normalization
  • C. RMSNorm
  • D. Instance Normalization
解答:LLaMA等大模型使用RMSNorm(Root Mean Square Normalization),它是LN的简化版——只除RMS不去均值,计算更高效,在大模型上与LN效果类似。