【Transformer】Residual and LayerNorm

image-20240805142301818

Layer Normal

Layer Normalization 的作用是把神经网络中隐藏层归一为标准正态分布,也就是$ i.i.d $独立同分布,以起到加快训练速度,加速收敛的作用

其中\(\mu_j\)表示均值,\(\sigma^2_j\)为方差,加 \(\epsilon\)是为了防止分母为 0 \[ LayerNorm(x)=\frac{x_{ij}-\mu _j}{\sqrt{\sigma^2_j+\epsilon}} \]

层规范化和批量规范化的目标相同,但Layer Normal是基于特征维度进行规范化。

对于Batch Normal来说,样本长度对计算方差影响比较大。 而Layer Normal针对每个样本计算方差

对于两个维度的情况,Batch Normal对于每个feature来做normal,Layer Normal则以样本为单位做normal

image-20240805143242244

对于三个维度的情况,在自然语言处理任务中,句子的长度往往不一致,有的很长有的很短

Batch normal会考虑所有的句子来normal,如果训练集和测试集句子长度分布差距巨大,会给训练带来麻烦

Layer normal则对句子内进行normal处理,最大程度避免句子长度不一的问题

image-20240805144748910

因此Batch normal在计算机视觉中被广泛应用,但在自然语言处理任务中(输入通常是变长序列)Batch normal通常不如Layer normal化的效果好

通过pytorch内置的LayerNorm和BatchNorm1模块实现normal处理

1
2
3
4
5
6
7
8
9
10
ln = nn.LayerNorm(2)
bn = nn.BatchNorm1d(2)
X = torch.tensor([[1, 2], [2, 3]], dtype=torch.float32)
ln(X), bn(X)
"""
(tensor([[-1.0000, 1.0000],
[-1.0000, 1.0000]], grad_fn=<NativeLayerNormBackward0>),
tensor([[-1.0000, -1.0000],
[ 1.0000, 1.0000]], grad_fn=<NativeBatchNormBackward0>))
"""

可以看出LayerNorm对样本内部实现了normal,BatchNorm1d则对feature实现了normal

使用残差连接和LayerNorm来实现AddNorm类

计算公式: \(LayerNorm(x+Sublayer(x))\)

1
2
3
4
5
6
7
8
9
class AddNorm(nn.Module):
"""残差连接后进行层规范化"""
def __init__(self, normalized_shape, dropout, **kwargs):
super(AddNorm, self).__init__(**kwargs)
self.dropout = nn.Dropout(dropout)
self.ln = nn.LayerNorm(normalized_shape)

def forward(self, X, Y):
return self.ln(self.dropout(Y) + X)

LayerNorm模块传入的参数为层归一化对应维度的形状

并且要求X、Y的形状相同才能实现残差连接

1
2
3
add_norm = AddNorm([3, 4], 0.5)
add_norm.eval()
add_norm(torch.ones((2, 3, 4)), torch.ones((2, 3, 4))).shape # torch.Size([2, 3, 4])