画像分類モデルの量子化#
この Jupyter ノートブックはオンラインで起動でき、ブラウザーのウィンドウで対話型環境を開きます。ローカルにインストールすることもできます。次のオプションのいずれかを選択します:
このチュートリアルでは、NNCF を使用して画像分類モデルに INT8
量子化を適用する方法を示します。Cifar10 データセットでトレーニングされた MobileNet V2 モデルを使用します。このコードは、カスタムモデルやデータセットに拡張できるように設計されています。このチュートリアルでは、NNCF でモデルの量子化を実行するため OpenVINO バックエンドを使用します。PyTorch モデルに量子化を適用する方法に興味がある場合は、こちらチュートリアルを確認してください。
このチュートリアルは次のステップで構成されます:
量子化用のモデルを準備します。
データロード機能を定義します。
量子化を行います。
元のモデルと量子化されたモデルの精度を比較します。
元のモデルと量子化されたモデルのパフォーマンスを比較します。
1 枚の画像で結果を比較します。
目次:
import platform
# 必要なパッケージをインストール
%pip install -q "openvino>=2023.1.0" "nncf>=2.6.0" torch torchvision tqdm --extra-index-url https://download.pytorch.org/whl/cpu
if platform.system() != "Windows":
%pip install -q "matplotlib>=3.4"
else:
%pip install -q "matplotlib>=3.4,<3.7"
Note: you may need to restart the kernel to use updated packages.
Note: you may need to restart the kernel to use updated packages.
from pathlib import Path
# データとモデルのディレクトリーを設定
DATA_DIR = Path("data")
MODEL_DIR = Path("model")
model_repo = "pytorch-cifar-models"
DATA_DIR.mkdir(exist_ok=True)
MODEL_DIR.mkdir(exist_ok=True)
モデルの準備#
モデルの準備には次の手順があります:
PyTorch モデルをダウンロードします
モデル変換 Python API を使用してモデルを OpenVINO 中間表現形式 (IR) に変換します
変換されたモデルをディスク上でシリアル化します
import sys if not Path(model_repo).exists():
!git clone https://github.com/chenyaofo/pytorch-cifar-models.git
sys.path.append(model_repo)
Cloning into 'pytorch-cifar-models'...
remote: Enumerating objects: 282, done.[K
remote: Counting objects: 100% (281/281), done.[K
remote: Compressing objects: 100% (96/96), done.[K
remote: Total 282 (delta 135), reused 269 (delta 128), pack-reused 1[K
Receiving objects: 100% (282/282), 9.22 MiB | 24.58 MiB/s, done.
Resolving deltas: 100% (135/135), done.
from pytorch_cifar_models import cifar10_mobilenetv2_x1_0
model = cifar10_mobilenetv2_x1_0(pretrained=True)
OpenVINO は、モデル変換 Python API を使用した OpenVINO 中間表現形式への変換により、PyTorch モデルをサポートします。ov.convert_model
は、PyTorch モデル・インスタンスを受け入れ、それを OpenVINO のモデルの openvino.runtime.Model
表現に変換します。オプションで、モデルトレースのヘルパーとして機能する example_input
と、モデルを静的形状に変換する input_shape
を指定できます。変換されたモデルは、推論のためデバイスにロードする準備ができており、save_model
関数を使用して次回使用するためにディスクに保存できます。モデル変換 Python API の詳細については、このページを参照してください。
import openvino as ov
model.eval()
ov_model = ov.convert_model(model, input=[1, 3, 32, 32])
ov.save_model(ov_model, MODEL_DIR / "mobilenet_v2.xml")
データセットの準備#
torchvision の CIFAR10 データセットを使用します。トレーニング設定から取得したモデルの前処理
import torch
from torchvision import transforms
from torchvision.datasets import CIFAR10
transform = transforms.Compose(
[
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.247, 0.243, 0.261)),
]
)
dataset = CIFAR10(root=DATA_DIR, train=False, transform=transform, download=True)
val_loader = torch.utils.data.DataLoader(
dataset,
batch_size=1,
shuffle=False,
num_workers=0,
pin_memory=True,
)
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to data/cifar-10-python.tar.gz
100%|██████████| 170498071/170498071 [00:06<00:00, 24813999.85it/s]
Extracting data/cifar-10-python.tar.gz to data
量子化を実行#
NNCF は、精度の低下を最小限に抑えながら、OpenVINO でニューラル・ネットワーク推論を最適化する一連の高度なアルゴリズムを提供します。MobileNetV2 を最適化するため、ポストトレーニング・モード (微調整パイプラインなし) で 8 ビット量子化を使用します。最適化プロセスには次の手順が含まれます:
量子化用のデータセットを作成します。
nncf.quantize
を実行して、最適化されたモデルを取得します。openvino.save_model
関数を使用して、OpenVINO IR モデルをシリアル化します。
検証データセットの作成#
NNCF は torch.utils.data.DataLoader
インターフェイスと互換性があります。量子化を実行するには、量子化中にモデルに適合する入力データを準備する変換関数を使用して nncf.Dataset
オブジェクトに渡す必要があります。この場合、ペア (入力テンソルとラベル) から入力テンソルを選択し、PyTorch テンソルを numpy に変換します。
import nncf
def transform_fn(data_item):
image_tensor = data_item[0]
return image_tensor.numpy()
quantization_dataset = nncf.Dataset(val_loader, transform_fn)
INFO:nncf:NNCF initialized successfully.Supported frameworks detected: torch, tensorflow, onnx, openvino
nncf.quantize を実行して最適化されたモデルを取得#
nncf.quantize
関数は、基本的な量子化を実行するためモデルと準備された量子化データセットを受け取ります。必要に応じて、subset_size
、preset
、ignored_scope
などの追加パラメーターを指定して、該当する場合は量子化の結果を改善できます。サポートされているパラメーターの詳細については、このページを参照してください
quant_ov_model = nncf.quantize(ov_model, quantization_dataset)
2024-07-13 00:36:24.894428: 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. 2024-07-13 00:36:24.926464: 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. 2024-07-13 00:36:25.567707: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT
Output()
Output()
OpenVINO IR モデルをシリアル化#
ov.convert_model
と同様に、量子化モデルは ov.Model
オブジェクトであり、デバイスにロードする準備ができており、ov.save_model
を使用してディスク上でシリアル化できます。
ov.save_model(quant_ov_model, MODEL_DIR / "quantized_mobilenet_v2.xml")
元のモデルと量子化されたモデルの精度を比較#
from tqdm.notebook import tqdm
import numpy as np
def test_accuracy(ov_model, data_loader):
correct = 0
total = 0
for batch_imgs, batch_labels in tqdm(data_loader):
result = ov_model(batch_imgs)[0]
top_label = np.argmax(result)
correct += top_label == batch_labels.numpy()
total += 1
return correct / total
推論デバイスの選択#
OpenVINO を使用して推論を実行するためにドロップダウン・リストからデバイスを選択します
import ipywidgets as widgets
core = ov.Core()
device = widgets.Dropdown(
options=core.available_devices + ["AUTO"],
value="AUTO",
description="Device:",
disabled=False,
)
device
Dropdown(description='Device:', index=1, options=('CPU', 'AUTO'), value='AUTO')
core = ov.Core()
compiled_model = core.compile_model(ov_model, device.value)
optimized_compiled_model = core.compile_model(quant_ov_model, device.value)
orig_accuracy = test_accuracy(compiled_model, val_loader)
optimized_accuracy = test_accuracy(optimized_compiled_model, val_loader)
0%| | 0/10000 [00:00<?, ?it/s]
0%| | 0/10000 [00:00<?, ?it/s]
print(f"Accuracy of the original model: {orig_accuracy[0] * 100 :.2f}%")
print(f"Accuracy of the optimized model: {optimized_accuracy[0] * 100 :.2f}%")
Accuracy of the original model: 93.61%
Accuracy of the optimized model: 93.57%
元のモデルと量子化モデルのパフォーマンスを比較#
最後に、ベンチマーク・ツールを使用して、FP32
モデルと INT8
モデルの推論パフォーマンスを測定します。
- OpenVINO の推論パフォーマンス測定ツール。
注: より正確なパフォーマンスを得るには、他のアプリケーションを閉じて、ターミナル/コマンドプロンプトで benchmark_app を実行することを推奨します。
benchmark_app -m model.xml -d CPU
を実行して、CPU で非同期推論のベンチマークを 1 分間実行します。GPU でベンチマークを行うには、CPU を GPU に変更します。benchmark_app --help
を実行すると、すべてのコマンドライン・オプションの概要が表示されます。
# 推論 FP16 モデル (OpenVINO IR)
!benchmark_app -m "model/mobilenet_v2.xml" -d $device.value -api async -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.2.0-15519-5c0f38f83f6-releases/2024/2
[ INFO ]
[ INFO ] Device info:
[ INFO ] AUTO
[ INFO ] Build .................................2024.2.0-15519-5c0f38f83f6-releases/2024/2
[ 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 9.87 ms
[ INFO ] Original model I/O parameters:
[ INFO ] Model inputs:
[ INFO ] x (node: x) : f32 / [...] / [1.3,32.32]
[ INFO ] Model outputs:
[ INFO ] x.17 (node: aten::linear/Add) : f32 / [...] / [1,10]
[Step 5/11] Resizing model to match image sizes and given batch
[ INFO ] Model batch size: 1
[Step 6/11] Configuring input of the model
[ INFO ] Model inputs:
[ INFO ] x (node: x) : u8 / [N,C,H,W] / [1,3,32,32]
[ INFO ] Model outputs:
[ INFO ] x.17 (node: aten::linear/Add) : f32 / [...]/ [1,10]
[Step 7/11] Loading the model to the device
[ INFO ] Compile model took 210.66 ms
[Step 8/11] Querying optimal runtime parameters
[ INFO ] Model:
[ INFO ] NETWORK_NAME:
[ 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: 24
[ INFO ] INFERENCE_PRECISION_HINT: <Type: 'float32'>
[ INFO ] KV_CACHE_PRECISION: <Type: 'float16'>
[ INFO ] LOG_LEVEL: Level.NO
[ INFO ] MODEL_DISTRIBUTION_POLICY: set()
[ INFO ] NETWORK_NAME: Model2
[ 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
[ INFO ] PERF_COUNT: 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 3.59 ms
[Step 11/11] Dumping statistics report
[ INFO ] Execution Devices:['CPU']
[ INFO ] Count: 88356 iterations
[ INFO ] Duration: 15003.01 ms
[ INFO ] Latency:
[ INFO ] Median: 1.85 ms
[ INFO ] Average: 1.85 ms
[ INFO ] Min: 1.20 ms
[ INFO ] Max: 9.01 ms
[ INFO ] Throughput: 5889.22 FPS
# 推論 INT8 モデル (OpenVINO IR)
!benchmark_app -m "model/quantized_mobilenet_v2.xml" -d $device.value -api async -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.2.0-15519-5c0f38f83f6-releases/2024/2
[ INFO ]
[ INFO ] Device info:
[ INFO ] AUTO
[ INFO ] Build .................................2024.2.0-15519-5c0f38f83f6-releases/2024/2
[ 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 14.98 ms
[ INFO ] Original model I/O parameters:
[ INFO ] Model inputs:
[ INFO ] x (node: x) : f32 / [...]/ [1.3,32.32]
[ INFO ] Model outputs:
[ INFO ] x.17 (node: aten::linear/Add) : f32 / [...]/ [1,10]
[Step 5/11] Resizing model to match image sizes and given batch
[ INFO ] Model batch size: 1
[Step 6/11] Configuring input of the model
[ INFO ] Model inputs:
[ INFO ] x (node: x) : u8 / [N,C,H,W] / [1,3,32,32]
[ INFO ] Model outputs:
[ INFO ] x.17 (node: aten::linear/Add) : f32 / [...]/ [1,10]
[Step 7/11] Loading the model to the device
[ INFO ] Compile model took 318.23 ms
[Step 8/11] Querying optimal runtime parameters
[ INFO ] Model:
[ INFO ] NETWORK_NAME:
[ 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: 24
[ INFO ] INFERENCE_PRECISION_HINT: <Type: 'float32'>
[ INFO ] KV_CACHE_PRECISION: <Type: 'float16'>
[ INFO ] LOG_LEVEL: Level.NO
[ INFO ] MODEL_DISTRIBUTION_POLICY: set()
[ INFO ] NETWORK_NAME: Model2
[ 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
[ INFO ] PERF_COUNT: 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 1.88 ms
[Step 11/11] Dumping statistics report
[ INFO ] Execution Devices:['CPU']
[ INFO ] Count: 166056 iterations
[ INFO ] Duration: 15001.00 ms
[ INFO ] Latency:
[ INFO ] Median: 1.01 ms
[ INFO ] Average: 1.04 ms
[ INFO ] Min: 0.73 ms
[ INFO ] Max: 7.22 ms
[ INFO ] Throughput: 11069.66 FPS
4 枚の写真の結果を比較#
# CIFAR10 データセットから可能なすべてのラベルを定義
labels_names = [
"airplane",
"automobile",
"bird",
"cat",
"deer",
"dog",
"frog",
"horse",
"ship",
"truck",
]
all_pictures = []
all_labels = []
# すべての写真とそのラベルを取得
for i, batch in enumerate(val_loader):
all_pictures.append(batch[0].numpy())
all_labels.append(batch[1].item())
import matplotlib.pyplot as plt
def plot_pictures(indexes: list, all_pictures=all_pictures, all_labels=all_labels):
"""Plot 4 pictures.
:param indexes: a list of indexes of pictures to be displayed.
:param all_batches: batches with pictures.
"""
images, labels = [], []
num_pics = len(indexes)
assert num_pics == 4, f"No enough indexes for pictures to be displayed, got {num_pics}"
for idx in indexes:
assert idx < 10000, "Cannot get such index, there are only 10000"
pic = np.rollaxis(all_pictures[idx].squeeze(), 0, 3)
images.append(pic)
labels.append(labels_names[all_labels[idx]])
f, axarr = plt.subplots(1, 4)
axarr[0].imshow(images[0])
axarr[0].set_title(labels[0])
axarr[1].imshow(images[1])
axarr[1].set_title(labels[1])
axarr[2].imshow(images[2])
axarr[2].set_title(labels[2])
axarr[3].imshow(images[3])
axarr[3].set_title(labels[3])
def infer_on_pictures(model, indexes: list, all_pictures=all_pictures):
"""Inference model on a few pictures.
:param net: model on which do inference
:param indexes: list of indexes
"""
output_key = model.output(0)
predicted_labels = []
for idx in indexes:
assert idx < 10000, "Cannot get such index, there are only 10000"
result = model(all_pictures[idx])[output_key]
result = labels_names[np.argmax(result[0])]
predicted_labels.append(result)
return predicted_labels
indexes_to_infer = [7, 12, 15, 20] # プロットするには、4 つのインデックスを指定
plot_pictures(indexes_to_infer)
results_float = infer_on_pictures(compiled_model, indexes_to_infer)
results_quanized = infer_on_pictures(optimized_compiled_model, indexes_to_infer)
print(f"Labels for picture from float model : {results_float}.")
print(f"Labels for picture from quantized model : {results_quanized}.")
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Labels for picture from float model : ['frog', 'dog', 'ship', 'horse'].
Labels for picture from quantized model : ['frog', 'dog', 'ship', 'horse'].
