TT Lab
はじめる
学ぶ 学習パス コース

AIダイエット失敗事件

AIダイエット失敗事件

TT Labで続きを見る

目標

手書き文字分類器を実際のINT8 ONNXに変換し、誤ったキャリブレーションのモデルを拒否します。Linux CPU上で精度・レイテンシ・メモリを測定し、MCU・NPU・GPUや実物の電力測定は行いません。

なぜ重要なのか

モデルが小さくても、入力の単位やキャリブレーションデータが間違っていると、正常なように実行されたまま誤答を出します。ファイルサイズ・精度・実行カーネル・レイテンシ・メモリを別々に確認してこそ、デプロイの判断に根拠が生まれます。Python・NumPy・JSONの基礎が必要です。モデルと依存関係は提供されるため、インターネットからのインストールは不要です。

ステップ

  1. 数字読み取り器の入力契約を書く—contract.jsonにfeatures=64、divisor=16、train=1077、calibration=360、test=360を整数で保存してください。提供されたdigits.npzの配列のシェイプとIDの個数を自分で確認してください。ほかのキーは入れません。
  2. 255で割っていた入力を直す—preprocess.pyのnormalize(pixels)を実装してください。uint8のN×64配列をfloat32に変換し、16で割った新しい配列を返します。入力のシェイプを保ち、元の配列は変更しません。ファイルを直接実行する必要はありません。
  3. キャリブレーションデータに評価サンプルが混ざらないようにする—calibration.jsonのidsキーに、提供されたids_calibrationの整数ID 360個を、重複なしで保存してください。順序は自由ですが、学習IDと評価IDは混ぜません。行位置と元のIDを混同しないでください。
  4. AIをダイエットさせる—quantize.pyを作成して実行し、candidate.onnxを作ってください。提供されたfp32.onnxをsource.onnxにコピーしたあと、calibration.jsonのIDに対応するx_calibrationをnormalizeで変換し、CalibrationDataReaderで読み込ませます。入力名はpixels、1サンプルのシェイプは1×64です。quantize_staticで、QDQ・活性化QInt8・重みQInt8・per_channel=Trueを使ってください。3つの線形層の整数の重み・実際の整数カーネルの実行・FP32に対する精度低下0.01以下を検査します。
  5. 間違えた数字の方向を記録する—metrics.pyのsummarize(predictions, labels)を実装してください。同じ長さで空でない、0–9の整数のリストを2つ受け取り、count・correct・accuracy・confusionだけを返します。countとcorrectは整数、accuracyは0–1の比率、confusionは実際のクラスを行、予測クラスを列とする整数の10×10配列です。対角和がcorrectです。
  6. 小さくなったが愚かになったモデルを拒否する—FP32・candidate.onnx・提供されたbad-calibration.onnxを、同じx_test/16でCPU推論し、y_testと比較してregression.jsonを作成してください。fp32・int8・badのそれぞれに、ステップ5の指標を保存し、reject_badには、FP32の精度からbadの精度を引いた値が0.01より大きいかどうかを、ブール値で書きます。文字列のtrueはブール値ではありません。精度を暗記して書かず、実行結果を記録してください。
  7. 小さくなったから速くなったとは言わない—benchmark.pyにlatency_stats(samples_us, batch_size)を実装してください。正で有限な時間のリストに対して、p50_usは中央値、p95_usはnearest-rankのceil(0.95×n)番目の値、samples_per_secondはbatch_size×n×1e6/時間の合計です。提供されたbench.py --work /root/quantizationを実行して、benchmark.jsonを作ってください。各モデル・バッチ1/32に、生の時間300個、3ラウンド、threads=1、unit=us/batch、データ・モデルのSHA-256、proc/VmHWM由来の正のpeak_rss_bytesが必要です。要約統計は、生のサンプルと一致している必要があります。
  8. 根拠と限界を一緒にデプロイする—release.jsonに、model=candidate.onnx、sha256=現在の候補のハッシュ、max_file_bytes=32768、max_accuracy_drop=0.01、target_benchmark_required=true、universally_faster=falseを保存してください。ほかのキーは入れません。実際のモデルが32KiB以下である必要があり、精度の回帰・統計・測定したモデルとの連結も、もう一度検査します。候補を変更した場合は、ステップ6–7の結果も作り直してください。

参考

作成・生成するファイルはすべて/root/quantizationの下です。まずmkdir -p /root/quantizationを実行してから、cd /root/quantizationを実行してください。Pythonは/opt/onnx-lab/bin/pythonを使います。スクリプトの実行例は/opt/onnx-lab/bin/python quantize.pyです。提供される素材フォルダーは/opt/lab/quantizationです。digits.npzには、x_train/x_calibration/x_test、y_train/y_calibration/y_test、ids_train/ids_calibration/ids_testの配列が入っています。fp32.onnxは基準モデル、bad-calibration.onnxは拒否するための比較群で、reference.json・splits.json・DATA-LICENSE.jsonは契約・分割・出典です。これらのバイナリ素材はイメージに入っているため、テキストとして書き直しません。候補のONNXも変換器が生成します。ステップ7のコマンドは、/opt/onnx-lab/bin/python /opt/lab/quantization/bench.py --work /root/quantizationです。benchmark.jsonは測定ヘルパーが生成するため、サンプルを手で作り込まないでください。採点は数値の計算とハッシュの連結を検査するだけで、測定の真実性を認証するものではありません。提出するソースとJSONはそれぞれ64KiB以下の通常ファイル、候補モデルは検査時に128KiB以下、最終的なデプロイ時に32KiB以下です。学生の関数は別のプロセスで呼び出されるため、出力ログを入れないでください。前のステップの関数が必要な場合は、インポートに対応した正解例と実行スクリプトを参考にしてください。関数の検査の制限時間は、1回の呼び出しあたり10秒です。ラボの時間が足りない場合は+時間で延長し、終了前にファイルを別に保管してください。セッション終了後、ファイルは残りません。

数字読み取り器の入力契約を書く

contract.jsonにfeatures=64、divisor=16、train=1077、calibration=360、test=360を整数で保存してください。提供されたdigits.npzの配列のシェイプとIDの個数を自分で確認してください。ほかのキーは入れません。

np.load(..., allow_pickle=False)とdata.filesで配列の名前を見て、shapeとlenで契約を確認してください。

255で割っていた入力を直す

preprocess.pyのnormalize(pixels)を実装してください。uint8のN×64配列をfloat32に変換し、16で割った新しい配列を返します。入力のシェイプを保ち、元の配列は変更しません。ファイルを直接実行する必要はありません。

データ型の変換は、割り算より先に行ってください。画像だからという理由で255を使うと、このモデルの入力と変わってしまいます。

キャリブレーションデータに評価サンプルが混ざらないようにする

calibration.jsonのidsキーに、提供されたids_calibrationの整数ID 360個を、重複なしで保存してください。順序は自由ですが、学習IDと評価IDは混ぜません。行位置と元のIDを混同しないでください。

NumPyの整数配列は、tolist()でJSONに書き込めるリストに変換できます。

AIをダイエットさせる

quantize.pyを作成して実行し、candidate.onnxを作ってください。提供されたfp32.onnxをsource.onnxにコピーしたあと、calibration.jsonのIDに対応するx_calibrationをnormalizeで変換し、CalibrationDataReaderで読み込ませます。入力名はpixels、1サンプルのシェイプは1×64です。quantize_staticで、QDQ・活性化QInt8・重みQInt8・per_channel=Trueを使ってください。3つの線形層の整数の重み・実際の整数カーネルの実行・FP32に対する精度低下0.01以下を検査します。

get_next()は、入力の辞書を1つずつ返し、最後まで読み終えたらNoneを返します。保護された元ファイルの隣には一時ファイルを書き込めないため、作業用のコピーを使ってください。

間違えた数字の方向を記録する

metrics.pyのsummarize(predictions, labels)を実装してください。同じ長さで空でない、0–9の整数のリストを2つ受け取り、count・correct・accuracy・confusionだけを返します。countとcorrectは整数、accuracyは0–1の比率、confusionは実際のクラスを行、予測クラスを列とする整数の10×10配列です。対角和がcorrectです。

実際の1を2と予測すると、[1][2]が増えます。行と列を入れ替えても精度は同じなので、非対角のセルを確認してください。

小さくなったが愚かになったモデルを拒否する

FP32・candidate.onnx・提供されたbad-calibration.onnxを、同じx_test/16でCPU推論し、y_testと比較してregression.jsonを作成してください。fp32・int8・badのそれぞれに、ステップ5の指標を保存し、reject_badには、FP32の精度からbadの精度を引いた値が0.01より大きいかどうかを、ブール値で書きます。文字列のtrueはブール値ではありません。精度を暗記して書かず、実行結果を記録してください。

ORTのCPUExecutionProviderと、intra/interのスレッド数1を使い、出力のargmax(axis=1)をsummarizeに渡してください。

小さくなったから速くなったとは言わない

benchmark.pyにlatency_stats(samples_us, batch_size)を実装してください。正で有限な時間のリストに対して、p50_usは中央値、p95_usはnearest-rankのceil(0.95×n)番目の値、samples_per_secondはbatch_size×n×1e6/時間の合計です。提供されたbench.py --work /root/quantizationを実行して、benchmark.jsonを作ってください。各モデル・バッチ1/32に、生の時間300個、3ラウンド、threads=1、unit=us/batch、データ・モデルのSHA-256、proc/VmHWM由来の正のpeak_rss_bytesが必要です。要約統計は、生のサンプルと一致している必要があります。

測定はヘルパーが行い、統計関数は自分で作ります。このステップの実行コマンドは、参考のセクションにあります。ファイルサイズと、プロセス全体のHWMを混同しないでください。

根拠と限界を一緒にデプロイする

release.jsonに、model=candidate.onnx、sha256=現在の候補のハッシュ、max_file_bytes=32768、max_accuracy_drop=0.01、target_benchmark_required=true、universally_faster=falseを保存してください。ほかのキーは入れません。実際のモデルが32KiB以下である必要があり、精度の回帰・統計・測定したモデルとの連結も、もう一度検査します。候補を変更した場合は、ステップ6–7の結果も作り直してください。

hashlib.sha256でモデルのバイト列をハッシュしてください。この実験のファイルの予算はRSSの予算ではなく、対象装置での性能検証を完了したとは書きません。