batchnorm和layernorm的理解
batchnorm和layernorm原理和区别
batchnorm
原理
对于一个特征tensor
x ∈ R b × c × f 1 × f 2 × … x \in \mathbb{R}^{b \times c \times f_1 \times f_2 \times \dots} x∈Rb×c×f1×f2×…
其中, c c c是通道, f f f是通道中各种特征,batchnorm是对所有batch的每个通道特征分别进行归一化,即对于第 i i i个通道,选出他的所有batch的第 i i i个通道的所有特征 x [ : , i , : , : , . . . ] x[:,i,:,:,...] x[:,i,:,:,...]进行归一化:
μ = 1 m ∑ i = 1 m x i σ 2 = 1 m ∑ i = 1 m ( x i − μ ) 2 x ^ i = x i − μ σ 2 + ϵ y i = γ x ^ i + β \mu = \frac{1}{m} \sum_{i=1}^{m} x_i \\ \sigma^2 = \frac{1}{m} \sum_{i=1}^{m} (x_i - \mu)^2 \\ \hat{x}_i = \frac{x_i - \mu}{\sqrt{\sigma^2 + \epsilon}} \\ y_i = \gamma \hat{x}_i + \beta μ=m1i=1∑mxiσ2=m1i=1∑m(xi−μ)2x^i=σ2+ϵxi−μyi=γx^i+β
其中, ϵ \epsilon ϵ是较小的数,为了防止分母为0, γ \gamma γ和 β \beta β是可训练参数,用于仿射变换
因此,输出向量形状不发生改变,可训练参数取决于通道 c c c的个数
代码
import torch
import torch.nn as nn## BatchNorm1d
batchnorm1d = nn.BatchNorm1d(num_features=10) # 输入特征数为 10
# 构造输入张量 (N, C)
x_1d = torch.randn(16, 10) # 模拟训练数据 (batch_size=16, num_features=10)
y = batchnorm1d(x)
# 打印结果
print("Output shape :", y.shape) # (N, C)## BatchNorm2d
batchnorm2d = nn.BatchNorm2d(num_features=16) # 输入通道数为 16
# 构造输入张量 (N, C, H, W)
x = torch.randn(8, 16, 32, 32) # 模拟训练数据
y = batchnorm2d(x)
# 打印结果
print("Output shape :", y.shape) # (N, C, H, W)## BatchNorm3d
batchnorm3d = nn.BatchNorm3d(num_features=8) # 输入通道数为 8
# 构造输入张量 (N, C, D, H, W)
x = torch.randn(4, 8, 16, 32, 32) # 模拟训练数据
y = batchnorm3d(x)
# 打印结果
print("Output shape :", y.shape) # (N, C, D, H, W)
作用
- 加速模型收敛
- 缓解梯度消失或爆炸问题
- 降低对初始化的敏感性
- 一定程度上防止过拟合
通俗理解
从图片数据 x ∈ R b × c × h × w x \in \mathbb{R}^{b \times c \times h \times w} x∈Rb×c×h×w来理解:对于不同通道的特征,我们经过batchnorm以后,不同通道的特征便失去了可比性或者相对性(通道一:9,8,7,6;通道二:8,7,6,5经过归一化后都一样),但是从人的视角来区分一张图像不是靠不同通道之间的差异性,换句话说RGB稍微变一下变成RRR一样可以区分出图片中物体、属于哪一类。因此我们使用batchnorm相当于告诉模型你应该从同一通道内的数据差异来得到结果,而不是通道间。
其实你不告诉模型(不用batchnorm),模型在多次训练后也可以发现相同的规律,只是模型训练就慢了,因此我们说batchnorm可以加速收敛
注意点
模型训练阶段,我们存在batch维度,使用batchnorm时模型的正向计算会受到batch大小影响。那么我们在推理阶段通常只有一个batch,怎么办呢?
解决方法:在pytorch中,模型训练时,调用 m o d e l . t r a i n ( ) model.train() model.train(),当创建一个 BatchNorm 层时,PyTorch 会自动初始化 r u n n i n g m e a n running_{mean} runningmean和 r u n n i n g v a r running_{var} runningvar,并将它们存储为模型的缓冲区(buffers)。
在训练阶段:训练模式下( m o d e l . t r a i n ( ) model.train() model.train()),每次对 mini-batch 进行前向传播时:
r u n n i n g m e a n = m o m e n t u m ⋅ r u n n i n g m e a n + ( 1 − m o m e n t u m ) ⋅ μ b a t c h r u n n i n g v a r = m o m e n t u m ⋅ r u n n i n g v a r + ( 1 − m o m e n t u m ) ⋅ σ b a t c h 2 r u n n i n g v a r = m o m e n t u m ⋅ r u n n i n g v a r + ( 1 − m o m e n t u m ) ⋅ σ b a t c h 2 running_{mean}=momentum⋅running_mean+(1−momentum)⋅μ_{batch} \\ running_{var}=momentum⋅running_var+(1−momentum)⋅σ_{batch}^2 \\ running_{var}=momentum⋅running_var+(1−momentum)⋅σ_{batch}^2 runningmean=momentum⋅runningmean+(1−momentum)⋅μbatchrunningvar=momentum⋅runningvar+(1−momentum)⋅σbatch2runningvar=momentum⋅runningvar+(1−momentum)⋅σbatch2
在推理阶段:推理模型下( m o d e l . e v a l ( ) model.eval() model.eval()),模型会使用 r u n n i n g m e a n running_{mean} runningmean和 r u n n i n g v a r running_{var} runningvar进行归一化,从而避免上述问题
layernorm
原理
对于一个时序特征tensor
x ∈ R b × t × d x \in \mathbb{R}^{b \times t \times d} x∈Rb×t×d
其中, t t t是时序, d d d是某一时间点的各种特征,layernorm是对某一时间点的特征进行归一化,即对于第 i i i个时间点,分别在各个batch上选出他的所有特征 x [ k , i , : ] x[k,i,:] x[k,i,:]进行归一化:
μ = 1 d ∑ i = 1 d x i σ 2 = 1 d ∑ i = 1 d ( x i − μ ) 2 x ^ i = x i − μ σ 2 + ϵ y i = γ d x ^ i + β d \mu = \frac{1}{d} \sum_{i=1}^d x_i \\ \sigma^2 = \frac{1}{d} \sum_{i=1}^d (x_i - \mu)^2 \\ \hat{x}_i = \frac{x_i - \mu}{\sqrt{\sigma^2 + \epsilon}} \\ y_i = \gamma_{d} \hat{x}_i + \beta_{d} μ=d1i=1∑dxiσ2=d1i=1∑d(xi−μ)2x^i=σ2+ϵxi−μyi=γdx^i+βd
其中, ϵ \epsilon ϵ是较小的数,为了防止分母为0, γ d \gamma_{d} γd和 β d \beta_{d} βd是可训练参数,用于仿射变换
因此,输出向量形状不发生改变,可训练参数取决于特征维度$d$
的个数
代码
import torch
import torch.nn as nn
# 特征维度 d=16,归一化最后一个维度
layernorm = nn.LayerNorm(normalized_shape=16)# 打印可训练参数
print("Trainable parameters (gamma):", layernorm.weight.shape) # gamma (scale) (16)
print("Trainable parameters (beta):", layernorm.bias.shape) # beta (shift) (16)# 构造输入张量 (b, t, d)
x = torch.randn(32, 10, 16) # 前向传播
output = layernorm(x)# 打印结果
print("Input shape:", x.shape) # [32, 10, 16]
print("Output shape:", output.shape) # [32, 10, 16]
作用
- 加速模型收敛
- 缓解梯度消失或爆炸问题
- 降低对初始化的敏感性
- 一定程度上防止过拟合
通俗理解
从时序数据(或者一段话)数据 x ∈ R b × t × d x \in \mathbb{R}^{b \times t \times d} x∈Rb×t×d来理解:对于不同时间点的特征,我们经过layernorm以后,不同时间点的特征便失去了可比性或者相对性(时间点1:9,8,7,6;时间点2:8,7,6,5经过归一化后都一样),。。。目前我也不知道怎么直观理解(后续有新的理解持续更新。。)
注意点
和batchnorm一样,在pytorch中,训练和推理时,一定要指定模型当前状态
总结
BatchNorm适用于CV,LayerNorm适用于NLP
本文是我学习过程中的个人理解,有不对的地方希望大家帮忙指出。希望可以抛砖引玉,欢迎大家在评论区和我交流。