隨筆

在 48GB Mac mini 上跑 27B 大模型:量化、KV 快取與上下文的工程取捨

Hohoo.

48GB Mac mini 本地部署 27B 模型的實測記錄:量化方案、KV 快取推導、統一記憶體 OOM 與上下文取捨。

2026-10-26 · Hohoo

瀏覽統計暫不可用

本文目錄 8
跳至正文 ↓

在 48GB 統一記憶體的 Mac mini 上跑 27B 量級的本地模型,成本最低,坑也最多。這篇記錄的不是「能跑通」,而是為什麼這樣配、為什麼那樣會崩——把量化、KV 快取、統一記憶體三個子系統拆到參數級。

結論速覽

決策點 結論 原因
模型規模 27B Q4 權重 16GB 留出 32GB 給 KV 與激活
量化方案 oQ4 類 4-bit(含異常值通道) 純 4-bit 在長文本質量上劣化,異常值通道是折衷
KV 快取 4-bit 每 token 開銷減半,長上下文救命
上下文 32K 封頂 KV 膨脹 + 激活峰值,64K 不可行
MTP 開啟 多 token 預測頭,花 ~2GB 換 30%+ 吞吐
定位 輔助/回退模型 64K 硬門檻決定它不能當主模型

1. 模型選型:27B 是甜點,但「Q4」不是一回事

權重記憶體的構成(以 4-bit + 異常值通道 + MTP 方案為例):

總權重 ≈ 主幹參數(4-bit) + Embedding(通常保持 16-bit) + MTP 頭(4-bit) + 輸出頭(16-bit)

27B 參數若全部壓到 4-bit:27B × 0.5B ≈ 13.5GB。但工程上不會這麼幹——Embedding 和輸出頭對精度敏感,通常保持高位,主幹裡也會有少量異常值通道保持高位。最終落到 16GB 左右,是「可用精度」和「記憶體預算」的平衡點。

量化方案對比:

方案 權重佔用 精度 速度 結論
FP16 ~54GB 基準 最慢 48GB 機器不可行
INT8 ~27GB 幾乎無損 中 上下文預算被壓縮
純 4-bit(NF4 類) ~16GB 長文本偶發質量劣化 快 可用但不夠穩
4-bit + 異常值通道(oQ4 類) ~16GB 接近 8-bit 快 推薦

4-bit 的核心矛盾:權重矩陣裡存在少量「異常值」通道,數值幅度遠大於其他通道,一刀切壓 4-bit 會讓這些通道量化誤差爆炸。oQ4 類方案的思路是把這些通道單獨識別出來保持高位——代價是幾個百分比的額外記憶體,換來長文本質量的明顯穩定。

MTP(多 token 預測)頭:不是「預測下一個詞」,而是並行預測未來若干個 token,再用小模型逐級校驗接受。吞吐提升通常 30–50%,代價是一份小頭參數 + 校驗計算,總記憶體開銷約 2GB。對 48GB 機器,這筆交易值得做。


2. KV 快取:上下文真正的成本中心

自回歸生成時,每個 token 的注意力 Key/Value 都要快取,KV 快取隨上下文線性增長——這是長上下文最大的隱性成本。

KV 每 token 開銷公式:

KV/token = 2 × 層數 × KV頭數 × head_dim × 每元素位元組數

以典型 GQA 架構為例(48 層、8 個 KV 頭、head_dim 128,代入你所用模型 config.json 的實際值):

快取位寬 每 token 8K 上下文 32K 上下文 64K 上下文
FP16 ~192KB 1.5GB 6.3GB 12.6GB
4-bit ~96KB 0.75GB 3.2GB 6.3GB

GQA(分組查詢注意力)是關鍵。 8 個 KV 頭對 48 個查詢頭,KV 開銷是全注意力的 1/6。如果沒有 GQA,32K 上下文的 KV 快取會直接翻倍到 12.6GB,這台機器基本告別長上下文。

由此看 64K 為什麼不可行:

  • KV 64K × 4-bit ≈ 6.3GB(理論可接受)
  • 但峰值激活值、生成時的臨時緩衝、以及 Metal 堆的碎片,會讓實際峰值遠超線性估算
  • 在 wired_limit 約束下,長上下文推理觸發 Metal OOM 是必然,不是偶然

KV 4-bit 的精度代價:小數位被壓掉後,長程注意力的細微排序可能出錯。本地工具場景(代碼補全、摘要、問答)影響可忽略;但涉及長文檔精確定位的任務,需要實測驗證。這也是「先用、再驗」的原因。


3. 統一記憶體:為什麼「記憶體還有剩」也會 OOM

Apple Silicon 是統一記憶體——CPU 和 GPU 共享同一塊物理記憶體,但被劃分為不同堆區:

  • CPU 堆:進程位址空間,權重加載、中間張量走這裡
  • Metal GPU 堆:推理計算與 KV 快取的主戰場,受 iogpu.wired_limit_mb 約束
  • wired 限制:GPU 可佔用的「已釘住」記憶體上限

關鍵坑:Activity Monitor 裡顯示「記憶體充足」不代表 GPU 堆沒滿。GPU 堆的可用空間由 wired 限制和已分配緩衝共同決定——OOM 常常發生在「總記憶體還有富餘」的時候。

排障順序:

  1. 看日誌 gateway.error.log 中的 Metal OOM 行
  2. 確認 KV 位寬、上下文長度是否超過預算
  3. 檢查 model_settings.json 是否生效(改完必須重啟服務——KV 快取分配器在加載時初始化,熱改無效)
  4. 調整 iogpu.wired_limit_mb 前先確認這是「限制」而非「瓶頸」——曾提到 45219,重啟後失效,說明該參數並非每次啟動都持久化,需要逐次驗證

4. 上下文長度:32768 是怎麼來的

模型本身支援遠大於 32K 的上下文,但實際交付被三重約束卡在 32768:

  1. 顯存預算:見第 2 節,KV 線性膨脹 + 激活峰值
  2. 服務端配置:model_settings.json 中的上下文上限,是保護性封頂
  3. 工具鏈門檻:Agent 框架主模型要求 64K 起步,32K 直接不滿足,只能降級為輔助/回退

配置層的正確姿勢:按 provider 設上下文,而不是全域一個值。 全域 context_length 一個值會卡死所有模型——雲端大視窗模型被壓小浪費預算,本地小視窗模型被拉大直接 OOM。正確做法是每個 provider 獨立配置(如 xfyun 給 262144、omlx 給 32768),框架在路由時按模型能力分配任務。

與 prompt caching 的關係:KV 快取同時也是 prompt caching 的載體。前綴穩定時,重複 prompt 的 KV 可以直接命中複用,省掉重複 prefill 計算。這要求請求側保持前綴結構穩定——這也是為什麼「上下文工程」(預算、卸載、壓縮)和 KV 量化是同一枚硬幣的兩面,而非兩個話題。


5. 排障手冊

症狀 排查路徑
生成突然中斷、報錯 看 gateway.error.log 的 Model fallback 與 Metal OOM
輸出明顯變慢 上下文接近上限時 KV 膨脹,檢查 agent.log 每輪實際上下文
配置改了沒生效 ~/.omlx/bin/omlx restart,確認服務重啟後再測
長 prompt 直接崩 降上下文封頂,或臨時降 KV 位寬驗證
記憶體夠卻 OOM 查 iogpu.wired_limit_mb 與 GPU 堆實際佔用,別信系統總記憶體

6. 給 48GB 機器的推薦配置

項目 推薦值 說明
模型 27B Q4(4-bit + 異常值通道 + MTP) 權重 16GB,留 32GB 給 KV 與激活
量化 4-bit + 異常值通道 精度與記憶體的折衷
KV 快取 4-bit 32K 上下文從 6.3GB 降到 3.2GB
上下文封頂 32K 務實上限,不要追 64K
MTP 開啟 吞吐 30%+ 提升,成本 ~2GB
配置生效 改完必須 restart model_settings.json 熱改無效
角色定位 輔助/回退模型 主模型留給 64K+ 的雲端方案

7. 總結

48GB Mac mini 跑本地 27B 模型,本質是在三個子系統之間做預算分配:

  1. 量化預算:權重壓到 16GB,用異常值通道保住精度
  2. KV 預算:4-bit + GQA 讓 32K 上下文落在 3.2GB,64K 則翻倍到 6.3GB 並觸發 OOM
  3. 記憶體預算:統一記憶體不是無限池,GPU 堆受 wired 限制,OOM 與總記憶體餘量無關

把這套參數配好、重啟生效、按 provider 設上下文,本地推理就能穩定服務於編碼輔助與敏感資料處理。剩下的——更大的上下文、更高的吞吐——交給更大的記憶體,或雲端。

本地模型的意義不是替代雲端,而是補上雲端給不了的那一格:隱私、離線、可控。

寫給未來的自己合上文章前,留下一點自己的理解。

你的私人學習便箋,只存在目前瀏覽器,不會上傳或公開。清理瀏覽器資料會遺失,請匯出留存。三種語言共用這篇文章的便箋。

接下來,走哪條路?

這篇文章暫時沒有對應的已發布關聯內容。 回到學習路線選擇下一步 →

依據文章關聯與同主題已發布內容整理,不使用隨機推薦。

← 全部文章