构建大型语言模型(从头开始)
- [3 编码注意力机制(Coding Attention Mechanisms)](#3 编码注意力机制(Coding Attention Mechanisms))
-
- [3.1 长序列建模问题(The problem with modeling long sequences)](#3.1 长序列建模问题(The problem with modeling long sequences))
- [3.2 用注意力机制捕获数据依赖关系(Capturing data dependencies with attention mechanisms)](#3.2 用注意力机制捕获数据依赖关系(Capturing data dependencies with attention mechanisms))
- [3.3 用自注意力关注输入的不同部分(Attending to different parts of the input with self-attention)](#3.3 用自注意力关注输入的不同部分(Attending to different parts of the input with self-attention))
3 编码注意力机制(Coding Attention Mechanisms)
本章涵盖
- 探索神经网络中使用注意力机制的原因
- 引入基本的自注意力框架并逐步发展为增强的自注意力机制
- 实现因果注意力模块,允许LLMs一次生成一个token
- 用 dropout 掩盖随机选择的注意力权重以减少过度拟合
- 将多个因果注意力模块堆叠成多头注意力模块
在上一章中,您学习了如何准备用于训练LLM的输入文本。这涉及到将文本分割成单独的单词和子词tokens,这些tokens可以被编码成向量representation,即所谓的嵌入,用于LLM。
在本章中,我们将研究 LLM 架构本身的一个组成部分,即注意力机制,如图 3.1 所示。
图 3.1 编码 LLM、在一般文本数据集上预训练 LLM 以及在labeled dataset上对其进行微调的三个主要阶段的mental model 。本章重点介绍注意力机制,它是 LLM 架构的一个组成部分。
注意力机制是一个综合性的主题,这就是我们用整整一章来讨论它的原因。我们将主要孤立地看待这些注意力机制,并在机械层面上关注它们。在下一章中,我们将围绕自注意力机制对 LLM 的其余部分进行编码,以查看它的实际效果并创建一个模型来生成文本。
在本章中,我们将实现注意力机制的四种不同变体,如图 3.2 所示。
图 3.2 该图描绘了我们将在本章中编码的不同注意机制,从自注意的简化版本开始,然后添加可训练权重。因果注意力机制为自我注意力添加了一个掩码,允许LLM一次生成一个单词。最后,多头注意力将注意力机制组织成多个头,使模型能够并行捕获输入数据的各个方面。
图3.2中所示的这些不同的注意力变体是相互依赖的,我们的目标是在本章结束时得到一个简洁而有效的多头注意力实现,然后我们可以将其插入到下一章我们将要编写的 LLM 架构中。
3.1 长序列建模问题(The problem with modeling long sequences)
在我们深入探讨本章后面的 LLM 核心的自注意力机制之前,先了解一下 LLM 之前没有注意力机制的架构有什么问题?假设我们想要开发一种语言翻译模型,将文本从一种语言翻译成另一种语言。如图 3.3 所示,由于源语言和目标语言的语法结构,我们不能简单地逐字翻译文本。
图 3.3 将文本从一种语言翻译成另一种语言(例如德语翻译成英语)时,不可能仅仅逐字翻译。相反,翻译过程需要上下文理解和语法对齐。
为了解决我们无法逐字翻译文本的问题,通常使用具有两个子模块的深度神经网络,即所谓的编码器和解码器。编码器的工作是首先读入并处理整个文本,然后解码器生成翻译后的文本。
当我们在第 1 章(第 1.4 节,将 LLM 用于不同任务)中介绍 Transformer 架构时,我们已经简要讨论了编码器-解码器网络。在 Transformer 出现之前,循环神经网络(RNN) 是最流行的语言翻译编码器-解码器架构。
RNN 是一种神经网络,其中先前步骤的输出被作为当前步骤的输入,这使得它们非常适合文本等序列数据。如果您不熟悉 RNN,请不要担心,您不需要了解 RNN 的详细工作原理即可跟随本讨论;我们这里的重点更多是编码器-解码器设置的一般概念。
在编码器-解码器 RNN 中,输入文本被送入编码器,编码器按顺序对其进行处理。编码器在每一步都会更新其隐藏状态( 隐藏层的内部值),试图在最终隐藏状态中捕获输入句子的完整含义,如图 3.4 所示。然后解码器使用这个最终的隐藏状态开始生成翻译后的句子,每次生成一个词。它也在每一步更新其隐藏状态,这些状态应该携带生成下一个词所需的上下文。
在一个编码器-解码器结构的循环神经网络(RNN)中,输入文本先由编码器处理。编码器通过逐步读取输入文本,每读取一步,就根据当前读入的信息更新其隐藏状态。这些隐藏状态试图在网络的每一层中保留越来越丰富的信息,最终在编码器的最后一步,其隐藏状态尝试包含整个输入文本的语义内容。
这个最终的隐藏状态然后被传递给解码器,解码器使用它作为开始生成翻译文本的基础。解码器在生成每个词的过程中也会逐步更新自己的隐藏状态,这些状态帮助解码器记住之前已生成的词的上下文,以便更准确地预测下一个词。这种方式允许模型在生成翻译时保持语言的连贯性和上下文的一致性。
图 3.4 在 Transformer 模型出现之前,编码器-解码器 RNN 是机器翻译的流行选择。编码器将源语言中的tokens序列作为输入,其中编码器的隐藏状态(中间神经网络层)对整个输入序列的压缩representation进行编码。然后,解码器使用其当前的隐藏状态开始逐个tokens的翻译。
编码器-解码器 RNN 的最大问题和限制是,RNN 无法在解码阶段直接从编码器访问早期隐藏状态。因此,它仅依赖于当前隐藏状态,其中封装了所有相关信息。这可能会导致上下文丢失,尤其是在依赖关系可能跨越很长距离的复杂句子中。
对于不熟悉 RNN 的读者来说,没有必要理解或研究这种架构,因为我们不会在本书中使用它。本节的要点是编码器-解码器 RNN 有一个缺点,这激发了注意力机制的设计。
3.2 用注意力机制捕获数据依赖关系(Capturing data dependencies with attention mechanisms)
在 Transformer LLM 之前,如前所述,通常使用 RNN 来执行语言建模任务,例如语言翻译。 RNN 可以很好地翻译短句子,但不能很好地翻译较长的文本,因为它们无法直接访问输入中的先前单词。
这种方法的一个主要缺点是 RNN 必须记住单个隐藏状态下的整个编码输入,然后再将其传递给解码器,如上一节中的图 3.4 所示。
因此,研究人员在 2014 年为 RNN 开发了所谓的Bahdanau 注意力机制(以相应论文的第一作者的名字命名),该机制修改了编码器-解码器 RNN,使得解码器可以在每次解码时选择性地访问输入序列的不同部分步骤如图 3.5 所示。
图 3.5 使用注意力机制,网络的文本生成解码器部分可以有选择地访问所有输入tokens。这意味着对于生成给定的输出tokens,某些输入tokens比其他输入tokens更重要。重要性由所谓的注意力权重决定,我们稍后将计算该权重。请注意,该图显示了注意力背后的总体思想,并未描述 Bahdanau 机制的确切实现,这是本书范围之外的 RNN 方法
有趣的是,仅仅三年后,研究人员发现构建用于自然语言处理的深度神经网络不需要 RNN 架构,并提出了原始的Transformer架构(在第一章中讨论),其自注意力机制受 Bahdanau 注意力机制的启发。
Self-attention自注意力是一种机制,允许输入序列中的每个位置在计算序列的representation时关注同一序列中的所有位置。
自注意力是基于 Transformer 架构的当代 LLM 的关键组成部分,例如 GPT 系列。
本章重点是编码和理解类似 GPT 模型中使用的自注意力机制,如图 3.6 所示。在下一章中,我们将编写 LLM 的其余部分。
图 3.6 自注意力是 Transformer 中的一种机制,用于通过允许序列中的每个位置与同一序列中的所有其他位置交互并权衡其重要性来计算更有效的输入表示。在本章中,我们将从头开始编写这种自注意力机制,然后再在下一章中编写类似 GPT 的 LLM 的其余部分。