DeepSeek 笔记
架构
Multi-Head Latent Attention
主要作用是加速推理。
kv cache
在之前的 transformer 架构中,我们了解到 Decoder 实际是一个 auto-regressive 的模型,推理时为了实现 auto-regressive,每个 token 生成时都需要前面所有已经生成的 token 信息,这样就避免不了大量的重复计算。这里的重复计算主要是指 token 变为 key 和 value 的过程,也就是 Embedding 和 Positional Encoding(见Transformer 架构图)。
通过将这些已经生成过的 key 和 value 矩阵保存下来用于之后的推理,也就是 kv cache 的作用。cache 带来如下好处:
- 计算 $k_t$ 可以利用 $k_{t-1}$ 的矩阵,在其上做一次 concatenate 附加当前的 token 计算信息即可,如果 $k_{t-1}$ 已经被缓存也就是无需重复计算,该操作的效率将大大提升。$v_t$ 同理。
- 当推理时遇到相同 $k_t$ 时可以利用之前保存的 cache 而不用重复计算。$v_t$ 同理。
但是这种简单的解决方案无非是空间换时间,带来的问题就是消耗了宝贵的内存。
MLA 就是用来解决 MHA 中的 kv cache 过大的问题,原理很简单,原来 Multi-Head Attention 中是把 query、key、value 分别投影到各自的若干个低维空间中(见Multi-Head Attention),假设 $h_t$ 表示输入序列中的第 t 个 token,由 Attention 的理念可知对于 query,key,value 来说这个值是相同的,由 MHA 的定义可知它们的投影的参数($𝑊^{𝑄}$,$𝑊^{K}$,$𝑊^{V}$)不同,所以经过投影后的结果各不相同:
\[q_𝑡 = 𝑊^{𝑄}h_𝑡,\] \[k_𝑡 = 𝑊^{𝐾}h_𝑡,\] \[v_𝑡 = 𝑊^{𝑉}h_𝑡,\] \[[q_{𝑡,1};q_{𝑡,2};\dots;q_{𝑡,𝑛_ℎ}]=q_𝑡,\] \[[k_{𝑡,1};k_{𝑡,2};\dots;k_{𝑡,𝑛_ℎ}]=k_𝑡,\] \[[v_{𝑡,1};v_{𝑡,2};\dots;v_{𝑡,𝑛_ℎ}]=v_𝑡,\]对 MHA 来说每个 head (分维度)的输出计算方式如下(每个 head 使用 $i$ 下标表示, $j$ 下标表示当前的 $q_t$ 需要和 t 位置以及 t 之前所有位置(1到t-1)的 key 计算关系,这是 transformer 实现 auto-regressive 的基本机制):
\[\mathbf{o}_{t,i} = \sum_{j=1}^{t} \text{Softmax}_j \left( \frac{\mathbf{q}_{t,i}^T \mathbf{k}_{j,i}}{\sqrt{d_h}} \right) \mathbf{v}_{j,i},\]所有分维度的结果进行整合,得到第 t 个 token 的结果:
\[\mathbf{u}_t = W^O [\mathbf{o}_{t,1}; \mathbf{o}_{t,2}; \dots; \mathbf{o}_{t,n_h}].\]对 MLA 来说就是将其中的 $k_t$ 和 $v_t$ 分别再次进行了压缩,这样 key 和 value 作为 cache 时更小:
\[c^{𝐾𝑉}_𝑡 =𝑊^{𝐷𝐾𝑉}h_𝑡,(1)\] \[k^{𝐶}_𝑡 =𝑊^{𝑈𝐾}c^{𝐾𝑉}_𝑡,\] \[v^{𝐶}_𝑡 =𝑊^{𝑈V}c^{𝐾𝑉}_𝑡,\]这里的(1)是将 MHA 的映射和压缩进行了整合,也就是将$h_t$(也就是非 muti-head 的 attention 中的 $k_t$ 或 $v_t$)使用 $𝑊^{𝐷𝐾𝑉}$ 矩阵进行映射+压缩(就是降维,D 就是 down 降维)得到 $c_t$ ($c$ 表示 cache),计算权重时,原表达式中的每个 head(维度)中的 $k_t$ 和 $v_t$ 都可以使用每个 head 对应的解压参数($𝑊^{𝑈𝐾}$ 和 $𝑊^{𝑈V}$) 和 $c_t$ 运算得到(而且 $c_t$ 是 key 和 value 共用的,cache 的占用空间非常小,如上图所示,就是 1/16)。
我们先不考虑 Positional Encoding,在 cache 命中的情况下可以这样计算:
\[\mathbf{o}_{t,i} = \sum_{j=1}^{t} \text{Softmax}_j \left( \frac{\mathbf{q}_{t,i}^T 𝑊^{𝑈𝐾}_{i} c^{𝐾𝑉}_{j}}{\sqrt{d_h}} \right) 𝑊^{𝑈V}_{i} c^{𝐾𝑉}_{j}\]现在考虑 Positional Encoding,这么做就带来了一个问题,MHA 中的 $k_t$ 实际是带了位置信息的(下面的 $k^R_t$,$v_t$ 不带),因为 transformer 架构使用了相对位置,也就是说 $t_1$ 位置的 $k^R_{t_1}$ 会因为当前位置 t 的不同有不同结果,比如在 $t_2 = t_1 + 1$,$t_3 = t_1 + 2$,在 $t_2$ 位置和 $t_3$ 位置计算 $k^R_{t_1}$ 会不同:
\[k^{R}_{t} = RoPE(W^{KR}h_t)\] \[k_{t,i} = [k^{C}_{t,i};k^R_t]\]所以只能去 cache 位置无关的 $k^{C}{t,i}$,$k^{R}{t}$ 依然要每次去重新计算。
DeepSeek 还对 query 也进行了压缩,方式和对 key 和 value 的压缩相似,主要是用于降低训练时的内存消耗。
总结下:和 MHA 对比, kv cahce 保存的是 $c_t$ 而不是 $k_t$ 和 $v_t$,占用内存更小,但因为即使 cache 命中后也需要做额外的解压操作,性能不如 MHA。
DeepSeekMoE
分为 Shared Expert(常驻) 和 Router Expert(共享)
Router Expert 会被分为几组,通过 Router 进行负载均衡,将问题分配到合适的组中。这是为了使用多节点训练是避免通信消耗,一般一个节点存放一组专家,同一个问题放在一个节点上处理,避免了多节点间的通信。
多节点间的前向和反向传播(pipeline parallel 流水线并行):
数据就像流水线一样在不同节点间通过,每个节点处理完一个数据就可以立即处理下一个数据,就像工厂里的流水线一样,每道工序都饱和运转。
多节点间同 batch 反向传播时的梯度整合(Data parallel 数据并行):
Multi-Token Prediction
Token Prediction 的意图是通过当前 token 预测下一个(组) token,图中最左边输入为 $t_1$ 到 $t_4$ ,参考的结果为 $t_2$ 到 $t_5$,也就是用 $t_1$ 预测 $t_2$ ,以此类推。但仅有最左边部分无法做到让 $t_1$ 预测 $t_3$,换句话说,$t_1$ 无法为预测 $t_3$ 做出贡献。于是添加了后面几个层,这些层都是用前一个层的输出作为输入,所以 $t_1$ 的信息被不断传递,每个 Cross-Entropy Loss 都会被用于改进最左边的主模型的权重。
专家负载均衡
训练过程记录每个 Router Expert 的负载率,仅激活负载率较低且与问题较相关的专家。
强化学习
Reward Model
使用 DeepSeek v3 的一个检查点开始训练专用的 Reward Model ,用来给主模型进行强化训练。该模型训练时不仅包含 reward 结果还加入了得到 reward 结果的 chain-of-thought(思维链)。
Rule-Based RM
数学、代码等能使用明确规则验证的问题使用该 RM。
比如代码可以使用编译器编译后运行输出结果
Model-Based RM
自由形式但有明确答案的,使用该 RM 匹配答案。
没有明确答案的(如创意写作),RM 根据输入的问题和结果给予反馈。
agent 就是模拟人类解决现实问题的方式执行对应步骤解决问题,也就是说它也要能拆分问题、运用工具、查找资料、进行决策等