量子化
Qualcomm® AI Hub Workbench enables converting floating point models to fixed point in a process called quantization. Fixed point representations map real numbers to integers (e.g., int8, int16), allowing faster and more memory-efficient inference. Quantized models can be compiled to all supported target runtimes on Qualcomm® AI Hub Workbench and can have up to a 3x improvement in performance. The Snapdragon® Hexagon Tensor Processor performs best with quantized operations.
To capture these performance improvements while retaining model accuracy, quantized models need to be calibrated with unlabeled sample input data. Calibration is the process of determining the fixed point mapping (scales and zero points) between floating point and its quantized integer representation. With an unquantized source model and calibration data, Qualcomm® AI Hub Workbench produces a quantized model asset that can be compiled to run on device.
量子化によりモデルの精度が低下する可能性があります。この機能は、さまざまなモデルタイプに対してより堅牢に動作するよう積極的に開発を進めています。問題が発生した場合は、Slack チャンネル にてご連絡ください。
概要
The Qualcomm® AI Hub Workbench quantize job takes in an unquantized ONNX as input and produces a quantized ONNX model as output. This can be used even if the source model is PyTorch and you want to deploy this to TensorFlow Lite or Qualcomm® AI Engine Direct, by building an end-to-end workflow with compile jobs in addition to the quantize job. We will walk through the following example:
- ソースモデルの準備
PyTorch モデルをロードします。
PyTorch モデルを TorchScript 形式にトレースします。
Call
submit_compile_job()to compile to ONNX.
- ONNX モデルの量子化
キャリブレーションデータをロードして前処理します。
Call
submit_quantize_job()to quantize the model.
- 量子化モデルのコンパイル
Call
submit_compile_job()to compile to TensorFlow Lite.
ソースモデルの準備
最初のステップは、モデルをトレースして ONNX にコンパイルすることです。ソースモデルがすでにONNXであっても、量子化前にコンパイラが最適化パスを実行できるため、ONNX にコンパイルすることをお勧めします。これにより、量子化中に問題を引き起こす可能性のある非最適化パターンが解決されます。
This step is done using a call to submit_compile_job() with the
option --target_runtime onnx. Please refer to モデルのコンパイル for
more information.
import os
import numpy as np
import torch
import torchvision
from PIL import Image
import qai_hub as hub
client = hub.Client()
# 1. Load pre-trained PyTorch model from torchvision
torch_model = torchvision.models.mobilenet_v2(weights="IMAGENET1K_V1").eval()
# 2. Trace the model to TorchScript format
input_shape = (1, 3, 224, 224)
pt_model = torch.jit.trace(torch_model, torch.rand(input_shape))
# 3. Compile the model to ONNX
device = hub.Device("Samsung Galaxy S24 (Family)")
compile_onnx_job = client.submit_compile_job(
model=pt_model,
device=device,
input_specs=dict(image_tensor=input_shape),
options="--target_runtime onnx",
)
assert isinstance(compile_onnx_job, hub.CompileJob)
unquantized_onnx_model = compile_onnx_job.get_target_model()
assert isinstance(unquantized_onnx_model, hub.Model)
ONNX モデルの量子化
The function submit_quantize_job() can be used to quantize an
ONNX model. The function takes an ONNX model and calibration data as input,
quantizes the model, and returns the quantized ONNX model.
The resulting quantized model is in ONNX "fake quantization" format. This is a quantization representation where ops technically have floating point inputs/outputs and quantization bottlenecks are represented separately with QuantizeLinear/DequantizeLinear pairs. This is similar to the static ONNX QDQ format here, except weights are still stored as floating point followed by QuantizeLinear. Note that this is the only ONNX quantization format that Qualcomm® AI Hub Workbench officially supports as input to compile jobs.
キャリブレーションデータには imagenette_samples.zip を使用します。以下のコードを実行する前に、ファイルをダウンロードしてローカルディレクトリに解凍してください。このチュートリアルでは100サンプルを使用します。一般的には500-1000サンプルを使用することをお勧めします。
この例では、上記の例の続きとして、8ビット整数の重みと8ビット整数のアクティベーション(つまり、w8a8)に量子化することを選択します。
# 4. Load and pre-process downloaded calibration data
# This transform is required for PyTorch imagenet classifiers
# Source: https://pytorch.org/hub/pytorch_vision_resnet/
mean = np.array([0.485, 0.456, 0.406]).reshape((3, 1, 1))
std = np.array([0.229, 0.224, 0.225]).reshape((3, 1, 1))
sample_inputs = []
images_dir = "imagenette_samples/images"
for image_path in os.listdir(images_dir):
image = Image.open(os.path.join(images_dir, image_path))
image = image.convert("RGB").resize(input_shape[2:])
sample_input = np.array(image).astype(np.float32) / 255.0
sample_input = np.expand_dims(np.transpose(sample_input, (2, 0, 1)), 0)
sample_inputs.append(((sample_input - mean) / std).astype(np.float32))
calibration_data = dict(image_tensor=sample_inputs)
# 5. Quantize the model
quantize_job = client.submit_quantize_job(
model=unquantized_onnx_model,
calibration_data=calibration_data,
weights_dtype=hub.QuantizeDtype.INT8,
activations_dtype=hub.QuantizeDtype.INT8,
)
quantized_onnx_model = quantize_job.get_target_model()
assert isinstance(quantized_onnx_model, hub.Model)
量子化モデルのコンパイル
量子化された ONNX モデルは、さらに TensorFlow Lite または Qualcomm® AI Engine Direct にコンパイルできます。ONNX モデルの量子化されたopsは、ターゲットランタイムアセットの量子化されたopsとなり、利用可能なハードウェアをより効果的に活用できるようになります。
デフォルトでは、入力と出力はfloat32のままです。これにより、整数と浮動小数点の両方をサポートするプラットフォームではいくつかのオーバーヘッドが追加される場合があります。ただし、浮動小数点をまったくサポートしないプラットフォームでは、より深刻な問題が発生する可能性があります。これを解決するために、--quantize_io コンパイルオプションを使用して、IO境界でも量子化を尊重するようにコンパイラに指示できます(詳細は Compile Options を参照してください)。この場合、整数型への変換と整数型からの変換は、統合コードの外部で行う必要があります。
# 6. Compile to target runtime (TFLite)
compile_tflite_job = client.submit_compile_job(
model=quantized_onnx_model,
device=device,
options="--target_runtime tflite --quantize_io",
)
assert isinstance(compile_tflite_job, hub.CompileJob)
詳細については ONNX モデルを TensorFlow Lite またはQNNにコンパイル を参照してください。
量子化オプション
以下の表は、各ランタイムでサポートされている精度を示しています。
重み |
アクティベーション |
混合精度* |
|
|---|---|---|---|
TFLite |
int8 |
int8 |
サポートされていません |
QNN |
int8 |
int8, int16 |
サポートされていません |
ONNX |
int8 |
int8, int16 |
サポートされていません |
*混合精度は、同じネットワーク内で異なる精度で異なるopsを実行することを可能にします。長期的には、すべてのランタイムが重みとアクティベーションのために int4, int8、および int16 に加えて混合精度をサポートする必要があります。
Please refer to submit_quantize_job() and Quantize Options for additional options.
量子化モデルのパフォーマンスベンチマーク
サンプル入力データを取得するプロセスを経る前に、モデルのレイテンシを迅速にベンチマークすることが有用な場合があります。このユースケースでは、キャリブレーションデータは単一のランダムサンプルである可能性があります。結果として得られる量子化モデルの精度は低くなりますが、正確なモデルと同じオンデバイスのレイテンシを持ちます。
import numpy as np
import qai_hub as hub
client = hub.Client()
device = hub.Device("Samsung Galaxy S24 (Family)")
calibration_data = dict(
image_tensor=[np.random.randn(1, 3, 224, 224).astype(np.float32)]
)
# Convert the input onnx to optimized ONNX then quantize to ONNX QDQ format
compile_onnx_job = client.submit_compile_job(
model="mobilenet_v2.onnx",
device=device,
input_specs=dict(image_tensor=(1, 3, 224, 224)),
)
assert isinstance(compile_onnx_job, hub.CompileJob)
unquantized_onnx_model = compile_onnx_job.get_target_model()
assert isinstance(unquantized_onnx_model, hub.Model)
quantize_job = client.submit_quantize_job(
model=unquantized_onnx_model,
calibration_data=calibration_data,
weights_dtype=hub.QuantizeDtype.INT8,
activations_dtype=hub.QuantizeDtype.INT8,
)
assert isinstance(quantize_job, hub.QuantizeJob)
quantized_onnx_model = quantize_job.get_target_model()
assert isinstance(quantized_onnx_model, hub.Model)
# Model can be compiled to tflite, qnn, or onnx format
compile_qnn_job = client.submit_compile_job(
model=quantized_onnx_model,
device=device,
options="--target_runtime qnn_context_binary --quantize_io",
)
assert isinstance(compile_qnn_job, hub.CompileJob)
compiled_model = compile_qnn_job.get_target_model()
assert isinstance(compiled_model, hub.Model)
profile_job = client.submit_profile_job(
model=compiled_model,
device=device,
)
assert isinstance(profile_job, hub.ProfileJob)