270 億參數上 iPhone 的實戰路線

270 億參數上 iPhone 的實戰路線

📌 本文重點

  • 27B 大模型壓縮後可在手機端側實用
  • 量化 +剪枝 +蒸餾是端側部署核心組合
  • 善用 NPU/GPU 與 RAG/Agent 架構提升體驗

在端側塞進 Qwen 3.6-27B 這種體量的模型,直接解決了三個痛點

  1. 隱私 & 合規:聊天、RAG 查文件、簡單 Agent 全在本機,不丟到雲端。
  2. 互動延遲:減少網路 RTT,短指令(改句子、補程式)能接近即時回應。
  3. 成本 & 擴展:不用為每個活躍使用者準備一個雲端 GPU,端側模型成為主力推理,引流雲端模型解決長上下文或高難任務。

PrismML 把 Qwen 3.6-27B 壓到能在 iPhone 17 Pro 上跑,給了我們一個清楚訊號:27B 級模型上手機是可行路線,但你要整套壓縮 + runtime + 系統調優一起做


重點說明:端側大模型的幾個關鍵技術

1. 量化:從 16bit 壓到 4bit / 2bit

目的:壓縮權重體積 + 減輕記憶體頻寬壓力

  • 粗估:
  • FP16:27B × 2 bytes ≈ 54 GB(純權重就爆)
  • 4bit:27B × 0.5 bytes ≈ 13.5 GB
  • 2bit:27B × 0.25 bytes ≈ 6.75 GB

💡 關鍵: 把 27B 權重從 54GB 壓到約 7–14GB,是端側部署能否落地的基礎門檻

  • 實務上常用 mixed-precision
  • attention / embedding 保留 8bit 或 16bit
  • MLP 可用 4bit 或 2bit(配合 group-wise scaling)
  • 在 iOS 端常見兩種路線:
  • GGUF + llama.cpp:用 Q4_K_M / Q3_K_M 這類 profile
  • Core ML / MLC:用 symmetric int4 / int8,用 LUT 把量化常數 bake 進權重

對專案的好處:

  • 同一隻 app 可以提供 小模型 + 壓縮大模型 的雙模式:日常用 7B,開「專業模式」時啟動 27B(但速度變慢 + 發熱)。

2. 剪枝與低秩分解:不只變小,還要能跑快

量化只壓體積,不一定壓算力;要在功耗有限的手機跑得動,需要再用:

  1. 結構化剪枝(structured pruning)
  2. 砍掉整個 head、channel 或 FFN neuron。
  3. 例如把 32 heads 剪到 24 heads,或把 FFN hidden dim 從 8k 降到 6k。
  4. 這會直接減少 MACs,對 NPU / GPU 友善。
  5. 低秩分解(LoRA / SVD 分解 FFN 權重)
  6. 把大矩陣 (W \in \mathbb{R}^{d \times d}) 分成 (U \in \mathbb{R}^{d \times r}, V \in \mathbb{R}^{r \times d}),r ≪ d
  7. 搭配蒸餾可以維持合理能力。

PrismML 類型的方案多半是 多階段:量化 + 剪枝 + 蒸餾 + runtime 特化 一起做,才有可能把 27B 折成 iPhone 等級的 latency。

3. KV Cache / 推理圖優化:token/s 的決勝點

端側 LLM 的痛點不是 model load,而是 持續推理的 token/s 與發熱

關鍵是:

  • KV Cache 緊縮
  • 精度壓到 8bit/4bit(與權重量化分開考慮)。
  • 善用 sliding window / attention sink,減少長對話時的 KV 佔用。
  • 推理圖優化(graph optimization)
  • 透過 MLC / Core ML / 自研 runtime 把整段 transformer block fuse 成 1–2 個大 kernel。
  • 減少 CPU ↔ NPU ↔ GPU 之間的 context switch。

效果:在 iPhone 17 Pro 這種等級裝置,27B 壓縮到 4bit + 優化 graph,合理預期 5–10 tok/s 左右(看溫控),可以應付聊天、簡單 RAG,但不是寫 3k 行程式碼那種長輸出場景。

💡 關鍵: 端側大模型的體感速度主要由 token/s 決定,5–10 tok/s 大致只適合短回覆與簡單任務

4. 善用手機 NPU / GPU:不是「丟上去就快」

iOS 上大致有三條路:

  1. Core ML + ANE(NPU)
  2. 最省電,但要配合 Core ML 支援的 op / dtype,需要前置轉換。
  3. Metal / MLC
  4. 走 GPU / CPU + 少量 NPU,對自訂 kernel 比較自由。
  5. llama.cpp(Metal 後端)
  6. 直接吃 GGUF,開 Metal 加速;部分 op 仍在 CPU。

實務上可以採用 混合策略:embedding / 前幾層在 NPU,後面在 GPU / CPU,平衡記憶體與溫度。


實作範例:在 iOS 上做聊天 / RAG / Agent 的架構

下面給一個「實際可落地」的 27B 壓縮部署思路,用 pseudo code 表示。

1. 推理框架選型與模型準備

方案 A:llama.cpp + GGUF(開發週期短)

  1. 先把 Qwen 3.6-27B 轉成 GGUF 並量化(在桌機完成):
python convert_qwen_to_gguf.py \
  --model qwen-3.6-27b \
  --out qwen-3.6-27b-q4_k_m.gguf

./quantize \
  qwen-3.6-27b-q4_k_m.gguf \
  qwen-3.6-27b-q4_k_m.gguf \
  Q4_K_M
  1. iOS 使用 llama.cpp Swift / C API:
let params = gpt_params_default()
params.n_ctx = 4096
params.n_gpu_layers = 20   // 前 20 層走 Metal

let ctx = llama_init_from_file("qwen-3.6-27b-q4_k_m.gguf", params)

func infer(prompt: String) -> String {
    llama_reset(ctx)
    llama_eval_prompt(ctx, prompt)
    var output = ""
    while !shouldStop(output) {
        let token = llama_eval_next(ctx)
        output += tokenizer.decode(token)
    }
    return output
}

注意:n_gpu_layers 設太高,會爆 VRAM / 發熱;設太低,會被 CPU 拖死。實測需要在 8–24 之間找平衡。

方案 B:MLC LLM + Metal(更深度優化)

  • 透過 MLC 工具鏈把 Qwen 3.6-27B 壓成 int4 + fused kernels,生成 iOS 專用模型包:
mlc_llm convert qwen-3.6-27b \
  --quantization q4f16_0 \
  --target iphone

mlc_llm build ios qwen-3.6-27b-q4f16_0

iOS 端呼叫:

let engine = MLCChatEngine(model: "qwen-27b-q4f16_0")

engine.generate(
  prompt: userPrompt,
  maxTokens: 512,
  temperature: 0.7,
  streamHandler: { token in
    appendToUI(token)
  }
)

實務建議:若不是要深度客製 runtime,MLC 會比自己改 llama.cpp 更好拿穩定 fps 和電源管理

2. RAG:本機嵌入 + 文件切片

架構:

  • 輕量 embedding 模型(如 384–768 dim)放端側
  • 文本切 chunk(512–1024 tokens),建立本地向量索引。
  • 查詢時在端側 embed + ANN 搜索 + top-k context 拼回 prompt,再丟給 27B 生成。

簡易 Swift 流程(以 Core ML embedding 模型為例):

// 1. 建立索引(啟動或背景時)
for chunk in documentChunks {
    let emb = embeddingModel.encode(text: chunk.text) // [Float]
    annIndex.add(vector: emb, id: chunk.id)
}

// 2. 查詢時
func ragAnswer(query: String) -> String {
    let qEmb = embeddingModel.encode(text: query)
    let topK = annIndex.search(query: qEmb, k: 4)
    let context = topK.map { $0.text }.joined(separator: "\
---\
")

    let prompt = """
你是一個助理。以下是知識庫內容:
\(context)

問題:\(query)
請根據知識庫回答,若沒有明確答案就說不知道。
"""
    return infer(prompt: prompt)
}

好處

  • 敏感文件不離開手機;
  • 即使 27B 很慢,RAG 因為 context 精準,需要生成的 token 也變少

3. 簡單 Agent:有限循環 + 工具調用

在端側做 Agent 不要太貪心,建議:

  • 限制 max steps(例如 4–6 步)。
  • 工具呼叫只做本機能力:檔案讀寫、日曆、藍牙裝置等。

簡化 pseudo code:

struct ToolCall { let name: String; let args: [String: Any] }

func runAgent(task: String) {
    var state = ""
    for step in 0..<6 {
        let prompt = buildAgentPrompt(task: task, state: state)
        let output = infer(prompt: prompt)

        if let tool = parseToolCall(output) {
            let result = executeTool(tool)
            state += "\
[tool-result]\
" + result
        } else {
            renderFinal(output)
            break
        }
    }
}

這種架構在 27B 上 iPhone 實務可行,但要注意:token/s 慢 → 每一輪推理越短越好,prompt 盡量模板化、少廢話。


建議與注意事項:坑在哪、怎麼踩得比較輕

1. 記憶體與冷啟動

  • iPhone 17 Pro 類級別裝置,27B 4bit 純權重就十幾 GB,實務上需:
  • 模型分片 / 分層載入
    • 常駐 7B 小模型;
    • 27B 大模型在「高需求模式」才 mmap 載入部分層(如後 12 層)。
  • 或採 Mixture-of-Experts(MoE) 類方案,如 GLM 5.2 + Flash MoE,那類模型在 Mac M5 上可跑 2–2.8 tok/s,給端側設計方向參考:不是所有 layer 都要 full dense
  • 冷啟動:
  • 首次 load 27B 量化模型,可能 3–10 秒,一定要做 loading UI + 延遲初始化(app 啟動時先載小模型,使用者開「專業模式」才載大模型)。

💡 關鍵: 3–10 秒的冷啟動延遲是端側大模型的 UX 關鍵,必須用分層載入與明確 UI 掩護

2. 電量與發熱

  • 連續推理 1–2 分鐘,大模型會把 SoC 拉到 TDP 上限,掉頻後 token/s 會明顯下降。
  • 實作上建議:
  • 限制 max_tokens,避免長篇輸出。
  • 使用 溫度監控(ThermalState,高溫時降速:
if ProcessInfo.processInfo.thermalState == .serious {
    params.n_threads = max(2, params.n_threads / 2)
}

3. 隱私與合規

  • Ce 合規角度,端側推理 + 本地 RAG 是加分項,但要注意:
  • 日誌、錯誤回報不得包含原文檔內容。
  • 若有雲端 fallback,要明確 UI 提示「此問題將送出至伺服器」。

4. 測試方法與性能預估

建議自建簡單基準腳本,統計:

  • 冷啟動時間(model load 完到第一 token)
  • token/s(前 50 tokens、整段平均)
  • 電池掉電率與機身溫度(5 分鐘連續推理)

例:用 llama.cpp CLI 在開發機 / TestFlight 內部版測一次:

./main -m qwen-27b-q4_k_m.gguf \
  -p "Hello" -n 256 -s 42 --timings

官方輸出會給:

  • prompt eval time / token
  • generation eval time / token

再用這些數據估算:

  • 聊天:平均 50–100 tokens → 是否能在 3–5 秒內完成?
  • RAG:查詢 + 150 tokens → 整體延遲能否低於 10 秒?

結論:端側 27B 的實際策略

對多數 iOS 專案,建議的 可實作路線 是:

  1. 產品層:
  2. 7B–8B 模型 作為主力。
  3. 提供「離線高精度模式」載入壓縮 27B,給重度用戶 & 高價方案。
  4. 技術層:
  5. 量化採 4bit mixed-precision,配合剪枝 + 蒸餾。
  6. 選擇 MLC / llama.cpp Metal 後端,不要自幹 runtime,除非是公司核心技術。
  7. 系統層:
  8. 做好 分層部署 + 延遲載入 + 熱管理
  9. 嵌入模型 + RAG 索引用小模型處理,27B 只負責最終生成。

這樣設計,你可以合理地把「看起來誇張的 27B」塞進 iPhone 裡,並且在聊天、RAG、輕量 Agent 這幾個場景拿到實際可用的體驗。真正的難點不只在模型壓縮,而是在整條端側推理鏈的工程化

🚀 你現在可以做的事

  • 在開發機上用 llama.cpp 或 MLC 先跑一版 Qwen 3.6-27B 4bit,量測 token/s 與冷啟動時間
  • 選定一個 7B 模型做端側 RAG PoC,實作本地嵌入與向量索引流程
  • 規劃產品中的「專業模式」或「離線高精度模式」,設計 7B / 27B 雙模型切換與載入策略

留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *