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

この記事で作るもの

  • Apple Silicon(M1/M2/M3/M4チップ)に最適化された「MLX」ライブラリを使い、日本語LLMを高速に動かすPythonスクリプト
  • ターミナル上でLLMとリアルタイムに対話できるストリーミング形式のチャットインターフェース
  • Hugging Faceから好みのモデルを自動ダウンロードし、量子化された状態で実行する環境

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

MacBook Pro M3 Max

64GB以上のユニファイドメモリは大規模モデルを高速に動かすための最低条件です

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

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

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

Apple Siliconを搭載したMacが必須です。Intel Macでは動作しません。 最も重要なのは「メモリ(ユニファイドメモリ)」の容量です。 LLMはモデルのパラメータをすべてメモリ上に展開するため、最低でも16GB、できれば32GB以上のメモリを推奨します。

8GBメモリのMacBook Airでも「4ビット量子化」された軽量なモデル(Llama-3-8BやGemma-2-9Bなど)なら動きますが、OSやブラウザのメモリ消費と競合して動作が極端に重くなる場合があります。 本気でローカルLLMを回すなら、Mac StudioやMacBook Proの「Max」チップ搭載モデル、メモリ64GB以上が理想的です。 ソフトウェアはすべてオープンソースなので、API利用料のような実行コストは0円です。

購入検討中の方へのアドバイスですが、ストレージよりもメモリを優先してください。 ストレージは外付けで増やせますが、Apple Siliconのメモリは後から増設できません。 LLM運用においてメモリ不足は「動作が遅い」ではなく「起動すらしない」という致命的な結果を招きます。

なぜこの方法を選ぶのか

MacでローカルLLMを動かす方法は、llama.cppやOllamaなど複数存在します。 その中でMLXを選ぶ最大の理由は「Apple純正の最適化」にあります。 MLXはAppleの機械学習チームが開発しており、Unified Memory(CPUとGPUが同じメモリを共有する仕組み)を最大限に活用する設計になっています。

PyTorchをMacで動かす場合、データの転送オーバーヘッドが発生しがちですが、MLXはフレームワークレベルでこれを排除しています。 また、Pythonから非常に触りやすく、既存の機械学習エコシステムとの親和性が高いのも特徴です。 「とりあえず動けばいい」ならOllamaが楽ですが、「自分でアプリを組みたい」「カスタマイズしたい」エンジニアにとってはMLXが最も自由度が高く、かつ高速な選択肢になります。

Step 1: 環境を整える

MLXの導入にはPython 3.10以降が必要です。 システムのPythonを汚さないよう、仮想環境を作成して進めます。

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

# 仮想環境の作成
python3 -m venv .venv

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

# MLX関連ライブラリのインストール
pip install mlx-lm mlx huggingface_hub

mlx-lm は、MLX上でLLMを簡単に扱うためのヘルパーライブラリです。 これを入れるだけで、Hugging Faceにある多くのモデルを数行で呼び出せるようになります。 内部では、Apple SiliconのGPU(Metal)を叩くための最適化コードが動いています。

⚠️ 落とし穴: macOSのバージョンが古いと、Metalの最新機能が使えずインストールに失敗します。macOS Ventura 13.5以上、できれば最新のSonoma以降にアップデートしてから実行してください。また、Xcode Command Line Toolsが必要になるため、未インストールの場合は xcode-select --install を先に実行してください。

Step 2: 基本の設定

次に、動かすモデルを選びます。 MLXで動かすには「MLX形式」に変換されたモデルを使うのが一番手っ取り早いです。 Hugging Faceの mlx-community というアカウントが、主要なモデルをすべて変換して公開してくれています。

今回は、日本語能力が高く軽量な「Llama-3-8B」の日本語調整版を使います。 以下のコードで、モデルのパスを指定し、推論の準備を整えます。

import os
from mlx_lm import load, generate

# 使用するモデルの指定
# Hugging Face上のリポジトリ名を指定するだけで自動ダウンロードされます
model_path = "mlx-community/Meta-Llama-3-8B-Instruct-4bit"

# モデルとトークナイザーの読み込み
# 4-bit量子化モデルなので、8Bパラメータでもメモリ消費は5GB程度に収まります
model, tokenizer = load(model_path)

load 関数を呼び出した際、初回は数GBのモデルデータがダウンロードされます。 2回目以降はキャッシュから読み込まれるため、起動は数秒で終わります。 4bit という表記があるものを選ぶのがコツです。精度を極端に落とさず、メモリ使用量を劇的に下げられます。

Step 3: 動かしてみる

まずは最小限のコードで、AIに質問を投げてみます。

# プロンプトの準備
prompt = "Apple Siliconのすごさを、エンジニア向けに3行で説明してください。"

# メッセージ形式(Chat Template)への変換
# Llama-3などのモデルには特定の対話フォーマットが必要ですが、apply_chat_templateがそれを自動で処理します
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(f"\nAIの回答:\n{response}")

期待される出力

AIの回答:
1. Unified Memory Architectureにより、CPUとGPU間で巨大なモデルデータをコピーせずに超低遅延で処理可能。
2. ワットパフォーマンスが極めて高く、MacBook Pro等のラップトップでもサーマルスロットリングを起こさず推論を維持できる。
3. 専用のMLアクセラレータとMetal APIの緊密な統合により、行列演算が最適化され、PyTorch等の既存環境を凌駕する速度を実現。

generate 関数の verbose=True を設定すると、生成速度(tokens/sec)が表示されます。 M2 Maxクラスなら秒間50〜100トークン程度出るはずです。 これは人間が読むスピードを遥かに上回る快適な速度です。

Step 4: 実用レベルにする

単発の回答ではなく、ChatGPTのように「文字が次々と表示される(ストリーミング)」かつ「継続的に対話できる」スクリプトにアップグレードします。 実務で使うツールを作るなら、このストリーミング処理は必須です。

import sys
from mlx_lm import load, stream

def chat_loop():
    model_path = "mlx-community/Meta-Llama-3-8B-Instruct-4bit"
    model, tokenizer = load(model_path)

    # 対話履歴を保持するリスト
    history = []

    print("AI Chat Bot (exitで終了)")

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

        history.append({"role": "user", "content": user_input})

        # モデルごとの適切なプロンプト形式に変換
        prompt = tokenizer.apply_chat_template(
            history, tokenize=False, add_generation_prompt=True
        )

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

        full_response = ""
        # stream関数で1トークンずつ取得
        for response in stream(model, tokenizer, prompt=prompt, max_tokens=1000):
            print(response, end="", flush=True)
            full_response += response

        print() # 改行
        history.append({"role": "assistant", "content": full_response})

if __name__ == "__main__":
    chat_loop()

このコードでは stream 関数を使っています。 generate と異なり、生成されたテキストが即座に response として返ってくるため、UXが劇的に向上します。 また、history リストに過去のやり取りを蓄積していくことで、文脈を考慮した会話が可能になります。

注意点として、ローカルLLMは履歴が増えるほど消費メモリと計算量が増えます。 実用的なツールにする場合は、過去5回分のやり取りだけを残す「スライディングウィンドウ」的な処理を組み込むのが私のいつものやり方です。

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

エラー内容原因解決策
ImportError: numpy.core.multiarray failed to importNumPyのバージョン不整合pip install --upgrade numpy を実行する
Killed または Pythonが強制終了メモリ(VRAM)不足より小さいモデル(4bitや1B/3Bモデル)を選択する
推論が異常に遅い(1tok/sec以下)他のアプリがGPU/メモリを占有しているブラウザや他の重いアプリを閉じて再試行する

次のステップ

MLXでローカルLLMを動かせるようになったら、次は「自分のデータ」を読み込ませる段階です。 具体的には以下の3つの方向に進むのが面白いと思います。

  1. RAG(検索拡張生成)の構築: 自分のメモやPDFファイルをベクトル化して保存し、MLX経由でLLMに参照させる仕組みです。 sentence-transformers などもMLXで動かせるため、完全にオフラインのナレッジベースが作れます。

  2. LoRAによるファインチューニング: MLXにはファインチューニング用のスクリプトも同梱されています。 自分の過去のメールやブログ記事を学習させて、自分そっくりの口調で返信してくれるモデルを作ることが可能です。

  3. ローカルAPIサーバー化: FastAPIなどと組み合わせて、自作のVS Code拡張機能やSlackボットのバックエンドとしてMLXを動かす構成です。 外部のAPIキーを使わずに、プライバシーを守りながらAI機能を業務に組み込めるのは大きな強みになります。

まずは、Hugging Faceで mlx-community を検索し、色々なモデル(Gemma-2, Qwen2, Phi-3など)を入れ替えて、モデルごとの「性格」の違いを体感してみてください。

よくある質問

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

動きます。ただし、Llama-3-8B-Instruct-4bit などのモデルで限界に近いです。 動作中はスワップが発生し、SSDへの負荷が高まる可能性があるため、常用するなら Phi-3-miniGemma-2-2B などのより軽量なモデルを選ぶのが賢明です。

Q2: 自分でモデルをMLX形式に変換することはできますか?

可能です。mlx-lm には変換スクリプトが含まれており、Hugging Faceにある通常のPyTorch/Safetensors形式のモデルを数コマンドでMLX形式に変換・量子化できます。お気に入りの新着モデルがMLX形式で公開されていなくても、自分で作れるのがこのライブラリの強みです。

Q3: MLXはNVIDIAのGPU(RTXシリーズなど)でも使えますか?

使えません。MLXはApple Siliconのハードウェア特性に特化したフレームワークです。 WindowsやLinux環境でNVIDIA GPUを使っている場合は、PyTorchやTensorRT-LLM、あるいは llama.cpp のCUDAビルドを使うのが最適な選択肢になります。


あわせて読みたい