【大模型】深度解答:OOV 与 Tokenizer 词汇表共用问题

写在前面:为什么这件事值得关心

很多人觉得"分词而已,差几个 token 有什么关系"。但实际上:同一段文本在不同模型下的 token 计数可能相差显著(实测中 10%~30% 的量级并不罕见,中文场景偏差尤其明显),而 API 是按 token 计费的,上下文窗口是按 token 算的。

行业通用的粗略锚点是:

  • 英文:1 token ≈ 4 个字符 或 ≈ 0.75 个单词
  • 中文:1 token ≈ 0.7~1.5 个汉字

再次强调:这只是行业经验值,唯一准确的数字只能来自对应模型自己的 tokenizer。

本文系统讲清三件事:OOV 是什么、词汇表在模型间如何共用、以及工程上怎么避免踩坑。


一、OOV 是什么?

一句话定义

OOV = Out Of Vocabulary(词表外的词),就是词表里没有收录、tokenizer 不认识的词。

大白话类比

你有一本词典(词表),遇到一个词典里没有的新词(比如"ChatGPT"在 2018 年),这个词就是 OOV。

各代分词算法的 OOV 情况

算法 OOV 严重程度 说明
词级分词 严重 英语常用词约 10~20 万,总词形更多,不可能全收录,遇到新词直接懵
字符级分词 极少 若词表只包含常用 Unicode 字符,仍有罕见字符无法覆盖;但现代大模型极少使用
子词分词(BPE 等) 轻微 罕见词拆成已知子词,大部分词都能处理
字节级 BPE 任何文字最终都能拆成 256 个字节,字节一定在词表里,彻底消除"无法编码"的问题

现代大模型怎么处理 OOV?

字节级 BPE 从根本上消除了"无法编码"的问题。

  • 任何词,哪怕是生僻字、emoji、乱码,最终都能拆成字节
  • 256 个字节是基础 token,一定在词表里
  • 所以 GPT-2 之后的模型,不存在"无法编码"的情况

但"能编码"不等于"切分得好"------一个罕见词可能被拆成 10 个字节碎片,模型理解起来还是困难。

字节级 BPE 的普及,使得每个模型都可以构建一个高度定制化的、覆盖其训练数据特征的词汇表。这种"定制化"正是本文后续讨论的核心:它使得不同模型之间的词汇表几乎不可能通用,从而引出了词汇表管理和共用的工程挑战。


二、每个模型系列都有独立的词汇表吗?

答案:是的,每个模型系列都有自己独立的词汇表。

词汇表(tokenizer)是训练模型时一起确定、一起发布的,不是通用的。

为什么不能通用?

  1. 训练数据不同:A 模型用英文数据多,B 模型用中文数据多,高频词不一样
  2. 词表大小不同:LLaMA 1/2 是 32K,Qwen 是 151,936(约 152K)
  3. 合并规则不同:BPE 合并了哪些字符对,取决于训练数据的统计
  4. 特殊 token 不同 :每个模型加的 <s><pad>、对话标记都不一样

类比

  • 就像每个出版社都有自己的词典------商务印书馆的《现代汉语词典》和上海辞书出版社的《辞海》,收词、释义、编号都不一样
  • 你不能用商务印书馆的词典页码,去查辞海里的词

三、同一系列的模型可以共用词汇表吗?

✅ 同一系列的同代模型通常共用

同一个模型家族的同代版本,共用同一个 tokenizer:

模型系列 共用 tokenizer 的版本 备注
LLaMA LLaMA-7B / 13B / 33B / 65B 共用 可通过 HuggingFace 仓库验证 tokenizer 文件完全一致
LLaMA2 LLaMA2-7B / 13B / 70B 共用 同上
Qwen(千问) Qwen-1.8B / 7B / 14B / 72B 共用 词表约 152K,中文优化
GPT-4 / GPT-3.5-turbo 共用 cl100k_base OpenAI 官方文档确认
GPT-4o 使用更新的 o200k_base 与 GPT-4 不共用

为什么要共用? 共用 tokenizer 意味着不同参数规模的模型处在同一个"语言空间"中,可以做 logit 蒸馏、模型权重合并(model merging)等操作。

需要澄清一个常见的误解:无论使用何种微调方法(包括 LoRA),都必须使用与基座模型完全相同的 tokenizer。 因为模型的 embedding 层权重是与词汇表一一对应的,tokenizer 变了,输入的 token id 就失去了原有语义,模型会彻底失效。

至于"共用 tokenizer 便于做 logit 蒸馏、模型权重合并",那是在同一个 tokenizer 空间下,不同参数规模的模型才能进行权重层面的融合。LoRA 本身只作用于 attention 的线性层,不直接修改 embedding,但这并不代表它可以跨越不同的 tokenizer。

⚠️ 跨代可能更换

同一系列的不同代际之间,tokenizer 可能更换,典型案例:

  • LLaMA 2 → LLaMA 3:Llama-3 将 tokenizer 由 SentencePiece 换成了 tiktoken,与 GPT-4 保持一致,同时词表大小由 32k 扩展到了 128k。这是教科书级的"跨代换 tokenizer"案例。
  • Qwen 2 → Qwen 2.5:基础词表没变(都是 152k 个常规 token),但控制 token 从 3 个扩展到了 22 个,新增了工具调用等专用 token。词表主体兼容,但特殊 token 集合已经不同。

补充一个容易搞混的点:LLaMA 1 和 LLaMA 2 的 tokenizer 是同一个。 Llama 2 论文明确写道:"Tokenizer:我们使用与 Llama 1 相同的 tokenizer,它采用了字对编码(BPE)算法,使用了来自 SentencePiece 的实现......总词汇量为 32k 个标记。" 所以"同系列跨代一定换 tokenizer"是不成立的,具体必须查阅官方文档确认。

❌ 跨系列不能共用

  • 千问(Qwen)的 tokenizer ≠ 豆包(Doubao)的 tokenizer
  • 千问 ≠ LLaMA ≠ GPT ≠ Claude
  • 不同公司、不同系列,词汇表几乎都不一样

关于豆包(Doubao):它是字节跳动出品,有自己独立训练的 tokenizer,系列内部各版本共用。但其具体词表大小和合并规则未完全公开,以上基于公开信息整理,具体以官方文档为准。


四、词汇表是每次都重新计算吗?

答案:不是,是训练时一次性确定,推理时加载固定文件。

训练阶段

  1. 收集海量训练文本
  2. 运行 BPE/Unigram 算法,统计频率,合并出词表
  3. 词表确定后,保存为文件(tokenizer.json、vocab.json、merges.txt 等)
  4. 用这个固定的 tokenizer 处理所有训练数据,训练模型

推理阶段

  1. 加载模型权重 + 加载 tokenizer 文件
  2. 用户输入 → 按固定规则切分 → 查表得到 id
  3. 不会重新计算词表,切分规则是固定的

类比:训练时 = 编词典,花了很大功夫统计、合并,最终出版一本固定词典;推理时 = 用这本出版好的词典查词,不会每次重新编词典。


五、不同模型分词不一样,是因为代码实现不同吗?

答案:算法可能相同,但词表数据不同,所以结果不同。

代码实现层面,大多数模型用的分词算法框架是一样的:

  • HuggingFace tokenizers 库(Rust 实现,速度快)------大部分开源模型
  • tiktoken------OpenAI 模型使用的分词库,已开源,可独立调用
  • SentencePiece------多语言模型、T5 等

算法框架可能都是 BPE,但切分结果不同,因为:

  1. 词表内容不同:合并了哪些字符对、每个 token 的 id 是多少,完全不同

  2. 预处理规则不同

    • 是否在词前加空格?
    • 是否统一小写?
    • 数字怎么处理?
    • 中文是按单字切、按字节回退,还是把高频二字词/三字词直接收录进词表?

    在 HuggingFace 的 tokenizers 库中,这些规则被明确地区分为 normalizer(文本规范化,如 Unicode 归一化、大小写转换)和 pre_tokenizer(预切分,如按空格或字节切分)。当你需要调试分词差异时,可以直接打印 tokenizer 对象的这些属性来查看具体配置。

  3. 特殊 token 不同<s></s><pad> 的 id 和数量不同

  4. 合并顺序不同:BPE 是按频率依次合并,合并顺序不同,最终切分结果不同

类比:两个人都用"按频率合并"的方法编词典,但一个用人民日报数据编,一个用小说数据编,最终两本词典的收词、编号都不一样,查同一个词结果当然不同。


六、调用公共 SDK 分词,结果就一样了吗?

答案:取决于用的是谁的 SDK、哪个模型。

情况 1:同一个 SDK + 同一个模型 → 结果一样

  • 用 OpenAI SDK 调 gpt-3.5-turbo,不管谁调用、在哪调用,分词结果都一样,因为 SDK 内部固定用 cl100k_base
  • 用 HuggingFace 加载 Qwen-7B,每次加载的都是同一个 tokenizer 文件,结果一样

情况 2:不同 SDK / 不同模型 → 结果可能不一样

  • 用 OpenAI SDK 分词 vs 用 HuggingFace 加载 LLaMA 分词 → 结果不同
  • 用千问的 tokenizer vs 用豆包的 tokenizer → 结果不同
  • 即使都是"中文分词",切出来的 token 数量和内容都可能不同

情况 3:同一个 SDK 但不同模型 → 看是否共用 tokenizer

OpenAI 的完整映射关系如下:

编码 对应模型
o200k_base GPT-4o、GPT-4.1、GPT-5、o1、o3、o4-mini
cl100k_base GPT-4、GPT-4 Turbo、GPT-3.5 Turbo
p50k_base text-davinci-003、Codex
r50k_base GPT-3

所以:gpt-3.5-turbo 和 gpt-4 都用 cl100k_base → 结果一样;但 gpt-4o / gpt-5 / o1 / o3 用 o200k_base → 结果不同;text-davinci-003 用 p50k_base → 结果不同。

HuggingFace 侧:Qwen-7B 和 Qwen-72B 共用 → 一样;Qwen-7B 和 LLaMA-7B → 不一样。

实践经验

  • 不要假设"都是中文分词结果就一样"
  • 不要假设"都是 BPE 结果就一样"
  • 必须用对应模型的官方 tokenizer 来分词

七、分词结果不一样,怎么控制差异?

核心原则:用哪个模型,就必须用哪个模型的 tokenizer。

1. 模型和 tokenizer 从同一路径加载,并使用官方 tokenizer 对象

python 复制代码
from transformers import AutoTokenizer, AutoModelForCausalLM

# 模型和 tokenizer 从同一个路径加载,保证配对
model_path = "Qwen/Qwen-7B"
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForCausalLM.from_pretrained(model_path)

不要自己单独搞一个 tokenizer,不要用别的模型的 tokenizer。新手常犯的错误是自行解析 vocab.jsonmerges.txt 实现分词,极易遗漏预处理规则(如空格折叠、Unicode 归一化)------始终使用 AutoTokenizer.from_pretrained() 加载的对象。

工程提示: 对于部分模型(如早期的 Qwen 版本),HuggingFace 要求设置 trust_remote_code=True 才能加载其自定义的分词器代码。在使用前,请确保你了解并信任该模型仓库的代码。

2. 微调时必须用基础模型的 tokenizer

  • 用 Qwen-7B 做基础模型微调 → 必须用 Qwen 的 tokenizer 处理训练数据
  • 不能用 LLaMA 的 tokenizer 处理数据,然后送入 Qwen 模型
  • 否则 id 对不上,模型看到的是乱码

3. 跨模型迁移时,必须重新分词

  • 从 LLaMA 迁移到 Qwen → 所有训练数据、向量库、缓存都要用 Qwen 的 tokenizer 重新处理
  • RAG 的向量库:如果换了 embedding 模型(tokenizer 也换了),所有文档必须重新向量化

4. 不要自己实现分词逻辑

  • 不要写"按空格切""按标点切"这种自定义逻辑
  • 用官方提供的 tokenizer,它包含了所有预处理规则、特殊 token 处理
  • 自己实现几乎一定会出错

5. 评估 token 数量时,用对应模型的 tokenizer

"1000 字等于多少 token"没有统一答案,中文的 token 比例因模型而异:

情况 切分方式 1 字 ≈ X token
中文优化词表(Qwen 等) 常用字直接注册为 token,非常用字拆字节 平均 1~1.5
非中文词表(LLaMA 32k) 多数汉字不在词表,拆成 UTF-8 字节 token,部分字节对可能合并 2~3
GPT-4 (cl100k_base) 介于两者之间,对中文有一定覆盖但不如 Qwen 1.5~2

算 API 费用、上下文窗口时,必须用对应模型的 tokenizer 计算。

6. 确保 tokenizer 来源和版本一致

BPE 的合并顺序由训练语料和算法确定性决定,不会因为"下载渠道"而变化。真正需要注意的是:同一模型系列的不同代际(如 Qwen2 与 Qwen2.5)发布的 tokenizer 文件本身可能不同,此外社区转换的第三方版本与官方发布版本也可能存在细微差异。始终从模型的官方仓库获取 tokenizer。


八、常见踩坑场景

坑 1:用 A 模型的 tokenizer 处理数据,训练 B 模型

  • 表现:模型不收敛、输出乱码、效果极差
  • 原因:id 完全对不上,模型看到的是错误的输入
  • 解决:确保 tokenizer 和模型配对

坑 2:RAG 换了 embedding 模型,但没重建向量库

  • 表现:检索准确率暴跌,返回不相关文档
  • 原因:主因是 embedding 模型换了,向量空间完全不同(即使 tokenizer 一样也不兼容);此外 tokenizer 变化还会导致同一段文本被切成不同片段,进一步加剧不一致
  • 解决:换 embedding 模型必须重建整个向量库

坑 3:微调时加了自定义 token,但没正确处理

  • 表现:自定义 token 被拆成碎片,模型识别不了
  • 原因:词表里没有这个 token,tokenizer 把它拆成了子词
  • 解决
python 复制代码
# 添加特殊 token(如对话标记、角色标识)
tokenizer.add_special_tokens({"additional_special_tokens": ["<|my_token|>"]})

# 或添加普通词汇(领域术语、产品名等)
tokenizer.add_tokens(["新词"])

# 两者都需要扩展 embedding 层,否则新 token 没有对应的向量
model.resize_token_embeddings(len(tokenizer))

注意: 添加 token 后,必须调用 model.resize_token_embeddings(len(tokenizer)) 来扩展模型的 embedding 层矩阵。否则,当模型遇到新 token 的 ID 时,会因为索引超出 embedding 矩阵的维度而直接报错。

坑 4:不同框架的 tokenizer 结果不一致

  • 表现:HuggingFace 切出来是 100 个 token,另一个框架切出来是 105 个
  • 原因:预处理规则有细微差异(比如空格处理、Unicode 归一化)
  • 解决:训练和推理用同一个框架的同一个 tokenizer

坑 5:多模态模型的 tokenizer 包含图像 token

  • 表现:直接传文本给多模态 tokenizer 没问题,但如果要处理图像,需要按特定格式传入
  • 补充:一张图片通常会被编码为几十到几百个 Vision Token,具体数量取决于图像分辨率和切块策略(例如,常见的 CLIP ViT-L/14 架构处理一张 224x224 图片会固定产生 256 个图像块 token 加 1 个分类 token,共 257 个)。这部分 token 同样计入上下文窗口和 API 费用------很多团队在接入多模态模型后上下文突然爆掉,就是没把图像 token 算进去
  • 解决:查阅模型文档,了解图像 token 的插入方式,并在容量规划时预留图像 token 预算

九、附录:Tokenizer 相关文件简介

文件 格式 说明
tokenizer.json JSON HuggingFace tokenizers 库的统一格式,包含词表、合并规则、预处理配置
vocab.json + merges.txt JSON + 文本 GPT-2 风格的 BPE 格式,vocab 存 token→id,merges 存合并规则
tokenizer.model 二进制 SentencePiece 的模型文件,包含词表和训练参数
tokenizer_config.json JSON 加载时的预处理配置(最大长度、特殊 token 行为等)
special_tokens_map.json JSON 特殊 token 到具体字符串的映射

后两个文件正是"为什么手动解析 vocab.json 会出错"的直接原因------AutoTokenizer.from_pretrained() 加载时会读取这些配置来完成预处理和特殊 token 注入,光有词表和合并规则是不够的。


十、全文要点回顾

  • OOV 已被字节级 BPE 消除,但"能编码"不等于"切分得好",罕见词仍可能被切成大量碎片。
  • 每个模型系列都有独立的词汇表,训练时一次性确定,推理时加载固定文件,不会重新计算。
  • 同系列的同代模型通常共用 tokenizer,跨代可能更换,跨系列不能共用(LLaMA 1/2 共用,LLaMA 3 换成了 tiktoken + 128k 词表)。
  • 不同模型分词结果不同,主要是词表数据和预处理规则不同,不是算法框架不同。
  • 调用公共 SDK 时,同一个模型的分词结果一致,但跨模型不一致。
  • 控制差异的唯一正确做法:用哪个模型就必须用哪个模型的官方 tokenizer,绝不混用;跨模型迁移时必须重新分词处理所有数据。

一句话总结:OOV 已被字节级 BPE 消除,但词汇表因模型而异、同系列共用、跨系列不共用;控制差异的唯一方法是始终使用对应模型的官方 tokenizer。

相关推荐
2301_786756601 小时前
不做分子仿真、不布局自动化实验室,垂直科研智能平台同样跑通 AI for Science 可持续变现
运维·人工智能·自动化
vivo互联网技术1 小时前
Octopus:基于无历史数据的梯度正交化的学习框架|CVPR 2026
深度学习·计算机视觉·llm
TMT星球1 小时前
快手Q2总收入355亿元:月活近8亿,核心商业收入同比增长7.4%
大数据·人工智能
霸道流氓气质1 小时前
ima.copilot-AI知识库-完整使用手册与最新动态
人工智能·copilot
牧羊人.3331 小时前
计算机视觉基础 第 9 章|实战:银行卡号识别
图像处理·人工智能·opencv·计算机视觉·图搜索算法
CRMEB1 小时前
商城大促活动策划全流程:从定目标到复盘四阶段
java·开发语言·人工智能·ai·开源·php
ClouGence1 小时前
自动化测试实战:手把手教你用 AI Agent 实现版本打包自动化
人工智能·ai编程·测试
vivo互联网技术1 小时前
协同文档下的 Agent 协作闭环:可回滚、可对比的透明化编辑实现
人工智能·笔记·agent
khs185718375272 小时前
AI液冷技术应用:超纯水保障算力芯片运行
人工智能·经验分享