OpenVINO™ を使用した YOLOv8 インスタンスのセグメント化モデルの変換と最適化#
この Jupyter ノートブックはオンラインで起動でき、ブラウザーのウィンドウで対話型環境を開きます。ローカルにインストールすることもできます。次のオプションのいずれかを選択します:
インスタンスのセグメント化は、オブジェクト検出よりも一歩進んで、画像内の個々のオブジェクトを識別し、それらを画像の残りの部分からセグメント化します。オブジェクト検出としてのインスタンスのセグメント化は、コンピューター・ビジョン・システムの主要コンポーネントとしてよく使用されます。リアルタイム・インスタンスのセグメント化モデルを使用するアプリケーションには、ビデオ分析、ロボット工学、自律走行車、複数オブジェクトの追跡とオブジェクトのカウント、医療画像分析などがあります。
このチュートリアルでは、OpenVINO を使用して PyTorch YOLOv8 を実行および最適化する手順を段階的に説明します。インスタンスのセグメント化シナリオに必要な手順を検討します。
このチュートリアルは次のステップで構成されます: - PyTorch モデルの準備。- データセットをダウンロードして準備します。- 元のモデルを検証します。- PyTorch モデルを OpenVINO IR に変換します。- 変換されたモデルを検証します。- 最適化パイプラインの準備と実行- FP32 モデルと量子化モデルのパフォーマンスを比較します。- FP32 モデルと量子化モデルの精度を比較します。- ライブデモ
目次:
PyTorch モデルを取得#
一般に、PyTorch モデルは、モデルの重みを含む状態辞書によって初期化された torch.nn.Module クラスのインスタンスを表します。このリポジトリーで入手可能な、COCO データセットで事前トレーニングされた YOLOv8 nano モデル (yolov8n とも呼ばれる) を使用します。この手順は他の YOLOv8 モデルにも適用できます。事前トレーニングされたモデルを取得する一般的な手順は次のとおりです。
- クラスのインスタンスを作成します。
- 事前トレーニングされたモデルの重みを含むチェックポイント状態辞書をロードします。
- 一部の操作を推論モードに切り替えるためモデルを評価に切り替えます。
この場合、モデルの作成者は、YOLOv8 モデルを ONNX に変換してから OpenVINO IR に変換できる API を提供します。したがって、これらの手順を手動で実行する必要はありません。
必要条件#
必要なパッケージをインストールします。
%pip install -q "openvino>=2024.0.0" "nncf>=2.9.0"
%pip install -q "torch>=2.1" "torchvision>=0.16" "ultralytics==8.2.24" onnx opencv-python tqdm --extra-index-url https://download.pytorch.org/whl/cpu必要なユーティリティー関数をインポートします。下のセルは、GitHub から notebook_utils Python モジュールをダウンロードします。
from pathlib import Path
# `notebook_utils` モジュールを取得
import requests
r = requests.get(
url="https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py",
)
open("notebook_utils.py", "w").write(r.text)
from notebook_utils import download_file# テストサンプルをダウンロード
IMAGE_PATH = Path("./data/coco_bike.jpg")
download_file(
url="https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/image/coco_bike.jpg",
filename=IMAGE_PATH.name,
directory=IMAGE_PATH.parent,
)data/coco_bike.jpg: 0%| | 0.00/182k [00:00<?, ?B/s]PosixPath('/home/maleksandr/test_notebooks/update_ultralytics/openvino_notebooks/notebooks/yolov8-optimization/data/coco_bike.jpg')モデルのインスタンス化#
モデルをロードするには、モデル・チェックポイントへのパスを指定する必要があります。モデルハブで使用可能なローカルパスまたは名前にすることができます (この場合、モデル・チェックポイントは自動的にダウンロードされます)。
予測を行う際、モデルは入力画像へのパスを受け入れ、Results クラス・オブジェクトを含むリストを返します。結果には、オブジェクト検出モデルのボックスと、セグメント化モデルのボックスとマスクが含まれます。また、描画用の plot() メソッドなど、結果を処理するユーティリティーも含まれています。
例を考えてみましょう:
models_dir = Path("./models")
models_dir.mkdir(exist_ok=True)from PIL import Image
from ultralytics import YOLO
SEG_MODEL_NAME = "yolov8n-seg"
seg_model = YOLO(models_dir / f"{}.pt")
label_map = seg_model.model.names
res = seg_model(IMAGE_PATH)
Image.fromarray(res[0].plot()[:, :, ::-1])Downloading ultralytics/assets to 'models/yolov8n-seg.pt'...
100%|██████████████████████████████████████████████████████████████████████████████| 6.73M/6.73M [00:01<00:00, 3.89MB/s]image 1/1 /home/maleksandr/test_notebooks/update_ultralytics/openvino_notebooks/notebooks/yolov8-optimization/data/coco_bike.jpg: 480x640 1 bicycle, 2 cars, 1 dog, 55.6ms
Speed: 1.8ms preprocess, 55.6ms inference, 2.0ms postprocess per image at shape (1, 3, 480, 640)
YOLOv8 は、OpenVINO IR を含むさまざまな形式にモデルをエクスポートする便利な API を提供します。model.export はモデルの変換を担当します。形式を指定する必要があり、さらにモデル内の動的な形状を保持することもできます。
# インスタンスのセグメント化モデル
seg_model_path = models_dir / f"{SEG_MODEL_NAME}_openvino_model/{SEG_MODEL_NAME}.xml"
if not seg_model_path.exists():
seg_model.export(format="openvino", dynamic=True, half=True)Ultralytics YOLOv8.1.42 🚀 Python-3.10.12 torch-2.2.2+cpu CPU (Intel Core(TM) i9-10980XE 3.00GHz)
PyTorch: starting from 'models/yolov8n-seg.pt' with input shape (1, 3, 640, 640) BCHW and output shape(s) ((1, 116, 8400), (1, 32, 160, 160)) (6.7 MB)
OpenVINO: starting export with openvino 2024.0.0-14509-34caeefd078-releases/2024/0...
OpenVINO: export success ✅ 1.8s, saved as 'models/yolov8n-seg_openvino_model/' (6.9 MB)
Export complete (3.0s)
Results saved to
/home/maleksandr/test_notebooks/update_ultralytics/openvino_notebooks/notebooks/yolov8-optimization/models
Predict: yolo predict task=segment model=models/yolov8n-seg_openvino_model imgsz=640 half
Validate: yolo val task=segment model=models/yolov8n-seg_openvino_model imgsz=640 data=coco.yaml half
Visualize: https://netron.app推論に IR モデルを使用する方法を置き換えるだけで、前処理と後処理にベース・モデル・パイプラインを再利用できます。
OpenVINO を使用して推論を実行するデバイスをドロップダウン・リストから選択します。
import ipywidgets as widgets
import openvino as ov
core = ov.Core()
device = widgets.Dropdown(
options=core.available_devices + ["AUTO"],
value="AUTO",
description="Device:",
disabled=False,
)
deviceDropdown(description='Device:', index=1, options=('CPU', 'AUTO'), value='AUTO')core = ov.Core()
seg_ov_model = core.read_model(seg_model_path)
ov_config = {}
if device.value != "CPU":
seg_ov_model.reshape({0: [1, 3, 640, 640]})
if "GPU" in device.value or ("AUTO" in device.value and "GPU" in core.available_devices):
ov_config = {"GPU_DISABLE_WINOGRAD_CONVOLUTION": "YES"}
seg_compiled_model = core.compile_model(seg_ov_model, device.value, ov_config)import torch
def infer(*args):
result = seg_compiled_model(args)
return torch.from_numpy(result[0]), torch.from_numpy(result[1])
seg_model.predictor.inference = infer
seg_model.predictor.model.pt = Falseres = seg_model(IMAGE_PATH)
Image.fromarray(res[0].plot()[:, :, ::-1])image 1/1 /home/maleksandr/test_notebooks/update_ultralytics/openvino_notebooks/notebooks/yolov8-optimization/data/coco_bike.jpg: 640x640 1 bicycle, 2 cars, 1 dog, 27.6ms
Speed: 3.5ms preprocess, 27.6ms inference, 4.5ms postprocess per image at shape (1, 3, 640, 640)
これで完了です! 結果は、元のモデルで生成されたものと同じです。
データセット上のモデルの精度をチェック#
最適化されたモデルの結果を元のモデルと比較するには、検証データセットでのモデルの精度に関する測定可能な結果をいくつか知っておくとよいでしょう。
YOLOv8 は COCO データセットで事前トレーニングされているため、モデルの精度を評価するにはそれをダウンロードする必要があります。YOLOv8 リポジトリーで提供されている指示に従って、元のモデル評価関数で使用するため、モデルの作成者が使用した形式でアノテーションをダウンロードする必要もあります。
注: 最初のデータセットのダウンロードが完了するまでに数分かかる場合があります。ダウンロード速度はインターネット接続の品質によって異なります。
from zipfile import ZipFile
from ultralytics.data.utils import DATASETS_DIR
DATA_URL = "http://images.cocodataset.org/zips/val2017.zip"
LABELS_URL = "https://github.com/ultralytics/yolov5/releases/download/v1.0/coco2017labels-segments.zip"
CFG_URL =
"https://raw.githubusercontent.com/ultralytics/ultralytics/v8.1.0/ultralytics/cfg/datasets/coco.yaml"
OUT_DIR = DATASETS_DIR
DATA_PATH = OUT_DIR / "val2017.zip"
LABELS_PATH = OUT_DIR / "coco2017labels-segments.zip"
CFG_PATH = OUT_DIR / "coco.yaml"
download_file(DATA_URL, DATA_PATH.name, DATA_PATH.parent)
download_file(LABELS_URL, LABELS_PATH.name, LABELS_PATH.parent)
download_file(CFG_URL, CFG_PATH.name, CFG_PATH.parent)
if not (OUT_DIR / "coco/labels").exists():
with ZipFile(LABELS_PATH, "r") as zip_ref:
zip_ref.extractall(OUT_DIR)
with ZipFile(DATA_PATH, "r") as zip_ref:
zip_ref.extractall(OUT_DIR / "coco/images")import numpy as np
from tqdm.notebook import tqdm
from ultralytics.utils.metrics import ConfusionMatrix
def test(
model: ov.Model,
core: ov.Core,
data_loader: torch.utils.data.DataLoader,
validator,
num_samples: int = None,
):
"""
OpenVINO YOLOv8 model accuracy validation function.Runs model validation on dataset and returns metrics
Parameters:
model (Model): OpenVINO model
data_loader (torch.utils.data.DataLoader): dataset loader
validator: instance of validator class
num_samples (int, *optional*, None): validate model only on specified number samples, if provided
Returns:
stats: (Dict[str, float]) - dictionary with aggregated accuracy metrics statistics, key is metric name, value is metric value
"""
validator.seen = 0
validator.jdict = []
validator.stats = dict(tp_m=[], tp=[], conf=[], pred_cls=[], target_cls=[])
validator.batch_i = 1
validator.confusion_matrix = ConfusionMatrix(nc=validator.nc)
model.reshape({0: [1, 3, -1, -1]})
num_outputs = len(model.outputs)
compiled_model = core.compile_model(model)
for batch_i, batch in enumerate(tqdm(data_loader, total=num_samples)):
if num_samples is not None and batch_i == num_samples:
break
batch = validator.preprocess(batch)
results = compiled_model(batch["img"])
if num_outputs == 1:
preds = torch.from_numpy(results[compiled_model.output(0)])
else:
preds = [
torch.from_numpy(results[compiled_model.output(0)]),
torch.from_numpy(results[compiled_model.output(1)]),
]
preds = validator.postprocess(preds)
validator.update_metrics(preds, batch)
stats = validator.get_stats()
return stats
def print_stats(stats: np.ndarray, total_images: int, total_objects: int):
"""
Helper function for printing accuracy statistic
Parameters:
stats: (Dict[str, float]) - dictionary with aggregated accuracy metrics statistics, key is metric name, value is metric value
total_images (int) - number of evaluated images
total objects (int)
Returns:
None
"""
print("Boxes:")
mp, mr, map50, mean_ap = (
stats["metrics/precision(B)"],
stats["metrics/recall(B)"],
stats["metrics/mAP50(B)"],
stats["metrics/mAP50-95(B)"],
)
# 結果をプリント
print(" Best mean average:")
s = ("%20s" + "%12s" * 6) % (
"Class",
"Images",
"Labels",
"Precision",
"Recall",
"mAP@.5",
"mAP@.5:.95",
)
print(s)
pf = "%20s" + "%12i" * 2 + "%12.3g" * 4 # プリント形式
print(pf % ("all", total_images, total_objects, mp, mr, map50, mean_ap))
if "metrics/precision(M)" in stats:
s_mp, s_mr, s_map50, s_mean_ap = (
stats["metrics/precision(M)"],
stats["metrics/recall(M)"],
stats["metrics/mAP50(M)"],
stats["metrics/mAP50-95(M)"],
)
# 結果をプリント
print(" Macro average mean:")
s = ("%20s" + "%12s" * 6) % (
"Class",
"Images",
"Labels",
"Precision",
"Recall",
"mAP@.5",
"mAP@.5:.95",
)
print(s)
pf = "%20s" + "%12i" * 2 + "%12.3g" * 4 # プリント形式
print(pf % ("all", total_images, total_objects, s_mp, s_mr, s_map50, s_mean_ap))元のモデル・リポジトリーは、精度検証パイプラインを表す Validator ラッパーを使用します。データローダーと評価メトリックを作成し、データローダーによって生成される各データバッチのメトリックを更新します。加えて、データの前処理と結果の後処理も担当します。クラスの初期化では、構成を提供する必要があります。デフォルトの設定を使用しますが、カスタムデータでテストするにはオーバーライドするいくつかのパラメーターに置き換えることができます。モデルは ValidatorClass メソッドに接続しており、これによりバリデーター・クラスのインスタンスが作成されます。
from ultralytics.utils import DEFAULT_CFG
from ultralytics.cfg import get_cfg
from ultralytics.data.converter import coco80_to_coco91_class
from ultralytics.data.utils import check_det_dataset
from ultralytics.utils import ops
args = get_cfg(cfg=DEFAULT_CFG)
args.data = str(CFG_PATH)seg_validator = seg_model.task_map[seg_model.task]["validator"](args=args)
seg_validator.data = check_det_dataset(args.data)
seg_validator.stride = 32
seg_data_loader = seg_validator.get_dataloader(OUT_DIR / "coco/", 1)
seg_validator.is_coco = True
seg_validator.class_map = coco80_to_coco91_class()
seg_validator.names = seg_model.model.names
seg_validator.metrics.names = seg_validator.names
seg_validator.nc = seg_model.model.model[-1].nc
seg_validator.nm = 32
seg_validator.process = ops.process_mask
seg_validator.plot_masks = []val: Scanning /home/maleksandr/test_notebooks/ultrali/datasets/coco/labels/val2017.cache...4952 images, 48 backgrounds,テスト関数の定義と評価器の作成が完了したら、精度メトリックを取得する準備が整います > 注: モデル評価は時間のかかるプロセスであり、ハードウェアによっては数分かかる場合があります。計算時間を短縮するために、評価サブセットのサイズで num_samples パラメーターを定義しますが、この場合、検証サブセットの違いにより、モデルの作成者が最初に報告した精度と比較できない可能性があります。完全なデータセットでモデルを検証するには、``NUM_TEST_SAMPLES = None`` を設定します。
NUM_TEST_SAMPLES = 300fp_seg_stats = test(seg_ov_model, core, seg_data_loader, seg_validator, num_samples=NUM_TEST_SAMPLES)0%| | 0/300 [00:00<?, ?it/s]print_stats(fp_seg_stats, seg_validator.seen, seg_validator.nt_per_class.sum())Boxes:
Best mean average:
Class Images Labels Precision Recall mAP@.5 mAP@.5:.95
all 300 2145 0.609 0.521 0.58 0.416
Macro average mean:
Class Images Labels Precision Recall mAP@.5 mAP@.5:.95
all 300 2145 0.605 0.501 0.558 0.353print_stats は、次の精度メトリックのリストを報告します:
Precisionは、関連するオブジェクトのみを識別するモデルの正確さの度合いです。Recallは、モデルがすべてのグラウンド・トゥルース・オブジェクトを検出する能力を測定します。mAP@t- 平均精度は、データセット内のすべてのクラスにわたって集計された適合率と再現率曲線下の領域として表されます。ここで、tは交差ユニオン (IOU) しきい値、つまり、グラウンドの真実と予測されたオブジェクト間の重複の度合いです。したがって、mAP@.5は、平均精度が 0.5 IOU しきい値で計算されたことを示します。mAP@.5:.95は、ステップ 0.05 で 0.5 から 0.95 までの範囲の IOU しきい値で計算されます。
NNCF トレーニング後の量子化 API を使用してモデルを最適化#
NNCF は、精度の低下を最小限に抑えながら、OpenVINO でニューラル・ネットワーク推論を最適化する一連の高度なアルゴリズムを提供します。YOLOv8 を最適化するため、ポストトレーニング・モード (微調整パイプラインなし) で 8 ビット量子化を使用します。
最適化プロセスには次の手順が含まれます:
量子化用のデータセットを作成します。
nncf.quantizeを実行して、最適化されたモデルを取得します。openvino.runtime.serialize関数を使用して、OpenVINO IR モデルをシリアル化します。
モデルの推論速度を向上させるため量子化を実行するかどうかを以下で選択してください。
import ipywidgets as widgets
int8_model_seg_path = models_dir / f"{SEG_MODEL_NAME}_openvino_int8_model/{SEG_MODEL_NAME}.xml"
to_quantize = widgets.Checkbox(
value=True,
description="Quantization",
disabled=False,
)
to_quantizeCheckbox(value=True, description='Quantization')to_quantize が選択されていない場合に量子化をスキップする skip magic 拡張機能をロードします
# `skip_kernel_extension` モジュールを取得
import requests
r = requests.get(
url="https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/skip_kernel_extension.py",
)
open("skip_kernel_extension.py", "w").write(r.text)
%load_ext skip_kernel_extension量子化の精度テストで検証データローダーを再利用します。そのため、nncf.Dataset オブジェクトにラップし、入力テンソルのみを取得する変換関数を定義する必要があります。
%%skip not $to_quantize.value
import nncf
from typing import Dict
def transform_fn(data_item:Dict):
"""
Quantization transform function.Extracts and preprocess input data from dataloader item for quantization.Parameters:
data_item: Dict with data item produced by DataLoader during iteration
Returns:
input_tensor: Input data for quantization
"""
input_tensor = seg_validator.preprocess(data_item)['img'].numpy()
return input_tensor
quantization_dataset = nncf.Dataset(seg_data_loader, transform_fn)INFO:nncf:NNCF initialized successfully.Supported frameworks detected: torch, onnx, openvinonncf.quantize 関数は、モデル量子化のインターフェイスを提供します。OpenVINO モデルのインスタンスと量子化データセットが必要です。オプションで、量子化プロセスの追加パラメーター (量子化のサンプル数、プリセット、無視される範囲など) を提供できます。YOLOv8 モデルには、活性化の非対称量子化を必要とする非 ReLU 活性化関数が含まれています。さらに良い結果を得るため、mixed 量子化プリセットを使用します。これは、重みの対称量子化と活性化の非対称量子化を提供します。より正確な結果を得るには、ignored_scope パラメーターを使用して、後処理サブグラフの操作を浮動小数点精度に保つ必要があります。
注: モデルのトレーニング後の量子化は時間のかかるプロセスです。ハードウェアによっては数分かかる場合があります。
%%skip not $to_quantize.value
ignored_scope = nncf.IgnoredScope(
names=[
"__module.model.22.cv3.0.0.conv/aten::_convolution/Convolution", # 後処理サブグラフ内
"__module.model.22.proto.cv1.conv/aten::_convolution/Convolution",
"__module.model.22.cv4.0.0.conv/aten::_convolution/Convolution",
"__module.model.16.conv/aten::_convolution/Convolution",
"__module.model.22.cv2.0.0.conv/aten::_convolution/Convolution",
"__module.model.6.cv1.conv/aten::_convolution/Convolution",
"__module.model.22.cv3.1.1.conv/aten::_convolution/Convolution",
"__module.model.21.cv2.conv/aten::_convolution/Convolution",
"__module.model.21.m.0.cv1.conv/aten::_convolution/Convolution",
"__module.model.22/aten::add/Add_6",
"__module.model.22/aten::sub/Subtract",
"__module.model.7.conv/aten::_convolution/Convolution",
"__module.model.12.cv1.conv/aten::_convolution/Convolution",
"__module.model.4.cv1.conv/aten::_convolution/Convolution",
"__module.model.22.cv2.2.1.conv/aten::_convolution/Convolution",
"__module.model.22.cv2.0.1.conv/aten::_convolution/Convolution",
"__module.model.22.cv4.2.1.conv/aten::_convolution/Convolution",
"__module.model.22.dfl.conv/aten::_convolution/Convolution",
"__module.model.22.cv3.2.2/aten::_convolution/Convolution",
"__module.model.22.cv3.0.2/aten::_convolution/Convolution",
"__module.model.15.cv1.conv/aten::_convolution/Convolution",
"__module.model.5.conv/aten::_convolution/Convolution",
"__module.model.0.conv/aten::_convolution/Convolution"
]
)
# セグメント化モデル
quantized_seg_model = nncf.quantize(
seg_ov_model,
quantization_dataset,
preset=nncf.QuantizationPreset.MIXED,
ignored_scope=ignored_scope
)INFO:nncf:23 ignored nodes were found by name in the NNCFGraph
INFO:nncf:Not adding activation input quantizer for operation: 1 __module.model.0.conv/aten::_convolution/Convolution
2 __module.model.0.conv/aten::_convolution/Add
3 __module.model.22.cv4.2.1.act/aten::silu_/Swish
INFO:nncf:Not adding activation input quantizer for operation: 25 __module.model.4.cv1.conv/aten::_convolution/Convolution
26 __module.model.4.cv1.conv/aten::_convolution/Add
27 __module.model.22.cv4.2.1.act/aten::silu_/Swish_7
INFO:nncf:Not adding activation input quantizer for operation: 43 __module.model.5.conv/aten::_convolution/Convolution
47 __module.model.5.conv/aten::_convolution/Add
51 __module.model.22.cv4.2.1.act/aten::silu_/Swish_13
INFO:nncf:Not adding activation input quantizer for operation: 54 __module.model.6.cv1.conv/aten::_convolution/Convolution
56 __module.model.6.cv1.conv/aten::_convolution/Add
59 __module.model.22.cv4.2.1.act/aten::silu_/Swish_14
INFO:nncf:Not adding activation input quantizer for operation: 98 __module.model.7.conv/aten::_convolution/Convolution
107 __module.model.7.conv/aten::_convolution/Add
116 __module.model.22.cv4.2.1.act/aten::silu_/Swish_20
INFO:nncf:Not adding activation input quantizer for operation: 106 __module.model.12.cv1.conv/aten::_convolution/Convolution
115 __module.model.12.cv1.conv/aten::_convolution/Add
123 __module.model.22.cv4.2.1.act/aten::silu_/Swish_27
INFO:nncf:Not adding activation input quantizer for operation: 46 __module.model.15.cv1.conv/aten::_convolution/Convolution
50 __module.model.15.cv1.conv/aten::_convolution/Add
53 __module.model.22.cv4.2.1.act/aten::silu_/Swish_31
INFO:nncf:Not adding activation input quantizer for operation: 74 __module.model.16.conv/aten::_convolution/Convolution
83 __module.model.16.conv/aten::_convolution/Add
92 __module.model.22.cv4.2.1.act/aten::silu_/Swish_42
INFO:nncf:Not adding activation input quantizer for operation: 75 __module.model.22.cv2.0.0.conv/aten::_convolution/Convolution
84 __module.model.22.cv2.0.0.conv/aten::_convolution/Add
93 __module.model.22.cv4.2.1.act/aten::silu_/Swish_38
INFO:nncf:Not adding activation input quantizer for operation: 102 __module.model.22.cv2.0.1.conv/aten::_convolution/Convolution
111 __module.model.22.cv2.0.1.conv/aten::_convolution/Add
119 __module.model.22.cv4.2.1.act/aten::silu_/Swish_39
INFO:nncf:Not adding activation input quantizer for operation: 76 __module.model.22.cv3.0.0.conv/aten::_convolution/Convolution
85 __module.model.22.cv3.0.0.conv/aten::_convolution/Add
94 __module.model.22.cv4.2.1.act/aten::silu_/Swish_40
INFO:nncf:Not adding activation input quantizer for operation: 127 __module.model.22.cv3.0.2/aten::_convolution/Convolution
134 __module.model.22.cv3.0.2/aten::_convolution/Add
INFO:nncf:Not adding activation input quantizer for operation: 77 __module.model.22.cv4.0.0.conv/aten::_convolution/Convolution
86 __module.model.22.cv4.0.0.conv/aten::_convolution/Add
95 __module.model.22.cv4.2.1.act/aten::silu_/Swish_60
INFO:nncf:Not adding activation input quantizer for operation: 78 __module.model.22.proto.cv1.conv/aten::_convolution/Convolution
87 __module.model.22.proto.cv1.conv/aten::_convolution/Add
96 __module.model.22.cv4.2.1.act/aten::silu_/Swish_35
INFO:nncf:Not adding activation input quantizer for operation: 234 __module.model.22.cv3.1.1.conv/aten::_convolution/Convolution
247 __module.model.22.cv3.1.1.conv/aten::_convolution/Add
258 __module.model.22.cv4.2.1.act/aten::silu_/Swish_50
INFO:nncf:Not adding activation input quantizer for operation: 289 __module.model.21.m.0.cv1.conv/aten::_convolution/Convolution
296 __module.model.21.m.0.cv1.conv/aten::_convolution/Add
301 __module.model.22.cv4.2.1.act/aten::silu_/Swish_53
INFO:nncf:Not adding activation input quantizer for operation: 295 __module.model.21.cv2.conv/aten::_convolution/Convolution
300 __module.model.21.cv2.conv/aten::_convolution/Add
304 __module.model.22.cv4.2.1.act/aten::silu_/Swish_55
INFO:nncf:Not adding activation input quantizer for operation: 331 __module.model.22.cv2.2.1.conv/aten::_convolution/Convolution
339 __module.model.22.cv2.2.1.conv/aten::_convolution/Add
344 __module.model.22.cv4.2.1.act/aten::silu_/Swish_57
INFO:nncf:Not adding activation input quantizer for operation: 333 __module.model.22.cv4.2.1.conv/aten::_convolution/Convolution
341 __module.model.22.cv4.2.1.conv/aten::_convolution/Add
346 __module.model.22.cv4.2.1.act/aten::silu_/Swish_65
INFO:nncf:Not adding activation input quantizer for operation: 349 __module.model.22.cv3.2.2/aten::_convolution/Convolution
353 __module.model.22.cv3.2.2/aten::_convolution/Add
INFO:nncf:Not adding activation input quantizer for operation: 243
__module.model.22.dfl.conv/aten::_convolution/Convolution
INFO:nncf:Not adding activation input quantizer for operation: 263
__module.model.22/aten::sub/Subtract
INFO:nncf:Not adding activation input quantizer for operation: 264
__module.model.22/aten::add/Add_6Output()/home/maleksandr/test_notebooks/update_ultralytics/openvino_notebooks/notebooks/yolov8-optimization/venv/lib/python3.10/site-packages/nncf/experimental/tensor/tensor.py:84: RuntimeWarning: invalid value encountered in multiply
return Tensor(self.data * unwrap_tensor_data(other))Output()%%skip not $to_quantize.value
print(f"Quantized segmentation model will be saved to {int8_model_seg_path}")
ov.save_model(quantized_seg_model, str(int8_model_seg_path))Quantized segmentation model will be saved to models/yolov8n-seg_openvino_int8_model/yolov8n-seg.xml予測を行うためデバイスにロードするのに適した OpenVINO モデル・クラス・インスタンスを返します。INT8 モデルの入力データと出力結果の形式は、浮動小数点モデルの表現と違いはありません。したがって、画像上の INT8 モデル結果を取得するために、上で定義した同じ検出関数を再利用できます。
%%skip not $to_quantize.value
device%%skip not $to_quantize.value
ov_config = {}
if device.value != "CPU":
quantized_seg_model.reshape({0: [1, 3, 640, 640]})
if "GPU" in device.value or ("AUTO" in device.value and "GPU" in core.available_devices):
ov_config = {"GPU_DISABLE_WINOGRAD_CONVOLUTION": "YES"}
quantized_seg_compiled_model = core.compile_model(quantized_seg_model, device.value, ov_config)%%skip not $to_quantize.value
def infer(*args):
result = quantized_seg_compiled_model(args)
return torch.from_numpy(result[0]), torch.from_numpy(result[1])
seg_model.predictor.inference = infer%%skip not $to_quantize.value
res = seg_model(IMAGE_PATH)
display(Image.fromarray(res[0].plot()[:, :, ::-1]))image 1/1 /home/maleksandr/test_notebooks/update_ultralytics/openvino_notebooks/notebooks/yolov8-optimization/data/coco_bike.jpg: 640x640 1 bicycle, 2 cars, 2 dogs, 26.8ms
Speed: 2.8ms preprocess, 26.8ms inference, 3.4ms postprocess per image at shape (1, 3, 640, 640)
元のモデルと量子化モデルを比較#
最後に、OpenVINO Benchmark ツールを使用して、FP32 と INT8 モデルの推論パフォーマンスを測定します。
注: より正確なパフォーマンスを得るには、他のアプリケーションを閉じて、ターミナル/コマンドプロンプトで
benchmark_appを実行することを推奨します。benchmark_app -m <model_path> -d CPU -shape "<input_shape>"を実行して、CPU で非同期推論のベンチマークを 1 分間実行します。GPU でベンチマークを行うには、CPUをGPUに変更します。benchmark_app --helpを実行すると、すべてのコマンドライン・オプションの概要が表示されます。
%%skip not $to_quantize.value
deviceif int8_model_seg_path.exists(): !benchmark_app -m $seg_model_path -d $device.value -api async -shape "[1,3,640,640]" -t 15[Step 1/11] Parsing and validating input arguments [ INFO ] Parsing input parameters [Step 2/11] Loading OpenVINO Runtime [ INFO ] OpenVINO: [ INFO ] Build .................................2024.0.0-14509-34caeefd078-releases/2024/0 [ INFO ] [ INFO ] Device info: [ INFO ] AUTO [ INFO ] Build .................................2024.0.0-14509-34caeefd078-releases/2024/0 [ INFO ] [ INFO ] [Step 3/11] Setting device configuration [ WARNING ] Performance hint was not explicitly specified in command line.Device(AUTO) performance hint will be set to PerformanceMode.THROUGHPUT. [Step 4/11] Reading model files [ INFO ] Loading model files [ INFO ] Read model took 15.84 ms [ INFO ] Original model I/O parameters: [ INFO ] Model inputs: [ INFO ] x (node: x) : f32 / [...]/ [?,3,?,?] [ INFO ] Model outputs: [ INFO ] *NO_NAME* (node: __module.model.22/aten::cat/Concat_8) : f32 / [...]/ [?,116,16..] [ INFO ] input.199 (node: __module.model.22.cv4.2.1.act/aten::silu_/Swish_37) : f32 / [...]/ [?,32,8..,8..][Step 5/11] Resizing model to match image sizes and given batch [ INFO ] Model batch size: 1 [ INFO ] Reshaping model: 'x': [1,3,640,640] [ INFO ] Reshape model took 9.23 ms [Step 6/11] Configuring input of the model [ INFO ] Model inputs: [ INFO ] x (node: x) : u8 / [N,C,H,W] / [1,3,640,640] [ INFO ] Model outputs: [ INFO ] *NO_NAME* (node: __module.model.22/aten::cat/Concat_8) : f32 / [...]/ [1,116,8400] [ INFO ] input.199 (node: __module.model.22.cv4.2.1.act/aten::silu_/Swish_37) : f32 / [...]/ [1,32,160,160] [Step 7/11] Loading the model to the device [ INFO ] Compile model took 304.42 ms [Step 8/11] Querying optimal runtime parameters [ INFO ] Model: [ INFO ] NETWORK_NAME: Model0 [ INFO ] EXECUTION_DEVICES: ['CPU'] [ INFO ] PERFORMANCE_HINT: PerformanceMode.THROUGHPUT [ INFO ] OPTIMAL_NUMBER_OF_INFER_REQUESTS: 12 [ INFO ] MULTI_DEVICE_PRIORITIES: CPU [ INFO ] CPU: [ INFO ] AFFINITY: Affinity.CORE [ INFO ] CPU_DENORMALS_OPTIMIZATION: False [ INFO ] CPU_SPARSE_WEIGHTS_DECOMPRESSION_RATE: 1.0 [ INFO ] DYNAMIC_QUANTIZATION_GROUP_SIZE: 0 [ INFO ] ENABLE_CPU_PINNING: True [ INFO ] ENABLE_HYPER_THREADING: True [ INFO ] EXECUTION_DEVICES: ['CPU'] [ INFO ] EXECUTION_MODE_HINT: ExecutionMode.PERFORMANCE [ INFO ] INFERENCE_NUM_THREADS: 36 [ INFO ] INFERENCE_PRECISION_HINT: <Type: 'float32'> [ INFO ] KV_CACHE_PRECISION: <Type: 'float16'> [ INFO ] LOG_LEVEL: Level.NO [ INFO ] NETWORK_NAME: Model0 [ INFO ] NUM_STREAMS: 12 [ INFO ] OPTIMAL_NUMBER_OF_INFER_REQUESTS: 12 [ INFO ] PERFORMANCE_HINT: THROUGHPUT [ INFO ] PERFORMANCE_HINT_NUM_REQUESTS: 0 [ INFO ] PERF_COUNT: NO [ INFO ] SCHEDULING_CORE_TYPE: SchedulingCoreType.ANY_CORE [ INFO ] MODEL_PRIORITY: Priority.MEDIUM [ INFO ] LOADED_FROM_CACHE: False [Step 9/11] Creating infer requests and preparing input tensors [ WARNING ] No input files were given for input 'x'!.This input will be filled with random values! [ INFO ] Fill input 'x' with random values [Step 10/11] Measuring performance (Start inference asynchronously, 12 inference requests, limits: 15000 ms duration) [ INFO ] Benchmarking in inference only mode (inputs filling are not included in measurement loop). [ INFO ] First inference took 50.67 ms [Step 11/11] Dumping statistics report [ INFO ] Execution Devices:['CPU'] [ INFO ] Count: 2124 iterations [ INFO ] Duration: 15076.69 ms [ INFO ] Latency: [ INFO ] Median: 84.69 ms [ INFO ] Average: 84.95 ms [ INFO ] Min: 43.23 ms [ INFO ] Max: 184.81 ms [ INFO ] Throughput: 140.88 FPS
if int8_model_seg_path.exists():
!benchmark_app -m $int8_model_seg_path -d $device.value -api async -shape "[1,3,640,640]" -t 15[Step 1/11] Parsing and validating input arguments [ INFO ] Parsing input parameters [Step 2/11] Loading OpenVINO Runtime [ INFO ] OpenVINO: [ INFO ] Build .................................2024.0.0-14509-34caeefd078-releases/2024/0 [ INFO ] [ INFO ] Device info: [ INFO ] AUTO [ INFO ] Build .................................2024.0.0-14509-34caeefd078-releases/2024/0 [ INFO ] [ INFO ] [Step 3/11] Setting device configuration [ WARNING ] Performance hint was not explicitly specified in command line.Device(AUTO) performance hint will be set to PerformanceMode.THROUGHPUT. [Step 4/11] Reading model files [ INFO ] Loading model files [ INFO ] Read model took 24.33 ms [ INFO ] Original model I/O parameters: [ INFO ] Model inputs: [ INFO ] x (node: x) : f32 / [...]/ [1,3,?,?] [ INFO ] Model outputs: [ INFO ] *NO_NAME* (node: __module.model.22/aten::cat/Concat_8) : f32 / [...]/ [1,116,21..] [ INFO ] input.199 (node: __module.model.22.cv4.2.1.act/aten::silu_/Swish_37) : f32 / [...] / [1,32,8..,8..] [Step 5/11] Resizing model to match image sizes and given batch [ INFO ] Model batch size: 1 [ INFO ] Reshaping model: 'x': [1,3,640,640] [ INFO ] Reshape model took 13.01 ms [Step 6/11] Configuring input of the model [ INFO ] Model inputs: [ INFO ] x (node: x) : u8 / [N,C,H,W] / [1,3,640,640] [ INFO ] Model outputs: [ INFO ] *NO_NAME* (node: __module.model.22/aten::cat/Concat_8) : f32 / [...]/ [1,116,8400] [ INFO ] input.199 (node: __module.model.22.cv4.2.1.act/aten::silu_/Swish_37) : f32 / [...]/ [1,32,160,160] [Step 7/11] Loading the model to the device [ INFO ] Compile model took 574.36 ms [Step 8/11] Querying optimal runtime parameters [ INFO ] Model: [ INFO ] NETWORK_NAME: Model0 [ INFO ] EXECUTION_DEVICES: ['CPU'] [ INFO ] PERFORMANCE_HINT: PerformanceMode.THROUGHPUT [ INFO ] OPTIMAL_NUMBER_OF_INFER_REQUESTS: 12 [ INFO ] MULTI_DEVICE_PRIORITIES: CPU [ INFO ] CPU: [ INFO ] AFFINITY: Affinity.CORE [ INFO ] CPU_DENORMALS_OPTIMIZATION: False [ INFO ] CPU_SPARSE_WEIGHTS_DECOMPRESSION_RATE: 1.0 [ INFO ] DYNAMIC_QUANTIZATION_GROUP_SIZE: 0 [ INFO ] ENABLE_CPU_PINNING: True [ INFO ] ENABLE_HYPER_THREADING: True [ INFO ] EXECUTION_DEVICES: ['CPU'] [ INFO ] EXECUTION_MODE_HINT: ExecutionMode.PERFORMANCE [ INFO ] INFERENCE_NUM_THREADS: 36 [ INFO ] INFERENCE_PRECISION_HINT: <Type: 'float32'> [ INFO ] KV_CACHE_PRECISION: <Type: 'float16'> [ INFO ] LOG_LEVEL: Level.NO [ INFO ] NETWORK_NAME: Model0 [ INFO ] NUM_STREAMS: 12 [ INFO ] OPTIMAL_NUMBER_OF_INFER_REQUESTS: 12 [ INFO ] PERFORMANCE_HINT: THROUGHPUT [ INFO ] PERFORMANCE_HINT_NUM_REQUESTS: 0 [ INFO ] PERF_COUNT: NO [ INFO ] SCHEDULING_CORE_TYPE: SchedulingCoreType.ANY_CORE [ INFO ] MODEL_PRIORITY: Priority.MEDIUM [ INFO ] LOADED_FROM_CACHE: False [Step 9/11] Creating infer requests and preparing input tensors [ WARNING ] No input files were given for input 'x'!.This input will be filled with random values! [ INFO ] Fill input 'x' with random values [Step 10/11] Measuring performance (Start inference asynchronously, 12 inference requests, limits: 15000 ms duration) [ INFO ] Benchmarking in inference only mode (inputs filling are not included in measurement loop). [ INFO ] First inference took 41.26 ms [Step 11/11] Dumping statistics report [ INFO ] Execution Devices:['CPU'] [ INFO ] Count: 3048 iterations [ INFO ] Duration: 15096.50 ms [ INFO ] Latency: [ INFO ] Median: 58.82 ms [ INFO ] Average: 59.20 ms [ INFO ] Min: 33.17 ms [ INFO ] Max: 120.39 ms [ INFO ] Throughput: 201.90 FPS
ご覧のとおり、単一画像テストでは、INT8 モデルと float モデルの結果に大きな違いはありません。量子化がモデル予測の精度にどのように影響するか理解するため、データセット上のモデル精度を比較することができます。
%%skip not $to_quantize.value
int8_seg_stats = test(quantized_seg_model, core, seg_data_loader, seg_validator, num_samples=NUM_TEST_SAMPLES)0%| | 0/300 [00:00<?, ?it/s]%%skip not $to_quantize.value
print("FP32 model accuracy")
print_stats(fp_seg_stats, seg_validator.seen, seg_validator.nt_per_class.sum())
print("INT8 model accuracy")
print_stats(int8_seg_stats, seg_validator.seen, seg_validator.nt_per_class.sum())
FP32 model accuracy
Boxes:
Best mean average:
Class Images Labels Precision Recall mAP@.5 mAP@.5:.95
all 300 2153 0.609 0.521 0.58 0.416
Macro average mean:
Class Images Labels Precision Recall mAP@.5 mAP@.5:.95
all 300 2153 0.605 0.501 0.558 0.353
INT8 model accuracy
Boxes:
Best mean average:
Class Images Labels Precision Recall mAP@.5 mAP@.5:.95
all 300 2153 0.522 0.538 0.555 0.376
Macro average mean:
Class Images Labels Precision Recall mAP@.5 mAP@.5:.95
all 300 2153 0.631 0.463 0.529 0.344これで完了です! 精度は変更されたようですが、大きな変化はなく、合格基準を満たしています。
モデルを最適化する他の方法#
非同期推論パイプラインや前処理 API などの別の OpenVINO メソッドによってパフォーマンスを向上させることもできます。
非同期推論パイプラインは、デバイスをより最適に活用するのに役立ちます。非同期 API の主な利点は、デバイスが推論でビジー状態のときに、アプリケーションが現在の推論が完了するのを待つのではなく、他のタスク (例えば、入力の入力や他の要求のスケジュール設定) を並行して実行できることです。openvino を使用して非同期推論を実行する方法を理解するには、非同期 API のチュートリアルを参照してください。
前処理 API を使用すると、前処理をモデルの一部にすることができ、アプリケーション・コードと追加の画像処理ライブラリーへの依存関係が削減されます。前処理 API の主な利点は、前処理手順が実行グラフに統合され、アプリケーションの一部として常に CPU 上で実行されるのではなく、選択したデバイス (CPU/GPU など) 上で実行されることです。これにより、選択したデバイスの使用率も向上します。詳細については、前処理 API チュートリアルの概要を参照してください。YOLOV8 オブジェクト検出モデルでどのように使用できるかを確認するには、OpenVINO チュートリアルを使用して YOLOv8 リアルタイム・オブジェクト検出を変換および最適化するを参照してください。
ライブデモ#
次のコードは、ビデオに対してモデル推論を実行します:
import collections
import time
import cv2
from IPython import display
def run_instance_segmentation(
source=0,
flip=False,
use_popup=False,
skip_first_frames=0,
model=seg_model,
device=device.value,
):
player = None
ov_config = {}
if device != "CPU":
model.reshape({0: [1, 3, 640, 640]})
if "GPU" in device or ("AUTO" in device and "GPU" in core.available_devices):
ov_config = {"GPU_DISABLE_WINOGRAD_CONVOLUTION": "YES"}
compiled_model = core.compile_model(model, device, ov_config)
def infer(*args):
result = compiled_model(args)
return torch.from_numpy(result[0]), torch.from_numpy(result[1])
seg_model.predictor.inference = infer
try:
# ターゲット fps で再生するビデオプレーヤーを作成
player = VideoPlayer(source=source, flip=flip, fps=30, skip_first_frames=skip_first_frames)
# キャプチャー開始
player.start()
if use_popup:
title = "Press ESC to Exit"
cv2.namedWindow(winname=title, flags=cv2.WINDOW_GUI_NORMAL | cv2.WINDOW_AUTOSIZE)
processing_times = collections.deque()
while True:
# フレームをグラブ
frame = player.next()
if frame is None:
print("Source ended")
break
# フレームがフル HD より大きい場合は、サイズを縮小してパフォーマンスを向上させます
scale = 1280 / max(frame.shape)
if scale < 1:
frame = cv2.resize(
src=frame,
dsize=None,
fx=scale,
fy=scale,
interpolation=cv2.INTER_AREA,
)
# 結果を取得
input_image = np.array(frame)
start_time = time.time()
detections = seg_model(input_image)
stop_time = time.time()
frame = detections[0].plot()
processing_times.append(stop_time - start_time)
# 最後の 200 フレームの処理時間を使用
if len(processing_times) > 200:
processing_times.popleft()
_, f_width = frame.shape[:2]
# 平均処理時間 [ms]
processing_time = np.mean(processing_times) * 1000
fps = 1000 / processing_time
cv2.putText(
img=frame,
text=f"Inference time: {processing_time:.1f}ms ({fps:.1f} FPS)",
org=(20, 40),
fontFace=cv2.FONT_HERSHEY_COMPLEX,
fontScale=f_width / 1000,
color=(0, 0, 255),
thickness=1,
lineType=cv2.LINE_AA,
)
# ちらつきがある場合はこの回避策を使用
if use_popup:
cv2.imshow(winname=title, mat=frame)
key = cv2.waitKey(1)
# escape = 27
if key == 27:
break
else:
# numpy 配列を jpg にエンコード
_, encoded_img = cv2.imencode(ext=".jpg", img=frame, params=[cv2.IMWRITE_JPEG_QUALITY, 100])
# IPython イメージを作成
i = display.Image(data=encoded_img)
# このノートブックに画像を表示
display.clear_output(wait=True)
display.display(i)
# ctrl-c
except KeyboardInterrupt:
print("Interrupted")
# 異なるエラー
except RuntimeError as e:
print(e)
finally:
if player is not None:
# キャプチャーを停止
Player.stop().stop()
if use_popup:
cv2.destroyAllWindows()Web カメラをビデオ入力として使用します。デフォルトでは、プライマリー・ウェブ・カメラは source=0 に設定されます。複数のウェブカメラがある場合、0 から始まる連続した番号が割り当てられます。前面カメラを使用する場合は、flip=True を設定します。一部のウェブブラウザー、特に Mozilla Firefox ではちらつきが発生する場合があります。ちらつきが発生する場合、use_popup=True を設定してください。
注: このノートブックをウェブカメラで使用するには、ウェブカメラを備えたコンピューター上でノートブックを実行する必要があります。ノートブックをリモートサーバー (例えば、Binder または Google Colab サービス) で実行する場合、ウェブカメラは動作しません。デフォルトでは、下のセルはビデオファイルに対してモデル推論を実行します。ウェブカメラ・セットでライブ推論を試す場合、
WEBCAM_INFERENCE = Trueを設定します
WEBCAM_INFERENCE = False
if WEBCAM_INFERENCE: VIDEO_SOURCE = 0 # ウェブカメラ
else: VIDEO_SOURCE = "https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/video/people.mp4"deviceDropdown(description='Device:', index=1, options=('CPU', 'AUTO'), value='AUTO')run_instance_segmentation(
source=VIDEO_SOURCE,
flip=True,
use_popup=False,
model=seg_ov_model,
device=device.value,
)
ソースの終わり