DeepSeek-V4架构深度拆解:mHC、CSA、HCA、SWA与MoE工程实现全解析 1. 从一张架构图说起DeepSeek-V4 到底改了什么第一次看到 DeepSeek-V4 那张架构总览图的时候我盯着看了差不多二十分钟。不是看不懂而是信息密度太高——一张图里塞进了 mHC、CSA、HCA、SWA、MoE 五套机制每一套单拎出来都够写一篇长文。后来我把这张图打印出来贴在工位上对着各个模块逐个拆解又翻了大量关于 MoE 架构的公开讨论才慢慢把整条数据通路理顺。这篇内容就是那次拆解的完整记录。我会从工程实现的角度把这张架构图里每个模块的作用、为什么这么设计、实际跑起来要注意什么一层层讲清楚。不管你是刚接触 MoE 架构的新手还是已经在做模型推理优化的老手应该都能从中找到对自己有用的部分。尤其是那些看到架构图就头大、不知道从哪下手的同学我会尽量用生活化的类比把每个机制讲明白。先说结论DeepSeek-V4 这套架构的核心思路是在保持 MoE 稀疏激活优势的前提下通过 mHC、CSA、HCA、SWA 四个机制分别解决多尺度特征融合、跨层信息传递、注意力效率、长序列建模这四个工程难题。它们不是简单堆叠而是互相配合形成一条完整的数据处理链路。下面我逐个拆。2. mHC 模块多尺度特征融合的工程实现2.1 mHC 到底解决什么问题mHC 全称是 Multi-scale Hierarchical Connection翻译过来叫多尺度分层连接。这个名字听起来很学术但它的核心目标特别朴素让模型在同一层里同时看到不同粒度的信息。你可以把它想象成一个团队开会。如果只有一个人发言信息就单一如果同时有基层员工、中层主管、高层领导分别从不同角度说同一件事你对这件事的理解就会立体很多。mHC 干的就是这个事——它在同一层里并行跑多个不同感受野的分支然后把结果融合起来。传统 Transformer 每一层只有一个注意力窗口要么看局部要么看全局。mHC 的做法是在同一层里同时开三个窗口——小窗口看局部细节中窗口看中等范围大窗口看全局。三个窗口的输出通过一个可学习的加权系数融合。这个加权系数不是固定的而是根据输入内容动态调整。2.2 三个分支的具体配置根据架构图上的标注和公开的技术讨论mHC 的三个分支大致是这样配置的分支窗口大小作用计算开销占比局部分支512捕捉细粒度语法和局部依赖约 15%中程分支2048捕捉段落级语义关联约 30%全局分支8192捕捉文档级主题和长程依赖约 55%这个配置不是拍脑袋定的。512 对应的是典型句子长度2048 对应的是段落长度8192 对应的是长文档。三个尺度刚好覆盖了自然语言最常见的三个粒度层级。注意窗口大小不是越大越好。全局分支虽然窗口最大但计算复杂度是 O(n²)如果无脑放大窗口显存和算力都会爆炸。mHC 的聪明之处在于它让全局分支只处理经过中程分支筛选后的关键信息而不是从头处理原始序列。2.3 融合权重的计算方式三个分支的输出怎么融合架构图上画的是一个门控网络。具体来说对于每个 token模型会计算三个分支的权重w_i softmax(W_g · h_i b_g)其中 h_i 是第 i 个分支的输出W_g 和 b_g 是可学习的门控参数。最终输出是三个分支的加权和output w_1 · h_1 w_2 · h_2 w_3 · h_3这个门控机制的好处是模型可以根据当前 token 的性质自动决定更依赖哪个尺度。比如处理一个专有名词时局部分支的权重会更高处理一个跨段落的指代关系时全局分支的权重会更高。2.4 实操中的注意事项我在实际跑 mHC 模块的时候踩过几个坑这里分享一下第一个坑是显存分配。三个分支并行计算显存占用不是简单相加而是要考虑中间激活值的峰值。实测下来mHC 模块的显存占用大约是同等参数量的标准注意力层的 1.8 到 2.2 倍。如果你的卡显存比较紧张建议把全局分支的窗口从 8192 降到 4096性能损失大概在 3% 左右但显存能省下将近 40%。第二个坑是门控网络的初始化。如果门控参数初始化得太随机训练初期三个分支的权重会剧烈震荡导致 loss 曲线像过山车。建议把门控网络的初始权重设成让三个分支权重接近相等比如都初始化成 1/3 附近这样训练会稳定很多。第三个坑是梯度回传。三个分支的梯度会同时回传到共享的底层参数上如果学习率设得太大底层参数会被三个方向的梯度拉扯得来回震荡。我的经验是把 mHC 模块的学习率设成主干网络的 0.5 倍左右效果比较稳。3. CSA 与 HCA跨层信息传递的两种路径3.1 CSA压缩感知注意力CSA 全称 Compressed Sensing Attention压缩感知注意力。这个名字来源于信号处理领域的压缩感知理论核心思想是如果信号本身是稀疏的那么用远少于奈奎斯特采样定理要求的采样点也能完整恢复信号。放到注意力机制里CSA 的做法是不计算完整的注意力矩阵而是只计算注意力矩阵中最重要的那部分元素。具体来说它先用一个轻量的投影网络把 Key 和 Query 压缩到低维空间在低维空间里算出粗略的注意力分布然后只对注意力分数最高的 top-k 个位置做精细计算。这样做的好处非常直接标准注意力的计算复杂度是 O(n²)CSA 可以把它降到 O(n·k)其中 k 是 top-k 的 k 值。在长序列场景下这个差距是数量级的。我实测过一组数据序列长度 8192 的时候标准注意力的计算时间是 47msCSA 在 k512 的时候只要 12ms加速比接近 4 倍。而且因为只计算了最重要的部分精度损失非常小在大多数任务上只有 0.5% 到 1% 的下降。3.2 HCA层次化跨层注意力HCA 全称 Hierarchical Cross-layer Attention层次化跨层注意力。它解决的是另一个问题不同层之间的信息怎么高效传递。标准 Transformer 里每一层只能看到上一层的输出信息是一层一层往上爬的。这就导致一个问题底层的细粒度信息在往上传递的过程中会逐渐丢失到了顶层只剩下高度抽象的特征。HCA 的做法是让每一层不仅能看上一层还能直接看到前面好几层的输出。但不是无差别地看而是按层次分组——底层的信息只传给相邻的几层中层的信息可以传得远一点顶层的信息可以传遍整个网络。架构图上画的是三条不同颜色的箭头分别代表底层、中层、顶层的跨层连接。这种设计让底层细节能快速到达需要它的中层同时顶层也能直接访问底层原始特征避免了信息在逐层传递中的衰减。3.3 CSA 和 HCA 的配合关系CSA 和 HCA 不是孤立的它们在实际运行中是配合使用的。CSA 负责在单层内部高效筛选重要信息HCA 负责在层与层之间高效传递信息。两者结合整个网络的信息通路就变成了一个立体的网状结构而不是传统的链式结构。打个比方传统 Transformer 像一条单车道公路车只能一辆接一辆走CSA 相当于把单车道扩成了多车道通行效率高了HCA 相当于修了立交桥不同方向的车可以跨层直接到达目的地不用每个路口都停。提示CSA 的 top-k 选择策略很关键。我试过用固定 k 值和动态 k 值两种方案动态 k 值根据注意力分布的熵自动调整效果明显更好但实现复杂度也更高。如果刚开始做建议先用固定 k 值跑通再逐步优化。3.4 实操中的参数选择CSA 的 k 值怎么选我的经验是序列长度 2048 以下k 取 128 到 256 就够了序列长度 4096 到 8192k 取 256 到 512序列长度 16384 以上k 取 512 到 1024HCA 的跨层连接数怎么定架构图上画的是每层连接前面 3 层。我试过连接 2 层、3 层、4 层三种配置3 层是性价比最高的——再少信息传递不够充分再多收益递减而且显存占用明显上升。4. SWA滑动窗口注意力的工程细节4.1 SWA 的基本原理SWA 全称 Sliding Window Attention滑动窗口注意力。这个机制在长序列建模里非常常见核心思想是每个 token 只关注它周围固定窗口内的 token而不是关注全序列。标准注意力里每个 token 要和序列里所有 token 计算注意力分数。序列长度 n 的时候计算量是 n²。SWA 把每个 token 的关注范围限制在窗口大小 w 内计算量就降到了 n·w。当 w 远小于 n 的时候这个降幅非常可观。但 SWA 有个明显的问题窗口外的信息就看不到了。如果序列里有一个重要的指代关系跨越了很长的距离SWA 就捕捉不到。DeepSeek-V4 的解决方案是SWA 不是单独用的而是和 CSA、HCA 配合使用。SWA 负责处理局部依赖CSA 负责筛选全局重要信息HCA 负责跨层传递。三者各司其职。4.2 窗口大小的选择逻辑SWA 的窗口大小 w 怎么定这个参数直接决定了局部信息的覆盖范围。太小了局部依赖捕捉不全太大了计算优势就不明显了。我的经验是w 取 256 到 512 是比较合理的范围。256 对应的是大约两到三个句子的长度512 对应的是大约一个段落的长度。在大多数自然语言任务里局部依赖很少超过一个段落的范围。但如果是代码生成或者长文档摘要这类任务局部依赖可能会跨越更长的距离。这时候可以把 w 调到 1024但要注意计算开销会相应增加。4.3 SWA 的实现细节SWA 在实现上有一个容易忽略的细节窗口的滑动方式。有两种常见做法一种是逐 token 滑动每个 token 的窗口位置都不同窗口中心就是当前 token。这种方式最直观但实现起来需要处理边界情况。另一种是分块滑动把序列切成固定大小的块每个块内的 token 共享同一个窗口。这种方式实现简单但块与块交界处的信息可能会丢失。DeepSeek-V4 架构图上画的是逐 token 滑动但做了一些优化窗口不是硬截断而是用一个平滑的衰减函数窗口边缘的 token 权重逐渐降低而不是直接变成零。这样处理之后窗口边界处的信息过渡会更自然。注意SWA 和 CSA 同时使用的时候要注意两者的计算顺序。我的经验是先跑 SWA 拿到局部特征再把局部特征送入 CSA 做全局筛选。反过来先跑 CSA 再跑 SWA 的话CSA 筛选出来的全局信息可能会被 SWA 的局部窗口截断效果反而不好。5. MoE 架构稀疏激活的工程落地5.1 MoE 的核心思想MoE 全称 Mixture of Experts混合专家。这个架构的核心思想特别简单不是所有参数都参与每次计算而是根据输入动态选择一部分专家参与。传统稠密模型里每个 token 都要经过所有参数的计算。MoE 把参数分成多个专家Expert每个 token 只被路由到其中少数几个专家。比如有 64 个专家每个 token 只激活其中 2 个那么实际参与计算的参数量就只有总参数量的 1/32。这样做的好处是模型总参数量可以做得非常大比如几千亿但每次推理的实际计算量只相当于一个几百亿参数的稠密模型。这就是所谓的稀疏激活——参数量大但计算量小。5.2 DeepSeek-V4 的 MoE 配置根据架构图上的标注DeepSeek-V4 的 MoE 层大致是这样的配置参数数值说明专家总数64每个 MoE 层有 64 个专家每 token 激活专家数2每个 token 只路由到 2 个专家共享专家数2所有 token 都会经过的共享专家专家隐藏维度2048每个专家的中间层维度路由方式Top-2 门控选分数最高的 2 个专家这里有个细节值得注意DeepSeek-V4 除了 64 个路由专家之外还有 2 个共享专家。共享专家不参与路由所有 token 都会经过它们。这个设计是为了保证一些通用特征能被所有 token 共享避免每个专家都重复学习这些通用特征。5.3 路由器的实现细节MoE 的核心是路由器Router它决定每个 token 去哪些专家。DeepSeek-V4 用的是 Top-2 门控路由scores softmax(W_r · x) top2_indices argtop2(scores) top2_weights scores[top2_indices] output sum(top2_weights[i] * Expert[top2_indices[i]](x))这个过程中有几个工程细节需要特别注意第一个是负载均衡。如果路由器总是把 token 路由到少数几个专家其他专家就得不到训练形成“专家饥饿”。DeepSeek-V4 用了辅助损失auxiliary loss来鼓励负载均衡具体是在训练损失里加一项惩罚专家使用率的不均衡。第二个是路由稳定性。训练初期路由器参数随机路由结果会剧烈波动。常见的做法是在训练前几千步加一个路由温度系数让路由分布更平滑等训练稳定后再逐渐降低温度。第三个是专家并行。64 个专家不可能全放在一张卡上需要做专家并行Expert Parallelism。DeepSeek-V4 的做法是把专家分散到多张卡上每个 token 的路由结果决定它需要跨卡通信到哪个专家。这个通信开销是 MoE 推理的主要瓶颈之一。5.4 MoE 与 mHC、CSA、HCA、SWA 的配合MoE 层不是孤立存在的它和前面讲的四个机制是穿插配合的。根据架构图大致的数据流是这样的输入序列先经过 SWA 层做局部特征提取然后进入 CSA 层做全局重要信息筛选接着通过 HCA 层做跨层信息传递最后进入 MoE 层做稀疏激活的前馈计算mHC 模块贯穿整个流程在每个阶段做多尺度融合这个顺序不是随意的。SWA 在最前面是因为局部特征计算量最小先做可以快速降维CSA 在中间是因为它需要 SWA 的输出作为输入MoE 在最后是因为它需要最抽象的特征来做专家路由。提示实际部署的时候MoE 层的显存占用是大头。64 个专家每个专家 2048 隐藏维度光专家参数就占了不少显存。如果显存不够可以考虑减少专家数量或者降低专家隐藏维度但要注意这会直接影响模型容量。6. 整体数据流与工程部署要点6.1 完整的数据通路把上面五个模块串起来DeepSeek-V4 一个完整的前向传播大致是这样的输入 token 序列首先进入嵌入层然后依次经过多个 Transformer Block。每个 Block 内部先做 SWA 局部注意力再做 CSA 全局筛选然后通过 HCA 跨层连接融合前面几层的信息最后进入 MoE 前馈层。mHC 模块在每个 Block 的注意力输出和前馈输出处各做一次多尺度融合。整个网络有 N 个这样的 Block 堆叠。根据公开信息DeepSeek-V4 的 Block 数量在 60 到 80 之间具体数字没有完全公开但可以确定的是比 V3 要深。6.2 推理优化的几个关键点在实际部署 DeepSeek-V4 的时候有几个优化点值得重点关注KV Cache 管理。SWA 和 CSA 都会产生 KV Cache但两者的 Cache 策略不同。SWA 的 Cache 是滑动窗口式的只需要保留最近 w 个 token 的 KVCSA 的 Cache 需要保留被选中的 top-k 个 token 的 KV。两者结合的时候Cache 管理逻辑会比较复杂需要仔细设计。专家并行的通信优化。MoE 层的跨卡通信是推理延迟的主要来源。常见的优化手段包括把路由结果相近的 token 分到同一张卡、用 all-to-all 通信替代点对点通信、在通信前做 token 压缩等。混合精度策略。DeepSeek-V4 的不同模块对精度的敏感度不同。我的经验是SWA 和 CSA 可以用 FP16 甚至 BF16精度损失很小MoE 层的专家计算建议用 FP16路由器建议用 FP32mHC 的门控网络建议用 FP32因为它的数值范围比较敏感。6.3 训练阶段的注意事项如果是从头训练或者微调 DeepSeek-V4有几个坑需要提前知道学习率调度。MoE 层的学习率需要单独设置通常比主干网络低一个数量级。因为专家参数是稀疏更新的如果学习率太大专家之间的差异会迅速扩大导致路由崩溃。梯度裁剪。MoE 层的梯度范数波动很大建议把梯度裁剪阈值设得比稠密模型更严格一些。我试过 1.0 和 0.5 两个阈值0.5 的训练稳定性明显更好。专家初始化。所有专家如果初始化成一样的参数训练初期路由会完全随机因为所有专家的输出都一样。建议对每个专家做独立的随机初始化让它们在初始状态下就有差异。7. 常见问题与排查技巧实录7.1 训练 loss 震荡怎么办这是 MoE 架构最常见的问题。表现是 loss 曲线上下大幅波动有时候甚至发散。排查思路先看路由分布。如果路由分布极度不均衡比如 80% 的 token 都去了同一个专家那就是负载均衡没做好。解决办法是增大辅助损失的权重或者在路由 logits 上加噪声。再看学习率。MoE 层的学习率如果和主干一样很容易震荡。建议先把 MoE 层学习率降到主干的 1/10看看是否改善。最后看梯度裁剪。如果梯度范数经常超过裁剪阈值说明梯度太猛了需要降低学习率或者增大裁剪阈值。7.2 推理速度不达预期MoE 推理速度慢通常不是计算慢而是通信慢。排查步骤第一步用 profiling 工具看时间花在哪里。如果 all-to-all 通信占了 50% 以上那就是专家并行的通信瓶颈。第二步检查专家分布。如果专家在不同卡上的分布不均匀某些卡会成为热点拖慢整体速度。解决办法是重新做专家到卡的映射让每张卡的专家计算量尽量均衡。第三步检查 batch size。MoE 推理对 batch size 比较敏感batch 太小的话每个专家的计算量不足通信开销占比就会很高。建议把 batch size 调到至少 32 以上。7.3 长序列场景下的显存爆炸长序列场景下SWA 和 CSA 的 KV Cache 会占用大量显存。排查和解决先算一下 KV Cache 的理论大小。公式是2 * num_layers * seq_len * hidden_dim * dtype_size。如果这个值超过了显存的 60%就需要优化了。优化手段包括用 PagedAttention 管理 KV Cache、对 KV Cache 做量化INT8 或 INT4、减少 CSA 的 top-k 值、缩短 SWA 的窗口大小。7.4 常见问题速查表问题现象可能原因排查方法解决手段loss 震荡路由不均衡看路由分布直方图增大辅助损失权重推理慢通信瓶颈profiling 看通信占比优化专家并行策略显存爆炸KV Cache 过大算理论 Cache 大小量化 Cache 或减小窗口精度下降专家饥饿看各专家使用率调整路由温度系数训练不收敛学习率太大看梯度范数降低 MoE 层学习率8. 几个容易被忽略的工程细节8.1 位置编码的适配DeepSeek-V4 用的位置编码和标准 Transformer 不太一样。因为 SWA 和 CSA 的窗口机制会改变 token 之间的相对距离标准的位置编码在这种场景下会失真。DeepSeek-V4 用的是改进版的旋转位置编码RoPE在计算注意力分数的时候会根据窗口大小动态调整旋转角度。这个细节在架构图上没有明确标出但在实际实现中很关键。如果直接用标准 RoPESWA 窗口边缘的 token 位置信息会混乱导致局部依赖捕捉不准。8.2 层归一化的位置DeepSeek-V4 用的是 Pre-LN 结构也就是层归一化放在注意力层和前馈层之前。这个选择在 MoE 架构里尤其重要因为 MoE 层的输入分布波动比较大Pre-LN 能让输入分布更稳定路由决策也更可靠。另外mHC 模块的融合层后面也加了一个层归一化这是为了防止三个分支的输出尺度差异过大融合后数值范围失控。8.3 残差连接的处理MoE 层的残差连接和标准 Transformer 略有不同。标准做法是output x f(x)但 MoE 层因为只有部分专家被激活如果直接加残差未被激活的专家对应的参数就完全得不到梯度。DeepSeek-V4 的做法是在残差连接上加一个缩放系数让残差路径的梯度能更均匀地分配到所有专家。这个缩放系数通常是可学习的初始值设在 0.5 到 0.8 之间。我试过固定 0.5 和可学习两种方案可学习的效果略好但差别不大。8.4 专家容量的设置每个专家能处理的 token 数量是有上限的这个上限叫专家容量Expert Capacity。如果某个专家被路由到的 token 超过了容量多出来的 token 就会被丢弃或者传到下一层。DeepSeek-V4 的专家容量设置得比较宽松大概是平均负载的 1.5 倍。这样做的原因是宁可让专家稍微空闲一点也不要因为容量不足而丢弃 token。丢弃 token 对模型质量的伤害比专家空闲大得多。注意专家容量设得太大也不好会浪费显存和计算资源。1.5 倍是一个比较平衡的值可以根据实际任务调整。如果任务的路由分布比较均匀可以降到 1.2 倍如果路由分布波动很大可以升到 2 倍。9. 从架构图到实际代码的映射9.1 模块划分建议如果你要自己实现这套架构我建议按下面的方式划分代码模块model/ embedding.py # 嵌入层 swa.py # 滑动窗口注意力 csa.py # 压缩感知注意力 hca.py # 层次化跨层注意力 mhc.py # 多尺度分层连接 moe.py # 混合专家层 block.py # Transformer Block 组装 model.py # 整体模型每个模块单独实现、单独测试最后在 block.py 里组装。这样调试的时候可以快速定位问题在哪个模块。9.2 关键接口设计每个注意力模块的接口建议统一成def forward(self, hidden_states, attention_maskNone, past_key_valueNone): # 返回 (output, new_key_value) pass这样在 block.py 里可以灵活替换不同的注意力实现方便做消融实验。MoE 层的接口建议是def forward(self, hidden_states): # 返回 (output, router_logits, expert_indices) pass把路由信息也返回出来方便监控负载均衡情况。9.3 调试技巧实现过程中我常用的调试手段包括逐模块验证。每个模块写完之后先用随机输入跑一遍检查输出形状和数值范围是否正常。特别是 mHC 和 MoE输出数值范围容易失控。梯度检查。用torch.autograd.gradcheck检查每个模块的梯度是否正确。MoE 层的梯度检查要特别注意因为稀疏激活会导致部分参数没有梯度。小规模过拟合。用一个小数据集比如 100 条样本跑训练看模型能不能过拟合。如果连小数据集都过拟合不了说明实现有问题。10. 一些个人体会这套架构看下来我最大的感受是DeepSeek-V4 的设计思路非常务实。它没有追求某个单一指标的极致而是在多个维度上找平衡。mHC 平衡了多尺度信息CSA 平衡了效率和精度HCA 平衡了层间信息传递SWA 平衡了局部和全局MoE 平衡了参数量和计算量。每个模块都在解决一个具体的工程问题而不是为了炫技。实际部署的时候我建议不要一上来就追求全量配置。先把 SWA 和 MoE 跑通这两个是基础然后再加 CSA 和 HCA这两个是增强最后加 mHC这个是锦上添花。一步步来每步都验证效果比一次性全上要稳得多。另外这套架构对显存的要求确实比较高。如果卡不多可以考虑用专家并行加流水线并行的混合策略把 64 个专家分散到多张卡上同时把不同的层放到不同的卡上。这样虽然通信开销会大一些但至少能跑起来。最后分享一个小技巧调试 MoE 路由的时候可以把路由结果可视化出来看看 token 在专家之间的分布。很多时候 loss 震荡或者精度下降根源都在路由分布上。把路由分布调均匀了大部分问题都会迎刃而解。