赞
踩
Transformer 就是一个 Seq2seq (Sequence-to-sequence) 的模型
输入一个序列,输出长度由模型决定。例如语音识别,输入的语音信号就是一串向量,输出就是语音信号对应的文字。但是语音信号的长度和输出的文字个数并无直接联系,因此需要机器自行决定:
对于世界上没有文字的语言,我们可以对其直接做语音翻译。另外,Seq2seq 还可以用来训练聊天机器人:输入输出都是文字(向量序列),训练集示例如下图:
各式各样的NLP问题,往往都可以看作QA问题,例如问答系统(QA),让机器读一篇文章,读入一个问题,就输出一个答案。而该问题就可以用 Seq2seq 的模型来解决:
输入一段文字,机器要做的就是产生一个语法的解析树,告诉我们哪些文字或单词组合起来是名词,哪些组合是形容词等,具体参考 《Grammar as a Foreign Language》。
定义:同一个事物可以属于多个类别。
举例:一篇文章可以对应多种领域。
多标签分类的问题不能当作多类别分类来解,例如给机器输入一篇文章分析其类别,但现在需要输出分数最高的前三名,然而每一篇文章对应的分类数目不一样,有些文章对应的分类数目小于三个,显然这不能得到预期结果。
利用Seq2seq,输入一篇文章,机器自行决定输出几个类别。
一般Seq2seq会分成两部分:Encoder、Decoder。
Encoder 要做的事情就是给一排向量,输出一排向量,这可利用多种模型实现,如 CNN, RNN 等。Transformer 中的 Encoder 就是用的 Self-attention。
注:Transformer 的Encoder架构不一定要这么设计,此处为原始论文中的架构设计。
当有一个文字输出错误,接下来Decoder就会用这个错误的结果【一步错步步错】:
下图中,Multi-head Attention其实就是多个Self-Attention结构的结合1。Decoder相比Encoder,在Multi-Head Attention上还加了一个 “Masked”(Masked Self-attention):
目前的Decoder的运行中,机器并不知道什么时候停下来,一直重复操作,如同“词语接龙”:机、器、学、习、惯、… 因此需要增加一个终止符号END来结束执行。
NAT 只需要一次性输入就可以产生整个句子。
Q:如何确定NAT decoder的输出长度?
A1:另外学习出一个分类器,它输入Encoder的input,输出Encoder应该输出的长度
A2:输出一个很长的序列,忽略END标识后的token(如上图)
优点:并行处理,速度快;输出长度可控。
缺点:NAT的表现往往逊色于AT:多模态问题 Multi-modality.
Transformer位于Decoder内,是连接Encoder和Decoder的桥梁,它有三个输入,其中两个来自于Encoder,一个来自Decoder。
跨越注意力 Cross Attention2
Cross Attention 机制不是 Transformer,其先于 Transformer 出现,后来出现了 Self-attention 才有了 Transformer。
在输入的时候会给Decoder正确答案,这种方式叫做"Teacher Forcing";使用交叉熵对模型进行评估。
对很多任务而言,也许Decoder无需产生输出,也许是从输出里面“复制”一些东西出来。
例如在聊天机器人中,对于用户输入的某人名字,机器无需进行处理,直接输出即可;或者复述一段不能识别的文字;亦或是提取大量文章中的摘要3。
- User: 你好,我是 Tom。
Machine: Tom 你好,很高兴认识你。- User: 李华写不了作业了!
Machine: 你所谓的“写不了作业”是什么意思?
机器翻译 | 聊天机器人 | 摘要提取 |
---|---|---|
![]() | ![]() | ![]() |
在一些任务中,输入和输出需要有固定的方式。例如:语音识别,语音合成等。
测试的时候,Decoder会产生错误的输出,但是训练的时候是完全正确的,这种不一致的现象叫做 Exposure Bias4。可以在训练的时候类似“加入扰动”的方式来解决——scheduled sampling(定时采样),这种方式会影响Transformer的并行化。
Transformer 分为两部分:Encoder
和 Decoder
import math import torch import torch.nn as nn import torch.nn.functional as F # 这里没有实现Embedding 和 Positional Encoding class TransformerEncoder(nn.Module): def __init__(self, n_head, d_model, d_ffn, act=F.gelu): """ :param n_head 头的个数 :param d_model 模型维数 :param d_ffn feed-forward networks 输出维数 :param act 激活函数 """ super(TransformerEncoder, self).__init__() self.h = n_head self.d = d_model # Attention self.q = nn.Linear(d_model, d_model) self.k = nn.Linear(d_model, d_model) self.v = nn.Linear(d_model, d_model) self.o = nn.Linear(d_model, d_model) # LayerNorm => Add & Norm self.LN1 = nn.LayerNorm(d_model) self.LN2 = nn.LayerNorm(d_model) # FFN self.ffn1 = nn.Linear(d_model, d_ffn) self.ffn2 = nn.Linear(d_ffn, d_model) self.act = act self.dropout = nn.Dropout(0.2) self.softmax = nn.Softmax(dim=-1) def attn(self, x, mask): Q = self.q(x).view(-1, x.shape[0], x.shape[1], self.d // self.h) # [head_nums, batch_size, seq_len, dim] K = self.k(x).view(-1, x.shape[0], x.shape[1], self.d // self.h) # [head_nums, batch_size, seq_len, dim] V = self.v(x).view(-1, x.shape[0], x.shape[1], self.d // self.h) # [head_nums, batch_size, seq_len, dim] attention = self.softmax(torch.matmul(Q, K.permute(0, 1, 3, 2)) / math.sqrt(self.d) + mask) attention = torch.matmul(attention, V) # multi_attn = torch.cat([_ for _ in attention], dim=-1) multi_attn = attention.view(x.shape[0], x.shape[1], -1) # 合并h个头的结果 out = self.o(multi_attn) return out def ffn(self, x): x = self.dropout(self.act(self.ffn1(x))) x = self.dropout(self.ffn2(x)) return x def forward(self, x, mask): x = self.LN1(x + self.dropout(self.attn(x, mask))) x = self.LN2(x + self.dropout(self.ffn(x))) return x if __name__ == '__main__': model = TransformerEncoder(2, 4, 8) x = torch.randn(2, 3, 4) mask = torch.randn(1, 1, 3, 3) o = model(x, mask) model.eval() traced_model = torch.jit.trace(model, (x, mask)) x = torch.randn(2, 3, 4) mask = torch.randn(1, 1, 3, 3) assert torch.allclose(model(x, mask), traced_model(x, mask))
(1)1. Masked Multi-Head Attention以 输入字符的embedding 和 位置编码 作为输入,对自身序列做表征,Masked 表示预测的字符必须从该被预测的字符之前的字符中得到,即 P(this word | past words)
【因果关系】,而 不能把未来的字符作为信息传入来预测。此处的Q, K, V均来自Decoder。
MASK如下图的矩阵乘法所示,将mask的部分用负无穷大表示,经过softmax函数之后就变为0,也就达到了mask的目的
(2)交叉注意力 以Masked Multi-Head Attention的输出作为Query,以Encoder的输出作为Value, 计算出Encoder输出序列和Decoder输入序列之间的关联性以计算出权重,再与Encoder的状态进行加权求和来得到一个新的表征。 此处的Q来自Decoder,K和V来自Encoder。
(3)FFN网络,与 Encoder 的 FFN 一样
(4)最后通过一个线性层来映射到一个概率空间:分类每个单词的得分
对于全局位置和局部位置不敏感,输入后字符位置信息丢失。因此需要一个带有位置信息的向量加到每一个embedding input上去,使得保留input输入的字符位置信息。每一个block都有残差连接,因此位置信息可以充分传递到上层网络中,所以不会出现随着网络层数增大而抵消位置信息的情况。
P
E
(
p
o
s
,
2
i
)
=
sin
(
p
o
s
1000
0
2
i
/
d
m
o
d
e
l
)
PE_{(pos, 2i)}=\sin(\frac{pos}{10000^{2i/d_{model}}})
PE(pos,2i)=sin(100002i/dmodelpos)
P
E
(
p
o
s
,
2
i
+
1
)
=
cos
(
p
o
s
1000
0
2
i
/
d
m
o
d
e
l
)
PE_{(pos, 2i+1)}=\cos(\frac{pos}{10000^{2i/d_{model}}})
PE(pos,2i+1)=cos(100002i/dmodelpos)
此处 pos
是每个单词在句子中的绝对位置,i
是维数,偶数用
sin
\sin
sin 计算,奇数用
cos
\cos
cos 计算。
关于更多Transformer,参考 Datawhale - 图解transformer
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。