现在我们已经对分词和嵌入有了一定的了解,接下来可以深入探讨语言模型的工作原理。在本章中,我们将研究Transformer语言模型的一些核心直觉。我们的重点是文本生成模型,以便更深入地理解生成型大型语言模型(LLM)。
我们将同时探讨相关概念,并提供一些代码示例来演示这些概念。首先,我们将加载一个语言模型,并通过声明一个管道来为生成任务做好准备。在第一次阅读时,你可以选择跳过代码,专注于理解相关概念。然后在第二次阅读时,通过代码来应用这些概念。
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
# 加载模型和分词器
tokenizer = AutoTokenizer.from_pretrained("microsoft/Phi-3-mini-4k-instruct")
model = AutoModelForCausalLM.from_pretrained(
"microsoft/Phi-3-mini-4k-instruct",
device_map="cuda",
torch_dtype="auto",
trust_remote_code=True,
)
# 创建生成管道
generator = pipeline(
"text-generation",
model=model,
tokenizer=tokenizer,
return_full_text=False,
max_new_tokens=50,
do_sample=False,
)
```
Transformer模型概览
让我们从模型的整体概览开始探索,然后看看自2017年引入Transformer模型以来,后续的工作是如何改进它的。
训练好的Transformer LLM的输入与输出
理解Transformer大型语言模型(LLM)行为的最常见方式是将其视为一个接收文本输入并生成响应文本的系统。一旦在足够大的高质量数据集上训练出足够大的文本输入-输出模型,它就能够生成令人印象深刻且有用的输出。图3-1展示了一个此类模型用于撰写电子邮件的示例。
模型并不是一次性生成完整的文本;实际上,它是一次生成一个词元(token)。图3-2展示了模型响应输入提示时的四个词元生成步骤。每个词元生成步骤都对应一次模型的前向传播(在机器学习中,这意味着输入进入神经网络,并通过计算图执行必要的计算,最终在另一端生成输出)。
在每次生成词元之后,我们通过将生成的词元附加到输入提示的末尾,来调整下一步生成的输入提示。我们可以在图3-3中看到这一过程。
这让我们更准确地理解了模型的工作原理,它只是根据输入提示预测下一个词元。围绕神经网络的程序基本上将其运行在一个循环中,逐步扩展生成的文本,直到完成。
在机器学习中,有一个专门的词汇来描述那些利用先前预测来进行后续预测的模型(例如,模型生成的第一个词元被用来生成第二个词元)。这些模型被称为自回归模型(autoregressive models)。因此,你会听到生成文本的LLM被称为自回归模型。这通常用来区分生成文本模型和像BERT这样的文本表示模型,后者并不是自回归的。
这种自回归的、逐词元生成的过程就是在使用LLM生成文本时的底层机制,正如我们在此看到的:
python
复制代码
prompt = "Write an email apologizing to Sarah for the tragic gardening mishap. Explain how it happened."
output = generator(prompt)
print(output[0]['generated_text'])
这将生成以下文本:
解决方案1:
主题:诚挚的道歉,关于园艺事故
亲爱的Sarah,
我希望你一切安好。我写这封信是为了表达我深深的……
我们可以看到,模型开始撰写电子邮件,首先从主题部分开始。它突然停止了,因为达到了我们通过设置max_new_tokens
为50个词元所规定的词元限制。如果我们增加这个限制,它将继续生成直到完成整封电子邮件。
前向传播的组件
除了循环之外,两个关键的内部组件是分词器和语言模型头(LM head)。图3-4展示了这些组件在系统中的位置。在前一章中,我们已经看到分词器如何将文本分解为一系列词元ID,这些ID随后成为模型的输入。
分词器之后是神经网络:一组执行所有处理任务的Transformer块。然后,这一堆栈后面连接语言模型头(LM head),它将堆栈的输出转换为下一个最有可能词元的概率分数。
回想一下第2章,分词器包含一个词元表——即分词器的词汇表。模型为词汇表中的每个词元都关联了一个向量表示(词元嵌入)。图3-5展示了一个具有50,000个词元的模型的词汇表及其关联的词元嵌入。
计算流程按照箭头从上到下的方向进行。对于每一个生成的词元,过程会依次通过堆栈中的每个Transformer块,接着进入语言模型头(LM head),最终输出下一个词元的概率分布,如图3-6所示。
语言模型头(LM head)本身是一个简单的神经网络层。它是可以附加到一组Transformer块上的多个“头”之一,用于构建不同类型的系统。其他类型的Transformer头包括序列分类头和词元分类头。
我们可以通过简单地打印模型变量来显示层的顺序。对于这个模型,我们看到以下结构:
Phi3ForCausalLM(
(model): Phi3Model(
(embed_tokens): Embedding(32064, 3072, padding_idx=32000)
(embed_dropout): Dropout(p=0.0, inplace=False)
(layers): ModuleList(
(0-31): 32 x Phi3DecoderLayer(
(self_attn): Phi3Attention(
(o_proj): Linear(in_features=3072, out_features=3072, bias=False)
(qkv_proj): Linear(in_features=3072, out_features=9216, bias=False)
(rotary_emb): Phi3RotaryEmbedding()
)
(mlp): Phi3MLP(
(gate_up_proj): Linear(in_features=3072, out_features=16384, bias=False)
(down_proj): Linear(in_features=8192, out_features=3072, bias=False)
(activation_fn): SiLU()
)
(input_layernorm): Phi3RMSNorm()
(resid_attn_dropout): Dropout(p=0.0, inplace=False)
(resid_mlp_dropout): Dropout(p=0.0, inplace=False)
(post_attention_layernorm): Phi3RMSNorm()
)
)
(norm): Phi3RMSNorm()
)
(lm_head): Linear(in_features=3072, out_features=32064, bias=False)
)
查看这个结构,我们可以注意到以下几点:
- 这展示了模型的各个嵌套层。模型的大部分被标记为
model
,接着是lm_head
。 - 在
Phi3Model
模型中,我们可以看到嵌入矩阵embed_tokens
及其维度。它有32,064个词元,每个词元的向量大小为3,072。 - 暂时跳过dropout层,可以看到下一个主要组件是由32个
Phi3DecoderLayer
组成的Transformer解码器层堆栈。 - 每个Transformer块包含一个注意力层和一个前馈神经网络(也称为mlp或多层感知器)。我们将在本章稍后详细介绍这些内容。
- 最后,我们看到
lm_head
接收大小为3,072的向量,并输出一个与模型已知词元数量相等的向量。该输出就是每个词元的概率分数,帮助我们选择输出词元。
从概率分布中选择一个词元(采样/解码)
在处理结束时,模型的输出是词汇表中每个词元的概率分数,正如我们之前在图3-6中所见。选择单个词元的方法被称为解码策略。图3-7展示了这个过程如何在一个例子中选出词元“Dear”。
最简单的解码策略是总是选择具有最高概率分数的词元。然而,在大多数实际用例中,这往往不会带来最佳的输出。一个更好的方法是引入一些随机性,有时选择第二高或第三高概率的词元。其基本思想是根据概率分数进行采样,正如统计学家所说的那样。
对于图3-7中的例子,这意味着如果词元“Dear”有40%的概率成为下一个词元,那么它就有40%的机会被选中(而不是贪婪搜索直接选取最高分数的词元)。通过这种方法,其他所有词元都有机会根据其分数被选中。
每次选择得分最高的词元称为贪婪解码。如果在LLM中将temperature参数设置为零,就会出现这种情况。我们将在第6章中介绍temperature的概念。
现在让我们仔细看看展示这一过程的代码。在这个代码块中,我们将输入的词元通过模型,然后通过lm_head
进行处理:
prompt = "The capital of France is"
# 对输入提示进行分词
input_ids = tokenizer(prompt, return_tensors="pt").input_ids
# 将输入提示分词后的结果移动到CUDA设备上
input_ids = input_ids.to("cuda")
# 获取lm_head之前的模型输出
model_output = model.model(input_ids)
# 获取lm_head的输出
lm_head_output = model.lm_head(model_output[0])
现在,lm_head_output
的形状为[1, 6, 32064]
。我们可以通过lm_head_output[0, -1]
获取最后生成的词元的概率分数,其中索引0
是批处理维度,索引-1
用于获取序列中的最后一个词元。这时,我们获得了所有32,064个词元的概率分数列表。接着,我们可以获取得分最高的词元ID,并将其解码为生成的输出词元对应的文本:
token_id = lm_head_output[0, -1].argmax(-1)
tokenizer.decode(token_id)
在这个例子中,结果是:
Paris
并行词元处理与上下文大小
Transformer的一个最吸引人的特性是,它们在语言处理任务中比以往的神经网络架构更适合并行计算。在文本生成中,我们可以通过观察每个词元的处理方式初步了解到这一点。我们从前一章了解到,分词器会将文本分解为词元。然后,每个输入词元通过自己的计算路径(至少这是一个不错的初步直觉)。我们可以在图3-8中看到这些独立的处理轨道或流。
当前的Transformer模型对于一次能够处理的词元数量是有限制的,这个限制称为模型的上下文长度。一个具有4K上下文长度的模型只能处理4K个词元,也就是说它只能有4K条这样的处理流。
每个词元流的起点是一个输入向量(包括词元嵌入向量和一些位置信息,我们将在本章稍后讨论位置信息嵌入)。在处理流的末端,另一个向量作为模型处理的结果出现,如图3-9所示。
对于文本生成,只有最后一个词元流的输出结果用于预测下一个词元。该输出向量是输入到语言模型头(LM head)中的唯一内容,LM head根据它计算下一个词元的概率。
你可能会疑惑,既然我们只使用最后一个词元的输出,为什么还要计算所有的词元流呢?答案是,前面的词元流计算是必要的,并且在计算最终词元流时会被使用。虽然我们不使用它们的最终输出向量,但在每个Transformer块中,早期的输出会用于Transformer块的注意力机制。
如果你在跟随代码示例,请回想一下,lm_head
的输出形状为[1, 6, 32064]
,这是因为它的输入形状为[1, 6, 3072]
,即一个包含六个词元的输入字符串,每个词元由一个大小为3,072的向量表示,这些向量对应的是经过Transformer块堆栈后的输出向量。
我们可以通过打印以下内容来查看这些矩阵的维度:
model_output[0].shape
输出为:
torch.Size([1, 6, 3072])
同样,我们可以打印lm_head
的输出:
lm_head_output.shape
输出为:
torch.Size([1, 6, 32064])
通过缓存键和值加速生成
回想一下,在生成第二个词元时,我们只是将生成的词元附加到输入中,然后再次通过模型进行前向传播。如果我们赋予模型缓存先前计算结果的能力(特别是注意力机制中的一些特定向量),我们就不再需要重复先前词元流的计算。这时,唯一需要计算的就是最后一个词元流。这个优化技术称为键和值缓存(kv cache) ,它显著加速了生成过程。键和值是注意力机制的核心组成部分之一,我们将在本章稍后详细讨论。
图3-10展示了在生成第二个词元时,由于缓存了先前词元流的结果,只有一个处理流处于活动状态。
在 Hugging Face 的 Transformers 中,缓存功能默认是启用的。我们可以通过设置 use_cache=False
来禁用它。通过让模型生成较长的文本,我们可以比较启用和禁用缓存时生成的速度差异:
prompt = "Write a very long email apologizing to Sarah for the tragic gardening mishap. Explain how it happened."
# 对输入提示进行分词
input_ids = tokenizer(prompt, return_tensors="pt").input_ids
input_ids = input_ids.to("cuda")
接着,我们计时生成100个词元时使用缓存的时间。在 Jupyter 或 Colab 中,我们可以使用 %%timeit
魔法命令来测量执行时间(它会多次运行命令并取平均值):
%%timeit -n 1
# 生成文本
generation_output = model.generate(
input_ids=input_ids,
max_new_tokens=100,
use_cache=True
)
在具有 T4 GPU 的 Colab 环境中,生成100个词元耗时约为4.5秒。如果禁用缓存呢?
%%timeit -n 1
# 生成文本
generation_output = model.generate(
input_ids=input_ids,
max_new_tokens=100,
use_cache=False
)
结果是21.8秒。差距非常显著。事实上,从用户体验的角度来看,即使是4秒的生成时间,对于一直盯着屏幕等待模型输出的用户来说也显得过长。这也是为什么LLM API在生成时会流式输出词元,而不是等待整个生成过程完成后再一次性输出的原因之一。
Transformer 块内部
现在我们可以讨论大部分处理发生的地方:Transformer 块。如图3-11所示,Transformer LLM 由一系列 Transformer 块组成(在原始的 Transformer 论文中通常有六个块,而在许多大型 LLM 中则超过一百个)。每个块处理其输入,然后将处理结果传递给下一个块。
一个 Transformer 块(如图 3-12 所示)由两个连续的组件组成:
- 注意力层 主要负责从其他输入词元和位置中整合相关信息。
- 前馈层 包含了模型大部分的处理能力。
前馈神经网络概览
一个简单的例子可以帮助直观理解前馈神经网络:假设我们将输入“The Shawshank”传递给语言模型,并期待它生成最可能的下一个词“Redemption”(参考1994年的电影《肖申克的救赎》)。
如图3-13所示,前馈神经网络(在模型的所有层中共同存在)是这种信息的来源。当模型成功训练了一个庞大的文本档案(其中包含了许多对“The Shawshank Redemption”的提及)时,它学会并存储了能够完成这一任务的信息和行为。
要成功训练一个大型语言模型(LLM),它需要记忆大量信息。但它并不仅仅是一个庞大的数据库。记忆只是令人印象深刻的文本生成的一个要素。模型能够使用相同的机制在数据点之间进行插值,并发现更复杂的模式,从而实现泛化——这意味着在它过去没见过的输入以及不包含在训练数据集中的情况下也能表现良好。
注意 当你使用一个现代商业LLM时,输出的结果与之前严格意义上提到的“语言模型”有所不同。比如,向像GPT-4这样的聊天LLM输入“The Shawshank”会生成这样的输出:
“The Shawshank Redemption 是一部1994年由Frank Darabont执导的电影,基于Stephen King的中篇小说《Rita Hayworth and Shawshank Redemption》改编。……等等。”
这是因为原始语言模型(如GPT-3)对普通用户来说难以有效利用。这就是为什么之后会对语言模型进行指令微调和基于人类偏好反馈的微调,以符合人们对模型输出的期望。
注意力层概览
在语言建模中,上下文至关重要。仅仅依靠简单的记忆和基于前一个词元的插值是有限的。我们知道这一点,因为在神经网络之前,这是构建语言模型的主要方法之一(参见Daniel Jurafsky和James H. Martin的《语音与语言处理》第3章“N-gram语言模型”)。
注意力机制是一种帮助模型在处理特定词元时整合上下文信息的机制。想想以下提示:
“The dog chased the squirrel because it”
为了让模型预测“it”之后的内容,它需要知道“it”指代什么。它是指“狗”还是“松鼠”?
在一个训练好的Transformer LLM中,注意力机制可以作出这一判断。注意力机制将上下文中的信息加入到“it”词元的表示中。图3-14展示了这种机制的简单版本。
模型基于从训练数据集中看到并学习到的模式来做出判断。也许之前的句子提供了更多线索,例如,提到狗时使用了“她(she)”,这就清楚地表明“it”指代的是松鼠。
注意力机制是关键
深入研究注意力机制是非常有价值的。注意力机制的最简化版本如图3-15所示。它展示了多个词元位置进入注意力层,最后一个是当前正在处理的位置(粉色箭头)。注意力机制在该位置的输入向量上运行,并将上下文中的相关信息整合到它为该位置生成的输出向量中。
注意力机制涉及两个主要步骤:
- 一种评分方法,用于评估之前每个输入词元与当前正在处理的词元(粉色箭头)之间的相关性。
- 根据这些评分,将来自各个位置的信息整合成一个输出向量。
图3-16展示了这两个步骤。
为了赋予Transformer更强大的注意力能力,注意力机制被复制并同时执行多次。每一次并行的注意力应用都被称为注意力头。这种方式增强了模型捕捉输入序列中复杂模式的能力,使其能够同时关注不同的模式。
图3-17展示了注意力头如何并行运行的直观过程,前一步是将信息分割,后一步是将所有注意力头的结果组合起来。
注意力的计算方式
让我们看看在单个注意力头内如何计算注意力。在开始计算之前,我们首先观察以下初始状态:
-
注意力层(在生成型LLM中)正在为一个位置处理注意力。
-
层的输入包括:
- 当前位置或词元的向量表示
- 之前词元的向量表示
目标是生成当前位置的新表示,包含之前词元的相关信息。例如,如果我们正在处理句子“Sarah fed the cat because it”中的最后一个位置“it”,我们希望“it”能够表示“cat”,因此注意力机制会将“cat”的相关信息引入“it”的表示中。
训练过程生成了三个投影矩阵,用于生成在计算中交互的组件:
- 查询投影矩阵(query projection matrix)
- 键投影矩阵(key projection matrix)
- 值投影矩阵(value projection matrix)
图3-18展示了在注意力计算开始之前,所有这些组件的初始状态。为简化起见,我们只看一个注意力头的计算,因为其他注意力头的计算是相同的,只是使用各自的投影矩阵。
注意力机制首先通过将输入与投影矩阵相乘,生成三个新的矩阵,分别称为查询矩阵(queries)、键矩阵(keys)和值矩阵(values)。这些矩阵将输入词元的信息投射到三个不同的空间,以帮助执行注意力机制的两个步骤:
- 相关性评分
- 信息整合
图3-19展示了这三个新矩阵,以及如何将每个矩阵的最底一行与当前位置关联,而上方的行则与之前的位置关联。
自注意力机制:相关性评分
在生成型Transformer中,我们一次生成一个词元,这意味着我们一次只处理一个位置。因此,注意力机制只关注当前位置,以及如何从其他位置提取信息来为该位置提供参考。
注意力机制的相关性评分步骤是通过将当前位置的查询向量与键矩阵相乘来完成的。这会生成一个分数,表示每个之前词元的相关性。通过softmax操作可以对这些分数进行归一化,使它们的总和为1。图3-20展示了这一计算得到的相关性评分。
自注意力机制:信息整合
现在我们已经得到了相关性评分,接下来将每个词元的值向量乘以该词元的分数。将这些结果向量相加,便得到该注意力步骤的输出,正如我们在图3-21中所见。
Transformer架构的最新改进
自从Transformer架构发布以来,针对其改进和创建更好模型的工作已经取得了显著进展。这些改进包括在更大数据集上的训练、对训练过程和学习率的优化,但也延伸到了架构本身。撰写本文时,许多原始Transformer的核心思想仍然保持不变。然而,有一些新的架构概念被证明非常有价值,它们提升了最新Transformer模型(如Llama 2)的性能。在本章的最后部分,我们将回顾一些Transformer架构的重要最新发展。
更高效的注意力机制
研究社区最关注的领域是Transformer的注意力层,这是因为注意力计算是整个过程中计算量最大的一部分。
局部/稀疏注意力
随着Transformer模型规模的不断扩大,稀疏注意力(“Generating long sequences with sparse transformers”)和滑动窗口注意力(“Longformer: The long-document transformer”)等想法提高了注意力计算的效率。稀疏注意力限制了模型可以关注的上下文范围,正如我们在图3-22中所见。
一种采用这种机制的模型是GPT-3,但它并不是对所有Transformer块都使用稀疏注意力——如果模型只能看到少量的前置词元,生成质量会大幅下降。GPT-3的架构交替使用了全注意力和高效注意力的Transformer块。因此,Transformer块在全注意力(如第1和第3块)和稀疏注意力(如第2和第4块)之间交替切换。
为了展示不同类型的注意力机制,可以参考图3-23,图中展示了不同注意力机制的工作方式。每个图显示了在处理当前词元(深蓝色)时,模型可以关注哪些之前的词元(浅蓝色)。
每一行对应一个正在处理的词元。颜色编码表示模型在处理深蓝色单元格中的词元时能够关注哪些词元。图3-24对此进行了更清晰的说明。
这张图还展示了解码器Transformer块的自回归特性(大多数文本生成模型由其构成);它们只能关注之前的词元。相比之下,BERT可以同时关注上下文(因此BERT中的B代表双向,即bidirectional)。
多查询和组查询注意力
一种最近的高效注意力优化是组查询注意力(“GQA: Training generalized multi-query transformer models from multi-head checkpoints”),被Llama 2和Llama 3等模型所使用。图3-25展示了这些不同类型的注意力,下一节将继续对此进行解释。
组查询注意力是基于多查询注意力(“Fast transformer decoding: One write-head is all you need”)的。这些方法通过减少参与计算的矩阵大小,提高了大型模型推理的可扩展性。
优化注意力:从多头到多查询再到组查询
本章前面我们展示了Transformer论文中描述的多头注意力。The Illustrated Transformer详细讨论了查询、键和值矩阵如何用于执行注意力操作。图3-26展示了如何为给定输入计算每个注意力头的独特查询、键和值矩阵。
多查询注意力的优化方式是共享所有注意力头的键和值矩阵。因此,每个头唯一的矩阵是查询矩阵,如图3-27所示。
然而,随着模型规模的增长,这种优化可能过于严格,影响模型质量。我们可以牺牲一点内存来提升模型的性能,这就是组查询注意力发挥作用的地方。它没有将键和值矩阵的数量削减到每个只使用一个,而是允许我们使用更多的矩阵(但仍少于注意力头的数量)。图3-28展示了这些组,每个注意力头组共享键和值矩阵的方式。
闪电注意力(Flash Attention)
闪电注意力是一种流行的方法和实现,能够显著加速Transformer大型语言模型(LLM)在GPU上的训练和推理。它通过优化在GPU的共享内存(SRAM)和高带宽内存(HBM)之间加载和传输的值,加快了注意力计算。详细介绍可以参考论文《FlashAttention: Fast and memory-efficient exact attention with IO-awareness》和后续的《FlashAttention-2: Faster attention with better parallelism and work partitioning》。
Transformer块
回想一下,Transformer块的两个主要组件是注意力层和前馈神经网络。更详细地观察这个块,还会揭示残差连接和层归一化操作,如图3-29所示。
撰写本文时,最新的Transformer模型仍然保留了主要组件,但做出了一些调整,如图3-30所示。
这个版本的Transformer块与原始版本的一个区别是,归一化操作发生在注意力层和前馈层之前。据报道,这可以减少训练时间(参见论文“On layer normalization in the Transformer architecture”)。另一个归一化的改进是使用RMSNorm,它比原始Transformer中使用的LayerNorm更简单且更高效(参见论文“Root mean square layer normalization”)。最后,取代原始Transformer中的ReLU激活函数,像SwiGLU这样的新变体(参见“GLU Variants Improve Transformer”)现在更加常见。
位置嵌入 (RoPE)
自原始Transformer模型以来,位置嵌入一直是其关键组件。它使模型能够跟踪序列/句子中词元/单词的顺序,这是语言中的重要信息来源。在过去几年中,提出了许多位置编码方案,其中旋转位置嵌入(或称“RoPE”,最早在“RoFormer: Enhanced Transformer with rotary position embedding”中提出)尤为值得注意。
原始Transformer论文及其早期的一些变体使用了绝对位置嵌入,基本上将第一个词元标记为位置1,第二个词元标记为位置2,依此类推。这些嵌入可以是静态方法(通过几何函数生成位置向量)或是学习得到的(模型训练过程中为其分配值)。当模型规模扩大时,这些方法会面临一些挑战,促使我们寻找提升效率的方法。
例如,使用大上下文进行有效训练时的一个挑战是,训练集中的许多文档远短于该上下文。将整个4K上下文分配给一条10个单词的短句是低效的。因此,在模型训练期间,不同文档被打包到每个训练批次的上下文中,如图3-31所示。
要了解更多关于文档打包的内容,可以阅读《Efficient sequence packing without cross-contamination: Accelerating large language models without impacting performance》并观看《Introducing packed BERT for 2X training speed-up in natural language processing》中的精彩视觉效果。
位置嵌入方法必须适应这些和其他实际考量。例如,如果文档50从位置50开始,那么如果我们告诉模型该文档的第一个词元是编号50的词元,这会给模型提供错误的信息,从而影响其性能(因为模型会假设有之前的上下文,而实际上之前的词元属于一个不同且不相关的文档,模型应该忽略它们)。
与在前向传播开始时添加的静态、绝对嵌入不同,旋转位置嵌入(RoPE) 是一种将位置信息编码的方法,既能捕捉绝对词元位置信息,也能捕捉相对位置信息。它基于在嵌入空间中旋转向量的思想。在前向传播过程中,这些嵌入在注意力步骤中被添加,如图3-32所示。
在注意力过程中,位置信息被特意混入查询矩阵和键矩阵中,紧接着我们对它们进行相乘以计算相关性评分,如图3-33所示。
其他架构实验和改进
关于Transformer的各种调整和改进不断被提出和研究。《A Survey of Transformers》总结了一些主要方向。Transformer架构也被不断适应于LLM之外的其他领域。计算机视觉是Transformer架构研究活跃的领域之一(参见《Transformers in vision: A survey》和《A survey on vision transformer》)。其他领域还包括机器人学(参见《Open X-Embodiment: Robotic learning datasets and RT-X models》)和时间序列(参见《Transformers in time series: A survey》)。
总结
在本章中,我们讨论了Transformer的主要直觉和使最新的Transformer LLMs得以实现的最新发展。我们介绍了许多新概念,以下是本章讨论的关键点:
- Transformer LLM一次生成一个词元。
- 生成的词元会附加到提示中,然后将更新的提示再次输入模型,进行另一轮前向传播以生成下一个词元。
- Transformer LLM的三个主要组件是分词器、Transformer块堆栈和语言建模头(LM head)。
- 分词器包含模型的词汇表。模型为这些词元关联了词元嵌入。将文本拆分为词元并使用这些词元的嵌入是词元生成过程的第一步。
- 前向传播流程依次经过所有阶段。
- 在流程接近尾声时,语言模型头会为下一个可能的词元评分。解码策略决定了选择哪个实际词元作为此生成步骤的输出(有时是最可能的词元,但并非总是如此)。
- Transformer表现优异的原因之一是它能够并行处理词元。每个输入词元进入各自的处理流。流的数量是模型的“上下文大小”,表示模型能够处理的最大词元数量。
- 由于Transformer LLMs通过循环逐个生成词元,因此缓存每一步的处理结果是个好主意,以避免重复计算(这些结果作为各种矩阵存储在层中)。
- 主要的处理发生在Transformer块中。它们由两个组件组成。一个是前馈神经网络,它可以存储信息并根据训练数据进行预测和插值。
- Transformer块的第二个主要组件是注意力层。注意力机制整合上下文信息,使模型能够更好地捕捉语言的细微差别。
- 注意力的过程分为两个主要步骤:(1)相关性评分和(2)信息整合。
- Transformer的注意力层同时进行多个注意力操作,每个操作发生在一个注意力头中,它们的输出汇总后构成注意力层的输出。
- 注意力可以通过在所有注意力头之间共享键和值矩阵,或在注意力头组之间共享来加速(组查询注意力)。
- 类似闪电注意力的方法通过优化GPU的不同内存系统上的操作,加速了注意力计算。
- Transformer在不同场景中持续得到新的发展和调整建议,包括语言模型和其他领域的应用。
在本书的第二部分,我们将介绍LLMs的一些实际应用。在第4章中,我们将从文本分类这一常见的语言AI任务开始。下一章将介绍如何应用生成模型和表示模型。