"突然想到个问题,如果我给 Jev 26 个字母做选择概率判断,那能不能达到说话的效果?🤔🤔"
"算上标点,你就得到了 Next Token Prediction?"
------ 某次微信群聊
这两句话,成了我接下来所有实验的起点。

一、起点:NanoGPT 与莎士比亚
如果你关注过 Andrej Karpathy 的教学内容,大概率听说过 NanoGPT------一个不到 300 行代码的极简 Transformer 实现,却能在纯 CPU 上训练出一个像模像样的语言模型。
它的训练数据是 TinyShakespeare:一份只有 1MB 的文本,包含了莎士比亚的几部戏剧。角色名、台词、舞台提示,全部以纯文本形式存在。
ROMEO:
But, soft! what light yonder window breaks?
It is the east, and Juliet is the sun.
就这么 1MB,却足够让一个小型 Transformer 学到英语的拼写规律、剧本的格式,甚至一些粗浅的语义。
我的实验环境是一台 Intel Mac(纯 CPU,无 GPU)。没有 A100,没有 H100,只有 4 个物理核心和一颗想搞明白"语言模型到底在学什么"的心。
二、字母的世界:NanoGPT 的原生视角
NanoGPT 使用的是字符级(Character-level)分词。整个词表只有 65 个符号:26 个小写字母、26 个大写字母、10 个数字、标点、空格和换行。
在这个视角下,模型看到的世界是这样的:
输入: T o b e , o r n o t t o b e
预测: o b e , o r n o t t o b e ,
模型的任务是:给定前面的所有字母,预测下一个字母是什么。
训练了 5000 步之后,模型确实能生成一些"看起来像莎士比亚"的文本:
BRUMENRE:
Weriessty the that of the meand...
格式对了(角色名 + 冒号 + 台词),但单词全是拼凑出来的伪英语。模型学会了字母的统计规律,却不懂什么是"单词"。
这就是字母级预测的天花板:模型的眼中只有 65 个符号,没有"词"的概念。
三、第一次变形:从"生成"到"判别"
群里的对话启发了我:如果不需要模型"生成"完整的句子,而是让它做概率判断呢?
比如,给定上下文 "To be, or not to be, that is the q",让模型判断下一个字符是元音、辅音还是标点。
这是一个从"生成式"到"判别式"的转变。我修改了模型的输出头,从 65 分类改为 3 分类:
0: 元音 (a, e, i, o, u)
1: 辅音 (b, c, d, f, ...)
2: 标点/空格 (, . ! ? \n 空格)
效果出奇地好:
Prompt: 'To be, or not to be, that is the q'
预测: VOWEL (元音) → 概率 70.8% ✅
模型完美地学会了英语拼写规则:q 后面几乎总是 u(元音)。
但这里有一个微妙的问题 :模型确实学会了"q 后面是元音",但它不知道那个元音是 u。它学到的是类别层面的规律 ,而不是词汇层面的知识。
这就像一个人知道"这个字的偏旁是三点水",但说不出这个字到底念什么。
四、BPE 的诱惑与陷阱
既然字母级只能预测元音辅音,那很自然的想法是:扩大词表,让模型直接预测单词。
我引入了 BPE(Byte-Pair Encoding),一种介于字母和单词之间的分词方式。它通过统计字符对的频率,自动合并出常见的子词:
4000 词表的 BPE:
'question' → [' question'] ← 完整单词 ✅
'going' → [' going'] ← 完整单词 ✅
'Romeo' → [' Romeo'] ← 完整单词 ✅
看起来很美,对吧?question 确实是一个完整的 token。
但训练出来的结果却让人崩溃:
Prompt: 'To be, or not to be, that is the '
预测: '\n'(15%), 'ing'(7%), 'ice'(3%), 'z'(4%)
没有 question。只有 \n(换行)、ing、ice、z。
我花了很长时间去排查:是模型太小?学习率太高?训练步数不够?我试了 1 层、2 层、4 层,试了 64 维、128 维,试了余弦退火、固定学习率,加了 Dropout、梯度裁剪......
所有的"参数调优"都没有解决问题。
直到我统计了训练数据中的词频:
'ing' → 出现 300+ 次
'ice' → 出现 50+ 次
' question' → 出现 1 次
那一刻,我突然明白了:问题不在模型,不在参数,不在训练策略。问题在于我让模型在 4000 个子词碎片中去猜一个只出现过 1 次的完整单词。
BPE 的世界观是"子词"。模型看到的是 ing、er、le、ice 这些碎片,它自然倾向于预测这些高频碎片。它不是学不会 question,它是在一个"碎片化"的世界观里,找不到通往 question 的路。
五、本体论的转变:从"碎片"到"概念"
这是整个实验中最关键的一次认知转变。
| 视角 | 模型眼中的世界 | 能学到什么 |
|---|---|---|
| 字母级 (65 词表) | 65 个符号 | 拼写规律(q 后是元音) |
| BPE 级 (4000 词表) | 4000 个子词碎片 | 高频词缀(ing, er, ice) |
| 单词级 (7307 词表) | 7307 个完整概念 | 语义、语法、搭配 |
这就是本体论(Ontology)的变化。
所谓本体论,就是"你认为世界由什么构成"。
- 字母级模型认为:世界由 65 个符号构成。
- BPE 模型认为:世界由 4000 个碎片构成。
- 单词级模型认为:世界由 7307 个概念构成。
当你改变了本体论,模型能学到的东西就完全不同了。
字母级模型永远学不会 question,因为 question 不在它的本体论里------它只认识 q、u、e、s、t、i、o、n 这 8 个符号。
BPE 模型理论上可以学 question(它确实在词表里),但它的注意力被 300 个 ing 和 50 个 ice 淹没了。
只有当你把 question 当作一个不可分割的整体------一个"概念"------模型才能真正学到它。
六、Word-Level:让每个单词成为一个概念
于是,我做了一个在 BPE 盛行的时代看起来"反潮流"的决定:放弃 BPE,使用 Word-level 分词。
python
# 不再使用 BPE
tokenizer = Tokenizer(models.WordLevel(unk_token="[UNK]"))
tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
每个完整的单词就是一个 token。没有子词,没有碎片,没有 ing 和 ice。
词表变成了 7307 个完整的英语单词。
训练 5000 步后的结果:
Prompt: '...wherefore art thou '
预测: 'art'(11%), 'hast'(8.8%), 'wilt'(4.5%), 'shalt'(4.2%), 'dost'(2.6%)
模型学会了古英语动词变位。 art thou 后面跟 hast、wilt、shalt、dost------这在莎士比亚的语法中是完全正确的。
Prompt: 'First '
预测: 'Citizen'(22.5%), 'Murderer'(16.2%), 'Senator'(11.9%)
模型学会了剧本的角色命名规则。 First Citizen、First Murderer、First Senator 都是莎翁剧本中真实存在的角色开场。
Prompt: '...that is the '
预测: 'king'(3.1%), 'people'(1.7%), 'world'(1.6%), 'queen'(1.1%)
虽然没有直接预测出 question(它在数据中只出现了 1 次),但模型预测出了 king、queen、world 这些语义相关的名词。
从 z、ing、ice 到 hast、Citizen、king------这不是参数的胜利,是本体论的胜利。
七、回到群聊的那句话
"突然想到个问题,如果我给 Jev 26 个字母做选择概率判断,那能不能达到说话的效果?"
答案是:能,但只能达到"拼写"的效果,达不到"说话"的效果。
26 个字母的概率判断,让你学到的是 q 后面是 u,是 ing 前面是 go。这是字母的世界。
"算上标点,你就得到了 Next Token Prediction?"
形式上,是的。但 Token 是什么,决定了模型能学到什么。
- 如果 Token 是字母,模型学到的是拼写。
- 如果 Token 是子词碎片,模型学到的是词缀。
- 如果 Token 是完整的单词,模型学到的是语义。
Next Token Prediction 的威力,不在于"预测下一个",而在于"Token 是什么"。
八、写在最后
这个实验跑在 Intel Mac 的纯 CPU 上,用 1MB 的莎士比亚,训练了十几个不同配置的模型。
最大的收获不是某个具体的参数配置,也不是某个最优的模型架构。而是一个认知上的转变:
当你改变模型观察世界的方式,你就改变了模型能学到的东西。
字母级、BPE、Word-level------它们不是"好与坏"的关系,而是不同的本体论。每一种本体论都定义了模型能看到的"最小概念单元"。
字母级模型看不到"单词",BPE 模型看不到"完整的词义",只有 Word-level 模型才能把 question 当作一个不可分割的概念来学习。
在优化参数之前,先问自己:我的模型,看到的是什么样的世界?
本文所有实验均在 Intel Mac(CPU)上完成,模型参数量 0.5M-1.8M,训练时间 5-30 分钟/次。代码基于 Karpathy 的 NanoGPT 由Qwen生成。