专栏名称: 吃果冻不吐果冻皮
专注于AI工程化(LLM、MLOps、LLMOps、RAG、Agent)落地。
目录
相关文章推荐
51好读  ›  专栏  ›  吃果冻不吐果冻皮

Deepseek V3 预训练策略解读

吃果冻不吐果冻皮  · 公众号  ·  · 2025-01-02 21:01

正文

原文:https://zhuanlan.zhihu.com/p/15073492309

训练策略

集群

2048*H800,256 nodes,配备NVLink,NVSwitch,以及IB。

策略推测

TP
PP
DP
SP
CP
EP
1
16
128
1
1
64

策略评价

1.选择2048张卡进行训练,应该可以保证在一个大集群中进行这个训练。

2.策略中不开TP,机器内部优先为EP组,256个专家的64EP并行,则单张卡中应该是4个专家。

3.Deepseek论文中提到不使用TP策略,主要是开销非常大(文中costly),这似乎也表明EP和TP组在机内的优先级竞争,EP是最优解?因为在之前Mixtral 8x7B的Moe模型中,同样也是选择了将EP打满的策略。 12.27更新:xffxff:MoE 训练到底是开 TP 还是 EP?,解释了这个原因 [1]

4.提到使用了ZeRO-1(DP),但我估计实现的方法应该和Magetron的Distributed Optimizer优化一样。

DS分布式训练亮点分析

1.自研的轻量级HAI-LLM框架

2.双流并行的PP组steady阶段优化

3.PP组双向管道调度

4.Moe路由的All2All优化设计

5.高精度的显存优化策略

双流并行的PP组steady阶段优化

计算通信流安排

文中示意图:

首先一个预备知识,在反向传递阶段,通常会进行两个操作,1.更新当前层的权重,2.将梯度继续传递到前一层。而通常这两个操作实际是并不存在数据依赖的,因此他们是可以拆开来进行。图中括号里面的B,W则分别是这两个操作的。

这一个思路实际在ZeroBubble中就已经出现:

Deepseek的这个设计中,有一个比较有意思的点是,在他自定义的设计后,可以使得barrier刚好停在两个流任务完成的时候,而不需要通信流或者是计算流进行等待。能有效的提高计算效率。

我们知道正常的执行逻辑上,按 顺序 来说

前向传递需要执行:ATTN(计算),DISPATCH(通信),MLP(计算),COMBINE(通信)。

反向传递需要执行:COMBINE(通信),MLP_B(计算),MLP_W(计算),DISPATCH(通信),ATTN_B(计算),ATTN_W(计算),这里注意,由于MLP_B在将梯度计算之后,就可以继续向下一层进行传递了,因此我们可以在后续执行MLP_W时,同时通信梯度计算的结果。

而在之前,虽然也存在着overlap的策略,但显然粒度上没有这么细致。具体来说可以参照下面的图,模拟了一个简单的流水,简单来说,就是计算的前向与反向由于不存在数据依赖,所以在计算完成后可以一边发送与接收数据一边进行反向的计算。但都是以一个PP层的粒度进行。没有DeepSeek文章中设计的细致。

PP组双向管道调度

论文中的图例,注释中也解释由于对称处理,所以从下往上的另外组并没有写出。

实际上应该是

图中的不同颜色标识,也和上文中介绍的一样,绿色长块表明是反向传递的B+W,绿色小块和蓝色小块则分别是B,W,混合块则是前文列出的混合计算模型。这里有一个潜在的点是,我们可以基本认为,前向的橙色块,绿色小块,蓝色小块的长度一致,因为他们的计算与通信逻辑是相同的。这也是我们常识中,反向传递的时间是前向传递时间2倍的主要原因,图中绿色长块的长度为2。混合块,则是因为包含了前向,反向两个阶段,所以块长度是3。

图中白色的部分则是PP流水线中常见的气泡(Bubble),我们是期望气泡越小越好。DeepSeek的这版PP设计,很明显就是努力的将中间部分的Bubble进行压缩。

相比于,传统版本的 错位1F1B 的PP训练

以及他参照的ZeroBubble的流水线图

前者是通过将PP的每一层错位展开,例如在PP=4的情况下,卡0包含0层与4层,交错训练,更细粒度训练来压缩气泡。而ZeroBubble则是将反向传递拆解为两个部分来进行训练。但ZeroBubble的流水线有一个明显的问题,就是优化器之后不能同步,导致实际的训练是一个异步流程。如果强制同步再进行下一个step,那还是会带来更多的Bubble,让效率变低。这也是一直没有被广泛使用的原因。

PP部分数学计算

这里我们探讨一下关于文中列出的数学表格进行分析:

首先是关于气泡的计算,对于DualPipe的设计来说,主要的气泡产生是两个部分,首先是warmup阶段与cooldown阶段,不可避免的一部分,毕竟中间的Rank要等两头的数据。另一部分就是混合模块与其他模块拼接产生的气泡部分。在我看来,如何做完美流水线,最好理解的方法就是填格子。对于DualPipe,有长度1,2,3的3种不同大小的方块格可以供选择,那么在满足数据依赖成立的情况下,自由度是非常高的。

其次是参数部分,由于是双向管道训练,所以你得存两份参数来进行训练,这比较好理解。但参数的规模文中说通过Zero1的DP进行卸载,开销不高。我们分析一下,8个routed专家的时候是37B,256个routed专家的时候是671B,所以4个routed专家的时候应该是26.8B(感谢评论区指正)。然后又因为PP=16,那在这种情况下,参数部分的总显存占用是26.8*2/16 = 3.35GB(如果是半精度),FP8的话则是1.675GB。因此额外的参数部分开销,用空间来换时间,真是洒洒水。

最后是激活层部分,我们也知道实际PP的优化是不影响激活层部分的显存占用的,PP流水的显存瓶颈一般在Rank_0和Rank_{pp-1}。因为流水虽然被分散为多段了,但是你会同时存储多个batchsize的中间结果,因此激活层部分的总量可以直观理解为不变。而DualPipe为什么会是PP+1,主要是因为另外一端的数据到了。参考下图,batch0的数据直接插入其中,导致相比之前的PP流水,额外多了1/PP(黄色方块部分),不过无伤大雅吧,这么一层PP的占用,大概也就是增加了1/16的额外空间。

Moe路由的All2All优化设计

Moe的通信,主要是两个部分,一个是将Token路由到对应的专家上(dispatch),另外一个则是将训练完的数据进行收集(combine)。

而实际上Moe的路由方案,一直以来就有两种,一种是从Mixtral就包含的All2All的方式,另外一种就是在Magetron中,直接使用All Gather与 Reduce Scatter的通信方案[2]。前者则是点对点通信显存开销比较小,一般传输多少数据就开多少的Buffer就行。但后者,则是需要将所有的Token全部路由到每一张卡上,然后再通过mask的方式来进行筛除,最后进行训练。这个过程中,所开辟的Buffer大小就会远大于前者。当时实习的组里,和mt也讨论过,前者虽然节省显存,但是通信的效率实际不高。而后者虽然开的Buffer较大,但是直接使用Magetron中的AG和RS(底层是NCCL),通讯的效率也是可以接受的。

而在DeepSeek的这版实现里面,很明显是对All2All的方法进行了细致的优化。

1.限制路由的节点范围最多是4个节点(最多在32张卡,128个专家)中进行选择

这一点实际很重要,它规避了在节点过多的情况下,Token随意路由导致通信大量拥塞的问题,虽然使用Group GEMM进行计算的话,通常size的情况应该不用过于担心。感觉这就是算法组和Infra组协商的结果了,要是算法组卡死啥都不愿意改,只能说Infra也很难做比较好的优化。

2.路由流程规范化

文章中定义的路由顺序,是机器间通过IB(50GB/s)来进行通信,而机器内部则走高带宽的NVLink(160GB/s)到达指定的机器。每当机器收到需要路由的Token数据时,都会优先及时处理以防止拥塞。无论在发送和回收时都遵循这项原则。

能做到这一点,首先是从网络拓扑的角度,IB在对应ID相同的显卡之间,只包含非常少量的交换机(感谢前mt的答疑)。因此选择直连的IB进行通信是最快的方案。其次文中也分析,由于两者之间的带宽差距是3.2倍,所以对于单个node如果只选择小于3.2个的专家来进行训练,那么对于NVLink来说,不会产生额外的开销。因此文中也提出,最多可以选择大约13个专家(4node*3.2E/node),4是前面限制选择最多4个节点。







请到「今天看啥」查看全文