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

この記事で作るもの

Apple Silicon(M1/M2/M3/M4)のパワーを最大限に引き出すフレームワーク「MLX」を使い、最新のLLM(Qwen2.5等)とチャットができるPythonスクリプトを構築します。

  • Pythonの基礎(pipインストールやスクリプト実行)ができること
  • Apple Silicon搭載のMacを使用していること
  • Hugging FaceのモデルをMac上で直接動かし、ブラウザを介さずオフラインで回答を得る

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

ローカルLLMを動かす上で、最も重要なのは「メモリ(ユニファイドメモリ)」の容量です。Apple Siliconの最大の特徴はCPUとGPUがメモリを共有している点で、MLXはこの仕組みをフルに活用します。

最低ラインはメモリ16GBです。8GBモデルでも動作はしますが、OSのシステム領域と取り合うため、3B(30億パラメータ)以下の非常に小さなモデルしかまともに動きません。仕事で「使える」レベルの7B(70億パラメータ)クラスを動かすなら、16GBは必須、32GBあれば快適です。

GPUコア数は多いに越したことはありませんが、それ以上にメモリ帯域幅が重要です。M2/M3 ProやMaxチップを搭載したモデルなら、推論速度が飛躍的に向上します。逆に、Intel MacではMLXは一切動作しません。

費用については、ハードウェアさえあれば無料です。API経由の従量課金を気にせず、1日に何万トークン投げても電気代以外はかかりません。Mac miniをローカルLLM専用サーバーとして常時起動させるのも、消費電力が低いApple Siliconならではの賢い選択です。

なぜこの方法を選ぶのか

MacでローカルLLMを動かす手段として、他に「Ollama」や「LM Studio」があります。これらはGUIで使いやすいですが、中身は「llama.cpp」というC++ベースのライブラリで動いています。

一方で「MLX」は、Appleの機械学習チームがApple Siliconのためにゼロから設計したフレームワークです。PyTorchに近い操作感でPythonから直接叩けるため、自分で開発するプログラムに組み込む際、圧倒的に柔軟性が高いのが特徴です。

具体的には、ユニファイドメモリへのアクセスが最適化されており、モデルのロード時間が短く、量子化(モデルの軽量化)されたモデルの実行速度がllama.cppと同等か、条件によってはそれ以上に高速です。「ただ動かす」だけでなく「自分のアプリに組み込む」ことを考えるなら、MLX一択だと私は断言します。

Step 1: 環境を整える

まずはMLXを動かすための専用仮想環境を作ります。グローバル環境を汚すと、後で他のライブラリと衝突して動かなくなる「依存関係地獄」に陥るからです。

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

# Python 3.10以上の環境を推奨(MLXの最適化が進んでいるため)
python3 -m venv .venv

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

# MLX関連のライブラリをインストール
# mlx-lmは、Hugging FaceのモデルをMLXで簡単に扱うための高レベルライブラリです
pip install mlx-lm huggingface_hub

MLXは頻繁にアップデートされます。最新のM4チップなどの機能を最大限使うために、常に最新版をインストールするようにしてください。

⚠️ 落とし穴: Xcode Command Line Toolsがインストールされていないと、インストール中にエラーが出ることがあります。その場合は xcode-select --install を実行してください。また、Pythonのバージョンが古すぎるとMLXのパッケージが見つからないため、必ず3.10以降を使用してください。

Step 2: 基本の設定

MLXでモデルを動かす際、最も重要なのは「どのモデルを、どの精度で読み込むか」です。今回は、日本語能力が高く軽量な「Qwen2.5-7B-Instruct」を、4bit量子化された状態で使用します。

import os
from mlx_lm import load, generate

# モデルの指定
# mlx-communityにあるモデルは、MLX用に最適化(量子化)済みなのでロードが速いです
model_id = "mlx-community/Qwen2.5-7B-Instruct-4bit"

# モデルとトークナイザーのロード
# load関数は、ローカルになければ自動的にHugging Faceからダウンロードします
model, tokenizer = load(model_id)

# 読み込みが完了したことを確認
print(f"Model {model_id} loaded successfully.")

ここで「4bit」という形式を選ぶのがポイントです。元のモデル(16bit)のままだと15GB以上のメモリを消費しますが、4bitに圧縮することで約5GBまでメモリ使用量を抑えられます。これにより、16GBメモリのMacでもサクサク動くようになります。

Step 3: 動かしてみる

最小限のコードで、LLMに質問を投げます。MLXはApple SiliconのGPUを自動的に認識して使用するため、特別なデバイス指定(device="cuda"など)は不要です。

# プロンプトの作成
# Qwenなどのチャットモデルには特定のフォーマットが必要です
prompt = "MacでローカルLLMを動かすメリットを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,
    max_tokens=500, # 必要以上に長く生成しないための制限
    temp=0.7        # 自由度(0に近いほど固く、1に近いほど独創的になる)
)

print(response)

期待される出力

MacでローカルLLMを動かすメリットは以下の3点です:
1. プライバシーの保護:データが外部サーバーに送信されないため、機密情報を扱えます。
2. コスト削減:API料金が発生せず、オフラインで何度でも実行可能です。
3. 低遅延:Apple Siliconのユニファイドメモリにより、高速なレスポンスが得られます。

MLXは「生成が終わってから一括で表示する」のがデフォルトの挙動です。しかし、これでは長い文章のときにフリーズしたように見えてしまいます。実用レベルにするには「ストリーミング出力」が必要です。

Step 4: 実用レベルにする

仕事で使えるレベルにするため、「ストリーミング出力(1文字ずつ表示)」と「継続的な会話」ができるチャットスクリプトを作成します。

import sys
from mlx_lm import load, stream_generate

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

def chat():
    # 会話履歴を保持するリスト
    history = []

    print("AI: こんにちは!何かお手伝いしましょうか? (exitで終了)")

    while True:
        user_input = input("You: ")
        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_generateを使うと、トークンが生成されるたびに取得できる
        for response in stream_generate(model, tokenizer, prompt, max_tokens=1000):
            print(response, end="", flush=True)
            full_response += response

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

if __name__ == "__main__":
    chat()

このコードでは stream_generate を採用しました。これにより、最初の1文字目が出るまでの時間(Time to First Token)が0.5秒以下になり、体感速度が劇的に向上します。また、履歴を history リストに蓄積することで、文脈を理解した回答が可能になります。

実務で活用する場合、例えばローカルにある議事録ファイルを読み込んで、このスクリプトに「この会議の決定事項をまとめて」と投げるような自動化ツールへの発展が容易です。

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

エラー内容原因解決策
ModuleNotFoundError: No module named 'mlx'仮想環境が未有効、またはインストール失敗source .venv/bin/activate を実行後、再度pipインストール。
Killed または Memory Errorメモリ不足他の重いアプリ(Chrome等)を閉じるか、より小さいモデル(例:Qwen2.5-1.5B-Instruct-4bit)を試す。
生成された日本語が文字化けするトークナイザーの不一致mlx-community 以外のモデルを使う際、MLX変換が正しく行われていない可能性があるため、公式変換済みモデルを選ぶ。

次のステップ

MLXでローカルLLMが動くようになったら、次は「自分専用のナレッジ」をAIに持たせる「RAG(検索拡張生成)」に挑戦してください。

MLXエコシステムには mlx-embeddings というライブラリもあり、テキストをベクトル化してデータベースに保存することも可能です。例えば、自分の書いた過去の記事や、社内のドキュメントを読み込ませ、その内容に基づいて回答する「プライベートAIアシスタント」を作ることができます。

また、mlx-lm.server コマンドを使えば、このローカル環境をOpenAI互換のAPIサーバーとして公開できます。これにより、CursorなどのAIエディタのバックエンドを自分のMacに差し替えることができ、開発コストをゼロに抑えるといった応用も可能です。

ローカルLLMは、もはや「動いてすごい」の段階を過ぎ、「どう実務に組み込むか」のフェーズに入っています。RTX 4090を回している私から見ても、MacでのMLX体験は「静音性」と「電力効率」の面で、開発者にとって極めて実用的な選択肢です。

よくある質問

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

動きますが、かなり制限されます。Qwen2.5-1.5BやLlama-3.2-1Bといった超軽量モデル(4bit版)であれば動作しますが、推論中にスワップが発生し、速度が低下する可能性があります。実用的な回答精度を求めるなら、16GB以上への買い替えを強くおすすめします。

Q2: MLXで独自のモデルをファインチューニング(学習)できますか?

可能です。mlx-lm にはLoRA(Low-Rank Adaptation)という手法を用いた学習スクリプトが含まれています。自分の過去のメール履歴やブログ記事を読み込ませて、自分の文体を模倣するAIを作ることも、Mac 1台で完結します。

Q3: Hugging Faceにある全てのモデルが動くのですか?

MLX形式に変換されている必要があります。ただし、mlx-lm には自動変換機能があるため、主要なアーキテクチャ(Llama, Mistral, Qwen, Phiなど)であれば、通常のモデルIDを指定するだけでライブラリが内部で変換して動かしてくれます。

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

Mac mini M2 (24GBメモリ)

MLXを常時稼働させるサーバーとして、コスパとメモリ容量のバランスが最高。

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

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


あわせて読みたい