WWDC Quick Look 💓 By SwiftGGTeam
Explore large language models on Apple silicon with MLX

Explore large language models on Apple silicon with MLX

元の動画を見る

ハイライト

M3 Ultra の 512GB ユニファイドメモリ環境では、MLX LM のコマンド 1 行で 4.5-bit 量子化・重み約 380GB の DeepSeek 670B モデルを動かせます。

主要内容

これまでローカルで大規模言語モデルを動かそうとすると、独立 GPU を搭載した PC、CUDA ツールチェーン、HuggingFace の変換スクリプト、量子化プラグイン、さらに VRAM が足りないときの offload など、さまざまな手間が必要でした。Mac ユーザーは NVIDIA カードも数十 GB の VRAM も持たないため、長らくこの世界から置き去りにされてきました。

WWDC 2025 で Apple が発表した MLX LM は、この長い手順を 2 ステップに圧縮します。pip install mlx-lm を実行し、コマンドを 1 つ叩くだけです。MLX は Metal でアクセラレートされ、ユニファイドメモリを活用するオープンソースの機械学習ライブラリで、CPU と GPU が同じデータを共有するため、VRAM とメインメモリを行き来するコピーが発生しません。MLX LM はその上に CLI ツールと Python API をラップしており、テキスト生成、モデル量子化、LoRA ファインチューニング、adapter のマージといった一連のフローをカバーし、Hugging Face とも直接連携します。Angelos のデモでは、M3 Ultra 上で 670B パラメーター・4.5-bit 量子化の DeepSeek V3 を実際に動かし、重みは約 380GB ですが、生成速度は読むスピードを上回っていました。これは、コンシューマー向けハードウェアでこの規模のモデルとローカル対話できるようになった初めてのケースです。

詳細

最小構成のテキスト生成(03:51)。 CLI のコマンド 1 行でダウンロード・ロード・推論をすべて済ませられます。

mlx_lm.generate --model "mlx-community/Mistral-7B-Instruct-v0.3-4bit" \
                --prompt "Write a quick sort in Swift"

ポイント:

  • --model には Hugging Face のリポジトリ ID またはローカルパスを指定でき、ローカルにモデルがなければ自動でダウンロードされます。
  • --prompt は入力テキストで、ツール内部で chat template が適用されます。
  • 加えて --top-p--temp--max-tokens でサンプリング挙動を調整でき、主要な推論ツールと同等のパラメーターが揃っています。

Python API で完全なモデルオブジェクトを取得(05:26)。 load が返すのは正真正銘の MLX ニューラルネットワークで、構造の表示やパラメーターの参照が可能です。

from mlx_lm import load, generate

model, tokenizer = load("mlx-community/Mistral-7B-Instruct-v0.3-4bit")

prompt = "Write a quick sort in Swift"
messages = [{"role": "user", "content": prompt}]
prompt = tokenizer.apply_chat_template(
    messages, add_generation_prompt=True
)

text = generate(model, tokenizer, prompt=prompt, verbose=True)

ポイント:

  • load はモデルと tokenizer を同時に返し、HF から自動でダウンロード・キャッシュします。
  • apply_chat_template は messages をモデルが期待する prompt 文字列にレンダリングします。
  • generate はブロッキング呼び出しで、verbose=True を指定すると生成しながら token とスループットを表示します。
  • 取得した modelprint(model.layers[0].self_attn) のように構造を確認でき、layer swap や低レベルでの改変が容易です。

KV Cache でマルチターン対話を維持(08:01)。 長い prompt やマルチターンのシーンでは、attention の再計算コストが極めて大きくなります。

from mlx_lm import load, generate
from mlx_lm.models.cache import make_prompt_cache

model, tokenizer = load("mlx-community/Mistral-7B-Instruct-v0.3-4bit")

prompt = "Write a quick sort in Swift"
messages = [{"role": "user", "content": prompt}]
prompt = tokenizer.apply_chat_template(
    messages, add_generation_prompt=True
)

cache = make_prompt_cache(model)

text = generate(model, tokenizer, prompt=prompt, prompt_cache=cache, verbose=True)

ポイント:

  • make_prompt_cache(model) で再利用可能な KV cache オブジェクトを作成します。
  • prompt_cache=generate に渡すと、呼び出すたびに前回の token 位置から続きを生成できます。
  • cache はインプレースで編集・保存が可能で、複数のセッション間で切り替えることもできるため、チャットボットや agent のシナリオに向いています。

レイヤー単位の混合精度量子化(10:33)。 Embedding 層と lm_head は量子化に敏感なので、これらだけ高い精度を保つ構成が取れます。

from mlx_lm.convert import convert

def mixed_quantization(layer_path, layer, model_config):
    if "lm_head" in layer_path or "embed_tokens" in layer_path:
        return {"bits": 6, "group_size": 64}
    elif hasattr(layer, "to_quantized"):
        return {"bits": 4, "group_size": 64}
    else:
        return False

convert(
    hf_path="mistralai/Mistral-7B-Instruct-v0.3",
    mlx_path="./mistral-7b-v0.3-mixed-4-6-bit",
    quantize=True,
    quant_predicate=mixed_quantization
)

ポイント:

  • quant_predicate は各レイヤーごとに呼ばれる関数で、そのレイヤーの量子化パラメーターまたは False(スキップ)を返します。
  • lm_headembed_tokens は 6-bit、group_size 64、それ以外は 4-bit にしています。
  • convert はダウンロード・変換・保存をワンステップで実行し、upload_repo を加えれば Hugging Face にプッシュバックも可能です。

LoRA ファインチューニング(13:37)。 4-bit 量子化済みモデルの上で adapter を直接学習できます。

mlx_lm.lora --model "mlx-community/Mistral-7B-Instruct-v0.3-4bit" \
            --train \
            --data /path/to/our/data/folder \
            --iters 300 \
            --batch-size 16

ポイント:

  • 量子化モデル上で adapter を学習するため、フル精度モデルを学習する場合に比べてメモリ使用量が大きく下がります。
  • --data はローカルのデータディレクトリを指し、Hugging Face 標準のデータセットフォーマットを使用します。
  • 学習が終わると adapter ファイルが単独で保存され、推論時には --adapter でロードできます。
  • 仕上げに mlx_lm.fuse16:29)で adapter をベースモデルにマージすれば、デプロイ時には量子化済みモデルファイル 1 つだけが残ります。

Swift 側への組み込み(17:14)。 Mac/iOS アプリに LLM 推論を組み込みます。

import Foundation
import MLX
import MLXLMCommon
import MLXLLM

@main
struct LLM {
    static func main() async throws {
        let modelId = "mlx-community/Mistral-7B-Instruct-v0.3-4bit"
        let modelFactory = LLMModelFactory.shared
        let configuration = ModelConfiguration(id: modelId)
        let model = try await modelFactory.loadContainer(configuration: configuration)

        try await model.perform({context in
            let prompt = "Write a quicksort in Swift"
            let input = try await context.processor.prepare(input: UserInput(prompt: prompt))

            let params = GenerateParameters(temperature: 0.0)
            let tokenStream = try generate(input: input, parameters: params, context: context)
            for await part in tokenStream {
                print(part.chunk ?? "", terminator: "")
            }
        })
    }
}

ポイント:

  • LLMModelFactory.shared.loadContainer(configuration:)ModelContainer を返します。これは Swift 6 の actor なので、並行処理の安全性が自然に確保されます。
  • context.processor.prepare(input:) が tokenize と chat template の適用をまとめて行います。
  • generate(...)AsyncSequence を返すため、for await で token をストリーミング消費できます。
  • マルチターン対話は、generate の前に context.model.newCache(parameters:) で KV cache を作成し、それを TokenIterator に渡すだけで実現できます(18:00)。

重要ポイント

  • 何をするか: Mac 上に自前のローカル推論サービスを構築し、プライバシー要件の高いデータ処理をクラウド API から置き換える

    • 価値: M シリーズ Mac のユニファイドメモリのおかげで、70B の 4-bit モデルが 64〜128GB のマシンで動作します。機密データを外部に出す必要がなく、推論コストは実質ゼロになります。
    • 始め方: pip install mlx-lm で導入し、まずは mlx_lm.generate --model "mlx-community/Mistral-7B-Instruct-v0.3-4bit" で動作確認をしてから、Python API をラップして HTTP インターフェースに仕立てます。
  • 何をするか: LoRA ファインチューニングで自社のドメイン知識を量子化済みベースモデルに注入する

    • 価値: 4-bit モデル上で adapter を学習するためメモリ使用量が低く、32GB Mac でも完了できます。adapter は数十 MB しかないので、顧客やユースケースごとに配布しやすいです。
    • 始め方: データを Hugging Face 標準の jsonl に整形し、mlx_lm.lora --train --data ./data --iters 300 を実行、続けて mlx_lm.fuse でマージしてデプロイします。
  • 何をするか: MLX Swift を Mac/iOS アプリに直接組み込み、オンデバイスのチャットやコード補完を実現する

    • 価値: Foundation Models フレームワークの内蔵モデルはサイズに上限がありますが、MLX を併用すれば 7B〜70B の任意のモデルを選択でき、品質の上限を引き上げられます。
    • 始め方: Xcode プロジェクトに MLXLLM の依存を追加し、17:14 の 28 行サンプルを参考に ModelContainer を組み立て、さらに KV cache を加えてマルチターン対話に対応させます。
  • 何をするか: レイヤー単位の混合精度量子化で、品質要求が高いシーンに折衷案を提供する

    • 価値: 純粋な 4-bit では数学やコード生成タスクでスコアが大きく落ちる場面があります。embedding や lm_head を 6-bit に引き上げると、サイズの増分は数 % で済みつつ、品質低下の大部分をリカバリーできます。
    • 始め方: 本文の mixed_quantization 関数をそのまま流用し、convert(quant_predicate=...) を呼んで、自社の評価セットで量子化前後のスコアを比較します。

関連セッション

コメント

GitHub Issues · utterances