
看到标题里的13我第一反应并不是“K3 有 13 个架构细节”而是“门控机制”这件事在 K3 里至少横跨了三层注意力层、专家混合层、FFN 激活层。你不把这条线打通只是记住 Gated MLA、KDA、SiTU-GLU、SwiGLU 这几个名词最后很容易变成“每个词都听过但画不出结构”。在展开之前我先说一个贯穿全文的判断K3 的门控不是某个激活函数换了个名字而是把“信息是否应该流过当前层”这个决策权写进了模型内部的不同粒度里。注意力有注意力门专家有路由门专家内部还有激活门。理解 K3 最有效的路径不是去背参数而是先搞清楚每一道门到底在控制什么。1. 先把“门控”从激活函数里解放出来1.1 门控的本质对信息流做乘法很多读者一听到门控第一反应是激活函数比如 SiLU、GELU、Swish。这个理解不完整。门控的核心操作本质上是一次逐元素乘法用一个控制向量去决定另一个向量里的每个位置保留多少。控制向量的取值范围可能是 0 到 1也可能是 -1 到 1。大于某个阈值就放大接近 0 就压制等于 0 就彻底关闭。这个机制最早给人留下印象是从 LSTM 和 GRU 开始的。LSTM 里的输入门、遗忘门、输出门本质上就是“决定记忆写多少、丢多少、读多少”。后来 Transformer 兴起后这个思想并没有消失而是被拆成更细的模块放进注意力、FFN 和 MoE 里。所以不要把 Gated MLA、KDA 的门控分支、SiTU-GLU、SwiGLU 当成“几个不同的函数”它们其实是同一种设计哲学在不同位置上的落地注意力层的门决定“聚合出来的上下文信息以多大比例进入当前层”专家路由的门决定“当前 token 由哪几个专家处理”专家内部的门决定“每个神经元是否被激活”KDA 的门控分支决定“常规注意力结果和知识/全局信息分支如何融合”。这是一个从宏观到微观的完整控制系统而不是孤立地换一个激活函数。1.2 Softmax 管竞争门控管开关为什么注意力已经很强大还要额外加门这里需要区分两个完全不同的机制。标准 self-attention 里的 softmax 会做一件事对一个 query 和所有 key 的点积分数做归一化让所有 attention weight 加起来等于 1。这保证模型必须在一组 token 之间做“竞争分配”。但它也带来一个边界即使某个 token 完全不重要softmax 通常还是会分给它一点权重。因为归一化要求“总量为 1”模型很难做到在某一个位置上的权重精致地等于 0。门控没有这个约束。一个 sigmoid 门输出 0.8就是 0.8输出 0.0就是彻底关闭。它不必和别的位置竞争可以独立决定“某个通道、某个 head、某一段上下文信息到底要不要通过”。所以你可以这样记softmax 管的是“分配”这些 token 里谁更重要门控管的是“开关”这些信息到底允不允许进入下一层。attention weight 和 gate value 的差别可以简单放在一起对比维度Attention WeightGate Value输出范围0 到 1且一行 softmax 求和为 10 到 1 或 -1 到 1不要求行和控制对象token 之间的权重向量、通道、分支级别的放缩典型位置QK^T 之后注意力输出之后、残差之前、FFN 内部核心语义哪些 token 值得被聚合这些聚合结果应该以多大比例通过K3 的 Gated MLA、KDA 分支以及 MoE 里的 GLU 变体都是在这个区分之上展开的。2. Gated MLA键值缓存被压缩后门控负责找回表达能力2.1 MLA 解决的是 KV cache而不是注意力计算本身先说 MLA 是什么。标准多头注意力在自回归解码时每一层都要为每个已经生成的 token 保存一份 key 和 value。随着序列变长KV cache 会线性增加。它的大致规模可以用下面这个关系估算KV cache 字节 ≈ 2K 和 V× 层数 × batch × 序列长度 × 头数 × head_dim × dtype 字节长上下文场景下序列长度一上来KV cache 就会非常夸张。于是出现了 Multi-head Latent Attention 这类做法核心思路是不再为每个 head 直接保存完整的 K 和 V而是把 K、V 的共同信息先压缩到一个低维 latent 向量里需要时再通过投影矩阵展开。这样做的好处很直接每层缓存的不再是n_heads * head_dim那么多份 key 和 value而是一个更小的 latent 向量。这对长文本推理和超大 batch 的显存规划非常友好。但压缩一定会有损失。latent 向量越小显存越省但 K、V 能表达的信息就越有限。这里就出现了一个矛盾你想用 MLA 压显存又不想因为压缩而损失注意力头的表达能力。2.2 Gated MLA 多出来的那个分支Gated MLA 解决这个矛盾的手段是加一个门控分支。可以把它理解成常规流程里query 和 latent 展开后的 key/value 做 attention得到一个上下文向量门控分支再根据当前输入或者 latent 状态算出一个控制信号决定这个上下文向量最终以什么比例输出。下面这段代码是一个抽象示意的写法不是 K3 原版实现但用来理解结构足够import torch import torch.nn as nn import torch.nn.functional as F class GatedMLAStyleBlock(nn.Module): def __init__(self, d_model, latent_dim, n_heads): super().__init__() self.n_heads n_heads self.head_dim d_model // n_heads self.q_proj nn.Linear(d_model, d_model) self.kv_proj nn.Linear(d_model, latent_dim) self.k_proj nn.Linear(latent_dim, d_model) self.v_proj nn.Linear(latent_dim, d_model) self.o_proj nn.Linear(d_model, d_model) # 门控分支从 latent 状态生成一个逐向量控制信号 self.gate_proj nn.Linear(latent_dim, d_model) def forward(self, x): B, T, _ x.shape q self.q_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2) c self.kv_proj(x) # 压缩后的 latent KV k self.k_proj(c).view(B, T, self.n_heads, self.head_dim).transpose(1, 2) v self.v_proj(c).view(B, T, self.n_heads, self.head_dim).transpose(1, 2) scores torch.matmul(q, k.transpose(-2, -1)) / (self.head_dim ** 0.5) attn torch.softmax(scores, dim-1) context torch.matmul(attn, v) context context.transpose(1, 2).contiguous().view(B, T, d_model) context self.o_proj(context) gate torch.sigmoid(self.gate_proj(c)) # 示意 return gate * context注意看这里的结构差异gate_proj的输入是 latent 向量c不是完整 K/V也不是 query。这意味着门控分支本身不需要缓存额外的历史状态它只需要当前 token 的 latent 表示就能判断这段注意力结果要不要进入残差流。当然K3 的真实实现大概率比我这里复杂得多。可能是多个投影、低秩门控、甚至基于 key 和 query 联合生成 gate。但无论如何关键思路是一致的MLA 负责压缩缓存门控分支负责在压缩之后保留信息选择能力。2.3 门控为什么比单纯加一层投影更灵活有些人可能会问既然压缩有损失多加一个线性层不就好了吗为什么偏偏用门控因为线性层对信息做的是“线性变换”它不会把一个向量整体清零也很难做到“这个 head 的输出我完全不要”。门控不一样它通过乘法可以直接把某些位置压到接近 0。这在数学上等于给模型一个能力根据当前上下文动态决定哪些通道、哪些 head 的输出要参与后续计算。这也解释了为什么 K3 要把 Gated MLA 和 KDA 的门控分支放在一起讨论。它们都是在“不该读的别读读完不该信的别信”这个逻辑上做文章。3. KDA 的门控分支注意力从“读什么”变成“信多少”3.1 把 KDA 当成一种带知识分支的注意力变体KDA 这个缩写目前有不同读法有人从“Knowledge-Driven Attention”的角度理解也有人从 Key-Decoupled 或类似结构的方向去推。与其纠结缩写不如先看它的字面位置KDA 在标题里出现时紧跟着一个词叫“门控分支”。所以我更愿意这样理解KDA 是一种注意力变体它不像标准 attention 那样只做 QK 匹配和 V 的加权求和而是额外引入一个分支。这个分支可能负责从全局记忆、知识状态或更高层语义中提取信息然后用门控去决定常规 attention 结果占多少这个额外分支的结果占多少在某些 token 上是不是应该直接忽略 attention 的一部分输出。如果 KDA 真的是这样工作的那它的意义就很清楚标准 attention 解决的问题是“哪些 token 之间应该建立联系”而 KDA 这门分支解决的问题是“这些联系建立起来之后我到底应该信多少”。3.2 门控分支和 attention head 的分工把 KDA 的门控分支和普通的 attention head 放在一起对比会发现它们根本不是同一种作用对象维度Attention HeadKDA 门控分支输入关系关注 token 与 token 之间的关系关注当前输出和知识/全局信息之间的关系输出粒度一组加权求和后的 context一个控制放缩的 gate训练目标倾向建立联系决定联系的可信度对长文本的意义在局部/全局范围内找依赖过滤噪声保留关键信息这种设计在长上下文场景下尤其有价值。因为当输入长度大到一定程度attention 的权重分布会变得稀疏且嘈杂。很多 token 之间有相关性但相关性不等于真正有用。如果你只能靠 attention weight 去选择模型很容易被无关段落干扰。门控分支提供的是第二道筛选。3.3 验证 KDA 门控是否有效可以先做一个小实验如果你想在真实模型里验证这类门控的作用不一定要复现 K3。你可以做一个很简单的对照实验用一个中等规模的 Transformer固定训练数据一组使用标准 attention另一组在 attention 输出后面加一个门控分支只让门控制输出的比例保持参数量接近对比验证集 loss 和多步推理任务。这类小实验能帮你快速获得一个感性认识门控到底是在帮模型变强还是只是增加了参数。在没做过这种对照之前不要轻易相信“门控一定更好”这种结论。4. MoE 里的 SiTU-GLU 和 SwiGLU路由之外还有一道门4.1 GLU 不是激活函数而是一个结构很多人把 SwiGLU 当成一个激活函数这是最常见的误解。严格来说SwiGLU 是“门控线性单元 Swish/SiLU 激活函数”的组合。GLU 本身是一个结构GLU(x) Linear_a(x) ⊗ σ(Linear_b(x))意思是一条分支做线性变换提供内容另一条分支也做线性变换但经过激活函数后作为门来控制第一条分支的输出。SwiGLU 就是把上面的σ替换成 SiLU。这个过程可以写成SwiGLU(x) SiLU(xW_gate) ⊗ (xW_up)在 LLaMA、Mistral、DeepSeek 等模型里这个模式很常见。它通常配合第三个投影矩阵W_down一起出现结构大致是FFN(x) W_down( SiLU(xW_gate) * xW_up )有人可能会问为什么 GLU 比普通 ReLU FFN 效果更好。一个比较合理的解释是普通 FFN 只有一层非线性变换而 GLU 相当于让网络同时学习“内容”和“内容的门控信号”。它多了一个乘法交互更强的表达能力来自这种交互而不是单纯来自激活函数本身。如果你看到代码里同时出现 GELU 和 SwiGLU也不用乱。GELU 是一个激活函数SwiGLU 是一个包含激活函数的结构。SwiGLU 的 gate 分支常用 SiLU如果 gate 分支用 GELU那就叫 GeGLU。4.2 SiTU-GLU 到底改了哪里SiTU-GLU 从命名习惯上看应该就是把 SwiGLU 里负责做门的激活函数从 SiLU 换成了 SiTU。在 K3 的公开资料还不完整的情况下我不会贸然给出 SiTU 的精确公式。但从结构上理解它不影响 GLU 的整体骨架只影响门分支里的非线性函数def gated_linear(x, W_gate, W_up, gate_fn): # 示意gate_fn 可以是 SiLU、GELU、SiTU 等 return gate_fn(x W_gate.T) * (x W_up.T)SiTU-GLU 和 SwiGLU 真正的差别不在于是否使用了“门”而在于门函数本身的梯度行为、饱和区间和计算成本。SwiGLU 的 SiLU 函数在负区间接近 0 但不会完全为 0这带来比较平滑的梯度。如果 SiTU 在相同位置有不同的曲率或饱和特性那么它对训练稳定性、收敛速度和推理效率的影响都会不同。所以看到“K3 用了 SiTU-GLU”时不要急着下判断说“它比 SwiGLU 更强”。更准确的理解是K3 在同样的 GLU 结构里换了一个更适配自家 MoE 规模的门控激活函数。具体强不强要看训练曲线、下游任务和部署成本三项结果而不是看论文里的公式。4.3 两个门不要搞混MoE Router 和 GLU Gate关于 MoE我想强调一个特别常见的混淆点MoE 的 Router 和专家内部的 GLU Gate 是两道完全不同的门。最近社区里常常有人拿 Gemma 4 26B A4B 这类 MoE 模型做部署教程。26B 是总参数量A4B 是每个 token 激活的参数量这类模型能在大规模参数下控制推理成本靠的就是 router 先做专家选择。Router 的典型工作方式是p_i softmax(W_router x)_i topK top-k(p_i) y sum_{i in topK} p_i