广西网站建设的公司哪家好,厨具网站模板,福田欧辉新能源公交车,长春火车站是哪个站教程是来自https://github.com/datawhalechina/learn-nlp-with-transformers/blob/main/docs/
图解Transformer
Attention为RNN带来了优点#xff0c;那么有没有一种神经网络结构直接基于Attention构造#xff0c;而不再依赖RNN、LSTM或者CNN的结构#xff0c;这就是Trans…教程是来自https://github.com/datawhalechina/learn-nlp-with-transformers/blob/main/docs/
图解Transformer
Attention为RNN带来了优点那么有没有一种神经网络结构直接基于Attention构造而不再依赖RNN、LSTM或者CNN的结构这就是Transformer。 Transformer直接基于Self-Attention结构取代了之前NLP任务中常用的RNN神经网络结构Transformer一个巨大的优点是模型在处理序列输入时可以对整个序列输入进行并行计算不需要按照时间步循环递归处理序列Transformer模型结构与seq2seq类似分为编码器和解码器。
Transformer的宏观结构
Transformer最开始提出来解决机器翻译任务因此可以先将Transformer这种特殊的seq2seq模型看做一个黑盒输入是法语文本序列输出是英语文本序列。 将上图的中间部分拆开成seq2seq标准结构如图左右分别是编码部分Encoders和解码部分Decoders。 然后再将上图中的编码器和解码器的细节绘出得到下图可以看到编码部分Encoders是由多层编码器Encoder组成Transformer论文中使用6层编码器也可以自行根据实验效果修改层数同理解码部分由多层的解码器组成论文里也是6层。每层编码器网络结构是一样的每层解码器网络结构也是一样的。不同层编码器和解码器网络结构不共享参数。 下面看一下单层encoder单层encoder主要由以下两部分组成
Self-Attention LayerFeed Forward Neural Network 编码器的输入文本序列 w 1 , w 2 , . . . , w n w_1,w_2,...,w_n w1,w2,...,wn最开始经过embedding转换得到每个单词的向量表示 x 1 , x 2 , . . . , x n x_1,x_2,...,x_n x1,x2,...,xn其中 x i ∈ R d x_i \in \mathbb{R}^{d} xi∈Rd是维度为 d d d的向量然后所有向量经过一个Self-Attention神经网络层进行变换和信息交互得到 h 1 , h 2 , . . . , h n h_1,h_2,...,h_n h1,h2,...,hn其中 h i ∈ R d h_i \in \mathbb{R}^{d} hi∈Rd是维度为 d d d的向量。self-attention层处理一个词向量的时候不仅会使用这个词本身的信息也会使用句子中其他词的信息。Self-Attention层的输出会经过前馈神经网络得到新的 x 1 , x 2 , . . . , x n x_1,x_2,...,x_n x1,x2,...,xn依旧是 n n n个维度为 d d d的向量。这些向量将被送入下一层encoder继续相同的操作。 与编码器对应解码器在编码器的self-attention和FFNN中间插入了一个Encoder-Decoder Attention层这个层帮助解码器聚焦于输入序列最相关的部分类似于seq2seq模型中的Attention层。 总结Transformer由编码部分和解码部分组成而编码部分和解码部分又由多个网络结构相同的编码层和解码层组成。每个编码层由self-attention和FFNN组成每个解码层由self-attention、FFNN和encoder-decoder attention组成。
Transformer结构细节
输入处理
词向量
和常见的NLP任务一样首先会使用词嵌入算法embedding algorithm将输入文本序列的每一个词转换为一个词向量。实际应用中的向量一般是256或者512维为了简化下面都以4维向量来示意。 如下图假设输入文本的序列包含了3个词那么每个词可以通过词嵌入算法得到一个4维向量于是整个输入被转化为一个向量序列。在实际应用中我们通常会同时给模型输入多个句子如果每个句子的长度不一样我们会选择一个合适的长度作为输入文本序列的最大长度如果一个句子达不到这个长度那么就填充一个特殊的”padding“词如果句子超过这个长度则做截断。最大序列长度是一个超参数通常希望越大越好但是更长的序列往往会占用更大的训练显存/内存因此需要在模型训练时候视情况而定。 输入序列每个单词被转换成词向量表示还将加上位置向量来得到该词的最终向量表示。
位置向量
如下图Transformer模型对每个输入的词向量都加上了一个位置向量。这些向量有助于确定每个单词的位置特征或者句子中不同单词之间的距离特征。词向量加上位置向量背后的直觉是将这些表示位置的向量添加到词向量中得到的新向量可以为模型提供更多有意义的信息比如词的位置词之间的距离等。 假设词向量和位置向量的维度是4下图为一种可能的位置向量词向量 带有位置编码信息的向量到底遵循什么模式原始论文中给出的设计表达式为 P E ( p o s , 2 i ) s i n ( p o s / 1000 0 2 i / d model ) P E ( p o s , 2 i 1 ) c o s ( p o s / 1000 0 2 i / d model ) PE_{(pos,2i)} sin(pos / 10000^{2i/d_{\text{model}}}) \ PE_{(pos,2i1)} cos(pos / 10000^{2i/d_{\text{model}}}) PE(pos,2i)sin(pos/100002i/dmodel) PE(pos,2i1)cos(pos/100002i/dmodel)上式中的 p o s pos pos代表词的位置 d m o d e l d_{model} dmodel代表位置向量的维度 i ∈ [ 0 , d m o d e l ] i \in [0, d_{model}] i∈[0,dmodel]代表 d m o d e l d_{model} dmodel维位置向量的第 i i i维。根据上述公式我们可以得到第 p o s pos pos位置的 d m o d e l d_{model} dmodel维位置向量。下图是一种位置向量在第4、5、6、7维度、不同位置的数值大小横坐标表示位置下标pos的值纵坐标表示数值大小如 P E ( p o s , 4 ) PE_{(pos, 4)} PE(pos,4)。 上述公式不是唯一生成位置编码向量的方法。但这种方法的优点是可以扩展到未知的序列长度。例如当我们的模型需要翻译一个句子而这个句子的长度大于训练集中所有句子的长度这时这种位置编码的方法也可以生成一样长的位置编码向量。
编码器encoder
编码部分的输入文本序列经过输入处理之后得到了一个向量序列这个向量序列将被送入第一层编码器第一层编码器输出的同样是一个向量序列再接着送入下一层编码器第一层编码器的输入是融合位置向量的词向量更上层编码器的输入则是上一层编码器的输出。 下图展示了向量序列在单层encoder中的流动融合位置信息的词向量进入self-attention层self-attention输出每个位置的向量再输入FFNN得到每个位置的新向量。
Self-Attention层
Self-Attention概览
假设我们想要翻译的句子是 The animal didnt cross the street because it was too tired 这个句子中的it是一个指代词那么it指的是什么它是指animal还是street这个问题对人来说很简单但是对模型来说并不是很容易。但是如果模型引入了Self Attention机制之后便能够让模型把it和animal关联起来了。同样的当模型处理句子中其他词时Self Attention机制也可以使得模型不仅仅关注当前位置的词还会关注句子中其他位置的相关的词进而可以更好地理解当前位置的词。 与RNN对比RNN在处理序列中的一个词时会考虑句子前面的词传过来的hidden state而hidden state就包含了前面的词的信息而Self Attention机制指的是当前词会直接关注到自己句子中前后相关的所有词语如下图it的例子 上图所示的it是一个真实的例子是当Transformer在第5层编码器编码it时的状态可视化之后显示it有一部分注意力集中在了The animal上并且把这两个词的信息融合到了it中。
Self-Attention细节
先用一个简单的例子来理解一下什么是self-attention自注意力机制。假设一句话包含两个单词Thinking Machines。自注意力的一种理解是Thinking-ThinkingThinking-MachinesMachines-ThinkingMachines-Machines共 2 2 2^2 22种两两attention。那么具体如何计算假设Thinking、Machines这两个单词经过词向量算法得到向量是 X 1 , X 2 X_1, X_2 X1,X2 1. q 1 X 1 W Q , q 2 X 2 W Q ; k 1 X 1 W K , k 2 X 2 W K ; v 1 X 1 W V , v 2 X 2 W V , W Q , W K , W V ∈ R d x × d k q_1 X_1 W^Q, q_2 X_2 W^Q; k_1 X_1 W^K, k_2 X_2 W^K;v_1 X_1 W^V, v_2 X_2 W^V, W^Q, W^K, W^V \in \mathbb{R}^{d_x \times d_k} q1X1WQ,q2X2WQ;k1X1WK,k2X2WK;v1X1WV,v2X2WV,WQ,WK,WV∈Rdx×dk 2-3. s c o r e 11 q 1 ⋅ q 1 d k , s c o r e 12 q 1 ⋅ q 2 d k ; s c o r e 21 q 2 ⋅ q 1 d k , s c o r e 22 q 2 ⋅ q 2 d k ; score_{11} \frac{q_1 \cdot q_1}{\sqrt{d_k}} , score_{12} \frac{q_1 \cdot q_2}{\sqrt{d_k}} ; score_{21} \frac{q_2 \cdot q_1}{\sqrt{d_k}}, score_{22} \frac{q_2 \cdot q_2}{\sqrt{d_k}}; score11dk q1⋅q1,score12dk q1⋅q2;score21dk q2⋅q1,score22dk q2⋅q2; 4. s c o r e 11 e s c o r e 11 e s c o r e 11 e s c o r e 12 , s c o r e 12 e s c o r e 12 e s c o r e 11 e s c o r e 12 ; s c o r e 21 e s c o r e 21 e s c o r e 21 e s c o r e 22 , s c o r e 22 e s c o r e 22 e s c o r e 21 e s c o r e 22 score_{11} \frac{e^{score_{11}}}{e^{score_{11}} e^{score_{12}}},score_{12} \frac{e^{score_{12}}}{e^{score_{11}} e^{score_{12}}}; score_{21} \frac{e^{score_{21}}}{e^{score_{21}} e^{score_{22}}},score_{22} \frac{e^{score_{22}}}{e^{score_{21}} e^{score_{22}}} score11escore11escore12escore11,score12escore11escore12escore12;score21escore21escore22escore21,score22escore21escore22escore22 5-6. z 1 v 1 × s c o r e 11 v 2 × s c o r e 12 ; z 2 v 1 × s c o r e 21 v 2 × s c o r e 22 z_1 v_1 \times score_{11} v_2 \times score_{12}; z_2 v_1 \times score_{21} v_2 \times score_{22} z1v1×score11v2×score12;z2v1×score21v2×score22 下面将上述self-attention计算的6个步骤进行可视化。
第一步对输入编码器的词向量进行线性变换得到Query向量 q 1 , q 2 q_1,q_2 q1,q2Key向量 k 1 , k 2 k_1,k_2 k1,k2Value向量 v 1 , v 2 v_1,v_2 v1,v2。这3个向量是词向量分别和3个参数矩阵相乘得到而这个矩阵也是模型要学习的参数。 其实就是3个向量给它们加上一个名称可以让我们更好地理解Self-Attention的计算过程和逻辑。Attention计算的逻辑常常可以描述为query和key计算attention得分然后根据attention得分对value进行加权求和。第2步计算Attention Score注意力分数。假设我们现在计算第一个词Thinking的Attention Score需要根据Thinking对应的词向量对句子中的其他词向量都计算一个分数。这些分数决定了我们在编码Thinking这个词时需要对句子中其他位置的词向量的权重。 Attention score是根据Thinking对应的Query向量和其他位置的每个词的Key向量进行点积得到的。Thinking的第一个Attention Score就是 q 1 q_1 q1和 k 1 k_1 k1的内积第二个分数就是 q 1 q_1 q1和 k 2 k_2 k2的点积。这个计算过程如下图所示图中的具体得分数据是为了表达方便自定义的。 第3步把每个分数除以 d k \sqrt{d_k} dk d k d_{k} dk是Key向量的维度也可以除以其他数除以一个数是为了在反向传播时求梯度时更加稳定。第4步接着把这些分数经过一个Softmax函数Softmax可以将分数归一化这样使得分数都是正数并且加起来等于1如下图所示。这些分数决定了Thinking词向量对其他所有位置的词向量分别有多少注意力下图的Softmax的数值算错了但是意思到了就行。 第5步得到每个词向量的分数后将分数分别与对应的Value向量相乘这种做法背后的直觉理解就是对于分数高的位置相乘后的值就越大我们把更多的注意力放到了它们身上对于分数低的位置相乘后的值就越小这些位置的词可能是相关性不大的。第6步把第5步得到的Value向量相加就得到了Self Attention在当前位置这里的例子是第1个位置对应的输出。
最后在下图展示了对第一个位置词向量计算Self Attention的全过程。最终得到的当前位置词向量会继续输入到前馈神经网络。注意上面的6个步骤每次只能计算一个位置的输出向量在实际的代码实现中Self Attention的计算过程是使用矩阵快速计算的一次就得到所有位置的输出向量。 上图是Thinking也就是第一个位置经过self-attention之后的向量表示。
Self-Attention矩阵计算
将self-attention计算6个步骤中的向量放一起比如 X [ x 1 ; x 2 ] X[x_1;x_2] X[x1;x2]便可以进行矩阵计算。下面依旧按步骤展示self-attention的矩阵计算方法。 X [ x 1 ; x 2 ] Q X W Q , K X W K , V X W V Z s o f t m a x ( Q K T d k ) V X[x_1;x_2] \\ QXW^Q,KXW^K,VXW^V \\ Zsoftmax(\frac{QK^T}{\sqrt{d_k}})V X[x1;x2]QXWQ,KXWK,VXWVZsoftmax(dk QKT)V
第一步计算QueryKeyValue矩阵。首先我们把所有词向量放到一个矩阵X中然后分别和3个权重矩阵 W Q , W K , W V W^Q,W^K,W^V WQ,WK,WV相乘得到QKV矩阵。矩阵X中的每一行表示句子中的每一个词的词向量。QKV矩阵中的每一行表示Query向量Key向量Value向量向量维度是 d k d_k dk。 第2步由于我们使用了矩阵来计算我们可以把上面的第2步到第6步压缩为一步直接得到Self Attention的输出。 Q Q Q是 L L L行每行是 d k d_k dk维的也就是 L ∗ d k L*d_k L∗dk的矩阵 Q K T QK^T QKT的结果是 L ∗ L L*L L∗L的矩阵其中的每个元素 Q K T i j q i ⋅ k j {QK^T}_{ij}q_i\cdot k_j QKTijqi⋅kj然后和 V V V相乘就是 L ∗ d k L*d_k L∗dk的矩阵每一行就是self Attention的输出以图为例第 1 1 1行的结果是 s o f t m a x ( q 1 k 1 d k ) v 1 s o f t m a x ( q 1 k 2 d k ) v 2 z 1 softmax(\frac{q_1k_1}{\sqrt{d_k}})v_1softmax(\frac{q_1k_2}{\sqrt{d_k}})v_2z_1 softmax(dk q1k1)v1softmax(dk q1k2)v2z1。
多头注意力机制
Transformer的论文通过增加多头注意力机制一组注意力称为一个attention head进一步完善了Self-Attention。这种机制从如下两个方面增强了attention层的能力
它扩展了模型关注不同位置的能力。在上面的例子中第一个位置的输出 z 1 z_1 z1仅仅是单个向量所以可能仅由第1个位置的信息主导了。而当我们翻译句子The animal didn’t cross the street because it was too tired时我们不仅希望模型关注到it本身还希望模型关注到The和Animal甚至关注到tired。这时多头注意力机制会有帮助。多头注意力机制赋予attention层多个”子表示空间“。下面我们会看到多头注意力机制会有多组 W Q , W K , W V W^Q,W^K,W^V WQ,WK,WV的权重矩阵在Transformer的论文中使用了8组注意力因此可以将 X X X变换到更多种子空间进行表示。接下来我们也使用8组注意力头attention heads。每一组注意力的权重矩阵都是随机初始化的但经过训练之后每一组注意力权重 W Q , W K , W V W^Q,W^K,W^V WQ,WK,WV可以把输入的向量映射到一个对应的”子表示空间“。 在多头注意力机制中我们为每组注意力设定单独的 W Q , W K , W V W^Q,W^K,W^V WQ,WK,WV参数矩阵。将输入X和每组注意力的 W Q , W K , W V W^Q,W^K,W^V WQ,WK,WV相乘得到8组QKV矩阵。 接着我们把每组QKV计算得到每组的Z矩阵就得到8个Z矩阵。 由于前馈神经网络层接收的是1个矩阵其中每行的向量表示一个词而不是8个矩阵所以我们直接把8个子矩阵拼接起来得到一个大的矩阵然后和另一个权重矩阵 W O W^O WO相乘做一次变换映射到前馈神经网络层所需要的维度。 总结一下就是
把8个矩阵 [ Z 0 , Z 1 , . . . , Z 7 ] [Z_0,Z_1,...,Z_7] [Z0,Z1,...,Z7]拼接起来把拼接后的矩阵和 W O W^O WO权重矩阵相乘得到最终的矩阵 Z Z Z这个矩阵包含了所有attention heads注意力头的信息。这个矩阵会输入到FFNN层。
下图是多头注意力所有内容的集合。 学会了多头注意力机制再回头看之前提到的it的例子不同的attention heads注意力头对应的it attention了哪些内容。下图中的绿色和橙色线条分别表示2组不同的attention heads 当我们编码it时其中一个attention head橙色注意力头最关注的是the animal另外一个绿色attention head关注的是tired。因此在某种意义上it在模型中的表示融合了animal和tire的部分表达。
Attention代码实例
下面的代码实现张量的第一维时batch size第2维是句子长度。代码中进行了详细的注释和说明。
import torch
from torch import nnclass MultiheadAttention(nn.Module):# n_heads: 多头注意力的数量# hid_dim: 每个词输出的向量维度def __init__(self, hid_dim, n_heads, dropout):super(MultiheadAttention, self).__init__()self.hid_dim hid_dimself.n_heads n_heads# 强制 hid_dim 必须整除 n_headsassert hid_dim % n_heads 0# 定义 W_q 矩阵self.w_q nn.Linear(hid_dim, hid_dim)# 定义 W_k 矩阵self.w_k nn.Linear(hid_dim, hid_dim)# 定义 W_v 矩阵self.w_v nn.Linear(hid_dim, hid_dim)self.fc nn.Linear(hid_dim, hid_dim)self.do nn.Dropout(dropout)# 缩放self.scale torch.sqrt(torch.FloatTensor([hid_dim // n_heads]))def forward(self, query, key, value, maskNone):# 注意Q, K, V的在句子长度这一个维度可以一样可以不一样。# K: [64,10,300], 假设batch_size为64有10个词每个词的Query向量是300# V: [64,10,300], 假设batch_size为64有10个词每个词的Query向量是300# Q: [64,12,300], 假设batch_size为64有12个词每个词的Query向量是300bsz query.shape[0]Q self.w_q(query)K self.w_k(key)V self.w_v(value)# 这里把 K Q V 矩阵拆分为多组注意力# 最后一维就是用 self.hid_dim // self.n_heads 来得到的表示每组注意力的向量长度# 每个 head 的向量长度是50# 64 表示 batch size6 表示有 6 组注意力10 表示有 10 词50 表示每组注意力的词的向量长度# K: [64,10,300] 拆分多组注意力 - [64,10,6,50] 转置得到 [64,6,10,50]# V: [64,10,300] 拆分多组注意力 - [64,10,6,50] 转置得到 [64,6,10,50]# Q: [64,12,300] 拆分多组注意力 - [64,12,6,50] 转置得到 [64,6,12,50]# 转置是为了把注意力的数量 6 放到前面 把 10 和 50 放到后面方便下面计算Q Q.view(bsz, -1, self.n_heads, self.hid_dim // self.n_heads).permute(0, 2, 1, 3)K K.view(bsz, -1, self.n_heads, self.hid_dim // self.n_heads).permute(0, 2, 1, 3)V V.view(bsz, -1, self.n_heads, self.hid_dim // self.n_heads).permute(0, 2, 1, 3)# 第 1 步: Q 乘以 K的转置除以scale# [64,6,12,50] * [64,6,50,10] [64,6,12,10]# attention: [64,6,12,10]attention torch.matmul(Q, K.permute(0, 1, 3, 2)) / self.scale# 如果 mask 不为空那么就把 mask 为 0 的位置的 attention 分数设置为 -1e10# 这里用“0”来指示哪些位置的词向量不能被attention到比如padding位置当然也可以用“1”或者其他数字来指示主要设计下面2行代码的改动。if mask is not None:attention attention.masked_fill(mask 0, -1e10)# 第 2 步: 计算上一步结果的 softmax再经过 dropout得到 attention# 注意这里是对最后一维做 softmax也就是在输入序列的维度做 softmax# attention [64,6,12,10]attention self.do(torch.softmax(attention, dim-1))# 第 3 步: attention结果与V相乘得到多头注意力的结果# [64,6,12,10] * [64,6,10,50] [64,6,12,50]# x: [64,6,12,50]x torch.matmul(attention, V)# 因为 query 有 12 个词所以把 12 放到前面把 50 和 6 放到后面方便下面拼接多组的结果# x: [64,6,12,50] 转置- [64,12,6,50]x x.permute(0, 2, 1, 3).contiguous() # contiguous确保张量在内存中是连续的# 这里的矩阵转换就是把多组注意力的结果拼接起来# 最终结果就是 [64,12,300]# x: [64,12,6,50]-[64,12,300]x x.view(bsz, -1, self.n_heads * (self.hid_dim // self.n_heads))x self.fc(x)return x# batch_size 为 64有 12 个词每个词的 Query 向量是 300 维
query torch.rand(64, 12, 300)
# batch_size 为 64有 12 个词每个词的 Key 向量是 300 维
key torch.rand(64, 10, 300)
# batch_size 为 64有 10 个词每个词的 Value 向量是 300 维
value torch.rand(64, 10, 300)
attention MultiheadAttention(hid_dim300, n_heads6, dropout0.1)
output attention(query, key, value)
# output: torch.Size([64, 12, 300])
print(output.shape)残差连接
到目前为止我们计算得到了self-attention的输出向量。而单层encoder里后续还有两个重要的操作残差链接、标准化。 编码器的每个子层Self Attention 层和 FFNN都有一个残差链接和层标准化Layer normalization如下图所示。 将Self-Attention层的层标准化和涉及的向量计算细节都进行可视化如下图所示。 编码器和解码器的子层里面都有层标准化。假设一个Transformer是由2层编码器和2层解码器组成的将全部内容细节展示起来如下图所示。
解码器
现在我们已经介绍了编码器中的大部分概念我们也知道了编码器的原理。现在让我们看下编码器和解码器是如何协同工作的。 编码器一般有多层第一个编码器的输入是一个序列文本最后一个编码器输出是一组序列向量这组序列向量会作为解码器的 K , V K,V K,V输入其中 K V 解码器输出的序列向量表示 KV解码器输出的序列向量表示 KV解码器输出的序列向量表示。这些注意力向量将会输入到每个解码器的Encoder-Decoder Attention层这有助于解码器把注意力集中到输入序列的合适位置如下图所示。 解码Decoding阶段的每一个时间步都输出一个翻译后的单词这里的例子是英语翻译解码器当前时间步的输出又重新作为输入 Q Q Q和编码器的输出 K , V K,V K,V共同作为下一个时间步解码器的输入。然后重复这个过程直到输出一个结束符这种是自回归式解码所以看起来有顺序不能并行。如下图所示 解码器中的Self Attention层和编码器中的Self Attention层的区别
在解码器里Self Attention层只允许关注到输出序列中早于当前位置之前的单词。具体做法是在Self Attention分数经过Softmax层之前屏蔽当前位置之后的那些位置将 attention score设置为-inf。解码器Attention层是使用前一层的输出来构造Query矩阵而Key矩阵和Value矩阵来自于编码器最终的输出。
线性层和softmax
Decoder最终的输出是一个向量其中每个元素是浮点数。我们通过线性层和softmax将这个向量转换为单词。
线性层就是一个普通的全连接神经网络可以把解码器输出的向量映射到一个更大的向量这个向量称为logits向量假设我们的模型有10000个英语单词模型的输出词汇表此logits向量便会有10000个数字每个数表示一个单词的分数。
然后Softmax层会把这些分数转换为概率把所有的分数转换为正数并且加起来等于1.然后选择最高概率的那个数字对应的词就是这个时间步的输出单词。
损失函数
Transformer训练的时候需要将解码器的输出和label一同送入损失函数以获得loss最终模型根据loss进行方向传播在下面用一个例子来说明训练过程的loss计算把”merci“翻译为”thanks“。
我们希望模型解码器最终输出的概率分布会指向单词”thanks“。但是一开始模型还没训练好它输出的概率分布可能和我们希望的概率分布相差甚远。如下图正确的概率分布应该是”thanks“单词的概率最大。但是由于模型的参数都是随机初始化的所以一开始模型预测所有词的概率几乎都是随机的。 只要Transformer解码器预测了一组概率我们就可以把这组概率和正确的输出概率做对比然后使用反向传播来调整模型的权重使得输出的概率分布更加接近整数输出。
那我们要怎样比较两个概率分布我们可以简单的用两组概率向量的空间距离作为loss向量相减然后求平方和再开方当然也可以用交叉熵cross-entropy和KL散度Kullback-Leibler divergence。
由于上面仅有一个单词的例子太简单了我们可以再看一个复杂一点的例子。句子输入是”je suis étudiant“输出是”i am a student“。这意味着我们的transformer模型解码器要多次输出概率分布向量
每次输出的概率分布都是一个向量长度是vocab_size前面约定最大的vocab size也就是向量长度是6但实际中的vocab size更可能是30000或者50000第1次输出的概率分布中最高概率对应的单词是”i“第2次输出的概率分布中最高概率对应的单词是”am“以此类推直到第5个概率分布中最高概率对应的单词是”“表示没有下一个单词了。
于是我们的目标概率分布如下图 我们用例子中的句子训练模型希望产生图中所示的概率分布我们的模型在一个足够大的数据集上经过足够长时间的训练后希望输出的概率分布如下图所示 我们希望模型经过训练之后可以输出的概率分布也就对应了正确的翻译。当然如果你要翻译的句子是训练集中的一部分那输出的结果并不能说明什么。我们希望模型在没见过的句子上也能够准确翻译。
额外提一下greedy decoding和beam search的概念
Greedy decoding由于模型每个时间步只产生一个输出我们这样看待模型是从概率分布中选择概率最大的词并且丢弃其他词。这种方法叫做贪婪解码greedy decoding。Beam search每个时间步保留k个最高概率的输出词然后在下一个时间步根据上一个时间步保留的k个词来确定当前应该保留哪k个词。假设k2第一个位置概率最高的两个输出词是”I“和”a“这两个词都保留然后根据第一个词计算第2个位置的词的概率分布再取出第2个位置上2个概率最高的词。对于第3个位置和第4个位置我们也重复这个过程。这种方法称为集束搜索beam search。