llama.cppとPLaMo翻訳モデルでOpenAI API互換の翻訳サーバーを立てる
llama.cppでPLaMo翻訳モデルを使い、OpenAI API互換サーバーを立てて翻訳を行うメモ。
llama.cppのインストール
macOSの場合、Homebrewでインストールできます。
brew install llama.cpp
次のバージョンで試しました。
$ llama-server --version
ggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices
ggml_metal_library_init: using embedded metal library
ggml_metal_library_init: loaded in 0.007 sec
ggml_metal_rsets_init: creating a residency set collection (keep_alive = 180 s)
ggml_metal_device_init: GPU name: Apple M4 Max
ggml_metal_device_init: GPU family: MTLGPUFamilyApple9 (1009)
ggml_metal_device_init: GPU family: MTLGPUFamilyCommon3 (3003)
ggml_metal_device_init: GPU family: MTLGPUFamilyMetal3 (5001)
ggml_metal_device_init: simdgroup reduction = true
ggml_metal_device_init: simdgroup matrix mul. = true
ggml_metal_device_init: has unified memory = true
ggml_metal_device_init: has bfloat = true
ggml_metal_device_init: has tensor = false
ggml_metal_device_init: use residency sets = true
ggml_metal_device_init: use shared buffers = true
ggml_metal_device_init: recommendedMaxWorkingSetSize = 103079.22 MB
version: 7490 (5182dd64c)
built with AppleClang 17.0.0.17000404 for Darwin arm64
OpenAI API Serverの起動
GGUF形式のPLaMo翻訳モデルを使ってllama.cppでOpenAI API互換サーバーを起動します。
llama-server -hf mmnga/plamo-2-translate-gguf --alias plamo2-translate --port 8000
初回実行時はモデルのダウンロードが行われます。サーバーが起動すると、http://localhost:8000でOpenAI API互換のエンドポイントが利用可能になります。
翻訳APIの呼び出し
PLaMo翻訳モデルは以下の形式のプロンプトを使用します。
<|plamo:op|>dataset
translation
<|plamo:op|>input lang=Japanese
ここに翻訳対象のテキストを書きます
<|plamo:op|>output lang=English
日本語から英語への翻訳
curlで日英翻訳を実行してみます。
curl http://127.0.0.1:8000/v1/chat/completions \
--json '{
"model": "plamo2-translate",
"messages": [
{
"role": "user",
"content": "<|plamo:op|>dataset\ntranslation\n<|plamo:op|>input lang=Japanese\n家計は火の車だ。毎月の支払いに追われ、貯金を切り崩す日々が続いている。\n<|plamo:op|>output lang=English"
}
]
}' -s | jq .
レスポンスのcontentフィールドに翻訳結果が返されます。
{
"choices": [
{
"finish_reason": "stop",
"index": 0,
"message": {
"role": "assistant",
"content": "Our household finances are in a mess. We're constantly being chased by monthly bills and have to keep dipping into our savings.\n"
}
}
],
"created": 1766412843,
"model": "plamo2-translate",
"system_fingerprint": "b7490-5182dd64c",
"object": "chat.completion",
"usage": {
"completion_tokens": 23,
"prompt_tokens": 41,
"total_tokens": 64
},
"id": "chatcmpl-IksvG1y16PKmmILVu1IFECqIk6pQJkgf",
"timings": {
"cache_n": 0,
"prompt_n": 41,
"prompt_ms": 15.982,
"prompt_per_token_ms": 0.38980487804878045,
"prompt_per_second": 2565.3860593167315,
"predicted_n": 23,
"predicted_ms": 563.22,
"predicted_per_token_ms": 24.487826086956524,
"predicted_per_second": 40.83661801782607
}
}
「火の車」という慣用句が適切に翻訳されていることが分かります。
英語から日本語への翻訳
逆方向の翻訳も同様に実行できます。input langとoutput langを入れ替えます。
先ほどの日英翻訳結果を、今度は日本語に翻訳してみます。
curl http://127.0.0.1:8000/v1/chat/completions \
--json '{
"model": "plamo2-translate",
"messages": [
{
"role": "user",
"content": "<|plamo:op|>dataset\ntranslation\n<|plamo:op|>input lang=English\nOur household finances are in a mess. We''re constantly being chased by monthly bills and have to keep dipping into our savings.\n<|plamo:op|>output lang=Japanese"
}
]
}' -s | jq .
{
"choices": [
{
"finish_reason": "stop",
"index": 0,
"message": {
"role": "assistant",
"content": "私たちの家計はめちゃくちゃだ。毎月の支払いに追われてばかりで、貯金を切り崩さなければならない状態が続いている。\n"
}
}
],
"created": 1766412917,
"model": "plamo2-translate",
"system_fingerprint": "b7490-5182dd64c",
"object": "chat.completion",
"usage": {
"completion_tokens": 20,
"prompt_tokens": 46,
"total_tokens": 66
},
"id": "chatcmpl-SZQLJKG4azv7woIIYzUTWyjc1DaUnbgZ",
"timings": {
"cache_n": 0,
"prompt_n": 46,
"prompt_ms": 10.012,
"prompt_per_token_ms": 0.21765217391304348,
"prompt_per_second": 4594.486616060727,
"predicted_n": 20,
"predicted_ms": 495.466,
"predicted_per_token_ms": 24.7733,
"predicted_per_second": 40.36603924386335
}
}
元の日本語とは異なる表現になっていますが、意味は正しく翻訳されています。
チャットテンプレートを使ったプロンプト形式の簡略化
上記の方法では、毎回プロンプト形式を指定する必要があります。llama.cppのチャットテンプレート機能を使うと、この定型部分を隠蔽してシンプルなAPIリクエストにできます。 ただし、翻訳元と翻訳先の言語をパラメータ化する方法がわからなかったため、ここでは日英翻訳専用のテンプレートを作成する例を示します。
以下のJinjaテンプレートを/tmp/ja2en.jinjaとして保存します。
<|plamo:op|>dataset
translation
<|plamo:op|>input lang=Japanese
{% for message in messages %}
{% if message.role == 'user' %}{{ message.content }}{% endif %}
{% endfor %}
<|plamo:op|>output lang=English
このテンプレートを使ってサーバーを起動します。
llama-server -hf mmnga/plamo-2-translate-gguf --alias plamo2-translate --port 8000 --chat-template-file /tmp/ja2en.jinja
これで、プロンプト形式を意識せずに翻訳対象のテキストのみを送信できます。
curl http://127.0.0.1:8000/v1/chat/completions \
--json '{
"model": "plamo2-translate",
"messages": [
{
"role": "user",
"content": "家計は火の車だ。毎月の支払いに追われ、貯金を切り崩す日々が続いている。"
}
]
}' -s | jq .
router modeを使用する
llama.cppの新機能であるrouter modeを使うと、複数のモデルを同時にサーバーでホストし、APIリクエストごとに使用するモデルを切り替えられます。 この場合は、チャットテンプレートを使用しない方がいいかもしれません。
router modeでは起動時にモデル名を指定しません。ただし、モデルは事前にダウンロードしておく必要があります。
llama-server --port 8000
次のようなログが出ます
main: router server is listening on http://127.0.0.1:8000
main: NOTE: router mode is experimental
main: it is not recommended to use this mode in untrusted environments
リクエスト時にmodelパラメータで使用するモデルを指定します。
curl http://127.0.0.1:8000/v1/chat/completions \
--json '{
"model": "mmnga/plamo-2-translate-gguf",
"messages": [
{
"role": "user",
"content": "<|plamo:op|>dataset\ntranslation\n<|plamo:op|>input lang=Japanese\n家計は火の車だ。毎月の支払いに追われ、貯金を切り崩す日々が続いている。\n<|plamo:op|>output lang=English"
}
]
}' -s | jq .
次のようなレスポンスが返されます。
{
"choices": [
{
"finish_reason": "stop",
"index": 0,
"message": {
"role": "assistant",
"content": "Our household is in dire straits. We're constantly being chased by monthly payments and having to withdraw from our savings.\n"
}
}
],
"created": 1766450748,
"model": "mmnga/plamo-2-translate-gguf",
"system_fingerprint": "b7490-5182dd64c",
"object": "chat.completion",
"usage": {
"completion_tokens": 23,
"prompt_tokens": 41,
"total_tokens": 64
},
"id": "chatcmpl-CLI7iYPEAXoH4qtZeQHSDIomjC3kTljz",
"timings": {
"cache_n": 0,
"prompt_n": 41,
"prompt_ms": 18.575,
"prompt_per_token_ms": 0.45304878048780484,
"prompt_per_second": 2207.2678331090174,
"predicted_n": 23,
"predicted_ms": 579.414,
"predicted_per_token_ms": 25.19191304347826,
"predicted_per_second": 39.69527833293638
}
}
llama.cppを使うことで、PLaMo翻訳モデルをOpenAI API互換のサーバーとしてローカルで実行できました。OpenAI APIで翻訳ができれば、既存のアプリケーションからも翻訳機能を利用しやすくなります。