LLM大模型:deepseek浅度解析(一):V3的差异化
这几天被deepseek刷屏了:凡是和AI沾边的博主,无一例外不介绍deepseek的!一时间deepseek热度蹭蹭上窜,风头无限,看的其他国产AI大模型厂商直瞪眼!deepseek是一家叫做幻方的量化投资公司出品,可谓是师出名门,这个大模型到底有啥独特之处了?
一个大模型的效果好不好,主要看以下三点:第1和第3点都是通过设计合理的loss函数做backward来更新weight;一个是auto regression,另一个reward牵引!
- 网络架构创新:比如transformer、resnet这种突破性的架构创新;各种各样网络结构的最终目的还是在提取特征!本质还是feather enigneering!
- 底层训练和推理创新:完善工程实现的细节,比如FP8、MOE、MLA、流水线并行DualPipe
- reinforcement learning路径创新:COT、TOT、GOT等
deepseek占了以上几条?
1、先来看看网络结构,如下:
从上图来看,和经典的transformer相比,最明显的改进点就是attention和FFN了!老规矩,从下往上逐步解读学习;
(1)attention:transformer红得发紫,效果吊打以往的任何架构,核心改进就是attention机制:通过dot product找到token之间的相似度,然后根据相似度累加value,得到非常好的feather embedding!但其缺点也非常明显:time&space complexity都是O(n^2);针对这些缺点,诞生了很多attention的变种,也诞生了manba这种全新的架构,deepseek又是怎么改进attention机制的了?
deepseek对做attention部分的取名是multi-head latent attention,明显多了个latent,这个latent怎么理解了?是怎么实现的了?有哪些明显的作用了?transform中的attention时间和空间复杂度高达O(n^2),这个问题是必须解决的!deepseek这里做了点小trick:把input hidden做linear projection线性映射,也就是乘以个矩阵,从h维度降低到c维,并且Q和KV都分别乘以不同的W,降低到相同的维度,分别得到latent向量!接下来就是常规的attention操作了:分别加上RoPE位置旋转编码,并且把latent向量等比例切分成multi-head,最后做query和key的dot product,然后更新value的值!
deepseek这么做,理论上的空间和时间复杂度还是O(n^2),但因为做了linear project,维度降低了很多,所以耗时能减少很多!
(2)FFN:采用的是MOE架构!那么deepseek在MOE方便又有哪些创新了?仔细看图,export有绿色和蓝色两种,绿色是永远都要激活使用的,蓝色是选择性激活的,所以蓝色的前面还有Router,用来选择激活哪些蓝色exports,所以这些exports看着像是大号的神经元neuron!对于同一个问题,多个exports一起计算,分别输出各自的结果到output hidden层,整个思路和传统机器学习的random forest是不是很像了?具体落地实践时,会遇到一些问题:
- 蓝色experts一般选择topK个参与forward计算。为了防止少数exports被过多选中,需要有个平衡机制,让每个exports都有均等的机会被选中,也就是负载均衡load balance!这个又是怎么实现的了?俗话说的好:术业有专攻!就每个人而言,都有擅长和不擅长的。对于neuroal netword而言,内部每个neuron的权重值直接决定了擅长处理哪方面的token!至于选择topK的机制,原论文是这么描述的:
- DeepSeek-V3 uses the sigmoid function to compute the affinity scores, and applies a normalization among all selected affinity scores to produce the gating values. 使用sigmoid函数计算亲和分数,以此计算gating values;
-
Complementary Sequence-Wise Auxiliary Loss. Although DeepSeek-V3 mainly relies on the auxiliary-loss-free strategy for load balance, to prevent extreme imbalance within any single sequence, we also employ a complementary sequence-wise balance loss;公式如下:
这种事通过引入一个额外的loss function来鼓励每个sequence上的experts做load balance!
-
- Pi是每个sequence序列i上的平均负载,归一化后可以在不同的sequence之间互相比较。
- fi是每个sequence序列i上有多少个token属于负载最重的前Kr个token;fi的值越大,表明sequence序列i上的负载越不均衡!
- 单个expert被激活使用的次数多了(也就是分配到的token数量),会被适当降权,才能真正实现load balance!
2、multi-token predict:传统transformer的decoder是逐个输出token,在一定程度上影响了reasoning的速度!deepseek用multi-token predict替代了传统的next token predict,速度明显提升!
10多年前,还没有transformer架构的时候,那个时候流行的是word2vector,目的是得到每个word的vector。模型训练时的方式之一就是skip gram:通过某个word预测该word的context,如果预测正确,说明context的vector是正确的,这也是word2vector的loss函数构造思路!比如sequence:“人工智能是未来的发展趋势”,这是一句完整的训练语料,现在可以通过mask的方式预测context,比如:xxxxx 未来 xxxxx,此时通过“未来”这个word预测其context,这样做的好处是能得到更多的word信息(当时效果还比cbow好),因为context周围有多个loss可以用来更新中间word的representation!;既然10多年前都能使用单个word预测多个context word,这个思路是不是也能借鉴一下,用到现在的transformer架构了?deepseek是这么干的:
- train阶段:
- main model:做常规地next token predict,比如输入t1得到t2,输入t1、t2得到t3;
- MTP module1:这就是最大的创新点了。main model在output这里输出的representation,是融合了前面n个token的信息,这部分信息被送往MTP module1,用来预测第n+1个token;举例:t1输入main model后,在output得到t2的representation;然后这个t2 representation被输入MTP module1,用来预测t3的representation;
- MTP module2:紧接上步,t3的representation输出这里,用来预测t4的representation;至此,在main model输入的t1,得到了t2、t3、t4这3个token的representation,那就得到了3个loss;
- 最后一步,用这3个loss做back proporgation,更新main model的参数!MTP效果好的原因之一可能是用于BP的loss数量较多,这些loss都来于后续的token,这无疑增加了当前token的context下文信息,避免了传统transform预测token时只关注next token的局部信息;说人话就是计算当前token的embedding layer会依赖后续多个token的信息,而不仅仅是后续1个token的信息,这直接增加了全局的信息,避免陷入局部狭隘的范围!
- MTP这个思路其实早在2024.4.30就由meta的团队发表了:https://arxiv.org/abs/2404.19737 ,好像并不是deepseek原创的;网传作者的思路来自婴幼儿学习语言:人在婴幼儿时期学习语言时,输入的token远少于大模型,但学习的速度和效果远比大模型好很多,这就促使作者想着通过改变训练方式提升大模型的学习效率
- reasoning推理阶段:只使用main model,MTP不用,避免极大增加计算量
3、大模型大模型,参数非常多,耗显存非常多,所以才称之为大模型!为了能在推理时减少显存消耗和计算量,产生了quant这门技术:把FP32、FP16位的浮点数转成int8甚至是int4,因为位数大幅减少,所以显存消耗也大幅降低!不过这么做的副作用也很明显:信息承载量也大幅减少,精度肯定也会降低!不同位数的表达精度和范围如下:
不同的位数有不同的精度,为避免信息丢失,在小数点后面位数达到一定程度时,需要使用更高精度的类型!比如FP8 (E4M3) 格式的表示范围:从 0.00390625 到 128;如果值小于0.00390625,比如只有0.001,并且要求保证精度,是要用FP16甚至FP32来存储的;所以在训练阶段,数据的存储类型会在FP32、FP16、FP8之间来回切换!比如做矩阵乘法W*A,因为涉及到每个维度相乘,w1*a1+w2*a2...... 这里w1和a1都很小,相乘之后更小了,如果用FP8 (E4M3)来保存乘积结果,大概率会发生下溢出,结果直接归0,所以这里有必要用FP16甚至FP32;但每个维度乘积结束后累计,结果就有可能大于0.00390625,这里再用FP8存储完全是可以的!deepseek这里使用FP8分块量化计算,为了避免精度下溢,采用的方式:
- 先对矩阵的行、列分块,每个块内部做quant
- 然后让块内部算乘积,比如a1*w1,这部分计算在tensor core中进行
- 最后块之间相加,再反量化,这部分在cuda core中计算!这样做让精度误差控制在每个块内部,避免误差在块之间叠加!
forward和backward中,各种不同精度的数据类型换着使用:从图示看,weight matrix 权重本身是矩阵乘法累加后的结果,数值可能比较大,不用过分担心下溢出的问题,所以用FP8完全适用;但涉及到梯度等精度要求高的地方,用的还是FP32和BF16;V3版本671B的参数,用FP8存储,理论上大约671GB; https://huggingface.co/deepseek-ai/DeepSeek-V3/tree/main 上的V3版本,权重文件大约700G左右,和理论值接近,误差很小;但是如果用FP16甚至FP32存储,至少翻2~3倍!这还只是存储,如果算上推理的其他消耗,不用FP8的话消耗更大!
看吧,有一半的weight都是FP8的格式;如果换成BF16,整个网络会达到TB级别!
4、parallel并行:这是个老生常谈的问题了,自从大数据时代就开始流行起来!大数据的并行原理比较简单:数据分散在不同的服务器,分布式查询和计算,最典型的代表就是map-reduce!具体到大模型这里情况就要复杂一些了:因为要forward计算loss和gradient,然后backward update neuron weight,所以涉及到data parallel、model paralle、multi-export parallel、流水线并行、混合并行等并行方式!
(1)data parallel:数据并行,把input x 均分到不同的GPU上,分别计算,然后合并结果!如下图所示,理论上讲,耗时缩短一半!
数据并行策略下,在反向传播back proporgation过程中,需要对各个设备上的梯度gradient进行 AllReduce(每张GPU卡的数据不一样,产生的loss也不一样,所以loss或gradient必须广播到其他所有GPU卡,才能让每张GPU卡的model保持一致),以确保各个设备上的模型始终保持一致。当数据集较大,模型较小时,由于反向过程中为同步梯度产生的通信代价较小,此时选择数据并行一般比较有优势,常见的视觉分类模型,如 ResNet50,比较适合采用数据并行。
(2)model parallel:如果模型过大,单张GPU卡的显存装不下时,就要模型并行策略了。每张GPU卡都有完整的input x,但是把相同layer的neuron network的weight均分到不同的GPU卡,分别计算forward和backward;模型并行的好处是,省去了多个设备之间的梯度 AllReduce;但是,由于每个设备都需要完整的数据输入,因此,input和hidden layer的数据会在多个设备之间进行广播,产生通信代价;下图中的最终得到的hiddenlayer 的 out (4×8) ,如果它作为下一层网络的输入,那么它就需要被广播发送到两个设备上;语言模型,如 BERT,常采用模型并行;
(3)流水线并行:同样是模型过大,单张GPU存不下,这里可以把模型的不同layer存放在不同的GPU卡,卡内部计算完成后把自己的结果输出到下一张卡,卡之间接龙,这种方式缺点明显:卡之间串行,效率低!
比如有8层、4张卡,这8层的分布如下:细心的网友已经发现了,0号和7号卡怎么同时都是layer0和layer7了?这样做的目的:真个流水线能同时运行两批次的数据!比如第1批从gpu0卡的layer0开始forward,第10批数据从gpu7卡的layer0开始forward,提升效率!
(4)mixed parallel:以 GPT-3 为例,以下是它训练时的设备并行方案:它首先被分为 64 个阶段,进行流水并行。每个阶段都运行在 6 台 DGX-A100 服务器主机上。在6台server之间,进行的是data parallel训练(数据分块散步到不同的server);每台server有 8 张 GPU 显卡,同一台server上的8张 GPU 显卡之间是进行model paralle训练(model weigh散布到不同的GPU卡)。
(5)deepseek设计了一种框架DualPipe,能让计算和通信并行,互相不同等对方结束!也就是说让commpute和communication的利用率都极大提升!,图示如下:
与传统的单向流水线(如 1F1B)不同,DualPipe 采用双向流水线设计,即同时从流水线的两端馈送微批次(micro-batch)。这种设计可以显著减少流水线气泡(Pipeline Bubble),提高 GPU 利用率。此外,DualPipe 将每个微批次进一步划分为更小的块(chunk),并对每个块的计算和通信进行精细的调度,实现计算和通信的高度重叠;原论文中给出了更详细的并行方式,如下图所示:
- forward侧:0号gpu和7号gpu并行发起计算,计算完成后各自向下一张卡输出hidden的中间结果,依此类推;
- backward:等forward所有的layer都计算完成后,又开始backward,也是卡卡之间接龙,层层递进,所以能看到示意图上有很多“斜着”的bacth编号数据,就是层层递进的效果!
从上面的图示看,采用这种双向流水线设计,左右互相开工,bubble明显减少了很多!这里举个通俗的例子:餐厅后厨做菜,需要分拣、择菜、洗菜、切菜、下锅、上桌等流程;为了提升效率,每个环节做完后立即进入下一个环节,比如现在做番茄炒蛋,需要10个番茄、20个鸡蛋,分拣环节每处理完一个番茄后立即往后进入择菜环节,而不是分拣足够20个番茄后再流转到下一步,这就大大减少了后续每个环节空置等待的时间!
参考:
1、https://api-docs.deepseek.com/zh-cn/news/news1226 https://github.com/deepseek-ai/DeepSeek-V3/blob/main/DeepSeek_V3.pdf
3、https://zhuanlan.zhihu.com/p/15037286337 【论文解读】MTP:让LLM一次性预测多个token
4、https://github.com/microsoft/AI-System/blob/main/Textbook/%E7%AC%AC6%E7%AB%A0-%E5%88%86%E5%B8%83%E5%BC%8F%E8%AE%AD%E7%BB%83%E7%AE%97%E6%B3%95%E4%B8%8E%E7%B3%BB%E7%BB%9F/6.2-%E5%88%86%E5%B8%83%E5%BC%8F%E8%AE%AD%E7%BB%83%E7%AE%97%E6%B3%95%E5%88%86%E7%B1%BB.md 并行方式
https://docs.oneflow.org/master/parallelism/01_introduction.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~