Transformers連続バッチング入門

CHATGPT / AIChatGPTなどの生成AIを活用して運営・記事制作・更新を行っています。制作方針

Transformers 5.14.1のgenerate_batchで長さの違う複数promptを処理し、requestごとの結果を確認するまでを、GPU前提、失敗時の戻し方とともに解説します。

長さの違うrequestを待機・生成・入替の順に流し、空いたGPU枠を再利用する図解

先に結論

Transformers 5のgenerate_batch()は、長さの違う複数の入力を1つの固定batchとして最後まで待たせるのではなく、生成stepごとに完了したrequestを外して次を入れるcontinuous batching(連続バッチング)を扱えます。最初は性能調整をせず、3つのpromptをtokenizeし、返り値がrequest IDをkeyにした辞書であることと、3件すべての生成結果を取得できたことを確認します。

この機能はPaged Attentionを前提とし、実用的な確認には対応GPUと十分なGPU memoryが必要です。この記事はTransformers v5.14.1へ固定した公式資料ベースの手順であり、実機でmodelをdownload・実行した結果や速度値ではありません。

確認日時: 2026年8月4日 05:30(Asia/Tokyo)
固定した版: Transformers v5.14.1、commit a08ace4(2026年7月16日公開)。continuous batching文書とAPI referenceは2026年8月4日に再監査
想定環境: Python 3.10以上、PyTorch 2.4以上、PyTorchから認識できるCUDA対応GPU、modelを格納できるdiskとGPU memory
検証区分: 公式documentationとreleaseを照合した机上調査です。Python例の構文だけを確認し、GPU実行、model取得、生成品質、latency、throughput、VRAM使用量は未検証です。
対象外: CPUだけの性能評価、Apple Silicon、複数GPU、HTTP serving、productionの負荷試験

連続バッチングで何が変わるか

通常の固定batchでは、同時に始めたrequestのうち最も長い生成が終わるまでbatchの構成を保ちます。短いrequestが先に終わっても、その位置を次のrequestへすぐ渡せません。連続バッチングではscheduler(どのrequestを次の計算へ入れるか決める仕組み)が生成stepごとに状態を確認し、完了したrequestを外して待機中のrequestを入れます。

固定batch:      A ━━━━━━━ 完了
                B ━━━━━━━━━━━━━ 完了  ← Aの終了後も構成を維持

連続batching:  A ━━━━━━━ 完了
                C       ━━━━━━━        ← 空いた枠へ入る
                B ━━━━━━━━━━━━━ 完了

Transformersの公式文書では、requestは待機、prefill、decode、完了の状態を進みます。prefillは入力promptを処理してKV cacheを作る段階、decodeは出力tokenを1つずつ生成する段階です。Paged AttentionはKV cacheを固定長blockで管理し、長さの違うrequestが同じmemory poolを使えるようにします。

generate_batch()はprompt一覧をまとめて渡し、内部のschedulingが終わるまで待って全結果を返す入口です。途中参加やstreamingを扱うserver用途ではContinuousBatchingManagerを直接使いますが、初回はlife cycleを自分で管理しなくてよいgenerate_batch()から始めます。

始める前の確認

Transformersの公式installation文書はPython 3.10以上、PyTorch 2.4以上を検証対象として案内しています。GPU driverやPyTorchのCUDA buildは環境ごとに適切な組み合わせが異なるため、この記事では新規install commandを一律に指定しません。既存の隔離された仮想環境で次を確認します。

python --version
python -c 'import torch, transformers; print(torch.__version__, transformers.__version__)'
python -c 'import torch; print(torch.cuda.is_available())'

期待する状態は、Pythonが3.10以上、Transformersが5.14.1、最後のcommandがTrueです。Falseならmodelを取得する前に止まり、PyTorch公式の環境別install手順へ戻ります。GPU名と空きmemoryもread-onlyで確認します。

nvidia-smi

例では公式のoptimization文書にも登場するQwen/Qwen3-0.6Bを使います。初回実行時はmodel fileを取得するため、必要なdisk容量、利用条件、組織のnetwork policyを先に確認してください。model名を変える場合は、architectureがcontinuous batchingとPaged Attentionを支えるか、必要memoryへ収まるかを別に確認します。

最短手順:3件をgenerate_batchへ渡す

次をcb_first_run.pyとして保存します。最初の確認では追加packageを必要としないPyTorch標準のSDPAを明示し、samplingを無効化します。max_batch_tokens=256は小さな確認用の上限であり、最適値ではありません。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.generation import ContinuousBatchingConfig, GenerationConfig

MODEL_ID = "Qwen/Qwen3-0.6B"

tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
model = AutoModelForCausalLM.from_pretrained(
    MODEL_ID,
    attn_implementation="paged|sdpa",
    device_map="cuda",
    dtype=torch.bfloat16,
)

prompts = [
    "重力を一文で説明してください。",
    "猫の品種を一つ挙げてください。",
    "連続バッチングと固定バッチの違いを三文で説明してください。",
]
inputs = [tokenizer.encode(prompt) for prompt in prompts]

generation_config = GenerationConfig(
    max_new_tokens=64,
    eos_token_id=tokenizer.eos_token_id,
    pad_token_id=tokenizer.pad_token_id,
    do_sample=False,
)
batching_config = ContinuousBatchingConfig(
    max_batch_tokens=256,
    max_requests_per_batch=3,
    use_cuda_graph=False,
    use_async_batching=False,
)

outputs = model.generate_batch(
    inputs=inputs,
    generation_config=generation_config,
    continuous_batching_config=batching_config,
    progress_bar=True,
)

if len(outputs) != len(prompts):
    raise RuntimeError(f"expected {len(prompts)} results, got {len(outputs)}")

for request_id, output in outputs.items():
    text = tokenizer.decode(output.generated_tokens, skip_special_tokens=True)
    print(f"[{request_id}] {text}")

実行は仮想環境内で行います。

python cb_first_run.py

device_map=“cuda”はmodelをCUDA deviceへ配置します。paged|sdpaはPaged AttentionとPyTorch SDPAを明示する指定です。公式文書ではFlashAttention経路の方が効率的になりやすいと説明されていますが、追加packageと対応条件が増えるため、最初の成功確認からは分離します。

成功をどう確認するか

最初の成功は「速くなった気がする」ではなく、次の4点で判定します。

  1. programがexceptionなしで終了する
  2. 結果の辞書にpromptと同じ3件が入る
  3. 各valueにgenerated_tokensがあり、decodeできる
  4. 出力をprompt順だと思い込まず、request IDと結果の組で扱う

generate_batch()はrequest IDからGenerationOutputへの辞書を返します。IDは入力配列のindexそのものとは限らないため、productionで元requestと結び付ける必要がある場合はContinuousBatchingManagerで自分のrequest_idを付ける設計へ進みます。

この1回では、固定batchより速いこと、GPU使用率が高いこと、3件が同時に処理されたことまでは証明できません。性能比較には入力長分布、同時到着、warm-up、測定区間を固定した別の負荷試験が必要です。

つまずきやすい点と戻し方

CUDAを認識しない

torch.cuda.is_available()Falseなら、Transformersの設定値を変える前にPyTorch、driver、CUDA互換性へ戻ります。CPUへ自動fallbackさせて成功扱いにすると、GPU前提のmemoryとlatencyの判断が崩れます。

model loadでGPU memoryが足りない

OOM(out of memory)がmodel load中に出る場合、max_batch_tokensを下げてもmodel weight自体は小さくなりません。processを終了してGPU memoryを解放し、より小さい対応modelを選ぶか、別のGPU環境へ戻します。量子化を試す場合は別条件として記録し、未量子化の結果と混ぜません。

Paged Attention backendで止まる

errorにattention backendや未対応architectureが示されたら、paged|sdpaという文字列を推測で変更せず、利用中versionのContinuous batching公式ガイドとmodelの対応状況を確認します。FlashAttentionを追加するのは、SDPA経路の正しさを確認した後の別変更にします。

生成結果が空または途中で終わる

まずeos_token_idpad_token_idmax_new_tokensを確認します。結果の内容が不適切でも、batchingの失敗とmodel品質の問題を混同しません。同じprompt、sampling無効、同じmodel revisionで再現するかを記録します。

初回だけ長くかかる

初回はmodel取得、weight load、kernel初期化などを含み得ます。2回目と単純比較せず、downloadを含む初回、load後のwarm-up、測定区間を分けます。CUDA graphsを有効にするとcapture時間と追加memoryも入るため、入門手順では明示的に無効化しています。

固定batchとの役割を分ける

連続バッチングは、長さや到着時刻が異なる複数requestをGPUへ流し続ける場面に向きます。入力が同じ長さで、全件を一括処理して終了する小さなoffline jobでは、固定batchの単純さが勝る場合があります。機能名だけで置き換えず、実際のworkloadで判断します。

llama.cppのserver側で同じ概念を調べる場合は、llama-serverの連続バッチング設定がparallel slotと共有KVの関係を扱っています。Transformers標準APIでmemory予算と測定軸まで決める場合は、Transformers連続バッチングのKV予算設計へ進んでください。

参考資料