OpenVINO™ を使用した YOLOv8 の変換と最適化

この Jupyter ノートブックは、ローカルへのインストール後にのみ起動できます。

GitHub

Ultralytics が開発した YOLOv8 アルゴリズムは、高速、正確、そして使いやすく設計された最先端の (SOTA) モデルであり、幅広いオブジェクト検出、画像セグメント化、および画像分類タスクに最適です。実現に関する詳細については、元のモデル・リポジトリーを参照してください。

このチュートリアルでは、精度制御を伴う量子化を PyTorch YOLOv8 に適用する方法を段階的に説明します。高度な量子化フローにより、精度メトリックを制御しながら 8 ビット量子化をモデルに適用できます。モデル内で最も影響力のある操作を元の精度に維持することで実現されます。このフローは基本的な 8 ビット量子化に基づいており、次のような違いがあります。

  • 精度メトリックを計算するには、キャリブレーション・データセットと評価データセットが必要です。最も単純なケースでは、両方のデータセットが同じデータを参照できます。

  • 評価関数は、精度メトリックの計算に必要です。ソース・フレームワークを使用可能な関数またはカスタム関数を使用できます。

  • 精度の検証は量子化プロセス中に数回実行されるため、精度制御による量子化は基本 8 ビット量子化フローよりも時間がかかる場合があります。

  • 一部の操作が元の精度で維持されるため、結果のモデルでは、基本 8 ビット量子化フローよりもわずかですがパフォーマンス向上が得られます。

注: 現在、精度制御を備えた 8 ビット量子化は、OpenVINO 表現のモデルでのみ使用できます。

精度制御を備えた量子化の手順を以下に説明します。

必要条件

必要なパッケージをインストールします。

%pip install -q "openvino>=2023.1.0"
%pip install -q "nncf>=2.6.0"
%pip install -q "ultralytics==8.0.43" --extra-index-url https://download.pytorch.org/whl/cpu

Pytorch モデルと OpenVINO IR モデルを取得

一般に、PyTorch モデルは、モデルの重みを含む状態辞書によって初期化された torch.nn.Module クラスのインスタンスを表します。このリポジトリーで入手可能な、COCO データセットで事前トレーニングされた YOLOv8 nano モデル (yolov8n とも呼ばれる) を使用します。この手順は他の YOLOv8 モデルにも適用できます。事前トレーニングされたモデルを取得する一般的な手順は次のとおりです。

  1. クラスのインスタンスを作成します。

  2. 事前トレーニングされたモデルの重みを含むチェックポイント状態辞書をロードします。

この場合、モデルの作成者は、YOLOv8 モデルを ONNX に変換してから OpenVINO IR に変換できる API を提供します。したがって、これらの手順を手動で実行する必要はありません。

import os
from pathlib import Path

from ultralytics import YOLO
from ultralytics.yolo.cfg import get_cfg
from ultralytics.yolo.data.utils import check_det_dataset
from ultralytics.yolo.engine.validator import BaseValidator as Validator
from ultralytics.yolo.utils import DATASETS_DIR
from ultralytics.yolo.utils import DEFAULT_CFG
from ultralytics.yolo.utils import ops
from ultralytics.yolo.utils.metrics import ConfusionMatrix

ROOT = os.path.abspath('')

MODEL_NAME = "yolov8n-seg"

model = YOLO(f"{ROOT}/{MODEL_NAME}.pt")
args = get_cfg(cfg=DEFAULT_CFG)
args.data = "coco128-seg.yaml"

モデルをロードします。

import openvino as ov


model_path = Path(f"{ROOT}/{MODEL_NAME}_openvino_model/{MODEL_NAME}.xml")
if not model_path.exists():
                                        model.export(format="openvino", dynamic=True, half=False)

ov_model = ov.Core().read_model(model_path)

バリデーターとデータローダーを定義

元のモデル・リポジトリーは、精度検証パイプラインを表す Validatorラッパーを使用します。データローダーと評価メトリックを作成し、データローダーによって生成される各データバッチのメトリックを更新します。加えて、データの前処理と結果の後処理も担当します。クラスの初期化では、構成を提供する必要があります。デフォルトの設定を使用しますが、カスタムデータでテストするにはオーバーライドするいくつかのパラメーターに置き換えることができます。モデルは ValidatorClass メソッドに接続しており、これによりバリデーター・クラスのインスタンスが作成されます。

validator = model.ValidatorClass(args)
validator.data = check_det_dataset(args.data)
data_loader = validator.get_dataloader(f"{DATASETS_DIR}/coco128-seg", 1)

validator.is_coco = True
validator.class_map = ops.coco80_to_coco91_class()
validator.names = model.model.names
validator.metrics.names = validator.names
validator.nc = model.model.model[-1].nc
validator.nm = 32
validator.process = ops.process_mask
validator.plot_masks = []
val: Scanning /home/ea/work/openvino_notebooks/notebooks/230-yolov8-optimization/datasets/coco128-seg/labels/train2017.cache... 126 images, 2 backgrounds, 0 corrupt: 100%|██████████| 128/128 [00:00<?, ?it/s

キャリブレーションと評価データセットの準備

1 つのデータセットをキャリブレーションと検証データセットとして使用できます。quantization_dataset という名前を付けます。

from typing import Dict

import nncf


def transform_fn(data_item: Dict):
    input_tensor = validator.preprocess(data_item)["img"].numpy()
    return input_tensor


quantization_dataset = nncf.Dataset(data_loader, transform_fn)
INFO:nncf:NNCF initialized successfully. Supported frameworks detected: torch, tensorflow, onnx, openvino

評価関数の準備

from functools import partial

import torch
from nncf.quantization.advanced_parameters import AdvancedAccuracyRestorerParameters


def validation_ac(
    compiled_model: ov.CompiledModel,
    validation_loader: torch.utils.data.DataLoader,
    validator: Validator,
    num_samples: int = None,
    log=True
) -> float:
    validator.seen = 0
    validator.jdict = []
    validator.stats = []
    validator.batch_i = 1
    validator.confusion_matrix = ConfusionMatrix(nc=validator.nc)
    num_outputs = len(compiled_model.outputs)

    counter = 0
    for batch_i, batch in enumerate(validation_loader):
        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)
        counter += 1
    stats = validator.get_stats()
    if num_outputs == 1:
        stats_metrics = stats["metrics/mAP50-95(B)"]
    else:
        stats_metrics = stats["metrics/mAP50-95(M)"]
    if log:
        print(f"Validate: dataset length = {counter}, metric value = {stats_metrics:.3f}")

    return stats_metrics


validation_fn = partial(validation_ac, validator=validator, log=False)

精度を制御して量子化を実行

キャリブレーション・データセットと評価データセットを提供する必要があります。同じデータセットでもかまいません。

  • パラメーター max_drop は精度低下のしきい値を定義します。量子化プロセスは、評価データセットの精度メトリックの低下が max_drop 未満になると停止します。デフォルト値は 0.01 です。max_drop 値に到達しない場合、NNCF は量子化を停止し、エラーを報告します。
  • drop_type は、精度の低下の計算方法を定義します: ABSOLUTE (デフォルト) または RELATIVE。
  • ranking_subset_size は、精度低下への影響度に基づいてレイヤーをランク付けするために使用されるサブセットのサイズ。デフォルト値は 300 で、サンプル数が多いほどランクが向上する可能性があります。ここでは、実行を高速化するため値 25 を使用します。

注: 実行には数十分かかる場合があり、最大 15GB の空きメモリーが必要です。

quantized_model = nncf.quantize_with_accuracy_control(
    ov_model,
    quantization_dataset,
    quantization_dataset,
    validation_fn=validation_fn,
    max_drop=0.01,
    preset=nncf.QuantizationPreset.MIXED,
    subset_size=128,
    advanced_accuracy_restorer_parameters=AdvancedAccuracyRestorerParameters(
        ranking_subset_size=25
    ),
)
2023-10-10 09:55:44.477778: I tensorflow/core/util/port.cc:110] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable TF_ENABLE_ONEDNN_OPTS=0.
2023-10-10 09:55:44.516624: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 AVX512F AVX512_VNNI FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
2023-10-10 09:55:45.324364: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT
Statistics collection: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 128/128 [00:16<00:00,  7.79it/s]
Applying Fast Bias correction: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 75/75 [00:03<00:00, 18.84it/s]
INFO:nncf:Validation of initial model was started
INFO:nncf:Elapsed Time: 00:00:00
INFO:nncf:Elapsed Time: 00:00:05
INFO:nncf:Metric of initial model: 0.366118260036709
INFO:nncf:Collecting values for each data item using the initial model
INFO:nncf:Elapsed Time: 00:00:06
INFO:nncf:Validation of quantized model was started
INFO:nncf:Elapsed Time: 00:00:01
INFO:nncf:Elapsed Time: 00:00:04
INFO:nncf:Metric of quantized model: 0.3418411101103462
INFO:nncf:Collecting values for each data item using the quantized model
INFO:nncf:Elapsed Time: 00:00:05
INFO:nncf:Accuracy drop: 0.024277149926362818 (DropType.ABSOLUTE)
INFO:nncf:Accuracy drop: 0.024277149926362818 (DropType.ABSOLUTE)
INFO:nncf:Total number of quantized operations in the model: 91
INFO:nncf:Number of parallel processes to rank quantized operations: 6
INFO:nncf:ORIGINAL metric is used to rank quantizers
INFO:nncf:Calculating ranking score for groups of quantizers
INFO:nncf:Elapsed Time: 00:02:16
INFO:nncf:Changing the scope of quantizer nodes was started
INFO:nncf:Reverted 1 operations to the floating-point precision:
    /model.22/Mul_5
INFO:nncf:Accuracy drop with the new quantization scope is 0.013359187935064742 (DropType.ABSOLUTE)
INFO:nncf:Reverted 1 operations to the floating-point precision:
    /model.1/conv/Conv/WithoutBiases
INFO:nncf:Accuracy drop with the new quantization scope is 0.01287864227202773 (DropType.ABSOLUTE)
INFO:nncf:Reverted 1 operations to the floating-point precision:
    /model.2/cv1/conv/Conv/WithoutBiases
INFO:nncf:Algorithm completed: achieved required accuracy drop 0.007027355074555763 (DropType.ABSOLUTE)
INFO:nncf:3 out of 91 were reverted back to the floating-point precision:
    /model.22/Mul_5
    /model.1/conv/Conv/WithoutBiases
    /model.2/cv1/conv/Conv/WithoutBiases

元のモデルと量子化されたモデルの精度とパフォーマンスを比較

これで、元の非量子化 OpenVINO IR モデルと量子化された OpenVINO IR モデルのメトリクスを比較して、max_drop を超えていないことを確認できます。

core = ov.Core()
quantized_compiled_model = core.compile_model(model=quantized_model, device_name='CPU')
compiled_ov_model = core.compile_model(model=ov_model, device_name='CPU')

pt_result = validation_ac(compiled_ov_model, data_loader, validator)
quantized_result = validation_ac(quantized_compiled_model, data_loader, validator)


print(f'[Original OpenVINO]: {pt_result:.4f}')
print(f'[Quantized OpenVINO]: {quantized_result:.4f}')
Validate: dataset length = 128, metric value = 0.368
Validate: dataset length = 128, metric value = 0.360
[Original OpenVINO]: 0.3677
[Quantized OpenVINO]: 0.3602

そして性能を比較します。

from pathlib import Path
# Set model directory
MODEL_DIR = Path("model")
MODEL_DIR.mkdir(exist_ok=True)

ir_model_path = MODEL_DIR / 'ir_model.xml'
quantized_model_path = MODEL_DIR / 'quantized_model.xml'

# Save models to use them in the commandline banchmark app
ov.save_model(ov_model, ir_model_path, compress_to_fp16=False)
ov.save_model(quantized_model, quantized_model_path, compress_to_fp16=False)
# Inference Original model (OpenVINO IR)
! benchmark_app -m $ir_model_path -shape "[1,3,640,640]" -d CPU -api async
[Step 1/11] Parsing and validating input arguments
[ INFO ] Parsing input parameters
[Step 2/11] Loading OpenVINO Runtime
[ INFO ] OpenVINO:
[ INFO ] Build ................................. 2023.2.0-12713-47c2a91b6b6
[ INFO ]
[ INFO ] Device info:
[ INFO ] CPU
[ INFO ] Build ................................. 2023.2.0-12713-47c2a91b6b6
[ INFO ]
[ INFO ]
[Step 3/11] Setting device configuration
[ WARNING ] Performance hint was not explicitly specified in command line. Device(CPU) performance hint will be set to PerformanceMode.THROUGHPUT.
[Step 4/11] Reading model files
[ INFO ] Loading model files
[ INFO ] Read model took 27.11 ms
[ INFO ] Original model I/O parameters:
[ INFO ] Model inputs:
[ INFO ]     images (node: images) : f32 / [...] / [?,3,?,?]
[ INFO ] Model outputs:
[ INFO ]     output0 (node: output0) : f32 / [...] / [?,116,?]
[ INFO ]     output1 (node: output1) : f32 / [...] / [?,32,8..,8..]
[Step 5/11] Resizing model to match image sizes and given batch
[ INFO ] Model batch size: 1
[ INFO ] Reshaping model: 'images': [1,3,640,640]
[ INFO ] Reshape model took 13.41 ms
[Step 6/11] Configuring input of the model
[ INFO ] Model inputs:
[ INFO ]     images (node: images) : u8 / [N,C,H,W] / [1,3,640,640]
[ INFO ] Model outputs:
[ INFO ]     output0 (node: output0) : f32 / [...] / [1,116,8400]
[ INFO ]     output1 (node: output1) : f32 / [...] / [1,32,160,160]
[Step 7/11] Loading the model to the device
[ INFO ] Compile model took 274.70 ms
[Step 8/11] Querying optimal runtime parameters
[ INFO ] Model:
[ INFO ]   NETWORK_NAME: torch_jit
[ INFO ]   OPTIMAL_NUMBER_OF_INFER_REQUESTS: 12
[ INFO ]   NUM_STREAMS: 12
[ INFO ]   AFFINITY: Affinity.CORE
[ INFO ]   INFERENCE_NUM_THREADS: 36
[ INFO ]   PERF_COUNT: False
[ INFO ]   INFERENCE_PRECISION_HINT: <Type: 'float32'>
[ INFO ]   PERFORMANCE_HINT: PerformanceMode.THROUGHPUT
[ INFO ]   EXECUTION_MODE_HINT: ExecutionMode.PERFORMANCE
[ INFO ]   PERFORMANCE_HINT_NUM_REQUESTS: 0
[ INFO ]   ENABLE_CPU_PINNING: True
[ INFO ]   SCHEDULING_CORE_TYPE: SchedulingCoreType.ANY_CORE
[ INFO ]   ENABLE_HYPER_THREADING: True
[ INFO ]   EXECUTION_DEVICES: ['CPU']
[ INFO ]   CPU_DENORMALS_OPTIMIZATION: False
[ INFO ]   CPU_SPARSE_WEIGHTS_DECOMPRESSION_RATE: 1.0
[Step 9/11] Creating infer requests and preparing input tensors
[ WARNING ] No input files were given for input 'images'!. This input will be filled with random values!
[ INFO ] Fill input 'images' with random values
[Step 10/11] Measuring performance (Start inference asynchronously, 12 inference requests, limits: 60000 ms duration)
[ INFO ] Benchmarking in inference only mode (inputs filling are not included in measurement loop).
[ INFO ] First inference took 42.88 ms
[Step 11/11] Dumping statistics report
[ INFO ] Execution Devices:['CPU']
[ INFO ] Count:            7716 iterations
[ INFO ] Duration:         60104.07 ms
[ INFO ] Latency:
[ INFO ]    Median:        88.61 ms
[ INFO ]    Average:       93.27 ms
[ INFO ]    Min:           52.72 ms
[ INFO ]    Max:           181.74 ms
[ INFO ] Throughput:   128.38 FPS
# Inference Quantized model (OpenVINO IR)
! benchmark_app -m $quantized_model_path -shape "[1,3,640,640]" -d CPU -api async
[Step 1/11] Parsing and validating input arguments
[ INFO ] Parsing input parameters
[Step 2/11] Loading OpenVINO Runtime
[ INFO ] OpenVINO:
[ INFO ] Build ................................. 2023.2.0-12713-47c2a91b6b6
[ INFO ]
[ INFO ] Device info:
[ INFO ] CPU
[ INFO ] Build ................................. 2023.2.0-12713-47c2a91b6b6
[ INFO ]
[ INFO ]
[Step 3/11] Setting device configuration
[ WARNING ] Performance hint was not explicitly specified in command line. Device(CPU) performance hint will be set to PerformanceMode.THROUGHPUT.
[Step 4/11] Reading model files
[ INFO ] Loading model files
[ INFO ] Read model took 32.74 ms
[ INFO ] Original model I/O parameters:
[ INFO ] Model inputs:
[ INFO ]     images (node: images) : f32 / [...] / [?,3,?,?]
[ INFO ] Model outputs:
[ INFO ]     output0 (node: output0) : f32 / [...] / [?,116,?]
[ INFO ]     output1 (node: output1) : f32 / [...] / [?,32,8..,8..]
[Step 5/11] Resizing model to match image sizes and given batch
[ INFO ] Model batch size: 1
[ INFO ] Reshaping model: 'images': [1,3,640,640]
[ INFO ] Reshape model took 18.09 ms
[Step 6/11] Configuring input of the model
[ INFO ] Model inputs:
[ INFO ]     images (node: images) : u8 / [N,C,H,W] / [1,3,640,640]
[ INFO ] Model outputs:
[ INFO ]     output0 (node: output0) : f32 / [...] / [1,116,8400]
[ INFO ]     output1 (node: output1) : f32 / [...] / [1,32,160,160]
[Step 7/11] Loading the model to the device
[ INFO ] Compile model took 574.58 ms
[Step 8/11] Querying optimal runtime parameters
[ INFO ] Model:
[ INFO ]   NETWORK_NAME: torch_jit
[ INFO ]   OPTIMAL_NUMBER_OF_INFER_REQUESTS: 12
[ INFO ]   NUM_STREAMS: 12
[ INFO ]   AFFINITY: Affinity.CORE
[ INFO ]   INFERENCE_NUM_THREADS: 36
[ INFO ]   PERF_COUNT: False
[ INFO ]   INFERENCE_PRECISION_HINT: <Type: 'float32'>
[ INFO ]   PERFORMANCE_HINT: PerformanceMode.THROUGHPUT
[ INFO ]   EXECUTION_MODE_HINT: ExecutionMode.PERFORMANCE
[ INFO ]   PERFORMANCE_HINT_NUM_REQUESTS: 0
[ INFO ]   ENABLE_CPU_PINNING: True
[ INFO ]   SCHEDULING_CORE_TYPE: SchedulingCoreType.ANY_CORE
[ INFO ]   ENABLE_HYPER_THREADING: True
[ INFO ]   EXECUTION_DEVICES: ['CPU']
[ INFO ]   CPU_DENORMALS_OPTIMIZATION: False
[ INFO ]   CPU_SPARSE_WEIGHTS_DECOMPRESSION_RATE: 1.0
[Step 9/11] Creating infer requests and preparing input tensors
[ WARNING ] No input files were given for input 'images'!. This input will be filled with random values!
[ INFO ] Fill input 'images' with random values
[Step 10/11] Measuring performance (Start inference asynchronously, 12 inference requests, limits: 60000 ms duration)
[ INFO ] Benchmarking in inference only mode (inputs filling are not included in measurement loop).
[ INFO ] First inference took 31.29 ms
[Step 11/11] Dumping statistics report
[ INFO ] Execution Devices:['CPU']
[ INFO ] Count:            15900 iterations
[ INFO ] Duration:         60077.25 ms
[ INFO ] Latency:
[ INFO ]    Median:        42.02 ms
[ INFO ]    Average:       45.18 ms
[ INFO ]    Min:           25.42 ms
[ INFO ]    Max:           117.81 ms
[ INFO ] Throughput:   264.66 FPS