赞
踩
Layer Normalization (LayerNorm) 是一种归一化技术,常用于深度学习模型中,特别是在 Transformer 模型中。
与 Batch normalization 不同,Layer normalization 是在特征维度上进行标准化的,而不是在数据批次维度上。
Layer normalization 的计算可以分为两步:
代码测试如下:
- import torch
- import torch.nn as nn
-
- class LayerNorm(nn.Module):
- def __init__(self,num_features,eps=1e-6):
- super().__init__()
- self.gamma = nn.Parameter(torch.ones(num_features))
- self.beta = nn.Parameter(torch.zeros(num_features))
- self.eps = eps
-
- def forward(self,x):
- mean = x.mean(dim=-1,keepdim=True)
- std = x.std(dim=-1,keepdim=True,unbiased=False)
- normalized_x = (x - mean) / (std + self.eps)
- return self.gamma * normalized_x + self.beta
-
- if __name__ == '__main__':
- batch_size = 2
- seqlen = 3
- hidden_dim = 4
-
- # 初始化一个随机tensor
- x = torch.randn(batch_size,seqlen,hidden_dim)
- print(x)
-
- # 初始化LayerNorm
- layer_norm = LayerNorm(num_features=hidden_dim)
- output_tensor = layer_norm(x)
- print("output after layer norm:\n,",output_tensor)
-
- torch_layer_norm = torch.nn.LayerNorm(normalized_shape=hidden_dim)
- torch_output_tensor = torch_layer_norm(x)
- print("output after torch layer norm:\n",torch_output_tensor)
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Pre Norm 和 Post Norm 的式子分别如下:
Post-LN :是在 Transformer 的原始版本中使用的归一化方案。在此方案中,每个子层(例如,自注意力机制或前馈网络)的输出先通过子层自身的操作,然后再通过层归一化(Layer Normalization)
Pre-LN:是先对输入进行层归一化,然后再传递到子层操作中。这样的顺序对于训练更深的网络可能更稳定,因为归一化的输入可以帮助缓解训练过程中的梯度消失和梯度爆炸问题。
参考上面Pre Norm的公式:
其中第二项的方差由于有 norm 是不随层数变化的,于是 x 的方差会在主干上随层数积累。
到了深层以后,单层对主干的影响可以视为小量,而不同层的统计上是相似的。
于是有 :
=
这样训练出来的深层 ResNet or Transformer,深层部分实际上更像扩展了模型宽度,所以相对好训练,但某种意义上并不是真正的 deep.
post-norm 则保证了主干方差恒定,每层对 x 都可能有较大影响,代价则是模型结构中没有从头到尾的恒等路径,梯度难以控制。通常认为会更难收敛,但训练出来的效果更好。
在Bert时代由于层数较浅,往往采用的是Post-Norm
而到了大模型时代,由于Transformer的层数开始加深,为了训练稳定性开始使用Pre-Norm。
RMSNorm层则是通过计算沿着最后一个维度的均方根来归一化输入
并使用可学习的权重向量对归一化后的结果进行缩放。
与RMS Norm是基于LN的一种变体,主要是去掉了减去均值的部分
比于LN,可以发现,不论是分母的方差和分子部分,都取消了均值计算,经作者在各种场景中实验发现,减少约 7%∼64% 的计算时间。
代码实现如下:
- import torch
- import torch.nn as nn
-
- class RMSNorm(nn.Module):
- def __init__(self,dim: int,eps: float = 1e-6 ):
- super().__init__()
- self.eps = eps
- self.weight = nn.Parameter(torch.ones(dim))
-
- def _norm(self,x):
- return x * torch.rsqrt(x.pow(2).mean(-1,keepdim=True) + self.eps)
-
- def forward(self,x):
- output = self._norm(x.float()).type_as(x)
- return output * self.weight
Deep Norm是对LN的的改进,主要有两点改进,其中 和
都是根据模型的Encoder(N)和Decoder(M)层数计算出来的,通过如下方案,作者把模型的层数提升到了1000+。
DeepNorm的表达式为:
其中, 是一个常数(
>1 ),
是参数为 的第
个Transformer子层(即注意力或前馈网络)的函数。DeepNet还将残差内部的权重
扩展了常数参数
。
具体方案如下:
下面补充一些文章做的分析,感觉很有借鉴意义。下面这幅图,是作者做的三组翻译任务,Encoder-decoder都是18层。
通过上图,可以发现:
代码实现如下:
- import torch
- from torch import Size
- from typing import Union,List
- from torch.nn import LayerNorm
-
- Number_Layer = 1000 # Encoder/Decoder
-
- class DeepNorm(torch.nn.Module):
- def __init__(self,normalized_shape: Union[int,List[int],Size],eps: float = 1e-5, elementwise_affine: bool = True):
- '''
- Deep Layer Normalization
- :param normalized_shape: input shape from an expected input of size
- :param eps: a value added to the denominator for numerical stability
- :param elementwise_affine: a boolean value that when set to ``True``, this module
- has learnable per-element affine parameters initialized to ones (for weights)
- and zeros (for biases). Default: ``True``.
- '''
- super(DeepNorm,self).__init__()
-
- self.alpha = (2 * Number_Layer) ** 0.25
- self.layernorm = LayerNorm(normalized_shape,eps=eps)
-
- def forward(self,x):
- x_normed = self.layernorm(x)
- return self.alpha * x + x_normed
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。