最初のモデルをコンパイルする
このチュートリアルでは、ResNet-50 ONNX モデルを Model Compiler を使用して、Post-Training Quantization (PTQ) ワークフローにかけます。その結果、.tar.gz 形式の MPK アーカイブが作成され、Neat ランタイムで使用できるようになります。
このワークフローは、以下の 4 つの段階で構成されます。
- モデルを読み込みます。
- デフォルトでは INT8 に量子化し、必要に応じて BF16 に量子化します。
- 正確性を検証する。
- MLSoC 上で実行できるように、コンパイルしてください。
前提条件
sima-cliがインストールされました(sima-cli のセットアップガイド を参照)。- Model Compiler を、Neat SDK または Ubuntu ホストにインストールします。次に、以下を入力してください。 次の環境で:
activate-model-compiler
サンプルを入手する
Model Compiler がインストールされている Neat SDK または Ubuntu ホストで、sima-cli を使用して、Model Compiler のサンプルをインストールします。
sima-cli neat install model-compiler/examples
チュートリアルの最後まで、Model Compiler 環境をアクティブな状態に保ってください。
量子化とコンパイルのサンプルを実行します。このスクリプトは、ResNet-50 ONNX モデルを生成し、まだ存在しない場合は、公開されている Open Images のキャリブレーションデータをダウンロードします。
cd resnet50-ptq
python3 compile.py
検証用の入力データが与えられた場合、プログラムはゴールデン・レトリバーをImageNetのクラス207として分類し、コンパイルされたアーカイブを生成する必要があります。
Validation image prediction:
class 207: 'golden retriever' -> 98.82%
Quantization complete.
Compiling model. Output directory: .../compiled_resnet50
Compiled MPK archive written to: .../compiled_resnet50/quantized_resnet50_mpk.tar.gz
生成された.tar.gzファイルを使用して、精度とパフォーマンスを検証するか、または、それを使用してパイプラインアプリケーションを構築します。
以下のセクションでは、各段階について説明します。完全なスクリプトは、最後に示されます。MLAテッセレーションはデフォルトで有効になっているため、コンパイルされたモデルは直接アクセラレータに送られます。詳細は、コンパイル > テッセレーションを参照してください。
仕組み
1. モデルを読み込む
ONNX ResNet-50 モデルを SDK の内部表現に読み込みます。
from afe.apis.loaded_net import load_model
from afe.apis.defines import gen1_target, gen2_target
from afe.load.importers.general_importer import onnx_source
from afe.ir.tensor_type import ScalarType
MODEL_PATH = "resnet50.onnx"
TARGET = gen2_target # gen2_target = Modalix, gen1_target = MLSoC
# Model information
input_name, input_shape, input_type = ("input", (1, 3, 224, 224), ScalarType.float32)
input_shapes_dict = {input_name: input_shape}
input_types_dict = {input_name: input_type}
# Load the ONNX model
importer_params = onnx_source(str(MODEL_PATH), input_shapes_dict, input_types_dict)
loaded_net = load_model(importer_params, target=TARGET)
入力テンソル "input" は、バッチサイズが1、3つのカラーチャネル、224×224ピクセルの形状 (1, 3, 224, 224) であり、型は float32 です。onnx_source は、モデル(ONNX ファイル自体は変更されません)を読み込む方法を記述します。load_model は、モデルを量子化の準備ができた LoadedNet に変換します。TARGET はプラットフォームを選択します。MLSoC の場合は gen1_target を、Modalix の場合は gen2_target を選択します。
2. 校正データセットを準備する。
量子化には、小さく、代表的なキャリブレーションデータセットが必要です。このデータセットは、FP32値を整数範囲にマッピングするためのスケーリング係数を設定し、過剰なクリッピングや精度の低下を防ぎます。
import cv2
import numpy as np
from sima_utils.data.data_generator import DataGenerator
from afe.core.utils import convert_data_generator_to_iterable
MODEL_INPUT_NAME = "input"
IMAGENET_MEAN = np.array([0.485, 0.456, 0.406], dtype=np.float32)
IMAGENET_STD = np.array([0.229, 0.224, 0.225], dtype=np.float32)
def preprocess(image: np.ndarray, size=(224, 224)) -> np.ndarray:
"""Resize to 224x224, scale to [0, 1], normalize. Returns HWC float32."""
image = cv2.resize(image, size).astype(np.float32) / 255.0
return ((image - IMAGENET_MEAN) / IMAGENET_STD).astype(np.float32)
# Build a DataGenerator from preprocessed NHWC calibration images.
calibration_images = np.stack([preprocess(image) for image in raw_calibration_images])
calib_data = convert_data_generator_to_iterable(
DataGenerator({MODEL_INPUT_NAME: calibration_images}))
DataGenerator に渡されるキャリブレーションデータは、NHWC 形式 ([batch, height, width, channels]) でなければなりません。これは、モデルへの入力テンソルが NCHW ([batch, channels, height, width]) 形式であっても同様です。この例のように、ONNX の入力形状が (1, 3, 224, 224) の場合です。上記の例では、すでに NHWC 形式が出力されます。これは、preprocess が HWC 形式の画像を出力するためです。前処理パイプラインで NCHW 形式の配列が生成される場合は、キャリブレーションデータセットを構築する前に、それらを転置してください。
# Convert NCHW -> NHWC
calibration_images = np.transpose(calibration_images, (0, 2, 3, 1))
calib_data = convert_data_generator_to_iterable(
DataGenerator({MODEL_INPUT_NAME: calibration_images}))
デプロイメントのワークロードで使用するのと同じ入力分布から、代表的な画像を抽出して使用してください。
3. 量子化する
モデルを読み込み、キャリブレーションデータを準備した後、量子化を実行します。パッケージ化されたサンプルでは、デフォルトでINT8が使用されます。これは、広くサポートされている方法であるためです。INT8量子化中に、一部のモデルで飽和に関する警告が表示される場合があります。コンパイルされた出力を使用する前に、量子化されたモデルを検証してください。
from afe.apis.defines import QuantizationParams, quantization_scheme, CalibrationMethod
from afe.core.utils import convert_data_generator_to_iterable
quant_configs = QuantizationParams(
calibration_method=CalibrationMethod.from_str('mse'),
activation_quantization_scheme=quantization_scheme(
asymmetric=True, per_channel=False, bits=8),
weight_quantization_scheme=quantization_scheme(
asymmetric=False, per_channel=True, bits=8),
)
sdk_net = loaded_net.quantize(
calib_data,
quant_configs,
model_name="quantized_resnet50",
)
この例では、活性化関数に対して 8 ビットの非対称テンソルごとの量子化を、重みに対して 8 ビットの対称チャネルごとの量子化を使用しています。BF16 およびキャリブレーションのオプションについては、量子化 を参照してください。
4. 正確性を検証する。
コンパイルする前に、量子化されたモデルをソフトウェアで実行し、sdk_net.execute(...) を使用して、依然として正しく分類できることを確認してください。
import numpy as np
def postprocess_output(output: np.ndarray, labels: list[str]):
probabilities = output[0][0]
idx = int(np.argmax(probabilities))
name = labels[idx] if idx < len(labels) else "?"
return idx, name, probabilities[idx]
# A known image: a Golden Retriever is ImageNet class 207.
with open("data/imagenet_labels.txt") as f:
labels = [line.strip() for line in f]
dog = preprocess(cv2.cvtColor(cv2.imread("data/golden_retriever_207.jpg"), cv2.COLOR_BGR2RGB))
output = sdk_net.execute(inputs={"input": np.expand_dims(dog, axis=0)})
idx, name, score = postprocess_output(output, labels)
print(f"class {idx}: '{name}' -> {100.0 * score:.2f}%")
正確で信頼性の高い予測、例えば207 'golden retriever' -> 98.82%は、前処理と量子化が適切に調整されていることを示します。誤った分類は、通常、前処理の不整合または量子化の問題を示しており、再調整が必要です。