【Deepseek技术原理】第一篇:深度剖析和图解模型结构MLA

作者:罗辑

目录

MLA(Multi-head Latent Attention多头潜空间注意力)的缘起

MHA(Multi-Head Attention)

瓶颈:为什么降低KV Cache的大小如此重要?

MQA(Multi-Query Attention)

GQA(Grouped-Query Attention)

MLA(Multi-head Latent Attention)

晴天霹雳

算法的折中

神来之笔

彩蛋

模型架构

总结


摘要:最近DeepSeek在国际很火,甚至引起了美国总统的关注,一方面效果比肩闭源模型ChatGPT/O1,另外它开启了除openAI的pretrain+SFT+RLHF之外的新范式,同时训练和运行成本极低,引起国内讨论DeepSeek是否是国运级的创新。网上文章很多,但是真正能讲清楚原理的却少有。本博客的特点是结合图示,去深刻理解论文中的各种数学公式,极大的加深对模型原理的理解。

关于DeepSeek在网上文章很多,但是愿意讲清楚算法细节原理和设计思路的的却少有。本博客的特点是结合大量的示意图图示,去深刻理解算法的设计原理和设计思路,极大的加深对模型原理的理解。特别感谢苏建林老师的博客引导。

Deepseek技术创新分为3大块,分别为:模型结构、训练方法、工程优化3个方面,难以一次性讲解清楚。慢工出细活,本人计划分3个博客进行讲解。本次重点讲解deepseek模型结构方面的创新。deepseek模型结构方面创新有MLA和MOE结构优化。本次说MLA

MLA(Multi-head Latent Attention多头潜空间注意力)的缘起

一切要从transformer架构说起,transformer架构创新的使用3个向量,来表征一个词(token),通过向量之间的相关性,来表征两个词与词之间的相关性。当然,词语意思还会结合在句子中的语境,有多个含义。比如“苹果”,可以表示手机,也可以是水果。通过词与词的相关性,通过transformer block逐层调整,也能间接的表示整个句子的含义。

这3个向量就是Q、K、V向量。他们都是需要存储在GPU显存中的,非常占显存,显存是非常昂贵的。所以MLA目标就是想办法减少QKV向量的存储空间。

MHA(Multi-Head Attention)

要彻底理解MLA,就必须得从祖先MHA说起。MHA(Multi-Head Attention),也就是多头注意力,是LLM开山之作《Attention is all you need》所提出的一种Attention形式,是当前主流LLM的基础。

图示和对应的公式如下,这样就能看懂公式的含义了。简单起见,这里省略了Attention矩阵的缩放因子。h是多头注意力的头,d为输入x经过embeding之后的维度,常见的设置是

,对于LLAMA2-7b有

主流的自回归LLM所用的Causal 因果Attention,也就是预测出1个新token时,前面已经输入的token和计算的K和V不会变。

比如预测:“跟着罗辑学大模型”。

当预测到“学”时,前面的“跟着罗辑”已经之前算好了KV,当接下来预测“大”时,前面“跟着罗辑”的KV不会改变,因此,这部分KV结果我们可以缓存下来供后续生成调用,避免不必要的重复计算,这就是所谓的KV Cache。这样就能看懂论文中的第一个图了。斜杠表示需要缓存的向量。

瓶颈:为什么降低KV Cache的大小如此重要?

一般情况下LLM的推理都是在GPU上进行,单张GPU的显存是有限的,一部分我们要用来存放模型的参数和前向计算的激活值,这部分依赖于模型的体量,选定模型后它就是个常数;另外一部分我们要用来存放模型的KV Cache,这部分不仅依赖于模型的体量,还依赖于模型的输入长度,也就是在推理过程中是动态增长的,当Context长度足够长时,它的大小就会占主导地位,可能超出一张卡甚至一台机(8张卡)的总显存量。

在GPU上部署模型的原则是:能一张显卡部署的,就不要跨多张卡;能一台服务器机器能部署的,就不要跨多台机。这是因为“卡内通信带宽 > 卡间通信带宽 > 机间通信带宽”,由于“木桶效应”,模型部署时跨的设备越多,受设备间通信带宽的的“拖累”就越大,模型推理就越慢。所以美国限制高端英伟达GPU出口中国,除了限制GPU运算量,卡的通信带宽也是限制得死死的。

MQA(Multi-Query Attention)

MQA,是减少KV Cache的一次非常朴素的尝试,首次提出自2019年《Fast Transformer Decoding: One Write-Head is All You Need》。思路很简单,直接让所有Attention Head共享同一个K、V。因为KV本来也是可训练矩阵Wk、Wv通过和X相乘得到的,相当于神经元的个数,可多可少,因此相当于减少了神经元的个数,模型的正向传播和反向传播还是能正常走得通。

这里重点解释一下图示表示的是8个注意力头head,这里的QKV都是对应一个token的,部分人认为是输入x不同的token共享一个K/V是理解错误的。不同的token,KV是不一样的,仅仅是同一个token的不同注意力头共享一个KV。因此MQA直接将KV Cache减少到了原来的1/h,这是非常可观的,单从节省显存角度看已经是天花板了。

使用MQA的模型包括PaLM、StarCoder、Gemini等。效果方面,目前看来大部分任务的损失都比较有限,且MQA的支持者相信这部分损失可以通过进一步训练来弥补回。此外,注意到MQA由于共享了K、V,将会导致Attention的参数量减少了将近一半,而为了模型总参数量的不变,通常会相应地增大FFN/GLU的规模,这也能弥补一部分效果损失。本身原理没有问题。

GQA(Grouped-Query Attention)

当然有人担心MQA对KV Cache的压缩太严重,以至于会影响模型的学习效率以及最终效果。事情做太绝,未来不相见。

为此,一个MHA与MQA之间的过渡版本GQA(Grouped-Query Attention)应运而生,出自23年论文《GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints》。

整体思想就是:不要所有的head共享一个KV,分个组,比如第1-2个head共享一个KV,第3-4个head共享一个KV。这样就形成了如下的图。

DeepSeek-V1用的就是GQA,还包括llama2/3-70B、StarCoder2、Yi、ChatGLM2、ChatGLM3等,比使用MQA的模型更多。

MLA(Multi-head Latent Attention)

有了MHA、MQA、GQA的铺垫,理解MLA(Multi-head Latent Attention)就相对容易了。

可以从两个方面去理解MLA,第一,不管是GQA还是MQA,都需要缓存K、V两个值,两个值不一样,能不能进一步压缩,只缓存一个值,然后通过两个矩阵反向生成两个不同的向量,当成KV,这样就仅仅缓存一个字就好了。比如以下公式,缓存ci就好了。

可以通过矩阵Wk和Wv生成kv,其中的Wk和Wv作为模型参数的一部分。有了这样想法,之前MHA的公式就可以改写一下,变成如下的公式:

ci怎么来的?通过输入X乘以一个矩阵Wc变换得来的。

于是就能深刻理解论文里面画的这个示意图。

另一个方面,如果把MQA中的KV,强制让它合并成一个C向量,理论上也是可行的,貌似就变成和MLA大同小异的模型结构了。但是在GPU计算时,都是通过矩阵批量运算的,为了保持运算的矩阵维度,我们会把C向量复制多份,可以理解为MLA实际上是把C向量单纯复制动作,变成了两个矩阵的投影,从而等同增加了神经元个数,提升了模型容量。

图示为MQA共用KV时计算时的复制示意图。

晴天霹雳

一切似乎都很完美,看上去一个又好又省的理想模型设计就要出炉了。但我们一直没有考虑token的位置信息,考虑文本的位置编码,用的最多的是苏神发明的RoPE(旋转位置编码),基本上绝大部分开源LLM都用它,也充分证明了其有效性。但是很遗憾,刚开始设想那种MLA并不兼容RoPE,准确来说MLA不是不能加RoPE,而是加了RoPE之后无法用恒等变换技巧来减少KV Cache。为此,DeepSeek团队还特地邀请苏神去讨论过这个问题寻求建议。当时苏神坦言他实际上也没能提出什么有效的建议,最简单的方式是放弃RoPE,换用其他基于Attention Bias的位置编码,如ALIBI,但DeepSeek的实验显示它明显不如RoPE好。

算法的折中

算法似乎走进了死胡同,但是deepseek采用了一种折中的方法,那就是向量的前一半使用不带RoPE,后一半带RoPE,折中的方式也能减少KV Cache。

于是才有了论文公式中的这个等式,

同理,Q也是同样的操作。

以下是Llama运用RoPE的架构图,可以作为对比。RoPE是作用在Q和K中添加。

神来之笔

算法似乎设计完了,缓存一个ci给KV使用就好了,但是deepseek V3最终采用的模型结构貌似又有不同,颇有种“说好一起穿开裆裤,你却包上了尿不湿”的感觉,论文中还没有特别的解释。

是什么呢?就是MLA的最终版本,还将Q的输入也改为了低秩投影形式,这与减少KV Cache无关。单纯是为了降低过度压缩向量维度带来的对模型伤害。在deepseek公布的模型配置文件可看到。"hidden_size": 7168, "num_attention_heads": 128,如果按照MHA的设定,我们会缓存128个维度为7168/128=56维的向量,等同拼在一起就是缓存了个7168维的长向量,但是deepseek最后将KV缓存的ci向量维度设为512,也就是参数"kv_lora_rank": 512,相当于把缓存7168维的向量降低到了缓存512维。一般情况下,我们会设计q的输入向量维度和kv的输入向量维度相同,也是512维,但是Q向量我们并不需要缓存啊,为何不把输入搞大点,不需要将7168维的向量也降低到了缓存512维,deepseek设置的"q_lora_rank": 1536,也就是1536维是512维的3倍。这里没什么算法将就,单纯人为设定去平衡模型参数大小和训练高效性而设定。只要最终经过矩阵映射后的qkv维度一致就行。也即是说,q的投影矩阵会比kv的投影矩阵大3倍。

分拆成公式,q就

如下:

而KV如下:

也可以认为这个是一个神来之笔,不需要缓存的向量,可以搞大点,大不了增加一点计算量,但是并不增加显存占用,可以提升模型容量和能力。

彩蛋

还有个小细节一般人注意不到,就是K矩阵,说好后半段运用RoPE,咋公式还变了呢?仔细看论文。对比看K和Q的计算公式。

对比Q,可以看到,q用的都是ct,而K不是。

对比写成更直观的公式更明显。

K的后半部分,也就是带RoPE的部分,其输入还是xi而不是ci。这里苏神都不知道原因。从算法理论上这里用ci也没有问题,可能是deepseek试验后发现输入xi更好?有机会更deepseek交流询问一下。

模型架构

通过这样的讲解,就能看懂论文中的如下模型结构示意图。对应的计算公式也能更模型结构对应上。

用一个模型实例去看模型参数量更直观。如下:

能理清楚此图中的维度时,就表示看懂了。

总结

洋洋洒洒已经3千字,本文详细描述了多头注意力的演变历程,从MHA向MQA、GQA,最终到MLA的变化理念,详细展开MLA的介绍。在本文中,MLA被视为GQA/MQA的一般化,它用投影矩阵的方式替代了MQA的分割、重复,并引入了一个恒等变换技巧来可以进一步压缩KV Cache,同时采用了一种向量混合方法来兼容RoPE。总的来说,MLA称得上是一种非常实用、高效的注意力变体,但整体还是transform框架,单纯的MLA并非是从0到1打破传统的国运级的创新。

下回讲模型MTP多词预测原理和设计思路。请持续关注我。

参考文献

  1. 缓存与效果的极限拉扯:从MHA、MQA、GQA到MLA - 科学空间|Scientific Spaces
  2. https://zhuanlan.zhihu.com/p/720106482
  3. https://zhuanlan.zhihu.com/p/720718887
  4. 【LLM】(KV cache优化)MHA、MQA、GQA、MLA、YOCO机制的区别_mla llm-优快云博客
  5. https://zhuanlan.zhihu.com/p/18071594122
  6. https://dataturbo.medium.com/deepseek-technical-analysis-2-mla-74bdb87d4ad2
### DeepSeek 文本生成图像功能介绍 DeepSeek Janus 系列是一款专为图像理解生成设计的统一多模态人工智能工具[^1]。该平台支持通过文本描述自动生成对应的高质量图像。 #### 准备工作 为了启动并操作此特性,需先安装必要的软件包以及下载预训练好的模型权重文件。具体来说: - 安装 Python pip 工具链; - 利用 `pip install` 命令来获取官方推荐的支持库集合; - 下载由开发者提供的最新版本的 DeepSeek 模型参数集; ```bash # 更新pip至最新版 python -m pip install --upgrade pip setuptools wheel # 安装所需依赖项 pip install deepseek-toolkit ``` #### 创建环境变量配置 设置好上述前提条件之后,还需定义一些重要的环境变量以便于后续调用 API 接口时能够顺利访问服务器资源服务端点地址。 ```bash export DEEPSEEK_API_KEY="your_api_key_here" export DEEPSEEK_SERVER_URL="https://api.deepseek.com/v1/" ``` #### 实现文本转图片流程 下面是一份简单的 Python 脚本示范如何利用 RESTful Web Service 形式的接口实现从输入一段文字描述到输出一张相应视觉化成果的过程。 ```python import requests from PIL import Image from io import BytesIO def generate_image_from_text(text_description, api_url, headers): payload = {"prompt": text_description} response = requests.post(api_url + "generate", json=payload, headers=headers) if response.status_code == 200: img_data = BytesIO(response.content) image = Image.open(img_data).convert('RGB') return image else: raise Exception(f"Error generating image: {response.text}") if __name__ == "__main__": description = input("请输入想要转换成图画的文字说明:") server_endpoint = os.getenv("DEEPSEEK_SERVER_URL") or "" auth_header = {'Authorization': f'Bearer {os.getenv("DEEPSEEK_API_KEY")}'} try: generated_img = generate_image_from_text(description, server_endpoint, auth_header) output_filename = 'output.png' generated_img.save(output_filename) print(f"成功保存生成的图片至{output_filename}") except Exception as e: print(e) ``` 这段代码展示了怎样发送 HTTP POST 请求给远程服务完成一次完整的创作周期——即接收用户指令、解析请求体中的 JSON 数据结构、执行内部算法处理逻辑直至最终返回二进制流形式的结果对象供客户端展示或进一步加工使用[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值