Kimi K3 架构篇:一文详解 2.8 万亿参数的巨型怪兽
Kimi K3 那 47 页报告我读了两遍。第一遍看的是「它做了什么」,第二遍看的是「它为什么非得这么做」。最深的印象不是 2.8 万亿参数,而是它把效率拆成了三条信息轴:序列、深度、宽度。
这篇是三部曲第一篇,只聊架构。我会尽量少搬公式,但有几个地方必须上公式才讲得清,我会一段一段拆开。后面两篇分别聊训练和部署。
先交代清楚 K3 是什么:总参数 2.8 万亿,每次推理只激活约 1040 亿(MoE 稀疏激活),上下文窗口一百万 token,原生多模态(文字 + 图像 + 视频),并且开放了权重。报告里反复出现一句话:相对上一代 K2,整体 scaling efficiency 提升了大约 2.5 倍。
这个「2.5 倍」是整篇题眼,但很容易被读成「堆参数堆出来的」。K3 确实比 K2 大了近 2.7 倍(K2 总参 1.04 万亿、激活 326 亿;K3 总参 2.8 万亿、激活 1042 亿),光看规模它本来就该更强。真正值得注意的是那个 2.5 倍 scaling efficiency:同样一份训练算力花下去,K3 这套「架构 + 数据 + 训练 recipe」组合,比 K2 的基线多换回约 2.5 倍的能力。也就是说,强的不只是「更大」,更是「同样算力更会花」。而这份「会花」,根基就在架构。
一个被低估的总框架:信息流有三条轴
读报告时我最受启发的,是作者把整个架构归纳成「沿三个维度扩展信息流」:
- 序列维度(sequence):每个 token 怎么和前面的 token 打交道,解决「长上下文」;
- 深度维度(depth):每一层怎么和前面的层打交道,解决「信息在层层传递里被压缩稀释」;
- 宽度维度(width):每个 token 走哪一路专家网络,解决「容量和算力的矛盾」。
图:Kimi K3 把架构效率拆成序列、深度、宽度三条信息轴。三轴各自填坑,组合起来才是那 2.5 倍。
我以前看模型架构,习惯一条线往下读(注意力层、FFN、残差、下一层)。K3 的好处是它明确告诉你:这三件事是分开设计的,各管一维,组合起来才有效。下面我顺着这三条轴拆。
序列轴:KDA,一种「记忆大小固定」的注意力
先说传统 Transformer 注意力的老毛病。它每次生成新 token,都要拿当前 query 去和前面所有 token 的 key/value 算一遍,这些 key/value 就存在 KV cache 里。上下文越长,KV cache 越大,显存和算力都线性涨。一百万 token 的上下文,KV cache 能吃掉吓人的显存。这就像你写一个服务,每次请求都要把整个历史会话从头拉一遍,会话越长越扛不住。
图:softmax 注意力的 KV cache 随长度线性增长;KDA 维护固定大小的递归状态 S,新 token 进来只更新 S,长上下文才便宜。
线性注意力(linear attention)的思路完全不同:它不存所有历史,而是维护一个固定大小的「递归状态」,每来一个新 token 就更新一下这个状态。类比一下,传统注意力像「把聊天记录全存数据库,每次查全表」;线性注意力像「维护一个滚动摘要,新消息进来就合并进摘要,旧消息不需要单独留着」。摘要大小是固定的,跟对话多长无关。
K3 的序列轴用的是 Kimi Delta Attention(KDA)。它在线性注意力的基础上加了一个「通道级遗忘门」:每个通道(可以理解为隐藏维度里的一个方向)有自己的保留系数,决定旧信息忘多少、新信息记多少。这比「所有通道一刀切地衰减」要细得多。
KDA 到底在算什么:把公式摊开
很多文章讲到这就停了,但我觉得公式必须摊开才真正的「讲透」。对单个注意力头,KDA 的递归状态是一个固定形状的矩阵S_t ∈ R^{dk × dv},它在一连串 token 上被「衰减、纠错、写入」地更新:
St=(I−βt⋅ktktT)⋅Diag(αt)⋅St−1+βt⋅ktvtTot=StT⋅qt S_t = (I − β_t · k_t k_tᵀ) · Diag(α_t) · S_{t-1} + β_t · k_t v_tᵀ o_t = S_tᵀ · q_tSt=(I−βt⋅ktktT)⋅Diag(αt)⋅St−1+βt⋅ktvtTot=StT⋅qt
图:KDA 的状态更新拆成四步——先逐通道衰减旧记忆,再沿当前 key 方向擦掉冲突信息,最后用 delta rule 写入新关联。
拆开看每一步:
Diag(α_t)是逐通道遗忘门。α_t是一个dk维向量,不是标量。也就是说,记忆矩阵的每一「行」(每个 key 通道)都有自己的遗忘率,有的信息能记很久,有的很快忘掉。这是 KDA 相对 Gated DeltaNet 多出来的那一步细粒度——Gated DeltaNet 只有一个标量α_t,全局一刀切地忘。I − β_t · k_t k_tᵀ是 delta rule 的「擦除」项。它只沿着当前 keyk_t的方向擦掉旧记忆里冲突的部分,而不是无差别地把整块记忆抹淡。β_t 是标量,控制「写多强」。β_t · k_t v_tᵀ是「写入」项,把当前 (key, value) 关联写进记忆。- 最终的读
o_t = S_tᵀ · q_t就是一次普通的矩阵乘。
这套更新有一个很关键的工程含义:状态S_t始终是dk × dv,跟上下文长度 T 无关。所以 KDA 的每一步计算量不随序列变长而增长,跨块传递、跨设备通信、跨请求复用都有了一个可控的上限。代价是它有损压缩——你不再能像全注意力那样直接回看「第 3721 个 token 的原文」,只能从一份不断被覆盖的摘要里读。
报告里还补了 Q/K 的前处理:query 和 key 先过一层短因果卷积(只看最近几个位置)、一个 Swish 激活、再做 L2 归一化;然后分出两个 sigmoid 分支——一个是低秩(先下投影再上投影,像个蝴蝶结)产生的逐通道遗忘门α_t,另一个是一条线性层产生的标量写入强度β_t。这些控制信号的来历讲清楚后,KDA 就不是一个黑箱了。
最被低估的一个细节:下界衰减
这是我觉得整篇报告里最该被讲出来的工程点,也是 KDA 能跑上 Tensor Core 的真正钥匙。
KDA 在 chunk 内要把 key 按「累积衰减」的倒数来缩放。问题来了:累积衰减是一堆小于 1 的数相乘,结果可以无限小,它的倒数就无限大,在 BF16 这种有限精度下会直接溢出。Kimi Linear(上一代)用负 Softplus 映射,但范围没有下界,对角线 tile 只能老老实实用逐个位置对的方式算,成了性能瓶颈。
K3 改了一刀:把衰减对数用一个带下界的 scaled sigmoid 来映射,固定gmin = -5。效果是什么?每个保留系数都大于e^-5 ≈ 0.0067,一个 16-token tile 内的累积衰减落在 (-80, 0) 区间,倒数小于 e^80,刚好还在 BF16 动态范围内。就这么一个改动,对角线 tile 和 off-diagonal tile 全都能用 dense 的 Tensor Core 矩阵乘法来算,原来那条「逐位置对」的慢路径整个被干掉了。
我读到这的时候挺感慨的:一个前沿模型的效率提升,常常不是什么惊天动地的算法,而是「把数值范围卡在硬件能吃下的区间里」这种很 dirty 但很关键的工程判断。Tensor Core 只认规整的矩阵乘,你不把范围收住,它就不给你加速。顺便提一句,KDA 在代数上属于 DPLR(Diagonalized Parallel Linear Recurrent)家族——把这点点出来,是想说明它不是一个拍脑袋的新东西,而是「线性注意力 → delta rule → 门控遗忘 → 逐通道门控」这条脉络上几乎必然的一步。
为什么是 3:1 的混合,而不是纯线性注意力
纯线性注意力有个先天缺陷:递归状态是「有损压缩」,全局的、精细的 token-to-token 交互会丢。所以 K3 在每个 block 里放 3 层 KDA,再跟 1 层 Gated MLA(Multi-head Latent Attention,最早来自 DeepSeek-V2)。MLA 的核心是把每个 token 的 KV 压缩成一个低维潜向量来缓存,省 KV cache 又保留全局注意力。K3 让 MLA 层负责「高保真的全局交互」,KDA 层负责「高效的局部/长程混合」,并且 backbone 最后一层强制是 MLA,保证输出前总能做一次全局注意力。
这个 3:1 的比例不是拍的。Kimi Linear 的报告里做过消融:3:1 在 validation PPL 上是最优的 Pareto 配置;NoPE 的 MLA 又在长上下文外推上明显占优。所以 K3 选它,是有实验支撑的,不是审美偏好。
这里还有个对部署极其友好的设计:K3 给所有 MLA 层用了 NoPE(No Position Encoding,不显式编码位置)。位置信息完全靠 KDA 的递归门控和衰减隐式表达。好处是当你要把上下文从 64k 拉到 1M 时,完全不用去改 RoPE 的 base、不用上 YaRN 那种插值技巧,模型「天然就能外推」。我后面写训练篇会讲,这个选择直接让一百万上下文的扩展变得便宜了很多。
深度轴:AttnRes,让每一层能「翻前面的笔记」
标准残差连接(residual)的做法是:每一层的输入 = 上一层输出 + 本层算的东西。所有历史信息被压进一个向量里逐层往下传。报告里打了个很准的比方:这其实和 RNN 在时间维上的瓶颈是一回事,只不过发生在深度维上。信息越多越深,那个单一状态就成了瓶颈。
图:标准残差只能看上一层;Block AttnRes 把 93 层分成 8 个 block,当前 block 能查询前面所有 block 的表示。
Attention Residuals(AttnRes)把「注意力」这套方法从序列维搬到了深度维:每一层不再只接上一层的输出,而是用一个可学习的伪 query,去对所有前面层的表示做加权读取,权重由数据自己决定。类比一下,普通残差像是「你只能看上一步的 diff」;AttnRes 像是「你可以直接在 git 历史里挑任意几个 commit 来参考,而且挑哪些由当前任务决定」。
完整版 AttnRes 要给每一层都存所有前面层的输出,内存是 O(Ld),层多了吃不消。K3 用 Block AttnRes 化解这个:把 93 层分成 8 个 block(每块约 12 层),block 内先求和成一个代表向量,block 之间再做完整注意力,只关注那 8 个 block 级表示。内存和跨 stage 通信从 O(Ld) 降到 O(Nd)。而且推理时,block 间的部分和可以用在线 softmax 和 block 内的部分和合并,省了不少时间。
这里有个工程权衡值得记一下:报告说 N=8 就能回收大部分收益。也就是说,不是越细越好,block 粒度是个可以调的旋钮,8 是一个性价比拐点。我自己在做系统的时候也常有这种体会:很多「更精细」的设计,收益曲线在第一个拐点之后就平了,后面全是复杂度成本。
一个有意思的开放问题是:AttnRes 本身也是不带位置编码的(NoPE)。那对于这种跨层回看的注意力,「位置」到底有多重要,目前还没有定论。K3 没回答,只是把它用上了。
宽度轴:Stable LatentMoE,把「专家」塞进紧凑潜空间
MoE(混合专家)程序员基本都熟:每个 token 由一个路由器挑几个「专家」网络来算,没被选中的不激活,所以参数虽多、单次要算的少。类比微服务:一个请求来了,网关按内容路由到少数几个服务实例,其他实例闲着。
图:每 token 从 896 个路由专家里激活 16 个 + 2 个共享专家。「激活爆炸」用 SiTU-GLU 防,「负载不均」用 Quantile Balancing 治。
K3 这次把路由专家池扩到了每层 896 个,每 token 激活 16 个,另有 2 个全宽共享专家。稀疏度从 K2 的 48 倍(384 专家 / 8 激活)提到 56 倍(896 / 16)。这种极端稀疏带来两个只在「大到 2.8 万亿」才暴露的坑,报告叫它们 failure modes,我觉得讲得特别实在。
第一个坑是激活爆炸。路由路径里串了将近四个连续矩阵乘(下投影、专家、上投影、再合并),在超低精度下,某些坐标会炸成离谱的大数,导致溢出。K3 的解法有两招:在 up-projection 前加一层 RMSNorm 把尺度压住;把常用的 SwiGLU 激活换成一个叫 SiTU-GLU 的新东西。SiTU-GLU 用tanh(x/α)这种 softcap 把乘积两端的增长都框住,原点附近又和 SwiGLU 几乎一样,所以局部响应保住了,大数不会飞。超参就两个:α=4(门分支)和α=25(上分支),都是拍出来的具体数,不是玄学。
第二个坑是负载不均。近千个专家,如果哪个专家被过度选中、哪个一直吃灰,专家并行训练就会跛脚,甚至有的专家根本训不动。常见的辅助损失(auxiliary loss)方法在 896 这个量级会失稳。K3 用 Quantile Balancing(QB):给每个专家的路由分数加一个偏置,这个偏置不是靠梯度更新,而是直接从「当前 batch 的 router 分数分位数」算出来,让每个专家恰好拿到目标负载(q = mk/n)。实现上为了跨成千上万张卡的全局 batch,他们用直方图估计分位数,一次 all-reduce 把每专家的 bin 计数加起来就能还原全局分位数,通信成本只有每专家几百个 bin。这套 QB 在推理时被冻结,训练时才动。
我挺喜欢 QB 这个点,因为它把「负载均衡」从「加个损失项软约束」变成「直接按目标分位数反解偏置」,是典型的「用统计估计换掉不可靠的梯度信号」的工程思路。
MoE 的存储真相:稀疏是算力省,不是存储省
这里必须补一个容易被宣传材料糊弄过去的点。MoE 看起来「只激活 3.7% 的参数,好省」,但部署时有个硬约束:
图:每 token 只激活 16/896 个专家,但路由随时可能调到任何一个,所以 896 个必须全部驻留显存。
路由决策发生在计算过程中,模型没法在推理时只把 16 个专家搬进显存、把其余 880 个丢在磁盘。所有专家必须同时住在快速显存里待命。这就像一家医院雇了 896 名专科医生,每个病人只看其中两三个,但你得把 896 个人都养在楼里。
所以 K3 的 2.8 万亿参数,即使压成 MXFP4,发布格式也有约 1.56 TB(96 个分片),BF16 全精度更是约 5.6 TB。这个存储下限决定了它只能跑在数据中心级 GPU 集群上——这一点我会在部署篇专门算账。这里先记住:MoE 省的是「每 token 的计算量」,不省「把所有专家装进内存」这件事。
顺带提一句原生视觉与优化器
两件事值得补,因为它们都体现了 K3「从头协同设计」的思路。
视觉上,K3 不是「先训语言模型再挂个视觉编码器」。它从预训练第一步就让语言和多模态联合优化:MoonViT-V2 把图像/视频编码成视觉特征,一个轻量 projector 投进共享 embedding 空间,然后和文字 token 在同一个 next-token 目标下交错训练。MoonViT-V2 是从零用 next-token prediction 训出来的,不需要对比预训练(contrastive pretraining),在达到基线效果的同时拿到了更稳的优化过程。视觉数据里他们还大量合成了「代码 + 渲染图」的配对(SVG、3D、网页、游戏、CAD),这点对程序员尤其友好,模型从根上就懂「代码能画出什么」。
优化器上,K3 用了 Per-Head Muon(而非普通 Adam)。Muon 对权重矩阵做正交化(把动量矩阵做牛顿-舒尔茨迭代正交化后再更新),对线性注意力这种「状态矩阵」特别友好——它能让递归状态的方向更稳定,缓解大矩阵训练里的病态条件。报告特意点出是 per-head 的,意味着每个注意力头的权重矩阵独立正交化,而不是整个大矩阵一起算。这个细节很多人会跳过,但它在 KDA 这种「记忆是矩阵」的结构里,比 Adam 更不容易训飞。
收尾:架构到底解决了什么
把三条轴合起来看,K3 的架构不是在单点堆料,而是系统性地把「信息流」做厚:
- 序列轴用 KDA 把长上下文的算力压下来(固定大小递归状态 + 下界衰减让 Tensor Core 全速跑);
- 深度轴用 AttnRes 把层间信息保住(Block 化把 O(Ld) 降到 O(Nd));
- 宽度轴用 Stable LatentMoE 把容量和算力的矛盾解开(SiTU-GLU 防爆炸、Quantile Balancing 防偏斜)。
再叠上 NoPE 让长上下文外推几乎免费、Per-Head Muon 让训练更稳、MoonViT-V2 让原生多模态从第一步就对齐,才凑出那 2.5 倍的 scaling efficiency。
说实话,我读的时候最被打动的不是某个模块多新颖,而是每个模块都在回答一个非常具体的问题:「在 2.8 万亿参数、一百万上下文、要在真实 GPU 上跑起来的前提下,这个数值/通信/内存的坑该怎么填」。KDA 的公式、gmin=-5、SiTU 的 α、QB 的直方图——这些都不是论文里用来好看的装饰,是逼出来的工程答案。
架构篇就到这里。
文末互动
- 开放问题:你觉得开源大模型眼下最该优先补的,是「底座规模」还是「推理深度」?
- 投票:你更想先看 K3 训练里的「白盒 RL 环境」,还是部署里的「KDA 上下文并行」?
- 小作业:把你最常用的一款模型,套进「序列 / 深度 / 宽度」三条轴里看看它的设计重点在哪,评论区交答案。
后续这个系列会一直更新的,下一篇我想写一篇专门讲解训练K3的篇幅,所以我们下一篇《训练篇》见。
