在自然语言处理(NLP)的领域中,"大模型"如同一位精通语言的大师,能够理解并生成各种文本。这些模型并非生来就具备这种能力,而是依赖于大量的训练和精心设计的数据预处理流程。在这一过程中,分词器扮演着核心角色,对于文本预处理至关重要。本文将深入探讨分词器的工作原理,以及一些流行大模型(例如LLaMA)的分词器实现细节,以帮助您理解这些大模型是如何处理自然语言的。
分词器的作用:从“字符”到“意义单元”
考虑这样一句话:“Hello world!”。如果没有分词器,计算机只能将其视为一串字符:
Hello world!
对于人类来说,理解这句话并不困难:“Hello”和“world”是两个独立的词,“!”是一个标点符号。然而,计算机缺乏直观的语言理解能力,它需要一个“分词器”来将这句话分解成有意义的单元。经过分词器的处理,计算机可以将这句话转化为:
['Hello', 'world', '!']
分词器的目标是将原始文本转换为模型可以处理的结构化形式。所谓的“单元”可能是词、子词或字符,这取决于所采用的分词策略。
分词器的工作原理:从规则到复杂算法
虽然分词器看似简单,但其实现涉及多个层次的算法。现代分词器不仅仅按词切割文本,而是采用了一些更复杂的算法,例如子词分词。我们将从传统的分词方法讲起,逐步深入到现代的分词算法。
1. 基于词的分割
最简单的分词方法是按词分割,这种方法在英语等语言中效果显著。例如,句子:
Hello world!
经过按词分割后,得到:
['Hello', 'world', '!']
这种方法直接依赖于空格和标点符号作为分割点,对于英语等词汇间有明确空格的语言非常有效。然而,对于中文和日文等没有空格的语言,需要更复杂的分词方法。
2. 基于字符的分割
对于中文这样的语言,字符本身就是语言的基本单位,因此可以按字符进行分割。例如:
你好,世界!
经过字符分割后,得到:
['你', '好', ',', '世', '界', '!']
这种方法简单直观,但对于一些复杂的任务(如机器翻译),可能不够精细。
3. 子词级分词:BPE与WordPiece
子词级分词的核心思想是通过将词拆解成更小的单位(如子词或字符),来解决词汇表稀疏的问题。现代的预训练大模型,尤其是像BERT、GPT等,都采用了这种方法。最常见的两种子词分词算法是 BPE(Byte Pair Encoding) 和 WordPiece。
BPE(Byte Pair Encoding)
BPE是一种基于统计的子词分词算法。其基本原理是通过统计文本中最频繁的字符对,并将它们合并成一个新的子词,直到达到词汇表的大小限制。以简单的文本为例:
原始文本:
low lower newest
-
初始时,我们按字符分割:
['l', 'o', 'w', ' ', 'l', 'o', 'w', 'e', 'r', ' ', 'n', 'e', 'w', 'e', 's', 't']
-
统计频率并合并最频繁的字符对。例如,
l
和o
频率最高,因此合并成lo
:['lo', 'w', ' ', 'lo', 'w', 'e', 'r', ' ', 'n', 'e', 'w', 'e', 's', 't']
-
继续统计并合并频率较高的字符对,如
lo
和w
:['low', ' ', 'low', 'e', 'r', ' ', 'n', 'e', 'w', 'e', 's', 't']
-
最终,合并到“newest”变为:
['low', 'low', 'er', 'new', 'est']
这种方法通过逐步合并字符对,逐步构建出词汇表,能够有效减少词汇表的大小,并且能够处理未见过的词。
WordPiece
WordPiece是另一种子词分词算法,广泛应用于BERT模型中。与BPE不同,WordPiece通过最大化文本的对数似然来合并最优的字对。假设我们有如下文本:
low lower newest
WordPiece的分词过程与BPE相似,但它通过一个最大化似然的方法来优化合并的方式。最终,WordPiece也能将文本拆解成子词单元。
举个例子:
-
“low” 会被分割成
['lo', 'w']
。 -
“lower” 会被分割成
['lo', 'w', 'er']
。 -
“newest” 会被分割成
['new', 'est']
。
WordPiece在实际应用中非常高效,尤其适合处理多语言和复杂的文本结构。
SentencePiece
SentencePiece 是由Google提出的一种分词算法,它不像BPE和WordPiece那样需要预先定义词汇表。它通过无监督的方式直接从文本中学习生成子词单元。SentencePiece特别适合于多语言环境,它能够同时处理多种语言的文本。
比如,对于句子:
低资源语言处理
SentencePiece可能会将它分割成:
['低', '资源', '语', '言', '处理']
SentencePiece的一个重要特点是,它能够在没有预定义词典的情况下直接进行训练,非常适合低资源语言或多语言环境。
常见的大模型分词器
除了BERT等常见模型,LLaMA、GPT等现代大语言模型也依赖分词器来处理文本。接下来,我们看一看这些大模型中的分词器实现,特别是它们的Tokenizer
类中的常见变量。
LLaMA中的Tokenizer
LLaMA(Large Language Model Meta AI)是Meta(Facebook)发布的一款强大的预训练模型,它也使用了基于BPE的分词器。LLaMA的Tokenizer
类实现中,通常包含以下几个关键变量:
-
vocab_size
:词汇表的大小。这个变量决定了分词器能处理的最大词汇量,更大的词汇表可以提高模型对新词的处理能力,但也会增加计算和存储开销。 -
tokens
:分词器内部存储的词汇表,包含所有的子词单位。 -
encode
:用于将文本转换为子词单元的函数。该函数会返回一个整数序列,表示每个子词的ID。 -
decode
:与encode
相对,用于将模型输出的ID序列转回为可读文本。
在LLaMA的Tokenizer
实现中,分词的核心通常依赖于BPE算法,这样可以更好地处理未见过的词汇。
选择合适的分词器
在选择分词器时,我们需要考虑以下几个因素:
- 任务类型:如果你的任务需要处理很多未知词,或者你的数据集中包含大量拼写错误,采用子词分词器(如BPE、WordPiece或SentencePiece)会更合适。
- 资源限制:不同分词器的效率和词汇表大小差异较大。对于计算资源有限的场景,选择一个更轻量级的分词器,如WordPiece,可能会带来更好的性能。
- 多语言支持:如果你的应用需要支持多种语言,SentencePiece是一种非常不错的选择,因为它能够无监督地学习语言的结构,适应多语言环境。
- 上下文感知能力:对于对话生成类任务(如chatbot),需要处理更多上下文信息,选择分词器时也要考虑到模型的上下文感知能力。
代码示例:使用Hugging Face的Tokenizer
from transformers import BertTokenizer
# 加载BERT的分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
# 示例文本
text = "Hello, world!"
# 使用分词器进行编码
encoded = tokenizer.encode(text, add_special_tokens=True) # add_special_tokens添加[CLS]和[SEP]
print("Encoded:", encoded)
# 解码
decoded = tokenizer.decode(encoded)
print("Decoded:", decoded)
在这段代码中,BertTokenizer
的encode
方法将输入的文本转换成整数ID