update with img path

This commit is contained in:
Ning Guo
2025-07-27 21:30:50 +08:00
parent 0423e97532
commit 0bddde0db1
9 changed files with 420 additions and 140 deletions

View File

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