ExecuTorch

ExecuTorchはPyTorchのプログラムをエッジ環境で実行します。LibreYOLOはtorch.exportのstrictモードでモデルをキャプチャし、XNNPACKへローワリングして、.pteプログラムとJSONメタデータサイドカーを1つの単位としてコミットします。

フラグ
export(format="executorch")
出力
.pteプログラム1つと、.pte.jsonのメタデータサイドカー1つ
追加インストール
pip install "libreyolo[executorch]"
再読み込み
LibreYOLO("weights/LibreYOLO9t.pte")
形状
固定。dynamic=Trueとbatch != 1は拒否されます。
精度
FP32のみ。half=Trueとint8=Trueは拒否されます。
デリゲート
XNNPACK、CPU。delegate='xnnpack'だけが受け付けられる値です。

インストール

インストール
# libreyolo[all]からあえて除外# ExecuTorchは組み合わせられるTorchのバージョンを制約するためpip install "libreyolo[executorch]"

この追加パッケージをあえてlibreyolo[all]の外に置いているのは、ExecuTorchが対応する Torchのバージョンを固定してしまい、インストールすると環境全体がその組み合わせに 引きずられるからです。制約を受け入れられる環境にインストールしてください。

Windowsでは、ローワリングの段階でExecuTorchに同梱されるflatc実行ファイルを 呼び出します。PATHに見つからない場合はエクスポートがその旨を伝える RuntimeErrorを送出しますが、Visual Studio 2022のDeveloper PowerShellから 実行すれば解決します。

エクスポート

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9t.pt") # weights/LibreYOLO9t.pteとweights/LibreYOLO9t.pte.jsonを出力path = model.export(format="executorch", imgsz=640)print(path)
CLI
libreyolo export --model LibreYOLO9t.pt --format executorch --imgsz 640
引数
model.export(    format="executorch",    imgsz=640,             # int、または (height, width)    batch=1,               # これ以外の値はValueErrorを送出    dynamic=False,         # TrueはValueErrorを送出    delegate="xnnpack",    # 受け付けられる唯一の値    device="cpu",          # これ以外のデバイスはValueErrorを送出    output_path=None,      # Noneならweights/<stem>.pteに出力)

キャプチャはtorch.export.export(..., strict=True)で、記録したトレースではなく ガード付きの本物のグラフキャプチャです。ホスト側のスカラー読み取りやデータ依存の 制御フローは黙って埋め込まれるのではなく拒否されるため、他の経路ではトレースに 成功するいくつかのファミリーがここで失敗します。理由は組み合わせごとにサポート マトリクスに記録しています。

ローワリングは、XNNPACKパーティショナーを使ってto_edge_transform_and_lowerを 実行します。結果にデリゲートされたパーティションが1つも含まれない場合、ポータブル カーネルだけのプログラムをXNNPACKと名乗らせるのではなく、エクスポートが例外を 送出します。

プログラムとサイドカーは一緒にコミットされます。どちらもステージングし、どちらも 入れ替え、失敗すれば元の状態にロールバックするため、片方だけの中途半端な組み合わせ がディスクに残ることはありません。

成果物を実行する

LibreYOLO経由
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("weights/LibreYOLO9t.pte")result = model.predict(SAMPLE_IMAGE)print(result.boxes.xyxy[:3])
ExecuTorchランタイムを直接使う
import jsonfrom pathlib import Path import torchfrom executorch.runtime import Runtime runtime = Runtime.get()print(runtime.backend_registry.is_available("XnnpackBackend")) program = runtime.load_program(Path("weights/LibreYOLO9t.pte").read_bytes())method = program.load_method("forward") # この経路では前処理と後処理は自分で行うoutputs = method.execute((torch.zeros(1, 3, 640, 640),))print([tensor.shape for tensor in outputs]) meta = json.load(open("weights/LibreYOLO9t.pte.json"))print(meta["model_family"], meta["task"], meta["executorch_delegate"])

LibreYOLO().pteという拡張子で振り分け、チェックポイントと同じResults オブジェクトを返します。読み込み時にサイドカーは必須です:プログラム自体はクラス名も タスクも入力サイズも持たないため、<program>.pte.jsonがないとバックエンドは FileNotFoundErrorを送出します。バックエンドはさらに、読み込みの前に導入済みの ランタイムがXnnpackBackendを提供しているかを確認し、ファイルをマッピングするのでは なくバイト列からプログラムを読み込むため、バックエンドが動いている間ずっとWindowsの ファイルロックを保持せずに済みます。

2つ目のスニペットはランタイムを直接使う経路です。そこでは前処理、デコード、NMS、 座標のスケール戻しは自分で行うことになります。

制約

バッチ1、固定形状、FP32、CPU。batch != 1dynamic=Trueはどちらもエクスポートが 何かを書き換える前にValueErrorを送出し、half=Trueint8=Trueは検証時に拒否 され、CPU以外のデバイスは受け付けられません。

このバージョンでは、delegate"xnnpack"だけを受け付けます。

分類のエクスポートではcrop_pctinterpolationという2つのメタデータキーが追加され、 ランタイムがそのファミリーのリサイズとセンタークロップの方針を再現できます。

ブロックされている項目には、カテゴリではなく具体的な失敗内容が書かれています。 D-FINEの検出とセグメンテーションは、strictキャプチャのもとでdeformable attention内の 未対応なContextVar読み取りに到達し、手動のgrid-sample経路を強制するとシリアライズ まではできるものの、デリゲートされたテンソルの次元順序が不正で実行時に失敗します。 DEIMとDEIMv2はキャプチャ、ローワリング、シリアライズまで進み、実行中に失敗します。 EoMTのセマンティックセグメンテーションは、マスク経路のデータ依存のシンボリック式で 失敗します。BiRefNetのマッティングは1024×1024でキャプチャできますが、 torchvision::deform_conv2dのoutバリアントがありません。SwinIRの復元は再読み込み まではできますが、次元順序の不一致によりaten::alias_copy.outで失敗します。

ファミリーとタスクの全体表は エクスポートマトリクスを参照してください。1つの 組み合わせだけを確認する場合:

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

devブランチのlibreyolo/export/executorch.py、libreyolo/export/exporter.py、libreyolo/export/support.py、libreyolo/backends/executorch.py、pyproject.tomlを読んで確認しました。