关于DeepSeek我是怎么研究的(4)
经历了3篇长文的先验知识的准备:
- 关于DeepSeek我是怎么研究的(1):通识RLM推理模型;
- 关于DeepSeek我是怎么研究的(2):了解推理模型背后的模仿人类的背景知识,system 1 & system 2 thinking;
- 关于DeepSeek我是怎么研究的(3):RLM推理机制的详细拆解;
终于来到正主DeepSeek了。
DeepSeek能够引起轰动甚至能把OpenAI等领跑者吓出一身冷汗,除了它的模型性能、成本优势之外,我个人更多的觉得是它的毫无保留和开源贡献,这让绝大多数没有足够资金支持的学术机构、中小企业看到了自己拥有自己大模型的可能性且有非常高的性价比。同时DeepSeek的论文事无巨细,毫无保留一步一步的介绍了自己的如何训练DeepSeek的,这样的开源精神远远超越了OpenAI论文中暧昧与含糊其辞。
2024年春节期间引起全员关注的DeepSeek主要有两个模型:DeepSeek-V3以及DeepSeek-R1,这两个模型均有对应的论文、测试数据、开源模型等原始资料可以支撑。这篇文章我先来研究一下DeepSeek-V3模型:
DeepSeek-V3 架构
DeepSeek-V3 基于 Transformer 框架,采用 MLA 和 DeepSeekMoE 架构。MLA 通过低秩联合压缩注意力键和值,减少推理过程中的 KV 缓存。DeepSeekMoE 使用更细粒度的专家和隔离的共享专家,以提高模型性能。此外,DeepSeek-V3 引入了无辅助损失的负载平衡策略,通过动态调整每个专家的偏差项来实现负载平衡,避免了辅助损失对模型性能的负面影响。多令牌预测(MTP)训练目标则通过预测多个未来 token 来增强模型性能。
DeepSeek MoE
图中展示了 DeepSeek 从传统 MoE 模型架构1的基础上,做的两部分改进2与3:
- 传统 MoE 模块: MoE 模块包含 \( N \) 个前馈神经网络(Feed-Forward Network, FFN)专家,每个专家在处理特定类型的数据上具有独特的优势。MoE 模块通过路由机制,根据输入数据的特征动态选择最合适的\( K \)个专家进行处理,而不是激活所有专家。所有专家的参数总和构成了整个 MoE 模块的参数量,在前向计算过程中,由于只激活了部分专家,实际参与计算的参数量被称为激活参数量。例如,Mixtral 8x7B 模型包含8个专家,每次选择其中的2个专家进行计算,模型的总参数量为46.7B,而激活参数量为12.9B。
- 细粒度专家划分: 不同于传统 MoE ,DeepSeek 把 \( N \) 个专家做更细粒度的划分,降低每一个专家的参数量,增大专家数量。图中b将 \( N \) 个专家拆分为 \( mN \) 个,每一个专家的隐层维度变为原来的\( 1/m \),相应地激活\( mK \)个专家。如此 MoE 模块的参数量以及激活参数量均保持不变,同时还可以更加灵活地组合多个专家。
- 共享专家分离: 把激活专家区分为共享专家(Shared Experts)和路由专家(Routed Experts)时,图中c所示,共享专家和路由专家在数据处理流程上有显著的区别。对于共享专家,输入数据无需经过路由模块的计算,所有数据都会直接通过共享专家进行处理。相反,对于路由专家,输入数据会先经过路由模块,该模块根据输入数据的特征选择最合适的专家进行计算。在这种架构中,路由模块通过计算输入数据与各个专家的匹配概率,选择概率最高的专家进行处理。最终,将路由专家和共享专家的计算结果相加,形成 MoE 模块的最终输出。通过这种方式,模型能够在处理不同输入数据时,既能捕捉到输入数据的共性,也能关注到输入数据的差异性。这种设计能够提高模型的泛化能力和适应性。
更进一步地,DeepSeek-V3 针对 MoE 中常见的负载不均衡问题,提出了一种新的负载均衡策略。在用于选择专家的 Gate 模块中引入了一个可学习的偏置项。在计算路由得分时,这个偏置项会被动态地加到每个路由专家的得分上。该方式的主要特点在于:
- 动态调整路由倾向: 通过学习偏置项,模型可以动态地调整对不同路由专家的偏好。如果某个专家的负载过重,其对应的偏置项可能会被学习为负值,从而降低其被选择的概率。反之,对于负载较轻的专家,其偏置项可能会被学习为正值,提高其被选择的概率。
- 无额外损耗: 该偏置项是直接通过模型的训练目标进行优化的,而不是通过一个独立的负载均衡损失函数。这意味着,模型在努力提高主要任务性能的同时,也会自然而然地学习到一种更均衡的路由策略,而不会因为额外的负载均衡损失而影响性能。
DeepSeek MLA
在标准的 Transformer 模型中,多头注意力(Multi-Head Attention, MHA)机制通过并行计算多个注意力头来捕捉输入序列中的不同特征。每个注意力头都有自己的查询(Query, Q)、键(Key, K)和值(Value, V)矩阵。对于序列中的每一个 token,都需要计算各自的 QKV,进而计算注意力。在推理过程中,当前大模型所采用的 token by token 递归生成方式,上文 token 的 KV 计算不会受到后续生成 token 的影响,因此可以缓存下来,避免重复计算,提高推理效率,这就是 KV cache 的由来。也就是说,当生成第\( t+1 \)个 token 时,可以利用之前事先算好的上文\( t \)个 token 的 KV 值。同样地,\( t+1 \)位置 token 的 KV 值计算出来后也将保存在 KV cache 中。
目前大模型对于注意力机制做的一些改进,包括MQA (Multi-Query Attention)、GQA (Group Query Attention)都是为了想方设法减少 KV Cache。DeepSeek 提出的 MLA 的出发点也是如此。减少KV Cache就可以实现在更少的设备上推理更长的Context,或者在相同的Context长度下让推理的batch size更大,从而实现更快的推理速度或者更大的吞吐总量。最终目的都是为了实现更低的推理成本。
如上图所示,MQA 与 GQA 的办法是通过共享 K,V 的注意力头,降低 KV Cache的数据维度。MLA 的办法本质上是对原本 MHA 的 KV Cache 作低秩分解,得到一个低维的隐向量(Latent Vector)。在推理阶段,MLA 只需要缓存该隐向量,由此大大降低需要缓存的数据量。
具体地,对于某一层某一个 token 的表征 \( h_{t} \), MLA 通过降维映射矩阵(down-projection matrix)\( {W}^{DKV} \)得到对 \( K \) 、\( V \) 压缩后的隐向量:
\( {c}^{KV}_{t}={W}^{DKV}{h}_{t} \)
在前向过程中,需要对此隐向量进行升维还原,即:
\( {k}^{C}_{t}={W} ^{UK}{c} ^{KV}_{t} ; {v} ^{C}_{t}={W} ^{UV}{c} ^{KV}_{t} \)
其中,\( {W}^{UK} \) 与 \( {W}^{UV} \) 为对应的升维映射矩阵(up-projection matrix),可得到\( h_{t} \)对应的 \( K \) 、\( V \)向量 \( {k}^{C}_{t} \)、\( {v}^{C}_{t} \)。可以这么理解,MLA利用低秩分解的办法,通过增加少量的模型参数变量(降维映射矩阵与升维映射矩阵),引入低维隐向量 \( {k}^{C}_{t} \)作为 KV Cache,降低 KV Cache 所带来的显存压力,以此降低推理成本。
此外,DeepSeek 还将 Query 也进行了低秩分解,能够在训练过程中降低显存占用,以此提高训练资源利用率。
上述方案还需要针对位置编码RoPE(Rotary Position Embedding)进行处理。因为如果在隐向量\( h_{t} \)中包含RoPE,经过升降维操作后,会对位置信息造成破坏。为了解决这个问题,MLA(Multi-Head Linear Attention)提出了“解耦RoPE”的方法。具体来说,对于隐向量\( {c}^{KV}_{t} \),不将位置编码包含在其中,而是专门为注意力头的 Query 和 Key 新增向量维度,以添加 RoPE 的位置信息。
DeepSeek MTP
当前主流的采用自回归的大模型都是单 token 预测。即根据当前上文预测下一个最可能的 token。而 MTP 的核心思想是让模型一次性预测多个 token,以提升了模型的训练效率、生成质量和推理速度。
比如现在上文是“今年春节的天气”,传统的单 token 预测模式会逐 token 预测“真的”、“好”、“冷”、“。”;而 MTP 会并行地预测这几个 token 。因此,模型不仅要学习预测下一个 token 的能力,还需要同时具备预测下\( n \)个token的能力。
这种方式在推理角度的好处显而易见,一次性生成多个 tokens,减少自回归生成的步数,达到推理加速效果。而在训练过程中,MTP 的训练目标函数同时考虑了多个 token 的估计准确性,因此被认为可以捕捉 token 间的依赖关系,从而提升模型效果。
这里将简单介绍 DeepSeek V3 的 MTP 模块采用了级联式的结构,使用k个串行模块来预测k个token,保留因果语言模型(Causal Language Model)的连接关系,有利于效果的提升,如下图所示:
图中展示了模型用\( D \)个顺序的模块,预测\( D \)个 tokens。定义预测深度为\( k \)的含义为,预测后续第\( k \)个 token。
首先,在每一个预测深度模块中,都共享了一个嵌入层(embedding layer),即模型除了最后负责最后预测部分的层之外,其他的所有层,用于得到 token 的 embedding。对于输入的第\( i \)个token,在第\( k \)个预测深度处,模型将前一个深度的表示与第\( i+k-1 \) 的 embedding 拼接,作为新的输入表示。
然后通过一个预测深度\( k \)专用的 Transformer 模块,生成当前预测深度的输出表示,将用于下一个深度的表示计算,同时用共享的输出层获得 token 预测, 与训练样本中\( i+k \)计算损失。
值得注意的是 DeepSeek V3 论文中根据使用 MTP 模块的实验结果,他们在推理过程中不使用 MTP 模块,只在训练过程中利用该模块约束模型的优化。
基础设施
DeepSeek-V3 在由 2048 个 NVIDIA H800 GPU 组成的集群上进行训练,采用 16-way Pipeline Parallelism (PP)、64-way Expert Parallelism (EP) 和 ZeRO-1 Data Parallelism (DP)。为了提高训练效率,设计了 DualPipe 算法,通过计算和通信的重叠来减少管道气泡。此外,开发了高效的跨节点 All-to-All 通信内核,充分利用了 InfiniBand (IB) 和 NVLink 带宽。
DeepSeek 模型训练
大模型训练大体可以分为3种模式,预训练(Pretraining),有监督精调(Supervised Fine-Tuning, SFT),基于人类反馈的强化学习(Reinforcement Learning from Human Feedback, RLHF)。SFT让模型通过学习训练数据数据分布的方式来提高模型在特定任务或指令上的表现,与其不同的是,RLHF使用人类反馈来定义奖励函数,然后通过强化学习算法优化模型。让模型能生成符合人类喜好的回复。
FP8 训练
DeepSeek-V3 采用了 FP8 混合精度训练框架,通过细粒度的量化策略和高精度累加来提高训练精度。FP8 训练框架通过将激活和权重量化为 FP8 格式,减少了内存消耗并加速了训练过程。此外,通过低精度存储和通信进一步降低了内存和通信开销。
为了增强训练稳定性以及维持训练效果不至于下降太多,DeepSeek 提出了一种精细的量化策略,另外为了进一步减少 MoE 训练中的内存和通信开销,在 FP8 中缓存和分发激活值,同时以BF16格式存储低精度优化器状态。在实验中,FP8 训练模型与 BF16 基线相比,相对损失误差始终低于0.25%,在训练随机性范围内是可以接受的。
基于此,DeepSeek-V3 文中提出了一种 FP8 训练的混合精度框架。在这个框架中,大多数计算密集型操作在 FP8 中进行,而一些关键操作则保持其原始数据格式,以平衡训练效率和数值稳定性。为了加速模型训练,主要的核心计算内核(如 General Matrix Multiplication,GEMM 操作)在 FP8 精度下实现,这些操作接受 FP8 张量作为输入,并生成 BF16 或 FP32 格式的输出。所有与线性操作相关的三个 GEMM(前向传播、激活反向传播和权重反向传播)都在 FP8 中执行,这种设计理论上将计算速度提高了一倍。此外,FP8 权重反向传播 GEMM 允许激活值以 FP8 格式存储,以便在反向传播中使用,从而显著减少了内存消耗。
训练框架在以下组件中保持了原始精度(如 BF16 或 FP32):Embedding 模块、输出头、MoE 门控模块、归一化算子和注意力算子等。这些高精度的保留确保了 DeepSeek-V3 的稳定训练动态。为了进一步保证数值稳定性,作者将模型的主权重、权重梯度和优化器状态均存储在更高的精度中。该混合精度框架示意图可见下图。
预训练
DeepSeek-V3 在 14.8 万亿个高质量 token 上进行了预训练,数据包括数学、编程样本和多语言覆盖。预训练过程中,采用了 Fill-in-Middle (FIM) 策略,以提高模型对中间文本的预测能力。此外,通过优化数据处理流程,减少了冗余并保持了语料库的多样性。
后训练
DeepSeek-V3 的后训练包括监督微调(SFT)和强化学习(RL)阶段。SFT 阶段使用了 1.5M 个实例,涵盖多个领域。RL 阶段则通过奖励模型和组相对策略优化(GRPO)来提升模型性能。奖励模型包括基于规则的奖励模型和基于模型的奖励模型,以确保模型输出的准确性和可靠性。
主流的 RLHF 算法有 PPO(Proximal Policy Optimization)、DPO(Direct Preference Optimization)以及 DeepSeek 用到的 GRPO 算法等(强化学习的基础理论具有较高的综合性以及深度,我就不深入学习了,主要也是弄不明白)。
在介绍GRPO之前,需要先了解PPO算法,因为GRPO可以算作是PPO的计算效率优化版本,在保持效果的同时,降低计算资源消耗。在强化学习领域,PPO 算法被广泛认为是强化学习中的基准算法之一。PPO 采用了 Actor-Critic 架构,这一架构可以形象地理解为:有一个演员(actor)在舞台上表演,而一个评论家(critic)在台下观看。演员的目标是通过不断调整自己的表演行为来获得观众的认可,并从观众那里获得及时反馈。而评论家的任务则是评估演员的表演,并提供全面的建议。
在自然语言处理(NLP)生成模型的场景中,被训练的模型相当于演员,其表演即为生成的回复。相应地,会有评论家和观众模型来评价回复的质量。具体来说,PPO使用了四个模型:(有没有发现对应了 Reasoning Schema)
- Policy 模型(又称 Actor):输入一段上文,输出下一个token的概率分布。该模型需要训练,是我们最终得到的模型。输出下一个token即为Policy模型的“行为”。
- Value 模型(又称 Critic):用于预估当前模型回复的总收益。该总收益不仅局限于当前token的质量,还需要衡量当前token对后续文本生成的影响。该模型需要训练。
- Reward 模型:事先用偏好数据进行训练,用于对Policy模型的预测进行打分,评估模型对于当前输出的即时收益。
- Reference 模型:与 Policy 模型相同,但在训练过程中不进行优化更新,用于维持模型在训练中的表现,防止在更新过程中出现过大偏差。
为了更直观地理解 Value 模型的总收益和 Reward 模型的即时收益,可以用“磨刀不误砍柴工”来举例说明。假设现在有一把钝刀,一分钟可以劈一根柴火;如果把刀磨锋利了,一分钟就可以劈两根柴火。现在你可以选择直接用钝刀劈柴,或者先把刀磨锋利。前者的当前收益比后者高,但未来的收益会低。也就是说,Value 模型会对后者“磨刀”这一行为更为推崇,而 Reward 模型会给前者“直接砍柴”一个更高的分数。
PPO 在大模型的 RLHF 阶段被成功应用,不断提升模型回复表现的上限。然而,PPO 在计算成本和训练稳定性方面仍然存在一定的挑战。GRPO 算法对此进行了优化,其核心目标是去除 Value 模型,以此来减少训练的计算资源。