1. What the problem to solve?
春节回去的时候正好碰上DeepSeek发布新的模型,一时间各路媒体讨论的沸沸扬扬,几乎上升到国运的高度。讨论的最重要的应该是两点:第一个是媲美其他主流模型的benchmark分数;第二个是说训练速度快了很多,使用了少的多的GPU时间。可是各路讨论多是繁华之论以及振奋人心的消息,而没有具体的DS为什么好,怎么好的。回来的第一个周末,花了周末的时间读了一下DS V3的论文。之所以选择V3因为R1是之于V3进行了RL和SFT来增强模型的推理能力,基本的模型还是V3,而且DS V3的论文也更详细。文章的第二部分讲述了DS在模型的架构上(science)有一些什么样的改进,第三部分讲述了在模型的训练上(engineering)有什么改进,然后两部分讲了pretraining和posttraining以及evaluation相关的东西。
2. How to solve / Model architecture?
2.1. Basic Architecture
模型的基本架构里面主要有两点:1是把原来的Attention改成了Multi-head Latent Attention;2是把原来的FFN改成了DeepSeekMoE。这样做的目的一是减少了计算量;二是减少了cache的data,从而节约了GPU的内存。具体如下:
DeekSeek模型架构
2.1.1. Multi-head latent attention (MLA)
相比较于原始的Attention模型里面介绍的注意力机制, DS使用Multi-head latent attention (MLA)的架构,跟原来的Multi-head attention不一样的是这里面从
这里
对Query,也有同样的处理来降维
2.1.2. DeepSeekMoE with Auxiliary-Loss-Free Load Balancing
Basic Architecture of DeepSeekMoE
在2017年的Attention论文里面,decoder先是计算Attention,然后再计算FFN从而predict最后的token。DS里面,FFN被进一步改成了MoE。MoE的意思是模型是由一些列的Expert构成。每个Expert可以是一个MLP,一个CNN或者其他的神经网络模型,或者每个Expert可以是一个tansformer的encoder/decoder。每个 Expert 负责学习不同的数据模式,MoE 通过 Gating Network 选择合适的Expert,使得不同的数据被送往最擅长Expert家处理。每预测下一个token的时候,MoE不是使用某一个Expert,也不是使用所有的Expert,而是选择其中的Top
MoE的架构见下图
在DS的FFN里面,DeepSeekMoE在MOE的基础上更进一步:1)他们把Expert进一步细化,然后既有一些Expert用来做routing,具体地说,对routingd的Expert,预测只是route到这些Expert的某一些上面。同时,他们还有一些Expert是共享Expert,也就是说,在预测的时候,他们每次都是被使用。2)DS优化了Expert的选择,使用affinity score来选择专家。3)DS的Expert更finer,同时也更稀疏。这样进一步降低了运算成本。
DeepSeekMoE的架构见下图
假设每个Expert
然后算出这
最后得到新的output为
Auxiliary-Loss-Free Load Balancing in DeepSeek-V3
实际运行的时候,可能会出现这种情况:模型的token大部分都是有某几个Expert来predict,剩下来的Expert使用率会比较少,导致Expert负载不均衡。以前的MOE使用auxiliary loss来让Expert负载均衡。通过惩罚imbalanced Expert utilization (加上更多的loss)来来达到均衡负载的目的。但是这样做有两个问题:1)Too strong auxiliary loss hurts model performance by interfering with learning objectives. 2)Difficult to tune: Balancing between efficiency and accuracy requires careful hyperparameter tuning.
DS提出了Auxiliary-Loss-Free Load Balancing来达到balanced routing of tokens to different Experts.具体的说实在gating function里面添加一个bias的项,通过bias的值来平衡Expert的调用。
具体的说,在计算权重
Complementary Sequence-Wise Auxiliary Loss
DeepSeek-V3 主要依赖 Auxiliary-Loss-Free 负载均衡策略来确保 Experts 间的负载均衡。然而,为了防止单个序列内部的极端不平衡,DeepSeek-V3 还引入了 Complementary Sequence-Wise Auxiliary Loss. 通常MoE模型的负载均衡都在batch level进行,但是对一个batch的多个sequence,某个sequence 内部某些Expert可能用的很多,而其他Expert用的很少。DS的想法是要在每个sequence level,也尽可能的让Expert utilization均衡,负载不会过于集中或稀疏。同时该损失的强度非常低,以避免对模型性能产生负面影响。
下面我们主要来理解原文中的公式,为什么这个公式就能够均衡 Sequence level的Expert 负载。
最后,损失函数公式里的
2.2. Multi-Token Prediction (MTP)
DS还使用了MTP,而不是像一般的pre-training那样仅仅只预测 next token(2017的Attention文章就是只predict next token)。这有两个好处:1. 同时预测多个token可以提高data的使用效率。2. 让模型在准备representation的时候能够考虑的更长远。跟原始的MTP文章不同的是,DS不是同时predict
MTP from Gloeckle et al. (2024), which is
DS的MTP模块结构用一句话来简单概括就是:DS的MTP使用
DeepSeek V3 MTP: Use D MTP module and
prediction:对于输入sequence的第
我的理解:
1) 传统的next token prediction:
2) 在DS的
在
在
在
MTP 的目标函数 对每个predict depth k,其损失函数仍然是cross-entropy,定义为
最终 MTP 的损失函数是对所有的预测深度 (
从DS的paper公式25看,
MTP 在推理的时候怎么用呢? MTP的主要目的是用来提高 main model的表现,在推理的时候,并不要使用MTP,而只是使用 main model来predict next token。
3. InfraStructures / 训练工程优化
3.1. Compure clusters
DS-V3 是在2048块H800上训练的,每个cluster有8块H800,nodes内通过NVLink和NVSwitch相连,nodes间通过InfiniBand来通信。
3.2. 训练框架
正常使用GPU进行训练的时候,有两个非常大的挑战:显存效率和计算效率,也就是显卡的显存和计算能力被使用了多少,有多少是浪费了。因为现在的模型太大,一张显卡或者一个cluster都放不下,所以通常需要把模型分散到不同的机器和显卡上。显卡,cluster组建集群参见kubernets的知识。为了把模型分散到不同的显卡上,通常有这么几个办法:1. Pipeline Parallelism / PP:这是对模型进行分割,对比较深的模型,模型的pipeline可以分到不同的显卡上。比如一个模型有32个transfomer,有八个显卡,那么可以每个显卡放4个,然后依次计算,这样可以使用micro-batch来充分的利用GPU。通常的PP会导致GPU闲置(见下图PP bubbles) 2. Tensor Parallelism / TP:这是对数据进行分割,对比较大的矩阵运算,可以分散到不同的显卡运算,这样既可以防止一张显卡容不下巨大的数据,也可以并行进行,加快速度。这些名字有时候不是很准确,其他还有Data Parallism, model parallelism. DS-V3因为有很多的Expert,所以他们还有 Expert Parallelism / EP。
PP bubbles:
setup | scenario | strategy |
---|---|---|
single node/multi-GPU | fits on single GPU | DistributedDataParallel or ZeRO |
doesn’t fit on single GPU | PipelineParallel, ZeRO or TensorParallel | |
largest model layer doesn’t fit | TensorParallel or ZeRO | |
multi-node/multi-GPU | fast inter-node connectivity (NVLink or NVSwitch) | ZeRO or 3D parallelism (PipelineParallel, TensorParallel, DataParallel) |
slow inter-node connectivity | ZeRO or 3D parallelism (PipelineParallel, TensorParallel, DataParallel) |
DS-V3是基于HAI-LLM训练框架。DS-V3通过16路PP,64路EP分布在8个节点上,以及ZeRO-1 DP.
DS主要做了这三个工程优化:1. 设计了DualPile的办法来优化PP。DualPipe有更少的 pipeline bubbles(指GPU闲置),它将Forward和Backword过程的计算和通信阶段重叠起来,从而解决了跨节点专家并行性带来的沉重通信开销的挑战。2. 其次,开发了高效的 cross-node all-to-all 通信内核,充分利用 IB 和 NVLink 带宽,节省通信专用的 Streaming Multiprocessors (SMs)。最后,优化了训练过程中的内存占用,从而能够在不使用昂贵的 TP 的情况下训练 DeepSeek-V3。
3.2.1 DualPipe
在深度学习的大模型训练中,计算(Computation) 和 通信(Communication) 是两个关键环节。1. Computation: 主要指前向Forward 和 BackPropagation computation;2. Communication: 包括不同 GPU 或计算节点间的数据传输,如参数更新、梯度交换等。跨节点 Expert Parallelism 引入的通信开销导致计算与通信比率低效。DualPipe是一个双Pipeline的架构,在Pipeline的两端同时输入 Micro-Batches 的数据,这样 Forward 和 Backward 计算可以同步进行,减少 GPU 空闲时间。同时采用Computation与Communication重叠的策略,使得Forward 和 BackPropagation 的计算任务以及数据通信能够在时间轴上更紧密地交错。通过把Forward和Backward chunk成小块,然后调整GPU SMs的使用比例,一部分用于Computation,另一部分用于Communication。
DualPipe with less bubble
class DualPipeModel(nn.Module):
def __init__(self, dim, rank, world_size):
...
def forward_backward_pipeline(self, input_tensor):
# 创建两个 CUDA 流,一个用于计算,一个用于通信
compute_stream = torch.cuda.Stream()
comm_stream = torch.cuda.Stream()
# Forward Pass
with torch.cuda.stream(compute_stream):
attn_out = self.attention(input_tensor)
mlp_out = self.mlp(attn_out)
# Backward Pass
with torch.cuda.stream(compute_stream):
loss = mlp_out.sum()
loss.backward()
# All-to-All Dispatch & Combine
with torch.cuda.stream(comm_stream):
tensor_to_send = input_tensor.clone()
received_tensor = torch.zeros_like(input_tensor)
# All-to-All
dist.all_to_all_single(received_tensor, tensor_to_send)
# synchronize
compute_stream.synchronize()
comm_stream.synchronize()
return received_tensor
3.2.2. Cross-Node All-to-All Communication
这一部分牵涉到更底层的工程细节,包括传说中的通过Nvidia的底层语言 PTX (Parallel Thread Execution) 来控制GPU的线程执行,减少通信对计算的影响,实现更高效的GPU利用,以及怎么充分利用集群的网络拓扑结构(IB & NVLink)。DS-V3 的原始paper里面也更多的是high-profile的描述而没有太多的细节。总体的思路是因为节点间IB的带宽比节点内NVLink的带宽要低,所以要想办法来合理分配通信路径,减少IB带宽的阻碍。
为了对 Cross-Node All-to-All Communication(包括 Dispatching 和 Combining)进行优化,DS设计了优化的网络拓扑结构(IB & NVLink)。在 dispatching 的时候,MOE gating algorithm 决定每个token 会被发送到哪些 Expert,每个 token 先通过 IB 发送到目标 node,然后再通过 IB-to-NVLink 将数据发送到最终的GPU。前面在模型架构已经提到,每个 token 最多发送给4个Node(NVLink的带宽为160GB/s,IB为50GB/s,大概是3.2倍,所以到了Node,token还能平均有3.2个Expert)。在 Combining 的时候反过来,每个 Expert 通过 NVLink 汇总数据的结果,然后通过 NVLink-to-IB 到 node汇总,IB 的接受和汇总同样由动态调整的warp处理。
3.2.3. Minimal Overhead
这一部分讲的怎么节省GPU内存的开支。第一个是在back propagaton的时候从新计算RMSNorm和升维投影,这样就不需要储存他们的值。第二个是咋Exponential Moving Average (EMA) 计算模型表现的时候,EMA参数放到CPU内存。第三个是在multi-token预测的时候,共享Embedding和Output Head的参数。
3.3. FP8 Training
首先加一个FP的知识,计算机用二进制表示浮点数,是分为正负号(Sign)、指数部分(exponent)、尾数部分(mantissa)三部分的,完整的表示为
Fig. NVIDIA FP8 Tensor Core
3.3.1. Mixed Precision Framework
DS-V3 使用混合精度的FP8,大部分需要很多计算的地方都是FP8,但是在少量重要的操作上保留了原来的精度,具体见下图。1. 在GEMM矩阵相乘的时候,输入是FP8输出是BF16或者FP32. 在下图,在forward pass,activation backward pass和weight backward pass这几步矩阵相乘都是FP8的输入精度,理论上这样跟BF16比可以快一倍。在需要高精度的地方,比如embedding,output head,MOE gating module,normalizaiton,以及attention等等,仍然保持高精度。
Mixed Precision
3.3.2. Improved Precision from Quantization and Multiplication
Fine-Grained Quantization 主要是为了解决低精度训练中 overflow 和 underflow 的问题。这些问题主要来源于 FP8 格式受限的动态范围,特别是 FP8 指数位减少导致的表示范围受限。为了解决这个问题,Fine-Grained Quantization 采用了一种更精细的量化方法,在更细粒度上进行 scaling:对activations,采用
3.4. Inference and Deployment
Stay tuned!