在自然语言处理(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