AI 系统原理:从命令到回复的完整流程
当你给 AI 输入一个命令时,从按下回车到看到回复,系统内部一步一步做了哪些事?本文按阶段梳理底层逻辑。
一、整体流程(高层)
你的输入 → 转成数字(分词)→ 送进模型 → 模型内部算很多次 → 一个一个「猜」下一个词 → 拼成完整回复 → 显示给你
本文约定 :阶段 1~3 统一用一个例子------用户输入「写一个 Python 函数」,得到 4 个 token,对应阶段 2 的 (4, 4) 张量,阶段 3 的示例也沿用同一组数据,方便对照理解。
下面按阶段拆开说明。
二、阶段 1:你的命令怎么被「读进去」
1.1 收到原始文本
- 系统拿到的是一整段字符串 ,例如:
"写一个 Python 函数,计算斐波那契数列第 n 项"
- 同时可能还会自动加上:
- 系统提示(system prompt):你是编程助手、要简洁、要写可运行代码等
- 对话历史:之前几轮问答(如果有)
- 上下文:当前打开的文件、选中的代码等(如 Cursor 会组好这些)
所以模型真正看到的是一大段「组装好的提示」,不只是你那一句。
1.2 分词(Tokenization)
- 模型不能直接处理汉字/英文,只能处理数字。
- 分词 :把这段文本切成小片段(token),每个 token 对应一个整数 ID。
- 例如:
"写"→ 某个 ID,"一个"→ 另一个 ID,"Python"→ 又一个 ID......
- 例如:
- 结果:输入从字符串 变成一串整数 ,例如:
[1234, 567, 890, 234, ...] - 不同模型有不同的词表(vocabulary),所以同一句话在不同模型里 token 序列可能不同。
阶段 1 结束后得到的数据是什么?
- 类型 :一维整数序列(在代码里常是
list[int]或张量shape = (序列长度 L,))。 - 含义:每个位置一个 token ID,顺序和原文一致。
- 示例(与后文阶段 2、3 统一:只取「写一个 Python 函数」这 4 个 token,简化词表):
| 位置 | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| 文本 | 写 | 一个 | Python | 函数 |
| 数据 | 1201 | 88 | 3921 | 2102 |
- 得到的数据 就是:
[1201, 88, 3921, 2102],长度 L = 4。 - 在实现里通常会再加一个「batch」维度,变成 shape
(1, 4),表示 1 条样本、长度 4。
底层逻辑 1:AI 的「输入」在数学上就是一串整数;所有语义都通过这串数字在后续步骤里被处理。
三、阶段 2:数字怎么变成「语义表示」(Embedding + 位置)
2.1 词嵌入(Embedding)
- 每个 token ID 会通过一个查表 + 线性变换 ,变成一个向量 (一列数字)。
- 例如:ID
1234→ 向量[0.12, -0.34, 0.56, ...](维度可能是 768、4096 等)
- 例如:ID
- 这些向量是在训练时学出来的,相近含义的词在向量空间里会靠近。
- 这一步把「离散的 token」变成「连续的向量」,方便后面做数学运算。
2.2 位置编码(Positional Encoding)
- 模型需要知道词的顺序(「没吃饭」和「饭没吃」不一样)。
- 会给每个位置的向量加上「位置信息」:
- 要么用公式算出来的固定位置编码;
- 要么用可学习的位置嵌入(learned position embedding)。
- 这样同一层的输入里,既包含「是什么词」,也包含「在第几位」。
阶段 2 结束后得到的数据是什么?
- 类型 :一个三维张量(tensor) ,形状为
(batch, 序列长度 L, 隐藏维度 D)。- 单条样本时 batch=1,常写成
(L, D)。
- 单条样本时 batch=1,常写成
- 含义 :
- 第 1 维:第几个位置(0 到 L-1);
- 第 2 维:该位置对应的向量,长度为 D(如 768、4096)。
- 示例(L=4 个 token,D=4 做演示,实际 D 很大):
假设阶段 1 得到 4 个 token ID:[1201, 88, 3921, 2102],对应「写」「一个」「Python」「函数」。
阶段 2 后每个位置变成一个 4 维向量(Embedding + 位置编码后):
位置 0(写) → [ 0.2, -0.1, 0.5, 0.0]
位置 1(一个) → [-0.3, 0.4, 0.1, 0.2]
位置 2(Python)→ [ 0.1, 0.0, -0.2, 0.6]
位置 3(函数) → [ 0.4, 0.3, 0.2, -0.1]
- 得到的数据 就是一个矩阵(张量),形状
(4, 4):
| 维度0 | 维度1 | 维度2 | 维度3 | |
|---|---|---|---|---|
| 位置0 | 0.2 | -0.1 | 0.5 | 0.0 |
| 位置1 | -0.3 | 0.4 | 0.1 | 0.2 |
| 位置2 | 0.1 | 0.0 | -0.2 | 0.6 |
| 位置3 | 0.4 | 0.3 | 0.2 | -0.1 |
- 之后模型(阶段 3)的输入就是这个张量:每一行是一个位置的「带顺序的语义向量」,模型只在这些数字上做运算。
底层逻辑 2:模型内部一直在处理「带顺序的向量序列」,而不是原始文字;语义和顺序都编码在这些向量里。
四、阶段 3:模型核心------一层层「理解」并算下一个词
大语言模型是多层 Transformer 。每一层大致做两件事:注意力 + 前馈网络。
3.1 自注意力(Self-Attention)
- 在做什么 :对当前序列里每个位置 ,看「其它所有位置」的向量,算一个加权和,得到新的表示。
- 权重是算出来的:模型会学「哪些词和哪些词更相关」。
- 例如处理「斐波那契」时,可能会更关注「数列」「第 n 项」「函数」这些词。
- 数学上:对每个 token 的向量,通过 Query、Key、Value 三个线性变换,用 Q 和所有 K 做相似度(常为点积),得到权重,再对 V 加权求和。
- 多头注意力:会做多组这样的计算(多个「头」),从不同角度抓关系,最后拼在一起再做一次线性变换。
所以:同一段输入在每一层里都会被「重新混合」一次,让每个位置的信息都融合了整段上下文的语义。
为什么用 Q、K、V 三个?理论依据是什么?
-
Q、K、V 不是三个「维度」 :它们是对同一个向量 h 做的三套不同的线性变换 (三个矩阵 W_Q、W_K、W_V)。每一套得到的还是和 h 同维的向量(例如我们的例子里都是 4 维)。阶段 3 输出仍是
(4, 4),是因为设计上每一层保持「序列长度 × 特征维度」不变:4 个位置、每位置一个 4 维向量,经过注意力 + FFN 后仍然 4 个位置、每位置 4 维(通过输出投影、残差、归一化保证形状一致),方便层层堆叠。 -
理论来源:检索(Retrieval)类比
经典思路来自信息检索和键值存储:
- Query(查询):当前位置「我要找什么」的表示;
- Key(键):每个位置「我是什么、可被怎么匹配」的表示;
- Value(值) :每个位置「被选中时,要贡献什么内容」的表示。
先拿 Q 和所有 K 算相似度(匹配程度)→ 得到权重;再用这些权重对 V 做加权和 → 得到「从别处取来的内容」。这样就把「谁和谁相关 」(Q·K)和「取什么内容」(V)分开了。
-
为什么要拆成 Q、K、V 而不是只用一个 h?
- 职责分离 :
- 用 Q 和 K 专门负责「算相关度」:两个向量越像,点积越大,注意力权重越高。
- 用 V 专门负责「被取走的内容」:即使用户「查的是关键词」,真正返回的可以是另一段更合适的信息。
- 若只用同一个向量既当「匹配键」又当「取出的值」,模型就没有自由度去学「匹配用一套标准、内容用另一套」;拆成 Q、K、V 后,W_Q、W_K、W_V 由数据学习,模型可以学到:例如用 K 突出「语法角色」,用 V 突出「语义内容」。
- 出处:这种 Q/K/V 设计在 2017 年《Attention is All You Need》(Vaswani 等)中系统化用于 Transformer,并成为大语言模型的标准组件。
- 职责分离 :
-
一句话 :用 Q、K、V 是为了把「按什么标准选 」(Q·K)和「选到后取什么」(V)分开,理论依据是检索/键值思想,让模型能学得更灵活。
3.2 前馈网络(FFN)具体在做什么?
- 位置 :在每一层里,自注意力算完之后,对每个位置得到的新向量(已经融合了上下文),再单独过一遍「前馈网络」。
- 公式 :对每个位置的向量 x (4 维为例),做两步线性 + 中间非线性:
- 中间表示 :u = Linear1(x ),把 4 维映射到更大的中间维度(如 16 维);再对 u 逐元素做 GELU(或 ReLU)激活。
- 输出 :y = Linear2( GELU(Linear1(x)) ),把中间维再映射回 4 维。
- 在做什么 :注意力只做了「按权重混合别的位置的向量」,全是线性组合;FFN 负责非线性,让模型能表达「如果这个位置在某种上下文里,就改写成另一种表示」这种更复杂的关系。可以理解为:注意力是「收集信息」,FFN 是「消化、改写」。
- 逐位置独立 :每个位置自己的向量单独过 FFN,位置与位置之间在 FFN 里不交互(交互已经在注意力里做完了)。
3.2 示例(沿用 (4,4),中间维取 8)
设注意力 + 残差 + 归一化后,位置 0 的向量为 x₀ = [0.11, 0.11, 0.15, 0.16](即 3.4 步骤 C 里位置 0 的新向量近似值)。
步骤 1:Linear1,4 维 → 8 维
- 有一个可学习矩阵 W1 ,形状 (4, 8)。u = x₀ × W1,得到 8 维向量。
- 假想算得:u = [0.2, -0.1, 0.3, 0.0, 0.1, 0.4, -0.2, 0.1]
步骤 2:GELU 激活
- 对 u 的每一个分量做 GELU:GELU(t) 会把负的压小、正的保留但非线性。公式上 GELU(t) ≈ t·Φ(t)(Φ 是标准正态分布函数),这里只关心「非线性」效果。
- 假想算得:GELU(u) = [0.12, -0.04, 0.18, 0.0, 0.06, 0.27, -0.08, 0.06]
步骤 3:Linear2,8 维 → 4 维
- 有一个可学习矩阵 W2 ,形状 (8, 4)。y = GELU(u) × W2,得到 4 维向量。
- 假想算得:y₀ = [0.13, 0.09, 0.17, 0.14]
这就是位置 0 经过 FFN 后的输出。位置 1、2、3 同理 ,各自用同一个 W1、W2,但输入不同(各自注意力后的向量),所以输出也不同。最后再对整层做残差 (FFN 输出 + 本层注意力前的输入)和层归一化,得到这一层最终的 (4, 4) 输出。
3.3 层层堆叠具体在做什么?
- 含义 :「层层堆叠」就是把「注意力 + FFN(+ 残差、归一化)」这一整块重复很多次,上一块的输出当作下一块的输入,中间形状不变(一直是 4 个位置 × 4 维)。
- 数据流 :
- 第 0 层输入:阶段 2 的 (4, 4)(写、一个、Python、函数 的嵌入+位置)。
- 第 0 层输出:经过一次注意力 + FFN + 残差 + 归一化,仍是 (4, 4),交给第 1 层。
- 第 1 层输入 = 第 0 层输出;第 1 层输出 = 第 2 层输入;... 一直到第 N-1 层。
- 第 N-1 层输出就是「阶段 3 的最终结果」,仍是 (4, 4);取最后一行的 4 维向量(即最后一个位置「函数」的表示)送到阶段 4。
- 为什么多层的效果不同:每一层都在「再看一遍整段、再混合、再非线性改写」一次。靠前的层通常学到更局部、更语法/词义的东西(例如「写」和「函数」都是动词相关);靠后的层在更抽象的表示上操作,更接近「这句话在问什么、接下来该接什么」这种高层语义,所以最后一层的最后一个位置才适合拿去预测下一个 token。
3.3 示例:两层之间怎么衔接(形状与含义)
-
第 0 层
- 输入:4 个位置向量(阶段 2 的 (4,4))。
- 经过注意力:每个位置都融合了 4 个 token 的信息。
- 经过 FFN + 残差 + 归一化:得到新的 (4,4),记作 L0_out。
- L0_out 的每一行,可以粗略理解为「每个位置在看过整句后的第一轮表示」。
-
第 1 层
- 输入:就是 L0_out(仍是 (4,4))。
- 再做一次注意力:每个位置又在「第一轮表示」的基础上,再和别的位置交互一次。
- 再 FFN + 残差 + 归一化:得到 L1_out,仍是 (4,4)。
- L1_out 的最后一行(位置 3「函数」)就是阶段 4 要用的向量 z。
-
若有很多层(如 32 层):就重复 32 次「输入 (4,4) → 注意力 → FFN → 残差 → 归一化 → 输出 (4,4)」;每一层的 (4,4) 在数值和语义上都会逐渐变化,越往后越「高层」,最后一层的最后一个位置才送去算下一个 token 的概率。
3.4 示例演示:某一层里具体在算什么
沿用阶段 2 的 (4, 4) 例子 :4 个 token (「写」「一个」「Python」「函数」),每个位置 4 维向量 (实际模型是 768/4096 等),只看一个注意力头。
输入(阶段 2 的输出,本层的输入)
就是阶段 2 得到的 (4, 4) 张量,每行是一个位置的向量:
- 位置 0(写):h₀ = [0.2, -0.1, 0.5, 0.0]
- 位置 1(一个):h₁ = [-0.3, 0.4, 0.1, 0.2]
- 位置 2(Python):h₂ = [0.1, 0.0, -0.2, 0.6]
- 位置 3(函数):h₃ = [0.4, 0.3, 0.2, -0.1]
也就是一个 (4, 4) 的矩阵。
步骤 A:算 Q、K、V
对每个位置 的向量 h,用三组可学习的权重矩阵(这里是 4×4)得到:
- Q = h × W_Q(Query:我在找什么)
- K = h × W_K(Key:我有什么可被找的)
- V = h × W_V(Value:找到我时取多少)
用假想的权重算出来(数字仅为演示),每个 Q、K、V 都是 4 维:
| 位置 | 文本 | Q (Query) | K (Key) | V (Value) |
|---|---|---|---|---|
| 0 | 写 | [0.3, 0.0, 0.4, 0.1] | [0.2, 0.1, 0.5, 0.0] | [0.2, 0.0, 0.3, 0.1] |
| 1 | 一个 | [-0.2, 0.4, 0.1, 0.2] | [-0.1, 0.3, 0.2, 0.2] | [-0.1, 0.3, 0.0, 0.2] |
| 2 | Python | [0.0, 0.1, -0.1, 0.5] | [0.1, 0.0, -0.1, 0.5] | [0.0, 0.1, -0.1, 0.4] |
| 3 | 函数 | [0.3, 0.2, 0.2, -0.1] | [0.4, 0.2, 0.1, -0.1] | [0.3, 0.2, 0.1, 0.0] |
步骤 B:用 Q 和 K 算「注意力权重」
对每个位置 ,用它的 Q 去和所有位置 的 K 做点积(再除以 √d 做缩放),得到「这个位置对每个位置的关注程度」:
- 位置 i 的 Q 与 位置 0,1,2,3 的 K 点积 → 得到 4 个数
- 对这 4 个数做 Softmax,得到权重和=1
假设算出来(示例):
| 对位置0(写) | 对位置1(一个) | 对位置2(Python) | 对位置3(函数) | |
|---|---|---|---|---|
| 位置 0 看 | 0.5 | 0.25 | 0.15 | 0.1 |
| 位置 1 看 | 0.2 | 0.4 | 0.2 | 0.2 |
| 位置 2 看 | 0.1 | 0.2 | 0.5 | 0.2 |
| 位置 3 看 | 0.15 | 0.2 | 0.25 | 0.4 |
解读:位置 3(函数)较关注自己(0.4)和位置 2(Python)(0.25),符合「函数」和「Python」的搭配;位置 0(写)最关注自己(0.5)。这些权重是算出来的,不是写死的。
步骤 C:用权重对 V 做加权和,得到新向量
对每个位置 ,用上面那一行的权重,对四个位置的 V 做加权求和,得到该位置本头的新表示。以位置 0 为例:
-
新位置 0 = 0.5×V₀ + 0.25×V₁ + 0.15×V₂ + 0.1×V₃
= 0.5×[0.2,0,0.3,0.1] + 0.25×[-0.1,0.3,0,0.2] + 0.15×[0,0.1,-0.1,0.4] + 0.1×[0.3,0.2,0.1,0]
= [0.1,0,0.15,0.05] + [-0.025,0.075,0,0.05] + [0,0.015,-0.015,0.06] + [0.03,0.02,0.01,0]
= [0.105, 0.11, 0.145, 0.16](近似)
-
新位置 1、2、3 同理,用各自那一行权重对 V₀~V₃ 加权求和。
多个头算完后会拼在一起,再经过一次线性变换,然后加上残差 (加上本层原始输入),再做层归一化,得到这一层注意力部分的输出。
步骤 D:前馈网络(FFN)
对每个位置 的新向量(上面得到的 4 维向量),独立做两次线性变换 + 中间非线性(如 GELU):
- 先扩维:4 维 → 中间隐藏层(如 16 维,实际更大)
- 做非线性激活
- 再压回 4 维
公式形如:FFN(x) = Linear2(GELU(Linear1(x)))。
然后再加残差、层归一化,得到这一层最终的输出 ,形状仍为 (4, 4),作为下一层的输入。
小结:阶段 3 在做什么
- 输入 :阶段 2 得到的
(4, 4)张量(本文统一示例),即 4 个位置、每位置 4 维向量。 - 每一层 :
- 用 Q、K、V 让每个位置「看」整段序列,算注意力权重;
- 用权重对 V 加权求和,得到带上下文的新向量;
- 再过 FFN,做非线性变换;
- 残差 + 归一化,输出给下一层(形状仍为
(4, 4))。
- 输出:最后一层、**最后一个位置(位置 3「函数」)**的向量,会送到阶段 4 去预测「下一个 token」。
底层逻辑 3:模型没有显式「理解」你的话,而是通过大量矩阵运算,把输入变成一组高维向量,再在最后一层用「最后一个位置的向量」来预测下一个 token 的概率分布。
五、阶段 4:从「最后一个向量」到「下一个词」
4.1 得到概率分布
- 取最后一层、最后一个位置的向量。
- 通过一个线性层 + Softmax ,映射到词表大小(例如 10 万多个 token),得到每个 token 作为下一个词的概率 。
- 例如:P("def") = 0.3,P("函数") = 0.15,P(" ") = 0.1......
示例:线性层 + Softmax 具体怎么算
沿用前文:阶段 3 最后一层、**最后一个位置(位置 3「函数」)**的向量,作为本步的输入。实际模型里这是高维向量(如 4096 维),这里仍用 4 维 和 缩小版词表 演示。
步骤 1:最后一位置的向量
- 记作 z ,形状
(4,)。假设经过多层后得到(示例数值):- z = [0.2, 0.5, -0.1, 0.3]
步骤 2:线性层(无偏置时可写成 z × W)
- 有一个可学习的权重矩阵 W ,形状为
(隐藏维度, 词表大小),即(4, V)。V 是词表大小(实际约 10 万+;这里用 V=6 做演示)。 - 计算 logits = z × W(即 z 与 W 的每一列做点积):
- 结果是一个长度为 V 的向量,每个分量对应一个 token 的「未归一化得分」。
假想的 W(4 行 6 列,每列对应一个 token):
| token 0 "(" | token 1 ":" | token 2 "def" | token 3 " " | token 4 换行 | token 5 "return" | |
|---|---|---|---|---|---|---|
| 维度0 | 1.0 | -0.5 | 0.8 | 0.2 | -0.3 | 0.6 |
| 维度1 | 0.2 | 0.4 | 0.5 | 0.1 | 0.0 | 0.3 |
| 维度2 | -0.3 | 0.1 | 0.2 | 0.0 | 0.2 | 0.1 |
| 维度3 | 0.5 | 0.3 | 0.4 | 0.1 | 0.2 | 0.2 |
-
logits = z · W 的每一列:
- logits[0] = 0.2×1.0 + 0.5×0.2 + (-0.1)×(-0.3) + 0.3×0.5 = 0.2 + 0.1 + 0.03 + 0.15 = 0.48
- logits[1] = 0.2×(-0.5) + 0.5×0.4 + (-0.1)×0.1 + 0.3×0.3 = -0.1 + 0.2 - 0.01 + 0.09 = 0.18
- logits[2] = 0.2×0.8 + 0.5×0.5 + (-0.1)×0.2 + 0.3×0.4 = 0.16 + 0.25 - 0.02 + 0.12 = 0.51
- logits[3] = 0.2×0.2 + 0.5×0.1 + (-0.1)×0.0 + 0.3×0.1 = 0.04 + 0.05 + 0 + 0.03 = 0.12
- logits[4] = 0.2×(-0.3) + 0.5×0.0 + (-0.1)×0.2 + 0.3×0.2 = -0.06 + 0 - 0.02 + 0.06 = -0.02
- logits[5] = 0.2×0.6 + 0.5×0.3 + (-0.1)×0.1 + 0.3×0.2 = 0.12 + 0.15 - 0.01 + 0.06 = 0.32
-
得到 logits = [0.48, 0.18, 0.51, 0.12, -0.02, 0.32]
步骤 3:Softmax
- 对 logits 做 Softmax,把任意实数变成「和为 1」的概率:
- 公式:P(token i) = exp(logits[i]) / Σⱼ exp(logits[j])
计算(保留两位小数):
- exp(0.48)≈1.62, exp(0.18)≈1.20, exp(0.51)≈1.66, exp(0.12)≈1.13, exp(-0.02)≈0.98, exp(0.32)≈1.38
- 分母 S = 1.62+1.20+1.66+1.13+0.98+1.38 ≈ 7.97
- P("(") = 1.62/7.97 ≈ 0.20
- P(":") = 1.20/7.97 ≈ 0.15
- P("def") = 1.66/7.97 ≈ 0.21
- P(" ") = 1.13/7.97 ≈ 0.14
- P(换行) = 0.98/7.97 ≈ 0.12
- P("return") = 1.38/7.97 ≈ 0.17
| 下一个 token | "(" | ":" | "def" | " " | 换行 | "return" |
|---|---|---|---|---|---|---|
| 概率 | 0.20 | 0.15 | 0.21 | 0.14 | 0.12 | 0.17 |
- 概率和 = 1。「def」概率最高 (0.21),若用贪婪解码就会选「def」作为下一个 token(符合「写一个 Python 函数」后面常接
def的写法)。
小结 :4.1 做的事就是------用最后一位置的向量 z 乘上词表大小的线性层 W 得到每个 token 的 logits,再对 logits 做 Softmax 得到概率分布;实际词表是 10 万+,形状是 (1, 4096) × (4096, 100000+) → (1, 100000+),再 Softmax,逻辑与上述示例一致。
权重矩阵 W 从哪来?和阶段 2 的词嵌入表有什么关系?
W 是训练出来的。
- 阶段 4 的 W (线性层权重)和阶段 2 的词嵌入(Embedding)表 、阶段 3 的 W_Q / W_K / W_V 、FFN 的权重等一样,都是模型的可学习参数。
- 训练时用大量文本(或对话数据):输入一段序列,模型预测「下一个 token」,把预测概率和真实下一个 token 做交叉熵损失 ,再通过反向传播 更新所有参数,其中就包括 W 。所以 W 不是人工填的表,而是数据驱动、梯度下降学出来的。
W 和阶段 2 的 Embedding 表在形状与作用上是对称的。
- 阶段 2 词嵌入表 E (常记作 embedding 或 token embedding):
- 形状一般是
(词表大小 V, 隐藏维度 D),即 V 行、D 列。 - 作用:token ID → 向量。取第 i 个 token 时,查 E 的第 i 行,得到 D 维向量。
- 即「从词表空间到隐藏空间」:输入侧,把离散 token 变成连续向量。
- 形状一般是
- 阶段 4 的 W (常叫 lm_head / output projection):
- 形状是
(隐藏维度 D, 词表大小 V),即 D 行、V 列 (和 E 的转置同形)。 - 作用:向量 → 每个 token 的得分。用最后一位置的 D 维向量 z 乘 W,得到 V 个 logits。
- 即「从隐藏空间回词表空间」:输出侧,把连续向量变回对每个 token 的分数。
- 形状是
所以:E 是「词 → 向量」,W 是「向量 → 词」,两者分别在输入和输出两侧连接「词表」和「隐藏表示」。
两种常见设计:独立 vs 共享(weight tying)
- 独立 :E 和 W 是两套参数,各自随机初始化,各自用梯度更新。模型自由度大,但参数量多(E 和 W 各约 V×D)。
- 共享(Weight Tying) :很多模型(如 GPT-2、部分 LLaMA)把输出层的 W 设为输入嵌入 E 的转置 ,即 W = Eᵀ (或等价地,用同一张表 E:输出时用 z × Eᵀ = z 与 E 的每一行做点积)。这样:
- 「下一个 token 的得分」= 当前隐藏向量 z 与该 token 的嵌入向量的点积:得分高等价于「z 和这个词的嵌入越像」。
- 参数量减半(只存一份 E),且输入/输出共用同一套「词表示」,训练更一致。
因此,若采用 weight tying,阶段 4 的 W 和阶段 2 的 Embedding 表本质是同一张表:阶段 2 用行(按 token 取向量),阶段 4 用列(即 E 的行)做点积得到 logits。
一句话 :W 和词嵌入表 E 一样都是训练得到的;E 负责「词 → 向量」,W 负责「向量 → 词」;有的模型里 W 直接取 E 的转置(共享参数),有的模型里 W 和 E 分开学。
4.2 解码(Decoding)------选下一个 token
- 贪婪:直接选概率最大的那个 token(简单、确定)。
- 采样:按概率随机抽一个(温度高更随机,温度低更确定)。
- Cursor/聊天产品通常会用采样 + 温度,让回答多样、自然;代码补全可能更偏贪婪或低温度。
选出来的 token 再转成文字(或保留为 ID),拼到已有序列末尾,作为「当前已生成的内容」。
采样具体是什么意思?「按概率随机抽」怎么抽?
- 采样 :手里已经有一份「每个 token 的概率」(4.1 里 Softmax 得到的那一列),不是 直接取概率最大的那个,而是按这份概率做一次随机抽取,抽到哪个 token 就输出哪个。
- 按概率随机抽 的做法(本质是「依分布抽样」):
- 把每个 token 看成一条线段,线段的长度 = 该 token 的概率 ,把所有线段首尾相接拼成一根总长为 1 的棍子。
- 在 [0, 1) 上均匀随机打一个点(例如 random 出 0.37),看这个点落在哪一段里,就选对应的 token。
- 等价说法:概率大的 token 对应的区间更长,被随机点命中的机会就更大;概率小的更短,被命中的机会更小。抽很多次时,每个 token 被选中的比例会趋近于它的概率。
小例子(沿用 4.1 的 6 个 token 与概率):
| token | "(" | ":" | "def" | " " | 换行 | "return" |
|---|---|---|---|---|---|---|
| 概率 | 0.20 | 0.15 | 0.21 | 0.14 | 0.12 | 0.17 |
-
区间划分:
0, 0.20) → "(" ; \[0.20, 0.35) → ":" ; \[0.35, 0.56) → "def" ; \[0.56, 0.70) → " " ; \[0.70, 0.82) → 换行 ; \[0.82, 1.0) → "return" 。
若随机数 = 0.18,落在 [0, 0.20),则结果为 "(" 。
每次调用都会重新随机,所以同一段上下文多次生成可能得到不同结果。
-
实现上 :等价于对「类别分布」(categorical distribution)做一次抽样,例如
numpy.random.choice(tokens, p=probs)或torch.multinomial(probs, 1)。
温度是什么?是哪个「维度」?
- 温度(temperature) 是一个标量超参数 ,就是一个数 (例如 0.5、1.0、2.0),不是 向量或矩阵的某一维;它用在 Softmax 之前 ,用来缩放 logits,从而改变概率分布的「尖锐程度」。
- 用法 :对 4.1 里得到的 logits 先除以温度 T,再做 Softmax:
- 新 logits = 原 logits / T
- 新概率 = Softmax(新 logits)
- 效果 :
- T = 1:不缩放,就是 4.1 里原来的概率(例如 "def" 0.21 最高)。
- T 很大 (如 2、5):logits 被缩小,彼此差距变小,Softmax 后概率会更平均 (更接近均匀),高概率的没那么高、低概率的没那么低 → 更随机、更多样。
- T 很小 (如 0.1、0.5):logits 被放大,差距拉大,Softmax 后最大的那个概率会更大 、别的更小 → 更确定、更「保守」,接近贪婪。
用 4.1 的 logits 看温度(logits = [0.48, 0.18, 0.51, 0.12, -0.02, 0.32],对应 6 个 token):
| 温度 T | 效果简述 | "def" 的大致概率 | 整体感觉 |
|---|---|---|---|
| T=0.5 | 分布更尖 | 约 0.35+ | 更确定,常选 "def" |
| T=1 | 原始分布 | 约 0.21 | 正常随机 |
| T=2 | 分布更平 | 约 0.18 | 更随机,别的 token 更容易被抽到 |
- 总结 :温度是一个标量 ,控制「logits 在 Softmax 前被缩小/放大的程度」,从而控制概率分布是更尖(更确定)还是更平(更随机) ;采样时用的是经过温度缩放后再 Softmax 得到的概率去做「按概率随机抽一个」。
T 是训练时自动算出来的吗?
- 不是 。温度 T 不参与训练,不是用梯度、反向传播学出来的参数。
- 训练时通常固定 T=1 (或等价地不除以 T),模型只学「在正常 Softmax 下预测下一个 token」;T 是在推理/生成阶段由人(或产品)设定的超参数,用来调节「这一轮生成要更稳还是更多样」。
- 例如:Cursor / ChatGPT 的界面里「创造性」「随机性」滑块,背后往往就是在调 T;代码补全为了稳定,常把 T 设得很小或直接用贪婪(相当于 T→0)。所以:T 是「用模型时你选的开关」,不是「模型自己学出来的」。
底层逻辑 4 :模型在每一步只会做一件事------根据当前整段序列,给出「下一个 token」的概率分布,然后由解码策略选一个。没有「想一整段再输出」,而是一个一个 token 生成。
六、阶段 5:循环生成,直到结束
5.1 自回归(Autoregressive)循环
- 把刚生成的 token 追加到输入序列末尾。
- 把新的整段序列再送进模型(再走一遍 2→3→4)。
- 再得到下一个 token 的概率分布,再选一个,再追加......
- 如此反复,直到:
- 出现「结束符」token(如
<eos>),或 - 达到最大生成长度,或
- 用户/系统触发停止。
- 出现「结束符」token(如
5.2 为什么叫「预测下一个词」
- 本质上,模型在每一步都在回答同一个问题:「给定到目前为止的全部内容,下一个最可能出现的 token 是什么?」
- 它没有「计划全文」、没有「查斐波那契公式」,而是通过训练时见过的海量类似文本,学到了「这种问题后面通常跟什么」的统计模式,所以能生成看起来像「理解题意并写代码」的结果。
底层逻辑 5 :整段回复 = 多次「看当前序列 → 算下一个 token → 拼上去」的循环;逻辑和规划都是隐式地存在于这些概率里,而不是显式编程出来的。
七、阶段 6:从 token 流到你能看到的回复
6.1 解码器(Detokenizer)
- 把生成的一串 token ID 用同一个词表反查成文字(或子词)。
- 可能还要做一些后处理:合并子词、处理特殊符号、控制显示格式等。
6.2 流式输出(你看到「一个字一个字蹦」)
- 每生成一个(或几个)token,就立刻解码并推给前端显示,不用等整段生成完。
- 所以你看到的是「边算边出字」;底层仍然是「算一个 token → 解码 → 再算下一个」。
八、流程图总览
你输入:"写一个 Python 函数,计算斐波那契数列第 n 项"
↓
┌─────────────────────────────────────────────────────────────┐
│ 1. 组 prompt(系统提示 + 历史 + 你的话 + 上下文) │
│ 2. 分词 → 一串 token ID │
│ 3. Embedding + 位置 → 每个 token 一个向量,带顺序 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 4. 多层 Transformer │
│ - 每层:自注意力(看整段关系)+ 前馈网络(非线性变换) │
│ - 最后一层最后一个位置的向量 = 「整段输入的总表示」 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 5. 线性层 + Softmax → 下一个 token 的概率分布 │
│ 6. 按策略选一个 token(贪婪/采样)→ 拼到序列末尾 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 7. 把「原输入 + 刚生成的 token」当作新输入,回到步骤 3 │
│ 重复 3→4→5→6→7,直到出现结束符或达到长度上限 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 8. 把整段生成的 token ID 解码成文字 → 你看到的回复 │
└─────────────────────────────────────────────────────────────┘
九、为什么「算下一个词的概率」就能正确回答问题?理论依据是什么?
整篇文章讲的都是:AI 只是在算「下一个 token 的概率分布」 ,再按概率选一个、拼上去,循环下去。一个很自然的问题是:凭什么这样做就能「正确回答问题」?理论依据在哪?
9.1 核心:预测得准,必然要学到「让句子成立」的规律
- 训练目标 :模型在训练时被要求最小化「预测下一个 token」的误差 ------也就是在大量文本上,让模型给出的概率分布尽量接近「真实下一个词是什么」。要预测得准,模型就必须在内部抓住什么样的上文后面通常会接什么的规律。
- 文本里已经编码了「对」的 pattern :人类写出来的文本(教材、问答、对话、代码)里,正确 的答案往往以「问题 → 正确答案」的形式反复出现;错误或胡说的写法在语料里要么很少,要么结构不同。所以,谁能在「给定上文,预测下一个词」这件事上做得好,谁就不得不 学到:在像「问句」的上文后面,接的往往是在数据里常出现的、看起来正确的那类续写 。也就是说,「正确回答问题」并不是模型额外学的一个能力,而是「把下一个词预测准」学好了之后的一种表现------因为「正确回答」在训练数据里本来就是高概率的续写。
9.2 要预测准,就必须隐式学到语法、语义和事实
- 语法与结构 :若不知道主谓宾、时态、搭配,就预测不准下一个词。所以优化「下一个词概率」会迫使模型学到语法、句法这类结构。
- 语义与常识:若不知道「苹果」和「吃」更常一起出现、「太阳从东边升起」这类模式,预测也会差。所以模型会隐式学到词义、常识、以及「什么和什么在现实中常一起被说到」。
- 问答与推理 :训练数据里若大量存在「问句 + 正确答句」的序列,那么在这些问句后面,高概率的下一句就是正确答句 。模型为了把概率预测准,就会学到「在这种问句后面,该接这样一段话」,从而在行为上表现为「正确回答问题」。
所以:正确回答,是因为在训练数据里「正确回答」本身就是高概率续写;模型只是被训练成把这种高概率如实反映出来。
9.3 信息论视角:预测准 ≈ 压缩得好 ≈ 抓住了规律
- 在信息论里,预测下一个符号有多准 和把这段数据压缩得多好 是等价的(熵、交叉熵)。模型要在大规模文本上把下一个词预测准,就相当于在学一个对文本分布的压缩表示 ;而要压缩得好,必须抓住数据里的规律(重复出现的结构、因果关系、事实性表述等)。
- 所以:「算下一个词的概率并追求预测准」本身就是一个目标,它倒逼模型去捕获语言和世界中的规律;这些规律里就包含「什么问题后面通常接什么正确答案」。
9.4 不保证「绝对正确」,只保证「和训练分布一致」
- 没有绝对对错 :模型不会「判断真理」,只会输出在它学到的分布下高概率的续写 。所以「正确」是相对于训练数据里常见、一致、被当作正确的那种写法而言的。数据有偏见或错误,模型也会学进去;数据里正确答法多,模型才更容易答对。
- 总结 :这套算法的理论依据是------在人类文本中,「正确回答」往往就是「问题之后的高概率续写」;把「下一个词的概率」算准了,模型就会把这种高概率续写输出出来,从而在行为上表现为正确回答问题。 所以不是「先会判断对错再回答」,而是「把概率学对了,对的答案自然概率高」。
十、五条底层逻辑总结
| # | 底层逻辑 |
|---|---|
| 1 | 一切输入都是数字:文本 → token ID → 向量;模型只处理数字和向量。 |
| 2 | 顺序很重要:通过位置编码和自注意力,模型在向量空间里保留了「谁在前谁在后」的信息。 |
| 3 | 理解 = 向量变换:没有符号化的「理解」,而是通过多层注意力 + 前馈,把输入变成一种高维表示,能用来预测下一个 token。 |
| 4 | 生成是逐步的:每次只预测「下一个 token」,再把它接上去,再预测再接,自回归循环。 |
| 5 | 能力来自训练:为什么能写斐波那契?因为训练数据里类似「写函数、数列、第 n 项」的模式出现很多次,模型学到了「这种上下文后,下一个 token 常是什么」的统计规律。 |
十一、核心组成:训练得到的「大表」+ 固定「算法」
可以把大语言模型看成两大部分:训练学出来的大矩阵(表) + 写死的计算流程(算法)。前者存的是「知识」,后者规定「怎么用这些表算一遍」。
11.1 训练得到的大表(矩阵 / 参数)
这些都是训练时用数据 + 梯度下降学出来的,推理时只做查表或矩阵运算,不再更新。
| 出现在哪 | 名称 / 作用 | 形状(示意) | 说明 |
|---|---|---|---|
| 阶段 2 输入 | Token 嵌入表 E | (V, D) | 词表 V 个 token,每个对应 D 维向量;token ID → 查一行 → 得到向量。 |
| 阶段 2 输入 | 位置编码/位置嵌入 | (最大长度, D) 或公式 | 给每个位置一个 D 维向量;可学习表或固定公式(如 sin/cos)。 |
| 阶段 3 每层 | W_Q, W_K, W_V | 各 (D, d) 或 (D, D) | 自注意力里把当前向量变成 Q、K、V 的线性层权重;多头时每头一套小矩阵。 |
| 阶段 3 每层 | 注意力输出投影 | (D, D) | 多头拼在一起后再做一次线性变换(有的实现合在 W_V 里)。 |
| 阶段 3 每层 | FFN 两层 | (D, D_mid), (D_mid, D) | 前馈网络:先扩维到 D_mid 再压回 D;中间有激活(如 GELU)。 |
| 阶段 3 每层 | LayerNorm 的 scale / shift | 各 (D,) | 层归一化里的可学习缩放和偏移。 |
| 阶段 4 输出 | 输出层 W(lm_head) | (D, V) | 最后一维向量 → 乘 W → 得到 V 个 logits;有的模型与 E 共享(W = Eᵀ)。 |
- V = 词表大小(约几万~十多万),D = 隐藏维度(如 768、4096),D_mid = FFN 中间层维度(常为 4D)。
- 层数多时,阶段 3 的那几类表会重复 N 次(N 层),所以总参数量里绝大部分来自这些重复的「层内表」。
11.2 固定算法(流程与公式)
这些是不随训练改变的步骤和公式:规定怎么用上面的表做计算、怎么得到下一个 token。
| 阶段 | 算法 / 步骤 | 在做什么 |
|---|---|---|
| 阶段 1 | 分词(Tokenization) | 按词表把文本切成 token,转成 ID 序列;规则 + 词表(词表可视为一种「表」,但通常不通过梯度更新)。 |
| 阶段 2 | Embedding 查表 | 对每个 token ID 查 E 的对应行,得到向量;再按位置加上位置编码。 |
| 阶段 3 | 自注意力 | 用 W_Q/W_K/W_V 算 Q、K、V → Q 与所有 K 点积(可除以 √d)→ Softmax 得权重 → 对 V 加权求和。 |
| 阶段 3 | 残差 + LayerNorm | 注意力输出加上本层输入(残差),再做 LayerNorm;FFN 后同样残差 + LayerNorm。 |
| 阶段 3 | 前馈 FFN | 对每位置向量:线性 → GELU(或 ReLU)→ 线性,公式固定,权重来自上表的 FFN 两层。 |
| 阶段 4 | 线性层 | 最后一位置向量 z × W(lm_head)→ 得到 V 个 logits。 |
| 阶段 4 | 温度缩放 | logits / T(T 为推理时设定的超参数,不训练)。 |
| 阶段 4 | Softmax | 对 logits 做 Softmax,得到每个 token 的概率,和为 1。 |
| 阶段 4 | 解码策略 | 贪婪:取 argmax;采样:按概率做一次随机抽样(如轮盘赌 / categorical)。 |
| 阶段 5 | 自回归循环 | 把选出的 token 拼到序列末尾,整段再走阶段 2→3→4,直到结束符或长度上限。 |
| 阶段 6 | 反分词(Detokenize) | 把生成的 token ID 序列按词表还原成文本(或子词拼成词)。 |
11.3 一句话对照
- 「表」 :E、位置、每层的 W_Q/W_K/W_V、FFN、LayerNorm、最后的 W 等,都是训练出来的大矩阵/向量,占模型体积的绝大部分。
- 「算法」 :分词、查表、Q/K/V 与注意力公式、残差、LayerNorm、FFN 公式、线性层、温度、Softmax、采样/贪婪、自回归、反分词,都是固定流程与公式,不占「可学习参数」。
所以:AI(这里指这类大语言模型)= 若干训练得到的大表 + 一套固定算法;能力来自表里的数,行为由算法决定。
十二、常见问题
Q1:矩阵表越大、维度越多,AI 就越聪明、包含的内容就越多吗?
大致趋势是对的,但不能简单画等号。
- **参数量(矩阵越大、维度越多、层数越多)**确实和模型能力强相关,业界观察是:在数据与训练跟得上的前提下,规模变大 (更多参数、更大隐藏维 D、更多层、更大词表)往往能带来更强的理解、推理和生成能力,这种现象叫「 scaling law」(缩放定律)。所以可以说:在同等训练和数据条件下,更大的模型通常有更大的「容量」,能记住和利用更多模式,表现往往更好。
- 但「聪明」和「包含的内容」不只由参数量决定:
- 训练数据 :模型里「包含什么」主要来自训练时喂进去的数据。表再大,若数据少或质量差,也学不到那么多有用内容;数据多且好,小模型也能学到不少,只是容量上限更低。
- 训练方式:同样的参数量,预训练、指令微调、对齐做得好不好,会明显影响最终是否「聪明」、是否少犯错。
- 效率与过拟合:矩阵过大而数据或算力有限时,可能训不充分或过拟合,反而不如稍小一点、训得更好的模型。
一句话 :更大、维度更多的矩阵表,相当于给模型更大的「脑容量」,在数据和训练跟得上的前提下,一般会更聪明、能承载更多学到的模式;但「包含的内容」和「聪明程度」还强烈依赖数据质量、训练过程,不能只看规模。
Q2:为什么同一个问题,多问几次回答会不一样?
因为阶段 4 用了采样而不是每次都选概率最大的 token。采样是按概率随机抽,所以每次生成的 token 序列可能不同,同一问题就会得到不同回复。若把温度 T 调低或设为贪婪解码(总选最大概率),同一输入下输出会稳定很多。
Q3:模型会「忘记」或「更新」之前学的内容吗?
推理时 :不会。模型只是用现有的表做计算,参数不变,不会因为和你多聊几句就改掉表里的数。
训练/微调时 :会。重新训练或微调会更新矩阵表,相当于「学新东西」,但可能覆盖或削弱以前学到的(这就是微调时的「灾难性遗忘」问题)。平时对话里你感觉到的「记住对话内容」,一般是把历史写进当前这次输入的 prompt,不是模型参数在变。
Q4:模型真的「理解」我的话吗?
按本文的底层逻辑:模型没有符号化的「理解」,只是在做向量变换 + 下一个 token 预测 。它学到的是「像你这样的输入,后面常接什么样的 token」的统计规律,所以表现像「懂了」------但本质是模式匹配与概率,不是人类那种理解。说它「理解」是一种方便的说法,严格讲是「在统计上很好地拟合了语言与任务模式」。
Q5:输入一个词,AI 就是在算「和它关联的词有哪些、概率多少」,再挑一个展示?关联特别多时,是不是就得维度很大才能覆盖?
你的直觉对了一大半,可以这样理解,再补两点精确化:
- 本质上 :模型确实是在算「在当前这段上下文 下,下一个 token 是哪一个的概率各是多少」,然后按概率选一个(贪婪或采样)展示。所以可以粗略说:AI 在算「和当前内容关联的下一段内容有哪些、概率大概多少」,再选一个相近的展示 。注意:模型看的是整段输入(可能很多词),不是单一个词;「关联」是「整段上下文 ↔ 下一个 token」的关联。
- 关联多 ↔ 需要更大容量 :如果一个词(或更准确说,某种上下文模式 )在真实数据里会接很多种不同的后续------例如「苹果」后面可能是水果、公司、手机、品牌等------模型就要在内部把「这种上下文」表示成一种向量,再通过最后的矩阵表映射到词表上,让很多个 下一个 token 都能得到合理的概率。
维度少 时,向量空间就那么大,能「区分」的不同模式有限,很多不同的关联会挤在一起,容易混淆(例如把「苹果公司」和「苹果水果」的后续搞混)。维度多、矩阵大 时,空间更大,可以给更多种「上下文→下一词」的关联各自留出足够的表示,所以要覆盖「一个词/一种上下文关联特别多」的情况,理论上就需要更大的矩阵、更多的维度 ,否则确实很难同时把这么多关联都区分开、概率都学准。
可以简单记:关联越多、要区分的模式越多 → 需要更大的「表」和更高维的向量,才能装得下、分得清;只有几个维度的话,很难覆盖大量不同的关联内容。
Q6:所以现在的 AI 都是分领域/分专业的,维度可以少一些,只训练该领域内容,准确度也更高?
是的,这个思路对。 分领域、分专业的模型正是这么做的:
- 领域小 → 要覆盖的「关联」少 :只做医疗、法律、代码、客服等某一类任务时,「上下文 → 下一个词」的模式只集中在这一块,不需要同时兼顾全网各种话题。所以用较小的矩阵、较少的维度和层数,就够把该领域内的主要关联学好,不必做成「通才」那种超大参数量。
- 维度/参数相对少的好处 :
- 同样算力下可以训得更充分(数据量相对需求更易满足);
- 少混入无关模式,不会像大模型那样在「苹果」上既背水果又背公司又背手机,在垂直领域里歧义更少;
- 推理更快、部署更省资源。
- 准确度 :在该领域内,用高质量专业数据训出来的小模型,往往能在该领域任务上达到更高准确度或更稳定,因为容量都用在「这一块」上了。通用大模型则是在「什么都懂一点」和「单领域极致」之间做权衡。
所以:分领域、分专业的 AI 用相对少的维度和参数、只训练该领域内容,是常见做法,也往往能在该领域里做到更高准确度、更省资源。
Q7:AI 没有判断对错的能力,理论上要在训练时把完全正确的知识喂给它,才能保证准确性?
大体对。 可以这样理解,再补一点现实中的情况:
- 没有内在的「对错判断」 :按本文的底层逻辑,模型只是在做下一个 token 的概率预测 ,学到的是「什么样的上下文后面常接什么」的统计规律。它不会 在内部先「判断这句话对不对」再决定说不说;输出什么,取决于训练数据里这类输入后面通常是什么。所以模型本身不具备「判断事实对错」的能力,它只会模仿数据里的模式。
- 训练数据决定「对错」倾向 :如果训练时喂进去的多数是正确、一致的知识,模型就更可能在这些问题上给出正确、一致的答案;如果数据里错误、矛盾、偏见多,模型也容易复现这些错误。因此理论上,要在训练时尽量提供正确、高质量的知识,才能提高模型在事实性上的准确性,这一点是对的。
- 现实中 :训练数据几乎不可能「完全正确」------会有噪声、过时信息、矛盾说法。模型有时会「平均」掉一部分错误,有时反而会放大错误或产生幻觉。所以除了尽量用好数据训练,还会用检索增强(RAG) 、引用外部知识库 、人类反馈与对齐(如 RLHF) 等手段,在推理时或训练中把「什么算对」再约束一下;但这类约束仍是靠数据和规则教给模型的,不是模型自己「会判断对错」。
一句话 :AI 没有天生的对错判断能力;理论上训练时给它的知识越正确、越一致,它在该类问题上的准确性越有保障;现实中会再配合检索、对齐等手段尽量提高可依性。
文档整理自对「给 AI 一个命令后系统一步一步做了哪些事」的讲解。