一、导读
今天抽空听完了《Maunus 产品合伙人张涛 - 手把手带你解读 DeepSeek R1 技术创新》,直接路转粉,产品同学能把技术讲的非常通透,非常不易,受到启发我也决定用通俗的语言介绍下Deepseek,再次读了一遍DeepSeek_R1/DeepSeek_V3摘录核心要点跟大家分享,本文重点涵盖训练范式、MOE、MLA、FP8混合训练,在下一篇中将介绍MTP、All2All通信、EP策略分析等偏工程架构的优化
二、整体架构
deepseek v3模型:
https://github.com/deepseek-ai/DeepSeek-V3/blob/main/inference/configs/config_671B.json
DS-v3整体架构是L层的transformer Block网络,其核心参数配置包括:模型层数61层,隐藏层维度7168,前馈网络维度18432,注意力头数128,词汇表大小129280,最大位置嵌入163840。通过精细的架构设计,DeepSeek-V3实现了在计算效率和性能上的平衡。
{
"vocab_size": 129280,
"dim": 7168,
"inter_dim": 18432,
"moe_inter_dim": 2048,
"n_layers": 61,
"n_dense_layers": 3,
"n_heads": 128,
"n_routed_experts": 256,
"n_shared_experts": 1,
"n_activated_experts": 8,
"n_expert_groups": 8,
"n_limited_groups": 4,
"route_scale": 2.5,
"score_func": "sigmoid",
"q_lora_rank": 1536,
"kv_lora_rank": 512,
"qk_nope_head_dim": 128,
"qk_rope_head_dim": 64,
"v_head_dim": 128,
"dtype": "fp8"
}
整体网络61层,包括前三层FFN,在MoE架构中,每一层都是MoE层,共有58层MoE层(第4层至第61层),每层专家总数为257个(包括1个共享专家和256个路由专家),模型总专家数为14,906个。每层活跃专家数为9个(包括1个共享专家和8个路由专家),整个模型的活跃专家数为522个
改变世界的模型代码实际也不到1k行,当然不含底层库代码,可以看看:
https://github.com/deepseek-ai/DeepSeek-V3/blob/main/inference/model.py,参数网络非常巨大,家里没矿基本就别想复现训练了~
三、核心技术亮点
3.1 训练方式的创新
3.1.1 大模型的训练类比
在介绍DeepSeek模型的训练之前,先简单介绍下大模型的训练的过程,可以类似人的成长过程:
#1 从Zero模型不断进行学习(带答案的样本)+考试(不给答案的试题),在过程中,本身会结合考试情况持续进行迭代,直到loss(类似正确率)收敛【这个过程类似满足毕业要求的大学生,掌握了一定的技能】
#2 在#1的基础上,针对领域的数据(带答案的样本)进行针对性增强【过程类似企业的专业领域的入职培训】
#3 在#2的基础上,进一步进行调优,包括让输出等更规范,总结更有调理等,在这个过程中同样也会有反馈和评估【类似主管反馈】
3.1.1 DeepSeek-R1的RL(Reinforcement Learning,强化学习)训练过程
DeepSeek-R1-Zero的诞生,严格依赖了上述DeepSeek V3 + RL,DS团队很早通过RL证明了随着迭代step的增长,思维链的生成会更丰富,通过这种持续训练的方式,本质上是非常巧妙的从DeepSeek-v3中,提取出来了思维链的文本信息(只是这个文本信息可读性一般),个人理解本质上是将输入和答案之间更加了更多的连接方式,同时通过多种方式交叉验证也进一步让答案更趋近于正确。
这里边也讲了一个比较有意思的RL激励目标的设定,如下图所示:
#1 在DeepSeek-R1-Zero的产出上,RL主要是基于答案的准确性+思维链format进行激励模型,让模型产出高质量的cold start数据
#2 cold start数据输入给DeepSeek-V3产出chckpoint1
#3 在此基础上通过调整RL的目标(增加了一致性激励),产出chckpoint2,并生成新的COT数据
#4 在COT数据+知识数据的叠加加,RL目标进一步增加人类的倾向性激励,最终诞生DeepSeek-R1
可以认为通过DeepSeek V3与DeepSeek-R1-Zero的交叉迭代,最终诞生了DeepSeek-R1,甚至可能通过反复的COT是否会进一步提升模型的表现?
论文中也指出R1的最终成果是DeepSeek-V3的基座大模型的成功的延续,这个方式放在普通基础模型上进行同样的RL训练就会效果可能会一般,因此“罗马不是一天建成的”,作业抄起来得抄全套
3.2 MOE 专家网络
DeepSeek的MOE除了每一层的设计上,引入了共享专家(1个)和256个路由专家,类似医院的全科和专门诊室分配,在分诊台的设计上也有创新,首先是整个模型前三次FFN对信息进行了充分融合,然后第四层开始才进行专家网络路由,在路由时基于一定的基础知识,加入了路由网络控制激活最多top(k=8)个路由网络。
MoE的工作流程如下:
- 数据输入:原始数据(如文本、图像)输入到MoE架构中。
- 门控网络计算:门控网络处理输入数据,计算每个专家的权重。
- 专家网络处理:根据权重,选择部分专家处理数据,每个专家生成中间结果。
- 结果融合:将各专家的输出按权重加权求和,得到最终结果
MOE的优势在于:
- 计算效率:通过稀疏激活专家,减少计算量,提高训练和推理速度。
- 模型性能:多个专家的集成增强了模型的表示能力和泛化能力。
- 灵活性与可扩展性:支持动态调整专家数量,适应不同任务需求。
那么每一个专家网络内部又是什么?用通俗的话说,本质上是一个特定的网络(network网络),每个网络并非出生时就已经被设计成专有的技能,而是在训练的过程中,因为不同的专家被安排(路由网络控制)了差异化的输入(toten),形成了差异化的经验(参数),训练的过程可以等价于人类学习了差异化的专业知识,形成了差异化的经验权重,当正式推理(问答咨询)时,同样特定的带有经验(模型权重)的专家被激活参与文本序列的预测:
- “细粒度/垂类专家” :通过细粒度专家切分 (Fine-Grained Expert Segmentation) 将一个 FFN 切分成 mmm 份。尽管每个专家的参数量小了,但是能够提高专家的专业水平
- “共享专家”:掌握更加泛化或公共知识的专家,从而减少每个细粒度专家中的知识冗余,共享专家的数量是固定的且总是处于被激活的状态
结合3.1的训练范式,通俗小结:
- 训练阶段:最开始的模型本质上没有所谓的专家存在,只有一个框架,类似于提供了一个空壳的医院,然后直接塞进去了几个只有1点点背景知识的“大学生”,分诊台(专家门控网络)往不同的屋子里“大学生”路由不同的病例(带答案),同时给一部分不带答案的病例让其预测,由于知道答案,可以反过来优化分配的策略并对专家进化反馈(进化);在两者协同的训练下,屋子里的“大学生”逐步进化成了“专家”,分诊台也进化成了有专业背景的护士分诊台
- 强化训练阶段:基于训练的产出模型,将带答案的病例,进一步按照结果的正确性、可读性等SFT模型优化和调整,让模型的输出效率更好
- 推理问答阶段:给出病例问题,分诊台基于积累的经验路由给不同的专家判断,然后综合给出预测的答案
3.3 MLA 多头注意力
deepseek技术解读(1)-彻底理解MLA(Multi-Head Latent Attention):
https://zhuanlan.zhihu.com/p/16730036197
MLA主要通过优化KV-cache来减少显存占用,从而提升推理性能,这是因为
要理解MLA,先得看MHA的cache设计,为了方便理解kv cache的成本,可以认为后一个toten的输入时,依赖了前N个toten在各层的网络缓存,因此1个toten会需要2(key+value)*层数l*每层注意力数量h*注意力的维数w*float宽度:
以上图为例,则1个toten存储的数量就是 2(key+value)*层数80*每层注意力数量64*注意力的维度128*float宽度2B=2*80*64*128*2B=2.63MB,这个缓存对于显存的使用非常巨大,B = 32 , S = 4096,这个缓存需求高达343GB
由于ds模型的输入带了think的toten都非常长,因此DeepSeek对kv进行了压缩,引入了MLA对矩阵进行降秩压缩,业界的压缩方案方案包括
方案 | 参数规模 | 优劣势 |
MQA(Multi-Query Attention) | 2*l*h*w*2B | 每层仅留下1个注意力向量 |
GQA(Group-Query Attention) | 2*l*g*w*2B | g是分组的数量,当g=h的时候与MHA就等价了 |
MLA的思路,本质上是把h*w的多头注意力向量转换成了一个两个压缩矩阵,在每次计算时再还原,通过增加计算时间再转换回原空间
- 对于input的ht维度为7168,经过低秩变换矩阵Wdkv(512x7168) 变成 Wdkv(512x7168)*ht(7168)=Ctkv 512x1维【参考线性代代数的矩阵乘法】
- 然后再通过两个KV变化矩阵Wuk/Wuv(64*128*512),将上述512维的矩阵进一步还原成 W(64*64*512)*ctkv(512*1)=64*128 【注意此处已经和MQA的h*w一致】
下面来计算,MLA的这种机制分别优化了多少存储和计算【下述估算可能不是特别严谨全面,可以参考对比】
- 存储:首先模型本身,相比MQA,新增了Wdkv、Wuk、Wuv三个转换矩阵,对应的大小为2*l层*(降秩512*7168+2个kv *64*128*512)*2B=2.5GB【注意这个是模型参数,不随输入变化,所以成本可控】;对于动态toten,每一层的缓存大小为 2*l*512*2B=160k (相比原来的2.62MB减少了16倍),因此对于长toten比较有吸引力(简单初略估计,只要toten超过1k,kv cache+模型现存 胜过了MQA)
- 计算:增加了低秩转换、KV变化矩阵恢复,也是非常海量的float增加,但由于都是矩阵计算,GPU比较擅长,就不单独估算计算成本了,对于位置偏移相关的也做了类似的优化(参考ROPE)
从上面可以看出,存储优化在长文本时更明显,短文本反而模型更大了,当然根据目前ds处理的任务复杂度,可以认为都是长文本(含思维链),所以这是非常亮眼的优化
3.4 FP8混合训练
重大的创新在于训练时已经结合网络特征部分选取了fp8,做到了native fp8的训练,不仅减少了传输而且对inffer时的量化损失也做到了最小。当然这里边挑战也是非常大的,因为训练过程中fp16降低为fp8可能导致精度不足,训练无法收敛的灾难!
因此由于不同模块的精度不同,就需要在模块之间穿插量化(to FP8)/反量化(to FP16)计算,DeepSeek kernel.py中统一提供了此类接口和优化,灵活的实现fp8和fp16的转换,从而节省一些训练参数的现存用量!
顺便提一下,在训练框架方面,研究团队开发的 DualPipe 算法(GPU在forward和backward中切换,IO同时并行传输)实现了高效的流水线并行处理,减少了流水线停滞,并通过计算和通信并行处理的方式降低了训练过程中的通信开销。如下图所示,gpu的气泡非常少,确实并行做的非常赞!只要维持适当的计算通信比例,就能在不同节点间实现细粒度专家分配,同时将全节点间的通信开销降至接近于零
四、人工智能的局限性
大模型出来后,很多都在讨论工作的可替代性问题,晚上下班正好路上听了 董坤 的人工智能的分享,分享下他的观点,供参考,或许可以缓解大家的一部分焦虑
问责性(Accountability)
- 不能承担后果,因此不能承担重大决策,比如医生虽然可能误诊,但是整体为后果负责,而AI大模型不具备这个能力
- 个人的决策对公司和家庭越发重要,人就越不可替代,因为决策的重要性,管理工作短时间还得依赖人,但可以AI辅助,比如人的招聘AI可以参与,但人的“选用育留”仍旧还需要高质量的管理者
有限性(Boundedness)
- AI无法理解情绪价值,情绪价值是智力资本时代创造价值的主导
- AI不能创新,只能因循守旧,而创新机制是管理价值的最大体现
欺骗性(Cheating)
- 长尾知识的可靠性不足
- 知识的的时效性等
愚蠢性(Dumbness)
- 背后的真正的语义其实并不理解,比如你吃了么等原始意图理解压根不需要思考的回答
不过董老师的观点还是在于人要回归创新创造上来,他的观点是大模型本身不具备创新,更多的在于历史知识的归纳,这个仁者见仁,智者见智了
附录:大模型带你解读 Deepseek-v3/model.py 核心模型代码
model.py 文件实现了一个基于 Transformer 架构的深度学习模型,其中集成了量化计算、混合专家(Mixture-of-Experts, MoE)机制等技术,以提升模型的计算效率和性能。下面将从代码的整体结构、主要模块和类的功能等方面进行详细解读。
1. 导入模块
python
import math
from dataclasses import dataclass
from typing import Tuple, Optional, Literal
import torch
from torch import nn
import torch.nn.functional as F
import torch.distributed as dist
from kernel import act_quant, weight_dequant, fp8_gemm
- 导入了数学计算、数据类定义、类型提示等基础模块。
- 导入了torch及其相关子模块用于构建和训练深度学习模型。
- 从kernel.py文件中导入了量化、反量化和 FP8 矩阵乘法等核心计算函数。
2. 全局变量定义
python
world_size = 1
rank = 0
block_size = 128
gemm_impl: Literal["bf16", "fp8"] = "bf16"
attn_impl: Literal["naive", "absorb"] = "absorb"
- world_size和rank用于分布式训练,分别表示进程总数和当前进程的编号。
- block_size是量化和反量化操作的块大小。
- gemm_impl指定矩阵乘法的实现方式,可选bf16(BFloat16)或fp8(FP8)。
- attn_impl指定注意力机制的实现方式,可选naive(简单实现)或absorb。
3. ModelArgs数据类
python
@dataclass
class ModelArgs:
max_batch_size: int = 8
max_seq_len: int = 4096 * 4
dtype: Literal["bf16", "fp8"] = "bf16"
# 其他属性...
- 该数据类用于定义模型的各种参数和超参数,包括最大批量大小、最大序列长度、数据类型、词汇表大小等。
- 通过使用@dataclass装饰器,可以方便地定义和管理这些参数。
4. 主要模块和类
4.1 ParallelEmbedding类
python
class ParallelEmbedding(nn.Module):
def __init__(self, vocab_size: int, dim: int):
pass
def forward(self, x: torch.Tensor) -> torch.Tensor:
pass
- 该类实现了支持分布式并行的嵌入层,用于将输入的 token 索引转换为嵌入向量。
- 在分布式环境下,会对输入的 token 进行处理,确保每个进程只处理自己负责的部分词汇表。
4.2 Linear类
python
class Linear(nn.Module):
dtype = torch.bfloat16
def __init__(self, in_features: int, out_features: int, bias: bool = False, dtype = None):
pass
def forward(self, x: torch.Tensor) -> torch.Tensor:
pass
- 自定义的线性层,支持量化权重和可选的偏置项。
- 根据权重的量化情况,在forward方法中会调用不同的线性计算方式。
4.3ColumnParallelLinear和RowParallelLinear类
python
class ColumnParallelLinear(Linear):
def __init__(self, in_features: int, out_features: int, bias: bool = False, dtype = None):
pass
def forward(self, x: torch.Tensor) -> torch.Tensor:
pass
class RowParallelLinear(Linear):
def __init__(self, in_features: int, out_features: int, bias: bool = False, dtype = None):
pass
def forward(self, x: torch.Tensor) -> torch.Tensor:
pass
- 分别实现了列并行和行并行的线性层,用于在分布式环境下进行矩阵乘法的并行计算。
4.4 RMSNorm类
python
class RMSNorm(nn.Module):
def __init__(self, dim: int, eps: float = 1e-6):
pass
def forward(self, x: torch.Tensor):
pass
- 实现了 Root Mean Square Layer Normalization(RMSNorm),用于对输入张量进行归一化处理。
4.5 MLA类
python
class MLA(nn.Module):
def __init__(self, args: ModelArgs):
pass
def forward(self, x: torch.Tensor, start_pos: int, freqs_cis: torch.Tensor, mask: Optional[torch.Tensor]):
pass
- 实现了多头注意力层(Multi-Headed Attention Layer),支持低秩自适应(LoRA)技术。
- 在forward方法中,会对查询、键和值进行投影、旋转位置编码等操作,并计算注意力分数。
4.6MLP类
python
class MLP(nn.Module):
def __init__(self, dim: int, inter_dim: int):
pass
def forward(self, x: torch.Tensor) -> torch.Tensor:
pass
- 实现了多层感知机(Multi-Layer Perceptron),作为 Transformer 中的前馈网络。
4.7 Gate类
python
class Gate(nn.Module):
def __init__(self, args: ModelArgs):
pass
def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
pass
- 实现了混合专家(MoE)模型中的门控机制,用于将输入路由到不同的专家模块。
4.8 Expert类
python
class Expert(nn.Module):
def __init__(self, dim: int, inter_dim: int):
pass
def forward(self, x: torch.Tensor) -> torch.Tensor:
pass
- 实现了 MoE 模型中的专家模块,每个专家模块是一个独立的多层感知机。
4.9MoE类
python
class MoE(nn.Module):
def __init__(self, args: ModelArgs):
pass
def forward(self, x: torch.Tensor) -> torch.Tensor:
pass
- 实现了混合专家(MoE)模块,结合了门控机制和多个专家模块。
- 在forward方法中,会根据门控机制的输出将输入路由到不同的专家模块,并将专家模块的输出进行加权求和。
4.10 Transformer类
python
class Transformer(nn.Module):
def __init__(self, args: ModelArgs):
pass
@torch.inference_mode()
def forward(self, tokens: torch.Tensor, start_pos: int = 0):
pass
- 实现了整个 Transformer 模型,包含嵌入层、多个 Transformer 块、归一化层和输出投影层。
- 在forward方法中,会对输入的 token 进行嵌入、位置编码、注意力计算、前馈网络计算等操作,最终输出词汇表上的 logits。
5. 辅助函数
python
def linear(x: torch.Tensor, weight: torch.Tensor, bias: Optional[torch.Tensor] = None) -> torch.Tensor:
pass
def precompute_freqs_cis(args: ModelArgs) -> torch.Tensor:
pass
def apply_rotary_emb(x: torch.Tensor, freqs_cis: torch.Tensor) -> torch.Tensor:
pass
- linear函数实现了线性变换,支持量化和非量化的权重。
- precompute_freqs_cis函数用于预计算旋转位置编码所需的复指数值。
- apply_rotary_emb函数用于将旋转位置编码应用到输入张量上。
总结
model.py 文件实现了一个功能丰富的 Transformer 模型,集成了量化计算、混合专家机制、分布式训练等技术。通过这些技术的应用,可以提高模型的计算效率和性能,同时减少内存占用。代码的结构清晰,各个模块和类的职责明确,便于扩展和维护。