在自然语言处理(NLP)领域,tokenizer 是一个重要的组件,它的主要作用是将文本字符串分割成更小的单位,这些单位通常被称为"标记"(tokens)。这些标记可以是单词、子词(subwords)、字符甚至是更复杂的结构。Tokenizer 的设计和实现对后续的 NLP 模型性能有着重要影响,因为它决定了模型如何理解和处理输入数据。
Tokenizer 的主要功能
- 分词(Tokenization):将连续的文本切分成独立的标记。例如,句子 "I love natural language processing" 可以被切分成 ["I", "love", "natural", "language", "processing"]。
- 规范化(Normalization):对文本进行标准化处理,比如转换为小写、去除标点符号等,以减少词汇的变异性。
- 特殊标记处理:添加一些特殊的标记,如开始标记(<s>)、结束标记(</s>)、未知词汇标记(<unk>)等,这些标记有助于模型更好地理解文本的结构和边界。
常见的 Tokenizer 类型
- 基于规则的 Tokenizer:使用预定义的规则或正则表达式来切分文本。例如,根据空格、标点符号等进行分词。
- 基于统计的 Tokenizer:利用统计方法来学习如何切分文本。例如,使用频率信息来决定哪些子词应该被合并为一个标记。
- 基于机器学习的 Tokenizer:训练一个模型来预测文本的最佳切分点。这种方法通常需要大量的标注数据。
应用场景
- 机器翻译:将源语言文本切分成标记,然后由模型生成目标语言的翻译。
- 情感分析:将评论或文本切分成标记,以便模型可以更好地理解其中的情感倾向。
- 文本分类:将文档切分成标记,用于训练分类模型。
- 命名实体识别:将文本切分成标记,以便模型可以识别出人名、地名等实体。
示例
假设我们有一个简单的句子:"Hello, how are you doing today?"
- 基于规则的 Tokenizer 可能会将其切分成:["Hello", ",", "how", "are", "you", "doing", "today", "?"]
- 基于统计的 Tokenizer 可能会根据频率信息将其切分成:["Hello", "how", "are", "you", "doing", "today", "?"]
- 基于机器学习的 Tokenizer 可能会根据训练数据将其切分成:["Hello", "how", "are", "you", "doing", "today", "?"]
主要介绍三种分词算法: BPE/wordpiece/unigram,以及分词工具sentencepiece
llama-tokenizer-js
https://belladoreai.github.io/llama-tokenizer-js/example-demo/build/
huggingface.co
https://huggingface.co/docs/transformers/v4.46.2/zh/main_classes/tokenizer
SentencePiece是什么
SentencePiece 是一个由 Google 开发的开源工具,旨在帮助构建高效的自然语言处理(NLP)模型。它是一个跨平台的库,可以在多种操作系统上运行,包括但不限于 Windows、Linux 和 macOS。SentencePiece 的主要功能是在不需要预先定义词汇表的情况下,自动从大规模文本数据中学习如何将文本切分为"子词单元"(subword units)。这种方法可以有效地处理未知词汇和低频词汇的问题,这对于提高自然语言处理模型的性能非常关键。
SentencePiece 的主要特点包括:
- 无需预定义词汇表:传统的自然语言处理方法通常需要预先定义一个词汇表,这可能会导致遇到未登录词(即不在词汇表中的词)时的问题。SentencePiece 可以动态地学习词汇表,从而更好地处理这些未知词汇。
- 子词单元:SentencePiece 使用一种称为"子词单元"的概念,即将单词分解为更小的单元。这种方式不仅可以有效地处理未知词汇,还能减少词汇表的大小,从而提高模型的效率。
- 支持多种语言:无论是空白分隔的语言(如英语),还是非空白分隔的语言(如中文、日文),SentencePiece 都能够有效处理。
- 灵活性:用户可以根据自己的需求调整分词策略,比如设置词块的最大长度、最小频率等参数。
应用场景
SentencePiece 被广泛应用于各种自然语言处理任务中,如机器翻译、情感分析、文本分类等。特别是对于那些需要处理大量文本数据的应用,SentencePiece 提供了一种高效且灵活的解决方案。此外,它也被用于构建大规模的语言模型,如 BERT、XLNet 等,以提高这些模型的泛化能力和处理未知词汇的能力。
BPE是什么
Byte Pair Encoding (BPE) 是一种用于自然语言处理的子词(subword)分割算法。BPE 通过迭代地合并最常见的字符对来生成词汇表,从而将单词拆分成更小的单元。这种方法特别适用于处理未知词汇和低频词汇,同时可以减少词汇表的大小,提高模型的效率和性能。
BPE 的工作原理
- 初始化:从原始文本中提取所有字符,并将每个单词视为一个字符序列。
- 统计频率:计算所有字符对(bigrams)的出现频率。
- 合并操作:选择频率最高的字符对,并将它们合并为一个新的子词单元。
- 更新:更新文本中的所有单词,将合并后的子词单元替换原来的字符对。
- 重复:重复上述步骤,直到达到预定的词汇表大小或没有更多的字符对可以合并。
Python 使用示例代码
下面是一个使用 sentencepiece
库实现 BPE 的示例代码。sentencepiece
是一个广泛使用的库,支持 BPE 和其他分词方法。
安装 sentencepiece
首先,你需要安装 sentencepiece
库。可以使用以下命令进行安装:
bash
pip install sentencepiece
python
import sentencepiece as spm
# 准备训练数据
with open('train.txt', 'w', encoding='utf-8') as f:
f.write("Hello, how are you doing today?\n")
f.write("I am fine, thank you.\n")
f.write("Natural language processing is fascinating.\n")
# 训练 BPE 模型
spm.SentencePieceTrainer.train(input='train.txt', model_prefix='bpe', vocab_size=1000)
# 加载训练好的模型
sp = spm.SentencePieceProcessor(model_file='bpe.model')
# 测试分词
text = "Hello, how are you doing today?"
encoded = sp.encode(text, out_type=str)
print(f"Encoded: {encoded}")
decoded = sp.decode(encoded)
print(f"Decoded: {decoded}")
代码解释
- 准备训练数据 :创建一个包含训练文本的文件
train.txt
。 - 训练 BPE 模型 :使用
SentencePieceTrainer.train
方法训练 BPE 模型。input
参数指定训练数据文件,model_prefix
参数指定输出模型文件的前缀,vocab_size
参数指定词汇表的大小。 - 加载训练好的模型 :使用
SentencePieceProcessor
加载训练好的 BPE 模型。 - 测试分词:对一段文本进行编码(分词)和解码(还原)。
输出结果
bash
Encoded: ['▁Hello', ',', '▁how', '▁are', '▁you', '▁doing', '▁today', '?']
Decoded: Hello, how are you doing today?
注意事项
- 词汇表大小 :
vocab_size
参数决定了最终词汇表的大小。较大的词汇表可以更好地捕捉文本中的细节,但也会增加模型的复杂度。 - 模型文件 :训练完成后,会生成两个文件:
bpe.model
和bpe.vocab
。前者是模型文件,后者是词汇表文件
WordPiece是什么
WordPiece 是另一种常用的子词(subword)分割算法,与 Byte Pair Encoding (BPE) 类似,但有一些不同之处。WordPiece 也是通过迭代地合并常见的字符对来生成词汇表,但它在选择合并字符对时使用了概率模型,确保生成的词汇表在训练数据上的覆盖率最大化。
WordPiece 的工作原理
- 初始化:从原始文本中提取所有字符,并将每个单词视为一个字符序列。
- 统计频率:计算所有字符对(bigrams)的出现频率。
- 合并操作:选择使词汇表在训练数据上的覆盖率最大的字符对,并将它们合并为一个新的子词单元。
- 更新:更新文本中的所有单词,将合并后的子词单元替换原来的字符对。
- 重复:重复上述步骤,直到达到预定的词汇表大小或没有更多的字符对可以合并。
Python 使用示例代码
下面是一个使用 transformers
库实现 WordPiece 的示例代码。transformers
是 Hugging Face 开发的一个广泛使用的库,支持多种预训练模型及其分词器。
安装 transformers
首先,你需要安装 transformers
库。可以使用以下命令进行安装:
bash
pip install transformers
python
from transformers import BertTokenizer
# 初始化 BERT 的 WordPiece 分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
# 测试分词
text = "Hello, how are you doing today?"
tokens = tokenizer.tokenize(text)
print(f"Tokens: {tokens}")
# 编码为模型输入
input_ids = tokenizer.encode(text, add_special_tokens=True)
print(f"Input IDs: {input_ids}")
# 解码回原始文本
decoded_text = tokenizer.decode(input_ids)
print(f"Decoded Text: {decoded_text}")
代码解释
- 初始化 WordPiece 分词器 :使用
BertTokenizer.from_pretrained
方法加载预训练的 BERT 模型的 WordPiece 分词器。这里使用的是bert-base-uncased
,这是一个不区分大小写的 BERT 模型。 - 测试分词:对一段文本进行分词,得到子词单元的列表。
- 编码为模型输入 :将文本编码为模型可以接受的输入格式,
add_special_tokens=True
表示添加特殊的标记,如[CLS]
和[SEP]
。 - 解码回原始文本:将编码后的输入解码回原始文本。
输出结果
bash
Tokens: ['hello', ',', 'how', 'are', 'you', 'doing', 'today', '?']
Input IDs: [101, 3793, 1010, 2088, 2003, 2117, 2644, 1029, 102]
Decoded Text: [CLS] hello, how are you doing today? [SEP]
注意事项
- 预训练模型 :
transformers
库提供了多种预训练模型,每种模型都有对应的分词器。你可以根据需要选择不同的模型,如bert-base-cased
(区分大小写)或roberta-base
。 - 特殊标记 :
add_special_tokens=True
参数会在编码时添加[CLS]
和[SEP]
等特殊标记,这些标记在 BERT 等模型中用于表示句子的开始和结束。 - 词汇表大小:预训练模型的词汇表大小通常是固定的,但你可以通过训练自己的分词器来定制词汇表大小。
unigram是什么
Unigram Language Model 是一种基于单个词(或子词)的概率模型,常用于自然语言处理中的分词任务。与 Byte Pair Encoding (BPE) 和 WordPiece 不同,Unigram 模型通过优化词汇表中每个子词的频率分布来生成子词单元。Unigram 模型的目标是使生成的子词单元在训练数据上的概率最大化。
Unigram 的工作原理
- 初始化:从原始文本中提取所有字符,并将每个单词视为一个字符序列。
- 初始化词汇表:将所有字符作为初始词汇表。
- 优化过程:通过迭代地调整词汇表中的子词单元,使生成的子词单元在训练数据上的概率最大化。
- 更新:在每次迭代中,重新计算每个子词单元的概率,并根据这些概率调整词汇表。
- 终止条件:当达到预定的词汇表大小或收敛条件时,停止迭代。
Python 使用示例代码
下面是一个使用 sentencepiece
库实现 Unigram 分词的示例代码。sentencepiece
是一个广泛使用的库,支持多种分词方法,包括 Unigram。
安装 sentencepiece
首先,你需要安装 sentencepiece
库。可以使用以下命令进行安装:
bash
pip install sentencepiece
python
import sentencepiece as spm
# 准备训练数据
with open('train.txt', 'w', encoding='utf-8') as f:
f.write("Hello, how are you doing today?\n")
f.write("I am fine, thank you.\n")
f.write("Natural language processing is fascinating.\n")
# 训练 Unigram 模型
spm.SentencePieceTrainer.train(
input='train.txt',
model_prefix='unigram',
vocab_size=1000,
model_type='unigram'
)
# 加载训练好的模型
sp = spm.SentencePieceProcessor(model_file='unigram.model')
# 测试分词
text = "Hello, how are you doing today?"
encoded = sp.encode(text, out_type=str)
print(f"Encoded: {encoded}")
decoded = sp.decode(encoded)
print(f"Decoded: {decoded}")
代码解释
- 准备训练数据 :创建一个包含训练文本的文件
train.txt
。 - 训练 Unigram 模型 :使用
SentencePieceTrainer.train
方法训练 Unigram 模型。input
参数指定训练数据文件,model_prefix
参数指定输出模型文件的前缀,vocab_size
参数指定词汇表的大小,model_type
参数指定使用 Unigram 模型。 - 加载训练好的模型 :使用
SentencePieceProcessor
加载训练好的 Unigram 模型。 - 测试分词:对一段文本进行编码(分词)和解码(还原)。
输出结果
bash
Encoded: ['▁Hello', ',', '▁how', '▁are', '▁you', '▁doing', '▁today', '?']
Decoded: Hello, how are you doing today?
注意事项
- 词汇表大小 :
vocab_size
参数决定了最终词汇表的大小。较大的词汇表可以更好地捕捉文本中的细节,但也会增加模型的复杂度。 - 模型文件 :训练完成后,会生成两个文件:
unigram.model
和unigram.vocab
。前者是模型文件,后者是词汇表文件。 - 模型类型 :
model_type
参数可以设置为unigram
、bpe
或char
,分别对应 Unigram、BPE 和字符级别的分词方法。