从 Token 到 Transformer · 01 / 10
把“周末去上海”发给大模型时,我们很容易想象:模型先看见这五个汉字,再开始理解句子。真实过程更机械,也更有意思。模型首先接到的不是文字,而是一串整数;在整数出现之前,文本还要经过 tokenizer,被切成一组来自固定词表的 token。
这一步经常被一句“把文字转成数字”带过。但 tokenization 决定了模型能看见什么、一次能装下多少内容,也决定了后续计算从哪里开始。它不是清洗数据的边角工作,而是语言与神经网络之间的接口。
模型为什么不能直接读字符串
神经网络的输入必须是形状明确的数值张量。Unicode 字符串没有可以直接参与矩阵乘法的形式,所以系统要先建立一套有限的词表,再把输入映射到词表中的编号。
一条简化后的链路是:
text原始文本 -> token 列表 -> token ID -> embedding 向量 -> Transformer 层
这里最容易混淆的是 token 与 token ID。token 是切分后的符号,ID 只是它在词表中的索引。编号 1532 并不比编号 97 “更有意义”,它只负责找到 embedding 表中的对应行。
为什么不按字或单词切
字符级方案很稳:只要字符集覆盖足够广,几乎不会遇到未知词。但它会把序列拉得很长。“tokenization” 若按字符拆开,需要模型重新学习这些字符如何组合成一个概念。
整词方案正好相反。常见词可以保留完整语义单位,但现实语言是开放的:新产品名、拼写变体、代码标识符和长尾术语每天都在出现。把所有完整单词收入固定词表既不现实,也会让词表膨胀。
现代语言模型因此多采用子词切分。高频片段可以作为一个 token,罕见词则拆成更小的已知片段。它是在三个目标之间做折中:
- 词表不能无限大;
- 序列不能被切得过长;
- 没见过的词仍要能够表示。
同一个词在不同 tokenizer 中可能有完全不同的切法。Token 不是语言里天然存在的原子,而是模型训练方案的一部分。
中文也不是“一个字一个 Token”
英文有空格,中文没有天然的词边界,但这不意味着中文一定逐字切分。具体结果取决于词表、训练数据和算法。常见短语可能整体进入词表,生僻字可能退化为更小的字节表示,中英文混排、数字和标点也会改变切分。
这解释了一个常见现象:肉眼看起来同样长的两段文本,在不同模型里可能消耗不同数量的 token。比较模型的上下文或调用成本时,不能用“一个汉字约等于一个 token”当作稳定规则,应该使用目标模型自己的 tokenizer 统计。
Tokenizer 还负责输入协议
Tokenizer 不只切正文。它通常还会加入一些特殊标记,用来表达会话边界、角色、填充或任务结构,例如序列开始、序列结束和 padding。随后,运行时还会生成 attention_mask 等辅助字段,告诉模型哪些位置是真实输入,哪些只是为了对齐批次而补出来的空位。
因此,一条聊天消息真正进入模型时,往往包含用户看不到的模板和控制 token。上下文窗口计算的是这整套序列,而不只是输入框里的正文。
Tokenization 会留下什么影响
切分方式会沿着整条系统链路向后传递:
- 切得更碎,序列通常更长,占用更多上下文;
- 专业词被反复拆散,模型需要更多层计算才能组合出稳定表示;
- 代码、表格和多语言文本的 token 密度差异,会影响吞吐与费用;
- 特殊 token 和聊天模板设计,会影响模型学到的交互边界。
这并不意味着“token 越少越好”。一个极小词表会让序列过长,一个极大词表则会增加 embedding 参数并稀释长尾 token 的训练机会。好的 tokenizer 是面向数据、模型规模和使用场景做出的系统选择。
小结
模型并不是先理解文字,再把理解结果交给计算。顺序恰好相反:文字先被 tokenizer 重写为有限词表中的符号,再变成 ID 和向量,理解才从后续层里逐步形成。
下一篇会沿着这条链路继续回答一个更实际的问题:为什么 token 数量会同时影响成本、延迟和回答质量。
