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から
実行すれば解決します。
エクスポート
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9t.pt") # weights/LibreYOLO9t.pteとweights/LibreYOLO9t.pte.jsonを出力path = model.export(format="executorch", imgsz=640)print(path)libreyolo export --model LibreYOLO9t.pt --format executorch --imgsz 640model.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と名乗らせるのではなく、エクスポートが例外を
送出します。
プログラムとサイドカーは一緒にコミットされます。どちらもステージングし、どちらも 入れ替え、失敗すれば元の状態にロールバックするため、片方だけの中途半端な組み合わせ がディスクに残ることはありません。
成果物を実行する
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("weights/LibreYOLO9t.pte")result = model.predict(SAMPLE_IMAGE)print(result.boxes.xyxy[:3])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 != 1とdynamic=Trueはどちらもエクスポートが
何かを書き換える前にValueErrorを送出し、half=Trueとint8=Trueは検証時に拒否
され、CPU以外のデバイスは受け付けられません。
このバージョンでは、delegateは"xnnpack"だけを受け付けます。
分類のエクスポートではcrop_pctとinterpolationという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つの 組み合わせだけを確認する場合:
libreyolo formats --family yolo9 --task detect