购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.2 文本分词

本节将介绍如何将输入文本分割为独立的词元,这是为大语言模型生成嵌入向量所必需的预处理步骤。词元既可以是单个单词,也可以是包括标点符号在内的特殊字符,如图2-4 所示。

图2-4 大语言模型中文本处理步骤概览。在这一步骤图中,我们将输入文本分割成了单独的词元,这些词元既可以是单词,也可以是诸如标点符号之类的特殊字符

为了训练大语言模型,我们将使用 Edith Wharton 的短篇小说 The Verdict 作为分词文本。由于这部小说已公开发表,因此可以用于大语言模型的训练。该文本可以在维基文库中找到,你可以将其复制并粘贴到文本文件中。我已经将其复制到了一个名为 the-verdict.txt 的文本文件中。

此外,你也可以在本书的 GitHub 仓库中找到 the-verdict.txt 文件,然后可以使用以下 Python 代码下载该文件。

import urllib.request
url = ("https://raw.githubusercontent.com/rasbt/"
       "LLMs-from-scratch/main/ch02/01_main-chapter-code/"
       "the-verdict.txt")
file_path = "the-verdict.txt"
urllib.request.urlretrieve(url, file_path)

接下来,可以使用 Python 的标准文件读取工具来加载 the-verdict.txt 文件,如代码清单 2-1 所示。

代码清单 2-1 通过 Python 读取短篇小说 The Verdict 作为文本样本

with open("the-verdict.txt", "r", encoding="utf-8") as f:
    raw_text = f.read()
print("Total number of character:", len(raw_text))
print(raw_text[:99])

打印( 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 个字符的短篇小说分割为独立的单词和特殊字符,以便在后续章节中将它们转换为嵌入向量,进而用于大语言模型训练。

注意 构建大语言模型需要处理数百万篇文章和成千上万本图书,通常多达数千兆字节(GB)数据。不过,出于教学目的,使用小规模的文本样本(如一本书)就足以说明文本处理步骤的核心思想,并且可以在合理时间内在消费级硬件上完成运行。

为了获取词元列表,应该如何有效地分割这段文本?为此,我们将进行一个小练习,使用 Python 的正则表达式库 re 进行说明。(你无须学习或记忆任何正则表达式语法,因为稍后我们将转而使用预构建的分词器。)

以简单的文本为例,通过使用 re.split 命令并配合适当的语法,我们可以按照空白字符分割文本:

import re
text = "Hello, world. This, is a test."
result = re.split(r'(\s)', text)
print(result)

运行代码得到的结果是一个包含单个单词、空白字符和标点符号的列表:

['Hello,', ' ', 'world.', ' ', 'This,', ' ', 'is', ' ', 'a', ' ', 'test.']

在大部分情况下,这种简单的分词方法能够将文本分割为单独的单词。但问题在于,一些单词仍然与标点符号相连,而我们希望这些标点符号作为单独的列表项。此外,我们没有将所有文本都转换为小写,因为大写形式有助于大语言模型区分专有名词和普通名词、更好地理解句子结构,并学会正确地生成大写字母。

接下来修改正则表达式,使其在空白字符( \s )、逗号和句号( [,.] )处进行分割:

result = re.split(r'([,.]|\s)', text)
print(result)

可以看到,我们如愿地将单词和标点符号分割成了独立的列表项:

['Hello', ',', '', ' ', 'world', '.', '', ' ', 'This', ',', '', ' ', 'is',
' ', 'a', ' ', 'test', '.', '']

一个小问题是列表中仍然包含空白字符。可以通过以下方法安全地删除这些冗余字符:

result = [item for item in result if item.strip()]
print(result)

去除空白字符后的输出如下所示。

['Hello', ',', 'world', '.', 'This', ',', 'is', 'a', 'test', '.']

注意 在开发简易分词器时,是将空白字符单独编码还是直接移除,取决于具体的应用场景和需求。移除空白字符可以减轻内存和计算的负担。然而,如果训练的模型需要对文本的精确结构保持敏感,那么保留空白字符就显得尤为重要(例如,Python 代码对缩进和空格具有高敏感性)。为了简化和缩短分词的输出,我们暂时选择移除空白字符。稍后,我们将改为采用保留空白字符的分词方案。

我们设计的分词方法在处理简单文本时表现良好。接下来,让我们再修改一下,使其能够处理其他类型的标点符号,比如问号、引号,以及短篇小说 The Verdict 的前 100 个字符中出现的双破折号等特殊字符:

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 所示,我们的分词方法现在可以成功处理文本中的各种特殊字符。

图2-5 我们的分词方法现已成功实现了将文本分割为单个单词和标点符号,这里文本被分割成 10 个独立的词元

现在,我们已经构建了一个简易分词器,让我们将其应用于短篇小说 The Verdict 的全文:

preprocessed = re.split(r'([,.:;?_!"()\']|--|\s)', raw_text)
preprocessed = [item.strip() for item in preprocessed if item.strip()]
print(len(preprocessed))

上述打印语句的输出是 4690 ,这是该文本的词元数量(不包括空白字符)。为了快速查看分词效果,可以打印前 30 个词元:

print(preprocessed[:30])

结果显示,分词器似乎很好地处理了文本,因为所有单词和特殊字符都被整齐地分开了。 I9puqKTmsatejcprhjT1OICUK7sV+EbjaEF5Fdn7Nie7aUfFegUiIxyjIRRc/bJL

['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']
点击中间区域
呼出菜单
上一章
目录
下一章
×