你有没有过这样的好奇——当你对着 ChatGPT 敲下“中国的首都是”,它为什么会毫不犹豫地接“北京”,而不是“上海”或者“南京”?明明它只是一个吞进文本、吐出文本的程序,里面到底发生了什么?

今天就顺着这条线,从 Token 这个最小单元开始,一步步拆解大模型预测下一个词的全过程。读完之后,你会发现“凭空生成”文本的能力不再是神秘黑盒,而是一套有迹可循的数学机制。
一、Token:LLM 的“最小货币单位”
我们读到的“我爱人工智能”是一串汉字,但 LLM 不认汉字,它只认数字。所以第一步,就是把自然语言切碎成一个个词元(Token)。
举个例子:英文词 unhappiness,LLM 不会整体处理,而是切成 ["un", "happi", "ness"]。中文也一样:"我爱人工智能,自然语言处理很有趣" → ["我", "爱", "人工智能", ",", "自然语言处理", "很", "有趣"]。
为什么要切这么碎?
- 如果只认完整词,英文几十万单词,中文几百万,模型根本记不过来;
- 切成子词后,模型只需要掌握几万个“基础积木”,组合效率高得多;
- 更小的词表意味着更快的运算和更低的显存开销。
每个 Token 在模型内部都有一个唯一的编号(Token ID),比如“你”可能对应 57668。你把 Prompt 发过去,模型收到的就是一堆 Token ID 数组。
输入,说到底就是一串数字。
二、Embedding:给数字赋予“灵魂”
Token ID 只是纯粹的索引,没有语义。57668 和 57669 之间不存在任何数学关系——你不能说“你”+1=“好”。
LLM 的第一步操作,就是把 ID 映射成一个高维向量,这个过程叫做 Embedding。
每个 Token ID 都对应一个向量(比如 1024 维或 4096 维),这个向量可以理解为该 Token 在高维空间中的坐标。语义相近的词,它们的向量在空间里距离就近。比如“国王”和“王后”的向量距离很近,而“苹果”离它们很远。
更有意思的是,向量之间还能做运算:
国王 - 男性 + 女性 ≈ 王后
这说明模型在训练中不仅学到了词义,还学到了语义关系。
LLM 内部有一张巨大的 Embedding Matrix(嵌入矩阵),相当于一张“向量查找表”。当拿到 Token ID 57668,它直接去这张表的第 57668 行,把对应的向量“抽出来”。
但到这里,每个 Token 还是孤立的——它们只知道“自己是什么”,却不知道“自己在句子里的什么位置”。
三、位置编码:让每个词知道“我在哪”
比较这两句话:
- “我咬了狗”
- “狗咬了我”
Token 完全一样,但顺序不同,意思天差地别。Embedding 本身不携带顺序信息,所以必须给每个向量叠加一个位置编码(Position Encoding)。
常见的做法是用正弦余弦函数生成每个位置的固定向量,然后直接加在 Embedding 上。这样一来,每个 Token 最终携带两类信息:
- 语义信息(它是什么)
- 位置信息(它在哪里)
现在,模型有了一个个带坐标和顺序的向量,但还不知道词与词之间的依赖关系——比如“它”指代谁?这就轮到自注意力机制登场了。
四、自注意力:让模型“读懂上下文”
这句英文:
The animal didn't cross the street because it was too tired.
这里的 it 到底指代 animal 还是 street?人类一眼就知道是动物,因为“tired”常用来修饰动物。模型怎么做到的呢?
答案就是 Self-Attention(自注意力)。
Q、K、V 三部曲
对每个 Token 的向量,模型会通过三个不同的线性变换,生成三个新向量:
- Q(Query):查询向量,代表“我在找什么?”——比如代词
it要找个先行词; - K(Key):键向量,代表“我能提供什么?”——相当于名片,告诉别人我有什么属性;
- V(Value):值向量,代表“我能提供什么实际内容?”——具体特征细节。
还是上面那个句子。假设我们只关注 it 和 animal、street 的关系:
token1:animal -> (Q1, K1, V1)
token2:it -> (Q2, K2, V2)
token3:street -> (Q3, K3, V3)
模型要做的是:用 it 的 Query 向量去和句子中每个词的 Key 向量做点积,得到一组注意力分数:
score_it_animal = Q2 · K1
score_it_street = Q2 · K3
如果 score_it_animal 远大于 score_it_street,就说明 it 和 animal 的相关性更强,模型会把更多注意力放到 animal 上。这些分数经 Softmax 归一化变成权重,再分别乘以对应词的 Value 向量,最后加权求和,得到 it 位置的新表示——这个新表示里融合了 animal 的语义细节(比如“疲倦”),从而正确地让 it 指代动物而非街道。
同样的道理可以解释歧义消解:
- “苹果手机” → 注意力分数会让“苹果”和“手机”强相关,向量偏向电子设备;
- “我吃了苹果” → 上下文中的“吃了”会拉高“苹果”作为水果的分数,语义转向食物。
这就是上下文消歧的核心——不是靠词典硬查,而是靠 QKV 的动态加权。
你可能会问:Q、K、V 是怎么来的?它们都是通过训练学出来的权重矩阵,直接对原始向量做线性变换。模型会自己调整这些矩阵,让注意力分数最有利于预测下一个词。
五、从注意力到预测:概率分布和自回归
经过多层自注意力(Transformer 的 Decoder 部分),模型已经为当前序列中每个位置生成了富含上下文信息的向量。接下来要做的事,就是预测下一个词。
对于最后一个位置(当前输入序列的末尾),模型取它的向量,通过线性层 + Softmax,映射成词表大小的概率分布。比如词表有 5 万个 Token,输出就是一个 5 万维的向量,每个维度代表对应 Token 成为下一个词的概率。
拿“中国的首都是”为例:
- “北京” 的概率:92%
- “北平” 的概率:4%
- “长安” 的概率:2%
- 其他所有词:共 2%
模型选择概率最高的那个(或者按温度采样),输出对应的 Token ID。然后,这个新 Token 被追加到输入序列末尾,模型再继续预测下一个词。
这就是自回归生成:每次生成一个词,然后把新词加入上下文,再生成下一个,直到遇到结束标记或达到最大长度。
六、串起全流程:一个完整的推理过程
现在把所有步骤串起来,看看一次完整的生成到底经历了什么:
- 输入:“中国的首都是”
- Tokenization:切分为
["中国", "的", "首都", "是"],转成 Token ID[123, 456, 789, 101] - Embedding:每个 ID 查表得到对应的语义向量(比如 1024 维)
- 位置编码:给每个向量加上位置信息(位置 0,1,2,3 的编码)
- 多层自注意力:经过 N 层 Transformer 块,每层都有 QKV 计算,让每个位置的向量都融合上下文
- 输出概率:取最后一个位置的向量,线性变换 + Softmax,得到词表大小的概率分布
- 采样:按概率选出“北京”(概率最高),输出 Token ID
- 循环:将“北京”加入输入,继续预测下一个词……直至结束
整个过程计算量极大,但核心思路就是不断重复步骤 5~7。
七、一些你可能关心的“实战问题”
1. 为什么 Tokenization 会影响计费和效果?
因为计费按 Token 数算,而不同模型的分词器(Tokenizer)切法不一样。比如英文中 ChatGPT 可能被切成一个 Token,也可能切成 Chat + G + PT。相同的文本在不同模型上的花费可能不同。而且切得越合理,模型理解越好——比如中文“人工智能”如果被切成“人工”和“智能”,就会丢失整体语义,所以现在主流中文模型都会把常用词保留为完整 Token。
2. 位置编码为什么重要?有没有更先进的做法?
早期用固定正弦编码,现在很多模型用可学习的位置编码,即把位置也当成参数来训练。还有 RoPE(旋转位置编码)等变体,本质都是给向量注入位置信息,只是实现方式不同。
3. 注意力机制真的能看到所有词吗?
理论上是,但实际上受限于上下文窗口(比如 8K、128K)。窗口之外的词完全看不见,这就是为什么长文本会丢失开头信息——因为注意力矩阵的计算量随窗口长度平方增长,模型只能截断。
写在最后
本质一句话:LLM 没有“思考”,没有“理解”,只有大规模统计模式匹配。但正是这种模式匹配的深度和广度,让它看起来像有了智能。
作为开发者,了解这些底层细节至少能带来两个直接好处:
- 调试 Prompt 时更有方向:知道模型“看”什么,才能更精准地给出提示;
- 估算成本更准确:理解 Token 计数逻辑,避免账单惊吓。
下次再调用大模型 API 时,不妨想想那一串串向量在空间中跳舞的样子——你会发现,它其实没那么神秘,反而有点可爱。
祝你代码无 Bug,Token 永不浪费。
