llm-jp/llm-jp-4-32b-a3b-baseの翻訳用SFT/DPOデータとLoRAを準備していました。単GPUのllm-jp-4-32b-a3b-base-NVFP4でPP/TGを測ると想定より速く、常駐モデルから必要時の翻訳バッチへ方針を変えました。LLM導入で翻訳と他の推論に使うVRAMを配分するためです。

モデル提供元: llm-jp on Hugging Face

実行コマンド

検証に使った起動コマンドです。

  podman run --rm -it \
  --device nvidia.com/gpu=0 \
  --ipc=host \
  -e HF_HOME=/hf \
  -e HF_HUB_OFFLINE=1 \
  -v /mnt/data/models:/hf/hub:ro \
  -p 9000:9000 \
  registry.home.arpa/vllm-openai:v0.18.0-cu130 \
  /hf/hub/llm-jp-4-32b-a3b-base-NVFP4 \
  --host 0.0.0.0 \
  --port 9000 \
  --trust-remote-code \
  --quantization compressed-tensors \
  --served-model-name translator \
  --dtype bfloat16 \
  --gpu-memory-utilization 0.80 \
  --max-num-seqs 8 \
  --max-model-len 32678 \
  --max-num-batched-tokens 1024 \
  --no-enable-prefix-caching
  

prefix cachingは無効にしました。翻訳では固定prefixの再利用が少ないため、まずキャッシュなしで測ります。

起動ログ

起動時の条件です。

  (APIServer pid=1) INFO 04-14 08:08:26 [utils.py:233] non-default args: {'model_tag': '/hf/hub/llm-jp-4-32b-a3b-base-NVFP4', 'host': '0.0.0.0', 'port': 9000, 'model': '/hf/hub/llm-jp-4-32b-a3b-base-NVFP4', 'trust_remote_code': True, 'dtype': 'bfloat16', 'max_model_len': 32678, 'quantization': 'compressed-tensors', 'served_model_name': ['translator'], 'gpu_memory_utilization': 0.8, 'enable_prefix_caching': False, 'max_num_batched_tokens': 1024, 'max_num_seqs': 8}
  

モデル読み込みから初期化完了までのログです。

  (EngineCore pid=118) INFO 04-14 08:08:35 [gpu_model_runner.py:4481] Starting to load model /hf/hub/llm-jp-4-32b-a3b-base-NVFP4...
(EngineCore pid=118) INFO 04-14 08:08:39 [default_loader.py:384] Loading weights took 3.32 seconds
(EngineCore pid=118) INFO 04-14 08:08:39 [gpu_model_runner.py:4566] Model loading took 18.23 GiB memory and 3.843664 seconds
(EngineCore pid=118) INFO 04-14 08:08:52 [monitor.py:48] torch.compile took 12.35 s in total
(EngineCore pid=118) INFO 04-14 08:08:52 [monitor.py:76] Initial profiling/warmup run took 0.51 s
(EngineCore pid=118) INFO 04-14 08:09:29 [gpu_worker.py:456] Available KV cache memory: 56.62 GiB
(EngineCore pid=118) INFO 04-14 08:09:29 [kv_cache_utils.py:1316] GPU KV cache size: 927,600 tokens
(EngineCore pid=118) INFO 04-14 08:09:29 [kv_cache_utils.py:1321] Maximum concurrency for 32,678 tokens per request: 28.38x
  

単発・一括の実測

単発リクエスト:

  (APIServer pid=1) INFO 04-14 08:11:32 [loggers.py:259] Engine 000: Avg prompt throughput: 12.5 tokens/s, Avg generation throughput: 25.6 tokens/s, Running: 0 reqs, Waiting: 0 reqs, GPU KV cache usage: 0.0%, Prefix cache hit rate: 0.0%
  

一括 (seq=8, ctx=32768) の代表ログ:

  (APIServer pid=1) INFO 04-14 07:34:20 [loggers.py:259] Engine 000: Avg prompt throughput: 353.8 tokens/s, Avg generation throughput: 157.0 tokens/s, Running: 1 reqs, Waiting: 0 reqs, GPU KV cache usage: 0.4%, Prefix cache hit rate: 0.0%
(APIServer pid=1) INFO:     10.0.2.100:41472 - "POST /v1/chat/completions HTTP/1.1" 200 OK
(APIServer pid=1) INFO:     10.0.2.100:41484 - "POST /v1/chat/completions HTTP/1.1" 200 OK
(APIServer pid=1) INFO:     10.0.2.100:36782 - "POST /v1/chat/completions HTTP/1.1" 200 OK
(APIServer pid=1) INFO:     10.0.2.100:36788 - "POST /v1/chat/completions HTTP/1.1" 200 OK
(APIServer pid=1) INFO:     10.0.2.100:36790 - "POST /v1/chat/completions HTTP/1.1" 200 OK
(APIServer pid=1) INFO 04-14 07:34:30 [loggers.py:259] Engine 000: Avg prompt throughput: 2768.4 tokens/s, Avg generation throughput: 159.5 tokens/s, Running: 1 reqs, Waiting: 0 reqs, GPU KV cache usage: 1.0%, Prefix cache hit rate: 0.0%
(APIServer pid=1) INFO 04-14 07:34:40 [loggers.py:259] Engine 000: Avg prompt throughput: 276.4 tokens/s, Avg generation throughput: 182.2 tokens/s, Running: 1 reqs, Waiting: 0 reqs, GPU KV cache usage: 0.5%, Prefix cache hit rate: 0.0%
(APIServer pid=1) INFO 04-14 07:34:50 [loggers.py:259] Engine 000: Avg prompt throughput: 0.0 tokens/s, Avg generation throughput: 182.1 tokens/s, Running: 1 reqs, Waiting: 0 reqs, GPU KV cache usage: 0.7%, Prefix cache hit rate: 0.0%
(APIServer pid=1) INFO 04-14 07:35:00 [loggers.py:259] Engine 000: Avg prompt throughput: 0.0 tokens/s, Avg generation throughput: 179.2 tokens/s, Running: 1 reqs, Waiting: 0 reqs, GPU KV cache usage: 0.9%, Prefix cache hit rate: 0.0%
  

今回観測した範囲です。

  • Prompt throughput: 約 350-1360 tok/s(観測上は 2768.4 tok/s の瞬間ピークあり)
  • Generation throughput: 約 152-183 tok/s
  • Prefix cache hit rate: 常時 0.0%(--no-enable-prefix-caching と整合)

今回の速度なら、翻訳を必要時にまとめて処理できます。max_num_seqs=8は控えめな設定で、短・中入力には増やせる余地があると考えています。

長い生成でのdecode速度

長い生成ではdecode速度が徐々に下がります。

  183.2 -> 180.5 -> 178.2 -> 176.4 -> 173.3 -> 170.0 -> 169.3 -> 167.9
169.7 -> 168.9 -> 167.2 -> 165.5 -> 163.7 -> 160.9 -> 159.4 -> 158.0 -> 156.3 -> 154.7 -> 152.2
  

常駐からバッチへ変更

実測した速度と安定性から、次の構成に変更しました。

  1. 自作ハーネスから translator ロールを削除
  2. Dagsterパイプラインのトリガーで翻訳バッチを起動
  3. 常時推論用VRAMを優先確保

翻訳専用LoRAのSFT/DPOを準備していましたが、PP/TGが十分速いため、常時VRAMを割り当てる必要性は下がりました。翻訳結果をすぐ再学習へ使う自動化がなければ、必要時に処理し、他の推論にVRAMを回す方針です。

次の実装案

llm-jp-4-32b-a3b-baseの翻訳専用化は手間がかかる可能性があります。次はthinking:lowで再検証する予定です。

thinkingのreasoning: low|middle|highを用途別に選べる前提で、次の2段を検討します。

  1. gemmatranslate4b-it で堅い日本語へ一次変換
  2. llm-jp-32b-a3b-thinking:low で流暢化・読み味調整

thinking:lowやLoRAで調整が難しい場合も、gemmatranslate-4b-itと組み合わせた2段構成の使える範囲を確認したいと考えています。

単GPUと2GPUの結果

llm-jp-4-32b-a3bは単GPUのNVFP4で動作しました。--tensor-parallel-size 2ではIntermediate size padding for w1 and w3 ... not currently supportedになり、--moe-backend cutlassなどauto以外でも解消しませんでした。2つのinstanceに分ける回避案はありますが、この検証では深追いしていません。翻訳はオンデマンドのバッチに切り替える方針です。