会加减乘除就够:十个问题,手算读懂 Kimi K3 心脏里的全部数学

上一篇 KDA 深读里的公式劝退你了?这篇是零基础陪读:用一块 2×2 的迷你内存和十个问题,每一步都能用小学算术手算,算完你能逐个符号读懂 KDA 的核心公式。

上一篇《源码级深读 Kimi K3 的心脏 KDA》里有不少公式。如果你被 St=(Iβtktkt)Diag(αt)St1+βtktvt\mathbf{S}_t = (\mathbf{I} - \beta_t \boldsymbol{k}_t \boldsymbol{k}_t^\top)\,\mathrm{Diag}(\boldsymbol{\alpha}_t)\,\mathbf{S}_{t-1} + \beta_t \boldsymbol{k}_t \boldsymbol{v}_t^\top 这行天书劝退了——这篇就是为你写的。前置要求只有三样:会加减乘除、看得懂坐标点、知道”函数”是输入进去出来一个输出。 我们造一块 2×2 的迷你内存,十个问题,每一步都手算,最后一节你会发现那行天书只是四个日常动作:查、写、忘、纠。

一句话主线:这些公式没有在发明新数学,它们只是把”查字典、贴便签、打折扣、擦错字”四个动作,写成了可以让计算机大规模执行的算术。 每个问题讲一个动作的零件,讲完立刻告诉你它对应天书里的哪个符号。

Q1:「猫」怎么变成一串数字?——向量

问题:计算机不认识”猫”,只认识数字。怎么把一个词的”意思”变成数字?

办法很土:找几个属性,逐项打分。比如就用两个属性〔毛茸茸程度,会游泳程度〕,每项 0 到 1:

  • 猫 = 0.9, 0.1
  • 鱼 = [0.1, 0.9]
  • 狗 = [0.8, 0.4]

这串数字就叫向量。写成 k=[0.9,0.1]\boldsymbol{k} = [0.9, 0.1]。真实模型里不是 2 个属性,而是 128 个甚至几千个,而且分数是模型自己学出来的——但道理一模一样:向量 = 一张属性打分表

对应天书:所有小写粗体字母 q,k,v\boldsymbol{q}, \boldsymbol{k}, \boldsymbol{v} 都是这种打分表。dk=128d_k = 128 的意思就是”这张表有 128 项”。

Q2:两个词像不像,怎么用算术算?——点积

问题:有了打分表,怎么算”猫和狗更像,还是猫和鱼更像”?

规则只有一句:对应位置相乘,再全部加起来。这叫点积

=0.9×0.8+0.1×0.4=0.72+0.04=0.76\text{猫}\cdot\text{狗} = 0.9 \times 0.8 + 0.1 \times 0.4 = 0.72 + 0.04 = 0.76 =0.9×0.1+0.1×0.9=0.09+0.09=0.18\text{猫}\cdot\text{鱼} = 0.9 \times 0.1 + 0.1 \times 0.9 = 0.09 + 0.09 = 0.18

0.76 > 0.18:猫和狗更像。为什么这样算有效?因为只有两边都在同一个属性上得高分,那一项的乘积才大。点积就是”共同强项计数器”。

顺手解决一个吓人符号:转置 \top。向量默认竖着写(列),k\boldsymbol{k}^\top 就是把它放倒(行)。点积写成 kq\boldsymbol{k}^\top \boldsymbol{q},只是”放倒一个、竖着一个,行乘列”的记账写法,算的还是对应相乘再相加

对应天书:凡是看到 ktq\boldsymbol{k}_t^\top \boldsymbol{q},\langle \cdot, \cdot \rangle,都在算”像不像”,输出一个普通的数。

Q3:怎么把「看到猫就想起鱼」存下来?——外积,一张便利贴

问题:点积能比较,但怎么”存”一条联想记忆?

为了手算干净,这一节用最简单的打分表:猫 = [1,0][1, 0],鱼 = [0,1][0, 1]。想存的记忆是:钥匙(key)是猫,值(value)是鱼——看到钥匙,吐出值。

工具叫外积:kv\boldsymbol{k} \boldsymbol{v}^\top,把 k\boldsymbol{k} 竖着、v\boldsymbol{v} 横着,拉出一张乘法表:

kv=[10][01]=[1×01×10×00×1]=[0100]\boldsymbol{k}\boldsymbol{v}^\top = \begin{bmatrix} 1 \\ 0 \end{bmatrix} \begin{bmatrix} 0 & 1 \end{bmatrix} = \begin{bmatrix} 1\times 0 & 1 \times 1 \\ 0 \times 0 & 0 \times 1 \end{bmatrix} = \begin{bmatrix} 0 & 1 \\ 0 & 0 \end{bmatrix}

这张 2×2 的数字表就叫矩阵,我们叫它 S\mathbf{S}(state,状态)。它就是我们的迷你内存——一整块”看到什么想起什么”的便利贴板。KDA 里每个注意力头的内存是 128×128 = 16384 个格子,但玩法和这 4 个格子完全相同。

对应天书:ktvt\boldsymbol{k}_t \boldsymbol{v}_t^\top 就是”把第 tt 个词的联想写成一张便利贴”;大写粗体 S\mathbf{S} 就是贴满便利贴的板子。

Q4:存进去了,怎么查出来?——矩阵 × 向量

问题:板子 S\mathbf{S} 里存着记忆,现在来了个查询”猫”(q=[1,0]\boldsymbol{q} = [1,0]),怎么取出”鱼”?

规则:矩阵乘向量 = 矩阵的每一行分别和这个向量做点积(Q2 的技能直接复用)。查询公式是 o=Sq\boldsymbol{o} = \mathbf{S}^\top \boldsymbol{q}(S\mathbf{S}^\top 是把 S\mathbf{S} 沿对角线翻个面):

S=[0010],Sq=[0×1+0×01×1+0×0]=[01]=  \mathbf{S}^\top = \begin{bmatrix} 0 & 0 \\ 1 & 0 \end{bmatrix}, \qquad \mathbf{S}^\top \boldsymbol{q} = \begin{bmatrix} 0\times 1 + 0 \times 0 \\ 1 \times 1 + 0 \times 0 \end{bmatrix} = \begin{bmatrix} 0 \\ 1 \end{bmatrix} = \text{鱼} \;✓

存进去一条猫→鱼,查”猫”,出来”鱼”。而且这个读取有个很好的性质:如果查询词和钥匙只是相似而不完全相同,取出来的就是”打了相似度折扣的值”——查得越像,取得越完整。整套读写可以浓缩成一句口诀:

o=Sq=v×(钥匙和查询的点积)\boldsymbol{o} = \mathbf{S}^\top\boldsymbol{q} = \boldsymbol{v} \times (\text{钥匙和查询的点积})

对应天书:ot=Stqt\boldsymbol{o}_t = \mathbf{S}_t^\top \boldsymbol{q}_t——每一步的输出,就是拿当前查询去内存板上”按相似度取货”。

Q5:多存几条,为什么会「串味」?——加法的代价

问题:内存板要存很多条记忆,直接加起来行不行?

试试。已有猫([1,0][1,0])→鱼([0,1][0,1]),再一条:狗→骨头。狗和猫有点像,狗 = [0.6,0.8][0.6, 0.8];骨头 = [1,0][1, 0]

S=[0100]+[0.60.8][10]骨头=[0100]+[0.600.80]=[0.610.80]\mathbf{S} = \underbrace{\begin{bmatrix} 0 & 1 \\ 0 & 0 \end{bmatrix}}_{\text{猫}\to\text{鱼}} + \underbrace{\begin{bmatrix} 0.6 \\ 0.8 \end{bmatrix}\begin{bmatrix} 1 & 0 \end{bmatrix}}_{\text{狗}\to\text{骨头}} = \begin{bmatrix} 0 & 1 \\ 0 & 0 \end{bmatrix} + \begin{bmatrix} 0.6 & 0 \\ 0.8 & 0 \end{bmatrix} = \begin{bmatrix} 0.6 & 1 \\ 0.8 & 0 \end{bmatrix}

现在查”猫”:用 Q4 的口诀,输出 = 鱼 ×(猫·猫)+ 骨头 ×(狗·猫)= 鱼 × 1 + 骨头 × 0.6 = [0.6,1][0.6, 1]

串味了:查猫,吐出来 1 份鱼混着 0.6 份骨头。因为狗和猫的钥匙不完全”垂直”(点积不为零),两条记忆在同一块板上互相渗透。存得越多,渗得越糊。这就是最原始的线性注意力 St=St1+ktvt\mathbf{S}_t = \mathbf{S}_{t-1} + \boldsymbol{k}_t\boldsymbol{v}_t^\top 的病:只会往上加,从不擦除,上一篇里叫”新旧记忆互相干扰”。

对应天书:上一篇”第一步:线性注意力——只会记、不会忘”说的就是这笔账。

Q6:记错了怎么擦?——误差与 delta rule

问题:猫的记忆要更新了——现在看到猫应该想起”毛线球”([1,0][1,0]),不是鱼。怎么在不清空整块板子的前提下,只改这一条?

日常逻辑三步走:先看看现在查出来是什么,和想要的差多少,然后把差值补上去:

  1. 旧答案:v=Sk=[0,1]\boldsymbol{v}_{\text{旧}} = \mathbf{S}^\top \boldsymbol{k} = [0,1](鱼)——板子还记着旧的。
  2. 误差:vv=[1,0][0,1]=[1,1]\boldsymbol{v}_{\text{新}} - \boldsymbol{v}_{\text{旧}} = [1,0] - [0,1] = [1,-1]
  3. 修正:把”钥匙 × 误差”这张便利贴贴上去,S=S+βk(vv)\mathbf{S}_{\text{新}} = \mathbf{S} + \beta\, \boldsymbol{k}(\boldsymbol{v}_{\text{新}} - \boldsymbol{v}_{\text{旧}})^\top

β\beta 是”这次改多少”,取 0 到 1。先用 β=1\beta = 1(全改)手算,从 Q3 的 S=[0100]\mathbf{S} = \begin{bmatrix} 0 & 1 \\ 0 & 0\end{bmatrix} 出发:

S=[0100]+[10][11]=[0100]+[1100]=[1000]\mathbf{S}_{\text{新}} = \begin{bmatrix} 0 & 1 \\ 0 & 0 \end{bmatrix} + \begin{bmatrix} 1 \\ 0 \end{bmatrix}\begin{bmatrix} 1 & -1 \end{bmatrix} = \begin{bmatrix} 0 & 1 \\ 0 & 0 \end{bmatrix} + \begin{bmatrix} 1 & -1 \\ 0 & 0 \end{bmatrix} = \begin{bmatrix} 1 & 0 \\ 0 & 0 \end{bmatrix}

再查猫:Sq=[1,0]\mathbf{S}_{\text{新}}^\top \boldsymbol{q} = [1, 0] = 毛线球,鱼被干净地擦掉了。注意那个 1-1:它就是”擦除”的来源——误差里带负号的部分,把旧记忆减掉了。如果取 β=0.5\beta = 0.5,你可以自己算一遍,结果是 [0.5,0.5][0.5, 0.5]:半鱼半毛线球,改了一半。

这套”查旧→算差→补差”就是大名鼎鼎的 delta rule(delta = 差值)。把第 3 步的括号拆开、整理一下(用到乘法分配律,外加一条”转置把乘法顺序翻面”的机械规则,不影响理解),就得到天书的标准写法:

S=(Iβkk)S+βkv\mathbf{S}_{\text{新}} = (\mathbf{I} - \beta \boldsymbol{k}\boldsymbol{k}^\top)\,\mathbf{S} + \beta \boldsymbol{k}\boldsymbol{v}_{\text{新}}^\top

其中 I\mathbf{I}单位矩阵(对角线全 1、其余全 0 的矩阵,乘任何东西都不变——矩阵世界里的”1”)。所以 (Iβkk)(\mathbf{I} - \beta\boldsymbol{k}\boldsymbol{k}^\top) 读作:“保持原样(I\mathbf{I}),但把 k\boldsymbol{k} 这个方向上的旧内容擦掉 β\beta 那么多”。

对应天书:上一篇”第二步:DeltaNet”的公式,你刚刚亲手算完了一遍。

Q7:凭什么说这是「梯度下降」?——下山的算术

问题:上一篇说 delta rule “本质是在对一个损失函数做在线梯度下降”。这句话到底什么意思?

先用一个一维例子把”梯度下降”本身讲掉。假设你的”错误程度”随位置 xx 变化:L(x)=(x3)2L(x) = (x-3)^2——在 x=3x=3 处错误为 0(谷底),离 3 越远错得越多。你现在站在 x=0x=0,看不见全景,只能感知脚下的坡度(这就是导数):LL 的坡度是 2(x3)2(x-3)

规则:朝坡度的反方向挪一步,步长 = 学习率 × 坡度。取学习率 0.25:

轮次位置 xx坡度 2(x3)2(x-3)挪动 0.25×-0.25\times坡度新位置
10−6+1.51.5
21.5−3+0.752.25
32.25−1.5+0.3752.625

每一步都朝谷底(3)靠近,步子越来越小——这就是梯度下降:摸着坡度下山。“梯度”是坡度在多维空间的名字,“学习率”就是步幅系数。

现在连回 delta rule。给记忆板定义一个”错误程度”:查出来的和想要的差多远,L(S)=12Skv2L(\mathbf{S}) = \frac{1}{2}\lVert \mathbf{S}^\top\boldsymbol{k} - \boldsymbol{v} \rVert^2(那个 2\lVert\cdot\rVert^2 读作”差值向量每一项平方后加起来”——就是把 Q6 第 2 步的误差变成一个衡量总错误的数)。对这个 LL 摸坡度、朝反方向挪一步,挪出来的公式恰好就是 Q6 的 delta rule,β\beta 恰好就是学习率。

不放矩阵求导过程,给你一个一维的验证,亲手就能对上:设内存只有一个格子 ss,钥匙、值都是单个数 k,vk, v。错误程度 L(s)=12(skv)2L(s) = \frac{1}{2}(sk - v)^2,坡度 = (skv)k(sk-v)k,挪一步:

s=sβ(skv)k=s(1βk2)+βvks_{\text{新}} = s - \beta(sk - v)k = s(1 - \beta k^2) + \beta v k

对照矩阵版 (Iβkk)S+βkv(\mathbf{I} - \beta\boldsymbol{k}\boldsymbol{k}^\top)\mathbf{S} + \beta\boldsymbol{k}\boldsymbol{v}^\top:k2k^2 对应 kk\boldsymbol{k}\boldsymbol{k}^\top,vkvk 对应 kv\boldsymbol{k}\boldsymbol{v}^\top,逐项同形。“记忆更新”和”下山”是同一件事——所以上一篇才说,这块内存是”测试时也在持续学习的联想记忆”:模型每读一个词,都在对自己的记忆做一步微型训练。

对应天书:Lt(S)=12Sktvt2\mathcal{L}_t(\mathbf{S}) = \frac{1}{2}\lVert \mathbf{S}^\top\boldsymbol{k}_t - \boldsymbol{v}_t\rVert^2 与”学习率为 βt\beta_t 的梯度下降”——你现在拥有读懂它的全部零件。

Q8:「遗忘门」是什么?——乘个小于 1 的数,仅此而已

问题:delta rule 只在”钥匙撞上”时纠错,平时过期的记忆怎么自动淡掉?

答案土得掉渣:每一步都把整块板子乘一个略小于 1 的数,比如 0.9。乘一次淡一点,乘 20 次:0.9200.120.9^{20} \approx 0.12——只剩 12%,自然淡忘。这个数就是遗忘门 α\alpha,取值 0 到 1:1 = 全记住,0 = 立刻清空。这就是上一篇”第三步:GDN”加的东西,术语叫”标量遗忘门”——标量 = 一个数管整块板子

KDA 的升级(第四步)是把”一个数”换成”一张清单”:每一有自己的折扣率。这就是 Diag(α)\mathrm{Diag}(\boldsymbol{\alpha})——把清单 α\boldsymbol{\alpha} 摆到对角线上、其余补零的对角矩阵。手算一下它乘 Q5 的板子,取 α=[0.9,0.5]\boldsymbol{\alpha} = [0.9, 0.5]:

Diag(α)S=[0.9000.5][0.610.80]=[0.540.90.40]\mathrm{Diag}(\boldsymbol{\alpha})\,\mathbf{S} = \begin{bmatrix} 0.9 & 0 \\ 0 & 0.5 \end{bmatrix}\begin{bmatrix} 0.6 & 1 \\ 0.8 & 0 \end{bmatrix} = \begin{bmatrix} 0.54 & 0.9 \\ 0.4 & 0 \end{bmatrix}

看效果:第一行(钥匙的”毛茸茸”通道)九折,第二行(“会游泳”通道)直接五折。每排货架有了自己的保质期旋钮——这就是上一篇说的”通道级精细遗忘”,KDA 名字里的 fine-grained gating 全部含义就在这个对角矩阵里。

对应天书:Diag(αt)St1\mathrm{Diag}(\boldsymbol{\alpha}_t)\,\mathbf{S}_{t-1} = “先按 128 项清单逐行打折,再进行本步的擦写”。

Q9:sigmoid、softplus、exp 是干嘛的?——保证旋钮不拧坏

问题:α\alpha 必须在 0 和 1 之间(折扣率),β\beta 必须非负(步幅)。可它们是神经网络算出来的,网络输出什么数都有可能。怎么办?

套一层”压缩函数”,把任意实数压进合法范围。三个常见零件:

  • exp(指数函数):exe^x。只需记住两条:输出永远是正数;输入是负数时输出在 0 和 1 之间(比如 e10.37e^{-1} \approx 0.37,e30.05e^{-3} \approx 0.05)。
  • sigmoid:11+ex\frac{1}{1+e^{-x}},把整条数轴压进 (0,1):sigmoid(0) = 0.5,sigmoid(4) ≈ 0.98,sigmoid(−4) ≈ 0.02。天生适合当”门”。KDA 用它算 β\beta 和输出门。
  • softplus:ln(1+ex)\ln(1 + e^x),“平滑版的只留正数”:输入 −5 时输出 ≈ 0.007(几乎切没),输入 5 时输出 ≈ 5.007(几乎原样)。

上一篇引过 KDA 开源实现里的一行源码:g = -exp(A_log) * softplus(f_proj(x) + dt_bias),然后 α=eg\alpha = e^{g}。现在你可以逐层读了:softplus 保证一个正数,乘上 eAlog-e^{A_\text{log}}(负 × 正)得到一个负数 gg,最后 e负数e^{\text{负数}} 必落在 (0,1)——两层保险,保证 128 个折扣旋钮永远拧在合法档位上,不管网络内部输出多离谱的数。

对应天书与源码:αt[0,1]dk\boldsymbol{\alpha}_t \in [0,1]^{d_k}βt=Sigmoid()[0,1]\beta_t = \mathrm{Sigmoid}(\cdots) \in [0,1] 的实现原理。

Q10:为什么全注意力「越聊越慢」,KDA 不会?——数格子

问题:上一篇说 1M 上下文时全注意力解码慢 6.3 倍、KV cache 省 75%。这些差距从哪来?数一数就知道。

全注意力的规矩:第 tt 个词要和前面所有 tt 个词逐一做点积。生成 TT 个词的总点积次数:

1+2+3++T=T(T+1)21 + 2 + 3 + \cdots + T = \frac{T(T+1)}{2}

T=1000T=1000 时约 50 万次;TT = 100 万时约 5000 亿次TT 扩大 1000 倍,工作量扩大约 100 万倍——工作量跟着 T2T^2 涨,记作 O(T2)O(T^2)(大 O 就是”增长档位”的记号,只看最猛的那一项)。而且它还得把每个词的 K、V 向量原样存着(这就是 KV cache),存储量跟着 TT 线性涨:单个注意力头、128 维、100 万个词,就是 2×128×106=2.562 \times 128 \times 10^6 = 2.56 亿个数——而且每生成一个新词都得把它们从显存里搬一遍

KDA 的规矩:不存历史,只维护那块 128×128 = 16384 个格子的板子。每来一个词,做的事永远是”打折、擦、写、查”这固定一套——第 1 个词和第 100 万个词的开销一模一样,总量 O(T)O(T),存储永远 16384 个数。同一个头,2.56 亿 对 1.6 万,15625 倍的差距。

这就是上一篇整个第一节(“瓶颈早就不是算力了”)的算术底座。当然,天下没有免费午餐:16384 个格子装不下 100 万个词的全部细节,这是有损压缩——所以 K3 才要每 3 层 KDA 配 1 层全注意力,精确检索的活儿留给无损的那 25%。

对应天书:O(T)O(T) vs O(T2)O(T^2)、“KV cache 随上下文线性增长”、“固定大小的矩阵内存”——全是这笔格子账。(顺带:上一篇表格里的”验证 PPL 5.65”,PPL 是衡量模型”惊讶程度”的指标,越低越好,展开讲见《困惑度 PPL 到底是什么》。)

终局:逐符号重读那行天书

现在把 KDA 的核心公式(上一篇 Eq. 1)拆开,每个零件你都手算过了:

St=(Iβtktkt)Q6:擦旧  Diag(αt)Q8:逐行打折  St1Q3:内存板+βtktvtQ3+Q6:写新便签,ot=StqtQ4:按相似度取货\mathbf{S}_t = \underbrace{(\mathbf{I} - \beta_t \boldsymbol{k}_t \boldsymbol{k}_t^\top)}_{\text{Q6:擦旧}}\;\underbrace{\mathrm{Diag}(\boldsymbol{\alpha}_t)}_{\text{Q8:逐行打折}}\;\underbrace{\mathbf{S}_{t-1}}_{\text{Q3:内存板}} + \underbrace{\beta_t \boldsymbol{k}_t \boldsymbol{v}_t^\top}_{\text{Q3+Q6:写新便签}}, \qquad \underbrace{\boldsymbol{o}_t = \mathbf{S}_t^\top \boldsymbol{q}_t}_{\text{Q4:按相似度取货}}

从右往左读,每个词进来时发生四件事:

动作符号人话哪个问题讲的
Diag(αt)\mathrm{Diag}(\boldsymbol{\alpha}_t)128 个通道各自按清单打折Q8、Q9
Iβtktkt\mathbf{I} - \beta_t\boldsymbol{k}_t\boldsymbol{k}_t^\top把这把钥匙方向上的旧内容擦掉一步(= 一步梯度下降)Q6、Q7
+βtktvt+\,\beta_t\boldsymbol{k}_t\boldsymbol{v}_t^\top贴上新便签Q3
Stqt\mathbf{S}_t^\top\boldsymbol{q}_t拿查询按相似度取货Q2、Q4

下标 tt 只是”第几个词”的计数器;St\mathbf{S}_tSt1\mathbf{S}_{t-1} 算出,这种”这一步依赖上一步”的写法叫递推——和”这个月存款 = 上个月存款 × 利率 + 本月工资”是同一种句式。

带走三句话:

  1. 所有这些公式只有四个动作:查(点积)、写(外积)、忘(乘小于 1 的数)、纠(减去误差)。 下次见到任何线性注意力公式,先找这四个动作分别藏在哪。
  2. “学习”在数学上就是”摸着坡度下山”;KDA 的精妙在于把”记忆更新”也写成了下山——于是推理时的每一个词,都是对记忆的一步微型训练。
  3. 能力差距经常是”数格子”的差距:O(T2)O(T^2)O(T)O(T)、2.56 亿对 1.6 万,架构革命的底层常常是一笔小学算术账。

如果想把这条数学线继续往上爬(矩阵求导、softmax、位置编码……),本站有一张从小学算术一路铺到前沿的路线图:《AI 数学:从算术到前沿》;读完本篇再回头看上一篇 KDA 深读,那些公式应该已经不再是天书了。

参考来源