量化
Qualcomm® AI Hub 允許將浮點模型轉換為定點模型,這個過程稱為 量化。定點表示將實數映射到整數(例如,int8、int16),允許更快且更高效的內存推理。量化模型可以編譯到 Qualcomm® AI Hub 支持的所有目標runtime,性能最多可提高3倍。Snapdragon® Hexagon Tensor Processor 在量化操作中表現最佳。
為了在保持模型準確性的同時捕捉這些性能改進,量化模型需要使用未標記的樣本輸入數據進行校準。校準是確定浮點和其量化整數表示之間的定點映射(比例和零點)的過程。使用未量化的來源模型和校準數據,Qualcomm® AI Hub 生成可以在設備上運行的量化模型資產。
模型在量化過程中可能會經歷準確性下降。我們正在積極開發此功能,使其對不同類型的模型更加穩健。如果您遇到問題,請在我們的 slack 頻道 上聯繫我們。
概述
Qualcomm® AI Hub 量化工作接受未量化的 ONNX 作為輸入,並生成量化的 ONNX 模型作為輸出。即使來源模型是 PyTorch 並且您希望將其部署到 TensorFlow Lite 或 Qualcomm® AI Engine Direct,也可以通過構建包含編譯工作的點到點工作流程來使用量化工作。我們將通過以下範例進行演示:
- 準備源模型
加載 PyTorch 模型。
將 PyTorch 模型跟踪到 TorchScript 格式。
調用
submit_compile_job()
編譯到 ONNX。
- 量化 ONNX 模型
加載和預處理校準數據。
調用
submit_quantize_job()
量化模型。
- 編譯已量化模型
調用
submit_compile_job()
編譯到 TensorFlow Lite。
準備源模型
第一步是追蹤模型並將其編譯為 ONNX 格式。我們建議即使來源模型已經是 ONNX 格式,也進行編譯,因為這樣可以讓編譯器在量化之前運行優化過程。這將確保解決那些可能在量化過程中引發問題的未優化模式。
此步驟是使用調用 submit_compile_job()
並選擇 --target_runtime onnx
完成的。請參閱 編譯模型 了解更多信息。
import os
import numpy as np
import torch
import torchvision
from PIL import Image
import qai_hub as hub
# 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 = hub.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 模型
可以使用 submit_quantize_job()
量化 ONNX 模型。該函數接受 ONNX 模型和校準數據作為輸入,量化模型並返回量化的 ONNX 模型。
生成的量化模型是 ONNX "假量化" 格式。這是一種量化表示,其中操作技術上具有浮點輸入/輸出,量化瓶頸分別由 QuantizeLinear/DequantizeLinear 對表示。這類似於靜態 ONNX QDQ 格式 here,除了權重仍然存儲為浮點,然後是 QuantizeLinear。請注意,這是 Qualcomm® AI Hub 官方支持作為編譯工作輸入的唯一 ONNX 量化格式。
對於校準數據,我們將使用 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 = hub.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 模型中的量化操作將成為目標運行時資產中的量化操作,準備更好地利用可用硬件。
預設情況下,它將輸入和輸出保留在 float32。這可能會在支持整數和浮點數學的平台上增加一些開銷。然而,在完全不支持浮點數學的平台上,這可能會導致更嚴重的問題。為了解決這個問題,我們可以告訴編譯器即使在 IO 邊界也尊重量化,使用 --quantize_io
編譯選項(請參閱 Compile Options)。在這種情況下,整數類型的轉換需要在模型以外的代碼中發生。
# 6. Compile to target runtime (TFLite)
compile_tflite_job = hub.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 了解更多信息。
量化選項
下表顯示了每個runtime支持的精度。
權重 |
激活 |
混合精度* |
|
---|---|---|---|
TFLite |
int8 |
int8 |
不支持 |
QNN |
int8 |
int8, int16 |
不支持 |
ONNX |
int8 |
int8, int16 |
不支持 |
*混合精度允許在同一網絡中以不同精度運行不同操作。從長遠來看,所有運行時應支持混合精度,除了 int4、int8 和 int16 用於權重和激活。
請參閱 submit_quantize_job()
和 Quantize Options 了解更多選項。
比較量化模型性能
有時在獲取樣本輸入數據之前快速比較模型延遲是有用的。對於這種使用情境,校準數據可以是單個隨機樣本。雖然生成的量化模型的準確性會很差,但它的設備延遲與準確模型相同。
import numpy as np
import qai_hub as hub
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 = hub.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 = hub.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 = hub.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 = hub.submit_profile_job(
model=compiled_model,
device=device,
)
assert isinstance(profile_job, hub.ProfileJob)