分组归一化 GroupNorm:小批次下的归一化选择
什么是分组归一化
在深度学习中,归一化层是稳定训练、加速收敛的核心组件。批归一化曾是最广泛使用的方案,但它有一个致命弱点:当批大小过小时,统计量估算不准,性能急剧下降。分组归一化正是为解决这一问题而设计,它将通道划分为多个组,在每个组内独立计算均值和方差,完全不依赖批维度。这使得分组归一化在小批次、变长序列、甚至批大小为 1 的极端场景下依然表现稳健。
分组归一化的核心思想可以概括为一句话:放弃跨样本的统计耦合,转而利用特征通道内的结构化分组,在单样本内部完成归一化。
为什么需要分组归一化
批归一化的困境
批归一化沿着批方向计算均值和方差,当批大小较大时,统计量稳定,归一化效果好,还能带来轻微的正则化。但以下场景会让批归一化捉襟见肘:
- 显存限制导致的小批次训练:目标检测、语义分割等高分辨率任务往往只能使用很小的批大小。
- 序列长度不一致:循环神经网络中,每个时间步的批大小可能不同,统计量波动剧烈。
- 在线学习或增强学习:每次只有一个或极少数样本,批统计几乎无意义。
- 生成模型和对比学习:经常需要处理单张图像或极小的数据单元。
在这些情况下,批归一化的错误率会明显上升,因为小批次下的均值方差估计带着很大的噪声,测试阶段的滑动平均统计量也难以准确代表全局分布。
分组归一化的定位
分组归一化更像一个“通道内的层归一化折中方案”。层归一化对单个样本的所有通道计算统计量,当通道间差异很大时,统一归一化会抹去通道特有的表征能力。实例归一化只对单通道的空间维度做归一化,完全丢失通道间的关联。分组归一化在它们之间找到了一个可调节的平衡点:将通道分成若干组,组内分享归一化参数,组间保持独立。这种设计让它在批大小很小时几乎不受影响,同时保留了通道间的结构化信息。
分组归一化的数学原理
给定一个特征图,形状通常是 (N, C, H, W),或者更一般地表示为 (N, C, *),其中:
N:批大小C:通道数*:空间维度(可以是一维、二维或三维)
分组归一化的计算步骤如下:
-
分组
将C个通道平均分为G个组,每组包含C//G个通道。要求C能被G整除。 -
计算统计量
对于每个样本的每个组,在组内所有通道和所有空间位置上计算均值μ和方差σ²。均值和方差的维度是(N, G),空间维度被打平成一体。 -
归一化与缩放平移
同一组内的所有元素共享一个均值和一个方差,用该组统计量进行归一化。随后像批归一化一样,应用可学习的缩放参数γ和偏移参数β。γ和β的形状与原始通道数相同,即每个通道有自己独立的仿射参数。
具体公式为:
μ_ng = (1 / (m * k)) * Σ_{c∈group_g} Σ_{spatial} x_nc...
σ²_ng = (1 / (m * k)) * Σ_{c∈group_g} Σ_{spatial} (x_nc... - μ_ng)²
x̂_nc... = (x_nc... - μ_ng) / sqrt(σ²_ng + ε)
y_nc... = γ_c * x̂_nc... + β_c
其中 m 是每组的通道数,k 是空间位置总数,ε 是一个极小常数防止除零。
与其它归一化的张量视图对比
假设输入张量形状为 (N, C, H, W),不同归一化方法的统计量计算轴如下:
- 批归一化:在
N, H, W上计算,每个通道独立。统计形状(C,)。 - 层归一化:在
C, H, W上计算,每个样本独立。统计形状(N,)。 - 实例归一化:在
H, W上计算,每个样本每个通道独立。统计形状(N, C)。 - 分组归一化:在每组内的
C_g, H, W上计算,每个样本每个组独立。统计形状(N, G)。
这种张量视角清晰揭示:分组归一化的统计量既不依赖批次样本间的混合,也不把所有通道强行揉在一起,而是把通道拆分成组,组内共享归一化。
何时使用分组归一化
适合的场景
- 小批次或批次大小 1 的训练:目标检测、分割、视频处理等。
- 循环神经网络:序列长度不固定,批次统计不稳定。
- 生成对抗网络:训练时判别器或生成器可能只处理极小批次。
- 自监督和度量学习:常用极小的批次进行对比学习。
- 模型部署需要消除批依赖:测试时无需维护滑动平均统计量。
不太适合的场景
- 大批次训练且通道数很少时,组数受限,分组过粗或过细都会削弱表征,此时批归一化更优。
- 对训练速度要求极高且已有大批次条件,批归一化的高度优化实现(如 cuDNN 融合)可能更快。
分组数 G 的选择
G 是一个超参数,常见设置有:
G = 32:当通道数较多(如 ResNet 的 256、512、1024)时,效果好且稳定。G = 16:适用于中等通道数。G = C // 2或G = C:退化为实例归一化或接近层归一化,可根据需求调整。
经验上,G 不宜太小(比如 2),否则组内统计过于粗糙;也不宜太多(接近实例归一化),否则丢失通道间协同信息。一个稳健的起点是 G = 32 或 G = 16。
在 PyTorch 中使用 GroupNorm
PyTorch 提供了 torch.nn.GroupNorm,使用极为简单:
import torch
import torch.nn as nn
# 假设特征图通道数为 128,分为 32 组
gn = nn.GroupNorm(num_groups=32, num_channels=128)
# 输入形状: (batch, 128, height, width)
x = torch.randn(4, 128, 32, 32)
y = gn(x) # 输出形状相同
几点注意事项:
num_channels必须能被num_groups整除,否则会报错。- 初始化时
γ初始化为 1,β初始化为 0,学习过程中自动调整。 - 与批归一化不同,分组归一化不维护 running mean / running var,因此切换
model.eval()时行为完全一致,没有训练和测试模式的差异。
替换批归一化的常见模式
在卷积层后直接替换即可。例如原有的残差块:
# 原始使用批归一化
class BottleneckBN(nn.Module):
def __init__(self, in_channels, out_channels):
...
self.bn1 = nn.BatchNorm2d(out_channels)
...
# 改为分组归一化,仅修改归一化层
class BottleneckGN(nn.Module):
def __init__(self, in_channels, out_channels, num_groups=32):
...
self.gn1 = nn.GroupNorm(num_groups, out_channels)
...
其他结构如解码器、自注意力模块等同样可直接替换。需要注意的是,分组归一化不引入跨样本依赖,因此在分布式训练时也无需同步统计量,减少了通信开销。
性能对比与研究结论
何恺明等人在《Group Normalization》论文中给出了充分的实验证据:
- 在 ImageNet 分类任务上,当批大小为 2 时,ResNet-50 采用分组归一化的 top-1 错误率比批归一化低约 3-5 个百分点。
- 在 COCO 目标检测和实例分割任务上,使用 Mask R-CNN 框架,分组归一化在极小批次下显著优于批归一化,甚至比大批次下的批归一化结果更优或持平。
- 分组归一化对
G的选择在一定范围内不敏感,G=8到G=32表现得都很稳定,展示了良好的鲁棒性。
这些结果奠定了分组归一化在小批次视觉任务中的地位,使其成为目标检测、分割等领域的默认选择之一。
实现注意事项与常见问题
通道数不能整除组数
确保 C % G == 0,否则无法均分。通常在网络设计中就固定好通道数与组数的整除关系。如果因结构调整导致无法整除,最简单的方式是调整通道数或组数。
计算效率
分组归一化需要对中间维度进行重塑和归约操作,如果实现不够优化,可能比高度融合的批归一化稍慢。现代深度学习框架已有优化,差距在可以接受的范围内。对于实际部署,推理时无需维护统计量,整体延迟往往与批归一化相当甚至更优。
与激活函数的顺序
和批归一化一样,通常按照“卷积 → 分组归一化 → 激活函数”的顺序放置。在某些现代结构中(如 pre-activation 残差块),顺序可能变为“分组归一化 → 激活 → 卷积”,两种方式均可,需根据整体网络设计来定。
与其它归一化混合使用
没有禁止混合使用的规则,但通常保持整个网络归一化方式一致更容易调优。少数情况会在网络早期用批归一化(如果该阶段批次够大),深层次改用分组归一化,这需要实验验证。
分组归一化抛弃了对批次大小的依赖,在小样本、变长序列和资源受限的场景下表现尤为突出。它通过灵活的分组机制在层归一化和实例归一化之间找到了平衡点,实现简单、概念直观,已经成为现代计算机视觉模型的基础构建模块之一。