量子化デバッガでの量子化誤差の調査

TensorFlow.org で表示 Google Colab で実行 GitHub でソースを表示 ノートブックをダウンロード TF Hub モデルを参照

完全な整数量子化では、モデルサイトとレイテンシが改善されますが、量子化されたモデルが必ずしも想定どおりに動作するとはかぎりません。通常、想定されるモデルの質 (例: 精度、mAP、WER) は、元の浮動小数点数モデルよりも少し低い程度です。ただし、モデルの質が想定を下回ったり、完全に誤った結果が生成される場合もあります。

この問題が発生すると、量子化量子化誤差の根本原因を特定するのが難しく、手間がかかります。そのうえ、量子化量子化誤差の修正はさらに困難です。このモデル調査プロセスを支援するために、量子化デバッガを使用すると、問題のあるレイヤーを特定できます。また、選択的量子化では、これらの問題のあるレイヤーを浮動小数点数にし、量子化の利点を抑えることで、モデルの精度を回復することができます。

注意: この API は実験段階であり、改良の過程で API が大きく変更される可能性があります。

量子化デバッガ

量子化デバッガでは、既存のモデルの量子化品質メトリック分析を実行できます。量子化デバッガは、デバッグデータセットを使用して、実行中のモデルのプロセスを自動化し、各テンソルの量子化品質メトリックを収集できます。

注意: 現在、量子化デバッガと選択的量子化は、int8 アクティベーションの完全な整数量子化でのみ動作します。

前提条件

モデルを量子化するパイプラインがすでにある場合は、量子化デバッガを実行する前提条件がすべて整っています。

  • 量子化するモデル
  • 代表データセット

モデルとデータのほかに、データ処理フレームワーク (例: pandas、Google Sheets) を使用して、エクスポートされた結果を分析する必要があります。

設定

このセクションでは、ライブラリ、MobileNet v3 モデルを準備し、100 画像のデータセットをテストします。

# Quantization debugger is available from TensorFlow 2.7.0
pip uninstall -y tensorflow
pip install tf-nightly
pip install tensorflow_datasets --upgrade  # imagenet_v2 needs latest checksum
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import tensorflow as tf
import tensorflow_datasets as tfds
import tensorflow_hub as hub

Boilerplates and helpers

test_ds = ds.map(lambda data: (data['image'], data['label'] + 1)).batch(16)
loss, acc = model.evaluate(test_ds)
print(f'Top-5 accuracy (float): {acc * 100:.2f}%')
eval_tflite(quantized_model, ds)

最小データセットでは、元のモデルの精度は圧倒的に高い上位 5 であることがわかりますが、量子化されたモデルでは大幅な精度損失が見られます。

ステップ 1. デバッガの準備

量子化デバッガを使用する最も簡単な方法は、モデルを量子化するために使用している tf.lite.TFLiteConverter を提供することです。

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset(ds)

# my_debug_dataset should have the same format as my_representative_dataset
debugger = tf.lite.experimental.QuantizationDebugger(
    converter=converter, debug_dataset=representative_dataset(ds))

ステップ 2. デバッガの実行と結果の取得

QuantizationDebugger.run() を呼び出すと、デバッガは、同じ演算位置の浮動小数点数テンソルと量子化されたテンソルの差異を記録し、特定のメトリックを使用して差異を処理します。

debugger.run()

処理されたメトリックにアクセスするには、QuantizationDebugger.layer_statistics を使用します。また、QuantizationDebugger.layer_statistics_dump() を使用して、CSV 形式でテキストファイルに落とすこともできます。

RESULTS_FILE = '/tmp/debugger_results.csv'
with open(RESULTS_FILE, 'w') as f:
  debugger.layer_statistics_dump(f)
head /tmp/debugger_results.csv

CSV ファイルの各行では、演算名とインデックスが先頭に入力され、その後に量子化パラメータと量子化誤差メトリック (ユーザー定義量子化誤差メトリックを含む) が入力されます。結果の CSV を使用すると、大規模な量子化量子化誤差メトリックによって、問題のあるレイヤーを特定できます。

pandas または他のデータ処理ライブラリを使用すると、詳細なレイヤー単位の量子化誤差メトリックを調査できます。

layer_stats = pd.read_csv(RESULTS_FILE)
layer_stats.head()

ステップ 3. データ分析

結果を分析するには、さまざまな方法を使用できます。まず、デバッガの出力から得られる有益なメトリックをいくつか追加します。(scale は各テンソルの量子化倍率を指します。)

  • Range (256 / scale)
  • RMSE / scale (sqrt(mean_squared_error) / scale)

量子化された分布が元の浮動小数点数分布と類似している場合、RMSE / scale1 / sqrt(12) (~ 0.289) に近くなります。この値が大きいほど、レイヤーが効果的に量子化されない確率が高くなります。

layer_stats['range'] = 255.0 * layer_stats['scale']
layer_stats['rmse/scale'] = layer_stats.apply(
    lambda row: np.sqrt(row['mean_squared_error']) / row['scale'], axis=1)
layer_stats[['op_name', 'range', 'rmse/scale']].head()
plt.figure(figsize=(15, 5))
ax1 = plt.subplot(121)
ax1.bar(np.arange(len(layer_stats)), layer_stats['range'])
ax1.set_ylabel('range')
ax2 = plt.subplot(122)
ax2.bar(np.arange(len(layer_stats)), layer_stats['rmse/scale'])
ax2.set_ylabel('rmse/scale')
plt.show()

範囲が広いレイヤーが多数存在し、一部のレイヤーは高い RMSE/scale 値を示しています。量子化誤差メトリックが高い値のレイヤーを取得します。

layer_stats[layer_stats['rmse/scale'] > 0.7][[
    'op_name', 'range', 'rmse/scale', 'tensor_name'
]]

これらのレイヤーでは、選択的量子化を試し、レイヤーを量子化しないことでモデルの質が改善されるかどうかを確認することができます。

suspected_layers = list(
    layer_stats[layer_stats['rmse/scale'] > 0.7]['tensor_name'])

さらに、最初の数レイヤーで量子化をスキップすると、量子化されたモデルの質を改善できます。

suspected_layers.extend(list(layer_stats[:5]['tensor_name']))

選択的量子化

選択的量子化は、一部のノードで量子化をスキップするため、元の浮動小数点数ドメインで計算が実行されることがあります。正しいレイヤーがスキップされると、レイテンシが長くなり、モデルサイズが大きくなりますが、モデルの質がある程度回復されることを期待できます。

ただし、整数のみのアクセラレータ (例: Hexagon DSP、EdgeTPU) で量子化されたモデルを実行する計画の場合は、選択的量子化によって、モデルの断片化が発生し、CPU とアクセラレータ間のデータ転送コストが主な原因で、推論レイテンシが低下します。これを防止するには、量子化認識トレーニングを実行して、モデル精度を維持しながら、整数ですべてのレイヤーを保持することを検討できます。

量子化デバッガでは、特定のレイヤー、または特定の演算のすべてのインスタンスで量子化をスキップするためのオプションとして、denylisted_nodes および denylisted_ops オプションを使用できます。前のステップで準備した suspected_layers を使用して、量子化デバッガを使用し、選択的に量子化されたモデルを取得できます。

debug_options = tf.lite.experimental.QuantizationDebugOptions(
    denylisted_nodes=suspected_layers)
debugger = tf.lite.experimental.QuantizationDebugger(
    converter=converter,
    debug_dataset=representative_dataset(ds),
    debug_options=debug_options)
selective_quantized_model = debugger.get_nondebug_quantized_model()
eval_tflite(selective_quantized_model, ds)

元の浮動小数点数モデルと比較すると、精度はまだ低めですが、111 レイヤー中の ~10 レイヤーで量子化をスキップするため、完全に量子化されたモデルから大幅に改善されています。

同じクラスのすべての演算を量子化しないこともできます。たとえば、すべての mean 演算で量子化をスキップするには、MEANdenylisted_ops に渡します。

debug_options = tf.lite.experimental.QuantizationDebugOptions(
    denylisted_ops=['MEAN'])
debugger = tf.lite.experimental.QuantizationDebugger(
    converter=converter,
    debug_dataset=representative_dataset(ds),
    debug_options=debug_options)
selective_quantized_model = debugger.get_nondebug_quantized_model()
eval_tflite(selective_quantized_model, ds)

このような手法では、量子化された MobileNet V3 モデル精度を改善できます。次に、モデルの精度をさらに改善するための高度な手法について説明します。

高度な使用

次の機能を使用すると、デバッグパイプラインをさらにカスタマイズできます。

カスタムメトリック

既定では、量子化デバッガは、float-quant 差異ごとに、5 つのメトリック (テンソルサイズ、標準偏差、平均誤差、最大絶対誤差、平均二乗誤差) を発行します。その他のカスタムメトリクスを追加するには、それらをオプションに渡します。各メトリックにおいて、結果は単一の浮動小数点数値です。結果のメトリックは、すべての例のメトリックの平均です。

  • layer_debug_metrics: 浮動小数点数からの演算出力と量子化された演算出力ごとの差異に基づいて、メトリックを計算します。
  • layer_direct_compare_metrics: 差異のみを取得するのではなく、未加工の浮動小数点数テンソルと量子化されたテンソル、および量子化パラメータ (scale、zero point) に基づいて、メトリックを計算します。
  • model_debug_metrics: float_model_(path|content) がデバッガに渡されるときにのみ使用されます。演算レベルのメトリックのほかに、最終レイヤー出力が元の浮動小数点モデルの参照出力と比較されます。
debug_options = tf.lite.experimental.QuantizationDebugOptions(
    layer_debug_metrics={
        'mean_abs_error': (lambda diff: np.mean(np.abs(diff)))
    },
    layer_direct_compare_metrics={
        'correlation':
            lambda f, q, s, zp: (np.corrcoef(f.flatten(),
                                             (q.flatten() - zp) / s)[0, 1])
    },
    model_debug_metrics={
        'argmax_accuracy': (lambda f, q: np.mean(np.argmax(f) == np.argmax(q)))
    })

debugger = tf.lite.experimental.QuantizationDebugger(
    converter=converter,
    debug_dataset=representative_dataset(ds),
    debug_options=debug_options)
debugger.run()
CUSTOM_RESULTS_FILE = '/tmp/debugger_results.csv'
with open(CUSTOM_RESULTS_FILE, 'w') as f:
  debugger.layer_statistics_dump(f)

custom_layer_stats = pd.read_csv(CUSTOM_RESULTS_FILE)
custom_layer_stats[['op_name', 'mean_abs_error', 'correlation']].tail()

model_debug_metrics の結果は、debugger.model_statistics とは別に確認できます。

debugger.model_statistics

(内部) mlir_quantize API を使用して詳細な機能にアクセスする

注意: 次のセクションの一部の機能である TFLiteConverter._experimental_calibrate_onlyconverter.mlir_quantizeは、実験段階の内部 API であり、後方互換性が保証されない方法で変更される場合があります。

from tensorflow.lite.python import convert

モデル全体の検証モード

デバッグモデル生成の既定の動作は、レイヤー単位の検証です。このモードでは、浮動小数点数演算と量子化演算のペアに対する入力元は、同じソース (前の量子化された演算) です。もう一つのモデルは、モデル全体の検証で、浮動小数点数モデルと量子化が分離されます。このモードは、誤差がどのようにモデルに反映されるのかを観察するときに、役立ちます。有効にするには、デバッグモードを手動で生成しながら、enable_whole_model_verify=Trueconvert.mlir_quantize に設定します。

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.representative_dataset = representative_dataset(ds)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter._experimental_calibrate_only = True
calibrated_model = converter.convert()
# Note that enable_numeric_verify and enable_whole_model_verify are set.
quantized_model = convert.mlir_quantize(
    calibrated_model,
    enable_numeric_verify=True,
    enable_whole_model_verify=True)
debugger = tf.lite.experimental.QuantizationDebugger(
    quant_debug_model_content=quantized_model,
    debug_dataset=representative_dataset(ds))

すでにキャリブレーションされたモデルからの選択的量子化

直接 convert.mlir_quantize を呼び出して、すでにキャリブレーションされたモデルから選択的量子化モデルを取得できます。これは、モデルを 1 回キャリブレーションし、さまざまな denylist を組み合わせて実験したいときに、特に役立ちます。

selective_quantized_model = convert.mlir_quantize(
    calibrated_model, denylisted_nodes=suspected_layers)
eval_tflite(selective_quantized_model, ds)