所要時間: 約25分 | 難易度: ★★☆☆☆

この記事で作るもの

Apple公式の機械学習フレームワーク「MLX」を利用して、最新のLLM(Llama 3.1やQwen2.5など)と対話できるストリーミング形式のチャットスクリプトを作成します。 Python環境の構築から、モデルの自動ダウンロード、メモリ効率を最大化する推論実行までを、外部APIを一切使わない完全ローカル環境で実現します。

  • Pythonの基礎的な文法(pipでのインストールや関数の呼び出し)を理解していること
  • Apple Silicon(M1 / M2 / M3 / M4チップ)を搭載したMacを使用していること

先に確認するスペック・料金

MLXを動かす上で最も重要なのは「メインメモリ(ユニファイドメモリ)」の容量です。 Apple Siliconのアーキテクチャでは、GPUがメインメモリを直接参照するため、一般的なWindows PCのように「VRAM(ビデオメモリ)」を別途気にする必要はありませんが、その分メインメモリを激しく消費します。

最低ラインはメモリ16GBです。 8GBモデルでも動作自体は可能ですが、4-bit量子化された7B(70億パラメータ)クラスのモデルを動かすと、OSの挙動が極端に重くなり、スワップが発生してSSDの寿命を縮めるリスクがあります。 仕事でストレスなくローカルLLMを活用したいのであれば、32GB以上のメモリを積んだモデルを強く推奨します。

コスト面では、MLX自体はオープンソースであり、モデルもHugging Faceから無料でダウンロードできるため、電気代以外のランニングコストは0円です。 API料金を気にせず、数千トークンの長文を何度でも投げ込めるのは、ローカル環境ならではの圧倒的なメリットといえます。

なぜこの方法を選ぶのか

Apple SiliconでLLMを動かす手段として、他にも「llama.cpp」や「Ollama」があります。 これらは非常に優秀ですが、あえて「MLX」を選ぶ理由は、Pythonとの親和性の高さと、Apple公式ゆえの最適化スピードにあります。

llama.cppはC++ベースで構築されており、Pythonから扱うにはバインディングが必要です。 対してMLXは、設計思想がNumPyやPyTorchに近く、Pythonエンジニアにとって学習コストが非常に低いのが特徴です。 また、新しいチップが発表された際の最適化も迅速で、Unified Memoryを活かした「Lazy Evaluation(遅延評価)」などの仕組みにより、推論速度だけでなくメモリ消費の効率も極めて高い水準にあります。

「とりあえず動かしたい」ならOllamaが楽ですが、「自分のシステムに組み込みたい」「カスタマイズしたい」のであれば、MLX一択だと私は考えています。

Step 1: 環境を整える

MLXは進化が非常に早いため、システム全体のPython環境を汚さないよう、必ず仮想環境を作成してください。 今回は、Apple Siliconへの最適化が進んでいる「Miniforge」または「uv」の使用を推奨しますが、標準の venv でも問題ありません。

# プロジェクト用のディレクトリを作成
mkdir mlx-test && cd mlx-test

# 仮想環境の作成(Python 3.10以上を推奨)
python3 -m venv .venv

# 仮想環境の有効化
source .venv/bin/activate

# 必要なライブラリのインストール
pip install -U mlx-lm huggingface_hub

mlx-lm は、MLX上でLLMを簡単に扱うためのハイレベルライブラリです。 これをインストールするだけで、モデルのダウンロード、量子化、推論までの面倒な処理をすべてパッケージ化された状態で利用できます。 huggingface_hub は、特定のモデルをダウンロードする際にコマンドラインからログインするために使用します。

⚠️ 落とし穴: Intel Mac(Core i7 / i9搭載モデル)ではMLXは動作しません。 インストール時にエラーが出る場合は、ターミナルが「Rosetta 2」経由で起動していないか確認してください。 arch コマンドを叩いて arm64 と表示されれば正常です。

Step 2: 基本の設定

まずは、動かしたいモデルを選定します。 MLXでは、Hugging Face上にある「MLX用に変換済みのモデル」を利用するのが最もスムーズです。 今回は日本語能力が高い Qwen/Qwen2.5-7B-Instruct-MLX-4bit を例に進めます。

# settings.py などの名前で保存する必要はありませんが、構成を理解するために見てください
from mlx_lm import load, generate

# モデルのパスを指定
# 初回実行時にHugging Faceから自動的にダウンロードされます
model_path = "mlx-community/Qwen2.5-7B-Instruct-4bit"

# モデルとトークナイザーの読み込み
# load関数は、Apple SiliconのGPUに最適化された形でメモリに展開します
model, tokenizer = load(model_path)

ここで 4bit 版を選んでいるのは、メモリ消費を抑えつつ推論速度を稼ぐためです。 無印(量子化なし)のモデルは精度こそ高いものの、メモリを大量に消費し、推論も遅くなります。 実務で使う分には、4-bit量子化による精度低下は体感できるレベルではなく、むしろレスポンスの速さによるメリットの方が遥かに大きいです。

Step 3: 動かしてみる

まずは最小限のコードで、モデルからレスポンスが返ってくるかを確認しましょう。

from mlx_lm import load, generate

model_path = "mlx-community/Qwen2.5-7B-Instruct-4bit"
model, tokenizer = load(model_path)

# プロンプトの準備
# チャット形式のモデルなので、適切なテンプレートを適用します
prompt = "Apple Siliconのすごさを3行で教えてください。"
messages = [{"role": "user", "content": prompt}]
formatted_prompt = tokenizer.apply_chat_template(
    messages, tokenize=False, add_generation_prompt=True
)

# 推論の実行
response = generate(model, tokenizer, prompt=formatted_prompt, verbose=True)

print(response)

期待される出力

1. 高いワットパフォーマンスにより、低電力で圧倒的な処理能力を実現している点。
2. ユニファイドメモリにより、CPUとGPUが高速にデータを共有できる点。
3. AI処理に特化したNeural Engineにより、ローカルでの推論が極めて速い点。

verbose=True を設定すると、トークンの生成速度(tokens per second)がコンソールに表示されます。 M2 MaxやM3 Maxクラスであれば、秒間50〜100トークン以上の爆速でテキストが生成されるはずです。 この「ヌルヌル動く」感覚こそがMLXの醍醐味です。

Step 4: 実用レベルにする

実際の業務で使う場合、生成が終わるまで待たされるのは苦痛です。 ChatGPTのように、生成された文字から順次表示していく「ストリーミング出力」を実装しましょう。 また、繰り返し利用することを想定し、簡単な対話ループを組み込みます。

import sys
from mlx_lm import load, generate
from mlx_lm.utils import generate_step

def stream_chat():
    model_path = "mlx-community/Qwen2.5-7B-Instruct-4bit"

    # 読み込み時の進捗を表示
    print(f"モデルを読み込んでいます: {model_path}...")
    model, tokenizer = load(model_path)

    print("\n--- チャットを開始します(終了するには 'exit' と入力) ---\n")

    while True:
        user_input = input("あなた: ")
        if user_input.lower() == "exit":
            break

        messages = [{"role": "user", "content": user_input}]
        prompt = tokenizer.apply_chat_template(
            messages, tokenize=False, add_generation_prompt=True
        )

        print("AI: ", end="", flush=True)

        # ストリーミング生成のコア部分
        # generate_stepを使うことで、1トークン生成されるたびに制御を戻せます
        tokens = []
        for response in generate_step(model, tokenizer, prompt):
            token = response.token
            text = response.text

            # 終了トークンが来たら停止
            if token == tokenizer.eos_token_id:
                break

            print(text, end="", flush=True)
            tokens.append(token)

        print("\n")

if __name__ == "__main__":
    try:
        stream_chat()
    except KeyboardInterrupt:
        print("\n終了します。")
        sys.exit(0)

このコードでは generate_step というジェネレータ関数を使用しています。 従来の generate 関数はすべて作り終えてから結果を返しますが、generate_step を使えば、AIが考えているそばから文字が画面に流れていきます。 UX(ユーザー体験)の観点から、ローカルLLMアプリを作る際はこれが必須の実装となります。

よくあるトラブルと解決法

エラー内容原因解決策
ModuleNotFoundError: No module named 'mlx'仮想環境が有効になっていないか、インストールに失敗している。source .venv/bin/activate を実行してから再インストール。
Killed または Out of Memoryメモリ不足でOSによってプロセスが強制終了された。モデルをさらに小さいもの(3Bや1B)に変えるか、4bit版を確実に選ぶ。
Gate ID などのエラーHugging Faceの特定モデル(Llama 3等)にアクセス権がない。Hugging Faceにログインし、利用規約に同意後 huggingface-cli login を実行。

次のステップ

MLXでローカルLLMが動かせるようになったら、次は「自分専用の知識」を教え込むステップに進んでみてください。 具体的には「RAG(検索拡張生成)」の構築です。 MLXには推論だけでなく、テキストをベクトル化するための mlx-embedding 的なアプローチや、既存モデルを自分のデータで追加学習させる「LoRA」という手法も用意されています。

Mac一台あれば、数年前なら数百万円のサーバーが必要だったことが、今や膝の上で完結します。 まずは、日々の定型業務メールの下書きや、ソースコードのレビューをこのローカル環境に投げ込んでみてください。 プライバシーを一切気にせず、社外秘の情報を扱える強みは、実務において計り知れない価値を生むはずです。

よくある質問

Q1: メモリ8GBのMacBook Airでも動きますか?

動きますが、かなり工夫が必要です。7Bモデルは厳しく、1.5Bや3Bクラスのモデル(Qwen2.5-3Bなど)を選ぶべきです。また、ブラウザなど他のアプリをすべて閉じないと、メモリプレッシャーでシステム全体がカクつきます。

Q2: 独自のGGUFファイルを読み込めますか?

MLXは独自のフォーマット(.safetensorsベース)を使用するため、llama.cppで使われるGGUFをそのまま読み込むことはできません。ただし、mlx-lm には変換スクリプトが含まれており、Hugging Faceの元の重みから簡単に変換可能です。

Q3: GPUの使用率が上がらないのですが。

MLXは「Lazy Evaluation」を採用しているため、計算が必要になる瞬間までGPUを動かしません。generate を実行した瞬間にアクティビティモニタの「GPUグラフ」が跳ね上がれば、正しくApple SiliconのGPUを活用できています。

📦 この記事に関連する商品(楽天メインで価格確認)

Mac Studio M2 Max

64GBのユニファイドメモリがあれば、30B以上の大型モデルもローカルで快適に動作します

楽天で価格を見る Amazonでも確認

※アフィリエイトリンクを含みます


あわせて読みたい