基本的な量子化フロー#
はじめに
基本的な量子化フローは、8 ビット量子化をモデルに適用する最も簡単な方法です。次のフレームワーク・モデルで利用できます: OpenVINO、PyTorch、TensorFlow 2.x、および ONNX。基本的な量子化フローは次の手順に基づいています:
環境をセットアップし、依存関係をインストールします。
例えば 300 サンプルのモデル内の活性化の量子化パラメーターの推定に使用される代表的なキャリブレーション・データセットを準備します。
量子化 API を呼び出して、8 ビット量子化をモデルに適用します。
環境の設定#
NNCF で量子化するには、別に Python 環境をセットアップすることを推奨します。これには、次のコマンドを実行します:
python3 -m venv nncf_ptq_env
DL フレームワークなど、モデル・オブジェクトのインスタンス化に必要なすべてのパッケージをインストールします。その後、その環境に NNCF をインストールします:
pip install nncf
キャリブレーション・データセットを準備#
このステップでは、キャリブレーション・データセットを表す nncf.Dataset
クラスのインスタンスを作成します。nncf.Dataset
クラスは、モデルのトレーニングや検証に使用されるフレームワーク・データセット・オブジェクトのラッパーにすることができます。クラス・コンストラクターは、データセット・オブジェクトとオプションの変換関数を受け取ります。
変換関数は、データセットからサンプルを取得し、推論のためにモデルに渡すことが可能なデータを返す関数です。例えば、この関数はデータテンソルとラベルテンソルのタプルを受け取り、後者を無視して前者を返すことができます。変換関数は、量子化 API との互換性のためデータセット・コードの変更を避けるのに使用されます。この関数は、推論のためモデルに渡す前に、データセットの各サンプルに適用されます。次のコードは、nncf.Dataset
クラスのインスタンスを作成する方法を示しています:
import nncf
import torch
calibration_loader = torch.utils.data.DataLoader(...)
def transform_fn(data_item):
images, _ = data_item
return images.numpy()
calibration_dataset = nncf.Dataset(calibration_loader, transform_fn)
import nncf
import torch
calibration_loader = torch.utils.data.DataLoader(...)
def transform_fn(data_item):
images, _ = data_item
return images
calibration_dataset = nncf.Dataset(calibration_loader, transform_fn)
import nncf
import torch
calibration_loader = torch.utils.data.DataLoader(...)
def transform_fn(data_item):
images, _ = data_item
return {input_name: images.numpy()} # input_name はモデルから取得する必要があります # model.graph.input[0].name など
calibration_dataset = nncf.Dataset(calibration_loader, transform_fn)
import nncf
import tensorflow_datasets as tfds
calibration_loader = tfds.load(...)
def transform_fn(data_item):
images, _ = data_item
return images
calibration_dataset = nncf.Dataset(calibration_loader, transform_fn)
フレームワーク・データセット・オブジェクトがない場合は、Python の Iterable
インターフェイス (画像のリストなど) を実装する独自のエンティティーを作成し、推論に適したデータサンプルを返すことができます。この場合、変換関数は必要ありません。
モデルの量子#
データセットの準備が整い、モデル・オブジェクトがインスタンス化されたら、8 ビット量子化を適用できます。各フレームワークの例については、このドキュメントの最後にある例のセクションを参照してください。
import openvino as ov
model = ov.Core().read_model("model_path")
quantized_model = nncf.quantize(model, calibration_dataset)
import torchvision
model = torchvision.models.resnet50(pretrained=True)
quantized_model = nncf.quantize(model, calibration_dataset)
import onnx
model = onnx.load("model_path")
quantized_model = nncf.quantize(model, calibration_dataset)
import tensorflow as tf
model = tf.saved_model.load("model_path")
quantized_model = nncf.quantize(model, calibration_dataset)
その後、必要に応じてモデルを OpenVINO 中間表現 (IR) に変換し、OpenVINO でコンパイルして実行できます。OpenVINO 開発ツールをまだインストールしていない場合は、pip install openvino
を使用してインストールします。
# モデルをコンパイルして量子化演算を int8 に変換
model_int8 = ov.compile_model(quantized_model)
input_fp32 = ...# FP32 モデル入力
res = model_int8(input_fp32)
# モデルを保存
ov.save_model(quantized_model, "quantized_model.xml")
import openvino as ov
input_fp32 = ...# FP32 モデル入力
# Torch モデルを OpenVINO モデルに変換
ov_quantized_model = ov.convert_model(quantized_model, example_input=input_fp32)
# モデルをコンパイルして量子化演算を int8 に変換
model_int8 = ov.compile_model(ov_quantized_model)
res = model_int8(input_fp32)
# モデルを保存
ov.save_model(ov_quantized_model, "quantized_model.xml")
import openvino as ov
# ONNX モデルを OpenVINO モデルに変換
ov_quantized_model = ov.convert_model(quantized_model)
# モデルをコンパイルして量子化演算を int8 に変換
model_int8 = ov.compile_model(ov_quantized_model)
input_fp32 = ...# FP32 モデル入力
res = model_int8(input_fp32)
# モデルを保存
ov.save_model(ov_quantized_model, "quantized_model.xml")
import openvino as ov
# TensorFlow モデルを OpenVINO モデルに変換
ov_quantized_model = ov.convert_model(quantized_model)
# モデルをコンパイルして量子化演算を int8 に変換
model_int8 = ov.compile_model(ov_quantized_model)
input_fp32 = ...# FP32 モデル入力
res = model_int8(input_fp32)
# モデルを保存
ov.save_model(ov_quantized_model, "quantized_model.xml")
量子化パラメーターのチューニング#
nncf.quantize()
関数には、より正確なモデルを取得するため量子化プロセスを調整できるオプション・パラメーターがあります。以下にパラメーターのリストとその説明を示します:
model_type
- 特定のタイプのモデルに必要な量子化スキームを指定します。Transformer
は、トランスフォーマー・モデル (BERT、DistilBERT など) の量子化後に精度を維持するためにサポートされる唯一の量子化スキームです。None
がデフォルトです。つまり、特定のスキームは定義されていません。nncf.quantize(model, dataset, model_type=nncf.ModelType.Transformer)
preset
- モデルの量子化スキームを定義します。2 つのタイプのプリセットが利用できます:PERFORMANCE
(デフォルト) - 重みと活性化の対称量子化を定義しますMIXED
- 重みは対称量子化で量子化され、活性化は非対称量子化で量子化されます。このプリセットは、非 ReLU および非対称活性化関数を備えたモデル (ELU、PReLU、GELU など) に推奨されます。nncf.quantize(model, dataset, preset=nncf.QuantizationPreset.MIXED)
fast_bias_correction
-False
に設定すると、モデルの精度を向上させるより正確なバイアス (エラー) 補正アルゴリズムが有効になります。このパラメーターは、OpenVINO および ONNX 表現でのみ使用できます。True
はデフォルトであり、量子化時間を最小限に抑えます。nncf.quantize(model, dataset, fast_bias_correction=False)
subset_size
- 活性化の量子化パラメーターを推定するためキャリブレーション・データセットからのサンプル数を定義します。デフォルト値は 300 です。nncf.quantize(model, dataset, subset_size=1000)
ignored_scope
- このパラメーターは、モデルの精度を維持するため量子化プロセスから一部のレイヤーを除外します。例えば、モデルの最後のレイヤーを量子化から除外する場合などに使用します。以下に、このパラメーターの使用例をいくつか示します:レイヤー名で除外します:
names = ['layer_1', 'layer_2', 'layer_3'] nncf.quantize(model, dataset, ignored_scope=nncf.IgnoredScope(names=names))
レイヤータイプで除外します:
types = ['Conv2d', 'Linear'] nncf.quantize(model, dataset, ignored_scope=nncf.IgnoredScope(types=types))
正規表現で除外します:
regex = '.*layer_.*' nncf.quantize(model, dataset, ignored_scope=nncf.IgnoredScope(patterns=regex))
サブグラフによる除外:
subgraph = nncf.Subgraph(inputs=['layer_1', 'layer_2'], outputs=['layer_3']) nncf.quantize(model, dataset, ignored_scope=nncf.IgnoredScope(subgraphs=[subgraph]))
この場合、入力ノードから出力ノードまでのグラフ内のすべての単純パスに沿ったすべてのノードが量子化プロセスから除外されます。
target_device
- ターゲットデバイスを定義します。最適化中にその特性が考慮されます。次の値がサポートされます:ANY
(デフォルト)、CPU
、CPU_SPR
、GPU
、およびNPU
。nncf.quantize(model, dataset, target_device=nncf.TargetDevice.CPU)
advanced_parameters
- 量子化アルゴリズムを微調整する高度な量子化パラメーターを指定します。nncf.quantization.advanced_parameters NNCF サブモジュールによって定義されます。デフォルトはNone
です。
量子化モデルの精度に満足できない場合は、精度を備えた量子化制御フローを試みることができます。