Back to blog
    GGUF + llama.cpp:在你的行動應用程式中部署微調模型
    ggufllama-cppmobile-developmentiosandroidon-device-aideploymentsegment:mobile-builder

    GGUF + llama.cpp:在你的行動應用程式中部署微調模型

    將微調 AI 模型打包為 GGUF 檔案並在 iOS 和 Android 上使用 llama.cpp 運行的實用指南。包含檔案大小、效能基準和整合模式。

    EErtas Team·

    你的微調模型在筆電上運作得很好。回應速度快,品質正是你需要的,評估結果也很穩固。現在你需要讓它在 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_08 位元幾乎無損失接近無損,Q4_K_M 的 2 倍大小
    F1616 位元基準線對大多數行動目標太大

    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 Release

    GGML_METAL=ON 旗標啟用 Metal GPU 加速。產生的 libllama.alibggml.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 Pro1.5BINT8136NPU (Apple ANE)
    Galaxy S25 Ultra2BINT891NPU (Google Tensor)
    iPhone 16 Pro1.5BQ422(持續)Metal GPU
    Snapdragon 8 Elite13BQ420+Hexagon NPU
    iPhone 15 Pro1BQ4約 18Metal GPU
    中階 Android (SD 7s Gen 3)1BQ48-12CPU 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_ROAMINGNETWORK_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.

    Early bird pricing starts at $14.50/mo — locked in for life. Plans for builders and agencies.

    Keep reading