🤗 Transformers Tokenizer 完全指南:从 PreTrainedTokenizer 到多模态特殊 Token 管理
本文以 docs/source/ko/main_classes/tokenizer.md 为基础撰写,并深入当前仓库源码验证实现细节。
🤗 Transformers 中的 Tokenizer(分词器) 负责将原始文本转换为模型可消费的数值输入,是任何 NLP/多模态模型推理与训练流程的第一环。本文围绕该仓库的 tokenizer 主类文档,系统讲解纯 Python 实现与基于 Rust 的 "Fast" 实现两套体系、核心基类 PreTrainedTokenizer / PreTrainedTokenizerFast、编码输出容器 BatchEncoding,以及多模态模型(如 LLaVA)特有的 extra_special_tokens 扩展机制。读完本文,你将掌握 tokenizer 的完整调用链、各类特殊 Token 的管理方法,以及如何在多模态场景下为 tokenizer 挂载自定义特殊 Token。
一、Tokenizer 在模型输入管线中的角色
一个 tokenizer 的核心职责是为模型准备输入:把字符串切分为子词(sub-word)token,再将 token 字符串映射为整数 ID,并生成 attention_mask 等模型输入。该库为几乎所有模型都内置了对应 tokenizer。
从源码结构看,tokenizer 体系呈现"基类 + 后端"的分层设计:
PreTrainedTokenizerBase:所有 tokenizer 后端的公共基类,定义在 tokenization_utils_base.py,负责编码方法、特殊 Token 管理、加载/保存等公共逻辑;PythonBackend:慢速(slow)tokenizer 的基类,定义在 tokenization_python.py,并在同文件末尾以PreTrainedTokenizer = PythonBackend对外暴露;TokenizersBackend:Fast tokenizer 的基类,定义在 tokenization_utils_tokenizers.py,同样以PreTrainedTokenizerFast = TokenizersBackend对外暴露;SentencePieceBackend:SentencePiece 系 tokenizer 的专用后端,定义在 tokenization_utils_sentencepiece.py。
也就是说,绝大多数模型 tokenizer 都是 PreTrainedTokenizer(Python)或 PreTrainedTokenizerFast(Fast)的子类,而这两个类共同继承 PreTrainedTokenizerBase。
二、两种实现:纯 Python 与 "Fast"(Rust)
库中绝大多数 tokenizer 都提供两个版本:
- 纯 Python 实现:完整、易读,不依赖外部编译库;
- "Fast" 实现:基于 Rust 库 🤗 Tokenizers 构建,带来两大核心优势:
- 显著的加速,尤其在批量 tokenization(batch tokenization)场景下;
- 额外的对齐方法,可在原始字符串(字符与单词)空间与 token 空间之间互相映射,例如获取"包含某个字符的 token 的索引",或"某个 token 对应的字符区间(span)"。
Fast 版本带来的对齐能力在问答、抽取式任务与可视化场景中极为实用,例如定位某段答案文本落在哪些 token 上。
两个基类 PreTrainedTokenizer 与 PreTrainedTokenizerFast 实现了以下公共能力:
- 加载与保存:从本地文件/目录实例化 tokenizer,或从库提供的预训练 tokenizer(经 Hub 下载)加载,也可将 tokenizer 保存到本地;
- 编码与解码:tokenization(字符串 → 子词 token 字符串)、token 字符串 ↔ ID 互转,以及 encoding/decoding(tokenize + 转为整数,以及逆过程);
- 词汇扩展:以与底层结构(BPE、SentencePiece、WordPiece 等)无关的方式向词汇表添加新 token;
- 特殊 Token 管理:添加 mask、句首(BOS)等特殊 token,将其绑定到 tokenizer 属性以便随时访问,并确保这些 token 在 tokenization 过程中不会被切开(默认
split_special_tokens=False)。
判断当前 tokenizer 是否为 Fast
PythonBackend.is_fast 属性返回 False(见 tokenization_python.py),Fast 后端则返回 True。也可直接通过 tokenizer.is_fast 在运行时判断,并据此决定能否使用对齐类方法。
三、核心方法速览
依据原文档的 autodoc 列表,PreTrainedTokenizer 与 PreTrainedTokenizerFast 暴露的核心方法完全一致:
| 方法 | 作用 |
|---|---|
__call__ |
主入口方法,完成单条/批量文本的编码,返回 BatchEncoding |
encode / decode |
文本 → ID 列表 / ID 列表 → 文本 |
batch_decode |
批量解码多个 ID 序列为文本 |
add_tokens |
向词汇表添加新 token(与底层算法无关) |
add_special_tokens |
添加特殊 token 并自动绑定属性 |
apply_chat_template |
将消息列表按模型对话模板渲染为输入 |
push_to_hub |
将 tokenizer 上传共享到 Hub |
all |
其余全部公开方法(详见 autodoc 文档) |
__call__ 的完整签名
以 tokenization_utils_base.py 中 PreTrainedTokenizerBase.__call__ 的签名为依据,常用参数包括:
text/text_pair:单条或批量文本,支持str、list[str](视为已预分词时需设is_split_into_words=True)、list[list[str]];text_target/text_pair_target:序列到序列任务中的目标端文本;add_special_tokens:是否自动添加特殊 token,默认True;padding:bool | str | PaddingStrategy,默认False,可传"max_length"或"longest";truncation:bool | str | TruncationStrategy,默认None,可传"longest_first"/"only_first"/"only_second";max_length、stride:截断与滑窗参数;pad_to_multiple_of:填充对齐到某个数的整数倍(配合padding="max_length"使用);padding_side:"right"或"left",覆盖 tokenizer 默认的padding_side;return_tensors:"pt"/"tf"/"np",返回 PyTorch / TensorFlow / NumPy 张量;return_token_type_ids、return_attention_mask、return_overflowing_tokens、return_special_tokens_mask、return_offsets_mapping、return_length:控制返回哪些字段。
调用示例:
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
batch = tokenizer(
["Hello world", "Second sentence"],
padding="longest",
truncation=True,
max_length=128,
return_tensors="pt",
)
# batch["input_ids"], batch["attention_mask"] 可直接送入模型
四、BatchEncoding:编码输出容器
BatchEncoding 是 PreTrainedTokenizerBase 的编码方法(__call__、encode_plus、batch_encode_plus)的输出容器,定义在 tokenization_utils_base.py,继承自 Python 字典(UserDict),因此可以像普通字典一样使用。
- 当 tokenizer 为纯 Python 实现时,
BatchEncoding行为与标准 Python 字典完全一致,持有input_ids、attention_mask等模型输入; - 当 tokenizer 为 Fast 实现时,
BatchEncoding额外携带tokenizers.Encoding信息(构造时通过encoding参数传入,见 L222-L243),从而提供字符串空间与 token 空间之间的高级对齐方法,例如token_to_chars、char_to_token、word_to_tokens、sequence_ids等。
此外,BatchEncoding 在构造时可指定 tensor_type 与 prepend_batch_axis,将整数列表直接转换为 PyTorch / NumPy 张量(convert_to_tensors),并可通过 n_sequences 获知每条样本由 1 条还是 2 条序列构成。
五、多模态 Tokenizer 与 extra_special_tokens
什么是多模态 Tokenizer
任何一个 tokenizer 都可以成为 "multimodal"(多模态)tokenizer:即 tokenizer 会把所有相关特殊 token 作为自身属性保存,便于随时访问。例如从 LLaVA 这类视觉-语言模型加载 tokenizer 后,可以直接通过 tokenizer.image_token_id 拿到用作占位符的特殊图像 token。
这在模型实现中确实是硬依赖:以 modeling_llava.py 为例,LLaVA 前向时会用 config.image_token_id 构造图像 token 掩码并定位 input_ids == image_token_id 的位置以插入图像特征,因此该 token 必须是 tokenizer 可访问的固定 ID。
启用额外特殊 Token
任何类型的 tokenizer 都可启用额外特殊 token:通过 extra_special_tokens 传入,然后保存 tokenizer。额外特殊 token 不必与某种模态强绑定,凡是模型需要频繁访问的 token 都可以放进来。原文档示例中,保存在 output_dir 的 tokenizer 将直接拥有三个额外特殊 token:
from transformers import AutoTokenizer
vision_tokenizer = AutoTokenizer.from_pretrained(
"llava-hf/llava-1.5-7b-hf",
extra_special_tokens={"image_token": "<image>", "boi_token": "<image_start>", "eoi_token": "<image_end>"}
)
print(vision_tokenizer.image_token, vision_tokenizer.image_token_id)
# ("<image>", 32000)
底层实现原理
结合 tokenization_utils_base.py 源码,extra_special_tokens 的完整处理链如下:
- 兼容旧参数:V5 起若检测到已废弃的
additional_special_tokens会先转换为extra_special_tokens(L996-L998); - 字典形式 → 模型专属特殊 Token:当
extra_special_tokens传入的是dict时,调用_set_model_specific_special_tokens(L1401-L1416),将{"image_token": "<image>"}这样的键值对追加进SPECIAL_TOKENS_ATTRIBUTES并写入_special_tokens_map,这正是示例中tokenizer.image_token属性得以存在的根本原因;若是list/tuple则存入_extra_special_tokens(L1029-L1038); - 未入词汇表则自动追加:初始化末尾(tokenization_python.py)会把不在词汇表中的特殊 token 以
special_tokens=True方式加入,因此<image>会自动获得新的 token ID(示例中为 32000); - 统一暴露:
all_special_tokens/all_special_ids(L1365-L1399)会将命名特殊 token 与额外特殊 token 合并去重后返回; - 持久化:保存 tokenizer 时,
tokenizer_config会写入extra_special_tokens字段(L2054-L2055),保证重新加载后属性依然可用。
额外特殊 token 会被视为 added tokens,在 tokenization 时不会被切开,且在解码时若设置 skip_special_tokens=True 也会被正确跳过(L949-L952)。
注意事项
extra_special_tokens支持list[str]、tuple或dict[str, str]三种形式,其他类型会抛出TypeError(L1038);- 字典的 key 若以
_token结尾且不在标准SPECIAL_TOKENS_ATTRIBUTES中,还会触发自动探测逻辑(L1039-L1047),即某些模型子类可能自动识别xxx_token类参数为模型专属 token; - 修改特殊 token 后必须保存 tokenizer,否则重新加载时属性不会保留;
- 命名特殊 token(
bos_token、eos_token、pad_token、cls_token、mask_token、unk_token、sep_token,见 L980-L988)与额外特殊 token 在 V5 中做了清晰分离,取用时应按需区分。
六、Fast tokenizer 的导入与相关文档
PreTrainedTokenizerFast 依赖 🤗 Tokenizers(Rust 库)。从该库获得的 tokenizers.Tokenizer 对象可以非常简单地加载进 🤗 Transformers——只需将 Rust 对象或 tokenizer.json 文件路径传给 Fast tokenizer 的构造/加载方法即可;TokenizersBackend.convert_to_native_format(tokenization_utils_tokenizers.py)正是负责从 tokenizer.json、sentencepiece 模型、vocab/merges 等序列化文件重建 Rust 后端的核心逻辑。
详细导入方法可参阅仓库内的《Using tokenizers from 🤗 tokenizers》页面(对应英文版为 fast_tokenizers.md)。
七、快速实践清单
from transformers import AutoTokenizer
# 1. 加载(默认优先 Fast 版本)
tok = AutoTokenizer.from_pretrained("bert-base-uncased")
print(tok.is_fast) # 判断是否为 Fast 实现
# 2. 编码与解码
ids = tok.encode("Hello world", add_special_tokens=True)
text = tok.decode(ids, skip_special_tokens=True)
# 3. 添加词汇(与 BPE/SentencePiece 结构无关)
tok.add_tokens(["my_new_token"])
print(tok.convert_tokens_to_ids("my_new_token"))
# 4. 添加特殊 token 并绑定属性
tok.add_special_tokens({"eos_token": "<|endoftext|>"})
# 5. 批量编码为张量
enc = tok(["a b c", "d e"], padding=True, truncation=True, return_tensors="pt")
# 6. 多模态额外特殊 token(保存后重新加载仍可用)
tok.save_pretrained("./my_tokenizer")
reloaded = AutoTokenizer.from_pretrained("./my_tokenizer")
总结而言,tokenizer 层是 Transformers 输入管线的统一入口:PreTrainedTokenizerBase 定义了公共协议,PythonBackend 与 TokenizersBackend 分别承载慢速与 Fast 实现,BatchEncoding 统一输出,而 extra_special_tokens 机制则为多模态模型(如图像 token)提供了标准化的特殊 token 挂载方式——这四条主线构成了理解本仓库全部 2000+ 模型 tokenizer 的完整框架。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0634
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
jforgamejforgame是一个一站式游戏服务器开发框架。包含游戏服务器开发所需要的各种组件,比如网关,socket服务端与客户端,自定义高效消息编解码,游戏热更新,游戏通用工具等等。包含游戏服,跨服,匹配服,后台管理系统等实现,同时提供大量业务案例以供学习。亦可用于其他socket应用,例如及时聊天等。Java01
fizz-gateway-nodeAn Aggregation API Gateway in Java . FizzGate 是一个基于 Java开发的微服务聚合网关,是拥有自主知识产权的应用网关国产化替代方案,能够实现热服务编排聚合、自动授权选择、线上服务脚本编码、在线测试、高性能路由、API审核管理、回调管理等目的,拥有强大的自定义插件系统可以自行扩展,并且提供友好的图形化配置界面,能够快速帮助企业进行API服务治理、减少中间层胶水代码以及降低编码投入、提高 API 服务的稳定性和安全性。Java00
certd开源SSL证书管理工具;全自动证书申请、更新、续期;通配符证书,泛域名证书申请;证书自动化部署到阿里云、腾讯云、主机、群晖、宝塔;https证书,pfx证书,der证书,TLS证书,nginx证书自动续签自动部署JavaScript00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00