上下文視窗與 KV 快取
上下文視窗是有標價的,而且用 VRAM 支付。每個留在上下文中的 token 都強迫模型在每一層記住兩個小向量,而這份記憶——KV 快取——隨每個 token 增長,悄悄吃掉你以為自己有的餘裕。它解釋了本機 LLM 最奇怪的幾種故障:第四十輪的 OOM、隨對話變長而似乎變慢的模型、那張規格表上寫「128k 上下文」的 8 GB 卡。
快取裡存了什麼
注意力要先拿到每個過去 token 的鍵與值投影,才能注意到那個 token。它們在預填時計算一次,並保留到請求結束:每層、每 token,各一個 K 向量與一個 V 向量,每個有 kv_heads × head_dim 個數字。這份存放區就是 KV 快取,也是上下文記憶體成本對每個正在服務的請求、永遠線性成長的原因。
公式
bytes per token = 2 (K and V) × layers × kv_heads × head_dim × 2 bytes (fp16)
實例,llama-3-8B 級 GQA(32 層、8 個 KV 頭、head_dim 128):
2 × 32 × 8 × 128 × 2 = 131,072 位元組 = 每 token 128 KB。
在 32k 上下文下,那是 32,768 × 128 KB ≈ 4 GB 的 KV 快取——與旁邊的 Q4 權重相當。用 4 個 KV 頭的精簡配置,每 32k 大約 2 GB。fp16 KV 下 8B 級模型的數量級:視架構每 32k 預留 0.5–4 GB。某個檢查點的精確數字直接來自它的 config.json——num_key_value_heads、num_hidden_layers 與頭大小。
GQA 是長上下文變便宜的原因
分組查詢注意力讓多個查詢頭共享少數幾個 KV 頭——例如 8 個 KV 頭服務 32 個查詢頭——以幾乎可忽略的品質代價,把快取縮成完整多頭注意力的四分之一。沒有 GQA,下面的表格會大 4–8 倍,24 GB 卡上的長上下文只是幻想。它是本機長上下文工作最大的架構禮物,也是現代 7–9B 模型能出貨 32k–128k 視窗的原因。
它長得多快
fp16 KV 的 8B 級 GQA 模型,每 token 128 KB:
| 上下文 | KV 快取大小 | 在 8 GB 卡上 |
|---|---|---|
| 4k | ~0.5 GB | 無感 |
| 16k | ~2.1 GB | 在 5 GB 權重旁已經有感 |
| 64k | ~8.4 GB | 就是裝不下,沒得商量 |
| 128k | ~16.8 GB | 是模型本身的 3 倍 |
記憶體隨 token 線性成長,但注意力計算量隨上下文平方成長——所以長上下文同時消耗 VRAM 與每 token 時間。
KV 爆炸時的槓桿
- **量化快取。**q8_0 減半、q4_0 降為四分之一,品質代價溫和——常常是整個堆疊裡最划算的容量交易。在 llama.cpp 中,KV 量化需要 flash attention,如下所示。
- **服務較短的上下文。**如果你的提示詞只有 2k,
-c 8192不花任何成本。為了「以防萬一」配置宣稱的 128k,是在預留你永遠不會有效使用的 VRAM。 - **滑動視窗模型。**某些架構不論名目上下文多少,都會限制每層的 KV 視窗——不必付線性帳單的長上下文行為,代價是舊內容的回憶有些損失。
- **Flash attention。**本質上是算力與核心效率的收益——精確注意力而沒有平方級的啟動值爆炸——而在 llama.cpp 中它也是解鎖 KV 量化的閘門。
llama-server -m model-8b-q4_k_m.gguf -c 65536 -fa --cache-type-k q8_0 --cache-type-v q8_0
宣稱 vs 有效的上下文
宣稱 128k 的模型跑得起 128k——快取裝得下、數學也成立。但它能不能在 128k 推理是另一回事。拉伸的 RoPE 與長上下文微調讓大海撈針分數維持高檔,但更廣泛的理解力常在抵達上限前就明顯鬆弛。把宣稱的視窗當物理上限、假設後半段品質變軟,在把工作流程押上去之前,先找該檢查點的社群長上下文評測。
迷失在中間,與衛生習慣
位置偏誤是真的:模型對提示詞的開頭與結尾注意力最強、在中間鬆弛。把指令與關鍵事實放在最前或最後;永遠不要把模型必須遵循的規格埋進貼上的文件裡。讓上下文小而銳利的習慣: