接下来,我们将把这些词元从 Python 字符串转换为整数表示,以生成 词元 ID (token ID)。这一过程是将词元 ID 转换为嵌入向量前的必经步骤。
为了将先前生成的词元映射到词元 ID,首先需要构建一张词汇表。这张词汇表定义了如何将每个唯一的单词和特殊字符映射到一个唯一的整数,如图2-6 所示。
图2-6 构建词汇表的过程。首先将训练集中的全部文本分词成独立的词元;然后将这些词元按字母顺序进行排列,并删除重复的词元;接下来将唯一的词元聚合到一张词汇表中,该词汇表定义了每个唯一的词元到唯一的整数值的映射。为简单起见,这里所展示的词汇表特意设置得很小,并且不包含标点符号和特殊字符
现在我们已经完成了短篇小说
The Verdict
的分词,并将结果存储在名为
preprocessed
的 Python 变量中。接下来,我们将创建一个包含所有唯一词元的列表,并将它们按照字母顺序排列,以确定词汇表的大小:
all_words = sorted(set(preprocessed)) vocab_size = len(all_words) print(vocab_size)
通过运行上述代码可以确定词汇表的大小为 1130。
随后,我们创建词汇表,并打印该词汇表的前 51 个条目作为示例,如代码清单 2-2 所示。
代码清单 2-2 创建词汇表
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)
如你所见,字典包含着许多独立的词元,它们均与唯一的整数标签相关联。
我们的下一个目标是使用这张词汇表将新文本转换为词元 ID,如图2-7 所示。
图2-7 从头开始对新的文本样本进行分词,并利用词汇表将文本词元转换为词元 ID。这张词汇表是基于整个训练集构建的,不仅可以应用于训练集本身,也适用于任何新的文本样本。为简单起见,这里所展示的词汇表不包含标点符号和特殊字符
为了将大语言模型的输出从数值形式转换回文本,还需要一种将词元 ID 转换为文本的方法。为此,可以创建逆向词汇表,将词元 ID 映射回它们对应的文本词元。
让我们在 Python 中实现一个完整的分词器类。此类包含一个用于将文本分词的
encode
方法,并通过词汇表将字符串映射到整数,以生成词元 ID。此外,我们还将实现一个
decode
方法,执行从整数到字符串的反向映射,将词元 ID 还原回文本。该分词器的实现代码如代码清单 2-3 所示。
代码清单 2-3 实现简单的文本分词器
使用
SimpleTokenizerV1
类,现在可以利用已有的词汇表实例化新的分词器对象,然后再使用这些对象对文本进行编码和解码,如图2-8 所示。
图2-8 分词器通常包含两个常见的方法:
encode
方法和
decode
方法。
encode
方法接收文本样本,将其分词为单独的词元,然后再利用词汇表将词元转换为词元 ID。而
decode
方法接收一组词元 ID,将其转换回文本词元,并将文本词元连接起来,形成自然语言文本
让我们创建一个
SimpleTokenizerV1
类的分词器实例对象,并将其应用于短篇小说
The Verdict
中的一段文本,试试看分词器的实际效果:
tokenizer = SimpleTokenizerV1(vocab) text = """"It's the last he painted, you know," Mrs. Gisburn said with pardonable pride.""" ids = tokenizer.encode(text) print(ids)
运行上述代码,将打印以下词元 ID:
[1, 56, 2, 850, 988, 602, 533, 746, 5, 1126, 596, 5, 1, 67, 7, 38, 851, 1108, 754, 793, 7]
接下来,试试
decode
方法能否将这些词元 ID 转换回文本:
print(tokenizer.decode(ids))
输出的文本如下所示:
'" It\' s the last he painted, you know," Mrs. Gisburn said with pardonable pride.'
根据上面的输出,可以观察到,
decode
方法成功地将词元 ID 转换回了原始文本。
到目前为止,进展非常顺利。我们实现了一个能够基于训练集对文本进行分词和反分词的分词器。现在,将这个分词器应用于训练集之外的新样本:
text = "Hello, do you like tea?" print(tokenizer.encode(text))
执行上述代码将导致以下错误:
KeyError: 'Hello'
问题在于,“Hello”这一单词并未在短篇小说 The Verdict 中出现,因此没有被收录到词汇表中。这一现象凸显了在处理大语言模型时,使用规模更大且更多样化的训练集来扩展词汇表的必要性。
在 2.4 节中,我们将在包含未知单词的文本上进一步测试分词器的表现,并探讨可以在训练过程中为大语言模型提供更多上下文信息的特殊词元。