跳到內容

Technology

新聞|ReplaySSM:混合模型解碼改存近期輸入,SSM 狀態每 16 步才寫回一次

Princeton Dao AI Lab 與 NVIDIA 發表 ReplaySSM,把 SSM 層的遞迴狀態改存成「檢查點加上近期輸入緩衝」,多數解碼步不再寫回狀態。整理它怎麼減少記憶體流量、讓推測解碼的回滾只需移動指標,以及怎麼讓低精度狀態的捨入誤差不再累積。

AI
Published: at 09:00 AM
編輯本頁下載圖卡

重點

為什麼 SSM 層會拖慢解碼

狀態空間模型(state-space model,SSM)用固定大小的狀態摘要過去所有 token,取代注意力層那個隨上下文變長的 KV cache。照理說,這讓解碼變便宜。實際上每一步都要把整個狀態從 HBM(GPU 的高頻寬記憶體)讀進來,做一次衰減加秩一更新,再整個寫回去。計算量很少,搬運量很大,所以 kernel 卡在記憶體頻寬。

以 Mamba-2 為例,每個 token 的遞迴是:

St=αtSt−1+vtkt⊤,ot=StqtS_t = \alpha_t S_{t-1} + v_t k_t^\top, \qquad o_t = S_t q_t

其中 StS_t 是狀態,αt\alpha_t 是依資料而定的衰減,vtv_t、ktk_t、qtq_t 是該 token 的輸入。

論文在 Nemotron-3-Super-120B-A12B-NVFP4、batch 256、單張 B300 上量測:上下文在 100K token 以內時,SSM 層是解碼步裡最大的延遲來源,比注意力層還慢。原因有兩個:

每步都寫回狀態還帶來另外兩個問題。推測解碼要在草稿被拒絕時回到最後接受的位置,SSM 狀態更新後很難倒回去。狀態量化之後,捨入誤差每一步都會重新進入遞迴,生成越長累積越多。

方法:存輸入,少存狀態

把遞迴在一個 hh 個 token 的窗口內展開:

St=γhSckpt+∑j=1hγj+1:h vjkj⊤S_t = \gamma_h S_{\mathrm{ckpt}} + \sum_{j=1}^{h} \gamma_{j+1:h}\, v_j k_j^\top

SckptS_{\mathrm{ckpt}} 是窗口開始前的狀態,γ\gamma 是累積衰減。也就是說,目前的狀態可以從一個舊檢查點加上近期輸入 (αj,vj,kj)(\alpha_j, v_j, k_j) 重建出來。ReplaySSM 就用這個形式存狀態:

  1. 一般步(non-flush):把這一步的輸入記錄附加到緩衝區,從檢查點與緩衝算出輸出,不寫回狀態。
  2. 清空步(flush):緩衝累積到 L 筆時,把這些輸入併進檢查點,寫回 HBM 一次,清空緩衝。

輸出與傳統解碼相同,差別只在浮點誤差。

在 L 步的窗口裡,兩種做法都要讀 L 次狀態,因為每步的輸出都需要它。傳統做法還要寫 L 次,ReplaySSM 只寫一次,另外讀寫的是比狀態小得多的輸入記錄,所以主要的狀態流量大約減半。L 越大,寫回攤得越薄,但每步要重播的輸入越多。四個模型在 batch 256 時都是 L = 16 最快。

Gated DeltaNet(GDN)與 Kimi Delta Attention(KDA)用的是 delta rule,更新前會先修改既有內容。論文把它改寫成同樣的外積形式,緩衝改存修正項 uju_j。實作涵蓋 Mamba-2 與 GDN,以 Triton kernel 寫在 vLLM 裡。

標準解碼的結果

模型SSM 類型權重GPUSSM/注意力層數
Nemotron-3-Nano-4BMamba-2BF161×H10021/4
Nemotron-3-Super-120B-A12BMamba-2NVFP41×B30040/8
Qwen3.5-4BGDNBF161×H10024/8
Qwen3.5-122B-A10BGDNNVFP41×B30036/12

對照組是 vLLM 調到最佳啟動設定的原生 kernel,比 Mamba-2 出廠預設值還快。在 batch 256、L = 16、FP32 狀態下:

加速隨 batch 變大而增加,直到傳統 kernel 吃滿頻寬為止。端到端的上限取決於 SSM 在解碼步裡的占比,注意力與模型其他部分沒有改動。

推測解碼:回滾只要移動指標

推測解碼先由小模型或 MTP head 猜出 T 個 token,再讓目標模型一次驗證,保留猜對的前綴。注意力層回滾只要把 KV cache 的指標往回移。SSM 狀態一旦吸收了草稿就倒不回去,所以 vLLM 的做法是每個草稿 token 存一份狀態快照,每步變成 1 次讀加 T 次寫,快照還會占滿記憶體、壓低能同時服務的請求數。先前的 Mamba in the Llama、Snakes and Ladders、STree 避開了逐 token 快照,但仍要額外搬運狀態,在服務級 batch 下都只比快照快幾個百分點。

ReplaySSM 的做法:

O=Sckpt QD+V(W⊙(K⊤Q))O = S_{\mathrm{ckpt}}\, Q D + V \bigl( W \odot (K^\top Q) \bigr)

DD 是草稿位置的累積衰減,WW 是帶因果遮罩的衰減矩陣。檢查點只讀一次,其餘是兩個小矩陣乘法,可以用 Tensor Core。這跟 prefill 用的分塊(chunkwise)算法是同一種形式,緩衝加草稿就是那個 chunk。

實驗用兩個 120B 級模型,各自的 MTP head 產生草稿,T = 4,GSM8K 題目,每個請求生成 256 個 token:

指標Nemotron-3-SuperQwen3.5-122B
batch 512 吞吐量,相對 vLLM 推測解碼1.85×2.10×
batch 512 吞吐量,相對標準解碼2.16×1.90×

兩者的接受長度相同。傳統推測解碼過了 batch 128 就不再變快,Qwen3.5-122B 在 batch 512 時甚至比不用推測解碼還慢;ReplaySSM 則隨 batch 持續成長。

驗證 kernel 本身的差距更明顯。在 Nemotron-3-Super、batch 256 下,傳統驗證 8 個 token 的延遲是一次標準解碼步的 4.66 倍;ReplaySSM 是 0.89 到 1.21 倍,範圍取決於草稿全被拒絕或全被接受。

低精度狀態:讀取用低精度,遞迴用高精度

狀態量化後,同一份狀態既用來算輸出,也是下一步更新的起點,捨入誤差會一路累積。KV cache 沒有這個迴圈,量化後的項目會被讀很多次,但從不寫回自己,所以比 SSM 狀態更耐低精度。這也是為什麼 Qwen3.5-122B-A10B-NVFP4 的權重用 4 位元、KV cache 用 FP8,SSM 狀態卻維持 FP32。

ReplaySSM 的分工讓兩件事可以用不同精度:

讀 NVFP4 狀態時,Mamba-2 的一般步 kernel 比讀 FP32 快 3.8 倍。GDN 要多一個調整:一般步算出的修正項來自低精度副本,所以另外快取 vv,在清空時用高精度狀態重算修正項。

輸出偏差用 KL divergence 量測(PG-19 文字、teacher forcing)。在 Qwen3.5-4B 生成到 128K token 時,NVFP4 讀取搭配 FP32 遞迴,KL 從 0.136 降到 0.0038,約 36 倍。遞迴改用 FP16 加隨機捨入,結果跟 FP32 很接近。

下游準確率是四個模型的平均變化(百分點,FP32 為基準):

狀態設定MMLU-ProGPQA-DiamondMath-L5
FP32 準確率(基準)77.6%73.7%92.3%
共用 FP8−0.24−1.05+0.01
共用 NVFP4−5.88−13.05−10.92
NVFP4 讀取/FP16 遞迴−0.15−2.36−0.10
NVFP4 讀取/FP8 遞迴−0.40−1.77−0.38

損失集中在長生成。Nemotron-3-Super 在 GPQA-Diamond 最長四分之一題目的生成長度中位數是 61K token,MMLU-Pro 與 Math-L5 約 8K。在較長的兩個四分位,共用 NVFP4 比 FP32 差 20.9 與 28.2 分;NVFP4 讀取搭配 FP8 遞迴只差 2.0 與 3.4 分。

跟既有做法的關係

限制與待確認

參考資料

註釋

編輯本頁

Podcast

關於語音摘要

此語音摘要由 Google NotebookLM 提供,不完全代表作者本人對文章的理解。