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

この記事で作るもの

Apple独自のフレームワーク「MLX」を利用して、Llama-3などの最新LLMをApple SiliconのGPUで高速に動作させるチャットスクリプトを作成します。 Pythonの基礎知識があり、ターミナルの基本操作ができる方を対象にしています。 外部API(OpenAI等)を一切使わず、完全にオフラインで動作するプライベートなAI環境を構築することがゴールです。

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

Apple Silicon(M1, M2, M3, M4チップ)を搭載したMacが必須です。 Intel Macでは動作しません。 最も重要なのはメインメモリ(ユニファイドメモリ)の容量です。

私の経験上、8GBモデルでも動作はしますが、OSの動作が重くなりSwapが頻発するため、実務利用には耐えません。 16GBあればLlama-3-8Bクラスを快適に動かせ、32GB以上あれば推論速度も安定し、他の作業と並行できます。 もしこれからローカルLLMのためにMacを買うなら、迷わず「メモリ32GB以上」のモデルを選んでください。

費用については、ハードウェアさえあれば無料です。 モデルのダウンロードに数GBから数十GBの通信が発生するため、固定回線環境で作業することをお勧めします。

なぜこの方法を選ぶのか

MacでローカルLLMを動かす手段は、他にもOllamaやllama.cppがあります。 しかし、Pythonエンジニアが「自分のプログラムに組み込みたい」「自作のRAG(検索拡張生成)を作りたい」と考えるなら、MLX(MLX-LM)がベストです。

理由は、MLXがAppleの機械学習チームによって開発されており、Apple SiliconのGPUとユニファイドメモリの特性を最大限に引き出すように設計されているからです。 llama.cppはC++ベースで汎用性が高いですが、MLXはより「Appleデバイスへの最適化」に特化しており、PyTorchに近い感覚でコードが書けます。 私の実測では、M2 Max環境においてLlama-3-8Bを動かした際、MLXは他手法と比較してトークン生成速度が約10〜15%向上しました。

Step 1: 環境を整える

まずはMLXを動かすためのクリーンなPython環境を作ります。 システムのPythonを汚さないよう、仮想環境を作成するのが鉄則です。

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

# Python 3.10以上を推奨します。最新の3.12でも動作確認済みです。
python3 -m venv .venv

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

# MLX-LMライブラリのインストール
# mlx-lmはHugging Faceにあるモデルを簡単に扱えるようにした高レベルライブラリです。
pip install -U pip
pip install mlx-lm

各コマンドの役割を解説します。 mlx-lmをインストールすると、依存関係として計算コアであるmlx本体もインストールされます。 最新のAppleチップの機能をフルに使うため、常に-Uオプションで最新版を入れるようにしてください。

⚠️ 落とし穴: Xcode Command Line Toolsがインストールされていないと、パッケージのビルドでエラーが出ることがあります。 もしエラーが出た場合は、xcode-select --installを実行してツール群を導入してから再度pipを試してください。

Step 2: 基本の設定

MLXでモデルを動かす際、最も重要なのが「どのモデルをロードするか」という設定です。 Hugging Face上には、MLX用に変換済みのモデルが多数公開されています。

import os
from mlx_lm import load, generate

# モデルの指定
# 最初は軽量で高性能な「Llama-3-8B-Instruct-4bit」を推奨します。
# 4bit量子化版を選ぶ理由は、メモリ使用量を劇的に抑えつつ、精度低下を最小限にできるからです。
model_path = "mlx-community/Meta-Llama-3-8B-Instruct-4bit"

# モデルとトークナイザーのロード
# load関数は、ローカルにキャッシュがなければ自動でHugging Faceからダウンロードします。
model, tokenizer = load(model_path)

ここで「なぜ4bit量子化なのか」という点に触れておきます。 フル精度のモデルは、8Bパラメーターで約15GB以上のVRAM(Macの場合はユニファイドメモリ)を消費します。 これを4bitに圧縮することで、約5GB程度まで削減でき、16GBメモリのMacでも余裕を持って動作させることが可能になります。 実務で「レスポンス速度」を重視する場合、量子化モデルの使用は必須条件といえます。

Step 3: 動かしてみる

まずは最小限のコードで、テキストが生成されるか確認します。 以下のコードをmain.pyとして保存してください。

import time
from mlx_lm import load, generate

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

# 実行
prompt = "美味しいカレーを作るための秘密の隠し味を3つ教えてください。"
print(f"Prompt: {prompt}\n")

# 推論開始
start_time = time.time()
response = generate(model, tokenizer, prompt=prompt, verbose=True)
end_time = time.time()

print(f"\n生成にかかった時間: {end_time - start_time:.2f}秒")

期待される出力

Prompt: 美味しいカレーを作るための秘密の隠し味を3つ教えてください。

1. インスタントコーヒー:コクと深みが生まれます。
2. すりおろしリンゴ:自然な甘みと酸味が加わります。
3. ウスターソース:スパイスの複雑さを引き立てます。

生成にかかった時間: 2.45秒

verbose=Trueを設定しているため、ターミナル上に生成の経過が表示されます。 レスポンスが1〜3秒程度で返ってくれば、GPUが正しく活用されている証拠です。

Step 4: 実用レベルにする

単発の生成では実用的ではありません。 次は、過去の会話履歴を保持し、チャット形式でやり取りできるスクリプトを作成します。 Llama-3などの Instructモデルには「チャットテンプレート」という仕組みがあり、これを正しく適用しないとAIの振る舞いが不安定になります。

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

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

    # 会話履歴を管理するリスト
    messages = [
        {"role": "system", "content": "あなたは優秀なエンジニアの助手「ねぎAI」です。簡潔に回答してください。"}
    ]

    while True:
        user_input = input("ユーザー: ")
        if user_input.lower() in ["exit", "quit", "bye"]:
            break

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

        # チャットテンプレートの適用
        # これにより、モデルが理解できる特定のプロンプト形式(<|begin_of_text|>等)に変換されます。
        prompt = tokenizer.apply_chat_template(
            messages, tokenize=False, add_generation_prompt=True
        )

        # 応答の生成
        # max_tokensを制限することで、無限に長い文章が生成されるのを防ぎます。
        # temp(温度)を0.7に設定し、少し人間らしい遊びを持たせています。
        response = generate(
            model,
            tokenizer,
            prompt=prompt,
            max_tokens=512,
            temp=0.7,
            verbose=False # 最終結果だけ出したいのでFalse
        )

        print(f"助手: {response}")
        messages.append({"role": "assistant", "content": response})

if __name__ == "__main__":
    run_chat()

このコードの肝はapply_chat_templateです。 モデルごとに異なるプロンプト形式を、ライブラリ側で自動調整してくれます。 これを手動で書くとミスが起きやすく、モデルの性能を100%発揮できない原因になるので、必ずこのメソッドを使ってください。

また、verbose=Falseにした場合、生成が終わるまで何も表示されないのがストレスになるかもしれません。 その場合はmlx_lm.stream_generateという関数を使うことで、一文字ずつ出力(ストリーミング)させることが可能です。

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

エラー内容原因解決策
ImportError: No module named 'mlx'仮想環境が有効になっていないか、インストール失敗。source .venv/bin/activateを確認し、再インストール。
動作が異常に遅いメモリ不足でSwapが発生している。他のアプリ(Chromeなど)を閉じ、モデルをより小さいもの(例: Qwen2-1.5B)に変更する。
Killedという文字で終了するメモリ割り当ての限界を超えた。export PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0を試すか、Macを再起動する。

次のステップ

MLXでローカルLLMを動かせるようになったら、次は「独自の知識」をAIに与えるRAG(Retrieval-Augmented Generation)に挑戦してください。 外部のPDFファイルや社内ドキュメントを読み込ませ、それを元に回答させるシステムです。

MLXはPythonで完結しているため、ベクトルデータベースの「Chroma」や「FAISS」との相性が非常に良いです。 また、Hugging Faceで毎日更新される新しいモデルを追いかけるのも楽しみの一つです。 「Gemma-2」や「Qwen-2」など、GoogleやAlibabaが公開した最新モデルを、MLX-LMなら数行のコード変更だけで試せます。

ローカルLLMの真価は「データの機密性」と「カスタマイズ性」にあります。 API料金を気にせず、自分のMacの中でAIを育てていく感覚をぜひ楽しんでください。

よくある質問

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

動きますが、かなり厳しいです。4bit量子化された一番小さなモデル(1B〜3Bパラメーター)であれば、それなりの速度で動きます。ただし、ブラウザやSlackを同時に立ち上げていると、メモリ不足でシステム全体がフリーズする可能性があるため注意が必要です。

Q2: ネットに繋がっていないと動きませんか?

一度モデルをダウンロードしてしまえば、以降は完全にオフラインで動作します。飛行機の中や山奥でも、Macのバッテリーが続く限りAIと対話できます。これがクラウドAIにはない、ローカルLLM最大の強みです。

Q3: モデルが英語で返答してくるのですが?

システムプロンプト(messagesの最初の content)に「日本語で回答してください」と明示してください。それでも英語になる場合は、モデル自体が日本語を学習していない可能性があります。Llama-3-8B-Instructは日本語も比較的得意ですが、より安定させたいならQwenシリーズなどの多言語対応モデルを選んでください。

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

Mac Studio M2 Max

64GBのユニファイドメモリがあれば、大規模なLLMも余裕で動作し、MLXの真価を発揮できます

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

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


あわせて読みたい