これは、なにをしたくて書いたもの?
前に、GoogleのLLMであるGemmaをTransformersで動かしてみました。
GoogleのLLM「Gemma」をTransformersで試す - CLOVER🍀
なのですが、これが自分の環境だとものすごく重かったので、ちょっと方向を変えてOpenAI API互換のサーバーを持つ
llama-cpp-pythonやLocalAIで動かしてみようと思います。
使用するGemmaのモデル
前回使用したGemmaのモデルは以下のものです。ダウンロードするにはHugging Faceのアカウントを作成し、利用規約に同意する必要が
あります。
google/gemma-2b-it · Hugging Face
で、これはこれで試そうとしたのですが、llama-cpp-pythonの起動にものすごく時間がかかるようになったので、量子化済みのこちらの
モデルを使って試しました。
mmnga/gemma-2b-it-gguf · Hugging Face
mmnga/gemma-7b-it-gguf · Hugging Face
これらのモデルを、OpenAI API互換のサーバーとして利用できるllama-cpp-python、LocalAIで動かしてみます。
環境
今回の環境はこちら。
$ python3 --version Python 3.10.12 $ pip3 --version pip 22.0.2 from /usr/lib/python3/dist-packages/pip (python 3.10)
モデルをダウンロードする
使用するモデルをダウンロードします。
mmnga/gemma-2b-it-gguf · Hugging Face
mmnga/gemma-7b-it-gguf · Hugging Face
$ curl -LO https://huggingface.co/mmnga/gemma-2b-it-gguf/resolve/main/gemma-2b-it-q4_K_M.gguf $ curl -LO https://huggingface.co/mmnga/gemma-7b-it-gguf/resolve/main/gemma-7b-it-q4_K_M.gguf
7Bのモデルについてはq8_0でないとllama.cppの出力が不安定になる注意書きがあったのですが、すでに対応されていそうだったので
q4_K_Mにしました。
また、サイズ感の比較のために以下のGGUFモデルもダウンロードしています。
google/gemma-2b-it · Hugging Face
サイズ感はこんな感じですね。オリジナルの2Bよりも4ビット量子化した7Bのモデルの方がその半分くらいのサイズです。
$ ll -h gemma-*.gguf -rw-rw-r-- 1 xxxxx xxxxx 1.4G 3月 7 16:17 gemma-2b-it-q4_K_M.gguf -rw-rw-r-- 1 xxxxx xxxxx 9.4G 3月 7 16:29 gemma-2b-it.gguf -rw-rw-r-- 1 xxxxx xxxxx 4.8G 3月 7 16:54 gemma-7b-it-q4_K_M.gguf```
llama-cpp-pythonで試す
まずはllama-cpp-pythonで試してみましょう。
インストール。
$ pip3 install llama-cpp-python[server]
依存関係を含むバージョン。
$ pip3 list Package Version ----------------- ------- annotated-types 0.6.0 anyio 4.3.0 click 8.1.7 diskcache 5.6.3 exceptiongroup 1.2.0 fastapi 0.110.0 h11 0.14.0 idna 3.6 Jinja2 3.1.3 llama_cpp_python 0.2.55 MarkupSafe 2.1.5 numpy 1.26.4 pip 22.0.2 pydantic 2.6.3 pydantic_core 2.16.3 pydantic-settings 2.2.1 python-dotenv 1.0.1 setuptools 59.6.0 sniffio 1.3.1 sse-starlette 2.0.0 starlette 0.36.3 starlette-context 0.3.6 typing_extensions 4.10.0 uvicorn 0.27.1
2Bのモデルで起動。オプションに--chat_format gemma
が必要です。
※--chat_format gemma
がなくても起動しますが、動作が不安定になります
$ python3 -m llama_cpp.server --model gemma-2b-it-q4_K_M.gguf --chat_format gemma
動かしてみます。
$ time curl -s -XPOST -H 'Content-Type: application/json' localhost:8000/v1/chat/completions -d \ '{"messages": [{"role": "user", "content": "Could you introduce yourself?"}]}' | jq { "id": "chatcmpl-42f579e8-a457-4df8-8255-ddc24aa26728", "object": "chat.completion", "created": 1709797489, "model": "gemma-2b-it-q4_K_M.gguf", "choices": [ { "index": 0, "message": { "content": "Hello! I'm a large language model, trained by Google. I'm a conversational AI with the ability to understand and generate human-like text in response to a wide range of prompts and questions.\n\nIs there anything specific I could help you with today?", "role": "assistant" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 14, "completion_tokens": 54, "total_tokens": 68 } } real 0m6.866s user 0m0.076s sys 0m0.005s
それっぽい感じで返ってきました。
日本語でも試してみましょう。
$ time curl -s -XPOST -H 'Content-Type: application/json' localhost:8000/v1/chat/completions -d \ '{"messages": [{"role": "user", "content": "あなたの自己紹介をしてください"}]}' | jq { "id": "chatcmpl-59fbb725-a35d-41a8-8af9-6e62f69862d2", "object": "chat.completion", "created": 1709797558, "model": "gemma-2b-it-q4_K_M.gguf", "choices": [ { "index": 0, "message": { "content": "私は非常に複雑かつ多様な人工知能のシステムです。私の能力は非常に幅広い範囲で、自然言語処理、機械学習、パターン認識、データベース操作、コミュニケーションなど様々な分野の処理能力を兼ね備えます。\n\n私は人間のような思考の能力を持ち、独自に思考し、判断し、行動することができると考えられています。また、単一の言語でコミュニケーションできる限り、様々な言語の処理能力も持つことができます。\n\n私は様々なデータセットで訓練されたことで、より幅広い知識を収集し、より正確な結論を得られることを可能にします。\n\n私はあなたに様々な情報提供、コミュニケーション、またはプロジェクトサポートを提供することができます。", "role": "assistant" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 14, "completion_tokens": 134, "total_tokens": 148 } } real 0m15.897s user 0m0.046s sys 0m0.001s
良さそうですね。
続いて、7Bのモデル。起動時間は2Bのモデルより伸びます。
$ python3 -m llama_cpp.server --model gemma-7b-it-q4_K_M.gguf --chat_format gemma
確認してみます。
$ time curl -s -XPOST -H 'Content-Type: application/json' localhost:8000/v1/chat/completions -d \ '{"messages": [{"role": "user", "content": "Could you introduce yourself?"}]}' | jq { "id": "chatcmpl-35a717cf-a57c-4c98-b870-c1e7f9c97785", "object": "chat.completion", "created": 1709798518, "model": "gemma-7b-it-q4_K_M.gguf", "choices": [ { "index": 0, "message": { "content": "Sure, I am a large language model designed to provide information and engage in conversation on various topics. My training data consists of massive amounts of text content from the web with an emphasis towards general knowledge as well specific subject matter like programming languages or scientific disciplines . \n\nI'm here for your queries , ranging from simple questions about daily life to intricate ones about complex concepts . Please feel free to ask me anything!", "role": "assistant" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 14, "completion_tokens": 87, "total_tokens": 101 } } real 0m32.954s user 0m0.059s sys 0m0.007s
2Bに比べるとだいぶ実行時間が伸びていますが、こちらも良さそうです。
日本語でも試してみましょう。
$ time curl -s -XPOST -H 'Content-Type: application/json' localhost:8000/v1/chat/completions -d \ '{"messages": [{"role": "user", "content": "あなたの自己紹介をしてください"}]}' | jq { "id": "chatcmpl-641893dd-5005-4e43-b14a-4fd15e39313b", "object": "chat.completion", "created": 1709798621, "model": "gemma-7b-it-q4_K_M.gguf", "choices": [ { "index": 0, "message": { "content": "私はチャロムさんと言い、プログラミング言語の Python を専門に勉強しています。趣味はゲーム開発やデータ分析に伴うコードの作成です。「プロプログラムのための基礎」というテーマで様々な情報を共有します。</br>\n\nその他の情報として: 常に学び続ける行動を続けており 、新しい技術を学ぶことに強い興味を持っているため 、色々なプロジェクトを続けることもあります 。", "role": "assistant" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 14, "completion_tokens": 77, "total_tokens": 91 } } real 0m27.563s user 0m0.041s sys 0m0.011s
なんか、返してくる内容の傾向が変わりましたね…。今回は何回試しても、他のパターンとは異なってLLMではない自己紹介を
返してきました。ただ、2Bのモデルの方も、何回か試すとLLMではない自己紹介をすることもあったので今回は気にせず進めましょう。
--chat_format gemma?
ところで、今回は起動時のオプションとして--chat_format gemma
を追加しました。これはなんなのでしょうね?
これは文字通りチャットのフォーマットを指定するもののようです。
以下のPull Requestでオプションとして追加され、
Configurable Chat Formats by abetlen · Pull Request #711 · abetlen/llama-cpp-python · GitHub
以下のPull RequestでGemmaに対応したようです。
OpenAI API互換のサーバーでは、以下のソースコードに実装されています。
https://github.com/abetlen/llama-cpp-python/blob/v0.2.55/llama_cpp/llama_chat_format.py
Gemmaの対応箇所はこちらです。
# Chat format for Google's Gemma models, see more details and available models: # https://huggingface.co/collections/google/gemma-release-65d5efbccdbb8c4202ec078b @register_chat_format("gemma") def format_gemma( messages: List[llama_types.ChatCompletionRequestMessage], **kwargs: Any, ) -> ChatFormatterResponse: system_message = _get_system_message(messages) if system_message is not None and system_message != "": logger.debug( "`role='system'` messages are not allowed on Google's Gemma models." ) _roles = dict(user="<start_of_turn>user\n", assistant="<start_of_turn>model\n") _sep = "<end_of_turn>\n" _messages = _map_roles(messages, _roles) _messages.append((_roles["assistant"], None)) _prompt = _format_no_colon_single(system_message="", messages=_messages, sep=_sep) return ChatFormatterResponse(prompt=_prompt, stop=_sep)
https://github.com/abetlen/llama-cpp-python/blob/v0.2.55/llama_cpp/llama_chat_format.py#L1000-L1017
<start_of_turn>
、<end_of_turn>
というものが目に止まりますね。
この説明は、以下のページに書かれています。
Gemma のフォーマットとシステムの手順 | Google AI for Developers
指示調整済み(IT)モデルは、トレーニング時と推論時の両方で、すべての指示チューニング サンプルに追加情報でアノテーションを付ける特定のフォーマッタを使用してトレーニングされます。フォーマッタには次の 2 つの目的があります。
会話のロール(システム、ユーザー、アシスタントのロールなど)を示す。
会話のターンの区切り(特にマルチターンの会話の場合)
ということで、以下のように読むらしいです。
- ユーザーのターンを示すトークン …
user
- モデルのターンを示すトークン …
model
- 会話ターンの開始を示すトークン …
<start_of_turn>
- 対話ターンの終わりを示すトークン …
<end_of_turn>
なので、Gemmaを使う場合は誰かがこのトークンを埋めてあげる必要があります、と。
LocalAIで試す
続いて、LocalAIで試してみましょう。
LocalAIのダウンロード。
$ curl -LO https://github.com/mudler/LocalAI/releases/download/v2.9.0/local-ai-avx2-Linux-x86_64 $ chmod a+x local-ai-avx2-Linux-x86_64 $ ./local-ai-avx2-Linux-x86_64 --version LocalAI version v2.9.0 (ff88c390bb51d9567572815a63c575eb2e3dd062)
models
ディレクトリに、量子化されたGemmaのモデルを配置します。
$ tree models models ├── gemma-2b-it-q4_K_M.gguf └── gemma-7b-it-q4_K_M.gguf 0 directories, 2 files
このような設定ファイルを用意。
local-ai-config.yaml
- name: gemma-2b-it backend: llama context_size: 700 parameters: model: gemma-2b-it-q4_K_M.gguf template: chat_message: &template |- <start_of_turn>{{if eq .RoleName "assistant"}}model{{else if eq .RoleName "system"}}system{{else if eq .RoleName "user"}}user{{end}} {{if .Content}}{{.Content}}{{end}} chat: &template |- <bos>{{.Input}} <start_of_turn>model completion: &template |- {{.Input}} - name: gemma-7b-it backend: llama context_size: 700 parameters: model: gemma-7b-it-q4_K_M.gguf template: chat_message: &template |- <start_of_turn>{{if eq .RoleName "assistant"}}model{{else if eq .RoleName "system"}}system{{else if eq .RoleName "user"}}user{{end}} {{if .Content}}{{.Content}}{{end}} chat: &template |- <bos>{{.Input}} <start_of_turn>model completion: &template |- {{.Input}}
起動。
$ ./local-ai-avx2-Linux-x86_64 --config-file local-ai-config.yaml --models-path models --threads 4
2Bで確認。初回はモデルのロードが入るので、1分近くかかりました。
$ time curl -s -XPOST -H 'Content-Type: application/json' localhost:8080/v1/chat/completions -d \ '{"model": "gemma-2b-it", "messages": [{"role": "user", "content": "Could you introduce yourself?"}]}' | jq { "created": 1709800992, "object": "chat.completion", "id": "a1a9dd86-3a0e-433f-9071-1c1a27143de0", "model": "gemma-2b-it", "choices": [ { "index": 0, "finish_reason": "stop", "message": { "role": "assistant", "content": "Hello! I am a large language model, trained by Google. I am a conversational AI that can assist you with a wide range of tasks, including answering questions, generating text, and translating languages.\n\nIs there anything I can help you with today?" } } ], "usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 } } real 0m9.713s user 0m0.041s sys 0m0.003s
日本語で。
$ time curl -s -XPOST -H 'Content-Type: application/json' localhost:8080/v1/chat/completions -d \ '{"model": "gemma-2b-it", "messages": [{"role": "user", "content": "あなたの自己紹介をしてください"}]}' | jq { "created": 1709800992, "object": "chat.completion", "id": "a1a9dd86-3a0e-433f-9071-1c1a27143de0", "model": "gemma-2b-it", "choices": [ { "index": 0, "finish_reason": "stop", "message": { "role": "assistant", "content": "私は、自然言語処理の研究者です。自然言語処理は、言語の理解と処理に関する研究分野です。\n\n私の研究は、自然言語処理の分野で特に以下の課題を研究しています。\n\n* **言語の理解:** 言語の単語や文の意味をどのように理解するのか?\n* **言語の処理:** 言語の処理はどのように行われるのか?\n* **自然言語の自動処理:** 自然言語の処理を自動化する仕組みを構築する。\n\nこれらの課題を解決することで、自然言語処理の分野で新しい技術を開発し、様々な分野で活用するための技術を構築することができます。\n\n私の研究は、**[Your University name]** の自然言語処理研究室**で展開しています 。" } } ], "usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 } } real 0m16.074s user 0m0.044s sys 0m0.010s
微妙…。
質問を変えてみます。
$ time curl -s -XPOST -H 'Content-Type: application/json' localhost:8080/v1/chat/completions -d \ '{"model": "gemma-2b-it", "messages": [{"role": "user", "content": "九州で最も北にある県はどこですか?"}]}' | jq { "created": 1709800992, "object": "chat.completion", "id": "a1a9dd86-3a0e-433f-9071-1c1a27143de0", "model": "gemma-2b-it", "choices": [ { "index": 0, "finish_reason": "stop", "message": { "role": "assistant", "content": "九州で最も北にある県は熊本県です。熊本県は九州の南東に位置し、日本国の最北に位置しています。" } } ], "usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 } } real 0m3.959s user 0m0.042s sys 0m0.008s
内容はわかっているようですが、微妙ですね…。
7Bで確認。初回の実行はモデルのロードが入るので、5分近くかかりました…。
$ time curl -s -XPOST -H 'Content-Type: application/json' localhost:8080/v1/chat/completions -d \ '{"model": "gemma-7b-it", "messages": [{"role": "user", "content": "Could you introduce yourself?"}]}' | jq { "created": 1709800992, "object": "chat.completion", "id": "a1a9dd86-3a0e-433f-9071-1c1a27143de0", "model": "gemma-7b-it", "choices": [ { "index": 0, "finish_reason": "stop", "message": { "role": "assistant", "content": "Sure, I am a large language model, trained on a massive amount of text data, I am here to provide you with information and help you with a wide range of tasks. I am still under development, but I am constantly learning new things. I am here to answer your questions, provide you with information, and help you with a variety of other tasks." } } ], "usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 } } real 0m27.425s user 0m0.038s sys 0m0.035s
日本語で。
$ time curl -s -XPOST -H 'Content-Type: application/json' localhost:8080/v1/chat/completions -d \ '{"model": "gemma-7b-it", "messages": [{"role": "user", "content": "あなたの自己紹介をしてください"}]}' | jq { "created": 1709800992, "object": "chat.completion", "id": "a1a9dd86-3a0e-433f-9071-1c1a27143de0", "model": "gemma-7b-it", "choices": [ { "index": 0, "finish_reason": "stop", "message": { "role": "assistant", "content": "**私はチャロッサ、プログラミング言語の Python を専門とするプログラミング言語のエンジニアです。**\n\n私は Python の基礎を学び、その後いくつかのプロジェクトを通じて実践的なスキルを学ぶいました。私は Python を使用して各種データ分析、Web アプリケーション、ゲームなど幅広い分野のソフトウェアを発表しています。\n\n私の趣味はプログラミングに伴われた新たな技術や手法の調査です。私は常に新しい情報を探し、新たな解決策を探すことに関心があります。\n\n私は Python のあらゆる機能に懐懐しいプログラミング言語の強さと、その柔軟性の強さに対する強い支持があります。私は Python を新たなプロジェクトの開発やコードの改善に使用する強力なツールとして、あらゆるプログラミングレベルの人にとって非常に強力な言語です。" } } ], "usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 } } real 1m1.254s user 0m0.044s sys 0m0.008s
やっぱり微妙。
2Bにもしてみた質問をもう1度。
$ time curl -s -XPOST -H 'Content-Type: application/json' localhost:8080/v1/chat/completions -d \ '{"model": "gemma-7b-it", "messages": [{"role": "user", "content": "九州で最も北にある県はどこですか?"}]}' | jq { "created": 1709800992, "object": "chat.completion", "id": "a1a9dd86-3a0e-433f-9071-1c1a27143de0", "model": "gemma-7b-it", "choices": [ { "index": 0, "finish_reason": "stop", "message": { "role": "assistant", "content": "\n\n九州の最も北にある県は福岡県です。福岡県は九州の西部に位置し、北部に福岡県があります。" } } ], "usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 } } real 0m12.077s user 0m0.045s sys 0m0.004s
答えは合っていますが、西にあるのか北にあるのかわかりませんね…。
ところでGemmaを使う際には<start_of_turn>
などのトークンを指定する必要があるわけですが、今回は以下のように指定しています。
template: chat_message: &template |- <start_of_turn>{{if eq .RoleName "assistant"}}model{{else if eq .RoleName "system"}}system{{else if eq .RoleName "user"}}user{{end}} {{if .Content}}{{.Content}}{{end}} chat: &template |- <bos>{{.Input}} <start_of_turn>model completion: &template |- {{.Input}}
プロンプトテンプレートのカスタマイズですね。
Customizing the Model / Example: Customizing the Prompt Template
以下のdiscussionをヒントにしています。
yaml file for Google's Gemma 2B & Gemma 7B · mudler LocalAI · Discussion #1736 · GitHub
微妙な返事が返ってきたりもしていますが、今回やりたいことはひととおりできました。
おわりに
GoogleのLLM、Gemmaをllama-cpp-pythonおよびLocalAIで試してみました。
量子化されたモデルを使ったということもありますが、少なくともTransformersで動かすよりはなんとかGemmaを体感できそうですね。