李宏毅《深度学习》- Transformer

一、Seq2seq

1. 简介

Transformer 就是一个 Seq2seq (Sequence-to-sequence) 的模型

输入一个序列,输出长度由模型决定。例如语音识别,输入的语音信号就是一串向量,输出就是语音信号对应的文字。但是语音信号的长度和输出的文字个数并无直接联系,因此需要机器自行决定:
在这里插入图片描述
对于世界上没有文字的语言,我们可以对其直接做语音翻译。另外,Seq2seq 还可以用来训练聊天机器人:输入输出都是文字(向量序列),训练集示例如下图:
聊天机器人训练集
各式各样的NLP问题,往往都可以看作QA问题,例如问答系统(QA),让机器读一篇文章,读入一个问题,就输出一个答案。而该问题就可以用 Seq2seq 的模型来解决:

question, context
Seq2seq
answer

2. 应用示例

(1)语法分析 (Syntactic Parsing)

输入一段文字,机器要做的就是产生一个语法的解析树,告诉我们哪些文字或单词组合起来是名词,哪些组合是形容词等,具体参考 《Grammar as a Foreign Language》
在这里插入图片描述

(2)多标签分类 (Multi-label Classification)

定义:同一个事物可以属于多个类别。
举例:一篇文章可以对应多种领域。

多标签分类的问题不能当作多类别分类来解,例如给机器输入一篇文章分析其类别,但现在需要输出分数最高的前三名,然而每一篇文章对应的分类数目不一样,有些文章对应的分类数目小于三个,显然这不能得到预期结果。
在这里插入图片描述
利用Seq2seq,输入一篇文章,机器自行决定输出几个类别。

3. 原理

在这里插入图片描述
一般Seq2seq会分成两部分:Encoder、Decoder。
在这里插入图片描述

3.1 Encoder

Encoder 要做的事情就是给一排向量,输出一排向量,这可利用多种模型实现,如 CNN, RNN 等。Transformer 中的 Encoder 就是用的 Self-attention。

Encoder 原理

注:Transformer 的Encoder架构不一定要这么设计,此处为原始论文中的架构设计。
在这里插入图片描述

3.2 Decoder

(1)自回归 Autoregressive (AT)

在这里插入图片描述
当有一个文字输出错误,接下来Decoder就会用这个错误的结果【一步错步步错】:
在这里插入图片描述
下图中,Multi-head Attention其实就是多个Self-Attention结构的结合1。Decoder相比Encoder,在Multi-Head Attention上还加了一个 “Masked”(Masked Self-attention):
Decoder
目前的Decoder的运行中,机器并不知道什么时候停下来,一直重复操作,如同“词语接龙”:机、器、学、习、惯、… 因此需要增加一个终止符号END来结束执行。

在这里插入图片描述

在这里插入图片描述

(2)非自回归 Non-autoregressive (NAT)

在这里插入图片描述
NAT 只需要一次性输入就可以产生整个句子。

  • Q:如何确定NAT decoder的输出长度?
    A1:另外学习出一个分类器,它输入Encoder的input,输出Encoder应该输出的长度
    A2:输出一个很长的序列,忽略END标识后的token(如上图)

  • 优点:并行处理,速度快;输出长度可控。
    缺点:NAT的表现往往逊色于AT:多模态问题 Multi-modality.

二、Transformer

Transformer位于Decoder内,是连接Encoder和Decoder的桥梁,它有三个输入,其中两个来自于Encoder,一个来自Decoder。
Transformer

跨越注意力 Cross Attention2
Cross Attention 机制不是 Transformer,其先于 Transformer 出现,后来出现了 Self-attention 才有了 Transformer。

在这里插入图片描述

1. 训练

在输入的时候会给Decoder正确答案,这种方式叫做"Teacher Forcing";使用交叉熵对模型进行评估。
在这里插入图片描述

训练 Seq2seq 的要点

1. 复制机制 Copy Mechanism

对很多任务而言,也许Decoder无需产生输出,也许是从输出里面“复制”一些东西出来。

例如在聊天机器人中,对于用户输入的某人名字,机器无需进行处理,直接输出即可;或者复述一段不能识别的文字;亦或是提取大量文章中的摘要3

  • User: 你好,我是 Tom
    Machine: Tom 你好,很高兴认识你。
  • User: 李华写不了作业了!
    Machine: 你所谓的“写不了作业”是什么意思?
机器翻译聊天机器人摘要提取
在这里插入图片描述在这里插入图片描述在这里插入图片描述

2. 语音识别应用:Guided Attention

在一些任务中,输入和输出需要有固定的方式。例如:语音识别,语音合成等。
语音合成

3. 束搜索 Beam Search

在这里插入图片描述
在这里插入图片描述

Exposure Bias

测试的时候,Decoder会产生错误的输出,但是训练的时候是完全正确的,这种不一致的现象叫做 Exposure Bias4。可以在训练的时候类似“加入扰动”的方式来解决——scheduled sampling(定时采样),这种方式会影响Transformer的并行化。

三、总结

在这里插入图片描述

参考 18、深入剖析PyTorch中的Transformer API源码 - deep_thoughts

Transformer 分为两部分:EncoderDecoder

1. Encoder

  • 以字符作为输入,以状态作为输出
  • 由输入字符得到Embedding (d_model=512),加入了位置编码作为输入
  • Encoder 由 N(num_encoder_layers=6)个 Block 构成
  • 每个 Block 分为两部分:多头注意力(Multi-Head Attention, nhead=8)和 前馈神经网络(FFN, dim_feedforward=2048)
  • 在self-attention后做层归一化,FFN后也会经过层归一化。dropout使得网络具备集成学习的特点,即每次 以一定概率丢弃神经元信息,相当于训练多个模型,使得泛化能力更强。
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))

2. Decoder

  • 以上一时刻的字符作为输入,把Encoder的状态作为输入的一部分,返回字符的预测概率
  • Decoder 也由 N (num_decoder_layers=6) 个 Block 构成
  • 每个 Block 分为三部分:Masked Multi-Head Attention、Multi-Head Attention和FFN

(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


  1. Attention机制详解(二)——Self-Attention与Transformer ↩︎

  2. Listen, attend and spell: A neural network for large vocabulary conversational speech recognition ↩︎

  3. Get To The Point: Summarization with Pointer-Generator Networks ↩︎

  4. Seq2Seq中Exposure Bias现象的浅析与对策 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Beta Lemon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值