推論性能
推論時の5つの制御によって、スループットまたは精度が変わります。CUDAグラフの再生、精度、バッチ処理、タイル分割、テスト時拡張です。それぞれが特定のファミリー集合に適用され、そのうち2つは節約ではなく精度またはレイテンシのコストを伴います。
制御とデフォルト
これらはすべてpredictの引数で、デフォルトではすべて無効です。
| 引数 | デフォルト | 効果 |
|---|---|---|
batch | 1 | フォルダーとリストのソースで、順伝播1回あたりの画像数 |
cuda_graph | False | キャプチャ済みCUDAグラフから順伝播を再生 |
tiling | False | 大きい画像を重なり合うタイルに分割 |
overlap_ratio | 0.2 | tilingが有効な場合のタイルの重なり |
augment | False | 反転したビューを実行して統合 |
half | 受け付けて警告し、無視 | |
device | None | 推論前にモデルを移動 |
imgszはモデルが動作する解像度を設定するため、コストにも影響します。ただし、まず精度に関する引数であり、ここではなくモデル側に属します。
バッチ処理
from pathlib import Pathfrom PIL import Image from libreyolo import LibreYOLO, SAMPLE_IMAGE folder = Path("batch_demo")folder.mkdir(exist_ok=True)image = Image.open(SAMPLE_IMAGE)for index in range(8): image.save(folder / f"frame_{index}.jpg") model = LibreYOLO("LibreYOLO9s.pt") # 対応ファミリーでは4個のチャンクごとに1回の積み重ねた順伝播を実行results = model(str(folder), batch=4)print(len(results), "results")from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt") for result in model("batch_demo", batch=4, stream=True): print(len(result.boxes))libreyolo predict model=LibreYOLO9s.pt source=batch_demo batch=4batchはフォルダーとリストのソースに適用されます。batch=1では、画像ごとに1回の順伝播を実行します。1を超えると、各チャンクを前処理して1つのテンソルに積み重ね、1回実行した後、各ファミリーの既存の単一画像後処理が想定どおりの入力を受け取れるように再度分割します。
積み重ねた経路は、次のすべてを満たす場合だけ使用されます。
batchが1より大きい。tilingが無効。- テスト時拡張が有効でない。
- ファミリーが
SUPPORTS_BATCHED_PREDICTを設定している。 - 基盤となるネットワークが学習モードでない。
最後の条件は単なる細部ではありません。学習モードのネットワークでは、積み重ねたチャンクを画像横断のバッチ統計で正規化し、同じチャンク内の画像が互いの予測を変えてしまいます。そのため、その場合は順次実行を維持します。
SUPPORTS_BATCHED_PREDICTのデフォルトはtrueです。次のファミリーは対象外となり、batchに関係なく順伝播1回につき1画像を実行します。Depth Anything V2、Depth Anything 3、EoMT、Faster R-CNN、FCOS、HRNet、L2CS-Net、LibreMODUS、MiDaS、MoGe-2、PP-OCRv5、Real-ESRGAN、RetinaNet、SAM 3D Body、SwinIR、YOLOv1、ZipDepth、すべてのオープンボキャブラリ検出器、すべてのVision Language Modelです。
もう1つフォールバックがあります。チャンク全体で前処理が形状、dtype、デバイスの一致する均一な(1, C, H, W)テンソルを返さない場合、積み重ねずに順次実行します。そのため、正しさが偶然同じサイズになった画像に依存することはありません。
大きいフォルダーでbatchとstream=Trueを組み合わせると、すべての結果をメモリに保持せずにバッチ順伝播を実行できます。
CUDAグラフ
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt", device="cuda") # 最初のリクエストとは別にウォームアップとキャプチャを一度だけ実行model.capture_graph() result = model(SAMPLE_IMAGE, cuda_graph=True)print(len(result.boxes))print(model.graph_info())from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt", device="cuda") # "auto" は形状が2回現れるまで待つため1回限りの処理は# キャプチャのコストを負担しないfor _ in range(3): model(SAMPLE_IMAGE, cuda_graph="auto") print(model.graph_info())model.release_graphs()CUDAグラフは順伝播を1回記録し、単一の起動として再生します。小型検出器ではバッチ1の時間の大きな割合がカーネル起動に費やされるため、それらの起動をまとめることでスループットが向上し、再生出力はeager実行とビット単位で一致します。
cuda_graphは3つの値を取ります。デフォルトのFalseでは何も行いません。Trueでは、各入力形状の初回使用時にキャプチャします。"auto"では形状が繰り返されるまで待ってからキャプチャするため、1回限りの処理や形状が変化する処理はキャプチャのコストを負担しません。
capture_graph(imgsz=None, batch=1, dtype=None)を使うと、そのコストを最初のリクエストから外せます。グラフはキャプチャした正確な形状にだけ有効なので、ここでのbatchは後からpredictを呼び出す方法と一致させる必要があります。
graph_info()は、キャプチャ済みグラフ、再生回数、eager実行へフォールバックした理由を報告します。release_graphs()はグラフと静的バッファを解放します。
キャプチャにはCUDAと、SUPPORTS_CUDA_GRAPHで有効化されたファミリーが必要です。ホスト側から見える処理を含まない順伝播が必要であり、ファミリーごとに検証されているためです。有効化されていないファミリーで要求すると、暗黙にeager実行へ移行せずNotImplementedErrorが発生します。
グラフは値ではなくメモリアドレスを記録するため、パラメータを移動する操作を行うと破棄されます。predict(device=...)によるデバイス変更、量子化、量子化解除はすべてキャプチャ済みグラフを無効にします。
ファミリーごとの完全な対応マトリクス、シームの分割、数値の規約についてはCUDAグラフを参照してください。
精度
pip install "libreyolo[onnx]"from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt")path = model.export(format="onnx") exported = LibreYOLO(path)result = exported(SAMPLE_IMAGE)print(len(result.boxes))from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt", device="cuda")path = model.export(format="onnx", half=True) exported = LibreYOLO(path)result = exported(SAMPLE_IMAGE)print(len(result.boxes))from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt", device="cuda") # キャストレシピは較正データを読み取らないmodel.quantize(recipe="fp16", calib=None) result = model(SAMPLE_IMAGE)print(len(result.boxes))推論時のhalf=Trueは何も行いません。コマンドライン互換性のため受け付けられ、何もしないことを示す警告を発し、どのファミリーにも届く前に破棄されます。CLIの--halfフラグも.ptモデルに対して同じ警告を表示します。
低精度化には実際に動作する2つの経路があります。
エクスポートした成果物では、export(format=..., half=True)を使ってエクスポート時に精度を選択し、生成されたファイルを変更せずにLibreYOLO()から再読み込みできます。
PyTorch実行では、model.quantize(recipe="fp16")がモデルをfloat16へキャストし、モデルの入力と出力をfloat32に保つフックをインストールします。"bf16"はbfloat16で同じ処理を行います。どちらのキャストも較正データを読み取らないため、calibは無視されます。現在、量子化はYOLOv9、RF-DETR、BiRefNet、FeyNobgの4ファミリーを対象とします。CPUデバイス上のキャストでは遅くなるという警告がログに記録されるため、これらのレシピはGPU向けです。
どちらの経路も数値を変えます。検出結果が同じになることをそのまま保証するものではないため、デプロイ前に検証してください。
タイル分割推論
from PIL import Image from libreyolo import LibreYOLO, SAMPLE_IMAGE # 画像が入力サイズより大きい場合だけタイル分割を実行large = Image.open(SAMPLE_IMAGE).resize((2048, 1536))large.save("large.jpg") model = LibreYOLO("LibreYOLO9s.pt") result = model("large.jpg", tiling=True, overlap_ratio=0.2)print(result.num_tiles, "tiles", len(result.boxes), "detections")タイル分割では、大きい画像を重なり合う正方形タイルに切り出し、それぞれで推論し、結果を統合します。画像全体のリサイズによって対象がモデルで解像できないほど小さくなる高解像度画像で、小物体を扱うための選択肢です。
タイルサイズはモデルの入力サイズ、または指定した場合はimgszで、正方形でなければなりません。overlap_ratioのデフォルトは0.2です。重なり合うタイルは、iouのしきい値でクラスごとのNMSを使って調整され、統合後の一覧がmax_detまでに切り詰められます。つまり、独自にはNMSを実行しないファミリーでも、タイル分割推論ではiouが効果を持ちます。
画像がすでに収まる場合、タイル分割は単に低コストになるのではなくスキップされます。両方の寸法が入力サイズ以下なら、代わりに通常の順伝播を1回実行します。分類、セマンティックセグメンテーション、embedタスクでもスキップされます。そこではタイル分割に意味がないため、1回の処理へフォールバックします。
再結合できないペイロードを持つタスクでは例外が発生します。対象はインスタンスセグメンテーションのマスク、回転バウンディングボックス、点、深度、エッジ、法線です。augmentと組み合わせることもできません。
結果にはresult.tiledとresult.num_tilesが含まれます。save=Trueを指定すると、タイル分割実行はruns/tiled_detectionsの下にディレクトリを作成し、すべてのタイル、アノテーション済み画像、グリッド表示、タイルサイズ、重なり、しきい値を記録したmetadata.jsonを保存します。result.tiles_pathとresult.grid_pathはそれらを指します。
テスト時拡張
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt") plain = model(SAMPLE_IMAGE)flipped = model(SAMPLE_IMAGE, augment=True) print(len(plain.boxes), "->", len(flipped.boxes))augment=Trueでは画像を複数回実行し、iouのしきい値でクラスごとのNMSを使って検出結果を統合します。タイル分割と同様に、通常はiouを無視するファミリーでも、この処理ではiouが重要になります。
実際には水平反転です。スケール一覧TTA_SCALESのデフォルトは単一スケールの1.0で、公開されているどのファミリーも上書きしていません。そのため、各ファミリーは元画像とその鏡像の2回の処理を実行します。TTA_FIXED_SIZEが設定されたファミリーは固定された正方形にリサイズするため、どの場合でもマルチスケールは何も行いません。
セマンティックセグメンテーションとパノプティックセグメンテーションは異なる統合処理を使います。反転したビューを元に戻し、ボックスとして統合する代わりに、2つのsoftmax分布をargmaxの前に平均します。
テスト時拡張はすべてのタスクで利用できるわけではありません。回転バウンディングボックス、姿勢推定、点、深度、法線、エッジ、復元、OCR、埋め込みベクトルのモデルでは例外が発生し、タイル分割とも組み合わせられません。
次のファミリーでは完全に無効になっているため、augment=Trueでも通常の処理を1回だけ実行します。BiRefNet、CenterNet、CLIP、DexiNed、FOMO、HRNet、L2CS-Net、LibreMODUS、NAFNet、PP-OCRv5、Real-ESRGAN、RetinaNet、SAM 3D Body、SigLIP2、SwinIR、TEED、すべてのSAMバリアント、すべてのオープンボキャブラリ検出器、すべてのVision Language Modelです。
測定
ハードウェア、ランタイム、精度、バッチサイズが示されていないミリ秒値は事実ではないため、このページにはレイテンシの数値を載せていません。ハードウェアとランタイムを横断した測定値はvisionanalysis.orgで公開され、libreyolo profileを使うと手元のマシンで特定のモデルを測定できます。