如果你还没来得及细看 Kimi K3 的开源代码,海外开发者 Baseten 已经替我们熬了两个通宵,喝掉整整 40 罐气泡水,把模型的技术细节从头到尾研究了一遍。
他不仅贯通了 K3 当下的设计,还从代码里梳理出一条从 2019 年持续至今的技术演进线。
先说一个最直观的对比:
2019 年的 GPT-2 有 1.24 亿参数,如今 Kimi K3 的单模型体量,相当于 22580 个 GPT-2 叠加在一起。
从 1.24 亿到 2.8 万亿,Kimi 团队花了七年时间才走到今天这套架构。
但这篇分析真正的价值,是指出了关键事实:两万多倍的跨越,核心不是靠堆叠参数。

他把线索梳理得非常清楚——Kimi K3 并非在某一时刻突然出现,而是沿着一条技术暗线迭代了多年。
下面我们就跟着他的思路,看看模型参数增长 22580 倍的过程中,架构究竟经历了哪些关键变化。
一、Kimi 入场前,模型如何处理记忆
2019 年的 GPT-2 虽然只有 1.24 亿参数,却已经定义了后来大模型最常见的工作模式:读入前文,再逐 Token 预测下文。
下面这张 GPT-2 预测下一个 Token 的流程图,展示得很明白:

可以看出,每生成一个新 Token,模型都得把前面的内容重新算一遍。上下文越长,重复计算就越多。
为了缓解这个问题,模型引入了 KV Cache,把已经处理过的 Key 和 Value 暂存起来,不必每次从头再算。下面是带 KV 缓存的注意力代码:
if past_kv is not None:
k_past = past_kv[0]
v_past = past_kv[1]
k = torch.cat((k_past, k), dim=2)
v = torch.cat((v_past, v), dim=2)
这种做法省下了重复计算,但缓存本身会随着上下文拉长而不断增长,显存压力也随之上升。

到了 2020 年,线性注意力机制 出现了。它不再死板地保留每一个 Token 的完整信息,而是把读过的内容压缩进一块固定大小的记忆中。
对应的核心代码是这样更新记忆的:
k = F.elu(k) + 1
k = k.transpose(-1, -2)
q = F.elu(q) + 1
S, z = cache if cache is not None else (0.0, 0.0)
S = S + k @ v
z = z + k
o = q @ S
denom = q @ z
o_scaled = o / denom
其中 S 就是模型一直维护的固定记忆。无论后续上下文有多长,模型都不需要保留一份越发臃肿的 KV Cache,这让长文本处理成本明显降低。
但线性注意力也存在问题:所有新信息持续写进同一块有限的记忆,时间长了就容易互相干扰。
为了解决这种干扰,DeltaNet 不再直接叠加新信息,而是先读取当前位置存了哪些内容,再计算新旧信息的差值,只把需要修正的部分写回去。
DeltaNet 的核心更新代码如下:
q = F.normalize(F.silu(q), dim=-1)
k = F.normalize(F.silu(k), dim=-1)
beta = torch.sigmoid(self.w_beta(x)).view(b, 1, t, 1)
S = cache if cache is not None else 0.0
v_old = k @ S
u = beta * (v - v_old)
S = S + k.transpose(-1, -2) @ u
o = q @ S
其中 v_old 代表从旧记忆中读出的信息,v - v_old 就是新旧之差。模型最终写回去的不是完整的新值,而是经过 beta 调节的修正量。
下面这张对比图能让人一眼看懂线性注意力与 DeltaNet 的差异:

- 左侧:线性注意力下,两个 Token 共用相同 Key 写入不同内容时,新旧数据直接叠加,最终导致记忆污染。
- 中间:DeltaNet 会先读取旧值并计算出修正量,再替换原先的信息。
- 右侧:验证更新后的结果——旧内容已被彻底清除,当前位置只保留新的 Value。
尽管如此,DeltaNet 仍需按 Token 顺序一步步修改记忆,这种串行方式对 GPU 很不友好。为了让它适配更大的语言模型,人们重新设计了计算方式:将连续 Token 划分成多个数据块,让每一块内容能够并行处理。

到 2024 年,这套机制已被扩展到 13 亿参数的模型,并在 1000 亿 Token 的数据上完成训练。
但 DeltaNet 仍然缺少一种能力:主动遗忘。
它可以在新信息到来时修改某条旧记录,却无法主动清理一批已经过时的内容。
比如对话突然从旅游规划转向公司财报,原来留存的酒店、机票信息可能已经无用,但 DeltaNet 只能等到新信息出现后逐条替换。
于是 Gated DeltaNet 增加了一个“遗忘开关”,其更新逻辑如下:
S = cache if cache is not None else 0.0
alpha = gate(x) # 控制旧记忆的保留比例
beta = torch.sigmoid(self.w_beta(x)) # 控制本次修改的幅度
v_old = k @ S
u = beta * (v - v_old)
S = alpha * S + k.transpose(-1, -2) @ u
o = q @ S
最关键的变化在最后两行:模型先用 alpha * S 衰减旧记忆,再把新的修正量写进去。
当 alpha 接近 1 时,旧记忆大部分继续留存;当 alpha 接近 0 时,旧记忆会被大幅削弱,为新内容腾出空间。

这样一来,即便没有新信息逐条替换,模型也能主动让整块旧记忆淡出。
到这一步,模型已经学会了写入、修改和遗忘。
不过,这个遗忘开关只能统一削弱整块记忆,无法精准决定哪些该留、哪些该忘。
随着大模型开始处理百万 Token 上下文和更复杂的 Agent 任务,这种“一刀切”的遗忘方式已经明显不够用了。
二、Kimi 重新设计记忆系统
Kimi 在 Gated DeltaNet 的基础上,提出了 Kimi Delta Attention(KDA)。
最显著的变化,就是 把统一的“遗忘开关”拆成了许多独立开关。
下图可以清晰地看到从 DeltaNet、Gated DeltaNet 到 KDA 的门控机制演进:

在 Gated DeltaNet 中,alpha 还是一个单一的数值;到了 KDA,它变成了 Diag(alpha),也就是从统一控制整块记忆,升级为按通道分别控制不同的记忆维度。
这样,模型可以让一部分记忆快速淡出,而让另一部分保存更久。
比如分析一份财报时,公司名称、年份和核心指标可以长期保留,而一些临时性的表述、重复信息则可以更快遗忘。换句话说,模型开始具备更细致的记忆管理能力——它不只知道什么时候该忘,还能判断具体应该忘掉什么。
下面是 Delta Rule、Gated Delta Rule 与 KDA 的代码对比,差异在实现中体现得非常清楚:

Kimi 官方的实验显示,这种设计在部分任务上的效果可以超过传统的完整注意力,同时还显著提升了长文本生成速度。
为了验证 KDA,Kimi 团队训练了一款总参数 480 亿、单次激活 30 亿参数的 Kimi Linear 模型,正式进入大规模 MoE 模型阶段。
在 100 万 Token 上下文下,该模型最多可减少 75% 的 KV Cache 占用,解码吞吐量最高达到传统注意力的 6 倍。
但 KDA 依然有一个先天限制:它会把大量历史信息压缩到固定大小的记忆中。
只要压缩,就必然丢失细节。模型可能记得一段话的大意,却未必能精准找回某个数字、一行代码或一句原文。
到了 Kimi K3,模型总参数从 480 亿大幅跃升到 2.8 万亿。Kimi 没有把所有希望全押在 KDA 上,而是让 KDA 与传统注意力机制配合工作。
下图是 Kimi K3 的整体架构,可以清晰看到它以“三层 KDA + 一层 MLA”为一个基本循环,并将这套结构重复 23 次。

- KDA 负责维护一块固定大小的长期记忆。它速度更快、成本更低,适合记录上下文的主要信息。
- MLA(Gated MLA) 则会定期回到完整上下文中,重新检索原始细节。它更像一次精确查询,适合找回具体数字、代码和原文位置。
可以把它们理解成日常工作的两种方式:平时先看整理好的会议纪要,快速掌握整体情况;需要核对关键数据时,再打开原始会议记录查证。
三、网络太深,Kimi K3 如何找回早期信息?
上文说到,Kimi K3 以“三层 KDA + 一层 Gated MLA”为基本循环。23 个循环组成 92 层,再多加一层 Gated MLA,最终堆叠出 93 个解码器层。
虽然 KDA 和 MLA 解决了如何在百万 Token 中保存并找回信息,这些信息进入模型后,还要在 93 层网络中不断向后传递。
传统 Transformer 靠残差连接传递层间信息。每过一层,模型都会把这一层的新结果直接加到原有结果上。层数越深,早期提取的内容就越容易被后面的信息冲淡。
为了避免这些被稀释的信息丢失,Kimi K3 引入了 AttnRes——也就是 “注意力残差”。
AttnRes 会根据当前正在处理的内容,为前面不同阶段的结果重新分配权重,让后面的网络层自行判断哪些早期信息更值得参考。
比如处理数学题时,模型可以重新关注早期网络层识别出的公式和条件;处理长文章时,则可以找回前面已经提取出的主题和关键事实。
下图对比了普通残差连接与 ResAttn 的结构差异:

左侧的传统残差连接会把之前各层的结果统一累加起来;右侧的 AttnRes 增加了多条跨层路径,并通过 alpha 权重来决定当前层应该从前面哪些阶段读取信息。
但如果每一层都重新检索之前的所有结果,训练成本和推理成本就会非常高。
因此 Kimi K3 采用了分块式 AttnRes:每经过 12 个解码器层,就把这段网络产生的中间结果整理成一个信息块,供后面的网络层按需访问。
93 层最终被划分成 8 个 AttnRes 信息块,后续层检索时只需在这些信息块之间选择,不必逐层读取全部历史结果。
下面是分块式 AttnRes 的核心实现:
V = torch.stack(blocks + [partial_block]) # [N+1, B, T, D]
K = norm(V)
logits = torch.einsum(
'd, nbt d -> nbt',
proj.weight.squeeze(),
K
)
h = torch.einsum(
'nbt, nbt d -> bt d',
logits.softmax(0),
V
)
return h
其中 V 保存了此前各个信息块以及当前块的结果;logits 会计算每个信息块对当前任务的重要程度;经过 softmax 后成为权重,再由模型按权重重新组合历史信息。
MLA 和 AttnRes 一起,解决了两个方向上的信息丢失问题:
- 上下文太长时,MLA 负责从前文中找回原始细节;
- 网络层数太深时,AttnRes 负责调取早期网络层提取出的中间结果。
四、控制计算量
Kimi K3 拥有 2.8 万亿参数,那这么大的模型怎么控制计算开销?
K3 采用了更细粒度的专家划分,总共设置 898 个专家,其中 2 个是所有 Token 都会调用的共享专家,另外 896 个则由路由器按需选取。
每处理一个 Token,最终只有 18 个专家真正参与计算,其余全部保持待命。
这种设计让 Kimi K3 能继续扩大总参数规模,同时把单 Token 的计算量控制在很有限的范围内。那 2.8 万亿代表的是模型整体容量,而不是生成每个 Token 都要跑遍全部参数。
此外,K3 还采用了潜在空间 MoE:输入先被压缩到更低维度的空间,再交给专家网络处理,计算完成后再重新投影回原来的维度。

根据原文分析,这项设计可以让专家网络的 FLOPs 几乎减少一半。Kimi K3 因此能够容纳数量更多、划分更细的专家,同时避免专家规模直接等比转化为计算成本。
五、总结
回头看这条技术演进路线,会发现每一次升级,都在解决上一种方法遗留下来的问题。

- GPT-2 使用 KV Cache 保存前文所有内容,记得完整,但上下文越长缓存就越大。
- 线性注意力把所有历史压缩进固定记忆,降低了长文本成本,却容易让不同信息互相干扰。
- DeltaNet 让模型能够修改旧记忆,减少新旧信息之间的冲突。
- Gated DeltaNet 加入主动遗忘能力,使模型能清理已经过时的内容。
- KDA 将遗忘机制进一步细化,让不同类型的信息拥有不同的保存时间。
- MLA 周期性地回到原始上下文,找回压缩记忆中丢失的细节。
- MoE 把庞大参数拆分给不同专家,每次只调用与当前内容相关的一小部分。
- AttnRes 则让模型重新读取早期网络层的中间结果,避免关键信息随着模型加深而被稀释。
所以,Kimi K3 并不是对传统 Transformer 的简单放大。它把过去七年里出现的多条技术路线组合、精调、融合到一起,才达成了能力层面真正的跨越。
前沿能力很少由某个孤立的灵感直接催生。
一篇论文里的技术方案,需要经过长期验证,再被放入完整体系中稳定运行,最终才会转化为用户真正能感受到的能力。
这也让大模型竞争的准入门槛变得更高。单点突破依然重要,但真正决定最终差距的,往往是一个团队能否把不同阶段的研究成果串联起来,并转化成可以规模化运转的系统。
站在更长的时间尺度来看,AI 的进步常常显得突然,背后却是多年积累在某个时刻集中成熟。
Kimi K3 只是这条规律的又一次体现。下一次能力边界的跃迁,大概也会以类似的方式发生。