Apple SiliconでのMLX:Appleのモデルではなく、自分のモデルが必要なとき
AppleのFoundation Modelsフレームワークが手渡すのはひとつのモデルだけです。システムが持つモデルで、封印されており、無料で、Appleのスケジュールに従って更新されます。端末上で行う言語処理のほとんどでは、これが正しい道具であり、その先に手を伸ばすのは誤りです。とはいえ、自分で選んだモデルが必要になる仕事もあります。特定のオープンウェイトなLLM、バージョンを固定したモデル、自前のデータで学習させたファインチューン、あるいはシステムモデルには備わっていない能力——そうしたものが必要なときです。自分のモデルを端末上で動かす必要があるなら、Foundation Modelsのひとつ下の層にあるのがMLXです1。
MLXは、Apple Silicon上で機械学習を行うためのAppleの配列フレームワークであり、アプリに直接組み込めるSwift向けのAPI(MLX Swift)が用意されています2。呼び出すだけのシステムフレームワークではなく、モデルの重みとともに同梱して出荷するライブラリなのです。この違いがそのままトレードオフの全体を決めており、それを理解することが、ひとつ下の層へ降りるべきか、Appleが置いた場所にとどまるべきかを判断する手がかりとなります。
要点
- MLXは、Apple Silicon向けに作られたNumPy風の配列フレームワークで、遅延評価、合成可能な関数変換、そしてMetalバックエンドを備えています2。
- 統一メモリモデルこそが、スマートフォン上で動作する理由です。 配列はCPUとGPUが共有するひとつのメモリプールに置かれるため、MLXは同じバッファを介して両者をまたいで動作し、ホストとデバイス間のコピーという負担がありません3。
- オープンウェイトなLLMを端末上で動かすには、
LLMModelFactoryを使い、mlx-community/Llama-3.2-3B-Instruct-4bitのような量子化モデルを指定したうえで、ChatSessionを通じて生成します4。 - ファインチューンにはLoRAアダプターを使います。小さなアダプターを学習させ、
adapters.safetensorsを同梱し、load(into:)が実行時にベースモデルのLinear層をLoRALinearへ差し替えます5。 - 自分のモデルを持つことの代償は、アプリのサイズ(重みは大きい)、メモリへの圧迫、システム統合の欠如、そしてすべての更新を自分で背負うことです。Foundation Modelsにこれらの代償がないのは、Appleが肩代わりしてくれているからです。
MLXとは何か、そしてなぜApple Siliconがそれを可能にするのか
MLXは、NumPyのように見える配列と演算に加えて、機械学習に必要な変換——自動微分、ベクトル化、そして計算グラフを構築して結果を読み出したときに初めて実行する遅延評価——を提供します2。それだけなら、これに当てはまるフレームワークは十数個あります。MLXがポケットに収まる端末上で数十億パラメータのモデルを動かせるのは、メモリモデルがあるからです。
デスクトップのGPUでは、データはシステムRAMに置かれ、計算するにはバスを介してGPUの別個のメモリへコピーし、結果をまた戻します。このコピーが負担であり、大きなモデルではそれが容赦なく効いてきます。Apple Siliconには統一メモリがあります。CPU、GPU、そしてNeural Engineがすべて直接アドレス指定できるひとつのプールです。MLXはこの事実を中心に据えて作られています3。配列は「CPU上」でも「GPU上」でもなく、メモリの中にあり、どのプロセッサもその場で演算します。コピーもバスの負担もありません。4ビットに量子化された30億パラメータのモデルは数ギガバイトに収まり、同程度のメモリを積んだ専用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)")
トークン単位で更新する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ビット(あるいはそれ以下)の重みであって、完全精度ではありません。第二に、重みは量子化しても大きいため、アプリに同梱するか(即座に使えるが、ダウンロードが重くなる)、初回起動時に取得するか(バイナリは軽いが、待ち時間と扱うべき失敗経路が生じる)を、意図して決めることになります。Foundation Modelsがこの問いを突きつけてこないのは、モデルがすでに端末上にあるからです。MLXでは、重みはあなたの問題なのです。
ファインチューン:新しいモデルではなく、LoRAアダプター
自分のモデルを持ち込む理由がベースモデルそのものにあることはまれで、たいていは自分のドメインをそこに教え込むためです。数十億パラメータのモデルを端末上で完全にファインチューンするのは取るべき手ではありません。取るべきはLoRA(低ランク適応)です。ベースモデルの挙動を調整する小さなアダプターの重みだけを学習させ、ベースには手をつけません。アダプターはギガバイトではなく、メガバイト単位です5。
MLX Swiftは、adapter_config.jsonとadapters.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、それともクラウドか
層は3つあり、選択を誤れば、能力を失うか、避けられたはずの大量の作業を背負うかのどちらかになります。
- システムモデルでその仕事をこなせるなら、Foundation Modelsです。無料で、プライベートで、出荷する重みはゼロ、管理するメモリもゼロ、そしてシステム統合がただで手に入ります。ここを既定とします。Appleがそのために作った端末上の言語処理(要約、分類、抽出、書き換え、構造化出力)は、議論の余地なくここに属します。
- システムが与えてくれないモデルが必要なときは、MLXです。特定のオープンウェイトなLLM、OSの更新で動いてしまわない固定バージョン、ドメイン向けのファインチューン、あるいはFoundation Modelsの守備範囲の外にあるアーキテクチャ(視覚言語モデルや、テキスト以外のモデル)。アプリのサイズ、メモリ、そして所有という代償を払い、その引き換えに制御を手に入れます。
- モデルが本当に大きくなければならないときは、クラウドです。最先端の推論、長文脈の分析、最大級のモデルにしかできず、数十億パラメータの端末上モデルでは届かないものです。端末上のモデルは最先端モデルの代替ではなく、曲線上の別の一点なのです。
率直に読み解けば、MLXは特定の理由のために意図して一段降りる選択であって、より良い既定値ではありません。あなたの機能にとってFoundation Modelsに欠けている能力を名指しできないなら、MLXは必要なく、それを出荷するとは、ギガバイト単位の重みと、本来抱えずに済んだメモリの予算を背負い込むことを意味します。
MLXに手を伸ばすべきでないとき
- システムモデルがすでにこなせる。 Foundation Modelsのタスク一覧を読み返しましょう。あなたのものがその中にあるなら、ここで止まることです。
- 重みの代償を払えない。 量子化された小さなモデルでも、依然として大きなアセットです。アプリのサイズや初回起動時のダウンロードがユーザーにとって現実的な制約であるなら、その制約だけで答えが決まることもあります。
- 固定モデルにNeural Engineの最も低消費電力な経路が必要。 変化しない、既知の出荷済みモデルには、Core MLとそのコンバーターが、最も厳しく電力と遅延を抑えてNeural Engineを狙います。MLXは柔軟性と研究水準の反復で輝き、Core MLは作り込まれた本番モデルで輝きます。両者は別々の道具であり、「端末上の機械学習」はひとつの判断ではないのです。
- 保守するつもりがない。 自分のモデルを持つとは、その更新も、セキュリティも、ドリフトも自分のものになるということです。システムモデルはAppleが代わりに更新してくれます。モデルを所有する人員を抱えていないなら、採用してはいけません。
MLXが報いてくれる技能は、いつ使うかについての自制です。このフレームワークは本当に見事なものです。自分のドメインにファインチューンした実物の言語モデルが、サーバーもトークンあたりの課金もなく、まさにこのために作られたメモリアーキテクチャを持つハードウェア上で、完全に端末内で動くのですから。その能力は、理由を名指しできたときには手を伸ばす価値があります。理由なしに手を伸ばせば、Appleの無料で、保守され、統合されたモデルを、いま自分のものになった、より重くて保守されない複製と引き換えにしただけです。その見極めこそが、仕事のすべてなのです。
FAQ
AppleのMLXフレームワークとは何ですか?
MLXは、Apple Silicon上で機械学習を行うための配列フレームワークで、NumPy風のAPI、合成可能な関数変換(自動微分、ベクトル化)、遅延計算、そしてMetalバックエンドを備えています2。MLX Swiftはそれをアプリに組み込むためのSwift向けAPIであり、自分のモデルを端末上で実行・ファインチューンできます。
MLXはApple Siliconの統一メモリをどう使うのですか?
MLXの配列は共有メモリに置かれるため、別個のメモリプール間でデータをコピーすることなく、CPUでもGPUでも演算が動きます3。この転送ゼロという性質こそが、Apple Siliconの統一メモリアーキテクチャを端末上でのモデル実行に効率的なものにしているのです。
MLXでオープンウェイトなLLMを端末上で動かせますか?
はい。LLMModelFactory.shared.loadContainer(from:using:configuration:)が、mlx-community/Llama-3.2-3B-Instruct-4bitのような量子化モデルをHugging Face Hubから読み込みます。ChatSessionは単発の呼び出しにrespond(to:)を提供し、container.generate(input:parameters:)は逐次出力のために.chunk(text)イベントをストリームします4。
MLXでモデルをファインチューンするにはどうすればよいですか?
新しいモデルではなく、LoRAアダプターを使います。LoRAContainer.from(directory:)が、adapter_config.jsonとadapters.safetensorsを収めたディレクトリからアダプターを読み込みます。container.updateを通じて適用すると、モデルのLinear層をLoRALinear層へ差し替え、実行時にアダプターをホットスワップできます5。
MLX対Foundation Models対Core ML:どれを使うべきですか?
Appleのシステムモデルでその仕事をこなせるなら、Foundation Modelsを既定にします(無料、プライベート、出荷する重みはゼロ)1。システムが与えてくれないモデルが必要なときだけ、MLXに手を伸ばします。特定のオープンウェイトなLLM、固定バージョン、ドメイン向けのファインチューン、あるいはFoundation Modelsの守備範囲の外にあるアーキテクチャです。Neural Engineの最も低消費電力な経路を必要とする作り込まれた本番モデルにはCore MLを、モデルが本当に最先端規模でなければならないときにはクラウドを使います。
MLXに手を伸ばすべきでないのはどんなときですか?
システムモデルがすでにこなせるとき、ギガバイト単位の重みを出荷する余裕がないとき、固定モデルならCore MLの最も低消費電力なNeural Engine経路のほうが向いているとき、あるいはモデルの更新・セキュリティ・ドリフトを所有する人員を抱えていないときです。MLXは名指しできる理由のために意図して一段降りる選択であって、より良い既定値ではありません。
-
MLXをFoundation Modelsフレームワークとの関係で位置づけると、Foundation ModelsはAppleの固定された端末上システムモデルを公開します(Apple Foundation Models: The On-Device LLM Frameworkを参照)。MLXは、自分で選んでファインチューンするモデルを動かします。両者は端末上スタックの異なる層で、異なるニーズに応えます。 ↩↩
-
Apple Machine Learning Research、MLXおよびMLX Swift。MLXは、Apple Silicon上で機械学習を行う配列フレームワークで、NumPy風のAPI、合成可能な関数変換(自動微分、ベクトル化)、遅延計算、そしてMetalバックエンドを備えています。MLX Swiftは、それをアプリに組み込むためのSwift向けAPIです。 ↩↩↩↩
-
MLXドキュメント、unified memory。MLXの配列は共有メモリに置かれ、演算は別個のメモリプール間でデータを転送することなくCPUでもGPUでも動きます。これが、Apple Siliconの統一メモリアーキテクチャを端末上でのモデル実行に効率的なものにしている性質です。ハードウェアの背景についてはApple SiliconのTBDRと統一メモリを参照。 ↩↩↩
-
Apple Machine Learning Research、MLX Swift Examples / MLX Swift LM。
LLMModelFactory.shared.loadContainer(from:using:configuration:)は、量子化モデル(たとえばmlx-community/Llama-3.2-3B-Instruct-4bit)をHugging Face Hubから読み込みます。ChatSessionは単発の呼び出しにrespond(to:)を提供し、container.generate(input:parameters:)はGenerateParametersとUserInputを介して逐次出力のための.chunk(text)イベントのストリームを生成します。 ↩↩↩↩ -
Apple Machine Learning Research、MLX Swift LM LoRA adapters reference。
LoRAContainer.from(directory:)は、adapter_config.jsonとadapters.safetensorsを収めたディレクトリからアダプターを読み込みます。container.updateを通じて適用すると、adapter.load(into: context.model)がモデルのLinear層をLoRALinear層へ置き換え、unload(from:)が一方を取り外すことで、アダプターを実行時にホットスワップできます。Appleのシステムモデル経路はFoundation Modelsのカスタムアダプターで比較できます。 ↩↩↩↩ -
著者によるMLXの実地作業:Apple Silicon上でMLXを介して固定予算の学習実験を回す自律的なML研究ループであり、検証時のbits-per-byteを最小化するためにアーキテクチャとハイパーパラメータを自律的に変更し、改善のみを残します。ここで述べた統一メモリと量子化の挙動は、その実験を反映しています。 ↩