Core ML
Core MLはAppleのオンデバイス向けモデル形式です。LibreYOLOはファミリーごとの前処理ラッパーの背後で検出モデルをトレースし、変換後のグラフが常に統一されたRGB画像入力を受け取るようにしたうえで、モデルのメタデータを添えたML Program形式の.mlpackageを書き出します。
- フラグ
export(format="coreml")- 出力
- ML Program形式の.mlpackageバンドル(ディレクトリ)を1つ
- 追加インストール
pip install "libreyolo[coreml]"- 再読み込み
LibreYOLO("weights/LibreYOLO9t.mlpackage") on macOS- 形状
- 固定です。入力は形状が固定されたct.ImageTypeです。
- 数値精度
- FP32、FP16(half=True)。INT8はありません。
- ファミリー
- 検出タスクのみ。yolox、yolo9、rtdetr、rfdetrが対象
インストール
pip install "libreyolo[coreml]"推論にはmacOSが必要です。LibreYOLO()はそれ以外のプラットフォームでは.mlpackageを
拒否して現在のプラットフォーム名を含むメッセージを返し、対応表は、ランタイムの同等性を
確認するにはmacOSのランナーが必要だという理由から、これらの組み合わせを利用可能として
記録しています。
エクスポート
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9t.pt") # weights/LibreYOLO9t.mlpackageバンドルを出力path = model.export(format="coreml")print(path)libreyolo export --model LibreYOLO9t.pt --format coremlmodel.export( format="coreml", imgsz=640, batch=1, half=False, # TrueにするとFLOAT16の演算精度で変換 compute_units="all", # all | cpu_and_gpu | cpu_and_ne | cpu_only output_path=None, # Noneならweights/<stem>.mlpackageに出力) # dynamicは受け付けられるが入力は固定形状のct.ImageType# 埋め込みメタデータはどちらの場合もdynamic=Falseを記録バンドルはチェックポイントのステム名でweights/に出力され、half=Trueのときは_fp16が
付きます。.mlpackageはディレクトリなので、ツリー全体をコピーしてください。
どのファミリーも前処理ラッパーの背後でトレースされるため、変換後のグラフが受け取る入力は
1つに統一されます:RGB、scale=1/255、バイアスなし、ct.ImageTypeとしての宣言。
ラッパーはファミリーごとの流儀、すなわちYOLOXでは0〜255の範囲のBGR、RF-DETRではImageNetの
平均と標準偏差、YOLO9とRT-DETRでは恒等変換を吸収します。Core MLの利用側がファミリー固有の
テンソルではなく普通の画像を渡せるのは、このためです。
変換先はML Programで、最小デプロイターゲットはiOS 15です。compute_unitsは変換後のモデルに
保存され、成果物を読み込むときに再度上書きできます。
モデルのメタデータは文字列としてuser_defined_metadataに入り、バックエンドはそこから
ファミリー、タスク、クラス名、入力サイズ、姿勢スキーマを読み取ります。
NMSの埋め込み
from libreyolo import LibreYOLO # YOLOXとYOLO9の検出のみ batch 1LibreYOLO("LibreYOLO9t.pt").export( format="coreml", nms=True, conf=0.25, iou=0.45,)libreyolo export --model LibreYOLO9t.pt --format coreml --nms \ --conf 0.25 --iou 0.45nms=Trueにすると、AppleのNonMaximumSuppressionレイヤーで終わるCore MLパイプラインで
モデルを包みます。出力は2つあります:形状がN×クラス数のconfidenceと、正規化された
xywhで形状がN×4のcoordinatesです。
対象はYOLOXとYOLO9の検出のみで、バッチサイズは1である必要があります。集合予測はクエリと
クラスにわたるtop-kを取るだけでIoUの段階がなく、そのレイヤーを使えないため、DETR系の
ファミリーは名前で拒否されます。max_detもここでは公開されていません。検出数の上限が
問題になる場合は、代わりにONNXのNMS埋め込みを使ってください。
成果物を実行する
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO( "weights/LibreYOLO9t.mlpackage", compute_units="all", # Neural Engineに固定するならcpu_and_ne)result = model.predict(SAMPLE_IMAGE)print(result.boxes.xyxy[:3])import coremltools as ctfrom PIL import Image mlmodel = ct.models.MLModel("weights/LibreYOLO9t.mlpackage")print(mlmodel.user_defined_metadata["model_family"])print(mlmodel.user_defined_metadata["names"]) # 入力はエクスポート時の固定サイズで "image" という名前の画像image = Image.open(SAMPLE_IMAGE).convert("RGB").resize((640, 640))out = mlmodel.predict({"image": image})print({name: value.shape for name, value in out.items()}) # この経路ではレターボックス処理と後処理は自前で行うLibreYOLO()は.mlpackageという拡張子のディレクトリを認識し、チェックポイントと同じ
Resultsオブジェクトを返します。この形式でファクトリが受け渡す引数はcompute_unitsだけで、
all、cpu_and_gpu、cpu_and_ne、cpu_onlyを指定できます。Core MLは代わりに
コンピュートユニットを通して処理を振り分けるため、device引数は無視されます。
2つ目のスニペットはランタイムを直接使う経路です。この経路ではレターボックス処理、デコード、
NMS、座標のスケール戻しは自前の作業になり、クラス名はuser_defined_metadataにあります。
制約
対応は4つのファミリーで、検出のみです:yolox、yolo9、rtdetr、rfdetr。ファミリーを
把握した前処理ラッパーがあってはじめて固定の画像入力という契約が正しくなり、対象外の
ファミリーでは誤った正規化で変換されてしまうため、それ以外は事前チェックで拒否されます。
エラーメッセージは代替としてONNXとTorchScriptを挙げます。
入力形状はct.ImageTypeによって固定されるため、dynamic=Trueを指定しても何も変わらず、
メタデータにはdynamic=Falseが記録されます。別の解像度が必要なら、もう1つバンドルを
エクスポートしてください。
half=TrueはFP16の演算精度で変換します。このエクスポーターにINT8の経路はありません。
ファミリーとタスクの全体表はエクスポート対応表を 参照してください。Appleのより新しいオンデバイス形式については Core AIを参照してください。1つの組み合わせを確認するには:
libreyolo formats --family yolo9 --task detect