注意力机制与 Transformer

13 天前(已编辑)
/ ,
147
2

动机

早期序列建模方法具有局限性:

  • RNN(循环神经网络):RNN 能够处理序列数据,但在处理长序列时存在梯度消失与梯度爆炸问题。

  • LSTM(长短时记忆)GRU(门控循环单元):为解决 RNN 的梯度问题,引入了更复杂的结构,如 LSTM 和 GRU,但计算效率相对较低。

  • CNN(卷积神经网络):虽然主要用于图像处理,但也可以应用于序列数据,如文本分类。然而,CNN 的感受野有限,不能直接捕捉全局依赖关系。

为了解决这些问题,注意力机制被引入。

首先明确一点,注意力是稀有资源,我们只有有限的处理能⼒,所以需要忽略无关的信息。

考虑人类视觉注意力的特点:

  • 非自主性提示:外部环境的刺激会引起人们的注意。如一堆书中的一本书的颜色与其他书不同,会引起人们的注意。
  • 自主性提示:自主性注意力是指人们在没有外部提示的情况下,根据自己的兴趣和目标选择注意的对象。如在一堆书中,人们会选择自己感兴趣的书。
human_attention

human_attention

注意力机制

不同于过往的深度学习模型,Transformer 摒弃了过往的循环机制 (这会导致梯度消失),使用了 自注意力机制

自注意力机制是一种计算序列中各个元素之间的依赖关系的方法。在自注意力机制中,每个元素都可以与序列中的其他元素相互作用,从而捕捉 长距离依赖关系

qkv

qkv

注意力提示:查询、键、值

  • 非自主性提示:键(Key)、值(Value),他们以 成对 的形式给出。
  • 自主性提示:查询(Query)
  • 注意力汇聚:打分函数,注意力权重矩阵,加权平均

注意力汇聚数学表达如下:

其中:

  • 是查询向量(Query),可以理解为当前的单词
  • 是键向量(Key),可以理解为前文(数据)中的所有单词(每个 对应一个单词)
  • 是值向量(Value),可以理解为前文(数据)中的所有单词的某种特征(如嵌入向量)
  • 注意力打分函数

可以看到,我们先将查询向量(Query)和键向量(Key)通过打分函数做了复合,得到了 注意力权重,然后将值向量(Value)与注意力权重做了 加权平均 (这里还要求了一下注意力汇聚部分的输出是一个加和为 1 的概率分布,所以可以在其中接一个 Softmax),从而得到了最终的输出。

直到现在,我们谈论的还是普通的注意力机制,因为打分函数 可以是一个 固定的函数,这是一种特殊情况。例如,采用核方法,可以将 定义为高斯核函数,这样就可以得到一个固定的注意力机制。

这里提到的核方法可以在 D2L / 注意力汇聚:Nadaraya-Watson 核回归 中找到更多信息。

自注意力机制(Self-Attention)

自注意力机制是注意力机制的一种特殊形式,其中 查询、键和值都来自同一个地方,即输入序列。

self-attention

self-attention

其中:

  • Q、K、V 是三个不同的空间,图中得到 Q、K、V 都是对于输入序列完成了线性变换。我们首先将输入序列转换为 嵌入表示, 然后,通过三个不同的线性层投影 ,分别生成 查询矩阵(Q)、键矩阵(K)和值矩阵(V)。这三个矩阵将用于计算注意力输出。
  • 是序列长度
  • 是嵌入的特征维度
  • 是 Q、K、V 矩阵的维度,也叫 head dim
self_attention_2

self_attention_2

打分函数:缩放点积注意力(Scaled Dot-Product Attention)

缩放点积注意力 是一种计算模型中不同位置之间相互关注程度的方法。这里的 “注意力” 可以理解为模型在处理数据时赋予不同部分的重要性。具体来说,它通过以下步骤实现。

查询(Q)、键(K)、值(V)矩阵的生成

首先,输入数据 经过三个不同的权重矩阵 转换,得到对应的查询(Q)、键(K)、值(V)矩阵。

这里 表示输入数据的特征维度, 表示转换后的特征维度(这里要求 Q、K、V 中都相等), 表示序列长度。

计算注意力得分

然后,使用矩阵乘法计算查询(Q)和键(K)的相似度,得到一个注意力权重矩阵。

这个注意力权重矩阵可以通过以下公式计算:

这里通过除以 ​ 进行缩放(缩放点积),防止相似度矩阵中的数值过大(累次运算的影响),使梯度保持稳定(想想 Softmax 在大数值时会导致梯度消失)。

这里注意,除以 进行缩放是对 得到的 矩阵的所有元素各自进行的。这里每个元素都是一个 得到的值,所有他的扩大的了 倍,因而需要缩放点积。后文给出证明。

这里要求了 Q、K 的维度相同,这样才能进行矩阵乘法。

Proof by GPT4:

在统计学中,如果向量中的每个元素都是独立的随机变量,并且每个元素都有零均值(0)和单位方差(1),那么这些元素的点积的均值确实是 0。这是因为均值是期望值的线性运算,当两个独立随机变量的均值都为 0 时,它们的乘积的期望值也为 0。

具体来说,对于两个向量 ,它们的点积是:

由于 都是独立的随机变量,且均值为 0,所以点积的期望值(均值)是:

现在让我们来看方差。对于两个独立随机变量 ,其乘积的方差是:

由于 的均值为 0,上述公式简化为:

因为 的方差都是 1(单位方差),所以 。那么对于每一对 ,它们乘积的方差是 1。

最后,考虑到点积是所有这些乘积的和,且这些乘积是独立的,所以点积的总方差是各个乘积方差的和:

因此,当向量 的长度为 ,且它们的元素都有零均值和单位方差时,它们的点积的均值为 0,方差为 。这就是为什么在缩放点积注意力机制中需要除以 来保持方差的一致性,不受向量长度的影响。

归一化

对缩放后的相似度矩阵应用 Softmax 函数,使得 每一列(注意不是矩阵全体) 的值加起来等于 1,也即转换为 概率分布,这个概率分布表示每个键对应的注意力权重。

将之前得到的注意力权重矩阵与值()矩阵相乘,得到最终的输出矩阵 ,它是输入序列的 加权表示,其中的权重由注意力得分决定。

加性注意力(Additive Attention)

加性注意力是另一种用于计算查询和键之间相似度的方法。它首先将查询和键分别经过线性变换(全连接层), 然后将两者相加,并通过非线性激活函数(如 )计算相似度。接下来,使用可学习的权重向量计算最终的相似度分数。

Attention 机制的计算公式如下:

其中:

  • : 查询向量(Query)集合。
  • : 键向量(Key)集合。
  • : 值向量(Value)集合。
  • : 查询向量的线性变换矩阵。
  • : 键向量的线性变换矩阵。
  • : 表示可学习的权重向量
  • : 向量或矩阵的加法。
  • : 表示应用 非线性激活函数

简而言之:先加起来(而不是乘起来)然后用了一层非线性激活函数与矩阵乘法。

多头注意力机制(Multi-Head Attention)

为什么要使用多头注意力机制?

增加模型的表达能力:多头注意力机制可以让模型同时关注不同位置的信息,从而提高模型的表达能力。

你可能会想:一个单一的注意力矩阵(头)可以有多个热点,这已经足够了啊?单个注意力矩阵确实可以关注多个信息点,但多头注意力机制能让模型在不同的 “头” 中分别学习到数据的不同表示。这样每个 “头” 可以捕捉到不同的信息,最后将这些信息综合起来,就可以得到更加丰富的数据表示。

举个例子,我们现在经常使用大模型,对于同样的提示,不同的大模型可以有不同的输出(关注不同的点),我们比较这些输出,就可以得到更加准确的结果(各取所长)。

多头注意力机制的计算过程

multi-head_attention

multi-head_attention
multi_head_attention_arch

multi_head_attention_arch

对于一个输入序列,假设其长度为 ,嵌入维度(特征维度)为 ,头数为 ​(num_heads),则多头注意力计算过程如下。

输入矩阵

,形状为:

对于一共 个注意力头,其中 表示注意力头的索引,每个注意力头独立的 QKV 权重矩阵为:

,形状为:

其中,,这里进行如此规定的原因为了保持输入输出的维度不变。

线性变换后的查询、键和值矩阵

,形状为:

缩放点积注意力计算中的相似度矩阵

,形状为:

缩放点积注意力的输出

,形状为:

这里的改变来自于 的尺寸变成了

多头注意力的拼接输出

,形状为:,因为我们拼接了 个头,每个头的维度为 ,总维度为

线性变换后的输出矩阵

,形状为:

多头注意力的输出

,形状为:

Transformer

transformer

transformer

位置编码

原先的注意力机制无法处理序列中的位置信息(各个单词在 Self-Attention 中是各个位置都是并行处理的,而不像 RNN 是顺序处理的,后者可以通过顺序来表达位置信息),为了解决这个问题,Transformer 引入了位置编码(Positon Embedding)。

位置编码的维数和嵌入向量的维数相同,他们会被加到一起。

  • 句子 = 词 + 位置顺序排列
  • 词信息转为 词嵌入
  • 位置信息转为 位置嵌入
  • 表达句子的向量序列 = 词嵌入向量 + 位置嵌入向量

使用三角函数来编码位置信息,因为其具有连续性且周期性:

  1. 连续性:三角函数是连续的,可以表示很小的位置变化。使用正弦和余弦函数,可以确保位置编码的输出在任何位置都是连续可导的,这对于基于梯度的优化算法来说是有益的。
  2. 周期性:三角函数有周期性,可以帮助模型捕捉到句子中的模式,还可以对任意长度的序列进行编码。通过使用正弦和余弦函数的不同频率,模型还可以学习到不同尺度的位置关系。

具体来说,对于位置编码,每个维度的位置编码会交替使用 sin 和 cos 函数,其公式如下:

position_encode

position_encode

其中:

  • 表示位置 和维度 的位置编码
  • 表示位置 和维度 ​ 的位置编码
  • 这张图中,横着是嵌入的维度顺序,竖着是输入向量的位置顺序。

这里的 是位置, 是嵌入向量的维度索引, 是嵌入向量的总维度。

通过这种方式,模型就可以根据正弦和余弦函数的值来判断单词的位置,进而理解单词的语序和句子结构。

编码器

  1. 源输入序列经过嵌入层的处理得到 词嵌入,并 加上位置编码,得到最终的输入向量。

  2. 通过 多头注意力 组线性层 对词嵌入表示进行变换,将其映射为三个不同的空间,得到查询矩阵 Q、键矩阵 K 和值矩阵 V。

  3. 计算 缩放点积注意力输出。使用 Q、K、V 三个矩阵,按照缩放点积注意力的计算公式进行计算,得到输出矩阵。这个输出矩阵包含了每个词向量对于其他所有词向量的注意力权重。

  4. 将输入向量和注意力输出向量相加,得到 残差连接 的结果(类似 ResNet)。

  5. 对残差连接的结果进行 层规范化 (Layer Normalization)

    • 将每层的输出值归一化到均值为 0、方差为 1 的范围内。
    • 层规范化将每个神经元的输出值 减去它在这一层的均值 ,然后除以标准差 ,最后乘以可学习的缩放因子 ,再加上可学习的偏置项

  6. 逐位前馈网络(Feed-Forward Network, FFN)用于在自注意力机制之后进行 非线性变换。逐位前馈网络由两个全连接层和他们之间的一个 ReLU 激活函数组成。

  7. 如果有 n 个编码器,则可以将它们 依次串联 起来。其中,第一个编码器的输入是词嵌入向量加上位置编码向量。

  8. 最后一个编码器的输出就是最终的编码表示,它将用于传递给 解码器 进行下一步的处理(会由此得出解码器中的一部分所需的 K、V )。

解码器

目标序列嵌入和位置编码

encoder 的输出并没直接作为 decoder 的直接输入

  • 解码器的输入被称为目标序列。初始 decoder 的 time step 为 1 时(也就是第一次接收输入), 其输入为一个特殊的 token,即目标序列开始的 token(如 <BOS>),也可能是其它视任务而定的输入等等,其目标则是预测翻译后的第 1 个单词(token)是什么
  • 然后 <BOS> 和预测出来的第 1 个单词一起,再次作为 decoder 的输入,得到第 2 个预测单词
  • 后续依此类推。直到遇到特殊的结束 token (如 <EOS>)或者达到最大输出长度为止。

目标序列首先经过嵌入层处理得到词嵌入,并加上 位置编码,得到最终的输入向量。

掩蔽多头注意力层(Masked Multihead Attention)

生成解码器自注意力矩阵,用于捕捉当前解码器状态与之前解码器状态的依赖关系。计算过程与编码器的自注意力矩阵相似。

掩码(Mask)机制屏蔽了未来的序列信息,防止未来信息泄露到当前位置上。在预测当前词时,只利用前面的词。创建一个掩码矩阵,形状为 ,其中 是输入序列的长度。掩码矩阵的元素 设置为 (对应不给看)或 (对应给看)。在 softmax 计算前,掩码矩阵与注意力分数相加,被掩盖位置的注意力分数变为负无穷,在 softmax 后,其对应的权重变为 ,不影响当前位置。

masked_multihead_attention

masked_multihead_attention

注意看这张图中,我们掩盖了注意力权重矩阵中的一半,使当前位置看不到未来信息。然后才去和 V 进行矩阵乘法。也即,需要在预测当前词时,只利用前面的词,而不使用后面的词。

编码器 - 解码器注意力层(Encoder-Decoder Attention)

将编码器输出的信息融入到当前解码器状态中,帮助解码器更好地进行下一步预测。在这个层中,查询向量 来自 前一个解码器层 的输出,而键向量 和值向量 来自 编码器 的输出。

逐位前馈网络和 AddNorm

结构同前文编码器。

解码器堆叠:堆叠 个解码器层,前一个解码器的输出和对应编码器的输出是下一个解码器的输入。

第 1 个解码器层:

个解码器层 () :

最后,我们可以对 最后一个解码器层 的输出施加 Softmax 函数来计算词汇表的概率分布:

预训练模型(Pre-trained Models)

预训练模型是一种强大的技术,用于在 无标签数据 上学习 通用语言表示,从而提高 NLP 任务的性能。

无标签数据:指我们收集到的数据没有明确的标记或分类信息。

  1. 预训练阶段:在大量无标签文本数据上进行预训练,学习通用的语言表示。
  2. 微调阶段:针对特定任务进行 微调 fine-tune, 使模型适应各种 NLP 任务。

GPT(Generative Pre-Trained Transformer)

基于 Transformer 的预训练生成模型

  • 预训练:在大量无标签文本数据上进行预训练,学习通用的语言表示。
  • 学习 单向 上下文信息(仅使用左侧的上下文信息来预测当前词)。
  • 使用 多个 Transformer 的解码器 堆叠
  • 自回归训练:预测下一个词的概率分布,从而学习语言模型。
  • 应用:文本生成、文本分类、问答系统等。

BERT

基于 Transformer 的双向编码器表示模型,学习 双向 上下文信息。

  • 预训练:在大量无标签文本数据上进行预训练,学习通用的语言表示。
  • 学习 双向 上下文信息(同时使用左侧和右侧的上下文信息预测当前词)。
  • 使用 多个 Transformer 的编码器 堆叠
  • 预训练任务:掩蔽语言模型(Masked Language Model,MLM)和下一句预测(Next Sentence Prediction,NSP)。
  • 应用:文本分类、问答系统、命名实体识别等。

差别

在 Transformer 架构中,编码器和解码器的设计有不同的目的。

编码器(Encoder)的设计目的是为了 理解输入文本的上下文关系。它通过自注意力机制(Self-Attention)来处理输入的文本,使得模型能够考虑到每个词与文本中其他词的关系。因此,BERT 使用多个编码器堆叠来更好地理解输入文本的双向上下文,这对于理解整个句子的含义非常重要。

解码器(Decoder)则是专注于生成文本。它不仅使用自注意力机制来理解已经生成的文本,还使用编码器 - 解码器注意力(Encoder-Decoder Attention)来 关注输入文本的哪些部分对于生成下一个词最为重要。GPT 使用多个解码器堆叠,因为它的目标是根据前面的文本生成下一个词,所以它不需要理解整个句子的双向上下文,只需要基于之前的词生成新的词即可。

简单来说,BERT 的目标是理解,所以用编码器;GPT 的目标是生成,所以用解码器。这就是为什么它们会选择不同的 Transformer 部件来堆叠。

大模型进化史

参数规模提升带来能力 “涌现 Emergent”:大型语言模型(LLM)在训练过程中学到的一种自发性的任务完成能力。模型基本结构和训练方式基本不变,只增大模型和数据规模,训练出的模型 “智能” 程度明显提高。

原因如下:

  1. 大量的训练数据:人类积累的所有信息,使模型有更丰富的知识基础。
  2. 模型容量:模型具有足够的容量来充分学习数据。
  3. 自回归和无监督训练:这种方式不需要标注海量数据,简化了训练过程。
  4. 迁移学习和微调:使模型能够适应不同任务,增强了模型的应用范围。
  5. 多任务学习:提高模型的泛化性,使其在多种任务上都能表现出色。

Credit

D2L / multi head attention

LiLianWeng / Transformer

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...