Intel Developer Cloudを使用してYOLOv8を評価する

2024年2月26日

はじめに

Intel製CPUの評価に使用できるクラウドサービスとして、Intel Developer Cloudというサービスがあります。 今回はこのサービスを使用して、画像の物体認識でよく使用されるモデルであるYOLOv8の評価を行いました。

Intel Developer Cloud

Intel Developer CloudはIntelが提供するクラウドサービスで、Intel製CPUの評価を簡単に行うことができます。

https://www.intel.com/content/www/us/en/developer/tools/devcloud/overview.html

いくつかのサービスがあり、それぞれ仕様や利用可能なマシンが異なります。

  • Intel Developer Cloud for the Edge
    • エッジ向けデバイス上でAIアプリケーションを中心に評価が可能
    • 評価用マシンは組み込み向けのCore系CPUを中心にAtomやXeonなども用意されている
  • Intel Developer Cloud for oneAPI Applications
    • 様々なインテル製デバイス上でoneAPIを使用したアプリケーションの評価が可能
    • 評価用マシンはXeon CPUの他、GPUやFPGAも用意されている
  • Intel Developer Cloud (Beta)
    • 最新のインテル製デバイスの評価が可能

今回はIntel Developer Cloud for the Edgeを使用してYOLOv8の評価を行います。

Intel Developer Cloud for the Edge

Intel Developer Cloudのうち、エッジ向けデバイスの評価に特化したサービスです。

一般的なPCでよく使用されているCore系CPUやAtomが評価用マシンとして用意されており、PC上で動作するアプリケーションの評価に適しています。 CPUの世代は古いものだと第6世代のインテル® Core™ i5-6500TE、新しいものだと第13世代のインテル® Core™ i9-13900TEと幅広く展開されていますので、世代間の性能を比較してハードウェア選定を行う用途にも使用できます。 当然すべてのCPUが用意されているわけではないので、その点はご注意ください。 実際に利用可能なマシンはこちらで公開されています。

Intel Developer Cloud for the EdgeではJupyterLabを使用して評価を行います。 OpenVINOやTensorFlowなど機械学習系のライブラリがインストール済みなので、すぐに使用することができます。

OpenVINO toolkit

OpenVINO toolkitは様々なインテルデバイス上で深層学習の推論処理を高速に実行するためのツールキットです。

https://docs.openvino.ai/

OpenVINO toolkitにはモデルを実行するためのランタイム以外にもいくつかのツールが含まれています。

  • Model Optimizer
    • モデルをOpenVINO IR形式に変換するための最適化ツール
    • ONNXモデルやTF SavedModelを読み込み、OpenVINO IR形式に変換可能
  • benchmark_app
    • OpenVINOを使用した際のパフォーマンスを測定するベンチマークツール
    • 実行ハードウェア(CPU/GPU)を指定した測定や、レイヤーごとの実行時間プロファイルなどが可能
  • Accuracy Checker
    • モデルの精度を測定するための評価ツール
    • OpenVINOの他TFLiteやONNX Runtimeといったランタイムにも対応しているため、様々なモデルの精度評価が可能

これらのツールはすべてIntel Developer Cloud上で使用可能です。

今回はこれらのツールを活用して、精度やパフォーマンスの測定を行います。

Neural Network Compression Framework (NNCF)

Neural Network Compression Framework (NNCF)はOpenVINOチームが開発しているモデル最適化ツールです。

https://github.com/openvinotoolkit/nncf

OpenVINO向けに量子化をはじめとした様々なモデル最適化を適用できます。

今回はPost-Training Quantization(PTQ)をYOLOv8に適用する際に使用します。

前準備

アカウントの作成

Intel Developer Cloud for the Edgeは無償で利用可能ですが、利用のためにはアカウントが必要です。

まずは、インテルアカウントを作成します。

Intelの公式サイトを開き、右上の人型アイコンからサインイン画面を開きます。

続いて、アカウントの作成をクリックします。

その後は手順に従い、インテルアカウントを作成してください。

インテルアカウント作成後は、Intel Developer Cloudアカウントを作成します。

Intel Developer Cloud for the Edgeを開き、Create an Intel Developer Cloud Accountをクリックします。

その後は手順に従い、Intel Developer Cloudアカウントを作成します。

Intel Developer Cloudアカウントあくまで利用するための手続きであり、ログイン自体はインテルアカウントで行うためご注意ください。

JupyterLabの起動

Intel Developer Cloud for the Edgeを開き、Bare Metal DevelopmentのGet Startedをクリックすると、JupyterLabが起動できます。

YOLOv8の評価

ここからは起動したJupyterLabを使用して、YOLOv8を評価します。

評価はOpenVINO Notebooksで公開されているノートブックを使用します。

OpenVINO NotebooksではOpenVINOを使用した様々なチュートリアルが公開されており、OpenVINOライブラリの基本的な使い方から主要なタスクの実用例、量子化などのモデル最適化まで、幅広く学ぶことができます。

今回はYOLOv8の実行と量子化を扱う下記のノートブックをベースに進めます。

https://github.com/openvinotoolkit/openvino_notebooks/blob/e6cc9287dbee0b799292831e1d49720a1cd2733e/notebooks/230-yolov8-optimization/230-yolov8-object-detection.ipynb

Notebookを開く

上記Notebookをダウンロードして、JupyterLabにアップロードします。

アップロードしたNotebookを開くとカーネルを選択する画面になるため、Python 3.10 (OpenVINO 2023.2 Latest)を選択します。

Intel Developer Cloud向けに書き換え

Intel Developer Cloud上では必要なライブラリが予めインストールされているため、セル1の内容(pip installのコマンド)を全てコメントアウトもしくは削除してください。

ログインノード上で実行

上記書き換えが完了したら、すべてのセルを実行するだけでモデルの最適化から評価までの一通りの処理が実行されます。

  • YOLOv8のダウンロード
  • ONNX形式でのエクスポート
  • OpenVINO IR形式への変換
  • NNCFを使用した量子化(PTQ/INT8)
  • 性能測定(スループット/レイテンシ)
  • 精度測定
  • 推論結果の可視化

ただし、Notebookを普通に実行した場合は、ログインノード(Xeonサーバ)上で処理が行われます。 エッジデバイス上の性能評価をする場合は、少し別の手順で実行する必要があります。

エッジデバイス上で実行

デバイス一覧の取得

Intel Developer Cloudには様々なデバイスが用意されていますので、どのデバイスで評価するかを選択する必要があります。

新しいセルを作成し下記のコマンドを実行すると、使用可能なデバイスの一覧が表示されます。

!pbsnodes | grep compnode | awk '{print $3}' | sort | uniq

各マシンのCPU型番やメモリ容量などが表示されますので、評価したいマシンを選んでください。

実行時には先頭にあるID(例:idc001sk1)を使ってマシンを指定します。

性能評価

エッジデバイス上で性能評価を行うためには、実行したい内容をスクリプトとして用意する必要があります。

今回は予め用意した下記のスクリプトを使用します。

このスクリプトは、モデルと実行デバイスをいくつかの組み合わせで実行しています。 モデルは通常のモデルと量子化済みのモデルの2種類、実行デバイスはCPU、GPU(iGPU)、AUTOの3種類の中から組み合わせて実行します。 AUTOはCPUとGPU両方を組み合わせて実行するモードです。

benchmark.sh

#!/bin/sh
#PBS -v VENV_PATH
#PBS -j oe

cd $PBS_O_WORKDIR

source ${VENV_PATH}/bin/activate

MODELS=("models/yolov8n_openvino_model/yolov8n.xml" "models/yolov8n_openvino_int8_model/yolov8n.xml")
DEVICES=("CPU" "GPU" "AUTO")

for model in "${MODELS[@]}"; do
  for device in "${DEVICES[@]}"; do
    echo -----
    echo model:  $model
    echo device: $device
    echo -----
    benchmark_app -m $model -d $device -api async -shape "[1,3,640,640]" -hint cumulative_throughput -t 120
  done
done

最後の行には改行が必須なのでご注意ください。

スクリプトを保存したら、qsubコマンドを使用してエッジデバイス上でこのスクリプトを実行するジョブを作成します。 新しいセルを作成して下記のコマンドを実行してください。

!qsub -l nodes=1:idc001skl benchmark.sh

idc001sklの部分は、実際に使用したいマシンのIDに変更してください。

実行すると、949569.v-qsvr-1.devcloud-edgeのような出力が得られます。 このうち最初の数値の部分(例だと949569)がジョブ番号になります。

指定したデバイスにもよりますが、数分程度待つとbenchmark.sh.o[ジョブ番号]というファイルに結果が出力されます。 このファイルを開くと、各モデル、ハードウェアごとの実行結果が確認できます。

精度評価

性能ほどではありませんが、精度もデバイスごとに異なる可能性があります。 対応命令セットの違いによる演算結果の微妙な誤差などが影響を与えるためです。

精度評価も性能評価と同様に、あらかじめ用意したスクリプトを使用します。 このスクリプトはNotebookの精度評価コードを元に、単独で実行できるように整理したものです。

そのまま実行すると、通常のモデルと量子化済みのモデルの精度評価をCPUとGPUそれぞれで実行します。評価対象や評価サンプル数などのパラメータは必要に応じて変更してください。

accuracy.sh

#!/bin/sh
#PBS -v VENV_PATH
#PBS -j oe

cd $PBS_O_WORKDIR

source ${VENV_PATH}/bin/activate

python3 accuracy.py

accuracy.py

from tqdm.notebook import tqdm
from ultralytics.yolo.utils.metrics import ConfusionMatrix
from ultralytics.yolo.utils import DEFAULT_CFG, ops
from ultralytics.yolo.cfg import get_cfg
from ultralytics.yolo.data.utils import check_det_dataset
from ultralytics import YOLO
import openvino.runtime as ov
import torch
import numpy as np


def main():
  det_model = YOLO("models/yolov8n.pt")
  args = get_cfg(cfg=DEFAULT_CFG)
  args.data = str("datasets/coco.yaml")
  det_validator = det_model.ValidatorClass(args=args)
  det_validator.data = check_det_dataset(args.data)
  det_data_loader = det_validator.get_dataloader("datasets/coco", 1)
  det_validator.is_coco = True
  det_validator.class_map = ops.coco80_to_coco91_class()
  det_validator.names = det_model.model.names
  det_validator.metrics.names = det_validator.names
  det_validator.nc = det_model.model.model[-1].nc
  NUM_TEST_SAMPLES = 300
  
  core = ov.Core()
  
  for model in ["models/yolov8n_openvino_model/yolov8n.xml", "models/yolov8n_openvino_int8_model/yolov8n.xml"]:
    for device in ["CPU", "GPU"]:
      print(f"""---
model:  {model}
device: {device}
---""")
      ov_model = core.read_model(model)
      det_stats = test(ov_model, device, core, det_data_loader, det_validator, num_samples=NUM_TEST_SAMPLES)
      print_stats(det_stats, det_validator.seen, det_validator.nt_per_class.sum())


def test(model:ov.Model, device:str, 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 = []
    validator.batch_i = 1
    validator.confusion_matrix = ConfusionMatrix(nc=validator.nc)
    model.reshape({0: [1, 3, -1, -1]})
    compiled_model = core.compile_model(model, device)
    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"])
        preds = torch.from_numpy(results[compiled_model.output(0)])
        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 results
    s = ('%20s' + '%12s' * 6) % ('Class', 'Images', 'Labels', 'Precision', 'Recall', 'mAP@.5', 'mAP@.5:.95')
    print(s)
    pf = '%20s' + '%12i' * 2 + '%12.3g' * 4  # print format
    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 results
        s = ('%20s' + '%12s' * 6) % ('Class', 'Images', 'Labels', 'Precision', 'Recall', 'mAP@.5', 'mAP@.5:.95')
        print(s)
        pf = '%20s' + '%12i' * 2 + '%12.3g' * 4  # print format
        print(pf % ('all', total_images, total_objects, s_mp, s_mr, s_map50, s_mean_ap))

if __name__ == "__main__":
  main()

こちらも同様にqsubコマンドで実行します。 新しいセルを作成して下記のコマンドを実行してください。

!qsub -l nodes=1:idc001skl accuracy.sh

idc001sklの部分は、実際に使用したいマシンのIDに変更してください。

指定したデバイスにもよりますが、十数分程度待つとaccuracy.sh.o[ジョブ番号]というファイルに結果が出力されます。 このファイルを開くと、各モデル、ハードウェアごとの実行結果が確認できます。

実行結果

今回はCPUによる性能の違いを見るために、下記の3種類のCPUで性能測定を行いました。

  • インテル® Core™ i7-8700T
  • インテル® Core™ i7-10700T
  • インテル® Core™ i5-12500TE

CPUの仕様は下記のとおりです。

インテル® Core™ i7-8700Tインテル® Core™ i7-10700Tインテル® Core™ i5-12500TE
発売日Q2’18Q2’20Q1’22
CPU世代第8世代第10世代第12世代
開発コードCoffee LakeComet LakeAlder Lake
コア数686 / 0 (P-Core/E-Core)
スレッド数121612
ベース周波数2.40 GHz2.00 GHz1.90 GHz
ターボブースト時周波数4.00 GHz4.50 GHz4.30 GHz
メモリー最大 DDR4-2666最大 DDR4-2933最大 DDR5-4800
メモリー帯域最大 41.6GB/s最大 45.8 GB/s最大 76.8 GB/s
iGPUIntel® UHD Graphics 630Intel® UHD Graphics 630Intel® UHD Graphics 770
iGPU世代第9.5世代第9.5世代第12世代
iGPUクロック最大 1.20 GHz最大 1.20 GHz最大 1.45GHz

OpenVINOの実行デバイスは、CPUとGPU(iGPU)の2つを測定しました。

結果は下記のようになりました。

CPU実行デバイス量子化なし量子化あり
i7-8700TCPU33.25 FPS60.93 FPS
i7-8700TGPU44.42 FPS41.71 FPS
i7-8700TAUTO53.97 FPS70.16 FPS
i7-10700TCPU39.12 FPS68.38 FPS
i7-10700TGPU52.09 FPS48.96 FPS
i7-10700TAUTO60.27 FPS82.24 FPS
i5-12500TECPU43.46 FPS116.65 FPS
i5-12500TEGPU78.20 FPS110.57 FPS
i5-12500TEAUTO79.69 FPS157.95 FPS

この結果から、新しいCPUほど推論性能が上がっていることがわかります。

特に新しいi5(i5-12500TE)は古いi7(i7-10700T)よりコア数が少ないにも関わらず、性能は大きく向上しています。 これは単純な演算性能やメモリ帯域の向上のほか、CPUのVNNI(INT8行列演算向けの命令セット)追加やiGPUのINT8サポートなどが効果的に作用していると考えられます。

また、AUTOを使用することでさらなる性能向上が得られています。 これはAUTOが自動でCPUとGPUの両方を活用して高速化するためです。 ただし、場合によっては速度が低下するケースも有るため、実際に性能を確認して必要に応じて設定を調整することが大切です。

まとめ

Intel Developer Cloudを使用することで、手元に環境を用意することなく手軽に物体認識の評価を行うことができました。

特にCPU間の比較のような評価を手元で行う場合は何台ものマシンを用意する必要があるため、Intel Developer Cloudのメリットが活かせます。

また、OpenVINO Notebooksでは、今回使用したノートブックのような実用例が多く公開されています。Intel Developer Cloudと組み合わせることで、最新のモデルを無料で手軽に試すことができます。

みなさんもぜひ興味のあるノートブックを元に実行してみてください。

About Author

kenta.fujinami

Leave a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

Recent Comments

Social Media