次の一語のために、本来は会話全体を読み直すはず。でもそうしない。 チャットボットは、あなたが読むのとほぼ同じ速さで言葉を流し出す。でも次の一語を選ぶたびに、モデルはこれまでのすべてを振り返るように作られている。それを毎回ゼロからやれば、長い会話は這うように遅くなる。なのに這わない——あふれ出す。流れ続けさせる仕掛けこそ、ここで語る話のすべてだ。
新しい一語ごとに、その前の全語を読み直す。積み上がっていく。 ∑t=1nt=n(n+1)2≈n22\sum_{t=1}^{n} t = \frac{n(n+1)}{2} \approx \frac{n^2}{2} モデルは一語ずつ書き、その一語を選ぶたびに接頭部すべてを振り返る。2語目は1語目を、100語目は99語を、1000語目は999語を読み直す。通話のたびに手で配線し直す電話交換台のように:同じ配線を何度も何度も引き直す。その手戻りをすべて足すと、コストは長さの二乗で膨らむ——会話が倍になれば、仕事は四倍。
でも過去は決して変わらない。なら、なぜ計算し直す? kj=xjWK,vj=xjWVk_j = x_j W_K, \qquad v_j = x_j W_V モデルが前の語を、アテンションが必要とする二つのもの——KeyとValue——に変えるとき、それらはその語だけで決まる。あとから加わる語が遡って書き換えることはできない。濡れたセメントに押された手形のように:固まった瞬間に、永久に決まる。一度計算すれば、それで終わり。
なら、保存しておく。KeyとValueの増えていく表を持ち続ける。 Attn=softmax (qt K1:t⊤d)V1:t\mathrm{Attn} = \mathrm{softmax}\!\left(\frac{q_t\,K_{1:t}^{\top}}{\sqrt{d}}\right) V_{1:t} 過去をやり直すな——しまっておけ。モデルが計算したKeyとValueは、すべて増えていく表に落ちていく。次の語を書くには、新しいQueryを一つ作り、その保存済みの表全体に照らして引く。料理人のミザンプラスのように:材料を一度だけ仕込めば、あとは必要なものに手を伸ばすだけ。その表こそがKVキャッシュだ。
こうして新しい一語は安くつく:一語だけ符号化し、残りはちらりと見るだけ。 O(d2)⏟one new word+O(t d)⏟the glance ≪ O(t d2)⏟re-encode all t\underbrace{O(d^2)}_{\text{one new word}} + \underbrace{O(t\,d)}_{\text{the glance}} \;\ll\; \underbrace{O(t\,d^2)}_{\text{re-encode all } t} 表を保存しておけば、語を一つ足すのにほとんどコストがかからない。モデルはその一語だけを符号化し——会話がどれだけ長くても一定のコスト——あとはキャッシュされた行をさっと見るだけ。道路を舗装するように:後ろに残した何キロもを敷き直したりはせず、先端にだけ新しいアスファルトを足す。接頭部すべてを再符号化する高コストは、まるごと消える。
落とし穴:その表は増える一方。今度の請求書はメモリだ。 cache bytes=2⋅L⋅n⋅dmodel⋅b\text{cache bytes} = 2 \cdot L \cdot n \cdot d_{\text{model}} \cdot b 速さを、場所と引き換えに買ったのだ。キャッシュは層ごと・これまでの語ごとに二つのベクトルをためる——会話が進むほど膨らむ一方。長い文脈ではギガバイト級にふくれ上がり、壁になるのは計算ではなくそれだ。何ひとつ下ろさない登山者のように:速さを保つためのメモが、一歩ごとに背中へ積み重なっていく。
プロンプトは一度だけ読む。あとは流す——安い一語ずつ。 これがKVキャッシュだ:メモリと時間を引き換えにして、過去を二度と計算し直さない。仕事を二つに分ける——プロンプト全体を並列の一回で読み、表を埋める。そのあとは行を一つ足して振り返るだけで、語を次々と流し出す。貨物列車のように:遠くへ行くのに組み直したりはしない——車両をもう一両つないで、走り続ける。
記憶に足すことはできる。書き直すことは決してできない。 🌱 キャッシュは追記専用だ。モデルが見たどの語も、落ちた瞬間に固定される——完璧に保たれ、決して書き換えられない。一度だけ刻まれ、ただ再生されるだけのレコードのように。そして新しい思考はいまも、そのすべてを振り返らねばならない。では、足すことしかできない——直すことも、縮めることもできない——記憶は、自由の一種なのか、それとも増えるしかない重さなのか?