Article

词元处理 Tokenizers

更新于:2026-07-17

第一章:初识 Tokenizers 库

1.1 什么是 HuggingFace Tokenizers

概念名称说明注意事项
HuggingFace Tokenizers一个独立的、高性能的分词库,专为自然语言处理任务设计,提供快速、灵活的文本到模型输入的转换能力。底层使用 Rust 实现,Python 接口调用,速度快于纯 Python 分词器。不要与 transformers 库混淆,它是独立库,但常与其配合使用。
核心目标将原始文本转换为模型可接受的数字表示(如 token IDs、attention mask、token type ids 等)。分词是 NLP 模型训练和推理的第一步,直接影响模型性能。
主要特性支持 BPE、WordPiece、Unigram、SentencePiece 等主流子词算法;支持并行处理;可训练自定义分词器;支持特殊标记管理。所有操作均不可逆(无法完全还原原始空格和标点),需注意预处理一致性。

1.2 Tokenizers 与 Transformers 库的关系

概念名称说明注意事项
tokenizers 库独立的分词引擎,负责高效执行分词逻辑,提供 Tokenizer 类及其组件(Normalizer, Model 等)。可单独使用,不依赖 transformers。
transformers 库高层模型库,包含预训练模型(BERT、GPT 等)、训练接口和 tokenizer 包装类。使用 PreTrainedTokenizer 和 PreTrainedTokenizerFast 包装 tokenizers.Tokenizer 实例。
关系总结transformers 中的 AutoTokenizer.from_pretrained() 若加载的是 Fast 版本,则内部使用 tokenizers 库;否则使用慢速 Python 实现。推荐使用 Fast Tokenizer(基于 tokenizers)以获得更好性能。
TokenizerFast 类transformers 提供的包装类,将 tokenizers.Tokenizer 集成进来,提供统一 API。支持 .encode(), .batch_encode_plus() 等方法,行为与慢速 tokenizer 一致。

1.3 安装与环境配置

操作步骤操作细节注意事项
安装 tokenizers 库在终端运行:pip install tokenizers建议在虚拟环境中安装,避免依赖冲突。
验证安装在 Python 中执行:from tokenizers import Tokenizer,若无报错则安装成功。print(Tokenizer) 可确认导入成功。
安装 transformers(可选)如需与模型集成,运行:pip install transformers大部分场景需要同时安装 transformers。
查看版本运行:python -c "from tokenizers import version; print(version)"建议使用最新稳定版以获得最佳功能支持。
兼容性要求Python 3.7+,操作系统不限(Linux/macOS/Windows 均支持)Windows 用户无需额外配置,pip 安装即用。

1.4 快速入门:一个完整的分词示例

方法名语法用途代码示例注意事项
Tokenizer.from_pretrainedTokenizer.from_pretrained(model_name)从 HuggingFace Hub 加载预训练分词器from tokenizers import Tokenizer
tok = Tokenizer.from_pretrained("bert-base-uncased")
model_name 需为支持的模型标识符,如 "gpt2", "roberta-base"
encodetok.encode(text)Encoding 对象对单个文本进行编码,返回包含 token IDs 等信息的对象encoding = tok.encode("Hello, world!")
print(encoding.ids) # [101, 7592, 1010, 2088, 102]
返回对象支持 .tokens(), .offsets(), .attention_mask() 等方法
encode_batchtok.encode_batch(list_of_texts)批量编码多个文本,效率更高texts = ["Hi there!", "How are you?"]
encodings = tok.encode_batch(texts)
推荐用于数据集预处理,充分利用并行能力
get_vocabtok.get_vocab()dict获取当前词汇表映射(token → id)vocab = tok.get_vocab()
print(len(vocab)) # 如 30522
词汇表大小取决于模型类型
decodetok.decode(token_ids, skip_special_tokens=True)将 token IDs 转回原始文本decoded = tok.decode([7592, 1010, 2088])
# "hello, world"
设置 skip_special_tokens=True 可去除 [CLS], [SEP] 等标记

示例完整流程:

from tokenizers import Tokenizer

# 加载预训练分词器
tok = Tokenizer.from_pretrained("bert-base-uncased")

# 编码文本
encoding = tok.encode("Transformers are great!")

# 输出结果
print("Tokens:", encoding.tokens)
print("IDs:", encoding.ids)
print("Decoded:", tok.decode(encoding.ids, skip_special_tokens=True))

输出示例:

Tokens: ['[CLS]', 'transformers', 'are', 'great', '!', '[SEP]']
IDs: [101, 19081, 2024, 2307, 999, 102]
Decoded: transformers are great!

第二章:核心组件与工作流程

2.1 分词流程总览(PreTokenizer → Tokenizer → PostProcessor)

概念名称说明注意事项
整体流程文本输入 → Normalizer → PreTokenizer → Model (BPE/WordPiece等) → PostProcessor → Encoder → 输出 IDs/Attention Mask 等各组件可独立配置,组合灵活。
Normalizer对原始文本进行标准化处理,如小写化、Unicode 规范化、去除重音等。应在 PreTokenizer 之前执行,确保输入一致性。
PreTokenizer将文本按规则切分为子单元(如按空格、标点),为后续子词分割做准备。切分方式影响最终 tokenization 结果,需根据语言选择。
Model核心分词模型(如 BPE),将 PreTokenizer 输出的词进一步拆分为子词单元。模型决定词汇生成方式和最终 token 表示。
PostProcessor在模型输出后添加特殊结构,如 BERT 的 [CLS] + A + [SEP] + B + [SEP]用于构造特定任务的输入格式,如句子对分类。
Encoder将处理后的 token 序列编码为模型所需格式(IDs, type_ids, attention_mask)。最终输出可直接送入模型 forward 函数。

2.2 Normalizer:文本预处理

方法名语法用途代码示例注意事项
Normalizerfrom tokenizers.normalizers import Normalizer抽象基类,定义标准化接口不可直接实例化需使用具体实现类
LowercaseLowercase()将所有字符转为小写from tokenizers.normalizers import Lowercase
norm = Lowercase()
常用于不区分大小写的模型(如 bert-base-uncased)
NFD / NFKD / NFC / NFKCNFD(), NFKD(), NFC(), NFKC()Unicode 标准化形式(分解/组合)from tokenizers.normalizers import NFD
norm = NFD()
推荐使用 NFC 保证字符一致性
StripStrip(left=True, right=True)去除字符串首尾空白Strip(left=True, right=False) # 仅去左空格默认左右都去
StripAccentsStripAccents()去除重音符号(如 é → e)from tokenizers.normalizers import StripAccents
norm = StripAccents()
对多语言文本有用
ReplaceReplace(pattern, content)正则替换(pattern 为字符串或 Regex 对象)Replace("http://", "http ")可用于 URL、邮箱等预处理
SequenceSequence([norm1, norm2])组合多个 Normalizer 顺序执行Sequence([Lowercase(), StripAccents()])执行顺序从左到右

2.3 PreTokenizer:分词前切分

方法名语法用途代码示例注意事项
PreTokenizerfrom tokenizers.pre_tokenizers import PreTokenizer抽象基类不可直接实例化需使用具体实现
WhitespaceWhitespace()按空白字符(空格、制表符等)切分from tokenizers.pre_tokenizers import Whitespace
pre_tok = Whitespace()
最常见方式
PunctuationPunctuation(behavior="isolated")将标点符号单独切出Punctuation(behavior="removed") # 可选 removed/isolatedbehavior 控制标点处理方式
DigitsDigits(individual_digits=True)数字是否逐位切分Digits(individual_digits=False) # 保持数字整体中文场景常设为 True
MetaspaceMetaspace(replacement="_", add_prefix_space=True)用特殊字符替换空格(用于 BPE)Metaspace(replacement="▁")常见于 SentencePiece 风格
ByteLevelByteLevel(add_prefix_space=True)按字节级别切分(用于 GPT-2、RoBERTa)from tokenizers.pre_tokenizers import ByteLevel
pre_tok = ByteLevel()
支持 emoji 和罕见字符
SequenceSequence([pre1, pre2])组合多个 PreTokenizerSequence([Whitespace(), Punctuation()])顺序执行,前一个输出为后一个输入

2.4 Model:核心分词模型(BPE、WordPiece、Unigram、SentencePiece)

方法名语法用途代码示例注意事项
BPEBPE(vocab, merges, unk_token=None, end_of_word_suffix=None)构建 BPE 模型from tokenizers.models import BPE
model = BPE(vocab, merges)
vocab 和 merges 通常由训练生成
WordPieceWordPiece(vocab, unk_token="[UNK]", max_input_chars_per_word=100)构建 WordPiece 模型(BERT 使用)from tokenizers.models import WordPiece
model = WordPiece(vocab)
max_input_chars_per_word 防止过长词 OOM
UnigramUnigram(pieces, unk_id=0)构建 Unigram 模型(SentencePiece 使用)from tokenizers.models import Unigram
model = Unigram(pieces)
pieces 为 (token, score) 列表
SentencePieceBPSentencePieceBP(model_file, vocab_size, reverse=False, add_prefix_space=False)从 .model 文件加载 SentencePiece BPESentencePieceBP("sp.model", vocab_size=32000)支持直接集成 SP 模型
SentencePieceUnigramSentencePieceUnigram(model_file, vocab_size)从 .model 文件加载 SentencePiece UnigramSentencePieceUnigram("sp.model")常用于多语言模型
savemodel.save(directory, file_prefix=None)保存模型到磁盘model.save("./my_model", "bpe")生成 vocab.json 和 merges.txt
get_vocabmodel.get_vocab(with_added_tokens=True)dict获取词汇表vocab = model.get_vocab()返回 token → id 映射

2.5 PostProcessor:后处理(如添加 [CLS]、[SEP])

方法名语法用途代码示例注意事项
PostProcessorfrom tokenizers.processors import PostProcessor抽象基类不可直接实例化需使用具体实现
TemplateProcessingTemplateProcessing(template, special_tokens)使用模板定义输出结构TemplateProcessing("$A", special_tokens=[("", 1), ("", 2)])$A 表示第一句,$B 第二句
BertProcessingBertProcessing(sep, cls)专为 BERT 设计:[CLS] A [SEP] B [SEP]BertProcessing(("[SEP]", 102), ("[CLS]", 101))第一个参数是 (sep_token, sep_id) 元组
RobertaProcessingRobertaProcessing(sep, cls, add_prefix_space=True)专为 RoBERTa 设计RobertaProcessing(("", 0), ("", 2))注意空格处理
SingleSingle(special_token, special_token_id)单句后处理,仅添加首尾标记Single("[CLS]", 101)用于单句分类任务
concatprocessor1.concat(processor2)连接两个 PostProcessorproc_a.concat(proc_b)构建复杂结构

2.6 Encoder:编码输出(Token IDs, Attention Mask 等)

方法名语法用途代码示例注意事项
encodetokenizer.encode(text)Encoding编码单个文本encoding = tokenizer.encode("Hello world")返回 Encoding 对象
encode_batchtokenizer.encode_batch(list_of_texts)批量编码encodings = tokenizer.encode_batch(["t1", "t2"])性能优于循环单条编码
Encoding.idsencoding.idsList[int]获取 token IDsinput_ids = encoding.ids可直接作为模型输入
Encoding.tokensencoding.tokensList[str]获取 token 字符串列表tokens = encoding.tokens用于调试和可视化
Encoding.attention_maskencoding.attention_maskList[int]获取 attention maskmask = encoding.attention_mask1 表示有效 token,0 表示 padding
Encoding.type_idsencoding.type_idsList[int]获取 token type ids(句子 A/B)type_ids = encoding.type_ids用于句子对任务
Encoding.offsetsencoding.offsetsList[Tuple[int, int]]获取每个 token 在原文中的字符位置start, end = encoding.offsets[0]用于 NER、QA 等任务
Encoding.overflowingencoding.overflowingList[Encoding]获取截断后的溢出部分if encoding.overflowing: ...配合 truncation 使用
Encoding.special_tokens_maskencoding.special_tokens_maskList[int]标记哪些是特殊 token([CLS], [SEP]mask = encoding.special_tokens_mask用于 loss 计算时忽略特殊 token

第三章:分词模型详解

3.1 BPE(Byte-Pair Encoding)模型原理与实现

概念名称说明注意事项
基本原理从字符级开始,迭代合并出现频率最高的相邻符号对,逐步构建子词单元。初始词汇为字符集,通过 merges 文件记录合并规则。合并规则是贪心的,不考虑全局最优。
训练过程1. 统计词频;2. 初始化词汇为字符;3. 重复:找最高频相邻对 → 合并 → 更新词频 → 加入 merges;4. 直到达到 vocab_size 或合并次数上限。需要设置 vocab_size 和 min_frequency 控制词汇表大小。
未知词处理将未登录词按字符或子词切分,使用已知 merges 规则分解。可能产生大量子词片段,影响语义完整性。
merges 文件记录所有合并操作的有序列表,每行两个符号表示一次合并。是 BPE 模型的核心组成部分,必须与 vocab 配合使用。
vocab 文件初始词汇表,包含所有基础字符及高频词,通常还包括特殊标记。常见格式为 JSON,键为 token,值为索引。
add_prefix_space是否在词前添加空格作为切分依据(如 GPT-2)设置为 True 可更好处理词边界,避免歧义。

3.2 WordPiece 模型原理与实现

概念名称说明注意事项
基本原理基于最大似然估计选择要合并的子词对,目标是最大化训练数据的似然。每次选择能使语言模型概率增益最大的 pair 进行合并。比 BPE 更注重统计意义,但训练更复杂。
unk_token用于表示词汇表外的词,默认为 [UNK]所有 OOV 词统一替换为此标记,损失语义信息。
max_input_chars_per_word单个词最大字符数限制(默认 100)防止过长单词导致内存溢出或性能下降。
分词策略采用贪心最长匹配:从左到右尽可能匹配最长的子词。可能无法覆盖所有可能的子词组合。
特殊标记处理支持 [CLS], [SEP], [MASK] 等 BERT 特有标记必须在词汇表中预定义并分配 ID。
词汇表构建初始为字符集,逐步扩展,最终包含常见词和子词。通常比 BPE 生成更多完整词 token。

3.3 Unigram 模型原理与实现

概念名称说明注意事项
基本原理假设每个 token 独立出现,基于概率模型评分。训练时从大词汇表开始,逐步移除低概率 token,保留高概率子词。不是合并过程,而是”淘汰”过程。
pieces 文件包含 (token, log_probability) 的列表,概率越高越可能被选中。概率为负数,绝对值越小越好。
分词方式对同一词可能有多种切分方式,选择总概率最高的路径。支持多路径解码,灵活性高。
lattice 解码构建所有可能的子词路径图,使用 Viterbi 算法找最优路径。计算量较大,但结果更优。
dropout在训练中随机丢弃某些子词路径,增强鲁棒性可防止过拟合,提高泛化能力。
词汇缩减通过 loss 值控制最终 vocab_size,自动裁剪低频 token不需要显式合并规则,配置更简单。

3.4 SentencePiece 模型(BPE/Unigram)集成

方法名语法用途代码示例注意事项
SentencePieceBPSentencePieceBP(model_file)加载 SentencePiece 的 BPE 模型文件from tokenizers.models import SentencePieceBP
model = SentencePieceBP("sp.model")
model_file.model 扩展名
SentencePieceUnigramSentencePieceUnigram(model_file)加载 SentencePiece 的 Unigram 模型文件from tokenizers.models import SentencePieceUnigram
model = SentencePieceUnigram("sp.model")
支持直接读取 SP 训练结果
add_prefix_spaceSentencePieceBP(..., add_prefix_space=True)是否在词前加空格常用于与 GPT 类模型兼容影响分词边界判断
vocab_sizeSentencePieceBP(..., vocab_size=32000)指定词汇表大小(用于验证)可用于调试和一致性检查实际大小由模型文件决定
savemodel.save("dir/")保存为 tokenizers 原生格式model.save("./converted")生成 vocab.json 和 config.json
get_vocabmodel.get_vocab()获取词汇表映射vocab = model.get_vocab()返回字典形式便于查看

3.5 模型选择与性能对比

模型类型优点缺点适用场景注意事项
BPE实现简单、速度快、社区支持好、适合英文对低频词处理较差,可能过度分割GPT、RoBERTa、大多数英文模型推荐搭配 ByteLevel PreTokenizer
WordPieceBERT 官方方案,平衡完整词与子词训练复杂,依赖最大似然估计BERT 及其变体中文常直接以字为单位
Unigram分词灵活,支持多路径,适合多语言计算开销大,训练时间长日文、韩文、XLM-R 等支持更好的未知词处理
SentencePiece无需空格分词,天然支持多语言默认移除空格,可能影响可读性多语言模型、无空格语言可设置 add_dummy_prefix=False 保留空格
性能对比(速度)BPE ≈ WordPiece > UnigramUnigram 解码较慢高吞吐场景优先选 BPE批量处理可缓解差异
性能对比(OOV率)Unigram < BPE ≈ WordPiece具体取决于训练数据领域迁移任务推荐 Unigram良好训练数据是关键
中文处理建议WordPiece(以字切分)或 BPE(字节级)避免按词切分导致 OOV中文 BERT、MacBERT可结合中文分词工具做 PreTokenizer

第四章:高级特性与自定义配置

4.1 自定义词汇表(Vocabulary)构建

操作步骤操作细节注意事项
准备训练语料收集领域相关文本,保存为纯文本文件(.txt)或按行分隔的 JSON/CSV数据质量决定词汇表效果,需清洗噪声
初始化词汇表可指定初始字符集或从已有词表加载建议包含基础字母、数字、标点及常用符号
设置 vocab_size在训练时指定目标词汇表大小(如 30000)过大会增加模型参数,过小会提高 OOV 率
设置 min_frequency设定词频阈值,低于此值的词不加入词汇表防止低频词膨胀词汇表
保留特殊标记在构建时预留 [PAD], [UNK], [CLS] 等 token 的 ID 位置必须在训练前定义,避免冲突
使用 BPE.train()调用 tokenizer.train(files, vocab_size=...) 开始训练支持多文件输入,自动处理大文件
验证词汇表使用 tokenizer.get_vocab() 检查生成的 token 分布查看是否包含预期的领域术语

4.2 特殊标记(Special Tokens)管理([PAD], [UNK], [CLS], [SEP], [MASK])

方法名语法用途代码示例注意事项
set_paddingtokenizer.enable_padding(pad_token="[PAD]", pad_token_id=0)配置填充参数tokenizer.enable_padding("[PAD]", 0)必须启用后 encode 才有 attention_mask
set_truncationtokenizer.enable_truncation(max_length=512)配置截断长度tokenizer.enable_truncation(512)超长文本将被截断
add_special_tokenstokenizer.add_special_tokens([{"id": 0, "content": "[PAD]"}, ...])添加多个特殊标记tokenizer.add_special_tokens(["[PAD]", "[UNK]"])可批量添加
token_to_idtokenizer.token_to_id("[CLS]")int获取特殊标记的 IDcls_id = tokenizer.token_to_id("[CLS]")用于构建输入结构
id_to_tokentokenizer.id_to_token(101)str根据 ID 获取 token 字符串sep_token = tokenizer.id_to_token(102)用于调试和解码
special_tokenstokenizer.special_tokensList[str]获取所有特殊标记列表tokens = tokenizer.special_tokens包括 [PAD], [UNK], [CLS]
unk_tokentokenizer.unk_token = "[UNK]"设置或获取未知标记print(tokenizer.unk_token)必须存在于词汇表中
pad_tokentokenizer.pad_token = "[PAD]"设置填充标记tokenizer.pad_token = "[PAD]"影响 padding 行为

4.3 添加新标记(add_tokens, add_special_tokens)

方法名语法用途代码示例注意事项
add_tokenstokenizer.add_tokens(["new_word", "domain_term"])添加普通词汇(不更新特殊标记)n_added = tokenizer.add_tokens(["covid", "vaccine"])返回实际添加数量
add_special_tokenstokenizer.add_special_tokens(["[NEW]", "[SEP2]"])添加特殊标记(会更新 special_tokens)tokenizer.add_special_tokens(["[NEW]"])特殊标记参与模型结构构建
add_tokens(对象形式)tokenizer.add_tokens([{"content": "token", "single_word": True}])控制是否允许子词切分add_tokens([{"content": "USA", "single_word": True}])single_word=True 表示禁止拆分
resize_token_embeddings需在 transformers 中调用 model.resize_token_embeddings(len(tokenizer))同步更新模型嵌入层大小model.resize_token_embeddings(len(tokenizer))添加 token 后必须调用
get_vocab_sizetokenizer.get_vocab_size(with_added_tokens=True)获取包含新增 token 的总大小size = tokenizer.get_vocab_size()用于验证添加结果

4.4 处理未知词(Unknown Tokens)策略

策略名称说明注意事项
[UNK] 替换所有未登录词统一替换为 [UNK] 标记简单但损失语义,影响下游任务
子词分解使用 BPE/WordPiece 规则将未知词拆分为子词如 “unhappiness” → “un” + “happiness”
字符级回退当子词也无法表示时,逐字符切分常见于中文或形态丰富语言
词汇表扩展在训练时加入领域术语,减少 OOV需重新训练或增量学习
dropout 机制训练中随机 masking 某些子词,提升鲁棒性类似数据增强
未知词检测通过 special_tokens_mask 或 token 是否为 [UNK] 判断用于后处理或日志记录

4.5 截断(Truncation)与填充(Padding)配置

方法名语法用途代码示例注意事项
enable_truncationtokenizer.enable_truncation(max_length=512, direction="right", stride=0)启用截断tokenizer.enable_truncation(512)默认从右侧截断
enable_paddingtokenizer.enable_padding(length=None, pad_token="[PAD]", pad_token_id=0, pad_to_multiple_of=None)启用填充tokenizer.enable_padding(pad_to_multiple_of=8)提高 GPU 利用率
max_lengthenable_truncation(max_length=512)最大长度限制不可超过模型最大上下文如 BERT 为 512
directiondirection="left" | "right" | "only_first" | "only_second"截断方向处理问答任务时可用 "only_first"控制保留哪部分信息
stridestride=64滑动窗口重叠长度用于长文本分块结合 overflowing 使用
lengthenable_padding(length=64)固定填充到指定长度否则按 batch 最大长度对齐可控但可能浪费
pad_to_multiple_ofpad_to_multiple_of=8填充到指定倍数优化 Tensor Core 计算推荐设置为 8 的倍数

4.6 编码缓存与性能优化

方法名语法用途代码示例注意事项
编码批处理encode_batch(texts)一次性处理多个文本encodings = tokenizer.encode_batch(texts)比循环调用快数倍
预分配内存使用固定 max_length + padding减少动态分配开销启用 padding 并指定 length适合固定长度任务
禁用不需要的字段如无需 offsets 可忽略减少输出体积只使用 ids 和 attention_mask特别在推理时有效
使用 Fast Tokenizer基于 tokenizers 库而非 Python 实现显著提升速度确保使用 tokenizer_fasttransformers 中默认优先加载
内存映射(mmap)处理超大语料训练时使用避免加载全部数据到内存内部自动启用适用于 >10GB 文本
缓存机制手动缓存 encode 结果到磁盘(如 pickle)避免重复编码cache[md5(text)] = encoding适用于固定数据集
并行处理利用多核 CPU 进行批处理Rust 后端自动支持encode_batch 天然并行不需额外配置

第五章:训练自己的分词器

5.1 准备训练语料数据

操作步骤操作细节注意事项
数据收集收集与目标领域相关的文本数据(如新闻、论文、对话记录)数据越贴近下游任务,分词效果越好
格式统一将所有文本转换为 UTF-8 编码的纯文本格式避免乱码和不可见字符干扰
清洗噪声去除无关 HTML 标签、特殊控制符、广告文本等可使用正则表达式或专用清洗工具
分行存储每个句子或文档占一行(适用于 .txt 文件)Tokenizer.train() 默认按行读取
多文件组织将大语料拆分为多个小文件(如 part_001.txt, part_002.txt提高加载效率,避免单文件过大
语言一致性确保语料主要为单一语言或明确标注多语言混合语言需特别处理分词策略
数据量建议至少 1GB 文本以获得稳定词汇表,更大更佳小数据集易过拟合或覆盖不足

5.2 使用 Tokenizer.train() 方法训练 BPE/WordPiece

方法名语法用途代码示例注意事项
Tokenizer.traintokenizer.train(files, trainer)开始训练分词器tokenizer.train(["data.txt"], trainer)files 支持字符串列表或单个字符串
BPETrainerBPETrainer(special_tokens=["[UNK]", "[CLS]"])配置 BPE 训练器from tokenizers.trainers import BPETrainer
trainer = BPETrainer(special_tokens=["[UNK]"])
必须指定 special_tokens
WordPieceTrainerWordPieceTrainer(vocab_size=30000, special_tokens=["[UNK]"])配置 WordPiece 训练器from tokenizers.trainers import WordPieceTrainer
trainer = WordPieceTrainer(vocab_size=30000)
不需要 merges 文件
设置模型类型tokenizer.model = BPE()WordPiece()先绑定空模型再训练tokenizer.model = BPE()
tokenizer.train(files, trainer)
模型类型决定训练方式
训练过程监控无内置进度条,可通过日志查看大语料可能耗时数分钟至数小时建议在后台运行
返回值无显式返回,结果更新 tokenizer 实例内部状态训练完成后 tokenizer 即可使用

5.3 训练参数详解(vocab_size, min_frequency, special_tokens 等)

参数名称语法位置说明示例值注意事项
vocab_sizeBPETrainer(..., vocab_size=30000)目标词汇表大小30000, 50000过大会增加模型负担,过小提高 OOV
min_frequencyBPETrainer(..., min_frequency=2)仅频率 ≥ 此值的 token 参与合并2, 5过滤低频噪声词
special_tokensBPETrainer(special_tokens=["[PAD]", "[UNK]"])定义特殊标记列表["[CLS]", "[SEP]", "[MASK]"]必须包含 [UNK],否则报错
limit_alphabetBPETrainer(limit_alphabet=1000)限制初始字符集大小1000防止 Unicode 膨胀
initial_alphabetBPETrainer(initial_alphabet=set("abc"))自定义初始字符集set(string.ascii_letters)扩展支持 emoji 或符号
show_progressBPETrainer(show_progress=True)是否显示训练进度True / False调试时启用
continuing_subword_prefixBPETrainer(continuing_subword_prefix="##")子词前缀(WordPiece 特有)"##"表示该子词是前一个词的延续
end_of_word_suffixBPETrainer(end_of_word_suffix="")词尾标记"</w>"BPE 中用于区分词边界

5.4 保存与加载自定义分词器

方法名语法用途代码示例注意事项
savetokenizer.save("path/to/tokenizer.json")保存整个分词器配置到单个文件tokenizer.save("my_tokenizer.json")推荐格式,包含所有组件
save_modeltokenizer.save_model("directory/", "prefix")仅保存模型部分(vocab + merges)tokenizer.save_model("./", "bpe")生成 vocab.json 和 merges.txt
from_fileTokenizer.from_file("tokenizer.json")从文件加载完整分词器tok = Tokenizer.from_file("my_tok.json")恢复训练后的状态
from_pretrainedTokenizer.from_pretrained("local/path")加载本地预训练分词器tok = Tokenizer.from_pretrained("./")目录需包含 config.json
保存目录结构save_model 生成两个文件:vocab.json 和 merges.txt分离模型与配置可手动编辑 vocab.json不要修改 merges.txt
加载兼容性从 JSON 加载可保留 Normalizer、PreTokenizer 等完整流程完整恢复训练配置推荐使用 save()

5.5 从文件训练(Text files, JSON, CSV)

文件类型操作方式代码示例注意事项
纯文本 (.txt)每行一个文本片段files = ["corpus.txt"]
tokenizer.train(files, trainer)
最简单,推荐首选
JSON 文件指定字段名提取文本需先解析 JSON,提取字段写入临时 txt不支持直接传 JSON 路径
CSV 文件指定列名提取文本import pandas as pd
df = pd.read_csv("data.csv")
df["text"].to_csv("tmp.txt", index=False, header=False)
注意引号和转义字符
多个文件传入文件路径列表files = [f"part_{i}.txt" for i in range(10)]自动按顺序读取
压缩文件需先解压gunzip data.txt.gz不支持直接读取 .gz
大文件流式处理库内部自动按行读取,无需全加载内存支持 >10GB 文件确保磁盘 I/O 性能
字段选择(JSON/CSV)使用外部脚本提取目标字段with open("data.json") as f:
  texts = [json.loads(line)["content"] for line in f]
确保字段存在且非空

第六章:与 Transformers 集成

6.1 将 tokenizers.Tokenizer 包装为 PreTrainedTokenizerFast

方法名语法用途代码示例注意事项
PreTrainedTokenizerFastPreTrainedTokenizerFast(tokenizer_object=...)包装原生 tokenizer 为 Transformers 兼容对象from transformers import PreTrainedTokenizerFast
fast_tok = PreTrainedTokenizerFast(tokenizer=tokenizer)
必须传入已训练的 tokenizers.Tokenizer 实例
from_filePreTrainedTokenizerFast.from_pretrained("path/to/tokenizer.json")从保存的 JSON 文件加载tok = PreTrainedTokenizerFast.from_pretrained("./my_tokenizer.json")路径指向 .json 文件或包含它的目录
from_pretrainedPreTrainedTokenizerFast.from_pretrained("local_dir/")加载本地保存的分词器tok = PreTrainedTokenizerFast.from_pretrained("./")目录需有 tokenizer.json 或 Hugging Face 标准结构
添加特殊标记映射fast_tok.add_special_tokens(...)同步特殊标记到 Transformers 接口fast_tok.add_special_tokens({"pad_token": "[PAD]"})确保模型配置能识别
绑定模型类型PreTrainedTokenizerFast(..., model_max_length=512, padding_side="right")设置模型相关参数fast_tok = PreTrainedTokenizerFast(tokenizer=tokenizer, model_max_length=512)影响 truncation 和 padding 行为
验证包装结果fast_tok.encode("Hello world")测试是否正常工作print(fast_tok.encode("test"))输出应为 list of int
与模型配合model = AutoModelForCausalLM.from_pretrained("config_only", vocab_size=len(fast_tok))使用自定义 vocab_size 初始化模型模型词汇表大小必须匹配分词器否则 embedding 维度不匹配

6.2 在模型训练中使用自定义分词器

操作步骤操作细节注意事项
准备数据集将原始文本通过自定义分词器编码为 input_ids, attention_mask 等建议使用 Dataset.map() 批量处理
编码函数定义定义 def tokenize(examples): return tokenizer(examples["text"], truncation=True, padding=True)传入列表字段
动态填充使用 DataCollatorWithPadding 或自定义 collator自动对齐 batch 内序列长度
模型初始化使用 AutoModel 或从配置初始化,确保 vocab_size 匹配model = AutoModel.from_config(config)
调整嵌入层若添加了新 token,调用 model.resize_token_embeddings(len(tokenizer))必须在训练前执行
训练循环将 encoded 数据送入模型训练可使用 Trainer 或自定义 loop
验证解码在评估时使用 tokenizer.decode() 查看生成文本skip_special_tokens=True 更清晰

6.3 保存与共享分词器配置

方法名语法用途代码示例注意事项
save_pretrainedtokenizer.save_pretrained("save_directory")保存为 Transformers 标准格式fast_tok.save_pretrained("./my_custom_tok/")生成 config.json, tokenizer.json, special_tokens_map.json 等
push_to_hubtokenizer.push_to_hub("your-username/repo-name")推送到 Hugging Face Hubfrom huggingface_hub import login
login(); fast_tok.push_to_hub("my-tok-uncased")
需先登录 huggingface-cli login
保存文件列表config.json, tokenizer.json, special_tokens_map.json, tokenizer_config.json完整保存所有配置可手动编辑后重载不要删除任何文件
从 Hub 加载AutoTokenizer.from_pretrained("your-username/repo-name")下载并加载共享分词器tok = AutoTokenizer.from_pretrained("nlpjungle/my-bio-tok")支持私有仓库(需权限)
版本控制推送时自动支持 Git 版本管理可回退到历史版本建议添加 README.md 说明用途
共享建议在模型卡片中说明训练语料、vocab_size、特殊标记等信息提高可复用性可附带使用示例代码

6.4 处理多语言与子词共享

概念名称说明注意事项
多语言训练语料混合多种语言文本进行训练(如 Wikipedia 多语言 dump)建议按比例采样,避免主导语言垄断
字节级 BPE(Byte-Level BPE)将字符转为字节序列再应用 BPEGPT-2、RoBERTa 使用
统一词汇表所有语言共享同一词汇表,促进跨语言迁移XLM-R、mBERT 采用此策略
子词重用常见子结构(如 “ing”, “tion”)在不同语言中复用提高低资源语言表现
语言标记(lang token)在输入前添加语言 ID 标记(如 <en>, <zh>用于多语言模型控制生成
分词一致性确保不同语言的标点、空格处理一致使用统一 Normalizer(如 NFD + StripAccents)
评估 OOV 率按语言分别统计未登录词比例发现特定语言问题
中文处理策略可结合字级分词或加入中文词典作为初始词汇避免过度拆分为单字

第七章:实战案例

7.1 中文文本分词器训练(基于 BPE)

操作步骤操作细节注意事项
准备中文语料收集中文维基百科、新闻、小说等文本,每行一个句子推荐使用 WikiZh 或 CLUE 数据集
预处理文本可选:进行简体化、去除特殊符号、规范化标点使用 opencc 转换繁体→简体:import opencc; cc = opencc.OpenCC('t2s'); text = cc.convert(text)
初始化 Tokenizer使用 BPE 模型并配置 Normalizer 处理中文字符from tokenizers import Tokenizer, normalizers
from tokenizers.normalizers import NFD, StripAccents
tokenizer = Tokenizer(BPE())
tokenizer.normalizer = normalizers.Sequence([NFD(), StripAccents()])
配置 PreTokenizer使用 Whitespace() 按空格切分(适用于已分词文本)或 CharDelimiterSplit('\n')tokenizer.pre_tokenizer = Whitespace()
定义 Trainer设置 vocab_size=20000,包含中文常用特殊标记from tokenizers.trainers import BPETrainer
trainer = BPETrainer(special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"], vocab_size=20000, min_frequency=2)
训练分词器调用 tokenizer.train(files=["zh_corpus.txt"], trainer=trainer)支持大文件流式读取
测试分词效果encode("自然语言处理很有趣")['自', '然', '语', '言', '处', '理', '很', '有', '趣']若未出现预期子词,检查语料频率
保存分词器tokenizer.save("zh-bpe-tokenizer.json")后续可用于 Transformers 集成

7.2 构建领域专用分词器(医学、法律文本)

领域类型医学文本法律文本对比说明
语料来源PubMed 文摘、临床记录、医学教科书判决书、法律法规、合同模板医学术语高度专业,法律文本结构严谨
特殊词汇”myocardial infarction”, “COVID-19”, “hemoglobin""plaintiff”, “jurisdiction”, “indemnification”建议提前构建领域词典
分词策略使用 BPE 并在 initial_alphabet 中加入希腊字母、化学符号使用 WordPiece,避免将长法律术语过度拆分医学更适合子词,法律可倾向完整词
训练参数vocab_size=50000, min_frequency=1(低频术语重要)vocab_size=30000, special_tokens=["[EVIDENCE]", "[VERDICT]"]医学需更大词汇表
添加领域标记add_special_tokens(["[DIAGNOSIS]", "[TREATMENT]"])add_special_tokens(["[CLAUSE]", "[PARTY_A]"])增强模型对结构的理解
Normalizer 配置保留大小写(如 DNA vs dna),不 strip accents统一小写,标准化引号(” → “)医学术语区分大小写
验证方法检查是否能正确分割 “atrial fibrillation” → [“atrial”, “fibrillation”]测试 “Section 5.2.1” 是否不被拆分可编写单元测试脚本
应用优势显著降低 OOV 率,提升命名实体识别(NER)性能提高法律文档分类和信息抽取准确率领域适配带来 5–15% 性能提升

7.3 处理长文本的滑动窗口编码

方法名语法用途代码示例注意事项
enable_truncationtokenizer.enable_truncation(max_length=512, stride=64, strategy="longest_first")启用带滑动窗口的截断tokenizer.enable_truncation(512, stride=64)stride 表示重叠长度
stride参数 in enable_truncation控制相邻 chunk 的重叠 token 数stride=64 表示每块重复前 64 个 token防止关键信息被切断
Encoding.overflowingencoding.overflowingList[Encoding]获取所有溢出的 chunksfor overflow in encoding.overflowing:
  print(overflow.ids)
主 encoding 是第一块
手动分块结合 offsets 定位原始文本位置用于问答任务中答案定位需记录每个 token 的 char offset
示例流程对 1000 字符文本,max_len=8, stride=2 → 分为多个 8-token chunk,重叠 2 个total_len = 1000
chunk_size = 512
stride = 128
encodings = tokenizer.encode_batch([text[i:i+chunk_size+stride] for i in range(0, total_len, chunk_size)])
实际使用 encode 自动处理
注意事项最大支持长度仍受限于模型(如 BERT 为 512);过多 chunks 影响推理速度;QA 任务中需确保答案落在某个 chunk 内建议设置 return_overflowing_tokens=True在 Transformers 中使用 TruncationStrategy.ONLY_FIRST 等策略

7.4 批量编码与数据集预处理

方法名语法用途代码示例注意事项
encode_batchtokenizer.encode_batch(text_list)高效批量编码texts = ["sentence1", "sentence2", ...]
encodings = tokenizer.encode_batch(texts)
比循环调用快 5–10 倍
启用 Paddingtokenizer.enable_padding(pad_token_id=0)统一对齐序列长度tokenizer.enable_padding()
encodings = tokenizer.encode_batch(texts)
自动生成 attention_mask
启用 Truncationtokenizer.enable_truncation(max_length=512)防止超长输入必须在 encode 前调用否则报错
与 Datasets 库集成dataset.map(tokenize_function, batched=True)直接处理 Hugging Face Datasetdef tokenize(examples):
  return tokenizer(examples["text"], truncation=True, padding=True)
encoded_ds = ds.map(tokenize, batched=True)
推荐方式,支持缓存
返回 Tensor 类型tokenizer(..., return_tensors="pt")直接返回 PyTorch 张量outputs = tokenizer(texts, return_tensors="pt", padding=True)可直接送入模型
性能优化技巧使用 batched=True、预分配内存、固定长度 padding处理百万级样本时显著提速可结合 num_proc 多进程
输出字段input_ids, attention_mask, token_type_ids(如有)标准模型输入格式model(**outputs)注意字段名称一致性
缓存机制map() 自动缓存到磁盘(arrow 文件)避免重复预处理第二次运行极快删除 cache_files 可强制重算

第八章:调试与性能分析

8.1 查看分词结果与解码还原

方法名语法用途代码示例注意事项
encode / encode_batchtokenizer.encode("text")Encoding获取编码对象encoding = tokenizer.encode("Hello world!")返回 Encoding 对象,包含丰富信息
encoding.tokensencoding.tokensList[str]查看分词后的 token 列表print(encoding.tokens) # ['Hello', 'world', '!']最直接的调试方式
encoding.idsencoding.idsList[int]查看对应的 token ID 序列input_ids = encoding.ids模型实际输入
decodetokenizer.decode(ids, skip_special_tokens=True)将 IDs 还原为原始文本text = tokenizer.decode([101, 7592, 2088, 102])skip_special_tokens 可隐藏 [CLS], [SEP]
decode_batchtokenizer.decode_batch(list_of_ids)批量解码texts = tokenizer.decode_batch([[...], [...]])高效查看多个样本
encoding.offsetsencoding.offsetsList[Tuple[int, int]]查看每个 token 在原文中的字符位置start, end = encoding.offsets[0]用于 NER、QA 定位任务
encoding.wordsencoding.wordsList[int or None]标记 token 属于第几个输入”词”(word)words = encoding.words多个 token 可能属于同一个 word
特殊标记可视化结合 special_tokens_mask 区分普通 token 与特殊标记mask = encoding.special_tokens_mask
print([(t, m) for t, m in zip(encoding.tokens, mask)])
便于理解后处理效果

8.2 分析词汇表覆盖度

方法名语法用途代码示例注意事项
get_vocabtokenizer.get_vocab()Dict[str, int]获取完整词汇表映射vocab = tokenizer.get_vocab()
print(f"Vocab size: {len(vocab)}")
返回字典形式,键为 token,值为 ID
token_to_id / id_to_tokentokenizer.token_to_id("token"), tokenizer.id_to_token(100)单个查询if tokenizer.token_to_id("covid") is not None: ...快速验证特定词是否存在
覆盖率计算自定义函数统计语料中可被完全表示的词比例def coverage(text):
  tokens = tokenizer.encode(text).tokens
  words = text.split()
  covered = sum(1 for w in words if any(w in t for t in tokens))
  return covered / len(words)
简化版,实际需更精细对齐
子词分析检查长词是否被合理切分tokenizer.encode("unhappiness")['un', 'happiness']理想情况是语义完整子词
高频词检查统计训练语料词频,对比是否进入词汇表使用 collections.Counter + 分词器验证发现遗漏的重要领域术语
低频词分布分析 vocab 中频率低于阈值的 token 数量[t for t, i in vocab.items() if freq[t] < 5]判断 min_frequency 设置是否合理
Unicode 覆盖测试 emoji、罕见字符是否可表示tokenizer.encode("👍🌍你好")验证多语言支持能力

8.3 监控 OOV(Out-of-Vocabulary)率

方法名语法用途代码示例注意事项
unk_tokentokenizer.unk_tokenstr获取未知标记字符串unk = tokenizer.unk_token # "[UNK]"确保其存在且正确设置
统计 [UNK] 出现次数在 encode 后统计 ids 中对应 [UNK] ID 的数量unk_id = tokenizer.token_to_id("[UNK]")
unk_count = sum(1 for i in encoding.ids if i == unk_id)
基础 OOV 检测方法
OOV 率计算([UNK] 出现次数 / 总 token 数) × 100%total_tokens = len(encoding.ids)
oov_rate = (unk_count / total_tokens) * 100
评估分词器泛化能力
按文本粒度统计对整个数据集计算平均 OOV 率oov_rates = [compute_oov(t) for t in test_texts]
avg_oov = sum(oov_rates) / len(oov_rates)
更具代表性
领域外测试集使用未参与训练的领域文本测试 OOV 率如医学分词器在法律文本上测试检验迁移能力
改进策略添加领域词到训练语料、降低 min_frequency、扩大 vocab_sizeretrain with new config根据 OOV 分析结果优化
注意事项中文因以字切分,OOV 率天然较低;英文专有名词易成 OOV;过度降低 OOV 可能导致词汇表膨胀平衡模型大小与性能

8.4 内存与速度优化技巧

优化方向技巧说明实现方式效果注意事项
使用 Fast Tokenizer基于 Rust 的 tokenizers 库比 Python 实现快 5–10 倍确保使用 AutoTokenizer.from_pretrained(..., use_fast=True)显著提升 encode/decode 速度Transformers 默认优先加载 fast 版本
批量处理批量编码比逐条处理高效得多使用 encode_batch()dataset.map(batched=True)提升 3–8 倍吞吐量减少 Python 循环开销
启用缓存Hugging Face Datasets 自动缓存预处理结果第二次运行跳过计算适合固定数据集缓存文件可能占用磁盘空间
减少输出字段仅请求需要的字段(如只用 input_ids 和 attention_mask)不访问 offsets、overflowing 等减少内存占用特别在推理时有效
固定长度 Padding使用 pad_to_multiple_of=8 或指定 lengthtokenizer.enable_padding(length=512)提高 GPU 利用率,利于 Tensor Core避免动态形状
内存映射(mmap)处理超大语料时不全加载入内存tokenizers 库内部自动支持大文件流式读取支持 >10GB 文本训练确保磁盘 I/O 性能
模型轻量化减小 vocab_size 或使用更简单模型(BPE vs Unigram)训练时设置 vocab_size=10000 而非 50000减少 embedding 层参数权衡 OOV 率
多进程处理利用 CPU 多核并行预处理dataset.map(..., num_proc=4)加速大规模数据集处理进程数不宜超过 CPU 核心数
禁用不必要的组件如无需 truncation/overflow,则不启用避免调用 enable_truncation减少计算路径保持 pipeline 简洁