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

この記事で作るもの

Apple Silicon(M1/M2/M3/M4チップ)のGPU性能をフルに活用し、ローカル環境で爆速なチャットAIを動かすPythonスクリプトを作成します。 Pythonの基礎知識(仮想環境の構築とライブラリのインストールができる程度)があれば、外部APIを一切使わずに、自分だけのプライベートなLLM環境が手に入ります。

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

ローカルLLMを動かす上で、もっとも重要なのはGPUの型番ではなく「ユニファイドメモリ(RAM)の容量」です。 Apple SiliconはCPUとGPUが同じメモリを共有するため、メモリ容量がそのまま扱えるモデルのサイズに直結します。

最低ラインはメモリ16GBです。 8GBモデルでも動作はしますが、OSやブラウザがメモリを消費しているため、モデルを読み込んだ瞬間にスワップが発生し、レスポンスが極端に低下します。 実務で「使える」速度を求めるなら、24GBまたは32GB以上のモデルを強く推奨します。

すでにMacを持っているなら追加費用は0円です。 これから購入を検討しているなら、MacBook ProのM3/M4シリーズや、据え置きならMac Studioが選択肢に入ります。 私はRTX 4090を2枚挿したPCも運用していますが、推論時の静音性とワットパフォーマンスに関しては、Apple Silicon + MLXの組み合わせが圧倒的に優れていると感じます。

なぜこの方法を選ぶのか

MacでローカルLLMを動かすには、llama.cppやOllamaといった素晴らしいツールが既にあります。 しかし、あえて「MLX」を選ぶ理由は、これがAppleの機械学習チームによって直接開発されているフレームワークだからです。

MLXは、Apple Siliconの「ユニファイドメモリ」アーキテクチャに最適化されています。 具体的には、メモリのコピーを最小限に抑える仕組み(ゼロコピー)が取り入れられており、PyTorchのMPS(Metal Performance Shaders)経由で動かすよりも、推論速度が1.5倍〜2倍近く速くなるケースを何度も目にしてきました。 また、Pythonで直接記述できるため、独自のRAG(検索拡張生成)システムやエージェントへ組み込む際の柔軟性が、他ツールとは比較にならないほど高いのがメリットです。

Step 1: 環境を整える

まずはMLXを動かすためのクリーンな環境を作ります。 macOSのバージョンは、最新のMetal機能を利用するためにSonoma 14.3以上を推奨します。

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

# Python 3.10以上で仮想環境を作成
# 3.12でも動作しますが、一部の依存ライブラリの関係で3.10 or 3.11がもっとも安定します
python3 -m venv .venv
source .venv/bin/activate

# mlx-lmパッケージをインストール
# これ一つでモデルのダウンロード、変換、推論がすべて完結します
pip install -U mlx-lm

mlx-lmは、Hugging Faceにある数千種類のモデルをMLX形式で直接扱えるようにする高機能ライブラリです。 これをインストールすることで、複雑なコンパイル作業なしにローカルLLMの世界へ入れます。

⚠️ 落とし穴: 古いIntel MacではMLXは動きません。 pip installは成功しても、実行時に「Illegal instruction」といったエラーが出ます。 必ず「このMacについて」からチップ名がApple M1/M2/M3/M4であることを確認してください。

Step 2: モデルの選定と初期化

次に、動かすモデルを選びます。 現在はGoogleの「Gemma-2-9b-it」や、Metaの「Llama-3.1-8B-Instruct」が非常に高性能で、日本語の処理能力も高いです。

ここでは、バランスの良い「Llama-3.1-8B-Instruct」を4ビットに量子化したものを使用します。 量子化とは、モデルの精度をわずかに犠牲にしてメモリ消費量を大幅に(約1/4に)削減する技術です。

from mlx_lm import load, generate

# モデルのパスを指定
# mlx-communityが公開している4bit量子化済みモデルを使うのが一番手っ取り早いです
model_path = "mlx-community/Meta-Llama-3.1-8B-Instruct-4bit"

# モデルとトークナイザーをロード
# 初回実行時は自動的にHugging Faceからダウンロードされます(約5GB)
model, tokenizer = load(model_path)

load関数を使う理由は、モデルの重みをApple Siliconのメモリ空間に効率よく配置するためです。 mlx-communityというリポジトリには、有志によってMLX専用に変換された最新モデルが即座にアップロードされるため、ここをチェックする習慣をつけるとトレンドに乗り遅れません。

Step 3: 動かしてみる

まずは最小限のコードで、モデルが言葉を発するか確認しましょう。 MLXのgenerate関数は非常にシンプルです。

# プロンプトを作成
# Llama 3のフォーマットに従う必要がありますが、まずはシンプルに
prompt = "Apple Siliconのすごさを、エンジニア向けに3行で説明してください。"

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

print(response)

期待される出力

1. CPUとGPUが同じメモリを共有するユニファイドメモリにより、データの移動コストがほぼゼロ。
2. MLXフレームワークにより、Metalの性能をPythonから直接、最大限に引き出せる。
3. 高いワットパフォーマンスを維持しつつ、RTX 30シリーズに匹敵する推論速度を静音で実現。

verbose=Trueに設定すると、1秒間に何トークン生成されたか(tokens per second)が表示されます。 M2 Pro以上のチップなら、秒間40〜60トークン程度の速度が出るはずです。 これは、人間が文章を読む速度を遥かに上回る快適なスピードです。

Step 4: 実用レベルにする

上記のコードでは、全ての生成が終わるまで待機する必要があります。 これではChatGPTのような「逐次表示される心地よさ」がありません。 実務で使うために、ストリーミング出力とチャット履歴の管理を実装したスクリプトへアップグレードしましょう。

import mlx.core as mx
from mlx_lm import load, generate
from mlx_lm.utils import stream_generate

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

    # チャット履歴を保持するリスト
    messages = [
        {"role": "system", "content": "あなたは優秀なITエンジニアです。簡潔かつ技術的に正確な回答をしてください。"}
    ]

    print("AI: こんにちは!何でも聞いてください。('exit'で終了)")

    while True:
        user_input = input("You: ")
        if user_input.lower() == "exit":
            break

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

        # モデル固有のチャットテンプレートを適用
        # これをやらないとモデルが「どこまでが自分の発言か」を理解できません
        prompt = tokenizer.apply_chat_template(
            messages, tokenize=False, add_generation_prompt=True
        )

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

        full_response = ""
        # ストリーミング生成を実行
        for response in stream_generate(model, tokenizer, prompt, max_tokens=1000):
            print(response, end="", flush=True)
            full_response += response

        print("\n")
        messages.append({"role": "assistant", "content": full_response})

if __name__ == "__main__":
    run_chat()

このコードのポイントは2つあります。 1つ目はtokenizer.apply_chat_templateです。 モデルごとに異なる特殊トークン(<|begin_of_text|>など)を正しく挿入してくれます。 これを怠ると、AIが独り言を始めたり、質問を繰り返したりする「ボケ」が発生します。

2つ目はstream_generateです。 1トークン生成されるごとにループが回るため、即座に画面に文字が表示されます。 レスポンス速度はユーザー体験に直結するため、自作ツールを作る際は必須の実装です。

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

エラー内容原因解決策
Out of memoryメモリ不足小さいモデル(3B以下)を使うか、4bit/2bit量子化モデルを選ぶ。
AttributeError: module 'mlx.core' has no attribute 'X'MLXのバージョンが古いpip install -U mlx mlx-lm で最新に更新する。
日本語が不自然モデルの選定ミスLlama-3-8B-Instruct の代わりに、日本語に強い Gemma-2Qwen2 を試す。

次のステップ

MLXでモデルを動かせるようになったら、次は「自分専用の知識」を教え込む段階です。 まずは、自分の過去のブログ記事やドキュメントをPDFから読み込ませる「RAG(Retrieval-Augmented Generation)」の実装に挑戦してみてください。

MLXにはmlx-embeddingsのような関連ライブラリもあり、テキストをベクトル化して検索する仕組みも同じエコシステム内で構築できます。 また、もしRTXシリーズのような強いGPUを積んだMac(そんなものはありませんが、クラウドのGPUなど)に慣れているなら、MLXでの「LoRAファインチューニング」も試す価値があります。 Mac Studioなら、一晩回せば特定の口調や専門知識に特化した自分だけのモデルが完成します。

ローカルLLMの最大のメリットは、機密情報を一切外に漏らさずに、何度でも無料で試行錯誤できることです。 まずはこのスクリプトをベースに、日々の定型業務を投げ込んでみるところから始めてみてください。

よくある質問

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

動くことは動きますが、非常にストレスフルです。3B(30億パラメータ)以下の非常に小さなモデルなら実用範囲内ですが、今回紹介した8Bモデルはメモリの大部分を専有するため、動作がガクガクになります。AIを触るなら、中古でも良いのでメモリ16GB以上のモデルを探すべきです。

Q2: モデルをダウンロードした場所はどこですか?

デフォルトでは ~/.cache/huggingface/hub に保存されます。数回試すと数十GBを簡単に消費するので、ディスク容量には注意してください。不要になったモデルはフォルダごと削除して問題ありません。

Q3: PyTorchとMLX、どっちを勉強すべきですか?

汎用性を求めるならPyTorchですが、Apple Silicon環境で最高のパフォーマンスを出したい、あるいはMac専用のAIアプリを作りたいならMLX一択です。記法はNumPyやPyTorchに似ているので、一度触れば移行は難しくありません。

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

MacBook Pro M3 36GB

MLXでの推論と開発を両立させるなら36GBメモリが最もコスパが良い

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

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


あわせて読みたい