AI系统工程 · 运行示例

贯通算例:两句话怎样穿过一层模型

阅读工具约 3.3 千字

同一词表、同一组参数、两句有标签的输入:从数字表示走到概率,再走一次带缓存的解码。

建议先读F01—F16。每张表的行对应哪个Token、列对应哪个特征均写明。显示数值保留六位小数,计算用未舍入值。这里不要求模型生成有意义的中文。

本章目录
运行示例 · X01

沿用“我爱学习/你爱”,把一层模型真正算到底

输入与条件。两句话仍采用六行四列Embedding表。下面指定一个未训练的教学模型:一层、隐藏宽度4、两个注意力头,每头宽度2,前置LayerNorm、残差、SwiGLU前馈和绑定Embedding的输出投影。为减少变量,本例不加位置编码、偏置或dropout。

步骤与对象内部动作中间结果 / 新状态
1分别查表“我/爱/学习”取第1、2、3行;“你/爱”取第4、2行两句分别是3×4和2×4。PAD只用于批次展示,实际数值见证逐条处理有效Token。
2逐位置归一化每一排4个数分别减均值、除以标准差归一化不会混合两句话,也不会把不同Token平均成一句话。
3计算两头注意力各头分别生成分数、因果掩码、概率和Value混合两个头每个位置各产出2个数,拼回4个数后,加回原来的Embedding。
4经过前馈层再次归一化,再计算0.25×SiLU(z)×z并加残差每个位置有4个更新后的数;最后位置才用于本轮下一个Token的预测。
5投影回词表最后位置的4个数与每个词表行点积,再做softmax本例解码规则显式禁止PAD;“我爱学习”末尾产生的最高概率项是“爱”,约0.307383。
6继续一次把刚选出的“爱”编号2输入,同时带入旧K/V增量最后状态与对“我/爱/学习/爱”全量重算相同,数值误差小于1e−12。

把过程连起来。下方逐表展示全部中间张量以及每个矩阵的取值。不能只从“预测出爱”理解模型;真正要观察的是同一份输入怎样经过每一个算子。

边界与反例。权重全部人为设置,预测重复的“爱”不体现语言能力。没有位置编码的这个简化模型也不代表现代商业模型的完整架构。

依据与适用范围:[EMBED] [LN] [SDPA] [GLU]。示例输入和场景由本书明确设定。

1.先固定全部参数,不把任何矩阵藏起来

词表与Embedding E

文字先变成编号,再取对应行。下面的矩阵乘法只作用于这些已声明的教学数字。

对象特征0特征1特征2特征3
0 · PAD0.0000000.0000000.0000000.000000
1 · 我0.200000-0.1000000.7000000.400000
2 · 爱-0.3000000.8000000.1000000.500000
3 · 学习0.6000000.200000-0.4000000.900000
4 · 你0.100000-0.5000000.9000000.300000
5 · !-0.2000000.4000000.500000-0.600000

Query投影 Wq

每个Token的行向量右乘这个矩阵。Q/K/V分头时把第0、1个特征交给头0,第2、3个特征交给头1。

对象特征0特征1特征2特征3
位置01.0000000.0000000.0000000.000000
位置10.0000001.0000000.0000000.000000
位置20.0000000.0000001.0000000.000000
位置30.0000000.0000000.0000001.000000

Key投影 Wk

每个Token的行向量右乘这个矩阵。Q/K/V分头时把第0、1个特征交给头0,第2、3个特征交给头1。

对象特征0特征1特征2特征3
位置00.0000001.0000000.0000000.000000
位置11.0000000.0000000.0000000.000000
位置20.0000000.0000000.0000001.000000
位置30.0000000.0000001.0000000.000000

Value投影 Wv

每个Token的行向量右乘这个矩阵。Q/K/V分头时把第0、1个特征交给头0,第2、3个特征交给头1。

对象特征0特征1特征2特征3
位置01.0000000.0000000.0000000.000000
位置10.0000000.5000000.0000000.000000
位置20.0000000.0000001.0000000.000000
位置30.0000000.0000000.0000000.500000

所有LayerNorm均按每行4个数计算,ε=0.00001、缩放为1、偏置为0。注意力输出投影为单位矩阵。前馈使用Wgate=Wup=I、Wdown=0.25I,所以输出的每项为0.25×SiLU(z)×z。最后再做一次LayerNorm,输出投影为E的转置。无训练、无位置编码、无dropout。以上都是本例明确设定,不是某个厂商模型的参数。

2.“我爱学习”的每一层中间数

本句编号为[1, 2, 3]。下面每行始终代表同一个位置;注意力会混合可见内容,前馈仍逐位置计算。“你爱”只有两个有效位置,PAD不参与本页的逐条运行。

查表后的初始表示 X

这就是文字逐Token查表后的结果;它还没有包含另一位置的内容。

对象特征0特征1特征2特征3
位置0 · 我0.200000-0.1000000.7000000.400000
位置1 · 爱-0.3000000.8000000.1000000.500000
位置2 · 学习0.6000000.200000-0.4000000.900000

第一层归一化后的输入

以本行自己的四个数求均值与方差,再归一化。

对象特征0特征1特征2特征3
位置0 · 我-0.342977-1.3719081.3719080.342977
位置1 · 爱-1.3869121.266311-0.4221040.542705
位置2 · 学习0.565020-0.256827-1.4895981.181406

Q = LN(X) × Wq

用于给当前查询与键配对打分。

对象特征0特征1特征2特征3
位置0 · 我-0.342977-1.3719081.3719080.342977
位置1 · 爱-1.3869121.266311-0.4221040.542705
位置2 · 学习0.565020-0.256827-1.4895981.181406

K = LN(X) × Wk

提供每个可读位置的Key;生成后可按层存入缓存。

对象特征0特征1特征2特征3
位置0 · 我-1.371908-0.3429770.3429771.371908
位置1 · 爱1.266311-1.3869120.542705-0.422104
位置2 · 学习-0.2568270.5650201.181406-1.489598

V = LN(X) × Wv

提供最终会按注意力权重混合的内容。

对象特征0特征1特征2特征3
位置0 · 我-0.342977-0.6859541.3719080.171488
位置1 · 爱-1.3869120.633155-0.4221040.271352
位置2 · 学习0.565020-0.128414-1.4895980.590703

头0:配对分数(已加因果掩码)

每项为该头的Q与K点积除以√2;未来键位置设为−∞。例如第0个查询只能读第0个Token。

对象键0 · 我键1 · 爱键2 · 学习
位置0 · 我0.665434−∞−∞
位置1 · 爱1.038316-2.483729−∞
位置2 · 学习-0.4858320.757798-0.205220

头0:逐行softmax权重

每一行的可见权重和为1。掩掉的位置权重是0,而不是从打印表里省略。

对象键0 · 我键1 · 爱键2 · 学习
位置0 · 我1.0000000.0000000.000000
位置1 · 爱0.9713090.0286910.000000
位置2 · 学习0.1726480.5987760.228576

头0:权重 × Value

每个输出分量都把各可见位置的对应Value分量乘权重再相加;不是直接挑一行。

对象特征0特征1
位置0 · 我-0.342977-0.685954
位置1 · 爱-0.372929-0.648107
位置2 · 学习-0.7605130.231337

头1:配对分数(已加因果掩码)

每项为该头的Q与K点积除以√2;未来键位置设为−∞。例如第0个查询只能读第0个Token。

对象键0 · 我键1 · 爱键2 · 学习
位置0 · 我0.665434−∞−∞
位置1 · 爱0.424101-0.323965−∞
位置2 · 学习0.784805-0.924251-2.488762

头1:逐行softmax权重

每一行的可见权重和为1。掩掉的位置权重是0,而不是从打印表里省略。

对象键0 · 我键1 · 爱键2 · 学习
位置0 · 我1.0000000.0000000.000000
位置1 · 爱0.6787570.3212430.000000
位置2 · 学习0.8204070.1485240.031070

头1:权重 × Value

每个输出分量都把各可见位置的对应Value分量乘权重再相加;不是直接挑一行。

对象特征0特征1
位置0 · 我1.3719080.171488
位置1 · 爱0.7955940.203569
位置2 · 学习1.0165490.199346

拼接两个头的输出

头0两个分量在前,头1两个分量在后;拼接本身不进行求和。

对象特征0特征1特征2特征3
位置0 · 我-0.342977-0.6859541.3719080.171488
位置1 · 爱-0.372929-0.6481070.7955940.203569
位置2 · 学习-0.7605130.2313371.0165490.199346

第一次残差:X + Attention输出

同一个位置、同一个特征逐元素相加。

对象特征0特征1特征2特征3
位置0 · 我-0.142977-0.7859542.0719080.571488
位置1 · 爱-0.6729290.1518930.8955940.703569
位置2 · 学习-0.1605130.4313370.6165491.099346

前馈前的第二次归一化

输入来自上一步残差,而不是重新使用原始Embedding。

对象特征0特征1特征2特征3
位置0 · 我-0.537546-1.1422231.5454070.134362
位置1 · 爱-1.548113-0.1932371.0283880.712962
位置2 · 学习-1.457026-0.1448670.2657551.336138

门控前馈输出

逐项计算0.25×SiLU(z)×z;所有投影已在参数节声明。

对象特征0特征1特征2特征3
位置0 · 我0.0266390.0789040.4921350.002408
位置1 · 爱0.1050690.0042180.1947550.085277
位置2 · 学习0.1002670.0024340.0099940.353417

第二次残差:上一步残差 + 前馈输出

这是本例这一层最终的上下文化表示。

对象特征0特征1特征2特征3
位置0 · 我-0.116338-0.7070502.5640430.573897
位置1 · 爱-0.5678600.1561111.0903490.788846
位置2 · 学习-0.0602460.4337700.6265431.452763

最后一个有效位置:最终LayerNorm

仅展示用于本轮下一个Token预测的最后位置;前面位置的隐藏状态已经在上表列出。

对象特征0特征1特征2特征3
位置2 · 学习-1.234289-0.3288680.0244411.538716

从最后位置的4个数,投影为6个词表分数

每个logit是最后向量与E对应行的点积。然后本例解码策略显式把PAD分数改为−∞,再归一化。PAD向量为零本身不会自动让其概率为零。

对象过滤前logit过滤PAD后概率
0 · PAD0.0000000.000000
1 · 我0.4186240.193974
2 · 爱0.8789940.307383
3 · 学习0.5687210.225387
4 · 你0.5246160.215663
5 · !-0.7956980.057593

本轮贪心选择:爱,编号2。这是人为小模型产生的概率,不是语言正确性结论。

3.“你爱”的每一层中间数

本句编号为[4, 2]。下面每行始终代表同一个位置;注意力会混合可见内容,前馈仍逐位置计算。“你爱”只有两个有效位置,PAD不参与本页的逐条运行。

查表后的初始表示 X

这就是文字逐Token查表后的结果;它还没有包含另一位置的内容。

对象特征0特征1特征2特征3
位置0 · 你0.100000-0.5000000.9000000.300000
位置1 · 爱-0.3000000.8000000.1000000.500000

第一层归一化后的输入

以本行自己的四个数求均值与方差,再归一化。

对象特征0特征1特征2特征3
位置0 · 你-0.199996-1.3999721.3999720.199996
位置1 · 爱-1.3869121.266311-0.4221040.542705

Q = LN(X) × Wq

用于给当前查询与键配对打分。

对象特征0特征1特征2特征3
位置0 · 你-0.199996-1.3999721.3999720.199996
位置1 · 爱-1.3869121.266311-0.4221040.542705

K = LN(X) × Wk

提供每个可读位置的Key;生成后可按层存入缓存。

对象特征0特征1特征2特征3
位置0 · 你-1.399972-0.1999960.1999961.399972
位置1 · 爱1.266311-1.3869120.542705-0.422104

V = LN(X) × Wv

提供最终会按注意力权重混合的内容。

对象特征0特征1特征2特征3
位置0 · 你-0.199996-0.6999861.3999720.099998
位置1 · 爱-1.3869120.633155-0.4221040.271352

头0:配对分数(已加因果掩码)

每项为该头的Q与K点积除以√2;未来键位置设为−∞。例如第0个查询只能读第0个Token。

对象键0 · 你键1 · 爱
位置0 · 你0.395964−∞
位置1 · 爱1.193865-2.483729

头0:逐行softmax权重

每一行的可见权重和为1。掩掉的位置权重是0,而不是从打印表里省略。

对象键0 · 你键1 · 爱
位置0 · 你1.0000000.000000
位置1 · 爱0.9753400.024660

头0:权重 × Value

每个输出分量都把各可见位置的对应Value分量乘权重再相加;不是直接挑一行。

对象特征0特征1
位置0 · 你-0.199996-0.699986
位置1 · 爱-0.229266-0.667110

头1:配对分数(已加因果掩码)

每项为该头的Q与K点积除以√2;未来键位置设为−∞。例如第0个查询只能读第0个Token。

对象键0 · 你键1 · 爱
位置0 · 你0.395964−∞
位置1 · 爱0.477546-0.323965

头1:逐行softmax权重

每一行的可见权重和为1。掩掉的位置权重是0,而不是从打印表里省略。

对象键0 · 你键1 · 爱
位置0 · 你1.0000000.000000
位置1 · 爱0.6902980.309702

头1:权重 × Value

每个输出分量都把各可见位置的对应Value分量乘权重再相加;不是直接挑一行。

对象特征0特征1
位置0 · 你1.3999720.099998
位置1 · 爱0.8356710.153067

拼接两个头的输出

头0两个分量在前,头1两个分量在后;拼接本身不进行求和。

对象特征0特征1特征2特征3
位置0 · 你-0.199996-0.6999861.3999720.099998
位置1 · 爱-0.229266-0.6671100.8356710.153067

第一次残差:X + Attention输出

同一个位置、同一个特征逐元素相加。

对象特征0特征1特征2特征3
位置0 · 你-0.099996-1.1999862.2999720.399998
位置1 · 爱-0.5292660.1328900.9356710.653067

前馈前的第二次归一化

输入来自上一步残差,而不是重新使用原始Embedding。

对象特征0特征1特征2特征3
位置0 · 你-0.355476-1.2244241.5404020.039499
位置1 · 爱-1.483366-0.2961881.1431170.636437

门控前馈输出

逐项计算0.25×SiLU(z)×z;所有投影已在参数节声明。

对象特征0特征1特征2特征3
位置0 · 你0.0130170.0851400.4885220.000199
位置1 · 爱0.1017230.0093540.2477050.066221

第二次残差:上一步残差 + 前馈输出

这是本例这一层最终的上下文化表示。

对象特征0特征1特征2特征3
位置0 · 你-0.086979-1.1148462.7884940.400197
位置1 · 爱-0.4275430.1422431.1833760.719288

最后一个有效位置:最终LayerNorm

仅展示用于本轮下一个Token预测的最后位置;前面位置的隐藏状态已经在上表列出。

对象特征0特征1特征2特征3
位置1 · 爱-1.373724-0.4328131.2864520.520084

从最后位置的4个数,投影为6个词表分数

每个logit是最后向量与E对应行的点积。然后本例解码策略显式把PAD分数改为−∞,再归一化。PAD向量为零本身不会自动让其概率为零。

对象过滤前logit过滤PAD后概率
0 · PAD0.0000000.000000
1 · 我0.8770870.242054
2 · 爱0.4545540.158638
3 · 学习-0.9573020.038659
4 · 你1.3928660.405426
5 · !0.4327950.155224

本轮贪心选择:你,编号4。这是人为小模型产生的概率,不是语言正确性结论。

4.把刚生成的Token真正送回去

第一句的本轮输出是“爱”,编号2。把它作为新Token输入,绝不是把上一轮的最终隐藏向量直接当下一轮输入。新Token仍先查E[2]。旧三个位置的K/V已保存,新Token产生自己的Q/K/V,再与旧K/V一起计算。

新Token“爱”的初始Embedding

对象特征0特征1特征2特征3
位置3 · 爱-0.3000000.8000000.1000000.500000

新位置的Query

对象特征0特征1特征2特征3
位置3 · 爱-1.3869121.266311-0.4221040.542705

追加的新Key

对象特征0特征1特征2特征3
位置3 · 爱1.266311-1.3869120.542705-0.422104

追加的新Value

对象特征0特征1特征2特征3
位置3 · 爱-1.3869120.633155-0.4221040.271352

带旧缓存得到的新位置最终状态

对象特征0特征1特征2特征3
位置3 · 爱-0.1924740.3958380.3687931.039792

缓存模式与全量重算:最后位置对照

程序核对全部四个分量:最大绝对差为0。这个检查要求使用相同参数、相同前缀和正确掩码。

对象特征0特征1特征2特征3
只算新Token+缓存-0.1924740.3958380.3687931.039792
整句四个Token重算-0.1924740.3958380.3687931.039792

自检:缓存为什么不能直接换另一句话的

第二句“你爱”的最后一个Token也是“爱”。是否可以把第一句三个位置的缓存接到第二句后面?请先自己判断。

查看参考答案

不能。Token编号相同只说明初始Embedding行相同,不说明两个前缀一样。K/V保存了对应前缀在该层的计算结果;跨句混用会把“我/爱/学习”的历史注入“你/爱”,改变可见信息与结果。

完整计算代码 · 机器可读结果

输入关键词即可检索。

↑ ↓ 选择 Enter 打开Esc 关闭 · 仅本地搜索

阅读设置

阅读设置仅保存在本机浏览器,不上传任何数据。