Mac のためのローカル LLM 環境 MLX-LM のススメ

前回の記事では、Qwen3 の MLX 版と Ollama (GGUF) 版の速度比較を行いました。結論として、MLX 版の LLM のほうが速いということがわかりました。

その後も主に MLX-LM で LLM を使っているのですが特に不具合も無く、なんなら慣れてしまえばとてもヨイということがわかってきました。というわけで本記事でやり方を一通り共有します。現在 Ollama を使用中で MLX-LM はまだ触っていない、という方が対象になるかと思うので、所々で Ollama との比較を入れていきます。

フロントエンドとしては、ボクは Dify をメインで使っていますが、よりお手軽な Open WebUI での使い方にも触れます。

書いていたら大作になってしまったので、気になるところだけでも覗いてみてください。

↑前回の MLX-LM vs Ollama 的記事

なぜ MLX-LM を使うのか

MLX-LM を使う理由は、MLX は Apple が開発している機械学習フレームワークなので Apple ハードウェア (M1~M4 等の Apple シリコンシリーズ) に最適化されており、単純に LLM の実行速度が Ollama (GGUF モデル) より速いからです。前回の記事で調べてみてはっきりとわかりました (量子化の違いもあり性能差はあるのでしょうが、それすら MLX のほうが上という調査結果もあります)。

実は Dify で MLX-LM を使い始めた当初、システム推論モデルとして Ollama のモデルを使用していました。すると、最初のチャットの後サマリ (タイトル?) の生成に Ollama のモデルが使われ、メモリの使用量が高止まりするような状況が頻発しました。それで MLX-LM はまだ実用には向かないと勝手に思い込んでいたのですが、Dify のシステムモデル設定で推論モデルも MLX の小さなモデルに変更したところ、チャットサマリ生成後もメモリプレッシャーがキレイに下がることがわかりました。MLX-LM だけを使用することで無駄にメモリが占有される問題は解消です。

また、サーバの起動やモデルのダウンロードで必要な長めのコマンドも仮想環境専用のaliasを登録することで解消できて、運用の手間が大幅に下がったことも大きいです (使い慣れている Ollama ではまだ MLX のモデルが使えないので仕方なくなんとかした、とも言えますけど) サーバ自体の起動も速いので、一度落としてあげ直すのも苦痛じゃないです。

MLX で LLM を動かすだけなら LM Studio という選択肢もあります。モデルの検索からダウンロード、テキストのチャット、ビジョンモデルに画像を認識させる、OpenAPI コンパチの API サーバを立ち上げる、等など様々な機能が利用できます。が、全部盛り過ぎてアプリケーション自体が重いのと、モデルを読み込むとその分メモリを占有し続けるのが個人的には気に入らないです。ネット上では、ボクはあまり気にしていませんが、プロプライエタリ (クローズドソース) だからダメだ、なんて論調もありますね。逆に「自分は LM Studio が好き、LM Studio で MLX のモデルを使う」という方はこれ以上読む必要はありません。LM Studio は使わないという人向けの内容です。

MLX-LM モデルの量子化について

新しめの MLX-LM には Learned Quantization (学習済み量子化?) という機能が導入されています。これまでの、全体を画一的に 8-bit や 4-bit に量子化するのではなく、より効率的に量子化を行うことで、結果としてモデルのサイズを小さくしたり、性能の劣化を小さくしたり、推論速度を上げたり、ということができるようです。Hugging Face ではDWQAWQDynamic等とモデル名に付いているものがこれらのテクニックを使って量子化されている事を示しています。詳細はこちら (公式):

https://github.com/ml-explore/mlx-lm/blob/main/mlx_lm/LEARNED_QUANTS.md

ボクも 32GB RAM の M2 max で google/gemma-3-12b-it の Dynamic-quant を数回チャレンジしてみたのですが、おそらくメモリ不足で macOS がクラッシュしてしまい、諦めました (量子化作業にはpip install datasetsが必要でした)。上の公式以外では詳細について書かれている記事などもほぼ見当たらず、今後に期待ですね。

モデルの選定

Mac の GPU に割り当てる VRAM 容量を増やしたり、モデルに最適な量子化が行われていたりしても、それらはより大きなパラメータサイズを使えるようになるほどの効果は期待しづらいです (32B を 70B にとか 4-bit を 8-bit に等はキツい)。なので、これまで Ollama で使っていたモデルの同レベルの量子化バージョンが、より速く低劣化で動き、より大きなコンテキスト長が使えるというのが MLX-LM モデルの大きなメリットになると思います。

モデルを選定するには、慣れないうちは LM Studio で MLX のみにチェックを入れて使えそう (Full GPU Offload Possible) なモデルを見つけて Model (例: /deepseek/deepseek-r1-0528-qwen3-8b) をコピーし、後述するコマンドでダウンロード、というのが良いと思います。慣れてきたら Hugging Face で “mlx gemma-3” 等と検索するのが早くなると思います。

下の記事ではより詳細に自分の RAM (ユニファイドメモリ) のサイズに合わせたモデルの見つけ方を説明しています。(英語ページが開いてしまったら、右の「日本語」をクリックしてください)

今回は MLX-LM に変換・量子化されたモデルを対象とした記事ですが、そもそもの LLM の性能差などを調べるのは、各種リーダーボードを見るのが良いでしょう。ボクは最近もっぱら↓のサイトで性能差を見ています。

https://artificialanalysis.ai (オープン、クローズド、複数選んで比較できます。新しいモデルが追加されるのも早い)

試した環境

  • Mac Studio M2 Max 32GB GPU (24,576 GB を VRAM に割り当て済み。OS 標準以上の容量を GPU に割り振る方法はこちら)
  •  macOS: Sequoia 15.5
  • Python 仮想環境: pipenv version 2025.0.3 (なぜ pipenv なのか、みたいな話はこちら)
  • Python: 3.12.11 (特に意味は無し。brew install [email protected]でインストール)
  • MLX-LM: 0.25.2 (pip install mlx-lmでインストール)
  • Open WebUI: 0.6.15 (pip install open-webuiでインストール)
  • Dify: 1.4.2 (LAN にいる別の Mac mini M1 にインストール。やりかたはこちら)
  • Ollama: 0.9.2 preview (Ollama 新アプリのプレビュー版。比較用に。詳しくはこちら)
  • LLM: Qwen/Qwen3-32B-MLX-4bit (17.42 GB / メインで使う LLM)
  • LLM: mlx-community/gemma-3-12b-it-4bit (8.07 GB / Dify のシステム推論モデルとして使用)

RAM が 32GB より小さい場合は LLM も性能がそれなりのものしか使えないので、正直実用的なローカル LLM 環境を作るのはキツいと思います。48GB 以上あれば Dify 含めて全て同一 Mac で動かせると思います。

仮想環境を作る

Python の仮想環境は、最低限 MLX-LM 実行用に一つ必要です。Open WebUI を新たにpipで導入する場合には専用にもう一つ作ったほうが良いと思います。お好みの仮想環境ツール+上記pipコマンドで作ってください。

もし新規で Dify をインストールする場合は Docker が必要となりますので、公式過去記事を参考に構築してください (CPU >= 2コア、RAM >=4GB の割り当てが必要)。

(蛇足) ボクはあまり人気が無いらしいpipenvを使ってます。仮想環境内だけで有効になるaliasを使って長くなりがちなコマンドを簡単に実行しています。特にこだわりや縛りの無い方はお試しあれ。(英語ページが開いてしまったら、右の「日本語」をクリックしてください)

pipenv内専用のaliasについてもう少し触れておくと、仮想環境のルートディレクトリに置いた.zshrc.localファイルに下記のように書き込んでおけば、pipenv shellで環境に入ったときだけmlxsvで MLX-LM の API サーバを実行でき、モデルのダウンロードはdownloadの後に Hugging Face のモデルを指定するだけで実行できるので便利です (例: download mlx-community/gemma-3-12b-it-4bit)。詳細は上記記事をご覧ください。

alias mlxsv='mlx_lm.server --host 0.0.0.0 --port 8585 --log-level DEBUG'
alias download='HF_HUB_ENABLE_HF_TRANSFER=1 huggingface-cli download'

MLX-LM 仮想環境でモデルをダウンロード&動作確認

mlx_lm.generateコマンドを使ったモデルのダウンロード方法をよく見ますが、やっているのはollama runコマンドでモデルをダウンロードしてチャット開始するのと近く、ダウンロード後にテキストの生成が行われます (チャットでは無く、生成のみ)。ollama pullのようにシンプルにモデルをダウンロードするだけであれば、Hugging Face のコマンドをインストールして使用するのが良いでしょう。というわけで、まずは MLX-LM 用に作った仮想環境に入ってから Huggng Face 関連コマンドをインストールします。

pip install -U huggingface_hub hf_transfer

次に、普通にやるより速いらしい以下の方法でモデルをダウンロードします (上記のaliasを設定済みであればdownload Qwen/Qwen3-32B-MLX-4bitで OK です)。モデルはお好みでどうぞ。

HF_HUB_ENABLE_HF_TRANSFER=1 huggingface-cli download Qwen/Qwen3-32B-MLX-4bit

動作確認はmlx_lm.chatコマンドでターミナルから行えます。下記例では最大トークン数をデフォルトより増やしています (Qwen3 のような thinking/reasoning モデルだと考えているうちに最大トークンに達してしまう)。

mlx_lm.chat --model Qwen/Qwen3-32B-MLX-4bit --max-tokens 8192
MLX-LM と Ollama との CLI チャット機能比較: mlx_lm.chatコマンドはあまりイケてません。ollama runコマンドのようにチャットを始めてから設定を変更したりはできませんし、いくつか改行するつもりでエンターキーを叩くと無言のプロンプトが LLM に送られて生成が始まりますし、LLM のテキスト生成を止めようと Ctrl + C するとコマンド自体が停止します (ズコー)。よって、ollama runの様な使い勝手は期待してはいけません。

次のコマンドでは Dify のシステム推論モデルとして設定する mlx-community/gemma-3-12b-it-4bit もダウンロードしています。Dify を使わない方は不要です。ファイルサイズは 8.1GB なので、上で落とした Qwen/Qwen3-32B-MLX-4bit の半分以下の時間で完了すると思います。

HF_HUB_ENABLE_HF_TRANSFER=1 huggingface-cli download mlx-community/gemma-3-12b-it-4bit

ダウンロードされたモデルを一覧表示するのは以下のコマンドです:

mlx_lm.manage --scan

ところがこのコマンドでは最初にダウンロードしたQwenリポジトリのモデルは表示されません。mlx-communityリポジトリのモデルは表示されます。API サーバを実行すればブラウザからは確認できるので、その方法は後ほど説明します。また、モデル名を指定すれば使用することも可能です。

チャットで使うならこう:

mlx_lm.chat --model Qwen/Qwen3-32B-MLX-4bit

削除するならこう:

mlx_lm.manage --delete --pattern Qwen/Qwen3-32B-MLX-4bit

モデルは/.cache/huggingface/hub/以下に保存されているため、ファインダーから削除しても問題ありません。

ところで先ほどのmlx_lm.manage --scanでモデルの実サイズは表示されるものの、他の情報は特に確認できません。Ollama では ollama show <modelname>でコンテキスト長や量子化方法等を確認できますが、代わりになる方法は MLX-LM にはありません。必要な場合は Hugging Face のモデルカードを確認するか、LM Studio がインストールしてあれば My Models で確認するか、といったところです。ただしコマンドでダウンロードしたモデルの名前は LM Studio では正しく表示できないので、ダウンロードしたタイミングなどで見分けましょう。モデルの詳細 (メタデータ) を確認する機能はぜひ MLX-LM に追加して欲しいところですよね (LM Studio は MLX-LM を内蔵しているので、同じ事ができると思うんですけど)。

OpenAI API コンパチのサーバを実行する

公式の実行方法 (↓ のリンク) をみるとモデル名を渡しているのでそのモデルしか使えないのかと思っていたのですが、サーバの起動時にモデル名を渡す必要はありません。起動後はクライアントで指定したモデルが利用できます。

https://github.com/ml-explore/mlx-lm/blob/main/mlx_lm/SERVER.md

上のaliasのとこにも書きましたが、ボクが MLX-LM の API サーバを実行するコマンドは以下の通りです。オプションがどれも不要であれば、mlx_lm.serverだけで大丈夫です。

mlx_lm.server --host 0.0.0.0 --port 8585 --log-level DEBUG
  • --host 0.0.0.0 こうするとの他のホストからもアクセスできます (ボクは Dify が別の Mac で動いているので必須)
  • --port 8585 デフォルトの8080 Open WebUI のデフォルトと被るので変えています
  • --log-level DEBUG プロンプトと速度 (tokens-per-sec = トークン/秒) やメモリの最大使用量が表示されます

コマンドを実行するとほどなく以下の様な画面になり、LLM が使用できるようになります。

% mlx_lm.server --host 0.0.0.0 --port 8585 --log-level DEBUG
/Users/handsome/Documents/Python/mlx-lm/.venv/lib/python3.12/site-packages/mlx_lm/server.py:880: UserWarning: mlx_lm.server is not recommended for production as it only implements basic security checks.
warnings.warn(
2025-06-28 18:56:23,071 - INFO - Starting httpd at 0.0.0.0 on port 8585...

UserWarningには、基本的なセキュリティチェックしか行っていないので本番環境での使用は推奨しない、と書かれています。閉じた環境で使う分には問題無いでしょう。

では簡単に、接続できるのかを試しておきましょう。ウェブブラウザで以下の URL を開くと、MLX-LM から利用できるモデルが表示されます。

http://localhost:8585/v1/models

表示例 (Qwen も見えてますね):

{"object": "list", "data": [{"id": "mlx-community/gemma-3-12b-it-4bit", "object": "model", "created": 1751104699}, {"id": "qwen/qwen3-1.7b", "object": "model", "created": 1751104699}, {"id": "Qwen/Qwen3-32B-MLX-4bit", "object": "model", "created": 1751104699}, {"id": "mlx-community/Qwen2.5-Coder-32B-Instruct-4bit", "object": "model", "created": 1751104699}, {"id": "mlx-community/QwQ-32b-4bit-DWQ", "object": "model", "created": 1751104699}]}

こうなれば、OpenAI API コンパチブルサーバに接続できるクライアントから MLX-LM の LLM を利用できるようになります。

サーバの停止とアップデート

アクティビティモニタでメモリメモリプレッシャーを見ていると、まれに黄色く高止まりすることがあります。そんなときは Ctrl + C で一度 MLX-LM サーバを止めて再度走らせるのが安心ですが、高止まりの原因が MLX-LM であれば次のチャットの後には平常に戻ることがほとんどです。他に GPU ヘビーなアプリを使っていなければ、雑に扱っても割と平気です。

アップデートに関しては Ollama のようなアイコンで知らせてくれたり、自動でダウンロードしてくれるような機能はありません。必要に応じてコマンドを叩く必要があります。

pip list|grep mlx # インストール済みバージョンの確認
pip install -U mlx-lm

https://pypi.org/project/mlx-lm (pip パッケージの情報)

もしアップデート後に不具合が出たら、上のコマンドで表示されたインストール済みバージョンに戻しましょう。例えばバージョン 0.25.1 に戻すならこんな感じです:

pip install mlx-lm==0.25.1

Open WebUI から接続する

Open WebUI を実行する

別の Python 仮想環境に Open WebUI をインストールした場合は、以下のコマンドでクライアントを実行できます (公式ではpipよりuvを強力に推していましたが、ボクは Open WebUI をメインで使わないのでなじみのあるpip使っちゃいました)。Docker で構築した人は飛ばしてください。

open-webui serve

オプションで--host (デフォルト: 0.0.0.0)、--port (デフォルト: 8080) の指定も可能です。

(ボクはコマンドを忘れがちなので、.zshrc_localalias sv='open-webui serve'と書いてきて、svで起動できるようにしています)

しばし待ち、ターミナルにロゴといくつかのINFOが表示されたらアクセスできるハズです (ロゴが収まりきらなかったのでコードブロックで貼り付けました)。


 ██████╗ ██████╗ ███████╗███╗   ██╗    ██╗    ██╗███████╗██████╗ ██╗   ██╗██╗
██╔═══██╗██╔══██╗██╔════╝████╗  ██║    ██║    ██║██╔════╝██╔══██╗██║   ██║██║
██║   ██║██████╔╝█████╗  ██╔██╗ ██║    ██║ █╗ ██║█████╗  ██████╔╝██║   ██║██║
██║   ██║██╔═══╝ ██╔══╝  ██║╚██╗██║    ██║███╗██║██╔══╝  ██╔══██╗██║   ██║██║
╚██████╔╝██║     ███████╗██║ ╚████║    ╚███╔███╔╝███████╗██████╔╝╚██████╔╝██║
 ╚═════╝ ╚═╝     ╚══════╝╚═╝  ╚═══╝     ╚══╝╚══╝ ╚══════╝╚═════╝  ╚═════╝ ╚═╝


v0.6.15 - building the best AI user interface.

https://github.com/open-webui/open-webui

Fetching 30 files: 100%|█████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 7708.23it/s]
INFO:     Started server process [84450]
INFO:     Waiting for application startup.
2025-06-28 19:08:50.198 | INFO     | open_webui.utils.logger:start_logger:140 - GLOBAL_LOG_LEVEL: INFO - {}
2025-06-28 19:08:50.198 | INFO     | open_webui.main:lifespan:514 - Installing external dependencies of functions and tools... - {}
2025-06-28 19:08:50.370 | INFO     | open_webui.utils.plugin:install_frontmatter_requirements:241 - No requirements found in frontmatter. - {}

以下の様な URL をブラウザで開きましょう (デフォルトでホストが0.0.0.0なので、自宅の Wi-Fi であれば iPhone 等から Mac の IP アドレスを指定してアクセスできます)。

http://localhost:8080

最初に管理者アカウントの作成があるんじゃないかと思いますので、終わらせてから進めてください。

OpenAI API として追加する

右上のアイコンから管理者パネルを開きます。

設定から接続を選び、OpenAI API接続の管理にあるプラスボタンをクリックします (下のスクリーンショットは設定済みの状態)。

Connection Type の右の「外部」をクリックして「ローカル」に変更し、URL に今回の例では「http://localhost:8585/v1」を入力し、保存します。

接続の下の「モデル」をクリックすると、ダウンロード済みのモデルが表示されると思います。もし表示されなければ、一度 Open WebUI のターミナルでサーバを Ctrl + C で止めて、再度実行してみてください。

Alibaba の回し者ではないです

ついでにやっておくべきオススメ設定

ここでモデルが見えれば新しいチャットの右にあるドロップダウンメニューから選んで使えるハズですが、その他いくつかやっておくべき設定を紹介します。

モデルの詳細設定をする

管理者パネル > 設定 > モデルで、モデル名をクリックするとデフォルトの設定を変更できます。システムプロンプトに「常に日本語で回答してください」と入れたり、高度なパラメータを表示して max_tokens を最大値にしておくと良いでしょう (デフォルトだと 128トークンしかない)。下にある資格のチェックボックスは、よくわからなければ全て外してしまいましょう。最後に「保存して更新」をクリックするのをお忘れ無く。

コンテキスト長は Ollama を Dify から使う場合などは注意して設定しないと大きな生成速度の低下を招きますが (参考記事)、Open WebUI だと max_tokens や num_ctx (Ollama) をどれだけ大きくしても?影響ないみたいです。どうやっているのかは未確認。

余計な仕事をさせない

管理者パネル > 設定 > インターフェースで、Follow Up Generation とオートコンプリート生成をオフにして保存します。いらないでしょ?

チャットタイトルについて

上と同じインターフェース画面で、タイトル生成についての設定があります。この生成処理にも LLM が使われるので、全く不要ならオフにする事もできます。有効にしておく場合、Qwen3 ではここでも思考プロセスが動いてしまうため、タイトル生成プロンプトに/no_thinkとだけいれて保存しましょう。こうすると、何の工夫も無くチャットに最初に入力した文章そのままがタイトルになり、余計な GPU の使用を防げます (デフォルトのタイトル生成プロンプトを見ると対策をしようとしているみたいですが、現状はうまく機能していません)。

Safari ユーザは日本語確定のエンターでメッセージが送信されるのを防ぐ

別記事にその方法を書いています。この方法はどうやら localhost に対しては使えないようなので、Mac には固定 IP アドレスを振り、// @include http://192.168.1.100:8080/*の様な形で対象を指定する必要があります。

Dify から接続する

OpenAI-API-compatible を使えるようにする

Dify のバージョン 1以上で使うには、まず OpenAI-API-compatible をプラグインからインストールします。

モデルを追加する

次に、右上の自分のアカウントアイコン > 設定 > モデルプロバイダーを開き、上で追加した OpenAI-API-compatible の「モデル追加」をクリックします。

Qwen/Qwen3-32B-MLX-4bit を追加するなら、こんな感じです。

  • Model Name: Qwen/Qwen3-32B-MLX-4bit
  • Model display name: (自分にわかりやすいように。例: MLX – Qwen/Qwen3-32B-MLX-4bit)
  • API Key: 不要
  • API endpoint URL: http://localhost:8585/v1 とか、別ホストなら http://192.168.1.100:8585/v1 とか
  • Completion mode: Chat
  • Model context size: 32768
  • Upper bound for max tokens: 32768
  • その他もろもろ: Not Support またはよしなに
  • Delimiter for streaming results: \n\n

チャットのタイトルを生成するモデルを選ぶ

また、Dify ではチャットタイトルを作るのはシステム推論モデル固定なため、小さめで thinking/reasoning ではない MLX-LM のモデルを設定しておきます。ここでは先ほどダウンロードしておいた Gemma 3 を上同様の要領で OpenAI-API-compatible モデルとして追加した後、指定しています (Model context size は 40960)。

あとはそれなりに

ここまでできたら、後は作ったアプリのモデルとして使用してみましょう。数字を鵜呑みにして良いのかわりませんが、いくつかチャットを行った後でアプリの「監視」メニューを見てみると、MLX-LM モデルのトークン出力速度が Ollama モデルより速いことが確認できます。

最後に

長々と書きましたが、使うほどに速さを実感しています。Ollama や LM Studio のようなユーザーフレンドリーさはありませんが、CLI での扱い方に慣れてしまえば MLX をサポートしない Ollama には戻れなくなると思います。ボクはディスク容量削減のため、Ollama からほとんどのモデルを削除してしまいました。

今回記事を書きながら Open WebUI をじっくり使ってみました。チャットだけなら十分ですね。タイトルの自動生成キャンセル技は速度を稼げるので地味に便利です。OpenAI API 接続だと tokens-per-sec が表示されないのは残念ですけど。RAG や MCP の利用もできるようなので、もっと使い込んでみようと思っています。

あとはやっぱり Qwen3 の性能の高さですよね。フロントエンド側でのサポートも進んでいて、QwQ だと丸見えになる思考が非表示になるのも地味にうれしいところです。政治的な話や中国にまつわる話を避ければおかしなところは感じないですし、最終的な回答に中国語が混ざる事も無く、当面はこれ一本でよさそうだと思っています。

Image by Stable Diffusion (Mochi Diffusion)

「リンゴTシャツを着てラマに乗る女性」いいんじゃないっすかコレ?もう一つステップを上げると破綻したので、これがベスト。

Date:
2025年6月29日 22:15:00

Model:
realisticVision-v51VAE_original_768x512_cn

Size:
768 x 512

Include in Image:
a lady with an apple t-shirt riding on a lama

Exclude from Image:

Seed:
391522385

Steps:
27

Guidance Scale:
20.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & GPU

Alibaba 公式 MLX 版 Qwen3 を他の量子化版と比較

Alibaba の Qwen チームが Mac 用に MLX 版の Qwen3 をリリースしたので Qwen/Qwen3-32B-MLX-4bit を使ってみました。他の記事でも書いているとおり Ollama では使えないので、MLX-LM をメインで使っています。また、MLX-LM、LM Studio、Ollama をバックエンドにしてそれぞれで使える Qwen3 の生成速度の違いも軽くテストしてみました。

海外の掲示板では、せっかく MLX 用に変換してくれたのに DWQ 量子化していないじゃないか、みたいなコメントも見ましたが、そのあたりの影響かな?と思えそうな結果になっています。

モデル情報元

公式 X:

https://twitter.com/Alibaba_Qwen/status/1934517774635991412

公式 Hugging Face:

https://huggingface.co/collections/Qwen/qwen3-67dd247413f0e2e4f653967f

MLX に限らず、Qwen チームが量子化した Qwen3 全てのバージョンやベースモデルがあります。

試した環境

  • Mac Studio M2 Max 32GB GPU (24,576 GB を VRAM に割り当て済み。やりかたはこちら)
  • macOS: Sequoia 15.5
  • Python 仮想環境: pipenv version 2025.0.3 (なぜ pipenv なのか、みたいな話はこちら)
  • Python: 3.12.11 (特に意味は無し)
  • MLX-LM: 0.25.2 (pip install mlx-lmでインストール)
  • Open WebUI: 0.6.15 (pip install open-webuiでインストール)
  • Ollama: 0.9.1 preview (新アプリのプレビュー版。詳しくはこちら)
  • LM Studio: 0.3.16 (build 8)
  • LLM: Qwen/Qwen3-32B-MLX-4bit (17.42 GB / 今回のメイン)
  • LLM: mlx-community/Qwen3-32B-4bit-DWQ (18.54 GB / 比較用)
  • LLM: qwen3:32b-q4_K_M (20 GB / Ollama のモデル、比較用)

インストール方法や各アプリケーションの使い方などはリンク先や他のウェブサイトを参照してください。少なくとも何らかの Python 仮想環境を作り、MLX-LM か LM Studio のインストールがしてあれば使えます。

モデルのダウンロード

色々方法を試した結果、MLX-LM で Hugging Face にアップされているモデルを使うのはこの方法がよさそうかと。MLX-LM 用にでも作った仮想環境に入り、Hugging Face 関連パッケージをインストールしてコマンドからインストールを行います。

モデルは自分の GPU (割り当て VRAM サイズ) に 100% 乗る、Qwen/Qwen3-32B-MLX-4bit にしていますので、お好みのものに変更してください。

pip install -U huggingface_hub hf_transfer
HF_HUB_ENABLE_HF_TRANSFER=1 huggingface-cli download Qwen/Qwen3-32B-MLX-4bit

ダウンロードが終わると、~/.cache/huggingface/hub/models--Qwen--Qwen3-32B-MLX-4bitに保存されます。mlx-community のモデルのようにmlx_lm.manage --scanでは表示されませんが、名前を指定すれば使えますので安心してください。

動作確認

MLX のチャット (CLI) でさっくり試せます。質問によっては思考 (<think>~</think>) だけでトークンを使い切ってしまうので、--max-tokens 8192等として上限を増やして実行したほうが良いでしょう。

mlx_lm.chat --model Qwen/Qwen3-32B-MLX-4bit --max-tokens 8192

実行結果のサンプル:

%  mlx_lm.chat --model Qwen/Qwen3-32B-MLX-4bit --max-tokens 8192
Fetching 10 files: 100%|███████████████████████████████████████████████████████████████████████████████████████████| 10/10 [00:00<00:00, 145131.63it/s]
[INFO] Starting chat session with Qwen/Qwen3-32B-MLX-4bit.
The command list:
- 'q' to exit
- 'r' to reset the chat
- 'h' to display these commands
>> こんにちは。自己紹介してください
<think>
The message is in Japanese. The person is greeting me and asking me to introduce myself.

I should respond in Japanese since the message was in Japanese. I'll provide a brief introduction about myself as an AI assistant.

The message says: "Hello. Please introduce yourself"

My response will be in Japanese:
</think>

こんにちは!私は通義千問(つうぎせんもん)で、英語ではQwenと呼ばれます。私はアリババグループ傘下の通義実験室が独自に開発した大規模言語モデルです。質問への回答や、物語、公文書、メール、脚本など文章の作成に加えて、論理的推論やプログラミング、さらにゲームにも対応できます。また、多言語をサポートしており、さまざまなタスクを効果的に支援できます。どうぞよろしくお願いいたします!
>> q

MLX-LM の CLI チャットは Ollama ほどイケてないので、動くのが確認できたらさっさと次に進みましょう。

API サーバを立てる

MLX-LM でサーバを実行するだけで API サーバとして使えます。セキュリティ的に本番環境向けでは無いという警告が出ますが、とりあえず使う分には良いでしょう。

ボクは LAN 内の別の Mac で動く Dify から接続するのと Open WebUI がポート 8080 を使っているということもあり、--host 0.0.0.0--port 8585を指定しています。--log-level DEBUGを付けると、トークンごと (?) の出力と、tokens-per-sec が表示されます。

またここで--model Qwen/Qwen3-32B-MLX-4bitとしてモデルを指定することもできますが、指定しなくてもクライアント側で指定したモデルがオンザフライで読み込まれるので気にしなくてよさそうです。

mlx_lm.server --host 0.0.0.0 --port 8585 --log-level DEBUG

サーバが立ち上がったかどうかは、ブラウザで利用可能なモデル一覧を表示させることで確認できます。

http://localhost:8585/v1/models

実行例:

{"object": "list", "data": [{"id": "lmstudio-community/DeepSeek-R1-0528-Qwen3-8B-MLX-4bit", "object": "model", "created": 1750501460}, {"id": "mlx-community/QwQ-32b-4bit-DWQ", "object": "model", "created": 1750501460}, {"id": "Qwen/Qwen3-32B-MLX-4bit", "object": "model", "created": 1750501460}, {"id": "mlx-community/Qwen3-32B-4bit-DWQ", "object": "model", "created": 1750501460}]}

mlx_lm.manage --scanでは表示されないQwenlmstudio-communityのモデルも見えますね。

Dify で作った超簡単チャットアプリで「こんにちは。自己紹介してください」と投げたときの token per sec (トークン数/秒) は以下となりました。悪くないですよね。個人的には 11 あればヨシと考えています。

2025-06-21 19:25:58,696 - DEBUG - Prompt: 39.744 tokens-per-sec
2025-06-21 19:25:58,697 - DEBUG - Generation: 17.347 tokens-per-sec
2025-06-21 19:25:58,697 - DEBUG - Peak memory: 17.575 GB

あとは Open WebUI なり Dify なりでモデルプロバイダとして登録し、使ってみるだけです (参考手順)。上記のコマンドでダウンロードしたモデルは、LM Studio でも使えるのでディスクスペースの有効活用になります。ただし、モデル名は読めません。下のスクリーンショットの一番上が Qwen/Qwen3-32B-MLX-4bit で、一番下は mlx-community/Qwen3-32B-4bit-DWQ です。なはは。

速度の違い (参考情報)

Dify を使って、いくつかの量子化バージョンと API サーバの組み合わせで Qwen3 32B を実行した結果が以下の表となります。

テスト内容としては、1ラウンドのプロンプトを 4回投げて「監視」画面でトークン出力速度の平均を見ました。

一つ失敗した点がありまして、設定した Size of context window: 25600 が Ollama には大きすぎたため GPU の使用率が 100% に行かず、10%/90% CPU/GPU という不利な結果となってしまいました (ollama psより)。よって、Ollama のみ最大トークン数を半分の 12800 に下げて 100% GPU で再テストしています。

実行中のメモリプレッシャーに関しては、MLX モデルは全て 8割ほどで推移していました。LM Studio はモデルのロード後常にメモリ上にあるため、メモリの占有量はほぼ変化しない代わりに推論の開始が早いという特徴があります。チャット内容のサマリが生成されないのは、Dify のシステム推論モデルに設定している Ollama のモデルが動くだけのメモリ容量がないからかもしれません。

モデルモデルサイズAPI サーバ平均トークン/秒特徴・メモリプレッシャ
Qwen/Qwen3-32B-MLX-4bit17.42 GBMLX-LM19.302サマリ生成の後半分程に下がる
Qwen/Qwen3-32B-MLX-4bit17.42 GBLM Studio23.19サマリが生成されない。メモリ使いっぱなし
mlx-community/Qwen3-32B-4bit-DWQ18.45 GBMLX-LM21.058サマリ生成の後半分程に下がる
mlx-community/Qwen3-32B-4bit-DWQ18.45 GBLM Studio24.503サマリが生成されない。メモリ使いっぱなし
qwen3:32b-q4_K_M20 GBOllama (max. 25600 tokens)9.511サマリ生成後はミニマム
qwen3:32b-q4_K_M20 GBOllama (max. 12600 tokens)12.146サマリ生成後はミニマム

評価に使ったプロンプトは以下の 4つとなります。全て一往復で終わらせています。メモリプレッシャーが下がって安定し、GPU の使用量がゼロになったのを確認してから新しいチャットで次のプロンプトを実行しています。

こんにちは。自己紹介してください
ボードゲーム「オセロ」のルールを正確に教えてください
微分積分を再度勉強しようと思います。数式を交えてさわりの部分を教えてください
あなたはマーケティングのプロフェッショナルです。
日本では一部の自動販売機では、夏でもホットの缶コーヒーが売られています。それは、夏場のタクシー運転手は暑い中タクシーを利用する乗客のために社内の温度を低くしており、冷えた体を温めるために缶コーヒーを求めるからです。
同じような視線からでも別の視線からでも構いませんが、現在は冬場しか売られていないコンビニレジ横のおでんを通年で販売するためにはどのような方策があるか、提案してください

System prompt と LLM の設定は以下の内容で行いました (最近の Qwen の LLM は優れた多言語対応が進んでいますが、念のため)。

  • System prompt:
日本語で質問されたら日本語で回答してください。
If asked in English, answer in English.
Never user Chinese
  • Temperature: 0.1
  • Max Tokens: 25600 (Ollama は Size of context window)
  • Thinking mode: True (Ollama には該当項目無し)
  • それ以外はデフォルト (未設定)

テストの結論

メモリに余裕があるなら、LM Studio + mlx-community/Qwen3-32B-4bit-DWQ の生成速度が最強ですね。トークン/秒の数字を鵜呑みにすれば、Ollama + Qwen3-32B-4Q_K_L の倍の速度が出ています。ただ、回答の中で「おでん」を「おでn」とか「オーデン」と書いていたので、この組み合わせだと何かが欠落するような要素があるのかもしれません。「オーデン」として夏に売り出すというのはアリかもしれませんけど。いや、どうか。

回答内容や日本語に安心感があったのは Qwen/Qwen3-32B-MLX-4bit でした。個人的には MLX-LM との組み合わせが使いやすいと感じています。

Qwen に限らず、今後 LLM の開発元が MLX 化と量子化までを行ってくれると搭載メモリの小さい Mac でも効果的に使える様になるはずなので、そんな未来に期待したいですね。

ただ今のところ MLX-LM の量子化については公式以外にあまり情報が無いのがつらいところです。

Image by Stable Diffusion (Mochi Diffusion)

「3人兄弟の徒競走」をイメージして描いてもらいましたが、やはり数字の指定には弱く、4人登場する画像が多かったです。顔の描写も人数が増えるほど破綻し、ステップ数を増やしても良くなるわけではないので後ろ向きのを採用しました。兄弟っぽいし、差も付いてるし。

Date:
2025年6月22日 1:01:01

Model:
realisticVision-v51VAE_original_768x512_cn

Size:
768 x 512

Include in Image:
footrace of three brothers on a track

Exclude from Image:

Seed:
309909096

Steps:
20

Guidance Scale:
20.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & GPU

New Ollama for macOS preview v0.9.1 が出たが MLX がサポートされたわけではない

なーんだ、そうなのか。という感じですよね。以前の記事で触れた X への投稿みたいな、「そろそろ MLX 対応するぜ」的匂わせなのか、全く関係ないのか。とりあえずいじってみたのでまとめました。

情報元は v0.9.1 のリリース

オフィシャルの GitHub リポジトリの該当リリースがこちらです:

https://github.com/ollama/ollama/releases/tag/v0.9.1

New Ollama for macOS and Windows preview

という見出しと共に、Download for macOS というリンクと、追加された設定機能のスクリーンショットが見つけられます。

2025年6月22日追記: すでに Version 0.9.2 の preview も出ていますね。ダウンロードは Release ページの Asset にある Ollama-Preview.dmg をクリック:

https://github.com/ollama/ollama/releases

macOS 向け Preview バージョンのハイライト

Preview の特徴をざっくりまとめるこういうことのようです (というか、現状これくらいの情報しかみあたらない):

  • Settings で、LAN やインターネットへ (簡単に) 公開する事ができるようになった (環境変数OLLAMA_HOSTと同じ?)
  • Settings で、ローカルのブラウザからのアクセスを有効にできるようになった (Ollama JavaScript Libraryを使う人には便利らしい? Open WebUI みたいなフロントエンドかと思ったらそうではなかった)
  • Settings で、モデルの保存場所を (簡単に) 変更できるようになった (環境変数OLLAMA_MODELSと同じ?)
  • macOS ネイティブアプリとなり、インストールに必要なサイズがかなり小さくなり、起動も速くなった (え?今まではネイティブアプリじゃ無かったの?)
  • Preview をアップデートすると通常の最新バージョンになってしまう (Restart to update すると、それはもう Preview では無くなってしまう、ということなので注意)

Settings ウィンドウはメニューバーのアイコンから開く事ができ、設定の保存は [ Update Settings] ボタンです。

Restart to update をすると通常の最新バージョンが入ってしまう罠

使ってみてどうか

どうなんですかね?Qwen3:32b と QwQ:32b を使ったチャットするだけアプリを Dify で作って速度を見てみましたが、LLM の動作に何か良い影響があった感じではありませんでした。ま、この部分は MLX 対応されてからのお楽しみでしょうね。最近動きが見えないですけど。

Settings でいじれる内容についても、個人的にはメリット無いです。下の別記事に書いていますが、ボクは macOS のログイン時に Ollama サーバが LAN に公開されるようにしているので起動も速くなったかどうかわかりません。外付けの SSD にモデルの保存先を変更するなら起動スクリプトにOLLAMA_MODELSを追加すれば良いし、今回の Preview バージョンによる恩恵は見つかっていません。追加情報や Preview のアップデートに期待、というところです。

というわけで、他に掘り下げる情報も見つからないので、今回はここまで。

Image by Stable Diffusion (Mochi Diffusion)

単純に「実験室のラマの赤ちゃん」をいくつか描いてもらいました。ビーカーをのぞき込む感じがプレビュー/レビューと重なったので、こちらを採用

Date:
2025年6月20日 19:41:28

Model:
realisticVision-v51VAE_original_768x512_cn

Size:
768 x 512

Include in Image:
a baby lama in a scientific lab

Exclude from Image:

Seed:
3804362856

Steps:
21

Guidance Scale:
20.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & GPU

Python の pipenv 環境で専用の alias や export を読み込む

本記事の内容をより正確に書くと「zsh (bash) が読み込まれたときに、カレントディレクトリにあるaliasexportの書かれた設定ファイルを自動で読み込ませる」方法です。Python の仮想環境pipenvは新しいシェルを読み込むので、結果としてalias等を自動的に設定することができます。pipenv環境から抜けると仮想環境内の設定が無効になるため、素や他の環境に影響を与えません。簡単に実現できますが、pipenvを使っている人が少ないのかズバリの情報が見つからなかったのでまとめました。

環境

シェル:zsh (bashでもできるらしいですが未確認です)

Python 仮想環境:pipenv

手順

macOS でpipenvを使うには、まずbrew install pipenvでインストールします。簡単に仮想環境を作る手順はこんな感じです:

mkdir my_project # プロジェクトディレクトリを作る
cd my_project # プロジェクトディレクトリに入る
pipenv --python 3.11 # Python 3.11 が入った仮想環境を作る
pipenv shell # 仮想環境に入る。出るときは exit または ctrl + D

~/.zshrc に一文追加

自分のホームディレクトリにある.zshrcに以下を追加します。コメント文は無くて構いません。

# カレントディレクトリに .zshrc.local ファイルが存在する場合は読み込む
[[ -f .zshrc.local ]] && source .zshrc.local

内容としては、&&の左の部分が条件式で、カレントディレクトリに.zshrc.localファイルが存在しているか調べています。真であれば右のsourceコマンドが実行され.zshrc.localファイルを読み込みます (試してませんが、書式としてはbashでも同じ方法でイケるらしいです)。(2025/07/02 訂正) Bash は上の書式が使えないので、以下の様にしてください。仮想環境内のファイルは.bashrc.localとしています。

if [ -f .bashrc.local ]; then
    source .bashrc.local
fi

pipenv のルートディレクトリに .zshrc.local ファイルを書く

その仮想環境内でのみ有効にしたいaliasexport、その他.zshrcに書けることはもちろん何でも書けます。とりあえず簡単なサンプルは以下の通りです:

alias t='time'
export HW="Hello, World!"

仮想環境に入り、試す

以下、実行例です:

$ pipenv shell # 仮想環境に入る
$ t # alias で登録した time コマンド
(time コマンドの実行結果が表示される)

$ echo $HW
Hello, World!
(export で登録した文字列が表示される)

仮想環境から出て、試す

以下、実行例です:

$ exit # または ctrl + D で仮想環境を抜ける
$ t
zsh: command not found: t
(t というコマンドはない)

$echo $HW

(空行が表示される)

注意点

GitHub 等に公開するプロジェクトでは.gitignore.zshrc.localを忘れずに追加しましょう。

venv でもほぼ同様のことをする

Python の標準的仮想環境ツールvenvでは新たにシェルを読み込まれません。よって、別の方法で同様の事を実現します。

bin/activateの最終行に以下を追加します。~/.zshrcに書いたものと同じです。

# カレントディレクトリに .zshrc.local ファイルが存在する場合は読み込む
[[ -f .zshrc.local ]] && source .zshrc.local

pipenvのやり方よりひと手間増えますが、これで一応同じ様な事ができます。

venv での違い、注意点

上記の方法では、シェルの再読み込みはせずに.zshrc.localを読み込んでいるので、deactivatevenv環境を抜けた後もエイリアスや環境変数が有効になっています。同一のターミナルで仮想環境を抜けた後も別の作業を続けることがよくあるという方は普段の環境変数などが上書きされている可能性があるので注意が必要です (ターミナルを閉じるのが手っ取り早い)。

なぜこんなことが必要だったのか

ボクは最近、mlx-lm.serverでサーバを立てて MLX 版 LLM を使うのですが、Ollama と違ってメモリが解放されない (メモリプレッシャーが高止まり状態になる) ことがちょいちょいあります。仕方が無いのでつど ctrl + C で止めて再度コマンドからサーバを立てるのですが、他のターミナルでコマンドを叩いていたりするとカーソルキーの上ですぐに呼び出せずストレスを感じていました。そこで、pipenvの環境内でのみ有効なaliasを作れないかと思った次第です。

ネット上では想像したほど簡単にその方法が見つからず、ローカルの QwQ や Qwen3、ChatGPT にも相談しながら最終的には自分で解決方法にたどり着きました。それぞれの LLM に評価をお願いしたところ「すばらしい!」と褒めてくれたので、うれしくてブログにまとめました。わはは!

Image by Stable Diffusion (Mochi Diffusion)

この記事にどのような画像が合うのかイメージか浮かばず、とりあえずいろんな自転車のあるショールームを描いてもらいました。依頼内容も画像もこれが正解なのかいまだにわかっていませんけど。

Date:
2025年6月14日 19:47:15

Model:
realisticVision-v51VAE_original_768x512_cn

Size:
768 x 512

Include in Image:
showroom with different types of bycicles

Exclude from Image:

Seed:
1251791658

Steps:
20

Guidance Scale:
20.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & GPU

© Peddals.com