上一篇《源码级深读 Kimi K3 的心脏 KDA》里有不少公式。如果你被 St=(I−βtktkt⊤)Diag(αt)St−1+βtktvt⊤ 这行天书劝退了——这篇就是为你写的。前置要求只有三样:会加减乘除、看得懂坐标点、知道”函数”是输入进去出来一个输出。 我们造一块 2×2 的迷你内存,十个问题,每一步都手算,最后一节你会发现那行天书只是四个日常动作:查、写、忘、纠。
一句话主线:这些公式没有在发明新数学,它们只是把”查字典、贴便签、打折扣、擦错字”四个动作,写成了可以让计算机大规模执行的算术。 每个问题讲一个动作的零件,讲完立刻告诉你它对应天书里的哪个符号。
Q1:「猫」怎么变成一串数字?——向量
问题:计算机不认识”猫”,只认识数字。怎么把一个词的”意思”变成数字?
办法很土:找几个属性,逐项打分。比如就用两个属性〔毛茸茸程度,会游泳程度〕,每项 0 到 1:
- 猫 = 0.9, 0.1
- 鱼 = [0.1, 0.9]
- 狗 = [0.8, 0.4]
这串数字就叫向量。写成 k=[0.9,0.1]。真实模型里不是 2 个属性,而是 128 个甚至几千个,而且分数是模型自己学出来的——但道理一模一样:向量 = 一张属性打分表。
对应天书:所有小写粗体字母 q,k,v 都是这种打分表。dk=128 的意思就是”这张表有 128 项”。
Q2:两个词像不像,怎么用算术算?——点积
问题:有了打分表,怎么算”猫和狗更像,还是猫和鱼更像”?
规则只有一句:对应位置相乘,再全部加起来。这叫点积。
猫⋅狗=0.9×0.8+0.1×0.4=0.72+0.04=0.76
猫⋅鱼=0.9×0.1+0.1×0.9=0.09+0.09=0.18
0.76 > 0.18:猫和狗更像。为什么这样算有效?因为只有两边都在同一个属性上得高分,那一项的乘积才大。点积就是”共同强项计数器”。
顺手解决一个吓人符号:转置 ⊤。向量默认竖着写(列),k⊤ 就是把它放倒(行)。点积写成 k⊤q,只是”放倒一个、竖着一个,行乘列”的记账写法,算的还是对应相乘再相加。
对应天书:凡是看到 kt⊤q、⟨⋅,⋅⟩,都在算”像不像”,输出一个普通的数。
Q3:怎么把「看到猫就想起鱼」存下来?——外积,一张便利贴
问题:点积能比较,但怎么”存”一条联想记忆?
为了手算干净,这一节用最简单的打分表:猫 = [1,0],鱼 = [0,1]。想存的记忆是:钥匙(key)是猫,值(value)是鱼——看到钥匙,吐出值。
工具叫外积:kv⊤,把 k 竖着、v 横着,拉出一张乘法表:
kv⊤=[10][01]=[1×00×01×10×1]=[0010]
这张 2×2 的数字表就叫矩阵,我们叫它 S(state,状态)。它就是我们的迷你内存——一整块”看到什么想起什么”的便利贴板。KDA 里每个注意力头的内存是 128×128 = 16384 个格子,但玩法和这 4 个格子完全相同。
对应天书:ktvt⊤ 就是”把第 t 个词的联想写成一张便利贴”;大写粗体 S 就是贴满便利贴的板子。
Q4:存进去了,怎么查出来?——矩阵 × 向量
问题:板子 S 里存着记忆,现在来了个查询”猫”(q=[1,0]),怎么取出”鱼”?
规则:矩阵乘向量 = 矩阵的每一行分别和这个向量做点积(Q2 的技能直接复用)。查询公式是 o=S⊤q(S⊤ 是把 S 沿对角线翻个面):
S⊤=[0100],S⊤q=[0×1+0×01×1+0×0]=[01]=鱼✓
存进去一条猫→鱼,查”猫”,出来”鱼”。而且这个读取有个很好的性质:如果查询词和钥匙只是相似而不完全相同,取出来的就是”打了相似度折扣的值”——查得越像,取得越完整。整套读写可以浓缩成一句口诀:
o=S⊤q=v×(钥匙和查询的点积)
对应天书:ot=St⊤qt——每一步的输出,就是拿当前查询去内存板上”按相似度取货”。
Q5:多存几条,为什么会「串味」?——加法的代价
问题:内存板要存很多条记忆,直接加起来行不行?
试试。已有猫([1,0])→鱼([0,1]),再加一条:狗→骨头。狗和猫有点像,狗 = [0.6,0.8];骨头 = [1,0]。
S=猫→鱼[0010]+狗→骨头[0.60.8][10]=[0010]+[0.60.800]=[0.60.810]
现在查”猫”:用 Q4 的口诀,输出 = 鱼 ×(猫·猫)+ 骨头 ×(狗·猫)= 鱼 × 1 + 骨头 × 0.6 = [0.6,1]。
串味了:查猫,吐出来 1 份鱼混着 0.6 份骨头。因为狗和猫的钥匙不完全”垂直”(点积不为零),两条记忆在同一块板上互相渗透。存得越多,渗得越糊。这就是最原始的线性注意力 St=St−1+ktvt⊤ 的病:只会往上加,从不擦除,上一篇里叫”新旧记忆互相干扰”。
对应天书:上一篇”第一步:线性注意力——只会记、不会忘”说的就是这笔账。
Q6:记错了怎么擦?——误差与 delta rule
问题:猫的记忆要更新了——现在看到猫应该想起”毛线球”([1,0]),不是鱼。怎么在不清空整块板子的前提下,只改这一条?
日常逻辑三步走:先看看现在查出来是什么,和想要的差多少,然后把差值补上去:
- 旧答案:v旧=S⊤k=[0,1](鱼)——板子还记着旧的。
- 误差:v新−v旧=[1,0]−[0,1]=[1,−1]。
- 修正:把”钥匙 × 误差”这张便利贴贴上去,S新=S+βk(v新−v旧)⊤。
β 是”这次改多少”,取 0 到 1。先用 β=1(全改)手算,从 Q3 的 S=[0010] 出发:
S新=[0010]+[10][1−1]=[0010]+[10−10]=[1000]
再查猫:S新⊤q=[1,0] = 毛线球,鱼被干净地擦掉了。注意那个 −1:它就是”擦除”的来源——误差里带负号的部分,把旧记忆减掉了。如果取 β=0.5,你可以自己算一遍,结果是 [0.5,0.5]:半鱼半毛线球,改了一半。
这套”查旧→算差→补差”就是大名鼎鼎的 delta rule(delta = 差值)。把第 3 步的括号拆开、整理一下(用到乘法分配律,外加一条”转置把乘法顺序翻面”的机械规则,不影响理解),就得到天书的标准写法:
S新=(I−βkk⊤)S+βkv新⊤
其中 I 是单位矩阵(对角线全 1、其余全 0 的矩阵,乘任何东西都不变——矩阵世界里的”1”)。所以 (I−βkk⊤) 读作:“保持原样(I),但把 k 这个方向上的旧内容擦掉 β 那么多”。
对应天书:上一篇”第二步:DeltaNet”的公式,你刚刚亲手算完了一遍。
Q7:凭什么说这是「梯度下降」?——下山的算术
问题:上一篇说 delta rule “本质是在对一个损失函数做在线梯度下降”。这句话到底什么意思?
先用一个一维例子把”梯度下降”本身讲掉。假设你的”错误程度”随位置 x 变化:L(x)=(x−3)2——在 x=3 处错误为 0(谷底),离 3 越远错得越多。你现在站在 x=0,看不见全景,只能感知脚下的坡度(这就是导数):L 的坡度是 2(x−3)。
规则:朝坡度的反方向挪一步,步长 = 学习率 × 坡度。取学习率 0.25:
| 轮次 | 位置 x | 坡度 2(x−3) | 挪动 −0.25×坡度 | 新位置 |
|---|
| 1 | 0 | −6 | +1.5 | 1.5 |
| 2 | 1.5 | −3 | +0.75 | 2.25 |
| 3 | 2.25 | −1.5 | +0.375 | 2.625 |
每一步都朝谷底(3)靠近,步子越来越小——这就是梯度下降:摸着坡度下山。“梯度”是坡度在多维空间的名字,“学习率”就是步幅系数。
现在连回 delta rule。给记忆板定义一个”错误程度”:查出来的和想要的差多远,L(S)=21∥S⊤k−v∥2(那个 ∥⋅∥2 读作”差值向量每一项平方后加起来”——就是把 Q6 第 2 步的误差变成一个衡量总错误的数)。对这个 L 摸坡度、朝反方向挪一步,挪出来的公式恰好就是 Q6 的 delta rule,β 恰好就是学习率。
不放矩阵求导过程,给你一个一维的验证,亲手就能对上:设内存只有一个格子 s,钥匙、值都是单个数 k,v。错误程度 L(s)=21(sk−v)2,坡度 = (sk−v)k,挪一步:
s新=s−β(sk−v)k=s(1−βk2)+βvk
对照矩阵版 (I−βkk⊤)S+βkv⊤:k2 对应 kk⊤,vk 对应 kv⊤,逐项同形。“记忆更新”和”下山”是同一件事——所以上一篇才说,这块内存是”测试时也在持续学习的联想记忆”:模型每读一个词,都在对自己的记忆做一步微型训练。
对应天书:Lt(S)=21∥S⊤kt−vt∥2 与”学习率为 βt 的梯度下降”——你现在拥有读懂它的全部零件。
Q8:「遗忘门」是什么?——乘个小于 1 的数,仅此而已
问题:delta rule 只在”钥匙撞上”时纠错,平时过期的记忆怎么自动淡掉?
答案土得掉渣:每一步都把整块板子乘一个略小于 1 的数,比如 0.9。乘一次淡一点,乘 20 次:0.920≈0.12——只剩 12%,自然淡忘。这个数就是遗忘门 α,取值 0 到 1:1 = 全记住,0 = 立刻清空。这就是上一篇”第三步:GDN”加的东西,术语叫”标量遗忘门”——标量 = 一个数管整块板子。
KDA 的升级(第四步)是把”一个数”换成”一张清单”:每一行有自己的折扣率。这就是 Diag(α)——把清单 α 摆到对角线上、其余补零的对角矩阵。手算一下它乘 Q5 的板子,取 α=[0.9,0.5]:
Diag(α)S=[0.9000.5][0.60.810]=[0.540.40.90]
看效果:第一行(钥匙的”毛茸茸”通道)九折,第二行(“会游泳”通道)直接五折。每排货架有了自己的保质期旋钮——这就是上一篇说的”通道级精细遗忘”,KDA 名字里的 fine-grained gating 全部含义就在这个对角矩阵里。
对应天书:Diag(αt)St−1 = “先按 128 项清单逐行打折,再进行本步的擦写”。
Q9:sigmoid、softplus、exp 是干嘛的?——保证旋钮不拧坏
问题:α 必须在 0 和 1 之间(折扣率),β 必须非负(步幅)。可它们是神经网络算出来的,网络输出什么数都有可能。怎么办?
套一层”压缩函数”,把任意实数压进合法范围。三个常见零件:
- exp(指数函数):ex。只需记住两条:输出永远是正数;输入是负数时输出在 0 和 1 之间(比如 e−1≈0.37,e−3≈0.05)。
- sigmoid:1+e−x1,把整条数轴压进 (0,1):sigmoid(0) = 0.5,sigmoid(4) ≈ 0.98,sigmoid(−4) ≈ 0.02。天生适合当”门”。KDA 用它算 β 和输出门。
- softplus:ln(1+ex),“平滑版的只留正数”:输入 −5 时输出 ≈ 0.007(几乎切没),输入 5 时输出 ≈ 5.007(几乎原样)。
上一篇引过 KDA 开源实现里的一行源码:g = -exp(A_log) * softplus(f_proj(x) + dt_bias),然后 α=eg。现在你可以逐层读了:softplus 保证一个正数,乘上 −eAlog(负 × 正)得到一个负数 g,最后 e负数 必落在 (0,1)——两层保险,保证 128 个折扣旋钮永远拧在合法档位上,不管网络内部输出多离谱的数。
对应天书与源码:αt∈[0,1]dk、βt=Sigmoid(⋯)∈[0,1] 的实现原理。
Q10:为什么全注意力「越聊越慢」,KDA 不会?——数格子
问题:上一篇说 1M 上下文时全注意力解码慢 6.3 倍、KV cache 省 75%。这些差距从哪来?数一数就知道。
全注意力的规矩:第 t 个词要和前面所有 t 个词逐一做点积。生成 T 个词的总点积次数:
1+2+3+⋯+T=2T(T+1)
T=1000 时约 50 万次;T = 100 万时约 5000 亿次。T 扩大 1000 倍,工作量扩大约 100 万倍——工作量跟着 T2 涨,记作 O(T2)(大 O 就是”增长档位”的记号,只看最猛的那一项)。而且它还得把每个词的 K、V 向量原样存着(这就是 KV cache),存储量跟着 T 线性涨:单个注意力头、128 维、100 万个词,就是 2×128×106=2.56 亿个数——而且每生成一个新词都得把它们从显存里搬一遍。
KDA 的规矩:不存历史,只维护那块 128×128 = 16384 个格子的板子。每来一个词,做的事永远是”打折、擦、写、查”这固定一套——第 1 个词和第 100 万个词的开销一模一样,总量 O(T),存储永远 16384 个数。同一个头,2.56 亿 对 1.6 万,15625 倍的差距。
这就是上一篇整个第一节(“瓶颈早就不是算力了”)的算术底座。当然,天下没有免费午餐:16384 个格子装不下 100 万个词的全部细节,这是有损压缩——所以 K3 才要每 3 层 KDA 配 1 层全注意力,精确检索的活儿留给无损的那 25%。
对应天书:O(T) vs O(T2)、“KV cache 随上下文线性增长”、“固定大小的矩阵内存”——全是这笔格子账。(顺带:上一篇表格里的”验证 PPL 5.65”,PPL 是衡量模型”惊讶程度”的指标,越低越好,展开讲见《困惑度 PPL 到底是什么》。)
终局:逐符号重读那行天书
现在把 KDA 的核心公式(上一篇 Eq. 1)拆开,每个零件你都手算过了:
St=Q6:擦旧(I−βtktkt⊤)Q8:逐行打折Diag(αt)Q3:内存板St−1+Q3+Q6:写新便签βtktvt⊤,Q4:按相似度取货ot=St⊤qt
从右往左读,每个词进来时发生四件事:
| 动作 | 符号 | 人话 | 哪个问题讲的 |
|---|
| 忘 | Diag(αt) | 128 个通道各自按清单打折 | Q8、Q9 |
| 纠 | I−βtktkt⊤ | 把这把钥匙方向上的旧内容擦掉一步(= 一步梯度下降) | Q6、Q7 |
| 写 | +βtktvt⊤ | 贴上新便签 | Q3 |
| 查 | St⊤qt | 拿查询按相似度取货 | Q2、Q4 |
下标 t 只是”第几个词”的计数器;St 由 St−1 算出,这种”这一步依赖上一步”的写法叫递推——和”这个月存款 = 上个月存款 × 利率 + 本月工资”是同一种句式。
带走三句话:
- 所有这些公式只有四个动作:查(点积)、写(外积)、忘(乘小于 1 的数)、纠(减去误差)。 下次见到任何线性注意力公式,先找这四个动作分别藏在哪。
- “学习”在数学上就是”摸着坡度下山”;KDA 的精妙在于把”记忆更新”也写成了下山——于是推理时的每一个词,都是对记忆的一步微型训练。
- 能力差距经常是”数格子”的差距:O(T2) 对 O(T)、2.56 亿对 1.6 万,架构革命的底层常常是一笔小学算术账。
如果想把这条数学线继续往上爬(矩阵求导、softmax、位置编码……),本站有一张从小学算术一路铺到前沿的路线图:《AI 数学:从算术到前沿》;读完本篇再回头看上一篇 KDA 深读,那些公式应该已经不再是天书了。
参考来源