ONNX

ONNXは移植可能なグラフ形式です。LibreYOLOはtorch.onnx.exportでモデルをトレースし、必要に応じてグラフを簡略化します。さらに、ファミリー、タスク、クラス名、入力サイズをファイル自体のメタデータに書き込むため、どのLibreYOLOバックエンドでも後処理を再構築できます。

フラグ
export(format="onnx")
出力
グラフにメタデータを埋め込んだ1個の.onnxファイル
追加パッケージ
pip install "libreyolo[onnx]"
再読み込み
LibreYOLO("weights/LibreYOLO9t.onnx")
形状
Pythonではデフォルトで動的バッチ。タスクごとの例外は以下を参照
精度
FP32、FP16(half=True)、INT8(int8=True、YOLO9の物体検出)

インストール

インストール
pip install "libreyolo[onnx]"

追加パッケージではonnxonnxsimonnxruntimeがインストールされます。ファイルの 書き出しにはonnxだけで十分です。onnxsimは簡略化処理を実行し、onnxruntimeは 成果物の実行とINT8キャリブレーションを行います。

エクスポート

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9t.pt") # weights/LibreYOLO9t.onnxを書き出しpath = model.export(format="onnx")print(path)
CLI
libreyolo export --model LibreYOLO9t.pt --format onnx
引数
model.export(    format="onnx",    imgsz=640,        # intまたは(height, width)    batch=1,    dynamic=True,     # Pythonのデフォルト。CLIのデフォルトはFalse    simplify=True,    # グラフにonnxsimを実行    opset=None,       # Noneは13、DETR系ファミリーは17を選択    half=False,       # FP16の重みと活性化    int8=False,       # QDQ INT8、YOLO9の物体検出のみ    data=None,        # キャリブレーション用data.yaml、INT8のみ    device=None,      # トレース用デバイス。Noneはモデルのデバイスを使用    output_path=None, # Noneはweights/<stem>.onnxに書き出し)

output_pathを指定しない場合、ファイルはチェックポイントのstemを名前として weights/に保存されます。その精度を要求した場合は_fp16または_int8が付加されます。

dynamicのデフォルトはPythonではTrue、CLIではFalseです。有効にするとバッチ軸が シンボリックになり、一部のタスクではさらに多くの軸が可変になります。セマンティック セグメンテーションではマスクの高さと幅も可変になり、Real-ESRGANの画像復元では空間軸が 可変になります。2段階検出器ではリサイズをグラフ内で行うため、入力画像の高さと幅が 可変のままになります。

opsetを省略すると、ファミリーごとに選択されます。DETR系ファミリー(detrdeformable_detrdinodetrdfinedeimdeimv2eclwdetrrfdetrrtdetrrtdetrv2rtdetrv4)に加え、deitmidasmoge2では、 aten::scaled_dot_productを変換できるopset 17が使われます。それ以外はすべて13です。 マッティングは常に19に引き上げられます。BiRefNetのデコーダーには、ONNXでopset 19から 定義されるDeformConv演算子が必要なためです。

simplify=Trueonnxsimを実行し、処理に失敗した場合は元のグラフを維持します。そのため、 簡略化エラーはエクスポート失敗ではなく警告になります。macOS arm64でonnx 1.22以降と onnxsim 0.6.5以前を組み合わせた場合、この処理は完全にスキップされます。この組み合わせでは Pythonプロセスが異常終了する可能性があるためです。

組み込みNMS

グラフにNMSを組み込む
from libreyolo import LibreYOLO # YOLO9の物体検出のみ、バッチ1。dynamicは強制的にFalseLibreYOLO("LibreYOLO9t.pt").export(    format="onnx",    nms=True,    conf=0.25,    iou=0.45,    max_det=300,)
CLI
libreyolo export --model LibreYOLO9t.pt --format onnx --nms \  --conf 0.25 --iou 0.45 --max-det 300

nms=TrueはYOLO9の物体検出だけに対応し、バッチ1が必要です。dynamic=Trueとともに指定すると 警告を記録して動的軸を無効にします。この場合、グラフには2つの出力があります。output(batch, max_det, 6)形状で、rawは未デコードの検出器テンソルです。LibreYOLO自身の バックエンドはrawを使うため、後処理がPyTorch経路と同一になります。

DeepStream

deepstream=TrueはONNX専用のオプションです。NVIDIA DeepStreamのパーサーが想定する レイアウトでグラフをエクスポートし、隣にconfig_infer_primary_<stem>.txt<stem>_labels.txtという2個のサイドカーファイルを書き出します。そのため、手作業で設定を 作らずに成果物をパイプラインへ組み込めます。

このオプションはnms=Trueと同時に使えません。両方を指定するとValueErrorが発生します。 DeepStreamは独自のクラスタリング段階で抑制を実行するためです。ONNX以外の形式に渡した場合も 同様に例外が発生します。対応するファミリーとタスクの一覧、およびパーサーのビルド方法は DeepStreamを参照してください。

INT8

キャリブレーションデータを使うINT8
from libreyolo import LibreYOLO LibreYOLO("LibreYOLO9t.pt").export(    format="onnx",    int8=True,    data="coco128.yaml",   # 数百枚の代表的な画像    fraction=1.0,)

int8=TrueはONNX Runtimeの静的量子化を実行し、float32の入力と出力を持つQDQグラフを 書き出します。量子化されるのはConvノードとGemmノードだけです。物体検出ヘッドの デコードをfloat32のままにするのは意図的です。この連結処理ではピクセル単位のボックス座標と 0〜1のクラススコアが混在します。ボックスの値の大きさに支配された単一のテンソル単位の 活性化スケールを使うと、すべてのスコアが0になるためです。

このフラグは現在、YOLO9の物体検出だけに適用できます。それ以外では事前検査時に NotImplementedErrorが発生します。dataを省略すると警告を表示してcoco8.yamlを 使いますが、8枚の画像は代表的なキャリブレーションセットではありません。PyTorchですでに 量子化したモデルは別の経路を使います。詳しくは量子化を参照してください。

成果物を実行

LibreYOLO経由
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("weights/LibreYOLO9t.onnx")result = model.predict(SAMPLE_IMAGE)print(result.boxes.xyxy[:3])
ONNX Runtimeを直接使用
import numpy as npimport onnximport onnxruntime as ort session = ort.InferenceSession(    "weights/LibreYOLO9t.onnx",    providers=["CPUExecutionProvider"],) # この経路では前処理と後処理を自分で実装batch = np.zeros((1, 3, 640, 640), dtype=np.float32)outputs = session.run(None, {session.get_inputs()[0].name: batch})print([out.shape for out in outputs]) # グラフにファミリー、タスク、クラス名、入力サイズを格納meta = {p.key: p.value for p in onnx.load("weights/LibreYOLO9t.onnx").metadata_props}print(meta["model_family"], meta["task"], meta["imgsz"])

LibreYOLO().onnx拡張子に応じて処理を振り分け、.ptチェックポイントと同じ Resultsオブジェクトを返します。エクスポート時にクラス名、タスク、入力サイズ、姿勢スキーマが グラフのmetadata_propsへ書き込まれているためです。device="auto"では、ONNX Runtimeが 利用可能と報告した場合にCUDAExecutionProviderを使い、それ以外ではCPUへフォールバックします。

2番目のスニペットはLibreYOLOをインストールしていない場合に使います。この経路では前処理、 デコード、NMS、座標の再スケーリングをすべて自分で実装します。メタデータブロックは引き続き 読み取れます。

制約

出力テンソル名はタスクごとに固定されています。メタデータを使わないコンシューマーは、次の名前と 一致させる必要があります。

タスク出力名
物体検出、グリッドヘッド、アンカーヘッドoutput
物体検出、DETR系pred_logitspred_boxes
物体検出、RF-DETRdetslabels
分類output
セマンティックセグメンテーションsemantic_logits
深度depth
サーフェス法線normal
エッジedges
画像復元restored
マッティングmatte
視線yaw_logitspitch_logits

RF-DETRは、入力テンソル名がimagesではなくinputである唯一のファミリーでもあります。

このバージョンでは、いくつかのタスクに固定解像度のランタイム契約があります。深度、 サーフェス法線、エッジではbatch != 1を拒否してdynamic=Falseを強制します。マッティングでは ネイティブの1024正方形を強制します。BiRefNetのSwin相対位置テーブルがその解像度に結び付いて いるためです。画像復元ではReal-ESRGAN以外の全ファミリーで固定キャンバスを強制します。 Real-ESRGANのジェネレーターは完全畳み込み型です。

YOLO9ファミリー、HRNet、NAFNet、Real-ESRGANでは長方形のimgszを使えます。固定の正方形を 必要とするファミリー(clipdeformable_detrdetrdinodetrdfinedeimdeimv2eclwdetrmoge2rtdetrrtdetrv2rtdetrv4rfdetrsiglip2ssd)では長方形を拒否します。

トレース前に拒否される組み合わせは2つあります。YOLO9はLibreYOLOで物体検出だけに対応するため、 YOLO9セグメンテーションは拒否されます。RTMDet-Insセグメンテーションは、動的カーネルによる マスクのデコードにエクスポート先ランタイムの契約がないため拒否されます。

ファミリーとタスクの完全な対応表は エクスポート対応表を参照してください。1つの組み合わせを調べるには、 ライブラリへ直接問い合わせます。

エクスポート前にファミリーとタスクを確認
libreyolo formats --family yolo9 --task detect

devブランチのlibreyolo/export/onnx.py、libreyolo/export/exporter.py、libreyolo/export/support.py、libreyolo/backends/onnx.py、libreyolo/cli/commands/export.pyを参照。