--- pipeline_tag: text-generation library_name: gguf tags: - moe - expert-paging - cpu - gguf license: other base_model: mrexodia/openPangu-2.0-Flash-GGUF --- # openPangu-2.0-Flash-base Q3_K_M — EPQ 專家分頁 一套實驗性的 CPU 推論路徑:把未使用的 Mixture-of-Experts(MoE)權重留在 GGUF 檔案,推論時只把用到的 expert 讀進有界的 RAM arena,讓 40 GiB 的模型能在較小的 記憶體下推論。**本 repo 不含模型權重**。 ## 一鍵啟動 只需要一條指令,腳本會自動 clone llama.cpp、套用 patch、編譯、下載模型、啟動 `llama-server`: ```bash git clone https://huggingface.co/HelloSun/openPangu-2.0-Flash-base-Q3_K_M_EPQ cd openPangu-2.0-Flash-base-Q3_K_M_EPQ ./serve.sh ``` 完成後伺服器在 `http://localhost:22222`。第一次會下載約 41 GiB 的模型並編譯, 之後再跑同一個指令即可直接啟動。 常用調整(環境變數): ```bash RAM_BUDGET_MB=8192 PORT=8080 ./serve.sh # 更小的記憶體預算、換埠 EPQ_MODEL=/path/to/model.gguf ./serve.sh # 使用已下載的模型 ``` 只想做單一步驟時: ```bash ./serve.sh download # 只下載模型 ./serve.sh build # 只編譯 ./serve.sh run # 只啟動伺服器 ``` ## 模型資訊 - GGUF:`openPangu-2.0-Flash-base-Q3_K_M.gguf`(43,729,887,872 bytes = 40.727 GiB) - 共 46 層,前 2 層 dense;44 層 MoE - 每個 MoE 層 256 個 expert;每 token 使用 8 個;另有 1 個 shared expert - Expert 投影:gate/up 為 Q3_K;down 為 Q4_K - Expert 權重合計 39.10 GiB;單一 expert triplet = 3.55 MiB(3,727,360 bytes) - Router:sigmoid gating、`expert_weights_norm=true`、`expert_weights_scale=2.5` - EPQ 以 expert triplet 為單位分頁;routing 與權重計算仍由 llama.cpp 負責 ## 實驗環境 本 repo 的數據在下列 Docker 容器內實測(非模擬): | 項目 | 實際可用值 | |---|---| | 平台 | Docker 容器(AWS;kernel `6.18.48-107.148.amzn2023.x86_64`,Debian 13 trixie) | | CPU | Intel Xeon Platinum 8559C;主機可見 192 個 logical CPU,cgroup 配額 **16 CPUs**(`cpu.max 1600000/100000`) | | 記憶體 | 主機 `MemTotal` 約 2000 GiB;本容器 cgroup 上限 **96.9 GiB**(`memory.max 104000000000` bytes) | | Swap | 1.6 TiB(`SwapTotal`);所有量測 swap 增量為 **0** | | 磁碟 | overlay 7.9 TiB,可用約 2.2 TiB | | GPU | 容器可見 8 個 `/dev/nvidia0-7`,但 NVML 初始化失敗 → **無法使用 GPU,全程 CPU** | 程式以 `prlimit --data` 進一步施加 8/16/32 GiB 的匿名記憶體硬上限;下表即為在 這些上限下的實測結果。 ## 記憶體與 decode 效能 以 Linux `prlimit --data` 施加硬性匿名記憶體上限。每級跑兩次,條件相同:ctx 2048、 ubatch 128、16 threads、greedy、生成 47 個 decode token。每輪 swap 均為 0。 | 匿名硬上限 | EPQ arena | 槽位 | 匿名 HWM | RSS HWM | decode tok/s(中位數) | prefill tok/s(中位數) | hot hit | SSD MiB / decode token | |---:|---:|---:|---:|---:|---:|---:|---:|---:| | 8 GiB | 0.69 GiB | 199 | 2.13 GiB | 3.29 GiB | **1.59** | **5.0** | 0.00% | 1,282.2 | | 16 GiB | 9.08 GiB | 2,616 | 10.52 GiB | 11.68 GiB | **4.88** | **3.9** | 66.79% | 255.6 | | 32 GiB | 25.08 GiB | 7,225 | 26.52 GiB | 27.68 GiB | **4.89** | **2.1** | 74.84% | 131.9 | openPangu 有約 **7.1 GiB 的固定常駐量**,因此 4 GiB 匿名上限不可行;本版最小可用 設定為 8 GiB。對照組未改的 upstream llama.cpp 需要單一 15.47 GiB 的匿名 CPU_REPACK 緩衝,在 8/16 GiB 上限下無法載入模型(EPQ 仍可推論);32 GiB 下 upstream 約 8.6 tok/s 但 RSS 約 55 GiB。完整量測、upstream 對照與 I/O 稽核見 [`results/report.md`](results/report.md)。 ### 本機重現與最佳記憶體(2026-10-16) 上表為作者容器的量測。以下為另一台主機重跑的結果:Intel Xeon Platinum 8559C、16 threads、 主機記憶體約 2 TiB(與其他使用者共享)、無 cgroup 記憶體上限。模型為同一個 `openPangu-2.0-Flash-base-Q3_K_M.gguf`(43,729,887,872 bytes)。參數與上表一致:ctx 2048、 ubatch 128、16 threads、greedy、生成 47 個 decode token;每級 2 次。原始數據見 [`results/strict_memory_levels_rerun.json`](results/strict_memory_levels_rerun.json)。 | 匿名硬上限 | EPQ arena (GiB) | 槽位 | 匿名 HWM (GiB) | RSS HWM (GiB) | decode tok/s(兩次) | decode 中位數 | prefill 中位數 | hot hit | evictions(第 1 次) | SSD MiB / decode token | |---:|---:|---:|---:|---:|---|---:|---:|---:|---:|---:| | 8 GiB | 0.69 | 199 | 2.13 | 3.29 | 1.59, 1.59 | **1.59** | 4.60 | 0.00% | 20,055 | 1,282.2 | | 12 GiB | 5.08 | 1,464 | 6.52 | 7.69 | 3.80, 4.02 | **3.91** | 4.05 | 56.35% | 7,377 | 416.1 | | 16 GiB | 9.08 | 2,616 | 10.52 | 11.68 | 4.46, 4.16 | **4.31** | 3.05 | 66.79% | 4,110 | 255.6 | | 20 GiB | 13.08 | 3,768 | 14.52 | 15.68 | 4.89, 4.70 | **4.79** | 2.50 | 72.26% | 1,850 | 171.6 | | 24 GiB | 17.08 | 4,920 | 18.51 | 19.68 | 4.61, 4.66 | **4.63** | 2.05 | 74.79% | 187 | 132.8 | | 32 GiB | 25.08 | 7,225 | 26.52 | 27.68 | 3.70, 3.48 | **3.59** | 1.35 | 74.84% | 0 | 131.9 | `SDQ_RESERVE_MB` 為 8 GiB 採 7400、其餘各級採 7000(6.84 GiB)。12、20、24 GiB 的值沿用 16、32 GiB 的換算:arena ≈ 上限 − 6.84 GiB − 0.08 GiB,與實測吻合。 六級皆 swap 為 0,且匿名峰值均低於上限。 **結論** - **建議:20 GiB(`RAM_BUDGET_MB=20480`)。** 本機 decode 中位數 4.79 tok/s 為本次最高,RSS 峰值 15.68 GiB。 - **省記憶體:16 GiB(`RAM_BUDGET_MB=16384`)。** decode 約為 20 GiB 的 90%(4.31 tok/s),RSS 峰值 11.68 GiB。 - **8 GiB 可執行但明顯變慢。** arena 只有 0.69 GiB,hot hit 為 0%,每 token 從 SSD 讀取約 1,282 MiB,decode 1.59 tok/s。 - **24 GiB 以上沒有收益。** 24 GiB 時 evictions 已降到 187;32 GiB 時為 0,hit rate 與 24 GiB 相同(約 75%),decode 反而降到 3.59 tok/s。 - 32 GiB 下 evictions = 0,因此每次 miss 都是該 expert 第一次被載入。本次生成的 miss 次數為 5,095,即約 5,095 個不同 expert 被載入(全部 11,264 個中的一部分)。這部分的 miss 無法靠增加記憶體消除。 - prefill tok/s 隨 arena 增大而單調下降(4.60 → 1.35)。原因未查明,decode 數據不受此項直接影響。 **注意** - 主機與其他使用者共享,同一設定單次結果的差異可達 20% 以上(16 GiB 冒煙測試另一次為 3.37 tok/s,與表中中位數 4.31 相差約 22%)。判讀以趨勢為主。 - 作者容器與本機的 32 GiB decode 差距(4.89 vs 3.59)可能來自主機負載,未進一步排查。 重現指令(需先依 `serve.sh build` 的步驟建置 `llama.cpp/build/bin/sdq-chat`;`--evict-helper` 預設為 `/tmp/pagecache_resident`,不存在時略過清快取步驟): ```bash python3 strict_bench.py --model "$EPQ_MODEL" --shards "$EPQ_MODEL" \ --bin llama.cpp/build/bin/sdq-chat \ --levels-mb 8192 12288 16384 20480 24576 32768 --repeat 2 --threads 16 --ctx 2048 \ --ubatch 128 --n-predict 48 \ --reserve-map 8192:7400,12288:7000,16384:7000,20480:7000,24576:7000,32768:7000 ``` ## 正確性驗證 | 檢查 | 結果 | |---|---| | NumPy 反量化 vs ggml Q3_K/Q4_K | **36/36 完全相等**(layer 2、5、20、45;expert 0、17、255) | | 單 expert L2/L5/L20/L45 E17 | 全部 PASS;最大相對誤差 0.0135–0.0204;correlation ~0.9998 | | 全層(真實 router)L2/L20/L45 | 全部 PASS;最大相對誤差 0.0048–0.0269;correlation ~0.9997–0.9998 | 驗證腳本:`verify_dequant.py`、`verify_epq.py`、`verify_moe_layer.py`。 ## 檔案內容 - `serve.sh` — 一鍵建置與啟動 - `llama.cpp-openpangu-v0.7.0.diff`、`0001-epq-openpangu.patch` — llama.cpp 整合 - `src/sdq_*.{cpp,h}` — EPQ 原始碼(沿用先前 EPQ 移植的內部命名) - `strict_bench.py`、`verify_*.py`、`watch_io.py`、`ggml_deq.cpp`、 `pagecache_resident.cpp` — 量測與驗證工具 - `results/` — 原始數據與報告 模型授權與使用條款依上游 openPangu 模型規定。