mirror of
https://github.com/skindhu/Build-A-Large-Language-Model-CN.git
synced 2026-07-29 15:20:24 +08:00
Revert "REAEME 文件中,增加「在线阅读」地址,并且,所有图片设置为居中。"
This commit is contained in:
@@ -25,15 +25,13 @@
|
||||
|
||||
|
||||
|
||||
在上一章中,我们介绍了大语言模型(LLM)的基本结构,并了解到 LLM 用海量文本数据集进行`预训练`。我们特别关注仅用**解码器**(Transformer 架构下)的 LLM,这也是 ChatGPT 和其他流行 GPT 的 LLM 所依赖的模型。
|
||||
在上一章中,我们介绍了大语言模型(LLM)的基本结构,并了解到它们会基于海量的文本数据集进行预训练。我们特别关注的是仅使用通用 Transformer 架构中解码器部分的 LLM,这也是 ChatGPT 和其他流行的类似 GPT 的 LLM 所依赖的模型。
|
||||
|
||||
在**预训练**阶段,LLM 逐字处理文本。通过**预测下一个单词任务**,来训练出拥有数百万到数十亿参数的 LLM,最终生成的模型具有出色的能力。随后可以进一步微调模型,以遵循指令或执行特定目标任务。然而,在我们接下来几章中实现和训练 LLM 之前,我们需要准备训练数据集,这也是本章的重点,如图 2.1 所示。
|
||||
在预训练阶段,LLM 逐字处理文本。通过使用下一个单词预测任务训练拥有数百万到数十亿参数的 LLM,最终能够生成具有出色能力的模型。这些模型随后可以进一步微调,以遵循指令或执行特定目标任务。然而,在我们接下来几章中实现和训练 LLM 之前,我们需要准备训练数据集,这也是本章的重点,如图 2.1 所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.1.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.1.png" width="75%" />
|
||||
|
||||
在本章中,您将学习如何为训练 LLM 准备输入文本。这包括将文本拆分为单个单词和子词token,并将这些token编码为 LLM 的向量表示。您还将了解一些先进的token分割方案,比如字节对编码,流行 LLM 中常用此类优化后的方案。最后,我们将实现一个采样和数据加载策略,以生成后续章节中训练 LLM 所需的输入输出数据对。
|
||||
在本章中,您将学习如何为训练 LLM 准备输入文本。这包括将文本拆分为单个单词和子词token,并将这些token编码为 LLM 的向量表示。您还将了解一些先进的token分割方案,比如字节对编码,这种方法在像 GPT 这样的流行 LLM 中得到应用。最后,我们将实现一个采样和数据加载策略,以生成后续章节中训练 LLM 所需的输入输出数据对。
|
||||
|
||||
|
||||
|
||||
@@ -43,9 +41,7 @@
|
||||
|
||||
将数据转换为向量格式的过程通常被称为嵌入(Embedding)。我们可以通过特定的神经网络层或其他预训练的神经网络模型来对不同类型的数据进行嵌入,比如视频、音频和文本,如图 2.2 所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.2.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.2.png" width="75%" />
|
||||
|
||||
如图 2.2 所示,我们可以使用嵌入模型来处理多种不同的数据格式。然而,需要注意的是,不同的数据格式需要使用不同的嵌入模型。例如,专为文本设计的嵌入模型并不适用于音频或视频数据的嵌入。
|
||||
|
||||
@@ -76,9 +72,7 @@
|
||||
|
||||
生成单词嵌入的算法和框架有很多。其中,Word2Vec是较早且最受欢迎的项目之一。Word2Vec通过预测给定目标词的上下文或反之,训练神经网络架构以生成单词嵌入。Word2Vec的核心思想是,出现在相似上下文中的词通常具有相似的含义。因此,当将单词投影到二维空间进行可视化时,可以看到相似的词汇聚在一起,如图2.3所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.3.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.3.png" width="75%" />
|
||||
|
||||
词嵌入可以具有不同的维度,从一维到数千维。如图2.3所示,我们可以选择二维词嵌入进行可视化。更高的维度可能捕捉到更细微的关系,但代价是计算效率的降低。
|
||||
|
||||
@@ -94,9 +88,7 @@
|
||||
|
||||
本节将讨论如何将输入文本拆分为单个token,这是创建 LLM 嵌入所需的预处理步骤。这些token可以是单个单词或特殊字符,包括标点符号,具体如图 2.4 所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.4.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.4.png" width="75%" />
|
||||
|
||||
我们即将用于 LLM 训练的文本数据集是一部由 Edith Wharton 创作的短篇小说《判决》,该作品已在网上公开,因此允许用于 LLM 训练任务。该文本可在 Wikisource 上找到,网址是 [https://en.wikisource.org/wiki/The_Verdict](https://en.wikisource.org/wiki/The_Verdict),您可以将其复制并粘贴到文本文件中。我已将其复制到名为 "the-verdict.txt" 的文本文件中,以便使用 Python 的标准文件读取工具进行加载。
|
||||
|
||||
@@ -192,9 +184,7 @@ print(result)
|
||||
|
||||
如图 2.5 所示,我们的分词方案现在能够成功处理文本中的各种特殊字符。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.5.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.5.png" width="75%" />
|
||||
|
||||
现在我们已经有了一个基本的分词器,接下来让我们将其应用于艾迪丝·沃顿的整篇短篇小说:
|
||||
|
||||
@@ -226,9 +216,7 @@ print(preprocessed[:30])
|
||||
|
||||
为了将先前生成的token映射到token ID,我们首先需要构建一个词汇表。这个词汇表定义了每个独特单词和特殊字符与唯一整数的映射,如图 2.6 所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.6.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.6.png" width="75%" />
|
||||
|
||||
在前一章节中,我们将艾迪丝·华顿的短篇小说进行分词,并将其存储在名为 preprocessed 的 Python 变量中。现在,让我们创建一个包含所有唯一token的列表,并按字母顺序对其进行排序,以确定词汇表的大小:
|
||||
|
||||
@@ -261,9 +249,7 @@ for i, item in enumerate(vocab.items()):
|
||||
|
||||
根据输出可知,词汇表包含了与唯一整数标签相关联的单个token。我们接下来的目标是利用这个词汇表,将新文本转换为token ID,如图 2.7 所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.7.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.7.png" width="75%" />
|
||||
|
||||
在本书后面,当我们想将 LLM 的输出从数字转换回文本时,我们还需要一种将token ID 转换为文本的方法。为此,我们可以创建一个词汇表的反向版本,将token ID 映射回相应的文本token。
|
||||
|
||||
@@ -300,9 +286,7 @@ class SimpleTokenizerV1:
|
||||
|
||||
使用上述的 SimpleTokenizerV1 Python 类,我们现在可以使用现有的词汇表实例化新的分词器对象,并利用这些对象对文本进行编码和解码,如图 2.8 所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.8.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.8.png" width="75%" />
|
||||
|
||||
让我们通过 SimpleTokenizerV1 类实例化一个新的分词器对象,并对艾迪丝·华顿的短篇小说中的一段文本进行分词,以便在实践中进行尝试:
|
||||
|
||||
@@ -358,15 +342,11 @@ KeyError: 'Hello'
|
||||
|
||||
具体来说,我们将修改在前一节中实现的词汇表和分词器类(修改后的类命名为SimpleTokenizerV2),以支持两个新的token:<|unk|> 和 <|endoftext|>,具体见图 2.9。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.9.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.9.png" width="75%" />
|
||||
|
||||
如图2.9所示,我们可以修改分词器,以便在遇到不在词汇表中的单词时使用一个<|unk|> token。此外,我们还会在不相关的文本之间添加一个特殊的<|endoftext|> token。例如,在对多个独立文档或书籍进行GPT类大语言模型的训练时,通常会在每个文档或书籍之前插入一个token,以连接前一个文本源,如图2.10所示。这有助于大语言模型理解,尽管这些文本源在训练中是连接在一起的,但它们实际上是无关的。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.10.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.10.png" width="75%" />
|
||||
|
||||
现在,让我们修改词汇表,将这两个特殊token <|unk|> 和 <|endoftext|> 包含在内,方法是将它们添加到我们在上一节中创建的唯一单词列表中:
|
||||
|
||||
@@ -553,9 +533,7 @@ print(strings)
|
||||
|
||||
BPE背后的算法将不在其预定义词汇表中的单词分解为更小的子词单元甚至单个字符,使其能够处理超出词汇表的单词。因此,得益于BPE算法,如果分词器在分词过程中遇到一个不熟悉的单词,它可以将其表示为一系列子词token或字符,如图2.11所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.11.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.11.png" width="75%" />
|
||||
|
||||
如图 2.11 所示,将未知单词分解为单个字符的能力确保了分词器以及随之训练的 LLM 能够处理任何文本,即使文本中包含训练数据中不存在的单词。
|
||||
|
||||
@@ -607,9 +585,7 @@ BPE背后的算法将不在其预定义词汇表中的单词分解为更小的
|
||||
|
||||
这些输入-目标对是什么样的呢?正如我们在第一章中所学,LLM通过预测文本中的下一个单词进行预训练,如图2.12所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.12.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.12.png" width="75%" />
|
||||
|
||||
在本节中,我们将实现一个数据加载器,通过滑动窗口方法从训练数据集中提取图 2.12 所示的输入-目标对。
|
||||
|
||||
@@ -694,9 +670,7 @@ and established himself in ----> a
|
||||
|
||||
具体来说,我们的目标是返回两个张量:一个输入张量,包括 LLM 看到的文本,另一个目标张量,包含 LLM 需要预测的目标,如图 2.13 所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.13.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.13.png" width="75%" />
|
||||
|
||||
虽然图2.13展示了字符串格式的token以供说明,但代码实现将直接操作token ID,因为 BPE 分词器的 encode 方法将分词和转换为token ID 的过程合并为了一个步骤。
|
||||
|
||||
@@ -800,9 +774,7 @@ print(second_batch)
|
||||
|
||||
如果我们将第一个批次与第二个批次进行比较,可以看到第二个批次的token ID 相较于第一个批次右移了一个位置(例如,第一个批次输入中的第二个 ID 是 367,而它是第二个批次输入的第一个 ID)。步幅设置决定了输入在批次之间移动的位置数,模拟了滑动窗口的方法,如图 2.14 所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.14.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.14.png" width="75%" />
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
@@ -853,9 +825,7 @@ Targets:
|
||||
|
||||
为 LLM 准备训练集的最后一步是将token ID 转换为嵌入向量,如图 2.15 所示,这将是本章最后两部分的主要内容。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.15.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.15.png" width="75%" />
|
||||
|
||||
除了图 2.15 中概述的过程外,还需注意的是,我们首先会以随机值初始化这些嵌入权重。这一初始化为 LLM 的学习过程提供了起始点。我们将在第 5 章中优化嵌入权重,作为 LLM 训练的一部分。
|
||||
|
||||
@@ -869,7 +839,7 @@ Targets:
|
||||
>
|
||||
> GPT 类模型(以及其他深度神经网络)是基于大量的矩阵运算和数值计算构建的,尤其是神经元之间的连接权重和偏置在训练过程中不断更新。这些运算要求输入的数据是**数值形式的向量**,因为神经网络只能对数值数据进行有效计算,而无法直接处理原始的离散文字数据(如单词、句子)。
|
||||
>
|
||||
> + **向量表示**:通过将每个单词、句子或段落转换为连续向量(Embedding),可以在高维空间中表示文本的语义关系。例如,通过词嵌入(如 Word2Vec、GloVe)或上下文嵌入(如 GPT 中的词嵌入层),每个单词都被转换为一个向量,这个向量可以用于神经网络的计算。
|
||||
> + **向量表示: **通过将每个单词、句子或段落转换为连续向量(Embedding),可以在高维空间中表示文本的语义关系。例如,通过词嵌入(如 Word2Vec、GloVe)或上下文嵌入(如 GPT 中的词嵌入层),每个单词都被转换为一个向量,这个向量可以用于神经网络的计算。
|
||||
>
|
||||
> 2. **向量嵌入的作用**
|
||||
>
|
||||
@@ -957,9 +927,7 @@ tensor([[ 1.2753, -0.2010, -0.1606],
|
||||
|
||||
输出矩阵中的每一行都是通过从嵌入权重矩阵进行查找操作获得的,如图2.16所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.16.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.16.png" width="75%" />
|
||||
|
||||
本节介绍了如何从token ID 创建嵌入向量。本章的下一节也是最后一节将对这些嵌入向量进行小的修改,以编码文本中token的位置信息。
|
||||
|
||||
@@ -971,17 +939,13 @@ tensor([[ 1.2753, -0.2010, -0.1606],
|
||||
|
||||
之前引入的嵌入层的工作方式是,无论token ID 在输入序列中的位置如何,相同的token ID 始终映射到相同的向量表示,如图 2.17 所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.17.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.17.png" width="75%" />
|
||||
|
||||
从原则上讲,确定性的、与位置无关的token ID 嵌入对于可重复性是有益的。然而,由于LLM的自注意力机制本身也是与位置无关的,因此向LLM注入额外的位置信息是有帮助的。
|
||||
|
||||
绝对位置嵌入与序列中的特定位置直接相关。对于输入序列中的每个位置,都会将一个唯一的绝对位置嵌入向量添加到token的嵌入向量中,以传达其确切位置。例如,第一个token将具有特定的位置嵌入,第二个token将具有另一个不同的嵌入,依此类推,如图2.18所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.18.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.18.png" width="75%" />
|
||||
|
||||
与关注token在序列中的绝对位置不同,相对位置嵌入强调的是token之间的相对位置或距离。这意味着模型学习的是“相隔多远”的关系,而不是“在什么确切位置”。这样的优势在于,即使模型在训练时没有接触过不同的长度,它也可以更好地适应各种长度的序列。
|
||||
|
||||
@@ -1076,9 +1040,7 @@ torch.Size([8, 4, 256])
|
||||
|
||||
我们创建的 input_embeddings,如图 2.19 所示,现在可作为LLM的核心模块的输入嵌入。我们将在第3章开始实现这些模块。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter2/figure2.19.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter2/figure2.19.png" width="75%" />
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user