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のランナーが必要だという理由から、これらの組み合わせを利用可能として 記録しています。

エクスポート

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9t.pt") # weights/LibreYOLO9t.mlpackageバンドルを出力path = model.export(format="coreml")print(path)
CLI
libreyolo export --model LibreYOLO9t.pt --format coreml
引数
model.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の埋め込み

AppleのNMSレイヤーを埋め込む
from libreyolo import LibreYOLO # YOLOXとYOLO9の検出のみ batch 1LibreYOLO("LibreYOLO9t.pt").export(    format="coreml",    nms=True,    conf=0.25,    iou=0.45,)
CLI
libreyolo export --model LibreYOLO9t.pt --format coreml --nms \  --conf 0.25 --iou 0.45

nms=Trueにすると、AppleのNonMaximumSuppressionレイヤーで終わるCore MLパイプラインで モデルを包みます。出力は2つあります:形状がN×クラス数のconfidenceと、正規化された xywhで形状がN×4のcoordinatesです。

対象はYOLOXとYOLO9の検出のみで、バッチサイズは1である必要があります。集合予測はクエリと クラスにわたるtop-kを取るだけでIoUの段階がなく、そのレイヤーを使えないため、DETR系の ファミリーは名前で拒否されます。max_detもここでは公開されていません。検出数の上限が 問題になる場合は、代わりにONNXのNMS埋め込みを使ってください。

成果物を実行する

macOSでLibreYOLOから実行
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])
coremltools単体
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だけで、 allcpu_and_gpucpu_and_necpu_onlyを指定できます。Core MLは代わりに コンピュートユニットを通して処理を振り分けるため、device引数は無視されます。

2つ目のスニペットはランタイムを直接使う経路です。この経路ではレターボックス処理、デコード、 NMS、座標のスケール戻しは自前の作業になり、クラス名はuser_defined_metadataにあります。

制約

対応は4つのファミリーで、検出のみです:yoloxyolo9rtdetrrfdetr。ファミリーを 把握した前処理ラッパーがあってはじめて固定の画像入力という契約が正しくなり、対象外の ファミリーでは誤った正規化で変換されてしまうため、それ以外は事前チェックで拒否されます。 エラーメッセージは代替としてONNXとTorchScriptを挙げます。

入力形状はct.ImageTypeによって固定されるため、dynamic=Trueを指定しても何も変わらず、 メタデータにはdynamic=Falseが記録されます。別の解像度が必要なら、もう1つバンドルを エクスポートしてください。

half=TrueはFP16の演算精度で変換します。このエクスポーターにINT8の経路はありません。

ファミリーとタスクの全体表はエクスポート対応表を 参照してください。Appleのより新しいオンデバイス形式については Core AIを参照してください。1つの組み合わせを確認するには:

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

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