以词汇翻译为例,将英文翻译成德文
首先会有一个词表,正常情况下,词表的长度就是所有的英文单词(德文单词)的长度,对应着每一个单词,如
src_vocab = {'P': 0, 'ich': 1, 'mochte': 2, 'ein': 3, 'bier': 4}
图片来源于:Pytorch中 nn.Transformer的使用详解与Transformer的黑盒讲解
首先看图,我们的inputs就是普通的向量,形状为[batch_size, src_len],但是推理模式和训练模式下的解码器输入是不同的,因为最后的output表示的是对下一个词的预测
(以下都省略了batch_size)
接下来,inputs进来后,要先经过一个embedding,这个的作用就是把单词映射到高纬度的矩阵,也就是把一个简单的单词,变成了一个d_model长度的有很多复杂信息的词。
所以经过embedding后,形状变成了[batch_size, src_len, d_model],然后再加上了位置编码(形状仍不变),就可以传入Encoding模块了。
传入之后,需要通过wq,wk,wv三个矩阵分别把输入矩阵转成q,k,v矩阵,用来做自注意力
如图:
要注意q和k必须是一样的,要不然后面没法做矩阵相乘了,所以大部分其实qkv的形状都是相同的。矩阵转换可以用矩阵相乘,也可以使用线性变换,因为形状都是从
[batch_size, src_len, d_model] 转成 [batch_size, src_len, d_k] (统一以d_k表示)
以上图为例子,x的形状为[2,4],则q*k^T=[2,2],表示每个单词和另外的单词的相关性,然后根据公式再softmax,再与v矩阵进行矩阵运算,得到一个[2,3]的矩阵,也就是z,当然这只是一个头,我们会算出多个头,也就是多个wq,wk,wv矩阵,然后把得到的这些个[2,3]的矩阵给concat拼接起来,假设得到[2,9],我们就再进行一个线性变换,转回原来的[src_len, d_model],也就是[2,4],这就是自注意力的输出了。
然后我们需要进入到Add & Norm层,也就是做个残差连接
然后考虑到此前一系列操作对复杂过程的拟合程度可能不足,所以通过增加全连接层,也就是前馈网络来增强模型的拟合能力
class PoswiseFeedForwardNet(nn.Module):
def __init__(self):
super(PoswiseFeedForwardNet, self).__init__()
self.fc = nn.Sequential(
nn.Linear(d_model, d_ff, bias=False),
nn.ReLU(),
nn.Linear(d_ff, d_model, bias=False))
def forward(self, inputs): # inputs: [batch_size, seq_len, d_model]
residual = inputs
output = self.fc(inputs)
return nn.LayerNorm(d_model).(output + residual) # [batch_size, seq_len, d_model]
所以常用的超参数就是
#===Transformer参数===#
d_model = 512 # Embedding Size 每一个字符转化成Embedding的大小
d_ff = 2048 # FeedForward dimension 前馈神经网络映射到多少维度
d_k = d_v = 64 # dimension of K(=Q), V
n_layers = 6 # number of Encoder of Decoder Layer encoder和decoder的个数,这个设置的是6个encoder和decoder堆叠在一起(encoder和decoder的个数必须保持一样吗)
n_heads = 8 # number of heads in Multi-Head Attention 多头注意力机制时,把头分为几个,这里说的是分为8个
Decoder和Encoder其实没差啥,就是训练的时候多了个mask,然后k和v都是Encoder 的输出,以预测的情况为例:
输入为[1,8],k和v都为[5,8],则q*k^T=[1,5],然后再与v进行矩阵运算得到[1,8],再多头,再线性,最后又得到[1,d_model],再前馈什么的,到最后一个线性变换输出size为词表的size,也就是预测最有可能是词表的哪个词,到此就结束了,Decoder的输出就是下一个词的概率。
Encoder在预测时也只做一次,后面的都一直用第一次的输出,也就是k和v,要不然计算量太大了。
参考: