Transformer详解,输入部分(词嵌入、位置编码)

2021-01-23  本文已影响0人  三角绿毛怪

Transformer解读

1.整体架构解读

image.png

由图可知:

1.1输入部分:

inputs和带标签的输入分别进encoder和decoder

Positional Encoding

1.2输出部分

线性层

softmax层

1.3编码器部分

由N个编码器堆叠而成

每个编码器有两个子层相连接

第一个子层->多头自注意力机制和规范化层以及一个残差连接

第二个子层->全连接层和规范化层以及一个残差连接

1.4解码器部分

由N个解码器堆叠而成

每个编码器有三个子层相连接

第一个子层->一个多头自注意力机制层和规范化层以及一个残差连接

第二个子层->多头注意力机制和规范化层以及一个残差连接

第三个子层->全连接层和规范化层以及一个残差连接

2.输入部分详解

2.1Embedding词嵌入

目的是为了将目标文本的数字表示->向量表示,为了在高维空间捕捉词汇间的关系

#1.embedding类的实现
import torch
from torch.autograd import Variable
import math
import torch.nn as nn

class Embeddings(nn.Module):#需要继承
    def __init__(self,d_model,vocab):#词嵌入的维度,词表大小
        super(Embeddings,self).__init__()#继承
        self.lcut = nn.Embedding(vocab,d_model)
        self.d_model = d_model

    def forward(self,x):
        """
        可以理解为该层的前向传播逻辑,所有层中都会有此函数。传给类的实例化对象时,自动调用
        :param x:输入进模型的文本通过词汇映射后的数字张量
        :return:
        """
        return self.lcut(x)*math.sqrt(self.d_model)

效果如下

d_model = 512
vocab = 100
x = Variable(torch.LongTensor([[1,2,3,4],[5,6,7,8]]))#[2,4]
emb = Embeddings(d_model,vocab)
embr = emb(x)
print("emb:",embr)
print(embr.shape)#[2,4,512]

>>>>>
emb: tensor([[[-13.7408,  -4.3108,  35.0170,  ..., -44.5515, -16.5556,  -2.9577],
         [ 23.3314,  -9.3100,  26.4966,  ...,   7.4887,  28.7678,  16.3725],
         [-13.5485,  28.9699,  35.8271,  ..., -22.9973,  -0.4782,   9.3057],
         [-26.4563,   6.6810, -58.8932,  ...,  55.4734,  16.3932, -38.4998]],

        [[  0.6895,  43.8544, -10.3074,  ...,  39.5331,  20.9398,  13.0055],
         [ 10.1109,  16.5785,   7.7799,  ...,  19.8802, -18.9864,  -0.1557],
         [-31.9423,  -5.7930,   2.0319,  ...,   0.4931, -27.7125,  -3.4999],
         [-47.6672,   4.2582, -32.6036,  ...,  12.7451,   9.8043,  -1.6442]]],
       grad_fn=<MulBackward0>)
torch.Size([2, 4, 512])

2.2Positional Encoding

2.2.1为什么需要位置编码器(没有上下文关系这种)

在Transformer编码器中没有针对词汇位置信息的处理,故需要在embedding层后加入位置编码器,将词汇位置不同可能会产生不同语义的信息加入到嵌入张量中(embedding),用来弥补位置信息的缺失。

2.2.2位置信息编码的实现

class PositionalEncoding(nn.Module):
    def __init__(self,d_model,dropout,max_len=5000):
        """
        :param d_model:embedding的维度
        :param dropout: Dropout的置零比例
        :param max_len: 每个句子的最大长度
        """
        super(PositionalEncoding, self).__init__()
        #实例化Dropout层
        self.dropout = nn.Dropout(p=dropout)

        #初始一个位置编码矩阵,大小是max_len*d_model
        pe = torch.zeros(max_len,d_model)
        #初始化一个绝对位置矩阵,词汇的位置就是用它的索引表示max_len*1
        position = torch.arange(0,max_len).unsqueeze(1)#由[0,1,2...max_len][max_len] -> [[0],[1]...[max_len]][max_len,1]
        #目的是要把position的信息放到pe里面去

        #定义一个变换矩阵使得position的[max_len,1]*变换矩阵得到pe[max_len,d_model]->变换矩阵格式[1,d_model]
        #除以这个是为了加快收敛速度
        #div_term格式是[0,1,2...d_model/2],分成了两个部分,步长为2
        div_term = torch.exp(torch.arange(0,d_model,2)*-(math.log(10000.0)/d_model))
        # print(div_term.shape)
        # print(position * div_term)
        # a = position*div_term
        # print(a.shape)
        #将前面定义好的矩阵进行奇数偶数赋值
        pe[:,0::2] = torch.sin(position*div_term)
        pe[:,1::2] = torch.cos(position*div_term)

        #此时pe[max_len,d_model]
        #embedding三维(可以是[batch_size,vocab,d_model])#vocab就是max_len
        #将pe升起一个维度扩充成三维张量
        pe = pe.unsqueeze(0)


        #位置编码矩阵注册成模型的buffer,它不是模型中的参数,不会跟随优化器进行优化
        #注册成buffer后我们就可以在模型的保存和加载时,将这个位置编码器和模型参数加载进来
        self.register_buffer('pe',pe)


    def forward(self,x):
        """
        :param x:x代表文本序列的词嵌入
        pe编码过长将第二个维度也就是max_len的维度缩小成句子的长度
        """
        x = x + Variable(self.pe[:,:x.size(1)],requires_grad=False)
        return self.dropout(x)


x = torch.LongTensor([[1,2,3,4],[5,6,7,8]])#[2,4]
emb = Embeddings(d_model,vocab)#[2,4,512]
embr = emb(x)
pe = PositionalEncoding(d_model,dropout=0.2,max_len=50)
pe_result = pe(embr)
#
print(pe_result)
print(pe_result.shape)#[2,4,512]
上一篇下一篇

猜你喜欢

热点阅读