Tokenizer 对模型性能的影响有多大?同样的架构和数据,只换分词器效果差多少

我最早以为 tokenizer 就是个“切词小工具”,把文本切成小块喂给模型就行了。直到有一次,我用完全相同的 Transformer 架构和训练数据,只是把 tokenizer 从 A 换成了 B,模型的 BLEU 分数直接掉了一大截。那一刻我才意识到:分词器不是一个预处理步骤,它几乎决定了模型能看到的世界的颗粒度。

AI technology illustration

很多人花大量时间调学习率、换注意力头数,却从来没人动过 tokenizer。因为它在模型之外,看起来太普通了。但如果你想真正理解为什么你的模型在某些语言上总是差一截,或者为什么同样的模型换个领域就失效,答案十有八九藏在这一层。

猜词游戏:BPE 怎么把文本切成 token

主流的 tokenizer 不是按词切,也不是按字符切,而是先从一个字符一个字符的“字母表”出发,然后重复统计语料中相邻字符对出现的频率,把最高频的字符对合并成一个“子词”。这个过程叫做 BPE(Byte Pair Encoding),这个方法最早来自压缩算法,后来被神经机器翻译借鉴,见 原始论文

举个例子:假设语料里反复出现 low,那么 BPE 可能会把 lo 合并成 lo,然后再把 low 合并成 low,最终变成一个 token。这样,出现频繁的单词变成单个 token,罕见单词则拆成若干已知子词,兼顾了词表的效率和表达力。

你可能觉得,合并不是挺好的吗?但这里有个微妙的地方:合并是基于统计频率,不是基于语义。比如英文里 there 经常被合并,但 boyfriend 这种有意义的组合反而很少被连在一起,因为它们在语料中不一定相邻出现。所以 BPE 切出来的 token 经常是“看起来古怪”的组合,比如 unable 分离,或 hello 单独出现。这种切分不一定会立即伤害模型,但会影响模型的泛化:同一个词根在不同上下文中被切成不同片段,模型就得分别学习这些片段。

关键问题在于:BPE 的合并规则是从训练语料里统计出来的。你用什么语料训练 tokenizer,它就认为什么内容“常见”。如果你用的是英语维基百科,那么英语子词会被合得很碎;而中文汉字因为总是单字出现,几乎不会被合并。同样的模型结构,换一个语料训练的 BPE,切出来的 token 序列可能完全不同。

WordPiece、Unigram 和 SentencePiece 又是什么

BPE 是贪心合并,WordPiece 则用似然增益来选择合并,Unigram 反过来从一个大词表里逐步删减。它们的具体优化目标不同,但实质都在做“子词折中”。SentencePiece 是一个更底层的框架,它把空格也当成普通字符,支持直接对原始文本训练,不需要预先分词,详见 SentencePiece 论文。这就是为什么日语、中文等没有空格的语言,可以直接用 SentencePiece 做 tokenizer,而不必先做分词。

但别搞混:SentencePiece 不是一种算法,它可以实现 BPE 或 Unigram。我们现在常说的“换 tokenizer”,通常指换的是底层算法、词表大小、训练语料,以及是否把空格作为 token。

同样的数据与架构,只换 tokenizer 影响有多大?具体维度见下表

维度 影响程度 说明
序列长度 极高 同样是“我爱北京天安门”,按字切是8个token,按SentencePiece可能是3-5个。更短的序列意味着更大有效上下文和更低训练成本。
多语言能力 如果tokenizer只在单一语言上训练,其他语言的token碎片化严重,模型很难学到跨语言共性。
罕见词/OOV BPE可以拼出罕见词,但如果切分不稳定,同一词的不同变形会被拆成不同子词,增加学习难度。
代码和数字 代码中大量空格和标点、数字的千分位等,不同tokenizer的切分会导致模型对结构信息的感知不同。

这里的“影响程度”不是凭空说的。几乎所有 tokenizer 相关论文的共识是:tokenizer 决定了语言建模的上限,而模型架构只决定能多接近这个上限。比如 ByT5 论文 就直接做了对比:用一个“字符级”模型去对比 BPE,结果在某些任务上 BPE 占优,但在拼写和噪声文本上字符级更好。哪种更好,取决于任务,也取决于你关心什么语言的 token 效率。

为什么中英文差异这么大?token 数量就是金钱

GPT-2 的 tokenizer 是基于英文语料训练的,它给英文分配了将近 100% 的 token。同样的文本,英文大概 100 个 token,中文可能需要 200 个甚至更多。这意味着在同样的上下文窗口里,中文模型能看到的文本量只有英文的一半。很多中文大模型明明参数量一样,效果缺一截,很大一部分原因就出在这里。

举个例子:“机器学习”这四个字,在英文语料训练的 BPE 中可能被切成“机”、“器”、“学”、“习”,而在中文语料训练的 SentencePiece 中可能变成一个 token“机器学习”。前者意味着模型需要从四个 token 的关系里推理出这个词的含义,而后者直接给了一个概念的完整表征。这个差别在文本分类、情感分析等任务上可能不明显,但在需要精确理解复合词含义的推理任务上,差距就放大了。

你可以这样理解:模型的上下文窗口是“个数”限制,不是“字数”限制。tokenizer 切得越碎,模型一次能读进的内容就越少,复杂推理需要的信息就更难“看全”。所以像 Llama、GPT 等模型在训练 tokenizer 时会专门加入多语语料,把词表做大到 10 万甚至 20 万,就是为了让更多语言变成“高频词”。

一个容易忽略的坑:tokenizer 和预训练模型绑定

我最早犯过这样的错:拿一个别人训练好的中文模型,觉得词表不够好,直接换了个新训练好的 tokenizer,然后想继续微调。结果模型崩溃——因为模型的嵌入矩阵是按旧词表初始化的,新词表和旧词表根本不兼容。这时候必须先初始化新嵌入层,或者干脆重新预训练。换 tokenizer 不是换插件,它是换掉模型的“看世界的方式”,你需要让模型重新学一遍。

这个坑让我后来养成了一个习惯:先看 tokenizer 的训练语料和词表,再决定用不用这个模型。很多开源模型放出时,tokenizer 的细节被一笔带过,但它实际是模型能力的一个隐藏标签。

如何选择/优化 tokenizer:我的几条经验

  1. 用任务相关的语料训练自己的 tokenizer,词表大小 40k-100k 是常见选择。过小会导致序列过长,过大又会让每个 token 的学习样本变稀疏。
  2. 对多语言任务,确保语料分布均衡,并采用 SentencePiece 的 byte_fallback 策略,避免 OOV。
  3. 定期检查 token 长度分布:如果某个语言的 token 数比英文长很多,就说明 tokenizer 对该语言不友好。
  4. 如果你的任务注重拼写、字母级变换(如化学式、表情符号),考虑字符级或字节级 tokenizer,但要做好序列变长的准备。
  5. 对于数字密集型任务,检查 tokenizer 是否把连续数字切成单个数字。GPT-2 的 BPE 会把每个数字单独切出来,这有助于预测每一位,但对于理解大数的整体量级反而有害。部分模型通过数字正则化或更细的切分策略来缓解这个问题。
  6. 不要迷信大模型的 tokenizer。OpenAI 的 tiktoken 分词器非常强大,但它是为通用语料训练的。最适合你的模型,往往是用你的领域语料训练出的专用 tokenizer。可以训练几个不同大小和算法的候选,然后在验证集上比较困惑度和序列长度,选择权衡最优秀的那个。

一个快速评估 tokenizer 质量的方法

拿一个你任务领域的长文档,用 tokenizer 编码,然后看生成的 token 序列是否符合直觉。如果一串自然语言被切成大量碎片,每个汉字都独立出现,那就是词表没学好。更量化的办法是计算“压缩率”——编码后的 token 数量除以原始字符数。对于英文,合格的 tokenizer 应该把平均压缩率做到 4 个字符/token 左右;对于中文,一个好的子词 tokenizer 至少要做到 1.5 个汉字/token 以上。低于这个数,你就该考虑重新训练了。

几个常见误解

换 tokenizer 是不是只要重新预处理数据就行了?

不行。嵌入层和输出层的维度都对应着词表。换了 tokenizer,模型结构即使不变,参数也成了随机初始化。你必须从头训练,或者做嵌入矩阵映射(通常得不偿失)。

词表越大越好吗?

当然不是。词表大了,嵌入层和输出头的参数会大得吓人,训练和推理的显存都会增加。而且很多“生僻词”如果低频,模型根本学不到它们的表示。

同一个 tokenizer 是不是对所有模型都适用?

不一定。任务的性质要求 tokenizer 能覆盖核心词汇。比如代码模型最好用包含空格、缩进 token 的 tokenizer,数学模型则要小心数字切分。

说到底,tokenizer 的下一个形态在哪里?

字节级模型(如 ByT5)把每个 UTF-8 字节当作 token,彻底绕开了“分词”这个概念,所有语言一视同仁。但代价是序列长度暴涨 4-8 倍,计算成本也暴涨。近期的 MEGABYTE 论文引入“分块”思想,试图在字节序列里做局部建模,但训练代价依旧偏高。

我认为,tokenizer 在最近几年内仍然是语言模型的必需品,但它的角色会逐渐被削弱。你会看到越来越多模型采用更通用的空间/字节混合方案,或者像 Mamba 那样用序列状态直接处理原始 token。尽管如此,今天的你在训练或微调模型时,花半天时间检查 tokenizer,绝对是最划算的超参数优化之一。因为改一个学习率可能只带来 0.5% 的提升,而换一个合适的 tokenizer,可能直接让你的模型在一个新语言上的得分上涨十个点。

原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/804.html

(0)
上一篇 2小时前
下一篇 2小时前

相关推荐