目录
声明:该代码仅作为个人学习使用,代码来源自b站up:水论文的程序猿。在学习过程中如果发现不妥之处自行修改,我只是进行了大致的代码简析。我自己发现不妥的地方会附上修改后的正确代码,建议对照下面链接中的内容进行学习learn-nlp-with-transformers/docs/篇章2-Transformer相关原理/2.2.1-Pytorch编写Transformer.ipynb at main · datawhalechina/learn-nlp-with-transformers · GitHub
Add&Norm
Norm里面做残差,会输入(x[残差值]和z[淡粉色]),输出z[紫粉色]
SublayerConnection:子层连接
假设LayerNorm已经做好了,现在做的是一个子层的连接
子层连接的初始化:要有一个LayerNorm,还要有一个Dropout
self.layer_norm = LayerNorm(size)
self.dropout = nn.Dropout(p=dropout)
SublayerConnection不仅仅做了残差,是把残差和LayerNorm一起做了,如下图所示
LayerNorm会接收X和Z的,现在不用关心,只需要输入size
Dropout就是去掉一些参数防止过拟合之类的.比如LayerNorm里面可能会有许多参数,Dropout会随机去掉里面的一些参数值,一般是0.1
在前馈forward中做dropout,最重要的是前面对LayerNorm做了初始化得到LayerNorm层,后把x和sublayer(x)输入进去
x:上一层的输入,即Self-Attention层的输入
sublayer(x):上一层的输出,即Self-Attention层的输出(sublayer需要手动指定传递到底是哪一层的输出)
def forward(self, x, sublayer):
return self.dropout(self.layer_norm(x + sublayer(x)))
class SublayerConnection(nn.Module):
"""
子层的连接: layer_norm(x + sublayer(x))
上述可以理解为一个残差网络加上一个LayerNorm归一化
"""
def __init__(self, size, dropout=0.1):
"""
:param size: d_model
:param dropout: drop比率
"""
super(SublayerConnection, self).__init__()
self.layer_norm = LayerNorm(size)
# TODO:在SublayerConnection中LayerNorm可以换成nn.BatchNorm2d
# self.layer_norm = nn.BatchNorm2d()
self.dropout = nn.Dropout(p=dropout)
def forward(self, x, sublayer):
return self.dropout(self.layer_norm(x + sublayer(x)))
LayNorm:归一化
LayNorm就是要实现上述的归一化公式
γ,β:定义了两个参数为可学习参数,可以随着训练过程的变化而变化
ε:防止分母为0
自己定义下面三个参数:
γ:self.ones_tensor = nn.Parameter(torch.ones(x_size)) # 按照特征向量大小返回一个全1的张量,并且转换成可训练的parameter类型
β:self.zeros_tensor = nn.Parameter(torch.zeros(x_size))
ε:self.eps = eps
X相关的几项是可以算出来的(X相当于X+Z)
具体归一化实现:
torch.ones(x_size)是为了获取输入的维度大小,使得定义的可学习参数能够顺利与X做运算
self.ones_tensor = nn.Parameter(torch.ones(x_size)) # 按照特征向量大小返回一个全1的张量,并且转换成可训练的parameter类型
self.zeros_tensor = nn.Parameter(torch.zeros(x_size))
self.eps = eps
数学公式实现:
mean:平均值
std:标准差
最后返回计算公式
def forward(self, x):
mean = x.mean(-1, keepdim=True)
std = x.std(-1, keepdim=True) # 求标准差
return self.ones_tensor * (x - mean) / (std + self.eps) + self.zeros_tensor # LayerNorm的计算公式
class LayerNorm(nn.Module):
"""
构建一个LayerNorm Module
LayerNorm的作用:对x归一化,使x的均值为0,方差为1
LayerNorm计算公式:x-mean(x)/\sqrt{var(x)+\epsilon} = x-mean(x)/std(x)+\epsilon
"""
def __init__(self, x_size, eps=1e-6):
"""
:param x_size: 特征的维度
:param eps: eps是一个平滑的过程,取值通常在(10^-4~10^-8 之间)
其含义是,对于每个参数,随着其更新的总距离增多,其学习速率也随之变慢。
防止出现除以0的情况。
nn.Parameter将一个不可训练的类型Tensor转换成可以训练的类型parameter,
并将这个parameter绑定到这个module里面。
使用这个函数的目的也是想让某些变量在学习的过程中不断的修改其值以达到最优化。
"""
super(LayerNorm, self).__init__()
self.ones_tensor = nn.Parameter(torch.ones(x_size)) # 按照特征向量大小返回一个全1的张量,并且转换成可训练的parameter类型
self.zeros_tensor = nn.Parameter(torch.zeros(x_size))
self.eps = eps
def forward(self, x):
mean = x.mean(-1, keepdim=True)
std = x.std(-1, keepdim=True) # 求标准差
return self.ones_tensor * (x - mean) / (std + self.eps) + self.zeros_tensor # LayerNorm的计算公式