Modular Connections

残差流之间的连接,本身也可以是模块化的模型

苏悦(Yue Su)· 2026 年 11 月 6 日

DeepSeek 提出的 mHC(Manifold-Constrained Hyper-Connections)把 Transformer 的单条残差流扩展为 N 条并行的流。于是每个子层都要回答三个问题:从哪些流读取输入,把输出写回哪些流,流与流之间又如何混合。这三件事合起来,就是残差流之间的连接。

在 mHC 中,这些连接只是几组系数:每个 token 经过一个小投影(再加上静态偏置),当场算出自己的读、写和混合权重。我的发现是,残差流之间的连接本身也可以是一个模块化的模型。每个子层可以维护一组读、写和混合模块,每个 token 像在 MoE 中那样路由到其中几个,再把它们加权组合。MoE 把子层内部的计算拆成了可路由的模块,这里拆开的则是残差流之间的连接。我把这种做法称为 Modular Connections,简称 MoC。

最近一项针对 DeepSeek-V4-Flash 的分析,从反方向印证了这个观点(Zhao et al., arXiv:2609.05309)。该模型的 mHC 本可以为每个 token 生成不同的连接,训练出来却很少用到这份自由:各子层读写的有效流数平均约为 2,同一段输入中近九成 token 共享同一条主导流;把 43 层中前 22 层的混合矩阵固定为各自在数据上的均值,困惑度仅上升 0.2%;后 21 层的混合矩阵则整体接近单位阵。换言之,连接本来就是模块化的:每个子层都有相对稳定的连接模式,只是被 mHC 逐 token 生成系数的方式掩盖了。

这个想法来自我在纸上推演 mHC 对单个 token 的计算。只要把 N 条流换一种顺序排开,读、写、混合就分别成了一个卷积、一个反卷积(转置卷积)和一个滑动的 4×4 映射(第 2 节)。从这个角度看,每个 token 的连接就是一组核,而核完全可以有很多个,交给 token 去挑选。第 3 节给出 MoC 的具体形式,第 4 节用它重新解读 DeepSeek-V4-Flash 中的观察,第 5 节报告 MoC 带来的实际收益。

1. mHC 的一步

标准的残差连接只有一条流:

xl+1 = xl + F(xl)

Hyper-Connections 把 C 维的残差状态扩展为 N 条流,记作 Xl ∈ ℝN×C,mHC 取 N = 4。每个子层(Attention 或 MLP)围绕这些流做三件事:

Xl+1 = Hres Xl + H⊤post F(Hpre Xl)

在 mHC 中,这三组系数都由当前 token 的状态生成:Hpre = σ(·),Hpost = 2σ(·),Hres 则经 Sinkhorn-Knopp 投影为双随机矩阵。因此对单个 token 而言,这三组系数就是一组确定的数。

下面的动画演示单个 token 的计算过程。为了便于观察,这里取 N = 4 条流,每条 C = 4 维。

流 1 流 2 流 3 流 4 层输入(底部色条 = 来自各流的比例) 层输出
动画 1 mHC 对一个 token 的一步(按流来看)
mHC 的一步 Xl+1 = Hres Xl + H⊤post F( Hpre Xl )

2. 发现:mHC 的一步是一组卷积

关键:交错排布

取 N = 4 条流,每条 C = 4 维,把它们交错排成一个 16 维向量 v,让位置 c·N + i 存放第 i 条流的第 c 维:

v[c·N + i] = Xl[i, c] 流 1 → 位置 1、5、9、13;流 2 → 位置 2、6、10、14;流 3、流 4 依此类推

这样排布之后,每 4 个相邻位置恰好是同一维度在 4 条流上的取值。mHC 的三步运算都只发生在同一维度的 4 条流之间,因此都变成了这个向量上窗口和步长均为 4 的操作:

两条支路相加,就得到下一层的状态。下面的动画逐步演示这一过程,可以随时暂停、单步执行,或点击下方的圆点跳到任意一步。

动画 2 同一个 token 的同一步,按卷积来看
排布 v[c·N + i] = Xl[i, c]
按流 Xl+1 = Hres Xl + H⊤post F( Hpre Xl )
按卷积 vl+1 = MixHres(v) + Deconvhpost( F( Convhpre(v) ) )

概括起来,对每个 token,在系数给定时有:

Xl+1 = Hres Xl + H⊤post F(Hpre Xl)
⟺
vl+1 = MixHres(v) + Deconvhpost(F(Convhpre(v)))

这一等价成立需要三个条件:v 按上述方式交错排布;卷积和反卷积的 kernel 与 stride 都等于 N,窗口互不重叠;滑动的 4×4 映射是纯线性的。

两种算法,同一结果

下面用随机数把同一步分别按两种方式算一遍:中间一栏用 mHC 的原始公式,右边一栏用卷积视角,依次做交错、卷积、F、反卷积,再加上滑动的 4×4,最后拆回 4 条流。这里的 F 取一个随机的「线性层 + tanh」。

动画 3 数值核对(每次点击都会换一组随机数)
输入 Xl:4 条流 × 4 维
按流计算:HresX + H⊤postF(HpreX)
按卷积计算,再拆回 4 条流
卷积视角下的结果 vl+1(16 维,交错排布)

用 PyTorch 也能直接验证,conv1d 和 conv_transpose1d 的核正是 mHC 的系数:

import torch, torch.nn.functional as F

N, C = 4, 4
x = torch.randn(N, C)                      # x[i, c]:第 i 条流的第 c 维
h_pre, h_post = torch.rand(N), torch.rand(N)
H_res = torch.rand(N, N)
f = torch.tanh                             # 任意的层函数

# 按流:mHC 原式
ref = H_res @ x + h_post[:, None] * f(h_pre @ x)[None, :]

# 按卷积:交错排布,位置 c*N + i 放 x[i, c]
v = x.T.reshape(1, 1, N * C)
u = F.conv1d(v, h_pre.view(1, 1, N), stride=N)                  # 读:卷积
dv = F.conv_transpose1d(f(u), h_post.view(1, 1, N), stride=N)   # 写:反卷积
m = F.conv1d(v, H_res.view(N, 1, N), stride=N)                  # 混合:滑动 4×4
m = m.transpose(1, 2).reshape(1, 1, N * C)
out = (m + dv).view(C, N).T                                     # 拆回 4 条流

print(torch.allclose(out, ref, atol=1e-6))                      # True

3. 方案:Modular Connections(MoC)

从卷积的角度看,mHC 中每个 token 都在「生成」自己的一组核:一个读卷积核 hpre、一个写反卷积核 hpost,以及一个滑动的 4×4 映射 Hres。

MoC 的做法是把这些连接做成模块:不再让 token 直接生成核,而是让模型学习一批卷积 / 反卷积专家和 4×4 混合专家,每个 token 像在 MoE 中那样路由到其中几个,再把它们加权组合。

三组专家

和 MoE 一样,每个子层都有自己的专家库,分为三组:

这些专家都是可学习的参数,由所有 token 共享。混合专家同样经过 Sinkhorn-Knopp 归一化,但归一化作用在专家参数本身:训练中每次参数更新后计算一次,结果由所有 token 共用;训练结束后,则可以一次算好并存下来。

用若干矩阵的组合来构造混合矩阵,并不是 MoC 首创。mHC-lite 把混合矩阵写成固定置换矩阵的凸组合,从而完全绕开了 Sinkhorn(Yang and Gao, arXiv:2601.05732)。MoC 的不同之处在于,读、写、混合三组都是可学习的专家,并且各自做稀疏路由。

路由与加权

对每个 token,路由器根据它的状态为每组专家打分,保留得分最高的 k 个,并在这 k 个分数上做 softmax,得到权重 g:

s = Router(xt), R = TopK(s, k), g = softmax(sR)

随后用选中的专家完成这一步计算:

u = Σe∈Rpre ge · Conv(v; K(e)pre)
Δv = Σe∈Rpost ge · Deconv(F(u); K(e)post)
m = Σe∈Rres ge · Mix(v; M(e))
vl+1 = m + Δv

由于卷积对核是线性的,Σe ge · Conv(v; K(e)) = Conv(v; Σe ge K(e)),实际计算时可以先把选中的专家核按权重合成一个核,再做一次卷积;反卷积和混合同理。权重 g 非负且和为 1,因此合成的混合矩阵是所选专家的凸组合。

下面的动画按「打分、选出 top-k、合成、接入」的顺序连续处理三个 token。为了演示,每组设 E = 6 个专家,每组选 k = 2 个。专家图标中颜色越深,表示对该流的权重越大;4×4 混合专家的第 i 行表示输出流 i 从各条流取多少。

动画 4 MoC:每个 token 路由到不同的卷积、反卷积和混合专家
路由 s = Router(xt)  R = TopK(s, k),g = softmax(sR)
读 u = Conv(v; Σe∈Rpre ge K(e)pre)
写 Δv = Deconv(F(u); Σe∈Rpost ge K(e)post)
混合 m = Mix(v; Σe∈Rres ge M(e))  vl+1 = m + Δv

与 mHC 的对照

mHCMoC
读、写、混合的核从哪里来每个 token 由投影直接生成从三组专家中路由、加权组合
token 决定什么核的具体数值选哪几个专家、各占多少权重
需要学习的参数生成核的投影和静态偏置专家核本身和路由器
Sinkhorn 归一化每个 token、每个子层各做一次对专家参数做,结果由所有 token 共用;训练完可以预先算好

4. 回看:mHC 实际学到的连接

引言提到的那项分析,测量了 DeepSeek-V4-Flash 全部 86 个子层(43 层,每层含 Attention 和 FFN 各一个)上 mHC 实际生成的连接,并在推理阶段做了干预实验。放在 MoC 的框架下看,它的每一项观察都对应着 MoC 的一个设计选择:

DeepSeek-V4-Flash 中的观察在 MoC 中
读写集中在少数几条流上。读、写的有效流数平均分别约为 2.0 和 1.8;同一段输入中,平均 87%(读)和 90%(写)的 token 共享同一条主导流;主导流随深度变化。读、写专家本身就是固定的连接模式,例如「主要从流 1、2 读取」。每个子层有自己的专家库,不同深度可以学到不同的模式。
每个 token 只需要少数几条通路。在所有子层上只保留每个 token 最大的 3 个读(或写)权重,并按比例放大以保持总和不变,困惑度仅上升 2.4%(或 2.7%);只保留 2 个时则上升 12%–14%。每个 token 只从专家库中选出 k 个专家组合,而不是得到一组取值任意的新系数。需要说明的是,这里剪去的是流的权重,而 MoC 选择的是专家,两者并不完全对应,但指向同一个方向:token 需要的只是少数几种选择。
混合主要由子层决定,逐 token 的变化作用很小。把前 22 层各子层的混合矩阵固定为其在 C4 样本上的均值,困惑度仅上升 0.2%。混合专家是子层的静态参数,子层特有的结构就保存在专家里,token 只在少数几个专家之间选择。当每个子层只有一个混合专家时,混合矩阵不再随 token 变化,相当于这里「固定为均值」的干预。
深层的残差混合很弱。后 21 层的混合矩阵整体接近单位阵,全部替换为单位阵后,困惑度仅上升 1.9%。单位阵只是混合专家可以学到的一种模式。哪些层需要混合、哪些层把各条流原样传下去,交给各层的专家库自己学习。

也就是说,在训练好的 DeepSeek-V4-Flash 中,连接已经接近「每个子层相对固定的模式,加上少量逐 token 的选择」。MoC 正是按这种结构设计的:模式由模块承载,选择交给路由器。

这些观察来自一个训练好的 mHC 模型,而 MoC 学到的连接也呈现同样的结构:无论是 512 步的短实验,还是正式训练第 6,000 步的 checkpoint,路由统计都是如此(见第 5 节)。训练全部完成后的情况,还有待进一步检验。

5. MoC 的优势

以下数据均来自笔者的实测。除正式训练外,所有测试都在同一型号的单张 GPU 上完成。模型为 1.36B 参数、23 层,代码与配置和正式训练一致:MoC 每组 4 个专家(E = 4),每个 token 选 2 个(k = 2),读、写、混合分别路由。作为对照的 mHC 和普通残差使用同一套代码。

推理时无需 Sinkhorn

MoC 的混合矩阵是专家参数,与 token 无关。训练结束后,只需对每个专家做一次 Sinkhorn 并缓存结果;推理时只要计算路由权重,再对选中的两个 4×4 矩阵加权求和。mHC 的混合矩阵则是逐 token 计算的,按照 mHC 论文的配置,推理时每个 token 在每个子层都要执行 20 轮 Sinkhorn 迭代。

下表给出单次前向传播的耗时(推理模式,bf16,取 30 次测量的中位数)。未编译部分的括号内,是 46 个子层中连接部分单独测得的耗时。

输入(条数 × 长度)普通残差mHCMoC(专家矩阵已缓存)MoC 相对 mHC
未编译
1 × 25615.7 ms110.3 ms(93.2)34.7 ms(17.2)3.2×
1 × 204827.7 ms113.2 ms(93.5)52.5 ms(25.4)2.2×
8 × 2048176.9 ms390.7 ms(218.5)345.1 ms(173.3)1.13×
用 torch.compile 编译后
1 × 2566.8 ms26.8 ms18.1 ms1.48×
1 × 204820.6 ms43.2 ms41.2 ms1.05×
8 × 2048127.4 ms259.8 ms263.3 ms0.99×
首次编译耗时约 25 秒约 4.7 分钟约 55 秒

未编译时,MoC 的优势最明显,而且输入越短,差距越大。token 较少时,mHC 在每个子层固定执行的 20 轮 Sinkhorn 会变成大量细碎的 kernel 调用,占据了前向耗时的大头。用 torch.compile 编译后,Sinkhorn 循环被融合,差距随之大幅缩小:短输入(1 × 256)下 MoC 仍快约 1.5 倍,长输入和大批量下两者基本持平。不过编译本身也有代价,同样的推理前向,mHC 需要编译约 4.7 分钟,MoC 只需约 55 秒。

缓存专家矩阵不会改变模型输出,缓存前后的 loss 逐位一致。即使不缓存,MoC 每次前向也只为全部子层的专家统一做一次 Sinkhorn,未编译时比缓存版本仅慢约 3.5 毫秒。

需要说明的是,这些测量基于笔者的 PyTorch 实现,没有使用 mHC 论文中手写的融合 kernel;这套代码目前也不支持 KV cache 解码,因此用短输入的完整前向来近似对延迟敏感的场景。此外,两种多流连接都明显慢于普通残差,这主要来自多条流读写带来的访存开销,而这正是 mHC 论文的融合 kernel 重点优化的部分。

编译快得多

下表测量的是单个训练 micro-step:在单卡上对 8 × 2048 个 token 做一次前向和反向传播,不含优化器更新,取 20 次的中位数。编译耗时为空缓存下的冷启动时间,两个模型同时编译,条件相同。

mHCMoC
未编译:每步耗时1.086 s1.003 s
未编译:峰值显存55.1 GiB52.9 GiB
首次编译耗时40.4 分钟2.5 分钟
编译后:每步耗时0.866 s0.808 s
编译后:峰值显存46.7 GiB46.7 GiB

编译完成后,两者的速度和显存相差无几,真正的差别在编译本身:MoC 需要 2.5 分钟,mHC 需要 40.4 分钟,约为前者的 16 倍。每次修改代码或模型配置都要重新编译,这项开销会反复出现。编译后的 MoC 已用于正式训练,至今运行稳定,第 1,350 至 6,340 步之间按理论 FLOPs 计算的 MFU 中位数约为 51%。当然,mHC 论文用手写的融合 kernel 优化了逐 token 的 Sinkhorn,mHC-lite 也指出 Sinkhorn 的高效实现离不开高度定制的 CUDA kernel。因此,这里的优势在于工程上更省事,而不是 mHC 无法做到。

可解释、可检查

MoC 中每个专家都有明确的编号,因此可以直接按专家做统计:哪些层偏好哪些专家,什么样的 token 走哪种模式;也可以逐个专家检查约束是否成立。相比之下,分析 mHC 需要直接处理逐 token 的系数矩阵,参考文献 4 正是这样做的。笔者在 512 步的短实验和正式训练第 6,000 步的 checkpoint 上做了这两类分析,路由统计分别基于 dev 分片的 4,194 万和 839 万个 token:

每组 4 个专家,按使用率排序第 512 步第 6,000 步
最常用的专家被多少 token 选中98.7%99.4%
第二常用83.2%84.2%
第三、第四常用15.3%、3.2%14.3%、2.4%
两个名额几乎固定的组(共 138 组)6163
第二个名额明显随 token 变化的组2119
几乎没被选中的专家(共 552 个)6785
列和偏离 1 超过 0.05 的混合专家(共 184 个)5581

从统计中可以直接读出路由的结构,而且这一结构在正式训练中保持稳定:每组中几乎总有一个专家被选中,第二个名额才在其余专家之间随 token 变化。这与第 4 节的观察一致,即相对固定的模式加上少量逐 token 的选择。

约束是否成立,也可以直接从参数上检查。所有混合专家的行和都精确为 1(误差小于 10−6),但 20 轮 Sinkhorn 并没有让每个专家的列和都收敛到 1:第 6,000 步时,184 个混合专家中有 81 个的列和偏离超过 0.05,中位偏差为 0.04。mHC 的逐 token 矩阵同样存在这个问题(mHC-lite 测得单层列和最多偏离 100%),但要发现它,需要对大量 token 的矩阵做统计。

6. 小结

7. 展望

我正在推进相应的实验,关注的问题只有一个:MoC 的效率优势要真正被采用,前提是效果与 mHC 相当。

目前的正式训练已经初步体现出这一点。我沿用第 5 节的配置(1.36B 参数、23 层),在约 49B tokens 上训练 MoC(共 46,875 步),每 250 步在同一验证集上测一次 BPB,并与已完成的 mHC 正式训练在相同步数上比较。截至 2026 年 11 月 6 日,训练已进行到第 6,360 步(约 14%)。自第 2,000 步起,MoC 与 mHC 的验证 BPB 之差始终在 −0.0009 到 +0.0017 之间,同时比普通残差低 0.013–0.016。需要注意,这只是单个种子在训练早期的结果;而且这次训练开启了 torch.compile,所用硬件也与作为参照的 mHC 训练不同。最终结论有待训练完成后给出。

如果你有任何建议或合作意向,欢迎直接联系我:selenyuesu@gmail.com。

引用

如果这篇文章对你的工作有帮助,欢迎引用:

@misc{su2026modular,
  author = {Su, Yue},
  title  = {Modular Connections: The Connections Between Residual Streams Can Themselves Be Modular Models},
  year   = {2026},
  month  = nov,
  url    = {https://selen-suyue.github.io/modular-connections/},
  note   = {Blog post}
}

参考

  1. Zhu et al. Hyper-Connections. arXiv:2409.19606.
  2. DeepSeek-AI. mHC: Manifold-Constrained Hyper-Connections. arXiv:2512.24880.
  3. Shazeer et al. Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer. arXiv:1701.06538.
  4. Zhao et al. How Does mHC Use Its Residual Streams? Selective Routing and Near-Identity Mixing. arXiv:2609.05309.
  5. Yang and Gao. mHC-lite: You Don’t Need 20 Sinkhorn-Knopp Iterations. arXiv:2601.05732.