• Transformer模型 | 个人理解


    一、Transformer整体架构图

    在这里插入图片描述

    二、Encoder端的输入

    以机器翻译任务为例子,训练数据是法语句子“Je suis etudiant”和翻译成英文后的句子“I am a student”。
    Inputs是法语句子“ J e {Je } Je s u i s {suis} suis e t u d i a n t {etudiant} etudiant”,句子中的单词“Je”经过word embedding转换为嵌入向量 x 1 {x_1} x1,再加上positional embedding(位置编码向量)得到第一个Encoder block的输入向量 x 1 {x_1} x1.
    句子中的每个单词都是自身嵌入向量+位置编码向量得到最终的Encoder端输入embedding.
    在这里插入图片描述

    三、self-attention

    3.1整体来看:

    每个单词的embedding 向量 x i {x_i} xi 进入self-attention,得到向量 z i {z_i} zi z i {z_i} zi再传入前馈神经网络
    在这里插入图片描述

    3.2从宏观上看self-attention:

    在这里插入图片描述
    对于RNN来说,在生成it_单词的向量 z i {z_i} zi时,只能考虑到it_前面的那些单词。但是Transformer在Encoder端使用self-attention,可以实现在生成每个单词对应的向量 z i {z_i} zi的时候,考虑到当前句子中所有单词和它之间的关系。
    这样向量 z i {z_i} zi中就包含了上下文信息。
    以上面的图为例,it_单词对应的向量 z i {z_i} zi包含了句子中所有其它单词对它的影响程度(联系紧密程度),也就是在it_单词对应的embedding向量中嵌入了上下文信息。
    初始的文本输入通过**word embedding转为嵌入向量+positional embedding(位置编码)**输入到第一个Encoder block,此时单词对应的embedding向量x i并没有包含整个句子的上下文信息,通过self-attention生成的向量z i就包含了整个句子的上下文信息,即句子中哪些单词对它更“重要”。

    3.3从微观上看self-attention

    3.3.1一个单词如何进行self-attention计算:

    现在来看self-attention中每一步是怎么计算得到最后的输出向量 z i {z_i} zi
    在这里插入图片描述

    第一步:生成Q K V,辅助计算注意力机制
    有三个权重矩阵 W Q {W^Q} WQ W K {W^K} WK W V {W^V} WV ,它们的初始值通常是通过一种随机初始化的方法来得到的,随着训练的进行,模型通过反向传播算法根据损失函数来调整权重矩阵的值,从而逐步优化模型参数。
    如上图所示,对于第一个单词“Thinking”进行self-attention计算,查询向量q1是x1*权重矩阵 W Q {W^Q} WQ 得到的,键向量k1是 x 1 {x_1} x1 * 权重矩阵 W K {W^K} WK 得到的,值向量 v 1 {v_1} v1 x 1 {x_1} x1 * 权重矩阵 W V {W^V} WV 得到的。
    对于句子中的每个单词,都要进行self-attention计算,得到向量 z i {z_i} zi
    在这里插入图片描述
    对于单词“Thinking”,进行self-attention计算(即QKV操作):
    第二步:单词“Thinking”对应的embedding是 x 1 {x_1} x1,Query向量是 q 1 {q_1} q1,利用 q 1 {q_1} q1计算当前句子中所有单词的Score, q 1 ∗ k 1 {q_1*k_1} q1k1得到第一个单词的分数, q 1 ∗ k 2 {q_1*k_2} q1k2得到第二个单词的分数……,这个分数就表明在生成向量 z 1 {z_1} z1的时候要对整个句子中每个单词关注多少;
    第三步:把得到的分数除以维度的平方根(这步是为了梯度更加稳定,有利于训练);
    第四步:把上一步结果通过softmax函数转换为[0,1]区间内的概率分布值,目的是让差异变大,也就说当前单词“Thinking”和句子中哪些单词联系密切对应的softmax值就越大,联系越弱对应的softmax值就越小;
    第五步:句子中每个单词的Value向量乘以对应的softmax分数,就是“Thinking”对应的值向量 v 1 ∗ 0.88 {v_1*0.88} v10.88,“Machines”对应的值向量 v 2 ∗ 0.12 {v_2*0.12} v20.12;
    第六步:对加权后的值向量求和,即对于单词“Thinking”把整个句子中所有单词加权后的值向量求和得到 z 1 {z_1} z1 z 1 {z_1} z1就是“Thinking”的embedding嵌入 x 1 {x_1} x1在self-attention层该位置的输出。
    这样,单词“Thinking”现在对应的嵌入向量 z 1 {z_1} z1就包含了该单词在整个句子中的上下文信息,这个单词和句子中其他单词之间的关系紧密程度。

    用到的核心公式是:
    在这里插入图片描述
    这样,self-attention计算就完成了,得到的 z i {z_i} zi向量可以传递给前馈神经网络,
    上面的过程是一个单词如何进行self-attention计算。

    3.3.2对整个句子中的单词同时进行self-attention计算过程:

    在实际模型中,为了加快计算效率,self-attention机制是通过矩阵的计算实现的:
    第一步,对整个句子生成Q K V向量
    在这里插入图片描述
    假设句子中是2个单词, X {X} X矩阵的维度就是2*(一个embedding的维度),前面的一个14的小方格代表一个单词,这个图中的24小方格就是代表两个单词。
    Q K V列上的维度是64,这是Transformer中设定的维度。
    Q是当前句子的查询向量,K是当前句子的键向量,V是当前句子的值向量矩阵;
    最后得到self-attention层计算得到的结果矩阵 Z {Z} Z

    前面讲的一个单词经过self-attention得到向量z,可以包含该单词在整个句子中的上下文信息,包含该单词和其他单词之间联系的紧密程度。
    那么这里利用矩阵经过self-attention得到矩阵 Z {Z} Z,矩阵Z中每个向量就对应了每个单词经过self-attention得到的输出 z i {z_i} zi向量。

    3.3.3多头注意力机制(multi-head attention)

    在Transformer中采用的是多头注意力机制(multi-head attention),
    多头注意力机制可以理解为使用多种不同的Q K V,计算得到不同的self-attention结果。
    为什么使用多头注意力机制?论文中给出的原因是:将模型分为多个头,形成多个子空间,让模型去关注不同方面的子信息,最后再将各个方面的信息综合起来。
    在这里插入图片描述
    每个头唯一的不同就是使用的权重矩阵WQ WK WV不相同,第0个头使用的权重矩阵是 W 0 Q {W_0^Q} W0Q W 0 K {W_0^K} W0K W 0 V {W_0^V} W0V ,第1个头使用的权重矩阵是 W 1 Q {W_1^Q} W1Q W 1 K {W_1^K} W1K W 1 V {W_1^V} W1V ……

    Transformer中使用的是8个注意力头,会得到8个注意力计算的结果矩阵 Z 0 {Z_0} Z0 Z 7 {Z_7} Z7
    注意,这里得到的8个结果矩阵都是为了计算一个句子的自注意力,
    换言之,如果是计算一个单词的自注意力,这里的8个结果向量都是为了计算这一个单词的自注意力。

    但是对于一个单词来说,前馈神经网络只接收self-attention该位置上的一个输出作为输出;对于一个句子来说,前馈神经网络只接收一个向量矩阵作为输入。也就是说,对于一个单词embedding向量 x i {x_i} xi来说,虽然经过多头注意力机制会得到8个向量 z 0 {z_0} z0 z 7 {z_7} z7 ,但需要把这8个向量弄成一个,作为给前馈神经网络的输入。
    在这里插入图片描述
    8个 Z {Z} Z矩阵合并成一个矩阵Z的方法如上图所示:
    需要把 Z 0 {Z_0} Z0 Z 7 {Z_7} Z7这8个矩阵进行合并:先进行concate连接操作,再乘上权重矩阵得到最后的输出矩阵 Z {Z} Z
    Z {Z} Z输入到前馈神经网络中,进行后续的计算。

    整体对于一个句子的多头注意力机制过程如下图:
    在这里插入图片描述
    第一个编码器的输入是嵌入向量组成的矩阵 X {X} X,后边编码器的输入(矩阵 R {R} R)是前一个编码器的输出,输出的矩阵 Z {Z} Z X {X} X R {R} R的维度相同,把矩阵Z作为后面前馈神经网络的输入。

    每个注意力头关注到的信息有什么不一样?

    在这里插入图片描述
    当只有one-head的时候,it_主要关注animal_;当有two-heads的时候,it_主要关注animal_和tire;当有all-heads的时候,比较难从图上进行解释,也反映了神经网络的可解释性比较差。

    3.3.4 padding操作

    前面展示的是输入一句话,但通常在训练过程中,都是采用batch的方式一次计算多句话,出现问题:不能保证每句话的长度都相同。解决办法:padding操作

    在这里插入图片描述
    当只输入一句话的时候,X的维度是2维的;当输入一个batch(多句话)的时候,X的维度是3维的,
    每一行中灰色的部分就是padding填充的,
    embedding_dimension设定的是512,X用图形化表示应该是3维,阴影的部分就代表维度512,想象一下相当于是多个二维的面叠起来构成立体的三维。
    在这里插入图片描述

    padding操作添加在self-attention部分,需要保证padding的操作不会影响softmax计算,所以灰色的部分填充负无穷,这样softmax计算结果是0,不会有影响。
    在这里插入图片描述
    在进行padding操作的时候,先得到padding 矩阵,0表示不需要填充,1表示需要填充,1乘上负无穷得到后面的矩阵,把后面这个矩阵输入到softmax中,得到score分数。
    注意:padding操作不仅仅存在于编码器,只要使用batch进行计算,都需要用到。

    3.4 self-attention和前馈神经网络之间的Add操作

    回顾一下Transformer整体结构图,可以看到其中绿色的方块“Add&Norm”,
    在这里插入图片描述
    通过上面的结构图可以看到,Add操作就是把“self-attention的计算结果向量矩阵 Z {Z} Z + 输入的embedding向量矩阵 X {X} X”;
    类似RestNet网络中的残差连接,起到特征增强的作用。

    3.5 self-attention和前馈神经网络之间的Normalization操作

    在这里插入图片描述
    使用的是NLP中常用的layer norm,layer norm就是对每个样本都计算均值和方差,再对每个特征归一。
    看上面的图,Self-attention层输出的向量 z {z} z,构成矩阵 Z {Z} Z 对( X {X} X+ Z {Z} Z)进行Layer Norm层归一化操作,得到新矩阵,再把矩阵中的向量输入前馈神经网络。

    3.6 Encoder端的整体数据流动过程

    在这里插入图片描述
    这里的前馈神经网络就是一个简单的MLP(多层感知机),包括两层Linear和一个ReLU激活函数。

    四、Decoder端的解码过程

    4.1 整体解码过程

    解码过程如上图所示,每个time step(时间步)会输出一个解码后的单词。
    在这里插入图片描述在这里插入图片描述
    传统的seq to seq模型的解码器部分使用的是RNN模型,

    在这里插入图片描述
    对于RNN模型,在训练过程中,输入t时刻的词,模型看不到未来的词,只有当模型训练结束,模型才能看到t+1时刻的词。
    因为RNN是串联的,所有RNN有上面的特性。但是Transformer是并行计算的结构,所以需要添加mask操作

    在这里插入图片描述

    4.2 Decoder中的Masked self-attention

    再来复习下Transformer的整体结构,
    在这里插入图片描述
    上图是Transformer训练过程的结构图,其中Decoder端的输入Outputs就是翻译的句子结果,比如翻译的句子对是,Outputs就是“我爱中国”,经过word embedding得到嵌入向量,再加上positional embedding,作为第一个Decoder block的输入,单个词对应的embedding向量和权重矩阵 W Q 、 W K 、 W V {W^Q、W^K、W^V} WQWKWV相乘得到查询向量 q {q} q,键向量 k {k} k,值向量 v {v} v。当前单词的q向量和句子中每个单词的k向量相乘得到每个单词的score。(即进行QKV计算)
    在这里插入图片描述
    对整个句子操作得到矩阵Scaled Scores,为了让当前时刻不看到后面的句子,采用mask操作,就是让Scaled Scores矩阵和Look-Ahead Mask相加得到最终的Masked Scores矩阵,就是把方格中对应的灰色部分数据填成负无穷,负无穷经过softmax变成0,。
    在这里插入图片描述
    在这里插入图片描述
    就是给了解码端“我”,让它预测下一个时间步的输出“爱”,这个时候不能让模型看到“我”后面的句子“爱中国”,因为本来就是让模型去预测“我”后面的下一个字是什么,如果不进行mask操作,相当于模型已经知道“我”后面的信息是“爱中国”,这样再预测就没有意义了。
    相当于 已经告诉了模型问题的答案,再让模型去学习怎么回答就没有意义了。

    4.3 Decoder中的Multi-Head Attention

    在这里插入图片描述
    Decoder端中第一个注意力机制是Masked Multi-Head Attention,
    Decoder端中第二个注意力机制Multi-Head Attention, K {K} K V {V} V来自Encoder的输出, Q {Q} Q来自上一层Masked Multi-Head Attention的输出。因为Decoder端中第二个注意力机制的 K {K} K V {V} V来自Encoder,所以这个注意力机制也被叫做Masked Encoder-Decoder Attention。

    4.4 Decoder中的Linear和softmax

    再来看下Transformer的整体结构
    在这里插入图片描述
    在这里插入图片描述
    解码组件最后输出一个向量(蓝色方块表示),需要把这个输出向量变成一个单词,这就是线性变换层要做的操作,线性变换层Linear是一个简单的全连接神经网络。把解码组件产生的向量投射到一个比它自身大的多的向量中。
    假设模型从训练集中学习到一万个不同的英语单词,那么线性变换层之后,向量的长度就是词表的长度_vocab_size,也就是一万。
    再进行softmax操作,就得到了这一万个词每个词对应的概率,在其中选择一个概率最大的作为输出,假设下标为5的单词概率最大,就输出下标5在词表中映射的单词am。

    五、Transformer的训练过程

    首先建立output vocabulary(输出词表),定义词表完成后,就可以使用一个和词表长度相同的向量来表示词表中的单词。
    下图词表中有6个单词,就可以使用一个维度为6的向量来表示词表中的每个单词,即为one-hot编码。
    训练刚开始的时候,模型的参数都是随机生成的,模型产生的概率分布在每个单元格里赋予了随机的数值,我们可以用真实的输出来比较它。
    输入是法语句子,期望的输出是“I am a student”,理想的概率分布如下图左边所示,但实际情况不可能,所以就期望通过softmax使得正确的输出对应的概率值最大。
    在这里插入图片描述
    优化的时候就是要优化这两个概率分布,使得它们尽可能相同,这就涉及到模型的损失函数问题,一般设置交叉熵损失,辅助模型进行反向传播。

    训练结束之后,进入生成阶段,就是输入一个句子,对这个句子进行解码,一个时间步生成一个单词,得到翻译后的句子。这个时候,就设计到解码策略的问题,即怎么选择当前要输出的单词。
    前面用的选择当前概率最大的词作为输出是贪婪采样策略,还有其它几种采样方式如下图:
    在这里插入图片描述
    贪婪采样:每个时间步选择当前概率最大的,作为输出;直到出现结束符或达到最大句子长度。
    在这里插入图片描述
    Beam Search:超参数集束宽度来设定每个时间步会保留几个取值路径,最后再比较这两个输出的概率大小,选择一个概率最高的,作为最终的输出。
    在这里插入图片描述
    基于温度系数的采样:在归一化(softmax)的过程中添加超参数t,通过改变t来控制概率分布的形状,使得分布更加均匀或集中。
    在这里插入图片描述
    Top-k和Top-p采样,先把词表中每个单词的概率进行降序排列,然后缩小采样范围,Top-k就是选前k个,Top-p就是词表中前多个单词的概率相加超过p,就选前多少个单词。然后再随机(真的随机)选择一个单词。
    在这里插入图片描述
    在这里插入图片描述

    内容来自看b站视频做的笔记,加深了对Transformer架构的理解
    :https://www.bilibili.com/video/BV1ng4y1K7GZ/?spm_id_from=333.1007.top_right_bar_window_history.content.click

  • 相关阅读:
    【前端知识】Three 学习日志(八)—— 全屏渲染
    JAVA多线程信号量Semaphore
    flink中配置Rockdb的重要配置项
    SLAM学习——开启cmake的第一个项目
    webgoat-Security Logging Failures安全日志记录失败
    大数据必学Java基础(二):Java核心机制
    Elasticsearch - What‘s new in 8.3
    Java 并发编程
    Netty(1)三大组件
    curl 工具的使用
  • 原文地址:https://blog.csdn.net/weixin_44021274/article/details/132819685