用 llama.cpp 在 iOS 與 Android 上執行 GGUF
如何把微調好的模型打包成 GGUF 並用 llama.cpp 跑在 iOS 和 Android 上:量化檔位怎麼選、真實檔案體積、吞吐表現,以及模型派送的幾種做法。
你的微調模型在筆電上運作得很好。回應速度快,品質正是你需要的,評估結果也很穩固。現在你需要讓它在 50,000 台 iPhone 上運行。
這是大多數行動開發者停下腳步的地方。將 AI 模型打包進應用程式二進位檔聽起來令人卻步:二進位檔大小、記憶體限制、熱節流、平台特定的建構系統。但工具已經成熟了。像 PocketPal AI 這樣的專案證明,一個完整的 llama.cpp 驅動的聊天介面可以在今天的旗艦手機上流暢運行。從微調權重到上架行動應用程式的路徑是明確的、可重複的,而且比 18 個月前輕鬆得多。
本指南將逐步說明每個環節:GGUF 格式、量化選擇、iOS 和 Android 整合、效能預期,以及交付策略。
GGUF 是什麼
GGUF(GGML Unified Format)是由 llama.cpp 專案維護的單一檔案模型格式。在 GGUF 之前,分發模型意味著要處理多個獨立的權重檔案、設定檔、分詞器詞彙表和特殊 token 檔案。GGUF 將所有內容打包成一個具有明確標頭結構的可攜式二進位檔。
一個 .gguf 檔案裡面包含什麼:
- 模型權重(量化或全精度)
- 架構中繼資料(層數、注意力頭數、上下文長度、rope 參數)
- 分詞器詞彙表和合併規則
- 特殊 token 定義(BOS、EOS、填充 token)
單一檔案設計使 GGUF 非常適合行動端部署。你引用一個路徑、載入一個檔案,推論就開始了。不需要多檔案設定。llama.cpp 原生讀取 GGUF,而 Ollama 也使用 GGUF 作為其內部儲存格式。
量化等級說明
量化將模型權重從 32 位或 16 位浮點數壓縮為較低精度的整數。這大幅減少了檔案大小和記憶體佔用,品質損失不大,對於領域特定任務通常感覺不到。
| 格式 | 每權重位元數 | 相對 FP16 品質 | 備註 |
|---|---|---|---|
| Q4_K_M | 約 4.5 位元 | 基準測試上 -2 至 4% | 行動端最佳大小-品質平衡 |
| Q5_K_M | 約 5.5 位元 | 基準測試上 -1 至 2% | 連貫性明顯更好,大約大 20% |
| Q8_0 | 8 位元 | 幾乎無損失 | 接近無損,Q4_K_M 的 2 倍大小 |
| F16 | 16 位元 | 基準線 | 對大多數行動目標太大 |
Q4_K_M 的具體檔案大小:
- Llama 3.2 1B:808 MB
- Llama 3.2 3B:2.02 GB
- Phi-3-mini 3.8B:約 2.3 GB
- Llama 3.1 8B:約 4.9 GB
對行動端而言,Q4_K_M 是預設建議。與 Q5_K_M 相比,對於領域特定微調模型(已經限制了輸出分布)的品質差異很小,而大小的節省很顯著。Q8_0 保留給桌面或離線功能平板電腦使用,在那裡儲存空間限制較小。
1B 模型的 Q4_K_M(808 MB)可 以輕鬆放在任何現代手機上。3B 模型(2.02 GB)在旗艦裝置上運作良好。4.9 GB 的 8B 模型只適合作為具有 6 GB 以上記憶體裝置的可下載資源,不適合作為打包的二進位檔。
llama.cpp 如何在行動端運作
llama.cpp 是一個純 C/C++ 的 GGUF 模型推論引擎。核心函式庫除了 C++ 標準函式庫外沒有外部依賴,這就是為什麼它幾乎可以在每個平台上編譯。
加速方面,llama.cpp 使用:
- Metal(iOS/macOS)透過 Apple 的 Metal Performance Shaders 進行 GPU 運算
- OpenCL 或 Vulkan(Android)在 Qualcomm、ARM 和 MediaTek 晶片上進行 GPU 運算
- NEON SIMD 指令用於 ARM CPU 矩陣運算
- NNAPI(Android)作為較新晶片組上硬體 NPU 的委派路徑
專案提供了範例二進位檔,但對於行動端整合,你需要將它編譯為靜態函式庫,並透過平台綁定呼叫它。官方的 Swift 和 Kotlin 包裝器模式都已經存在,而 PocketPal AI 專案是兩個平台都有良好維護的開源參考。
iOS 整合
建構設定
llama.cpp 使用 CMake。對於 iOS,你需要交叉編譯一個針對 arm64-apple-ios 的靜態函式庫:
cmake -B build-ios \
-DCMAKE_TOOLCHAIN_FILE=ios.toolchain.cmake \
-DPLATFORM=OS64 \
-DGGML_METAL=ON \
-DBUILD_SHARED_LIBS=OFF \
-DLLAMA_BUILD_TESTS=OFF \
.
cmake --build build-ios --config ReleaseGGML_METAL=ON 旗標啟用 Metal GPU 加速。產生的 libllama.a 和 libggml.a 靜態函式庫連結到你的 Xcode 專案中。
或者,llama.cpp Swift Package Manager 套件提供了預建構的 XCFramework,位於 github.com/ggml-org/llama.cpp,它會為你處理 CMake 步驟。
Swift 綁定
SPM 套件暴露了一個 LlamaContext Swift 類別,具有 init(model: URL) 初始化器和一個非同步的 complete(prompt: String) -> AsyncStream<String> 方法用於串流 token。
最小化的整合看起來像這樣:
import llama
class ModelRunner {
private var context: LlamaContext?
func load(modelURL: URL) async throws {
context = try await LlamaContext.createContext(path: modelURL.path)
}
func stream(prompt: String) -> AsyncStream<String> {
guard let ctx = context else { return AsyncStream { $0.finish() } }
return AsyncStream { continuation in
Task {
for await token in ctx.completionStream(text: prompt) {
continuation.yield(token)
}
continuation.finish()
}
}
}
}iOS 上的記憶體管理
iOS 會在沒有警告的情況下終止超出記憶體預算的程序。關鍵規則:
- 載入模型一次,在應用程式啟動時或在專用的背景執行緒上。不要每次請求都重新初始化。
- 保守設定
n_ctx。 上下文長度直接決定 KV 快取大小。2048 token 的上下文比 8192 token 的使用少得多的記憶體。大多數行動端使用場景需要少於 2048 個 token。 - 監控記憶體警告。 實作
applicationDidReceiveMemoryWarning,並透過清除 KV 快取(呼叫llama_kv_cache_clear)而不是卸載整個模型來回應。 - 使用
mmap載入旗標。 llama.cpp 支援記憶體映射檔案載入(--mmap),讓作業系統可以將模型權重分頁進出。在 iOS 上,這會降低峰值 RSS,代價是首個 token 延遲略高。
1B Q4_K_M 模型(808 MB)加上 2048 token 的 KV 快取,可以在 iPhone 12 及以後的裝置記憶體預算內舒適運行。3B 模型需要至少 6 GB 記憶體的裝置(iPhone 15 Pro 及以後)。
Android 整合
NDK 建構
Android 使用 NDK 工具鏈處理 C/C++ 程式碼。將 llama.cpp 作為子模組新增或將其原始碼複製到 app/src/main/cpp/,然後設定 CMakeLists.txt:
cmake_minimum_required(VERSION 3.22)
project(llama_android)
add_subdirectory(llama.cpp)
add_library(llama_jni SHARED jni_bridge.cpp)
target_link_libraries(llama_jni llama ggml android log)在 build.gradle(app 模組)中:
android {
defaultConfig {
externalNativeBuild {
cmake {
cppFlags "-std=c++17"
arguments "-DGGML_OPENMP=OFF", "-DLLAMA_BUILD_TESTS=OFF"
}
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
}要在 Android 上啟用 GPU 加速,傳遞 -DGGML_OPENCL=ON(需要 OpenCL 標頭)或 -DGGML_VULKAN=ON(需要 Vulkan SDK)。在 Snapdragon 裝置上,Qualcomm 的 QNN 後端透過 -DGGML_QNN=ON 提供 NPU 加速,但這需要 Qualcomm AI SDK。
JNI 包裝器和 Kotlin 橋接
在 jni_bridge.cpp 中建立一個精簡的 JNI 橋接,包裝 llama.cpp C API:
extern "C" JNIEXPORT jlong JNICALL
Java_com_yourapp_LlamaWrapper_loadModel(JNIEnv *env, jobject, jstring modelPath) {
const char *path = env->GetStringUTFChars(modelPath, nullptr);
llama_model_params mparams = llama_model_default_params();
llama_model *model = llama_load_model_from_file(path, mparams);
env->ReleaseStringUTFChars(modelPath, path);
return reinterpret_cast<jlong>(model);
}在 Kotlin 端,一個精簡的包裝類別持有原生指標並暴露基於協程的 API:
class LlamaWrapper(private val modelPath: String) {
private var modelHandle: Long = 0
fun load() {
modelHandle = loadModel(modelPath)
}
fun complete(prompt: String, onToken: (String) -> Unit) {
generateTokens(modelHandle, prompt, onToken)
}
fun close() {
if (modelHandle != 0L) {
freeModel(modelHandle)
modelHandle = 0
}
}
private external fun loadModel(path: String): Long
private external fun generateTokens(handle: Long, prompt: String, cb: (String) -> Unit)
private external fun freeModel(handle: Long)
companion object {
init { System.loadLibrary("llama_jni") }
}
}將模型載入保持在綁定到 Application 生命週期的單例中,而不是個別的 Activity。在每次畫面切換時重新建立模型會導致明顯延遲和過度的電池消耗。
效能基準
在當前旗艦硬體上運行 Q4 或 Q8 量化模型的實際吞吐量:
| 裝置 | 模型 | 量化 | Token/秒 | 後端 |
|---|---|---|---|---|
| iPhone 17 Pro | 1.5B | INT8 | 136 | NPU (Apple ANE) |
| Galaxy S25 Ultra | 2B | INT8 | 91 | NPU (Google Tensor) |
| iPhone 16 Pro | 1.5B | Q4 | 22(持續) | Metal GPU |
| Snapdragon 8 Elite | 13B | Q4 | 20+ | Hexagon NPU |
| iPhone 15 Pro | 1B | Q4 | 約 18 | Metal GPU |
| 中階 Android (SD 7s Gen 3) | 1B | Q4 | 8-12 | CPU NEON |
作為參考:人類的閱讀速度大約是每秒 5-7 個 token。上面列出的每一款旗艦裝置,搭配 1-3B 模型都超過了舒適的閱讀速度。即使是中階裝置每秒 8-12 個 token,對大多數使用場景也感覺流暢。
NPU 的數據(iPhone 17 Pro 每秒 136 個 token,Galaxy S25 Ultra 每秒 91 個 token)代表了能力的躍進。在 NPU 加速路徑上,延遲降至 CPU 基準線的大約 1-20%,而每兆運算的功耗效率顯著優於 GPU 或 CPU 推論。
這些數字對使用者體驗的意義: 在 iPhone 16 Pro 上每秒 22 個 token,200 個 token 的回應在不到 10 秒內呈現完成。首 token 延遲(串流開始前的時間)通常是 200-800ms,取決於提示詞長度。兩者對大多數應用程式內助手模式都是可接受的。
散熱管理和電池
持續的 LLM 推論是行動處理器處理的最耗熱工作負載之一。iPhone 16 Pro 在持續負載下會損失約 44% 的吞吐量,因為 SoC 會降頻以保護硬體溫度。基準測試顯示 22 tok/s 的裝置,在連續推論 5-10 分鐘後可能只能達到 12-14 tok/s。
實際的緩解策略:
限制推論時間。 對大多數使用場景,回應在 30 秒內完成。設定適合你使用場景的最大 token 數(n_predict)。這限制了每次請求的熱量影響。
增加請求間延遲。 對於背景處理任務,在完成之間插入短暫的暫停。即使短暫的暫停也能讓 SoC 在下一次推論之前散熱。
對持續任務選擇較小的模型。 1B 模型以更高的吞吐量生成 token,產生的熱量遠少於 3B 模型。對於分類、擷取或格式化任務,較小的模型通常能產生同等的結果。
在 Android 上監控裝置溫度。 ThermalManager API(Android 10+)以 0-6 的級距暴露熱狀態。註冊監聽器,並在裝置升溫時降低推論頻率或上下文長度。iOS 上沒有直接等效的 API,但你可以測量吞吐量下降作為替代指標。
電池指引: 1B 模型在 iPhone 16 Pro 上以全速持續運行,SoC 功耗比基準線大約多 2-3 瓦。活躍的推論工作階段相比典型應用程式使用,大約減少 20-30% 的電池續航力。對大多數應用程式內助手模式(使用者發起的短暫請求,中間有暫停),影響要小得多。
模型交付策略
如何將 GGUF 檔案送到裝置上,和如何運行它一樣重要。
打包在應用程式二進位檔中
優點:零延遲的首次使用體驗,不需要網路,不需要下載 UX。 缺點:App Store 大小限制。Apple 允許不超過 200 MB 的無線下載無需使用者確認;超過 4 GB 的應用程式需要透過 Wi-Fi 下載。Google Play 有類似的限制。
適用於: 1B Q4_K_M 模型(808 MB)作為可下載資源,透過 iOS On-Demand Resources 或 Android App Bundle 資產套件。模型不在主要二進位檔中,但在安裝時自動下載。
首次啟動時下載
對較大模型最常見的模式。應用程式不帶模型出貨,在首次啟動時(或在「設定 AI 功能」選擇畫面上)從你的 CDN 下載 GGUF。
實作注意事項:
- 在 iOS 上使用
URLSession背景下載任務,這樣即使使用者將應用程式放到背景,下載仍會繼續。 - 在 Android 上使用
WorkManager搭配NETWORK_NOT_ROAMING或NETWORK_UNMETERED約束。 - 顯示進度並提供清楚的說明(「正在下載 AI 模型,2 GB,建議使用 Wi-Fi」)。
- 積極快取下載內容。如果檔案已存在且通過校驗碼驗證,不要重新下載。
- 考慮將 GGUF 儲存在應用程式的
Application Support目錄(iOS)或filesDir(Android),而不是共用位置,以避免在儲存空間不足時被作業系統清理。
差異更新
當你發布新的微調版本時,你可能不想每次更新都推送 2 GB 給每個使用者。差異更新模式:
- 僅 LoRA 適配器: 如果你的更新是對相同基礎模型的新微調,只發送 LoRA 適配器檔案(通常 20-200 MB),並在推論時將其載入到凍結的基礎模型上。llama.cpp 透過
--lora旗標或等效 API 呼叫支援 LoRA 適配器。這比替換整個 GGUF 更節省頻寬。 - GGUF 差異: 對於需要新基礎的架構變更,有工具可以計算 GGUF 版本之間的二進位差異。如果只有部分權重改變,補丁比完整重新下載小得多。
- 版本標記的 CDN 路徑: 將模型儲存在
/models/v2/model.Q4_K_M.gguf等路徑,並在應用程式啟動時檢查版本端點。只在遠端版本較新時才更新本地副本。
llama.cpp 的平台替代方案
在投入 llama.cpp 整合路徑之前,評估平台管理的 API 是否能滿足你的需求。
Apple Foundation Models API(iOS 18.4+ / WWDC 2025)
Apple 發布了一個用於裝置端推論的公開 Swift API,目標是其大約 3B 參數的裝置端模型。該 API 是高階的:你用 GenerationOptions 結構描述任務,然後接收文字、結構化 JSON 或工具呼叫。
優點: 不需要下載或維護模型,由 Apple 進行硬體最佳化,極其簡單的 Swift API,沒有記憶體管理煩惱。
缺點: 你無法載入自定義微調模型。你被 限制在 Apple 的基礎模型及其能力範圍內。不適用於 Android。專業領域的模型品質可能不足。
在以下情況使用 Foundation Models API:你的任務夠通用,Apple 的基礎模型能很好地處理,而且你想在不管理模型檔案的情況下快速出貨。
在以下情況使用 llama.cpp 搭配自定義 GGUF:你需要領域特定品質、跨平台行為,或對模型精確輸出的控制。
Google Gemini Nano(Android,ML Kit GenAI APIs,Google I/O 2025)
Google 的 ML Kit 現在透過託管 API 暴露裝置端 Gemini Nano 推論。與 Apple 的方案類似,這運行的是由作業系統管理的固定模型,而非自定義模型。
優點: 簡單的 API,在支援的 Pixel 和合作夥伴裝置上不需要下載,與現有 ML Kit 模式整合。
缺點: 僅在 Pixel 9 和部分其他裝置上可用。不支援自定義模型。跨裝置一致性有限。
對於目標廣泛裝置支援且使用自定義微調模型的生產應用程式,llama.cpp 搭配 GGUF 仍然是最具可攜性的方案。
端到端檢查清單
出貨前,請確認:
- GGUF 以適合你目標裝置層級的量化匯出(Q4_K_M 適用於大多數行動目標)
- 檔案大小符合你的交付策略(低於 200 MB 適合即時 OTA,低於 4 GB 適合延遲下載)
- 上下文長度(
n_ctx)設定為所需最低值,而非模型的最大值 - 模型在應用程式啟動時或在專用背景佇列載入一次,而非每次請求
- 記憶體警告已處理:在卸載完整模型之前先清除 KV 快取
- 熱節流已測試:運行推論 10 分鐘以上,並驗證持續負載下的輸出品質
- 首次啟動模型交付的背景下載已實作,並有進度回饋
- 在嘗試載入之前已對下載的 GGUF 進行校驗碼驗證
- Token 限制(
n_predict)設定為合理上限,以限制最壞情況的推論時間
使用 Ertas 開始
上述整合工作假設你已經有了一個微調後的 GGUF。微調步驟就是 Ertas 的用武之地。
上傳你的領域資料,視覺化地設定訓練參數,然後以你目標量化等級匯出結果為 GGUF。Ertas 處理雲端 GPU 運算、資料集格式化和量化匯出。你會拿回一個 .gguf 檔案,準備好放入上述描述的 iOS 或 Android 整合中。
行動端推論層是開源基礎設施。差異化因素是裡面的模型:一個理解你的領域、你使用者的語言,以及你產品特定輸出需求的模型。這就是微調產生的,也是你擁有的部分。
Ship AI that runs on your users' devices.
Free plan with 30 credits/mo, no card required. Paid plans from $10/mo USD.
Keep reading
應用程式開發者的微調指南:非機器學習工程師版
為行動應用程式開發者準備的 AI 模型微調實用指南。學習 LoRA、QLoRA 和 GGUF 匯出,無需機器學習背景。
GGUF 是什麼?本地 AI 模型的文件格式
GGUF 格式是什麼、為什麼 Ollama 和 LM Studio 等本地 AI 工具都使用它,以及挑選模型時如何讀懂 Q4_K_M 這類量化命名。
從雲端 API 遷移到裝置端 AI:完整指南
將行動應用程式從雲端 AI API 遷移到裝置端推理的分步遷移計畫。資料提取、微調、整合、測試、發布和監控。