Skip to content
Go back

llmをMacBook Air M2上で無料で動かす (mlxとggufを比較)

Published:  at  12:00 AM

Run LLMs on macOS using llm-mlx and Apple’s MLX frameworkという記事で、mlxを使って、macOS上でllmを動かす方法が紹介されている。

mlxとはAppleが開発するApple Siliconのための機械学習フレームワーク。mlx-lmというllmをmlxで動作させるためのPythonライブラリも提供されている。

しばらく自身のMacBook Air上でllmを動かしていなかったので、自分のMacBookでmlxを利用したllmの実行を試してみることにした。

PCのスペック

MacBook Air M2 (2022)

LM Studioを使ってローカルでllmを実行

今回はLM Studioというオープンソースのデスクトップアプリを使ってllmを動かしてみる。

LM Studioは従来のllmのファイル形式であるggufだけでなく、mlxのファイル形式もサポートしている。(v0.3.4以降でmlxがサポートされた)

LM StudioのUIから、HuggingFaceが提供するmlx/ggufのモデルを検索し、ダウンロードして使うことができる。

lm-studio-download-models

Mistral 7Bをmlx/ggufで比較

16GBメモリのMacBook Airのため、巨大なモデルは動かすことはできない。今回はMistral 7B Instruct v0.3の4GB程度のモデルファイルでmlx/ggufそれぞれの形式を比較してみる。

入力プロンプトは 「ツリーを巡回するPythonコードを簡潔に説明して」

TL;DR

先に結論だが、mlxとggufとで大差はなさそうだった。

あくまで今回の比較は、4GB程度のモデルでの比較であるため、より巨大なモデルでの比較を行うと、性能差がより顕著に出ると思われる。

次の表により詳細な結果をまとめた:

mlxgguf
Model NameMistral 7B Instruct v0.3 4bitMistral 7B Instruct v0.3 Q4_K_M
File Size4.08 GB4.37 GB
Memory Usage4.28 GB4.71 GB
Token Speed20.81 tok/sec17.60 tok/sec
Time to First Token0.63s0.52s
Output Tokens531639

実際の回答:

mlxの回答

Pythonでツリー(Tree)を輪遡させる(Recursively traverse)シンプルなコードは次のようになります:

def traverse(node, func):
   if node:
       # 現在のノードを処理
       func(node)

       # 子ノード(左側と右側)について、同じ処理を繰り返してください
       traverse(node.left, func)
       traverse(node.right, func)

# 木構造で表現されたデータを持ち、leftとrightに子ノードがあるnodeオブジェクト
root = MyNode(1) # 例: Root Node
root.left = MyNode(2)
root.right = MyNode(3)
root.left.left = MyNode(4)
root.left.right = MyNode(5)

# 木を輪遡させる処理
traverse(root, print) # 上記の構造に対して、各ノードを出力

上記のコードでは、traverse() 関数に木構造を持ち、func を呼び出すようになっています。この func 関数は処理を行うたびに、渡されるノードオブジェクトに対して何らかの処理を行います。一度呼び出されるたびに、現在のノードを処理してから子ノード(左側と右側)について、再帰的にこの処理を繰り返します。

上記の例では、print() 関数を func として与えています。これにより、rootノードから、子ノードを辿ってrootノードから根拠した木構造の全てのノードが出力されます。

ggufの回答

以下は、Pythonで木(Tree)を深さ優先探索(Depth-First Search, DFS)または幅優先探索(Breadth-First Search, BFS)する方法の簡単な例です。

これらの例は、Python標準ライブラリに含まれるcollections.dequeを使用して、ノードを追跡したり、前へ戻れたりすることが可能です。

# 深さ優先探索 (Depth-First Search, DFS)
class Node:
   def __init__(self, val):
       self.val = val
       self.children = []

def dfs(node):
   print(node.val)
   for child in node.children:
       dfs(child)

root = Node("root")
root.children.append(Node("A"))
root.children.append(Node("B"))
root.children[0].children.append(Node("D"))
root.children[1].children.append(Node("E"))
root.children[1].children.append(Node("F"))
root.children[0].children.append(Node("C"))
root.children[2].children.append(Node("G"))

dfs(root)  # root, A, D, C, B, E, F, G の順に出力されます。

# 幅優先探索 (Breadth-First Search, BFS)
from collections import deque

def bfs(node):
   q = deque()
   q.append(node)
   while q:
       current_node = q.popleft()
       print(current_node.val)
       for child in current_node.children:
           q.append(child)

root_bfs = Node("root")
# ノードの設定は同じですが、DFSと異なり次第に子ノードを追加します。
root_bfs.children.append(Node("A"))
root_bfs.children.append(Node("B"))
root_bfs.children[0].children.append(Node("D"))
root_bfs.children[1].children.append(Node("E"))
root_bfs.children[1].children.append(Node("F"))
root_bfs.children[0].children.append(Node("C"))
root_bfs.children.append(Node("G"))

bfs(root_bfs)  # root, A, B, C, D, E, F, G の順に出力されます。

おまけ: CLIで実行する

冒頭の記事で紹介されているが、CLIツールLLMでもmlxを使ってllmを動かすことができる。

まずuvを使ってLLMをインストールする:

uv tool install llm --python 3.12

ここで--python 3.12は、内部で利用されているsentencepieceのwheelがpython3.12までしか提供されていないため指定している。

プラグインであるllm-mlxをインストールする:

llm install llm-mlx

mlxモデルファイルは、CLIからダウンロードすることもできるが、せっかくなのでLM Studioでダウンロードしたファイルを使いまわしたい。

~/.lmstudio/models/mlx-communityにダウンロードしたmlxモデルが保存されている:

 ls -l ~/.lmstudio/models/mlx-community
total 0
drwxr-xr-x@ 9 kh03  staff  288 Feb 18 21:31 Mistral-7B-Instruct-v0.3-4bit

このモデルファイルをLLMで使うためには、モデル定義を記述するjsonファイルを編集する必要がある。 以下でjsonを開ける:

 open "$(llm mlx models-file)"

jsonに以下のキーを追加する:

{
  "~/.lmstudio/models/mlx-community/Mistral-7B-Instruct-v0.3-4bit": {
    "aliases": ["lmstudio"]
  }
}

キー自体は、LM Studioでダウンロードしたモデルのパスを指定し、aliasesにはCLIで利用するモデルの名前を自由に指定する。 (ここのモデル登録は少々面倒なので、コマンドを追加できないかIssue作成した)

あとはaliasを指定して、LLMを実行する:

 llm -m lmstudio-mistral hi

まとめ

より大きなモデルをローカルで利用したい場合はMacBook Proの上位モデルが適しているかもしれないが、今回のような小規模なモデルであれば、MacBook Air M2でも十分動かすことができた。

何よりもローカルで動かせば無料だし、外部のサーバーに依存しないので、セキュリティ的にも安心である。色々と試すにはかなりお勧めできる。

またCLIを利用すれば、ほかのプログラムとPipeを通じて組み合わせることもできる。



Previous Post
Python並行処理: yt-dlpでの動画ダウンロードを高速化する
Next Post
プラグインで拡張可能なソフトウェアを設計・実装する方法 by Simon Willison