模型不认识字

在你的屏幕上,“猫”是一个字;在模型眼里,它是一个整数,比如 37286。

中间这个翻译环节,就是分词器(Tokenizer)。它要回答一个问题:怎么把任意一段人类文字,切成模型认识的“碎块”,并给每个碎块一个编号?

切得好不好,直接影响模型的能力与成本——这事比看起来重要。

三种切法,两种不行

按字切:每个字一块。问题很直接:序列太长。

一句话几十个字,就是几十块,模型的处理成本随之上涨。英文更明显:一个单词被拆成七八个字母,模型还得在很短的距离里学会拼单词,纯属浪费算力。

按词切:每个词一块。问题更严重。

词是无穷多的。“内卷”“元宇宙”“vibe coding”都是新词,词表根本追不完。一旦遇到词表里没有的词,模型只能把它当不存在。

还有形态问题:run / runs / running 是一个词家族,按词切会变成互不相干的块,模型得重新学一遍。

折中:子词(subword)。这是今天几乎所有主流模型的选择——常见词整块保留,长词拆成更有意义的片段。

子词是怎么被“选”出来的

最经典的方法叫 BPE(字节对编码),过程大致是这样:

  1. 一开始,把所有文字都拆成最小单位(字节或字符)
  2. 统计哪种“相邻组合”出现得最多
  3. 把出现最多的那一对,合并成一个新块
  4. 重复几万次,直到词表装满

比如大量中文语料里,“天”和“气”总是挨着出现——合并成“天气”。高频组合不断被吸收成整块,低频的保持碎块状态。

结果是一个自动平衡的词表:常用的整块直达,生僻的拆碎处理,永远没有“词表外”这个概念。再离谱的新词,也不过是“较多碎块的组合”而已。

中文的特殊处境

一个汉字通常对应几个 token,具体看模型怎么设计词表:有的按字节切(一个汉字要拆成好几个字节块),有的按字切,有的把高频词整体收进去。

同样是表达一个意思,中文消耗的 token 通常比英文更多——这直接关联两件事:你付的 API 费用,和模型能“看到”多长的上下文。

墨造旁注:同样大小的上下文窗口,装中文会比装英文少一截内容。做长文任务时,这个差距会真实咬人。

一个具体例子

拿“墨造”这两个字来说。如果词表里“墨”是单字块,而“造”被拆成字节,那么它们进入模型时可能是:墨 + 造⁽¹⁾ + 造⁽²⁾——三个 token,而不是两个。

你看不见这件事,但每次调用 API 的账单里,它的痕迹都在。

为什么这对你重要

下一站

数字从哪来解决了。但 37286 只是一个编号,它本身没有任何含义。

下一篇,看模型怎么把编号变成真正的“意义载体”——词嵌入。