介绍 随着LLM参数量持续地增加,其在训练和推理过程中面临着巨大的计算资源和低推理效率的挑战。尽管也出现了Grouped-Query Attention (GQA) 和 Multi-Query Attention (MQA)这类改进Multi-Head Attention (MHA) 以提高推理效率的自注意力机制技术,但模型性能可能会有所降低。 根据论文及博客,DeepSeek-V2在DeepSeek上进行改进,但并没有沿用主流的“类LLaMA的Dense结构”和“类Mistral的Sparse结构”,而是对Transformer架构中的自注意力机制进行了全方位的创新,提出了MLA(Multi-head Latent Attention)结构,并使用了自研的稀疏MoE技术进一步将计算量降低,大幅提高了推理效率。 
DeepSeek-V2 包含236B参数,每个Token激活2.1B参数,支持长达 128K 的上下文长度。与DeepSeek 67B相比,DeepSeek-V2 在性能上取得了显著提升,节省了42.5%的训练成本,减少了93.3%的KV缓存,并将最大生成吞吐量提高到了5.76倍。 这真的是真开源啊!深度求索已经将DeepSeek-V2 模型已完全上线至官网的开发平台供用户使用,DeepSeek-V2 API也是物美价廉,一块钱100万token,价格仅为GPT-4-Turbo的近百分之一!并且,深度求索秉持着最开放的开源精神,将该模型和论文完全开源,可免费商用! 模型架构 模型架构图 
模型定义 DeepseekForCausalLM( (model): DeepseekModel( (embed_tokens): Embedding(102400, 5120) (layers): ModuleList( (0): DeepseekDecoderLayer( (self_attn): DeepseekAttention( (q_a_proj): Linear(in_features=5120, out_features=1536, bias=False) (q_a_layernorm): DeepseekRMSNorm() (q_b_proj): Linear(in_features=1536, out_features=24576, bias=False) (kv_a_proj_with_mqa): Linear(in_features=5120, out_features=576, bias=False) (kv_a_layernorm): DeepseekRMSNorm() (kv_b_proj): Linear(in_features=512, out_features=32768, bias=False) (o_proj): Linear(in_features=16384, out_features=5120, bias=False) (rotary_emb): DeepseekYarnRotaryEmbedding() ) (mlp): DeepseekMLP( (gate_proj): Linear(in_features=5120, out_features=12288, bias=False) (up_proj): Linear(in_features=5120, out_features=12288, bias=False) (down_proj): Linear(in_features=12288, out_features=5120, bias=False) (act_fn): SiLU() ) (input_layernorm): DeepseekRMSNorm() (post_attention_layernorm): DeepseekRMSNorm() ) (1-59): 59 x DeepseekDecoderLayer( (self_attn): DeepseekAttention( (q_a_proj): Linear(in_features=5120, out_features=1536, bias=False) (q_a_layernorm): DeepseekRMSNorm() (q_b_proj): Linear(in_features=1536, out_features=24576, bias=False) (kv_a_proj_with_mqa): Linear(in_features=5120, out_features=576, bias=False) (kv_a_layernorm): DeepseekRMSNorm() (kv_b_proj): Linear(in_features=512, out_features=32768, bias=False) (o_proj): Linear(in_features=16384, out_features=5120, bias=False) (rotary_emb): DeepseekYarnRotaryEmbedding() ) (mlp): DeepseekMoE( (experts): ModuleList( (0-159): 160 x DeepseekMLP( (gate_proj): Linear(in_features=5120, out_features=1536, bias=False) (up_proj): Linear(in_features=5120, out_features=1536, bias=False) (down_proj): Linear(in_features=1536, out_features=5120, bias=False) (act_fn): SiLU() ) ) (gate): MoEGate() (shared_experts): DeepseekMLP( (gate_proj): Linear(in_features=5120, out_features=3072, bias=False) (up_proj): Linear(in_features=5120, out_features=3072, bias=False) (down_proj): Linear(in_features=3072, out_features=5120, bias=False) (act_fn): SiLU() ) ) (input_layernorm): DeepseekRMSNorm() (post_attention_layernorm): DeepseekRMSNorm() ) ) (norm): DeepseekRMSNorm() ) (lm_head): Linear(in_features=5120, out_features=102400, bias=False) )
MLA(Multi-head Latent Attention) MLA的示意图和完整公式  
低秩KV联合压缩(Low-Rank Key-Value Joint Compression) MLA的核心是通过低秩联合压缩来减少Key和Value的维度,同时也Query进行低秩的压缩,这导致计算量大幅度降低,并且在推理时,只需要缓存 (潜在向量的维度极小),这极大地减少了所需要的KV缓存。 

# 首先,使用全连接层(self.q_a_proj)对输入的隐状态(hidden_states)进行投影变换 q = self.q_b_proj(self.q_a_layernorm(self.q_a_proj(hidden_states)))
q = q.view(bsz, q_len, self.num_heads, self.q_head_dim).transpose(1, 2)
# 将查询表示(q)分为两部分:没有经过位置编码的部分(q_nope)和经过位置编码的部分(q_pe) q_nope, q_pe = torch.split( q, [self.qk_nope_head_dim, self.qk_rope_head_dim], dim=-1 )
# 使用MQA(Multi-Query Attention)对输入的隐状态进行处理,得到压缩后的键值对表示(compressed_kv) compressed_kv = self.kv_a_proj_with_mqa(hidden_states)
# 将压缩后的键值对表示分为两部分:低秩压缩的键值对部分和经过位置编码的键部分(k_pe) compressed_kv, k_pe = torch.split( compressed_kv, [self.kv_lora_rank, self.qk_rope_head_dim], dim=-1 )
k_pe = k_pe.view(bsz, q_len, 1, self.qk_rope_head_dim).transpose(1, 2)
# 对压缩后的键值对升维,包括RMSNorm(self.kv_a_layernorm)和全连接层(self.kv_b_proj) kv = ( self.kv_b_proj(self.kv_a_layernorm(compressed_kv)) .view(bsz, q_len, self.num_heads, self.qk_nope_head_dim + self.v_head_dim) .transpose(1, 2) )
解耦RoPE位置编码(Decoupled Rotary Position Embedding) DeepSeek-V2不能直接将RoPE应用在压缩后的KV上,因为RoPE矩阵是位置敏感的,在推理过程中生成的token与当前的位置是相关联的,这就像是一张高清图片压缩成一个标清图片,其位置信息发生了改变,不能直接使用。 这就导致DeepSeek-V2需要重新计算每个token的位置信息,这会降低推理速度。为了解决MLA中的RoPE与低秩KV联合压缩不兼容的问题,DeepSeek团队提出了解耦RoPE的策略。 
在这种策略中,DeepSeek-V2使用额外的多头查询(multi-head queries)和共享的键(shared keys)来携带位置编码信息。这样,即使在低秩压缩的情况下,也能有效地保持位置信息,并且不会增加推理时的计算负担。 # 使用torch.split函数将键值对表示(kv)拆分成两部分: # k_nope是没有经过位置编码的键部分,不包含位置信息 # value_states是值部分,它将用于后续的位置编码和注意力权重的计算 k_nope, value_states = torch.split( kv, [self.qk_nope_head_dim, self.v_head_dim], dim=-1 )
kv_seq_len = value_states.shape[-2]
# 如果past_key_value不为空,即存在可复用的键值对缓存,则更新kv_seq_len以包含之前缓存的序列长度 # 这样做可以在自回归解码时利用之前的计算结果,提高效率 if past_key_value is not None: kv_seq_len += past_key_value.get_usable_length(kv_seq_len, self.layer_idx)
# 调用self.rotary_emb函数,根据value_states和更新后的序列长度kv_seq_len计算旋转位置编码的cos和sin值 cos, sin = self.rotary_emb(value_states, seq_len=kv_seq_len)
# apply_rotary_pos_emb函数将位置信息融合到查询和键的表示中 q_pe, k_pe = apply_rotary_pos_emb(q_pe, k_pe, cos, sin, position_ids)
# 初始化查询状态(query_states)的张量,这个张量将用于存储融合了解耦RoPE的查询表示 query_states = k_pe.new_empty(bsz, self.num_heads, q_len, self.q_head_dim)
# 将未经过位置编码的查询表示(q_nope)复制到 query_states 张量的前一部分,即那些不包含位置编码的维度。 # 这样做可以有利于后续将原始的查询表示与含有位置编码信息的查询表示分开来处理 query_states[:, :, :, : self.qk_nope_head_dim] = q_nope query_states[:, :, :, self.qk_nope_head_dim :] = q_pe
# 初始化键状态(key_states)的张量,这个张量将用于存储融合了解耦RoPE的键表示 key_states = k_pe.new_empty(bsz, self.num_heads, q_len, self.q_head_dim) key_states[:, :, :, : self.qk_nope_head_dim] = k_nope key_states[:, :, :, self.qk_nope_head_dim :] = k_pe
MLA与MHA、GQA和MQA对比 MLA 只需要少量的 KV 缓存,相当于只有 2.25 组的 GQA,但却能获得比 MHA 更强的性能。 

DeepSeekMoE 对于前馈神经网络,DeepSeek-V2 采用了自研的稀疏MoE架构DeepSeekMoE,它主要包括细粒度专家(Finegrained experts)和共享专家(shared experts)这两个核心组件。在激活的专家参数量和总专家参数量相同的情况下,DeepSeekMoE的性能可以远远超越传统的MoE架构。 
DeepSeekMoE的计算过程如下: 
预训练 数据构建 DeepSeek-V2在保持与DeepSeek 67B相同的数据处理流程的同时,增加了数据量并提升了数据质量。为了扩大预训练语料库,DeepSeek团队深入挖掘了互联网数据并优化了数据清洗步骤,还引入更多中文数据。除了关注数据量,DeepSeek团队还注重数据质量,收集了更多高质量的预训练数据,并改进了基于质量(quality-based)的过滤算法。预训练语料库包含8.1T 个 Token,其中,中文Token数量比英文Token多12%。 分词器 分词器沿用与 DeepSeek 67B 中使用的相同的分词器,它是基于BBPE算法构建的,词表大小为100K。 模型超参数 { 'num_hidden_layers': 60, // Transformer层的数量 'hidden_size': 5120, // 隐藏层的大小 'num_attention_heads': 128, // 注意力头的数量 'kv_lora_rank': 512, // KV压缩维度 'q_lora_rank': 1536, // Query压缩维度 'qk_rope_head_dim': 64, // 解耦Query和Key的每个头部维度 'n_shared_experts': 2, // MoE层中的共享专家数量 'n_routed_experts': 160, // MoE层中的路由专家数量 'moe_intermediate_size': 1536, // 每个MoE专家的中间隐藏层的维度 'num_experts_per_tok': 6, // 每个token激活的专家数量 'routed_scaling_factor': 16.0, // 路由专家的缩放因子 'rms_norm_eps': 1e-06 // RMS归一化的epsilon值 }
训练超参数 训练超参数包括使用AdamW优化器,设置β1=0.9,β2=0.95,权重衰减为0.1。学习率采用预热和步衰减策略,最初从0线性增加到最大值,在训练约60%的token后乘以0.316,再训练约90%的token后再次乘以0.316。最大学习率设置为2.4×10^-4,梯度裁剪范数为1.0。批次大小从2304逐渐增加到9216,最大序列长度设置为4K,训练使用8.1Ttoken。采用流水线并行性,将模型的不同层部署在不同设备上,每层的路由专家均匀部署在8个设备上。设备限制的路由确保每个token发送至最多3个设备。平衡损失中的λ1设置为0.003,λ2为0.05,λ3为0.02。训练期间采用token丢弃策略以加速,但评估时不丢弃token。 实验设备 DeepSeek-V2 使用了内部开发的 HAI-LLM 框架进行训练,它采用了16路零气泡流水线并行、8路专家并行以及ZeRO-1数据并行。由于DeepSeek-V2的激活参数量较少,并且部分运算符被重新计算以节省激活内存,它避免了张量并行,减少了通信成本。训练效率通过重叠共享专家计算和专家并行全通信进一步提升,同时定制了CUDA核心以加速通信、路由算法和专家间融合计算。此外,MLA也基于改进版的FlashAttention-2进行了优化。 实验在配备 NVIDIA H800 GPU 的集群上进行,每个节点包含8个GPU,这些GPU通过 NVLink 和 NVSwitch 在节点内连接,节点间使用 InfiniBand 互连。 上下文扩展 DeepSeek-V2 初始预训练后,采用YaRN将上下文窗口长度从4K扩展到128K。尽管训练仅在序列长度为32K的条件下进行,但当模型在上下文长度为128K的情况下进行评估时,仍然显示出强大的性能。  大海捞针实验评估结果 对于预训练得到的Base版本的语言模型,DeepSeek-V2 在多种语言理解和生成任务的基准测试上进行了评估,并与代表性的开源模型进行了比较,包括 DeepSeek 67B、Qwen1.5 72B、LLaMA3 70B 和 Mixtral 8x22B。 
在训练成本方面,DeepSeek-V2 由于激活参数较少,因此在训练每万亿tokens时,与 DeepSeek 67B 相比,可以节省42.5%的训练成本。 在推理效率方面,DeepSeek-V2 通过将参数转换为 FP8 精度,并采用 KV 缓存量化,显著减少了所需的 KV 缓存。这使得 DeepSeek-V2 能够处理更大的批量大小,从而提高了推理吞吐量。在单个节点上使用 8 个 H800 GPU 进行评估时,DeepSeek-V2 的生成吞吐量超过了每秒 50K 个tokens,这是 DeepSeek 67B 最大生成吞吐量的 5.76 倍。 对齐 在预训练之后,进入对齐阶段,包括有监督微调(SFT)阶段和强化学习(RL)阶段 有监督微调(SFT)指令数据 DeepSeek团队构建了一个包含1.5M实例的指令调优数据集,其中1.2M个实例用于提高有用性,0.3M个实例用于提高安全性。DeepSeek-V2使用这个数据集进行了2个epoch的微调,学习率设置为5×10^-6。在评估DeepSeek-V2的Chat模型时,主要使用了生成类的基准测试,但也包括了一些代表性的多项选择任务,如MMLU和ARC。此外,还使用IFEval来评估模型指令跟随的能力。在预训练之后,进入对齐阶段,包括有监督微调(SFT)阶段和强化学习(RL)阶 强化学习(Reinforcement Learning,RL) GRPO算法 为了节省RL训练的成本,DeepSeek团队采用了Group Relative Policy Optimization (GRPO)算法。GRPO的核心创新在于它去除了PPO中的价值函数(critic),而是通过从策略模型中采样一组输出,然后计算这些输出的平均奖励作为基线。这种方法显著减少了训练资源的消耗,因为它不需要训练和存储与策略模型同等大小的价值函数模型。 

GRPO的步骤 
采样:对于每个问题,GRPO从旧策略模型中采样一组输出。 奖励计算:使用奖励模型为每个采样输出打分,得到一组奖励。 基线估计:将这些奖励进行归一化处理(减去组平均值,除以组标准差),然后使用归一化的奖励作为基线。 优化目标:GRPO通过最大化一个包含相对优势的优化目标函数来更新策略模型。相对优势是指在同一组中,各个输出相对于基线的优势。 迭代训练:GRPO可以进行迭代训练,其中奖励模型会根据策略模型的新采样结果不断更新,以提供更准确的训练信号。
GRPO的优势 内存效率:由于不需要价值函数,GRPO在内存使用上更加高效。 计算效率:GRPO通过减少模型大小以降低计算负担。 性能提升:在数学推理任务中,GRPO能够显著提高模型的性能。
训练策略 DeepSeek团队发现在推理数据(如代码和数学)上的RL训练与一般数据的训练有显著不同。因此,他们采用了两阶段的RL训练策略:首先进行推理对齐,然后进行人类偏好对齐。在推理对齐阶段,训练一个奖励模型来优化代码和数学推理任务的反馈;在人类偏好对齐阶段,采用多奖励框架,从有用性、安全性和规则性等奖励模型中获取奖励。 
训练效率优化 为了提高在大型模型上进行强化学习(RL)训练的效率,DeepSeek团队实施了一系列工程优化措施,包括:(1)采用混合引擎,针对训练和推理采用不同的并行策略以提高GPU利用率;(2)利用较大batch的vLLM作为推理后端以加速推理速度;(3)精心设计的调度策略,实现了模型在CPU和GPU之间的切换,在训练速度和内存消耗之间取得了近乎最优的平衡。 
评估结果 基准测试 
英文能力 
中文能力 
数学能力 
代码能力 
讨论 SFT数据规模 DeepSeek团队探讨了监督式微调(SFT)数据集大小对模型性能的影响。早期研究认为,少于10,000个SFT实例就足以达到满意的效果。然而,在DeepSeek-V2的实验中,使用少于这一数量的SFT实例会导致模型在IFEval基准测试上的表现显著下降。这表明LLM需要一定量的数据来培养其特定技能,而数据量的减少不能完全通过增加模型规模来弥补。 强化学习的对齐税 在人类偏好对齐的实验过程中,DeepSeek团队观察到模型在开放式生成基准测试上的性能显著提升,但同时也注意到“对齐代价”现象,即对齐过程可能会使模型在某些标准基准测试(如BBH)上的表现下降。 在线强化学习 在人类偏好对齐的实验过程中,DeepSeek团队发现在线方法显著优于离线方法,并投入了巨大努力实施在线RL框架来对齐DeepSeek-V2,但在线和离线对齐的结论可能因不同情境而异。 参考 1. DeepSeek-V2/deepseek-v2-tech-report.pdf at main · deepseek-ai/DeepSeek-V2 (github.com) 2. DeepSeek发布全球最强开源MoE模型 3. deepseek-ai/DeepSeek-V2-Chat · Hugging Face 4. [2402.03300] DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models (arxiv.org)
|