Transoformer Pytorch实现

发布时间 2023-10-30 15:36:06作者: Laplace蒜子

LayerNorm层

作用

BatchNorm计算的是一批样本,样本间同一个通道进行正则化

LayerNorm计算的是一个样本内,一个样本内进行正则化。

实现代码

class LayerNorm(nn.Module):
    def __init__(self,features,eps=1e-6):
        super(LayerNorm,self).__init__()
        self.a_2 = nn.Parameter(torch.ones(features))#可学习的参数
        self.b_2 = nn.Parameter(torch.zeros(features))#可以学习的参数
        self.eps =eps
    def forward(self,x):
        #BatchNorm计算的是一批样本,相同位置之间的均值和方差。
        #LayerNorm计算的是一个样本内,所有单词的均值和方差。
        mean = x.mean(-1,keepdim=True) #计算最后一维度的均值 3x100
        std = x.std(-1,keepdim=True) #计算最后一个维度的方差
        return self.a_2 * (x-mean) / (std+self.eps) + self.b_2 #计算每个样本x内正则化

 

位置编码

作用

弥补自注意力机制的短板,区分不同位置字符的绝对位置。

自注意力机制没有位置信息,比如“我爱你”和“你爱我”,每个词组合后注意力得分是相同的,所以需要在每个字符的嵌入上加上位置编码。

公式

pos为token的位置,dim为token嵌入后的维度,i为单个token的嵌入后的维度中第i个向量。

结构

每个token嵌入后的维度为dim,每个token就对应一个dim维的位置编码。

 

代码实现

class PositionalEncoding(nn.Module): #整理特征,不改变特征维度
    def __init__(self,max_len,dim,dropout):
        #max_len:位置编码的总个数,位置编码与输入的特征无关,可以预先生成指定个数,与特征相加的时候可以切特取部分。
        #dim:每个词的维度
        super(PositionalEncoding,self).__init__()
        self.dropout = nn.Dropout(p=dropout)
        pe = torch.zeros(max_len,dim) #生成max_len x dim 维度的矩阵
        position = torch.arange(0,max_len).unsqueeze(1) #
        div_term = torch.exp( torch.arange(0,dim,2) * -(math.log(10000.0)/dim)).unsqueeze(0)
        pe[:,0::2]=torch.sin(position*div_term)
        pe[:,1::2]=torch.cos(position*div_term)
        pe = pe.unsqueeze(0)
        self.register_buffer('pe',pe) #保证pe在训练的时候不会更新。

    def foward(self,x):
        out = x + self.pe[:,:x.size(1)] #将与x对应位置的位置编码加起来。
        out = self.dropout(out)
        return out

可以知道位置编码和特征x无关,且分子和分母公共的部分,可以用切片传播方式实现。

可以将相除改成相乘。

其中 -ln10000 / dim这是一个固定的数值。

pe用torch.zeros生成 ( max_len , dim ) 维度的0占位符。

position用torch.arrange生成0到max_len,形状为 ( maxlen,)  数组,表示生成0到max_len个字符的位置编码,为[0,1,2,3,...,max_len],再改变形状为 ( maxlen , 1 )。

div_ter先生成 [0,2,...,dim],形状为(dim/2,)占位符,一共dim/2个,因为2i和2i+1位置是相同,只不过是一个用sin和cos。然后再乘以一个固定的数值 -ln10000 / dim,再取exp,得到如下:

e^{  [0,2,...,dim] * -ln10000/dim  },形状为(1,dim/2)

position的形状为 ( maxlen,1 )

position 和 div_term相乘后就是( maxlen,dim/2 ) 

取 sin( position * div_term) 赋值给pe第二维度的偶数下标。

取cos(position * div_term) 赋值给pe第二维度的奇数下标。

最后将pe第0维度扩充,为了对应句子的批次。