Laguna-S-2.1 Q4_K_M:EPQ MoE 專家分頁實驗

把 EPQ(Expert Paging Quantization) 的 「MoE 專家常駐 RAM 分頁」推理架構,移植到 poolside/Laguna-S-2.1-GGUF 的 Q4_K_M 上。

Laguna-S-2.1-Q4_K_M 的 GGUF 有 96.0 GB,其中 85.4 GiB 是 48 層 × 256 個路由 expert 的權重。EPQ 不把這些 expert 全部放進 RAM,而是:

  • 只保留 attention / dense / shared-expert / router / embedding 等非 expert 權重常駐;
  • 把每個 expert 的 ffn_gate_exps / ffn_up_exps / ffn_down_exps 放在 固定大小的 RAM arena 內當「槽位」;
  • 依 token 的路由結果,用 O_DIRECT 從 SSD 把選中的 expert 讀進槽位,算完即被淘汰;
  • MoE 的加權累加用一個自訂 ggml op 完成,全程不是「先整批載入再一次算」。

結果:一顆 96 GB 的模型可以在 4 GiB RAM 上限內跑起來,代價是速度與 SSD 讀取量。

結論

RAM 上限 結果 arena 槽位(小/大) expert 覆蓋率 匿名峰值 RSS 峰值 decode SSD
2 GiB FAIL(arena 太小) - - - 280 MB 286 MB - -
4 GiB PASS 1.94 GiB 240 (180/60) 2.3% 2980 MB 6858 MB 0.56 tok/s 3648 MB/tok
8 GiB PASS 5.69 GiB 804 (668/136) 6.7% 6818 MB 10697 MB 0.83 tok/s 2456 MB/tok
16 GiB PASS 13.7 GiB 1932 (1604/328) 16.0% 15013 MB 18891 MB 1.11 tok/s 1792 MB/tok
32 GiB PASS 29.7 GiB 4187 (3475/712) 34.8% 31397 MB 35275 MB 2.00 tok/s 1020 MB/tok
64 GiB PASS 57.7 GiB 8134 (6750/1384) 67.6% 60073 MB 63950 MB 1.92 tok/s 538 MB/tok
  • 量測方式:prlimit --data=<上限> 硬限制 + 20 ms 取樣的匿名 RSS 峰值追蹤 (strict_bench.py),EPQ_RESERVE_MB=2048,-c 2048 -b 128 -t 16,-n 16, greedy(--temp 0)。64 GiB 這列用 EPQ_RESERVE_MB=6144(見「已知陷阱 1」)。
  • 「expert 覆蓋率」= arena 大小 ÷ 路由 expert 總位元組(85.4 GiB)。
  • 2 GiB 是真下限:扣除 non-expert 常駐與 I/O 緩衝後 arena 剩不到一個 大 expert(18 MiB)可用,pager 直接拒絕啟動(回報「expert arena 太小」), 不會給你一個靜默錯誤的答案。

快速開始

# 抓基底(poolsideai/llama.cpp 的 laguna 分支)
git clone -b laguna https://github.com/poolsideai/llama.cpp.git llama-laguna
cd llama-laguna && git checkout 06f8ceb

# 放 sdq_* + 套 patch
cp /path/to/laguna-epq/sdq_*.h /path/to/laguna-epq/sdq_*.cpp src/
git apply /path/to/laguna-epq/0001-epq-laguna.patch

cmake -B build_epq -DCMAKE_BUILD_TYPE=Release -DLLAMA_CURL=OFF \
      -DGGML_NATIVE=OFF -DGGML_AVX512=ON -DGGML_AVX512_VBMI=ON \
      -DGGML_AVX512_VNNI=ON -DGGML_AVX512_BF16=ON -DGGML_AVX2=ON \
      -DGGML_FMA=ON -DGGML_F16C=ON
cmake --build build_epq -j16 --target epq-chat llama-server llama-perplexity

# 跑(4 GiB 上限)
EPQ_REQUIRE_PAGER=1 EPQ_RAM_BUDGET_MB=4096 EPQ_RESERVE_MB=2048 EPQ_PREFETCH=0 \
  ./build_epq/bin/epq-chat -m laguna-s-2.1-Q4_K_M.gguf \
    -p "Explain MoE routing." -n 24 -c 2048 -b 128 -t 16 \
    --temp 0 --no-interactive --no-chat --ram-budget-mb 4096

或直接 ./build.sh <llama.cpp-checkout> [build-dir]。

模型與路由

poolside/Laguna-S-2.1-GGUF Q4_K_M,general.architecture = laguna:

參數 值
block_count 48
leading_dense_block_count 1(第 0 層是 dense)
expert_count 256
expert_used_count 10
embedding_length 3072
expert_feed_forward_length 1024
expert_shared_feed_forward_length 1024(第 0 層)
context_length 1048576
routed_scaling_factor 2.5

權重組成(路由 expert 部分)

層 張量型別 每個 expert 3 張矩陣 每 expert 位元組
1–39(39 層) Q4_K(type 12) ffn_{gate,up,down}_exps,各 1,769,472 B 5,308,416 B(5.06 MiB)
40–47(8 層) BF16(type 30) 同上,各 6,291,456 B 18,874,368 B(18 MiB)

路由 expert 合計 91,653,931,008 B = 85.36 GiB。因為有兩種尺寸,pager 開兩種槽位 (小 5.06 MiB / 大 18 MiB),大 expert 只進大槽,小 expert 可進任一槽。

嚴格 RAM 上限實測

詳細輸出見 results/strict_memory_levels.md 與 results/strict_memory_levels_final.json。

掃描重點:

  • 4 GiB:arena 1.94 GiB,命中率 0(工作集比 arena 大,幾乎每個 expert 每次都被淘汰 重讀),0.56 tok/s、3.6 GB/tok。
  • 8 GiB:arena 5.69 GiB,命中率開始起來(20.7%),0.83 tok/s。
  • 16 GiB:arena 13.7 GiB,命中率 31.8%,1.11 tok/s、1.8 GB/tok。
  • 32 GiB:arena 29.7 GiB,命中率 44.7%,2.00 tok/s、1.0 GB/tok。
  • 64 GiB:arena 57.7 GiB,命中率 51.8%,1.92 tok/s、538 MB/tok;arena 已達 routed expert 總量的 67.6%,工作集幾乎全部命中,evictions 為 0。

讀表時請注意

  • 匿名峰值是硬上限量測的主角:prlimit --data 限制的是匿名映射的 size, 我們用 20 ms 取樣的 Anonymous RSS 峰值確認它沒有超過上限(anon_within_limit)。
  • RSS 峰值 > RAM 上限是正常的:96 GB 的模型檔仍然被 mmap, 檔案對映 RSS 不算在 RLIMIT_DATA 內;真正的預算限制是匿名記憶體。
  • arena 不是全部的匿名用量:執行緒堆疊、ggml compute buffer、I/O 對齊緩衝 另外要算,所以 EPQ_RESERVE_MB 必須蓋得住(見下)。

Top-k 消融:原始 10 vs 4

Laguna-S-2.1 的原始路由是 expert_used_count = 10。epq-chat --expert-used N 會同時覆寫 llama graph 與 pager 看到的 top-k(EPQ_EXPERT_USED), 在不改動 GGUF、不改 arena 設定的情況下做 A/B:

RAM 上限 top-k decode SSD 命中率 evictions expert requests PPL
64 GiB 10(原始) 2.42 tok/s 305 MB/tok 78.4% 64 35176 1.9462
64 GiB 4 3.52 tok/s 242 MB/tok 69.0% 0 14598 8.0007
16 GiB 10(原始) 1.30 tok/s 1561 MB/tok 49.0% 15998 35176 —
16 GiB 4 3.42 tok/s 481 MB/tok 56.2% 4462 14598 —
  • 量測:greedy、-n 64、-c 2048 -b 128 -t 16、EPQ_PREFETCH=0; 64 GiB 用 EPQ_RESERVE_MB=6144,16 GiB 用 2048(同上面的掃描)。
  • 速度:64 GiB 下 top-4 快 1.45 倍,16 GiB 下快 2.63 倍;SSD 讀取量分別少 20.5% 與 69.2%。top-4 每 token 只碰 4 個 expert,所以請求數少 58.5%, 低 RAM 時工作集縮小、eviction 從 15998 降到 4462,加速最明顯。
  • 品質:用 --ppl-file 對同一段 799-token 英文散文(Alice in Wonderland 開頭,results/ppl_en.txt)做 teacher-forced NLL,top-10 的 perplexity 是 1.9462, top-4 是 8.0007(4.11 倍)。同一 prompt 的 greedy 輸出也很快分岔, top-4 的樣本有重複詞與離題。
  • 結論:top-4 是「用模型品質換速度/記憶體」的旋鈕,不是等價加速; 要保留原始模型行為就維持 top-10。詳細原始資料見 results/topk_ablation*.json 與 results/topk_levels/。

Least-Stale 淘汰策略:LRU vs Least-Stale

SpecMD(arXiv 2602.03921)提出 Least-Stale: 把快取頁分成 stale(上一個 forward pass 用過、這一輪還沒用)與 current (這一輪用過或已被預取),先淘汰 stale,並依 layer 位置保護這一輪還沒輪到的層。 我們在 sdq_pager 加入兩種實作,用 EPQ_EVICT 切換(預設 lru,原行為不變):

EPQ_EVICT 策略
lru 原本的 LRU(預設)
ls 論文式兩佇列:stale 先於 current;佇列內 layer 由小到大(FIFO)
ls_pos 位置感知:stale-left → current-left → stale-right → current-right;左右兩側都先挑 layer 較大者(離下一次使用最遠)

量測條件同 top-k:greedy、原始 top-10、-n 64、-c 2048 -b 128 -t 16、 EPQ_PREFETCH=0。8 GiB 用 EPQ_RESERVE_MB=2000,16/64 GiB 用 6144:

RAM 上限 策略 命中率 evictions SSD decode
8 GiB LRU 31.94% 23131 2286 MB/tok 0.88 tok/s
8 GiB LS 19.41% 27536 2693 MB/tok 0.66 tok/s
8 GiB LS + 位置 27.90% 24550 2541 MB/tok 0.80 tok/s
16 GiB LRU 42.21% 18961 1840 MB/tok 1.10 tok/s
16 GiB LS 24.56% 25170 2500 MB/tok 0.81 tok/s
16 GiB LS + 位置 36.20% 21073 2013 MB/tok 1.00 tok/s
64 GiB LRU 78.454% 62 304.2 MB/tok 1.87 tok/s
64 GiB LS 78.451% 63 304.4 MB/tok 2.30 tok/s
64 GiB LS + 位置 78.468% 57 302.7 MB/tok 2.57 tok/s
  • 64 GiB 幾乎沒有壓力:arena 57.7 GiB、8134 槽位,整輪只有 57–63 次淘汰, 三種策略的命中率差 < 0.02 個百分點,SSD 差 < 0.5%,decode 速度差在雜訊內。 LS + 位置少 5 次淘汰、SSD 少 1.5 MB/tok,但不足以宣稱加速。
  • 8/16 GiB 由 LRU 勝出:arena 分別只放得下 811 與 1368 個槽位,而 top-10 每輪工作集約 480 個 expert,等於快取可容納 1.7–2.9 輪。Least-Stale 會把 上一輪的 stale-left expert 淘汰掉,但這些 expert 下一個 token 很可能再用; LRU 淘汰的是「好幾輪沒被用到」的頁,對這個工作負載更準。
  • 位置感知明顯優於論文式兩佇列:ls 在 8/16 GiB 會大量淘汰 stale-right (23164/21115 次),也就是這一輪還沒輪到、馬上要用的層,命中率因此掉到 19–25%。ls_pos 把 stale-right 保護住(只剩 942/450 次),命中率回到 27.9%/36.2%,證明「不要淘汰這一輪還沒用到的層」這個部分是對的;但還是 敵不過 LRU 對跨 token 重複使用的掌握。
  • 結論:預設維持 LRU;EPQ_EVICT=ls/ls_pos 保留為可重現的實驗旋鈕。 SpecMD 的加速前提是「快取小於一輪工作集 + 預取把 current 佇列補熱」;本實驗 關掉預取、且快取可放 1.7 輪以上,Least-Stale 的 stale 佇列反而會每輪洗掉 工作集。這是這個 pager/工作負載下的負面結果,不是正確性問題(淘汰只改變 哪些權重留在 RAM,不改變 routing 或數學)。

正確性

移植只改「權重從哪裡來」,不改數學。因此驗證有兩層:

一、逐層對純 NumPy(含 routing)

EPQ_MOE_DUMP 會把某一層 build_moe_ffn 的輸入(cur、selected_experts、weights) 與輸出 dump 出來,verify_moe_layer_laguna.py 用 NumPy 重算整層並比對:

層 型別 rel error corr 結果
1 Q4_K 0.0096 0.99989 PASS
40 BF16 0.0025 0.999997 PASS

(routing 的 selected_experts 與 weights 逐元素完全一致;殘差來自量化點積 的浮點順序差,量級符合量化誤差。)

二、跨 RAM 預算的 greedy 輸出一致性

同一 prompt、--temp 0、-n 16,分別在 4 GiB 與 32 GiB 上限下生成, 逐字比對生成文字:

IDENTICAL greedy output (4 GiB vs 32 GiB)

分頁層完全沒有改變結果——命中與否、槽位多寡只影響速度與 SSD 讀取量。

環境變數

變數 說明
EPQ_PAGER 設 0 關閉 pager(走原生 mmap 路徑)
EPQ_REQUIRE_PAGER 設 1 時 pager 初始化失敗就讓載入失敗(不要靜默回退)
EPQ_RAM_BUDGET_MB RAM 總預算(MB),arena 會扣掉已用與預留
EPQ_EXPERT_USED 覆寫路由 top-k(同時作用於 llama graph 與 pager;預設用 GGUF 的 10)
EPQ_RESERVE_MB 保留給 arena 以外匿名用量的 MB(本機建議 2048,見下)
EPQ_PREFETCH 設 0 關閉背景預取
EPQ_EVICT 淘汰策略:lru(預設)、ls(Least-Stale 兩佇列)、ls_pos(Least-Stale + layer 位置)
EPQ_STATS_FILE pager 指標 JSONL 輸出(掃描腳本在用)
EPQ_BENCH_NO_EOG 忽略 EOG,固定生成 N token(量測用)
EPQ_MOE_DUMP il=層 格式,dump 該層 MoE 輸入/輸出供驗證
EPQ_BIG_THRESHOLD_BYTES 大小槽位的分界(預設依 tensor 型別自動判斷)

已知陷阱

1. EPQ_RESERVE_MB 決定成敗(本機要用 2048)

arena 之外還有匿名用量:16 條 OpenMP 執行緒堆疊、glibc per-thread arena、 ggml compute buffer(約 56 MiB)等。用 EPQ_RESERVE_MB=1400 時,32 GiB 上限 會在 decode 途中 std::bad_alloc(在 worker 執行緒內未被捕捉 → abort)。 本機實測 EPQ_RESERVE_MB=2048 才穩定;掃描表中的數字都用 2048, 唯一例外是 64 GiB:本機記憶體守衛會在單一行程 RSS 超過 64 GiB 時直接 SIGKILL, 而 64 GiB 上限的 RSS 峰值約 66 GiB(arena + mmap 的非 expert 權重), 因此該列改用 EPQ_RESERVE_MB=6144,讓 RSS 峰值降到 63.95 GB(62.5 GiB)再跑; arena 因此是 57.7 GiB,而不是 2 GiB reserve 下的 ~62.7 GiB。prlimit --data 與 --ram-budget-mb 仍然是 65536 MB,RAM 上限沒有放寬。

2. arena 下限

要跑得起來,arena 至少要能放一個大 expert(18 MiB)+ I/O 緩衝。 本機 context 2048、-t 16 下,2 GiB 預算扣完剩不到,pager 直接拒絕啟動。

3. BF16 expert 需要另外補 kernel 分派

Laguna 的第 40–47 層是 BF16。參考版 sdq_moe.cpp 的 dot_for / act_type_for / quantize_act 沒有 BF16 case,若不補會落在未支援型別而 abort。已補。

4. 這個 fork 沒有 LLM_ARCH_BAILINGMOE3

從 xing4 帶過來的 swiglu clamp guard 引用了 LLM_ARCH_BAILINGMOE3,本 fork 沒有 這個列舉,編譯會失敗。改成只檢查 DEEPSEEK4 / DFLASH / STEP35。

5. hf CLI 在此環境壞掉

hf / 舊 huggingface-cli upload 會因 typer/click 版本不相容丟 AttributeError: 'StyledContext' object has no attribute '_opt_prefixes'。 用 huggingface_hub Python API(HfApi.upload_file / create_repo)上傳。

限制與注意事項

  • 這是一台 Intel Xeon Platinum 8559C(雙插槽)、16 vCPU cgroup 配額、 1999 GiB 實體 RAM 的機器;build 用 GGML_NATIVE=OFF + 明確 AVX512 旗標重現。
  • 本機有一支記憶體守衛,所有子行程合計 RSS > 80 GiB、或單一行程 RSS > 64 GiB 就會被 SIGKILL,所以無法用原生路徑整份載入 96 GB 做端到端對照; 正確性改以逐層 NumPy + 跨預算一致性證明。64 GiB 那列因此提高了 EPQ_RESERVE_MB(見「已知陷阱 1」)。
  • 速度是 CPU、單機、-t 16 的數字,沒有 GPU offload(EPQ 路徑強制 n_gpu_layers=0)。
  • Q4_K_M 的 attention 等非 expert 權重仍隨模型檔 mmap;EPQ 只分頁路由 expert。

證據

檔案 內容
results/strict_memory_levels.md 嚴格 RAM 上限掃描表
results/strict_memory_levels_final.json 掃描原始資料(含 pager stats 併入)
results/strict_low.json、results/strict_high.json、results/strict_64g.json 掃描的三個批次(64 GiB 為 EPQ_RESERVE_MB=6144)
results/strict_levels/level_*_r0.jsonl 各預算的 pager 指標(命中率、淘汰、I/O)
results/topk_ablation.json、results/topk_ablation_16g.json top-10 vs top-4 A/B(64 GiB / 16 GiB,含 greedy 輸出)
results/topk_ppl.json top-10 vs top-4 的 teacher-forced PPL
results/ppl_en.txt PPL 用的 799-token 文字(Project Gutenberg #11 開頭,public domain)
results/topk_levels/ top-k A/B 與 PPL 的 pager stats
results/least_stale.json LRU vs Least-Stale vs Least-Stale+位置(8/16/64 GiB)彙總
results/topk_evict_levels/ 上述淘汰策略實驗的 pager stats(含 stale/current 淘汰群組計數)
results/verify_layer1_q4k.txt 第 1 層(Q4_K)對 NumPy
results/verify_layer40_bf16.txt 第 40 層(BF16)對 NumPy
results/identity_4096mb.gen、results/identity_32768mb.gen 跨預算 greedy 一致性
results/epq_l1.1、results/epq_l40.40 上述兩層的 EPQ_MOE_DUMP 原始資料

致謝

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for HelloSun/laguna-s-2.1-Q4_K_M_EPQ

Quantized
(3)
this model

Paper for HelloSun/laguna-s-2.1-Q4_K_M_EPQ