← 所有文章

在 Apple Silicon 上運行 MLX:當您需要自己的模型,而非 Apple 的模型時

Apple 的 Foundation Models 框架只給您一個模型:系統內建、封裝完整、免費,且依 Apple 的時程更新。對於大多數裝置端的語言工作,這就是正確的工具,硬要捨近求遠反而是個錯誤。但有些工作需要由您親自挑選的模型:某個特定的開放權重 LLM、一個您鎖定的版本、用自己資料訓練出的微調模型,或是系統模型不具備的某項能力。當您需要在裝置上運行自己的模型時,Foundation Models 底下的那一層就是 MLX1

MLX 是 Apple 為 Apple Silicon 上的機器學習打造的陣列框架,並附有一套 Swift API(MLX Swift),可直接嵌入 App 中2。它不是您去呼叫的系統框架;它是一套由您隨 App 一起發佈的函式庫,連同模型權重一併打包。這項差異就是整筆交易的核心,搞懂它,您才能決定要往下深入一層,還是留在 Apple 為您安排好的位置。

TL;DR

  • MLX 是一套為 Apple Silicon 打造、類似 NumPy 的陣列框架,具備惰性求值、可組合的函式轉換,以及 Metal 後端2
  • 統一記憶體模型正是它能在手機上運作的原因。 陣列存放在 CPU 與 GPU 共用的同一個記憶體池中,因此 MLX 能在兩者之間運行於相同的緩衝區上,毋須付出主機到裝置的複製代價3
  • 透過 LLMModelFactory 在裝置上運行開放權重 LLM,指向像 mlx-community/Llama-3.2-3B-Instruct-4bit 這樣的量化模型,再以 ChatSession 進行生成4
  • LoRA 轉接器進行微調:訓練一個小型轉接器,發佈 adapters.safetensors,再以 load(into:) 在執行階段把基礎模型的 Linear 層替換成 LoRALinear5
  • 擁有自己模型的代價:App 體積(權重很大)、記憶體壓力、缺乏系統整合,以及每一次更新都得自己扛。Foundation Models 完全沒有這些成本,因為這些都由 Apple 買單。

MLX 是什麼,以及為何 Apple Silicon 讓它成為可能

MLX 提供的陣列與運算看起來就像 NumPy,再加上機器學習所需的各種轉換:自動微分、向量化,以及會建構運算圖、只在您讀取結果時才真正執行的惰性求值2。光憑這些,能描述的框架有十幾種。真正讓 MLX 得以在您口袋裡的裝置上運行數十億參數模型的,是它的記憶體模型。

在桌上型電腦的 GPU 上,資料存放在系統 RAM,您得透過匯流排把它複製到 GPU 獨立的記憶體中才能運算,算完再把結果複製回來。那次複製就是代價,對大型模型而言更是慘烈。Apple Silicon 採用統一記憶體:CPU、GPU 與神經網路引擎全都直接定址同一個記憶體池。MLX 正是圍繞著這項事實建構的3。一個陣列不是「在 CPU 上」或「在 GPU 上」;它就在記憶體裡,任何處理器都能就地對它運算。沒有複製,沒有匯流排代價。一個量化成 4 位元的 30 億參數模型只佔幾 GB,運行時也省去了那些在記憶體相當的獨立 GPU 機器上會讓同樣工作變得不切實際的往返搬運。Apple 多年前所做的硬體決策,正是裝置端運行一個真正模型之所以可行的根本原因,而這套以圖塊為基礎、採用統一記憶體的架構,就是 MLX 賴以立足的底層基礎。

在裝置上運行 LLM

從「我想要某個特定模型」到文字顯示在螢幕上,這條路很短。MLX Swift 的 LLM 層會從 Hugging Face Hub 載入量化模型並運行它4

let container = try await LLMModelFactory.shared.loadContainer(
    from: HubClient.default,
    using: TokenizersLoader(),
    configuration: .init(id: "mlx-community/Llama-3.2-3B-Instruct-4bit")
)

let session = ChatSession(container)
let response = try await session.respond(to: "Summarize this in one line: \(text)")

若要逐 token 呈現 UI,改為生成串流,並在區塊抵達時即時渲染4

let input = try await container.prepare(input: UserInput(prompt: prompt))
let stream = try await container.generate(input: input, parameters: GenerateParameters())
for await event in stream {
    if case let .chunk(text) = event { /* append to UI */ }
}

有兩個細節承載了大部分的實務份量。首先,模型 ID 裡的 4bit 並不是可有可無的裝飾:量化才是讓模型塞進記憶體、並在裝置上以堪用速度運行的關鍵。您發佈的是 4 位元(或更低)的權重,而非完整精度。其次,即使量化過,權重依然龐大,因此您得審慎決定:要把它們打包進 App(即開即用,但下載肥大),還是在首次啟動時下載(二進位精簡,但要等待,還得處理失敗路徑)。Foundation Models 從不會丟出這道難題,因為模型早已在裝置上。換成 MLX,權重就是您自己的問題。

微調:是 LoRA 轉接器,而非全新模型

之所以要帶上自己的模型,原因鮮少在於基礎模型本身;而在於教會它您的領域。在裝置上對數十億參數的模型做完整微調並非上策。LoRA(低秩適應)才是:您訓練一小組轉接器權重來調整基礎模型的行為,而基礎模型本身原封不動。這個轉接器是 MB 等級,而非 GB 等級5

MLX Swift 會從一個放有 adapter_config.jsonadapters.safetensors 的目錄載入訓練好的轉接器,再把它套用到已載入容器中的模型上5

let adapter = try LoRAContainer.from(directory: adapterURL)
await container.update { context in
    try? adapter.load(into: context.model)   // swaps Linear layers for LoRALinear
}

load(into:) 會把模型的標準 Linear 層替換成 LoRALinear 層,將轉接器的低秩差值折疊進去,於是推論結果現在就反映了您的微調。由於模型存在於容器之中,您要透過 container.update 來套用轉接器;而且您可以在執行階段熱抽換轉接器(unload(from:) 卸下一個、load(into:) 載入另一個),讓單一基礎模型針對不同功能展現不同行為。這套模式與 Apple 透過 Foundation Models 自訂轉接器為系統模型所提供的做法如出一轍:差別在於這裡基礎模型、訓練流程與成果全歸您所有,而不是去適應一個您看不見的模型。

抉擇:Foundation Models、MLX,還是雲端

三個層次,選錯了不是賠上能力,就是賠上一堆本可避免的工夫。

  • Foundation Models——當系統模型能勝任該任務時。免費、私密、零權重要發佈、零記憶體要管理,還白白賺到系統整合。預設就選這裡。Apple 為它打造的那些裝置端語言任務(摘要、分類、抽取、改寫、結構化輸出),就該歸在這裡,沒有例外。
  • MLX——當您需要一個系統不會提供的模型時:某個特定的開放權重 LLM、一個不會隨 OS 更新而漂移的鎖定版本、一個領域微調模型,或是一種落在 Foundation Models 範疇之外的架構(視覺語言模型、非文字模型)。您付出的是 App 體積、記憶體與所有權,換來的是控制權。
  • 雲端——當模型確實非得龐大不可時:前沿推理、長脈絡分析,以及任何最大型模型才做得到、而幾十億參數的裝置端模型力有未逮的事。裝置端不是前沿模型的替代品;它是曲線上的另一個點。

老實說:MLX 是為了某個明確理由而審慎往下走的一步,而非更好的預設選項。如果您說不出 Foundation Models 對您的功能究竟缺了哪項能力,那您就不需要 MLX,硬塞它只意味著背上幾 GB 的權重,以及一份您本來不必扛的記憶體預算。

何時不該動用 MLX

  • 系統模型已經能做。 重新讀一遍 Foundation Models 的任務清單。如果您的需求名列其中,到此為止。
  • 您負擔不起權重。 一個量化過的小模型依然是個龐大的資產。如果 App 體積或首次啟動下載對您的使用者是個實實在在的限制,這個限制本身或許就替您拍板定案了。
  • 您需要神經網路引擎針對固定模型的最低功耗路徑。 對於一個已知、已發佈、不會變動的模型,Core ML 及其轉換器能以最緊湊的功耗與延遲鎖定神經網路引擎。MLX 的長處在於彈性與研究等級的迭代;Core ML 的長處在於鎖定的生產模型。它們是不同的工具,「裝置端 ML」並不是單一個決策。
  • 您不會去維護它。 擁有自己的模型,意味著您得負責它的更新、安全與漂移。Apple 會替您更新系統模型。如果您沒有人力去擁有一個模型,那就別採用它。

MLX 所獎賞的本領,是對「何時該用它」的克制。這套框架確實非凡:一個真正的語言模型,針對您的領域微調過,完全在裝置上運行,無需伺服器、沒有按 token 計費的成本,跑在一套記憶體架構正是為此而生的硬體上。當您已說出理由時,這份能力值得您伸手去取。沒有理由就伸手,您就是拿 Apple 那個免費、有人維護、整合完善的模型,換來一份更沉重、無人維護、如今由您獨自扛起的副本。判斷力,才是這整份工作的全部。

常見問題

Apple 的 MLX 框架是什麼?

MLX 是一套用於 Apple Silicon 機器學習的陣列框架,具備類似 NumPy 的 API、可組合的函式轉換(自動微分、向量化)、惰性運算,以及 Metal 後端2。MLX Swift 是用來把它嵌入 App 的 Swift API,讓您能在裝置上運行並微調自己的模型。

MLX 如何運用 Apple Silicon 的統一記憶體?

MLX 的陣列存放在共用記憶體中,因此運算能在 CPU 或 GPU 上執行,毋須在各自獨立的記憶體池之間複製資料3。正是這項零搬運的特性,讓 Apple Silicon 的統一記憶體架構在裝置端模型執行上如此高效。

我能用 MLX 在裝置上運行開放權重的 LLM 嗎?

可以。LLMModelFactory.shared.loadContainer(from:using:configuration:) 會從 Hugging Face Hub 載入像 mlx-community/Llama-3.2-3B-Instruct-4bit 這樣的量化模型;ChatSession 提供 respond(to:) 供單次呼叫,而 container.generate(input:parameters:) 則串流 .chunk(text) 事件以供增量輸出4

我要如何用 MLX 微調模型?

用 LoRA 轉接器,而非全新模型。LoRAContainer.from(directory:) 會從一個放有 adapter_config.jsonadapters.safetensors 的目錄載入轉接器;透過 container.update 套用後,它會把模型的 Linear 層替換成 LoRALinear 層,並能在執行階段熱抽換轉接器5

MLX、Foundation Models 與 Core ML:我該用哪一個?

當 Apple 的系統模型能勝任該任務時,預設選 Foundation Models(免費、私密、零權重要發佈)1。只有在您需要一個系統不會提供的模型時,才動用 MLX:某個特定的開放權重 LLM、一個鎖定的版本、一個領域微調模型,或一種落在 Foundation Models 範疇之外的架構。當固定的生產模型需要神經網路引擎的最低功耗路徑時,用 Core ML;而當模型確實非得是前沿規模不可時,用雲端

我何時不該動用 MLX?

當系統模型已經能做時、當您負擔不起發佈幾 GB 權重時、當固定模型用 Core ML 的最低功耗神經網路引擎路徑會更合適時,或當您沒有人力去擁有一個模型的更新、安全與漂移時。MLX 是為了一個明確理由而審慎往下走的一步,而非更好的預設選項。



  1. 將 MLX 相對於 Foundation Models 框架定位:Foundation Models 揭露的是 Apple 固定的裝置端系統模型(參見 Apple Foundation Models:裝置端 LLM 框架);MLX 則運行您自行挑選並微調的模型。兩者在裝置端技術堆疊的不同層次上,回應不同的需求。 

  2. Apple Machine Learning Research,MLXMLX Swift。MLX 是一套用於 Apple Silicon 機器學習的陣列框架,具備類似 NumPy 的 API、可組合的函式轉換(自動微分、向量化)、惰性運算,以及 Metal 後端。MLX Swift 是用來把它嵌入 App 的 Swift API。 

  3. MLX 文件,unified memory。MLX 的陣列存放在共用記憶體中;運算能在 CPU 或 GPU 上執行,毋須在各自獨立的記憶體池之間搬運資料,正是這項特性讓 Apple Silicon 的統一記憶體架構在裝置端模型執行上如此高效。硬體背景:Apple Silicon 的 TBDR 與統一記憶體。 

  4. Apple Machine Learning Research,MLX Swift Examples / MLX Swift LMLLMModelFactory.shared.loadContainer(from:using:configuration:) 會從 Hugging Face Hub 載入量化模型(例如 mlx-community/Llama-3.2-3B-Instruct-4bit);ChatSession 提供 respond(to:) 供單次呼叫,而 container.generate(input:parameters:) 則透過 GenerateParametersUserInput 產出一串 .chunk(text) 事件以供增量輸出。 

  5. Apple Machine Learning Research,MLX Swift LM LoRA adapters referenceLoRAContainer.from(directory:) 會從一個包含 adapter_config.jsonadapters.safetensors 的目錄載入轉接器;透過 container.update 套用後,adapter.load(into: context.model) 會把模型的 Linear 層替換成 LoRALinear 層,而 unload(from:) 則可卸下其中一個,使轉接器得以在執行階段熱抽換。可對照 Apple 在 Foundation Models 自訂轉接器中的系統模型做法。 

  6. 作者親身的 MLX 實作:一個自主的 ML 研究迴圈,透過 MLX 在 Apple Silicon 上運行固定預算的訓練實驗,自主修改架構與超參數以最小化驗證 bits-per-byte,並只保留有所改善的結果。本文所述的統一記憶體與量化行為,正反映了那段實驗經驗。 

相關文章

Apple Foundation Models:裝置端LLM框架完全解析

Apple的Foundation Models框架:LanguageModelSession、@Generable引導式生成、工具呼叫、可用性判斷,以及何時該離開裝置端模型。

3 分鐘閱讀

Apple Vision Framework:多數開發者忽略的裝置端電腦視覺

Apple Vision 提供超過二十多項裝置端電腦視覺操作。多數開發者預設使用 OpenAI Vision,但這些任務 Vision 能在毫秒內免費於裝置上完成。

3 分鐘閱讀

建構 AI 系統:從 RAG 到 Agent

我建構了一個 3,500 行的 Agent 系統,包含 86 個 Hook 和共識驗證機制。以下是我在 RAG、微調和 Agent 編排方面的經驗分享。

3 分鐘閱讀