Skip to content

台大李宏毅 ML 2026 導讀:加快生成(下)——KV Cache 省了時間,卻撐爆倉庫,以及各種瘦身法

2026年9月30日1 分鐘
TL;DRKV Cache 把已經算過的 key 和 value 存起來,decode 時就不必重算,但它每個 token 都要佔一份記憶體:以 Gemma 2 27B 為例,一個 token 約 0.72MB,A100 80GB 只夠放約 11.4 萬個 token。李宏毅接著整理了一串瘦身法:讓多個 query 共用 key/value(MQA、GQA)、把 key/value 壓成一個向量又不必解壓(MLA)、只看一段範圍(Sliding Window、StreamingLLM)、把沒人理的 key/value 丟掉(Scissorhands、H2O),最後講跨對話的 prompt caching:只有前綴完全相同才會命中,所以 system prompt 要把穩定的內容放前面。

🌏 English version

本文依據台大李宏毅《機器學習 2026 Spring》3/20 那一週的教材。 這是台大李宏毅 機器學習 2026 Spring 導讀系列第 7 篇。上一篇 Flash Attention 的結尾,範例 Colab 把序列拉長十倍後,GPU 直接 CUDA out of memory,爆掉的是倉庫(HBM)而不是工作台(SRAM)。這一篇回答那個懸念:是什麼把倉庫撐爆?又有哪些辦法讓倉庫多撐一點?

用到的官方材料:講義 inference.pdf 第 29–55 頁,以及課程頁列出的影片加快語言模型生成速度 (2/2):KV Cache。存取等級是 A3:投影片 pdf/pptx 與錄影都公開。對應的實作在 HW3 的 vLLM 實驗。

場景:KV Cache 本身很簡單

老師開場說,cache 和 cash 同音,而它確實和錢有點關係,這要到本文最後才揭曉。

回顧生成過程:prompt 進來是 Prefill,所有 token 一次算出 q、k、v,可以平行算 attention;之後一個一個吐 token 是 Decode。Prefill 算完,q 丟掉,k 和 v 存下來,這就是 KV Cache(投影片第 30–33 頁)。

為什麼只存 k、v?因為第 4 個 token 生出來以後,只需要算它自己的 q4、k4、v4,拿 q4 去和前面存好的 k1…k3 以及 k4 算 attention,再對 v1…v4 做 weighted sum。前面 token 的 key 和 value 不必重算,而舊的 query 以後再也用不到。

直覺:它會撐爆你以為很大的倉庫

KV Cache 在實作上的麻煩是記憶體。原因有兩個:每個 token 都要存一組 k、v,序列越長存越多;而且不只一組,multi-head attention 每個 head 都有自己的 k、v,每一層也都有。

第 35 頁用 Gemma 2 27B 的架構表估算(先忽略它實際用了 GQA,把 32 個 head 都當成各有一組 k、v):

展開:每個 token 的 KV Cache 大小

46(層)× 32(head)× 128(維度)× 2(FP16 每個數 2 bytes)× 2(key 和 value)= 753,664 bytes ≈ 736 KB ≈ 0.72 MB

A100 有 80GB,除下來大約只能放 114k 個 token。

一個 token 不到 1MB 聽起來不多,但 A100 整張卡只夠放約 11 萬個 token,而 agent 的 context 需求常常遠超過 10 萬。所以後面一連串的發明,目標都是讓倉庫多撐一點。

機制一:讓 query 共用 key 和 value

Multi-query attention(MQA)(第 36 頁):一樣有多個 query,但所有 query 共用同一組 key 和 value。因為只有 k、v 會存進倉庫,query 多幾組沒關係。老師說 MQA 的表現並不好。

Grouped-query attention(GQA)(第 37 頁)介於兩者之間:保留多組 k、v,但每組配給多個 query。前面 Gemma 2 架構表寫的 GQA 就是這個,投影片也標註 Llama、Gemma 等模型都在用。

老師點出一個設計上的因果:為什麼是讓多個 query 共用 k、v,而不是讓多組 k、v 共用 query?如果不知道 KV Cache,兩種共用看起來都行;正因為只有 k、v 需要存,才會選擇壓縮 k、v 的數量。

機制二:壓縮 key 和 value,而且不必解壓

**Multi-head Latent Attention(MLA)**出自 DeepSeek-V2(第 38–43 頁)。想法是在輸入 x 和多組 k、v 之間放一個 bottleneck:x 先被壓成一個維度較小的向量 c,倉庫裡只存 c,需要時再乘上不同矩陣得到各組 key 或 value。這一定要在訓練時就教模型這樣壓。

直覺上,存壓縮向量好像意味著每次算 attention 都要先把所有 c 解壓回 k、v,那就省不到算力了。MLA 神奇的地方是不需要解壓。

展開:為什麼可以在壓縮空間裡直接算

Key 端:k_i = W_K · c_i,所以

a_i = q^T k_i = q^T W_K c_i = (W_K^T q)^T c_i = q'^T c_i,其中 q' = W_K^T q

先把 q 壓成 q'(只要做一次,和序列長度無關),再直接和每個 c_i 做內積。這不是近似,結果完全相同。

Value 端:v_i = W_V · c_i,所以

O = Σ_i â_i v_i = Σ_i â_i W_V c_i = W_V (Σ_i â_i c_i)

先在壓縮空間對 c_i 做 weighted sum,最後只解壓一次。

重點是避免對「和序列長度成正比的東西」逐一解壓。老師補充,文獻上 MLA 的結果甚至可能比原本的 multi-head attention 稍好一點。

機制三:改變 attention 的範圍

Sliding Window Attention(第 44–45 頁):每個 query 只看前面固定長度的範圍,例如 4096 個 token,KV Cache 就有固定上限。代價是單層看得比較短,但 Transformer 有很多層,上一層看到的位置本身已經看過更前面,所以層數夠多時,整體能看的範圍仍然可以很大。投影片提到它曾用在 Mistral 7B 的某個版本;GPT-OSS 則是一層 sliding window、一層完整 attention 交錯。

StreamingLLM(第 46 頁,Efficient Streaming Language Models with Attention Sinks):純 sliding window 在長輸入時表現會變差,但只要 window 額外包含整個序列最前面的幾個 token,表現就大幅回升,而且可以不用額外訓練。論文的 perplexity 圖顯示,dense attention 在超過訓練長度後、window attention 在超過 window 後,表現都突然崩壞,StreamingLLM 則能撐到訓練時沒見過的長度。

為什麼開頭幾個 token 這麼重要?老師的解釋是:attention weight 合起來必須是 1,每個 query 都得 attend 某些東西。沒什麼好看的時候,模型的預設行為就是 attend 第一個 token。你把第一個 token 拿掉,模型就不知道該怎麼辦。

機制四:把沒人理的 key 和 value 丟掉

Pruning KV Cache(第 47–48 頁)引了兩篇 2023 年的論文:Scissorhands 與 H2O。兩篇都觀察到:每次只有一小部分 token 被 attend,少數 token 會反覆吸走大量 attention。所以一組 k、v 如果一直沒人 attend,就把它丟掉,像倉庫裡一直沒人拿的東西過幾天就清掉。

Scissorhands 的實驗顯示壓縮到 5 倍(只保留約 20% token 的 k、v),很多任務的表現仍然差不多。老師也提醒,後來有更多文獻指出,遇到很難的任務,隨便丟 k、v 表現還是會變差,如何剪枝才有效至今仍有大量研究。

連回模型:跨對話的 cache,也就是錢

前面的 KV Cache 都在同一段對話裡。第 49 頁把它延伸到跨對話:「大家好我是大金」和「大家好我是小金」前五個 token 完全相同,這五個 token 的 k、v 就可以直接複製過去用。

但後面那個「金」不能共用。雖然是同一個字,每個 token 的表示都取決於前面看過什麼,前面的「大」換成「小」,後面所有 token 的 k、v 都會不同。只有完全相同的前綴才能共用。

這就是 cache 和錢的關係。第 50 頁截了 OpenAI 的定價頁:投影片上的 gpt-5.4 短 context 輸入每百萬 token $2.50,cached input 只要 $0.25。服務商願意打折,是因為 key 和 value 已經算好,幾乎不用再算。同一頁也引了 OpenAI prompt caching 說明的圖:只改最後幾個 token 仍然命中;在最前面多加一個 token,整段就 cache miss。

什麼時候前綴會一樣?用 AI agent 的時候(第 51–52 頁)。還記得 OpenClaw 那篇,agent 會在你每句話前面加上很長的 system prompt,裡面是名字、靈魂、目標,長時間不太變動。所以 system prompt 的排法有講究:

  • 穩定不動的放前面:有哪些工具與用法、AGENTS.md 的行為規則、SOUL.md/IDENTITY.md/USER.md 等身分資訊、可用的 SKILL。
  • 可能變動的放後面:老師舉的例子是日期。system prompt 每次都會帶現在的時間,只要它放在前面,cache 就會被破壞。

投影片引了 OpenClaw 的 issue #27732 作為實際社群討論排列方式的例子。第 53 頁再舉一個 prompt 寫法:「幫我訂從台北到波士頓的班機」和「幫我訂從舊金山到紐約的班機」只命中開頭幾個字;改寫成「幫我訂從 x 到 y 的班機」並把 x、y 的值放在最後,就能命中更長的前綴。

第 54 頁引了 2026 年 1 月的論文 Don't Break the Cache,在長時程 agent 任務上實測 prompt caching。投影片上的圖顯示成本降幅:GPT-5.2 79.6%、Claude Sonnet 4.5 78.5%、Gemini 2.5 Pro 52.2%、GPT-4o 45.9%。

怎麼做:打開你的 agent 或 LLM 應用送出的完整 prompt,找出所有每次都會變的欄位(時間戳、使用者名稱、檢索結果),確認它們都在固定內容之後。

總結表:每個方法付出什麼代價

第 55 頁把兩堂課的方法填進上一篇開頭的那個框架:

方法做法改變原本的 attention?需要訓練模型?其他代價
Flash Attention少搬資料否否一點額外運算+一點點燒腦
KV Cache儲存算好的 key 和 value否否佔用記憶體
Multi-query attention多個 query 共享 key 和 value是是可能明顯傷害模型能力
Grouped-query attention同上(分組)是是
Multi-head Latent Attention壓縮 key 和 value是是
Sliding Window Attention改變 attention 範圍是?
StreamingLLM改變 attention 範圍是?
Pruning KV Cache丟棄 key 和 value是否可能明顯傷害模型能力
Speculative Decoding用小模型預言生成結果否(理論上)否小模型還是要耗費額外算力

投影片在 sliding window 與 StreamingLLM 的訓練欄打了問號,老師的解釋是各篇文獻做法不同:你可以訓練一個 full attention 模型,推論時才改成 sliding window;StreamingLLM 原始論文也兩種都試過。Speculative Decoding 為什麼理論上不改變結果,老師留給作業去讀原始論文,也就是下一篇 HW3 的論文題。

想深入

這一篇可以確認與不能確認的

可以確認:講義第 29–55 頁的文字與圖(Gemma 2 架構表、OpenAI 定價截圖、Don't Break the Cache 的長條圖都直接看了投影片影像)、影片的逐字字幕(YouTube 上的 zh-TW 字幕)、引用論文的標題(在 arXiv 核對過)。

不能確認:字幕把 head 數聽成 30、把定價例子說成另一個模型,本文以投影片上的數字(32 個 head、gpt-5.4 的價格)為準。投影片只截了定價表,本文沒有另外核對 OpenAI 目前的價格,實際價格請以官網為準。總結表中 GQA、MLA、Sliding Window、StreamingLLM 的「其他代價」欄在投影片上是空的。

系列導覽:系列總覽|上一篇 加快生成(上):Flash Attention|下一篇 HW3:LLM Fast Inference

參考資料