マルチモーダルモデルのLLaVAをApple Silicon (M1, M2, M3) Mac で 動かす方法4つ
はじめに
大規模言語モデルの llama を画像も入力できるようにした LLaVA を M1 Mac で動かしてみました。一部動いていないですが。。。
いろんな方法があるので整理してみます。
Llava について詳しく知りたい方は下記サイトを見てみるのが良いと思います。
下記サイトで実際に試せるので、ローカルで動かしたくなるケースは少ないかもですが。。。
また、Replicate という Hugging Face に似たサービスで、制限つきですが無償で API が公開されているので、サクッと API で呼び出したい方はこちら。
Python とか Node.js の SDK もあってサクッと呼び出せます。
LLaVA
まずは、github 上にあるこれを動かそうとしてみました。でも、動かない・・・。動いた人いれば情報ください。
やったのはここらです。
pyenv install 3.10
pyenv local
python -m venv .venv
source .venv/bin/activate
python -mpip install --upgrade pip # enable PEP 660 support
pip install -e .
pip install torch==2.1.0 torchvision==0.16.0
pip uninstall bitsandbytes
python -m llava.serve.controller --host 0.0.0.0 --port 10000
python -m llava.serve.gradio_web_server --controller http://localhost:10000 --model-list-mode reload
python -m llava.serve.model_worker --host 0.0.0.0 --controller http://localhost:10000 --port 40000 --worker http://localhost:40000 --model-path liuhaotian/llava-v1.5-13b --load-4bit --device mps
こんなエラーが出ました。
% python -m llava.serve.model_worker --host 0.0.0.0 --controller http://localhost:10000 --port 40000 --worker http://localhost:40000 --model-path liuhaotian/llava-v1.5-13b --load-4bit --device mps
2023-11-29 22:28:40 | INFO | model_worker | args: Namespace(host='0.0.0.0', port=40000, worker_address='http://localhost:40000', controller_address='http://localhost:10000', model_path='liuhaotian/llava-v1.5-13b', model_base=None, model_name=None, device='mps', multi_modal=False, limit_model_concurrency=5, stream_interval=1, no_register=False, load_8bit=False, load_4bit=True)
2023-11-29 22:28:40 | INFO | model_worker | Loading the model llava-v1.5-13b on worker d9bfa3 ...
2023-11-29 22:28:40 | ERROR | stderr | Traceback (most recent call last):
2023-11-29 22:28:40 | ERROR | stderr | File "/Users/ito/.pyenv/versions/3.10.13/lib/python3.10/runpy.py", line 196, in _run_module_as_main
PyTorch が Mac に対応してたりするので、動く気はしてるけど、よくわかんないです。。。
諦めて、次。
参考
llama-cpp
llama を C++で動かすやつ。
MacBook で動かすのメインゴールらしいです。
The main goal of llama.cpp is to run the LLaMA model using 4-bit integer quantization on a MacBook
Makefile
に色々書かれてるので、make
するだけでそれっぽく動きます。すごい。Apple Silicon 版 CUDA の Metal も使うようにしてくれていて、GPU を使って動いてくれてます。すごい。
make
動かす前に、モデルのデータを探してくる必要があります。
トラップがあって、.bin
のモデル (GGML) だと動かなくて、.gguf
(GGUF)のモデルを使う必要がある。2023-08 あたりに、破壊的変更があって、そうなったみたい。
検索して出てくる記事が、古い記事が多くて要注意。
GGUF のモデルは Hugging Face 上に色々あります。
TheBloke, mmnga ってアカウントの方々が色々公開してくれてるので、それを使ったら良さそうです。自分で、コンバートする方法もあるみたいですが、試してないです。巨人の肩に立ったらええやん?
たとえば、上記サイトからELYZA-japanese-Llama-2-7b-fast-instruct-q4_K_M.gguf
をダウンロードしてきて下記のようなコマンドで試せます。
./main -m ./models/ELYZA-japanese-Llama-2-7b-fast-instruct-q4_K_M.gguf --temp 0.1 -p "[INST]プログラムを勉強する際、どの言語がいいでしょうか。簡潔に回答してください。日本語でお願いします。[/INST]"
肝心の llava は、まず下記サイトから、ggml-model-q4_k.gguf
とmmproj-model-f16.gguf
をダウンロードします。
その後、models
フォルダにおいて、下記コマンドを実行します。tokyo.jpeg は適当に拾った画像を同じフォルダにおいて試しました。
./llava-cli -m models/ggml-model-q4_k.gguf --mmproj models/mmproj-model-f16.gguf --image tokyo.jpeg -p 'これは何?日本語で答えて'
いい感じに動いて感動。先人たちに感謝。
参考
llama-cpp-python
次は、llama.cpp を python 向けにラップしたライブラリ。こちらもすんなり動きました。素晴らしい。
準備はこれくらい。
pip install llama-cpp-python
まずは、llama を動かしてみます。モデルを、llama-cpp の時と同じように、事前に Hugging Face からダウンロードして使います。
from llama_cpp import Llama
llm = Llama(model_path="../llama.cpp/models/llama-2-7b-chat.Q4_K_M.gguf")
output = llm(
"Q: Name the planets in the solar system? A: ", # Prompt
max_tokens=32, # Generate up to 32 tokens
stop=["Q:", "\n"], # Stop generating just before the model would generate a new question
echo=True # Echo the prompt back in the output
)
print(output)
実行はこんな感じ。
python main.cpp
llava はこんな感じ。URL 指定するパターンと、ローカル画像を使うパターン。
from llama_cpp import Llama
from llama_cpp.llama_chat_format import Llava15ChatHandler
### Test
clip_model_path = "../llama.cpp/models/mmproj-model-f16.gguf"
model_path = "../llama.cpp/models/ggml-model-q4_k.gguf"
chat_handler = Llava15ChatHandler(clip_model_path=clip_model_path)
llm = Llama(
model_path = model_path,
chat_format = "llava-1-5",
chat_handler = chat_handler,
n_ctx = 2048, # n_ctx should be increased to accomodate the image embedding
n_gpu_layers = 1,
logits_all = True,
verbose = False
)
output = llm.create_chat_completion(
messages=[
{"role": "system", "content": "あなたは完璧に画像を日本語で説明するアシスタントです"},
{
"role": "user",
"content": [
{"type": "image_url", "image_url": {"url": "https://zounokuni.com/wp/wp-content/themes/animal/images/animals/animals_list_15@2x.png"}},
{"type": "text", "text": "この画像を日本語で詳細に説明してください。"}
]
}
]
)
print(output)
from llama_cpp import Llama
from llama_cpp.llama_chat_format import Llava15ChatHandler
import base64
def image_to_base64_data_uri(file_path):
with open(file_path, "rb") as img_file:
base64_data = base64.b64encode(img_file.read()).decode('utf-8')
return f"data:image/png;base64,{base64_data}"
file_path = '../llama.cpp/tokyo.jpeg'
data_uri = image_to_base64_data_uri(file_path)
### Test
clip_model_path = "../llama.cpp/models/mmproj-model-f16.gguf"
model_path = "../llama.cpp/models/ggml-model-q4_k.gguf"
chat_handler = Llava15ChatHandler(clip_model_path=clip_model_path)
llm = Llama(
model_path = model_path,
chat_format = "llava-1-5",
chat_handler = chat_handler,
n_ctx = 2048, # n_ctx should be increased to accomodate the image embedding
n_gpu_layers = 1,
logits_all = True,
verbose = False
)
output = llm.create_chat_completion(
messages=[
{"role": "system", "content": "あなたは完璧に画像を日本語で説明するアシスタントです"},
{
"role": "user",
"content": [
{"type": "image_url", "image_url": {"url": data_uri}},
{"type": "text", "text": "この画像を日本語で詳細に説明してください。"}
]
}
]
)
print(output)
どっちもいい感じです。
Ollama
まだ、llava 使えないけど、イシューも PR も出てるのでそのうち使えそう。素晴らしい。
おわりに
ざっと、試したり調べたことを整理してみました。
MacBook で動かすなら、llama.cpp と llama-cpp-python でいいかという気持ちになってます。Apple Silicon の GPU サポートがいつの間にか充実してるんだなぁと思いました。
Discussion