目的
DeepSeek V2論文のMulti-head Latent Attention (MLA)における、下記表現を理解するために「数式で」整理をすることが目的の記事です。
主に、「どの重みを」「どのように吸収するのか」を「数式で」整理します。
(そのうち詳細な解説付きの記事にするかも)
In addition, during inference, since W^{UK} can be absorbed into W^Q , and W^{UV} can be absorbed into W^{O} , we even do not need to compute keys and values out for attention.
本記事は、DeepSeek V2の論文中に記載されているMulti-head Latent Attention (MLA)について理解されていることが前提になります。
https://arxiv.org/abs/2405.04434
なお、RoPE導入後も成立するという主張に関しては、「今回は」取り扱いません。
ただ、基本的な主張は変わらないです。そのうち記事にするかもです。
とはいえ、論文中では行間がすごく広くなっているMLAに関して、詳細に記述しているので、誰かの理解の助けになれば嬉しいです。
あと、今のAIでは書けない記事を書きたかった。
(o1 proとかに解説を依頼したとしても、ここまで詳細には解説してくれないはずです)
なお、最近流行りのDeepSeek-R1に関しても論文解説記事を書いています。
本記事とは異なり、理論に詳しくない方が読んで面白い!と思っていただけるように記事を書いたつもりなので、ぜひ読んでいただけると嬉しいです。
https://zenn.dev/asap/articles/34237ad87f8511
数式でMLAの整理
Scaled Dot-Product Attentionは下記で表現されます。
\text{Attention}_{(i)}(Q_{(i)}, K_{(i)}, V_{(i)}) = \text{softmax} \left( \frac{Q_{(i)}K_{(i)}^\top}{\sqrt{d_h}} \right) V_{(i)}
上記を前提に、MLAの式展開を考察していきます。
記号の導入
MLAは主にKVキャッシュとクエリの圧縮を目的にしています。
したがって、圧縮されたKV表現と圧縮されたクエリ表現を取り扱うために、記号を導入していきます。
圧縮KV表現とKV
ここでは、圧縮KV表現と、元のKVについて記載していきます。
MLAにおいてKVの圧縮表現\mathbf{C}^{KV} \in \mathbb{R}^{T \times d_c} を導入します。
T は全体のtoken数、d_c は圧縮KV表現の次元数(論文では512次元)です。
この圧縮表現は下記のより作成されます。
(各tokenごとに下記の式変形が適用されます。つまりtoken数分だけ並列処理されます)
\begin{equation}
\mathbf{c}_t^{KV} = W^{DKV} \mathbf{h_t}
\end{equation}
\begin{equation}
\mathbf{C}^{KV} =
\begin{bmatrix}
(\mathbf{c}_1^{KV})^T \\
(\mathbf{c}_2^{KV})^T \\
\vdots \\
(\mathbf{c}_T^{KV})^T
\end{bmatrix}
\end{equation}
ただしt は、あるt token目の特徴量であることを示します。
また、\mathbf{h_t} \in \mathbb{R}^{d} は、前の層の、あるt token目での出力で、d は前の層の出力次元数です(論文中では5120次元)
また、W^{DKV} \in \mathbb{R}^{d_c \times d} はdown-projection行列です。
\mathbf{c}_t^{KV} \in \mathbb{R}^{d_c} は、あるt token目での、KVの圧縮表現です。
そして、\mathbf{C}^{KV} \in \mathbb{R}^{T \times d_c} は\mathbf{c}_t^{KV} を全てのtokenでまとめた行列になります。
KVの圧縮表現\mathbf{C}^{KV} を利用すると、キーとバリューを復元できます。
\begin{equation}
\mathbf{K} = \left(W^{UK} (\mathbf{C}^{KV})^T\right)^T
\end{equation}
\begin{equation}
\mathbf{V} = \left(W^{UV} (\mathbf{C}^{KV})^T\right)^T
\end{equation}
W^{UK},W^{UV} \in \mathbb{R}^{d_hn_h \times d_c} はup-projection行列です。
圧縮されたKVから、マルチヘッドの全てのキー、バリュー表現を復元します。
d_h は1ヘッドあたりの次元数(論文では128次元)、n_h はマルチヘッドのヘッド数(論文では128個)
\mathbf{K}, \mathbf{V} \in \mathbb{R}^{T \times d_hn_h} は、それぞれ圧縮されていないキー、バリュー表現です。
圧縮クエリ表現とQ
続いて、クエリに関しても学習時のパラメータ削除のために圧縮します。
\begin{equation}
\mathbf{c}_t^{Q} = W^{DQ} \mathbf{h_t}
\end{equation}
\begin{equation}
\mathbf{C}^{Q} =
\begin{bmatrix}
(\mathbf{c}_1^{Q})^T \\
(\mathbf{c}_2^{Q})^T \\
\vdots \\
(\mathbf{c}_T^{Q})^T
\end{bmatrix}
\end{equation}
また、W^{DQ} \in \mathbb{R}^{d_c' \times d} はdown-projection行列です。
d_c' は圧縮クエリ表現の次元数(論文では1536次元)
\mathbf{c}_t^{Q} \in \mathbb{R}^{d_c'} は、あるt token目での、クエリの圧縮表現です。
\mathbf{C}^{Q} \in \mathbb{R}^{T \times d_c'} は、token全体でのクエリの圧縮表現になります。
このクエリの圧縮表現\mathbf{C}^{Q} を利用すると、元のクエリを復元できます。
\begin{equation}
\mathbf{Q} = \left(W^{UQ} (\mathbf{C}^{Q})^T\right)^T
\end{equation}
W^{UQ} \in \mathbb{R}^{d_hn_h \times d_c'} はup-projection行列です。
クエリの圧縮表現から、マルチヘッドの全てのクエリ表現を復元します。
\mathbf{Q} \in \mathbb{R}^{T \times d_hn_h} は、それぞれ圧縮されていないクエリ表現です。
通常のMHA(Multi-Head Attention)のQKVと、 MLAの違い
通常のMHAにおいて、QKVはそれぞれ下記で表現されます。
MHAでのキー表現
\begin{equation}
\mathbf{k}_t = W^{K} \mathbf{h_t}
\end{equation}
\begin{equation}
\mathbf{K} =
\begin{bmatrix}
(\mathbf{k}_1)^T \\
(\mathbf{k}_2)^T \\
\vdots \\
(\mathbf{k}_T)^T
\end{bmatrix}
\end{equation}
したがって、
\begin{equation}
\mathbf{K} =
\begin{bmatrix}
(W^{K} \mathbf{h_1})^T \\
(W^{K} \mathbf{h_2})^T \\
\vdots \\
(W^{K} \mathbf{h_T})^T
\end{bmatrix}
\end{equation}
なお、\mathbf{k}_t \in \mathbb{R}^{d_hn_h} 、W^{K} \in \mathbb{R}^{d_hn_h \times d} 、\mathbf{K} \in \mathbb{R}^{T \times d_hn_h}
以下はほぼ同じなので、隠しておきます。興味あればご覧ください。
MHAでのクエリ表現
MHAでのクエリ表現
\begin{equation}
\mathbf{q}_t = W^{Q} \mathbf{h_t}
\end{equation}
\begin{equation}
\mathbf{Q} =
\begin{bmatrix}
(\mathbf{q}_1)^T \\
(\mathbf{q}_2)^T \\
\vdots \\
(\mathbf{q}_T)^T
\end{bmatrix}
\end{equation}
したがって、
\begin{equation}
\mathbf{Q} =
\begin{bmatrix}
(W^{Q} \mathbf{h_1})^T \\
(W^{Q} \mathbf{h_2})^T \\
\vdots \\
(W^{Q} \mathbf{h_T})^T
\end{bmatrix}
\end{equation}
なお、\mathbf{q}_t \in \mathbb{R}^{d_hn_h} 、W^{Q} \in \mathbb{R}^{d_hn_h \times d} 、\mathbf{Q} \in \mathbb{R}^{T \times d_hn_h}
MHAでのバリュー表現
MHAでのバリュー表現
\begin{equation}
\mathbf{v}_t = W^{V} \mathbf{h_t}
\end{equation}
\begin{equation}
\mathbf{V} =
\begin{bmatrix}
(\mathbf{v}_1)^T \\
(\mathbf{v}_2)^T \\
\vdots \\
(\mathbf{v}_T)^T
\end{bmatrix}
\end{equation}
したがって、
\begin{equation}
\mathbf{V} =
\begin{bmatrix}
(W^{V} \mathbf{h_1})^T \\
(W^{V} \mathbf{h_2})^T \\
\vdots \\
(W^{V} \mathbf{h_T})^T
\end{bmatrix}
\end{equation}
なお、\mathbf{v}_t \in \mathbb{R}^{d_hn_h} 、W^{V} \in \mathbb{R}^{d_hn_h \times d} 、\mathbf{V} \in \mathbb{R}^{T \times d_hn_h}
見るとわかるように、それぞれほぼ同じように構成されます。
なので、キー表現に着目して、MHAとMLAの違いを見ていきます。
MLAにおけるキー表現
重要なのは、「圧縮KV表現とKV」の章で記載した下記の3式です。
\mathbf{c}_t^{KV} = W^{DKV} \mathbf{h_t}
\mathbf{C}^{KV} =
\begin{bmatrix}
(\mathbf{c}_1^{KV})^T \\
(\mathbf{c}_2^{KV})^T \\
\vdots \\
(\mathbf{c}_T^{KV})^T
\end{bmatrix}
\mathbf{K} = \left(W^{UK} (\mathbf{C}^{KV})^T\right)^T
それぞれ式番号(1)(2)(3)に対応しています。
これをまとめていきます。
式(2)(3)から
\begin{equation}
\mathbf{K} = \left(W^{UK} [\mathbf{c}_1^{KV}, \mathbf{c}_2^{KV}, \cdots ,\mathbf{c}_T^{KV}]\right)^T
\end{equation}
式(1)(11)から
\mathbf{K} = \left(W^{UK} [W^{DKV} \mathbf{h_1} ,W^{DKV} \mathbf{h_2} ,\cdots ,W^{DKV} \mathbf{h_T}]\right)^T
= [W^{UK}W^{DKV} \mathbf{h_1} ,W^{UK}W^{DKV} \mathbf{h_2} ,\cdots ,W^{UK}W^{DKV} \mathbf{h_T}]^T
となるため、これまでの書き方と表記を揃えると
\begin{equation}
\mathbf{K} =
\begin{bmatrix}
(W^{UK}W^{DKV} \mathbf{h_1})^T \\
(W^{UK}W^{DKV} \mathbf{h_2})^T \\
\vdots \\
(W^{UK}W^{DKV} \mathbf{h_T})^T
\end{bmatrix}
\end{equation}
なお、念のため記載すると、\mathbf{K} \in \mathbb{R}^{T \times d_hn_h} 、W^{UK}W^{DKV} \mathbf{h_t} \in \mathbb{R}^{d_hn_h} です。
ちなみに、MHAのキー表現は下記でした。
\begin{equation}
\mathbf{K} =
\begin{bmatrix}
(W^{K} \mathbf{h_1})^T \\
(W^{K} \mathbf{h_2})^T \\
\vdots \\
(W^{K} \mathbf{h_T})^T
\end{bmatrix}
\end{equation}
したがって、適切に学習がなされた場合、式(12)(13)から、W^{K} = W^{UK}W^{DKV} となります。
このとき、W^{K} の行列のランクやW^{UK} やW^{DKV} の行列の自由度d_c によって、どの程度劣化するかが決まります。
万が一、W^{K} の行列のランクが、W^{UK} やW^{DKV} の行列の自由度d_c よりも小さい場合は、適切に学習がなされることにより、劣化が生じません。
ついでに、W^{K} \in \mathbb{R}^{d_hn_h \times d} は、パラメータ数がd_hn_h \times d なのに対して、W^{UK} \in \mathbb{R}^{d_hn_h \times d_c} とW^{DKV} \in \mathbb{R}^{d_c \times d} に分割した場合、パラメータ数がd_hn_h \times d_c + d_c \times d となります。
ここで、d はd_c と比較して非常に大きいため、前者のパラメータよりも、少ないパラメータにて近似が可能です。
深層学習の世界において、しばしば学習可能なパラメータ数を少なくして近似することで、精度の向上が見られたことがあり(画像処理でいうCNNの世界)、今回MHAよりもMLAが性能が良い結果になったのは、その観点も効いているのかもしれません。
be absorbedってどういうこと??
さて、実際にここから、論文中の下記の内容が成立することを確かめます。
In addition, during inference, since W^{UK} can be absorbed into W^Q , and W^{UV} can be absorbed into W^{O} , we even do not need to compute keys and values out for attention.
W^{UK}の吸収
単一ヘッドを前提とした場合、あるヘッドi に着目すると、Attentionは下記のように表すことができます。
\begin{equation}
\text{SDPA}_{(i)}(Q_{(i)}, K_{(i)}, V_{(i)}) = \text{softmax} \left( \frac{Q_{(i)}K_{(i)}^\top}{\sqrt{d_h}} \right) V_{(i)}
\end{equation}
なお、\text{SDPA}_{(i)} \in \mathbb{R}^{T \times d_h} です。
SDPAはScaled Dot-Product Attentionのことです。
今回の
W^{UK} can be absorbed into W^Q
の部分は、Q_{(i)}K_{(i)}^\top に関連する部分なので、その部分に着目します。
ただし、ヘッドごとに見ていくため、これまでの記号は分割されて下記のようになります。
Q_{(i)} \in \mathbb{R}^{T \times d_h} 、K_{(i)} \in \mathbb{R}^{T \times d_h} 、W^{UQ}_{(i)} \in \mathbb{R}^{d_h \times d_c'} 、W^{UK}_{(i)} \in \mathbb{R}^{d_h \times d_c}
下記は、ヘッドごとに共通で利用するため変わりません。
\mathbf{C}^{Q} \in \mathbb{R}^{T \times d_c'} 、\mathbf{C}^{KV} \in \mathbb{R}^{T \times d_c}
さて、MLAにおいてQ_{(i)}K_{(i)}^\top は下記のように書き下せます。
Q_{(i)}K_{(i)}^\top = \left(W^{UQ}_{(i)} (\mathbf{C}^{Q})^T\right)^T W^{UK}_{(i)} (\mathbf{C}^{KV})^T
= \mathbf{C}^{Q} (W^{UQ}_{(i)})^T W^{UK}_{(i)} (\mathbf{C}^{KV})^T
\begin{equation}
= \left((W^{UK}_{(i)})^T W^{UQ}_{(i)} (\mathbf{C}^{Q})^T \right)^T (\mathbf{C}^{KV})^T
\end{equation}
上記において、W^{UK}_{(i)} \in \mathbb{R}^{d_h \times d_c} 、W^{UQ}_{(i)} \in \mathbb{R}^{d_h \times d_c'} は学習時は分割して学習された重み行列になります。
そこで、推論時には、これらの重み行列から事前に、(W^{UK}_{(i)})^T W^{UQ}_{(i)} \in \mathbb{R}^{d_c \times d_c'} を計算しておき、新しい一つの重み行列にすることもできます。
そうすると、圧縮KV表現\mathbf{C}^{KV} と圧縮クエリ表現\mathbf{C}^{Q} から明示的にキーとクエリを復元することなく、一撃でQ_{(i)}K_{(i)}^\top を計算できます。
これが推論時に、「W^{UK} はW^{UQ} (W^{Q} )に吸収される」という言葉の意味だと思われます。
W^{UV}の吸収
今回の、
W^{UV} can be absorbed into W^{O}
の部分は、Attentionマップを作成し、バリューとの行列積を計算した後、各Headごとの出力をW^{O} で一つにまとめる部分です。この部分は少し入り組んでいるので、少し整理していきます。
まず、Attentionマップの部分をAtn_{(i)} とまとめます。
具体的には下記です。
\begin{equation}
\text{Atn}_{(i)}(Q_{(i)}, K_{(i)}) = \text{softmax} \left( \frac{Q_{(i)}K_{(i)}^\top}{\sqrt{d_h}} \right)
\end{equation}
ここで、\text{Atn}_{(i)} \in \mathbb{R}^{T \times T} です。
すると、Scaled Dot-Product Attentionは下記のように表せます。
\begin{equation}
\text{SDPA}_{(i)} = \text{Atn}_{(i)} V_{(i)}
\end{equation}
その場合、MHA(Multi-Head Attention)は、下記のように表現されます。
\begin{equation}
\text{MHA}(Q, K, V) = W^{O}
\begin{bmatrix}
\text{SDPA}_{(1)}^T \\
\text{SDPA}_{(2)}^T \\
\vdots \\
\text{SDPA}_{(n_h)}^T
\end{bmatrix}
\end{equation}
ここで、W^{O} \in \mathbb{R}^{d \times d_hn_h} 、{SDPA}_{(i)}^T \in \mathbb{R}^{d_h \times T} であり、
\begin{equation}
\begin{bmatrix}
\text{SDPA}_{(1)}^T \\
\text{SDPA}_{(2)}^T \\
\vdots \\
\text{SDPA}_{(n_h)}^T
\end{bmatrix}
\in \mathbb{R}^{d_hn_h \times T}
\end{equation}
となります。
ここで、ちょっとトリッキーですが、W^{O} を下記のように分解していきます。
\begin{equation}
\text{MHA}(Q, K, V) = [W^{O}_{(1)}, W^{O}_{(2)}, \cdots, W^{O}_{(n_h)}]
\begin{bmatrix}
\text{SDPA}_{(1)}^T \\
\text{SDPA}_{(2)}^T \\
\vdots \\
\text{SDPA}_{(n_h)}^T
\end{bmatrix}
\end{equation}
このとき、W^{O}_{(i)} \in \mathbb{R}^{d \times d_h} となります。
このとき、行列積の性質として下記が成立します。
\begin{equation}
\begin{aligned}
\text{MHA}(Q, K, V) &=[W^{O}_{(1)}, W^{O}_{(2)}, \cdots, W^{O}_{(n_h)}]
\begin{bmatrix}
\text{SDPA}_{(1)}^T \\
\text{SDPA}_{(2)}^T \\
\vdots \\
\text{SDPA}_{(n_h)}^T
\end{bmatrix} \\
&= W^{O}_{(1)} \text{SDPA}_{(1)}^T \\
&\quad + W^{O}_{(2)} \text{SDPA}_{(2)}^T \\
&\quad + \cdots + W^{O}_{(n_h)} \text{SDPA}_{(n_h)}^T
\end{aligned}
\end{equation}
式(17)を展開して、下記のように書きます。
\begin{equation}
\begin{aligned}
\text{MHA}(Q, K, V) &= W^{O}_{(1)} \left(\text{Atn}_{(1)}V_{(1)}\right)^T \\
&\quad + W^{O}_{(2)} \left(\text{Atn}_{(2)}V_{(2)}\right)^T \\
&\quad + \cdots + W^{O}_{(n_h)} \left(\text{Atn}_{(n_h)} V_{(n_h)}\right)^T
\end{aligned}
\end{equation}
このとき、式(4)から下記にように式変形できます。
!
式(4)は下記です。
\mathbf{V} = \left(W^{UV} (\mathbf{C}^{KV})^T\right)^T
なお、\mathbf{C}^{KV} \in \mathbb{R}^{T \times d_c} 、W^{UV} \in \mathbb{R}^{d_hn_h \times d_c} 、\mathbf{V} \in \mathbb{R}^{T \times d_hn_h}
すると、各Headごとに分解すると下記にようになります。
\mathbf{V}_{(i)} = \left(W^{UV}_{(i)} (\mathbf{C}^{KV})^T\right)^T
= \mathbf{C}^{KV} (W^{UV}_{(i)})^T
なお、\mathbf{C}^{KV} \in \mathbb{R}^{T \times d_c} 、W^{UV}_{(i)} \in \mathbb{R}^{d_h \times d_c} 、\mathbf{V}_{(i)} \in \mathbb{R}^{T \times d_h}
\begin{aligned}
\text{MHA}(Q, K, V) &= W^{O}_{(1)} \left(\text{Atn}_{(1)}\mathbf{C}^{KV} (W^{UV}_{(1)})^T \right)^T \\
&\quad + W^{O}_{(2)} \left(\text{Atn}_{(2)}\mathbf{C}^{KV} (W^{UV}_{(2)})^T \right)^T \\
&\quad + \cdots + W^{O}_{(n_h)} \left(\text{Atn}_{(n_h)} \mathbf{C}^{KV} (W^{UV}_{(n_h)})^T \right)^T
\end{aligned}
\begin{equation}
\begin{aligned}
\text{MHA}(Q, K, V) &= W^{O}_{(1)} W^{UV}_{(1)} (\mathbf{C}^{KV})^T \text{Atn}_{(1)}^T \\
&\quad + W^{O}_{(2)} W^{UV}_{(2)} (\mathbf{C}^{KV})^T \text{Atn}_{(2)}^T \\
&\quad + \cdots + W^{O}_{(n_h)} W^{UV}_{(n_h)} (\mathbf{C}^{KV})^T \text{Atn}_{(n_h)}^T \\
\end{aligned}
\end{equation}
上記において、W^{O}_{(i)} \in \mathbb{R}^{d \times d_h} 、W^{UV}_{(i)} \in \mathbb{R}^{d_h \times d_c} は学習時は分割して学習された重み行列になります。
そこで、推論時には、これらの重み行列から事前に、W^{O}_{(i)} W^{UV}_{(i)} \in \mathbb{R}^{d \times d_c} を計算しておき、新しい一つの重み行列にすることもできます。
そうすると、圧縮KV表現\mathbf{C}^{KV} から明示的にバリューを復元することなく、一撃で\text{MHA}(Q, K, V) を計算できます。
これが推論時に、「W^{UV} はW^{O} に吸収される」という言葉の意味だと思われます。
まとめて
「W^{UK}の吸収」のまとめ
「W^{UK} の吸収」の章にて、式(15)から下記の式が成立しています。
Q_{(i)}K_{(i)}^\top = \left((W^{Q}_{(i)})' (\mathbf{C}^{Q})^T \right)^T (\mathbf{C}^{KV})^T
ただし、まとめた式(吸収した式)に、(W^{Q}_{(i)})' という名前をつけています。
(W^{Q}_{(i)})' は下記で定義されています。
(W^{Q}_{(i)})' = (W^{UK}_{(i)})^T W^{UQ}_{(i)}
ただし、(W^{Q}_{(i)})' \in \mathbb{R}^{d_c \times d_c'} 。
また、W^{UK}_{(i)} \in \mathbb{R}^{d_h \times d_c} 、W^{UQ}_{(i)} \in \mathbb{R}^{d_h \times d_c'}
その上で、Scaled Dot-Product Attentionにおける、Attentionマップの式は下記のようになります。
\text{Atn}_{(i)}(Q_{(i)}, K_{(i)}) = \text{softmax} \left( \frac{\left((W^{Q}_{(i)})' (\mathbf{C}^{Q})^T \right)^T (\mathbf{C}^{KV})^T}{\sqrt{d_h}} \right)
「W^{UV}の吸収」のまとめ
「W^{UV} の吸収」の章にて、式(23)から下記が成立しています。
\begin{aligned}
\text{MHA}(Q, K, V) &= (W^{O}_{(1)})' (\mathbf{C}^{KV})^T \text{Atn}_{(1)}^T \\
&\quad + (W^{O}_{(2)})' (\mathbf{C}^{KV})^T \text{Atn}_{(2)}^T \\
&\quad + \cdots + (W^{O}_{(n_h)})' (\mathbf{C}^{KV})^T \text{Atn}_{(n_h)}^T \\
\end{aligned}
ただし、まとめた式(吸収した式)に、(W^{O}_{(i)})' という名前をつけています。
(W^{O}_{(i)})' は下記で定義されています。
(W^{O}_{(i)})' = W^{O}_{(i)} W^{UV}_{(i)}
ただし、(W^{O}_{(i)})' \in \mathbb{R}^{d \times d_c} 。
また、W^{O}_{(i)} \in \mathbb{R}^{d \times d_h} 、W^{UV}_{(i)} \in \mathbb{R}^{d_h \times d_c} 。
全体のまとめ
Multi-head Latent Attention (MLA)を計算するにあたり、必要なのは下記に全て示ました。
Q_{(i)}K_{(i)}^\top = \left((W^{Q}_{(i)})' (\mathbf{C}^{Q})^T \right)^T (\mathbf{C}^{KV})^T
\begin{aligned}
\text{MHA}(Q, K, V) &= (W^{O}_{(1)})' (\mathbf{C}^{KV})^T \text{Atn}_{(1)}^T \\
&\quad + (W^{O}_{(2)})' (\mathbf{C}^{KV})^T \text{Atn}_{(2)}^T \\
&\quad + \cdots + (W^{O}_{(n_h)})' (\mathbf{C}^{KV})^T \text{Atn}_{(n_h)}^T \\
\end{aligned}
ここからわかることとして、推論時には(W^{Q}_{(i)})' と(W^{O}_{(i)})' という新しい重み行列を事前に計算してモデルにおいておくことで、キーバリュークエリを明示的に計算する必要がなく、少ない計算回数にて MLAを計算することが可能だとわかりました。
以上が、論文中の
In addition, during inference, since W^{UK} can be absorbed into W^Q , and W^{UV} can be absorbed into W^{O} , we even do not need to compute keys and values out for attention.
だと考えています。
私の疑問点
MLAは主にKVキャッシュの圧縮や、クエリの圧縮による、メモリ効率を向上させることに非常に重きを置いた手法に見えています。
一方で、計算回数を減らすために、行列を吸収させる場合、吸収後の行列のパラメータ数は非常に多くなるように思います。
例えば下記の式で考えます。
(W^{Q}_{(i)})' = (W^{UK}_{(i)})^T W^{UQ}_{(i)}
ただし、(W^{Q}_{(i)})' \in \mathbb{R}^{d_c \times d_c'} であり、また、W^{UK}_{(i)} \in \mathbb{R}^{d_h \times d_c} 、W^{UQ}_{(i)} \in \mathbb{R}^{d_h \times d_c'} です。
このとき、論文中の数字(d_c = 512, d_c' = 1536, d_h = 128 )を代入すると、(W^{Q}_{(i)})' のパラメータ数は786,432 、W^{UK}_{(i)} のパラメータ数は65,536 、W^{UQ}_{(i)} のパラメータ数は196,608 となります。
すなわち、吸収前のパラメータ数は65,536 + 196,608 = 262,144
吸収後のパラメータ数は786,432 となってしまいます。
これでは、推論時の必要パラメータが多くなってしまうため、 MLAの利点であるメモリ効率向上と相反する形に見えます。
という意味で、この吸収は実装されているのか?されていないのか?
されている場合は、メモリ効率の低減は、無視ができる程度なのか?そうでないのか?
この辺り、まだ私の中でもあまり固まっていない疑問ですが、詳しい方に教えていただけると嬉しいです。
なお、「吸収して大きな行列を作っているのではなく、2つの行列のまま2回行列を掛け算している」という考えは、私はないと思っております。なぜからその方法であればRoPEを導入した際に問題にならないからです。
その場合、普通に2つの行列演算の間に回転行列をかければいいだけのはずで、わざわざRoPEを適用するQuery・Key、適用しないQuery・Keyを分ける必要はないですよね?
ぜひ詳しい方!教えていただけると嬉しいです!
まとめ
読んでくださってありがとうございました!
MLAの「be absorbed」の部分の検証をしたいと思っていた方の参考になれば幸いです。
また、ぜひ私の疑問点にも、ご回答をもらえると嬉しいです。
Attentionについてのおすすめ書籍
機械学習エンジニアのためのTransformers ―最先端の自然言語処理ライブラリによるモデル開発
Hugging Faceの開発者が書いた本ですので、信頼できます。
当然のことながら、Attentionレイヤーの詳細な解説に関しても記載してあり、理論・基礎を学ぶ上でも非常におすすめの書籍です。
大規模言語モデル入門
大規模言語モデル入門Ⅱ〜生成型LLMの実装と評価
よく紹介させていただいておりますが、こちらの書籍は、LLMのファインチューニングから、RLHF、RAG、分散学習にかけて、本当に幅広く解説されており、いつも参考にさせていただいております。
特に1冊目は、TransformerのAttentionレイヤーの数式に関してもわかりやすく説明されておりおすすめです。
Discussion
実装はされていないようでした.個別に Q, K を計算してその積を計算していました.
主観ですが,数百トークンを入力したときは無視できるのかなと思います.
しかし,最近よく見かける,まとめて proj して split する方法を使えばより簡単に行列積の演算回数は減らせるので,low-rank のパラメータをわざわざ大きくするのはあまり良い方法には感じませんでした...Q, K = X \cdot \text{concat}(W_Q, W_K) をした後に QK^T を計算
proj+split:
余談ですが,分析研究では対象をシンプルにするためにこの QK, VO の合体が頻出します.https://arxiv.org/abs/2405.00208 の式 2, 3
まず、ご回答いただきありがとうございます。
DeepSeek-V2のPyTorch実装がここから見れるということや、「まとめて proj して split する方法」も言われてみれば「確かに!」でしたが、実際の実装を見たことがなかったので初めて知りました!
ありがとうございます。
確かに、こちらのPyTorch実装を見ると、愚直にKVを一度計算しているように見えます(行列を吸収するのは実装されていない)
しかしながら、そう考えるとDeepSeek-V2論文の下記記載部分(2.1.3)と矛盾します。
ここに記載の通り、「RoPEをQとKに適応すると、行列吸収ができなくなる」ので、わざわざ論文2.1.3にて、RoPEを適用したQKと適用していないQKを分けて処理をしています。
(この処理は、PyTorch版のコードでも実装されています)
しかしながら、記載いただいたPyTorch実装のように、一度圧縮表現C_t^{KV} からK Vを計算するのであれば、RoPEを導入したとしても問題なく計算可能(普通にKを計算した後に、RoPEを適用し、あらためてQK^T を計算すれば良い)なので、わざわざRoPEを適用するために、特殊な処理を考える必要はないはずです。
一方で、DeepSeek-V2論文3.1.3にて下記の記載がございます。
これを見ると、DeepSeek社内部での学習や、APIで呼び出された時の内部の推論に関してはPyTorchではなく、独自フレームワークを利用している可能性が高いです。
そちらのフレームワークでは、もしかしたら行列吸収の形で実装されており、だから、RoPEの部分で問題があったのかもしれません。
これはおっしゃる通りかもですね。
KVキャッシュされるtoken数が増えるほど、圧縮されるメモリの絶対量は増えそうですので、1パラメータの重み行列の大きさを無視できるタイミングはありそう。
こちらも軽く見させていただきました!
普通のTransformerでもこの行列吸収はできるよな?とは思っていたのでその疑問解消できました!
新情報:github にある DeepSeek-V3 のコードに
absorb
の分岐がありました. APIの内部では吸収の方で動いているような気がしてきました