diff --git a/Book/2.处理文本数据.md b/Book/2.处理文本数据.md
index 8eb8edc..666bfba 100644
--- a/Book/2.处理文本数据.md
+++ b/Book/2.处理文本数据.md
@@ -45,7 +45,7 @@
> [!TIP]
>
-> 个人思考:这里聊一下检索增强技术(RAG),目前已经广泛应用于特定领域的知识问答场景。尽管GPT在文本生成任务重表现强大,但它们依赖的是预训练的知识,这以为着它们的回答依赖于模型在预训练阶段学习到的信息。这就导致了几个问题:
+> **个人思考:** 这里聊一下检索增强技术(RAG),目前已经广泛应用于特定领域的知识问答场景。尽管GPT在文本生成任务重表现强大,但它们依赖的是预训练的知识,这以为着它们的回答依赖于模型在预训练阶段学习到的信息。这就导致了几个问题:
>
> + **知识的有效性:** 模型的知识基于它的预训练数据,因此无法获取最新的信息。比如,GPT-3 的知识截止到 2021 年,无法回答最新的事件或发展。
> + **模型大小的限制:** 即使是大型模型,所能存储和运用的知识也是有限的。如果任务涉及特定领域(如医学、法律、科学研究),模型在预训练阶段可能没有涵盖足够的信息。
@@ -65,7 +65,7 @@
本章接下来的部分将系统地介绍准备 LLM 使用的嵌入所需的步骤,这些步骤包括将文本拆分为单词、将单词转换为token,以及将token转化为嵌入向量。
-
+
## 2.2 文本分词
@@ -73,9 +73,7 @@
-我们即将用于 LLM 训练的文本数据集是一部由 Edith Wharton 创作的短篇小说《判决》,该作品已在网上公开,因此允许用于 LLM 训练任务。该文本可在 Wikisource 上找到,网址是 https://en.wikisource.org/wiki/The_Verdict,您可以将其复制并粘贴到文本文件中。我已将其复制到名为 "the-verdict.txt" 的文本文件中,以便使用 Python 的标准文件读取工具进行加载。
-
-`#000051 Listing 2.1 Reading in a short story as text sample into Python`
+我们即将用于 LLM 训练的文本数据集是一部由 Edith Wharton 创作的短篇小说《判决》,该作品已在网上公开,因此允许用于 LLM 训练任务。该文本可在 Wikisource 上找到,网址是 [https://en.wikisource.org/wiki/The_Verdict](https://en.wikisource.org/wiki/The_Verdict),您可以将其复制并粘贴到文本文件中。我已将其复制到名为 "the-verdict.txt" 的文本文件中,以便使用 Python 的标准文件读取工具进行加载。
```python
# Listing 2.1 Reading in a short story as text sample into Python
@@ -85,5 +83,238 @@ print("Total number of character:", len(raw_text))
print(raw_text[:99])
```
+另外,您可以在本书的 GitHub 仓库中找到名为 "the-verdict.txt" 的文件,网址是 [https://github.com/rasbt/LLMs-from-scratch/tree/main/ch02/01_main-chapter-code](https://github.com/rasbt/LLMs-from-scratch/tree/main/ch02/01_main-chapter-code)
+便于演示的目的,print命令输出文件的总字符数以及前100个字符。
+
+```
+Total number of character: 20479
+I HAD always thought Jack Gisburn rather a cheap genius--though a good fellow enough--so
+it was no
+```
+
+我们的目标是将这篇 20,479 个字符的短篇小说拆分为单词和特殊字符,然后在接下来的章节中将这些token转换为 LLM 训练所需的嵌入。
+
+> [!NOTE]
+>
+> **样本规模**
+>
+> 请注意,在处理 LLM 时,通常会处理数百万篇文章和数十万本书——也就是几 GB 的文本。然而,为了教学目的,使用像单本书这样的小文本样本就足够了,这样可以阐明文本处理步骤的主要思想,并能够在消费级硬件上合理地运行。
+
+要如何做才能最好地拆分这段文本以获得token列表呢?为此,我们来进行一个小小的探讨,使用 Python 的正则表达式库 re 进行说明。(请注意,您不需要学习或记住任何正则表达式语法,因为在本章后面我们将使用一个预构建的分词器。)
+
+使用一些简单的示例文本,我们可以使用 re.split 命令,按照以下语法拆分文本中的空白字符:
+
+```python
+import re
+text = "Hello, world. This, is a test."
+result = re.split(r'(\s)', text)
+print(result)
+```
+
+执行结果是一个包含单词、空白和标点符号的列表:
+
+```
+['Hello,', ' ', 'world.', ' ', 'This,', ' ', 'is', ' ', 'a', ' ', 'test.']
+```
+
+请注意,上述简单的分词方案仅仅用于将示例文本拆分为单个单词,然而有些单词仍然与我们希望单独列出的标点符号相连。我们也无需将所有文本转换为小写字母,因为大写字母有助于 LLM 区分专有名词和普通名词,理解句子结构,并学习生成正确的大写文本。
+
+让我们修改正则表达式,将空白字符(\s)、逗号和句点([,.])单独拆分出来:
+
+```python
+result = re.split(r'([,.]|\s)', text)
+print(result)
+```
+
+我们可以看到,单词和标点符号现在已经成为单独一项,跟我们预期一致:
+
+```
+['Hello', ',', '', ' ', 'world', '.', '', ' ', 'This', ',', '', ' ', 'is', ' ', 'a', ' ', 'test', '.', '']
+```
+
+一个剩余的小问题是列表仍然包含空白字符。我们可以安全地按如下方式删除这些多余的字符:
+
+```python
+result = [item for item in result if item.strip()]
+print(result)
+```
+
+```
+['Hello', ',', 'world', '.', 'This', ',', 'is', 'a', 'test', '.']
+```
+
+> [!NOTE]
+>
+> **关于是否删除空白字符的探讨**
+>
+> 在开发一个简单的分词器时,是否将空白字符编码为单独的字符,或者直接将其删除,取决于我们的应用和需求。删除空白字符可以减少内存和计算资源的消耗。然而,如果我们训练的模型对文本的确切结构敏感(例如,Python 代码对缩进和空格非常敏感),那么保留空白字符就很有用。在这里,为了简化和缩短分词化输出,我们选择删除空白字符。稍后,我们将切换到一个包含空白字符的分词化方案。
+
+我们上面设计的分词方案在简单的示例文本中表现良好。让我们进一步修改它,使其能够处理其他类型的标点符号,如问号、引号,以及在 Edith Wharton 短篇小说的前 100 个字符中看到的双破折号,还有其他特殊字符:
+
+```python
+text = "Hello, world. Is this-- a test?"
+result = re.split(r'([,.:;?_!"()\']|--|\s)', text)
+result = [item.strip() for item in result if item.strip()]
+print(result)
+```
+
+执行后输出如下:
+
+```
+['Hello', ',', 'world', '.', 'Is', 'this', '--', 'a', 'test', '?']
+```
+
+如图 2.5 所示,我们的分词方案现在能够成功处理文本中的各种特殊字符。
+
+
+
+现在我们已经有了一个基本的分词器,接下来让我们将其应用于艾迪丝·沃顿的整篇短篇小说:
+
+```python
+preprocessed = re.split(r'([,.:;?_!"()\']|--|\s)', raw_text)
+preprocessed = [item.strip() for item in preprocessed if item.strip()]
+print(len(preprocessed))
+```
+
+上述代码的输出是4690,这是小说的token数量(不包含空白字符)。
+
+让我们检查一下前30个token:
+
+```python
+print(preprocessed[:30])
+```
+
+生成的输出显示,我们的分词器似乎很好地处理了文本,因为所有单词和特殊字符都被很好地分开了:
+
+```
+['I', 'HAD', 'always', 'thought', 'Jack', 'Gisburn', 'rather', 'a', 'cheap', 'genius', '--', 'though', 'a', 'good', 'fellow', 'enough', '--', 'so', 'it', 'was', 'no', 'great', 'surprise', 'to', 'me', 'to', 'hear', 'that', ',', 'in']
+```
+
+
+
+## 2.3 将 tokens 转换为token IDs
+
+在前一章节中,我们将艾迪丝·华顿的短篇小说分词为单独的token。在本节中,我们将把这些token从字符串转换为整形,以生成所谓的token ID。这一步是将token ID 转换为嵌入向量的中间步骤。
+
+为了将先前生成的token映射到token ID,我们首先需要构建一个词汇表。这个词汇表定义了每个独特单词和特殊字符与唯一整数的映射,如图 2.6 所示。
+
+
+
+在前一章节中,我们将艾迪丝·华顿的短篇小说进行分词,并将其存储在名为 preprocessed 的 Python 变量中。现在,让我们创建一个包含所有唯一token的列表,并按字母顺序对其进行排序,以确定词汇表的大小:
+
+```python
+all_words = sorted(set(preprocessed))
+vocab_size = len(all_words)
+print(vocab_size)
+```
+
+在通过上述代码确定词汇表的大小为 1,130 后,我们通过以下代码创建词汇表并打印其前 51 个条目以便于说明:
+
+```python
+vocab = {token:integer for integer,token in enumerate(all_words)}
+for i, item in enumerate(vocab.items()):
+ print(item)
+ if i > 50:
+ break
+```
+
+输出如下:
+
+```
+('!', 0)
+('"', 1)
+("'", 2)
+...
+('Her', 49)
+('Hermia', 50)
+```
+
+根据输出可知,词汇表包含了与唯一整数标签相关联的单个token。我们接下来的目标是利用这个词汇表,将新文本转换为token ID,如图 2.7 所示。
+
+
+
+在本书后面,当我们想将 LLM 的输出从数字转换回文本时,我们还需要一种将token ID 转换为文本的方法。为此,我们可以创建一个词汇表的反向版本,将token ID 映射回相应的文本token。
+
+让我们在 Python 中实现一个完整的分词器类,其中包含一个 encode 方法,该方法将文本拆分为token,并通过词汇表进行token字符串到整数(token ID)的映射,以通过词汇表生成token ID。此外,我们还将实现一个 decode 方法,该方法进行整数到字符串的反向映射,将token ID 转换回文本。
+
+该分词器的代码实现如下:
+
+```python
+# Listing 2.3 Implementing a simple text tokenizer
+class SimpleTokenizerV1:
+ def __init__(self, vocab):
+ self.str_to_int = vocab #A
+ self.int_to_str = {i:s for s,i in vocab.items()} #B
+
+ def encode(self, text): #C
+ preprocessed = re.split(r'([,.?_!"()\']|--|\s)', text)
+ preprocessed = [item.strip() for item in preprocessed if item.strip()]
+ ids = [self.str_to_int[s] for s in preprocessed]
+ return ids
+
+ def decode(self, ids): #D
+ text = " ".join([self.int_to_str[i] for i in ids])
+
+ text = re.sub(r'\s+([,.?!"()\'])', r'\1', text) #E
+ return text
+
+
+#A 将词汇表作为类属性存储,以方便在 encode 和 decode 方法中访问
+#B 创建一个反向词汇表,将token ID 映射回原始的文本token
+#C 将输入文本转换为token ID
+#D 将token ID 还原为文本
+#E 在指定的标点符号前去掉空格
+```
+
+使用上述的 SimpleTokenizerV1 Python 类,我们现在可以使用现有的词汇表实例化新的分词器对象,并利用这些对象对文本进行编码和解码,如图 2.8 所示。
+
+
+
+让我们通过 SimpleTokenizerV1 类实例化一个新的分词器对象,并对艾迪丝·华顿的短篇小说中的一段文本进行分词,以便在实践中进行尝试:
+
+```python
+tokenizer = SimpleTokenizerV1(vocab)
+text = """"It's the last he painted, you know," Mrs. Gisburn said with pardonable pride."""
+ids = tokenizer.encode(text)
+print(ids)
+```
+
+上面的代码打印出以下token ID:
+
+下来,让我们看看能否通过 decode 方法将这些token ID 转换回文本:
+
+```python
+print(tokenizer.decode(ids))
+```
+
+输出如下:
+
+```
+'" It\' s the last he painted, you know," Mrs. Gisburn said with pardonable pride.'
+```
+
+根据以上的输出,我们可以看到 decode 方法成功将token ID 转换回了原始文本。
+
+到目前为止,一切都很顺利。我们实现了一个分词器,能够根据训练集中的片段对文本进行分词和去分词。现在让我们将其应用于训练集中未包含的新文本样本:
+
+```python
+text = "Hello, do you like tea?"
+print(tokenizer.encode(text))
+```
+
+执行上述代码将导致以下错误:
+
+```
+...
+KeyError: 'Hello'
+```
+
+问题在于短篇小说《裁决》中没有使用“Hello”这个词。因此,它不包含在词汇中。这突显了在处理大型语言模型时,需要考虑大型和多样化的训练集以扩展词汇的必要性。
+
+在下一节中,我们将进一步测试分词器在包含未知词汇的文本上的表现,并且我们还将讨论可以用于在训练期间为LLM提供更多上下文的额外特殊tokens。
+
+
+
+## 2.4 添加特殊上下文tokens
diff --git a/Image/chapter2/figure2.5.png b/Image/chapter2/figure2.5.png
new file mode 100644
index 0000000..abbe618
Binary files /dev/null and b/Image/chapter2/figure2.5.png differ
diff --git a/Image/chapter2/figure2.6.png b/Image/chapter2/figure2.6.png
new file mode 100644
index 0000000..00e037b
Binary files /dev/null and b/Image/chapter2/figure2.6.png differ
diff --git a/Image/chapter2/figure2.7.png b/Image/chapter2/figure2.7.png
new file mode 100644
index 0000000..59fb5de
Binary files /dev/null and b/Image/chapter2/figure2.7.png differ
diff --git a/Image/chapter2/figure2.8.png b/Image/chapter2/figure2.8.png
new file mode 100644
index 0000000..facaa2b
Binary files /dev/null and b/Image/chapter2/figure2.8.png differ
diff --git a/Image/image2.8.png b/Image/image2.8.png
new file mode 100644
index 0000000..e1ffee2
Binary files /dev/null and b/Image/image2.8.png differ