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

AIダイエット失敗事件

較正データなしで — 動的量子化を実測する

TT Labで続きを見る

目標

dyntool.pyを作って自分のモデルに動的量子化をかけ、グラフがどう変わるか、ファイルがどこで減りどこがそのままか、入力の分布が変わってもなぜ耐えるか、そしてこの方式が合わない場合がどんな姿かを、すべて数字で残します。

なぜ重要なのか

静的量子化は、代表的な入力を集めないと始められず、そのデータを手に入れる作業が現場で最も時間がかかります。動的量子化は、活性化値の範囲を事前に決めず、推論のたびにそのテンソルを見て決めるので、モデルファイルが1つあればできます。そのため、何をするかを決める前に、まずかけてみるベースラインになります。 その代わり、何を得て何を失うかを知ってこそ選べます。得るものは、分布が揺れても耐える性質です。バッチごとに物差しを作り直すので、キャリブレーションのときに見た範囲の外に出ることがありません。失うものは再現性です。スケールがそのバッチ全体の最大値で決まるため、外れ値1行が同じバッチのほかの行まで引き下げます。単独で入れたときは問題なかった入力が、隣のせいで悪くなるのです。 ファイルサイズも、測ってみないとわかりません。減るのは行列だけで、バイアスはfloat32のまま残り、減った行列の隣には、スケールとゼロポイントが付いてきます。重みが小さいモデルでは、新しく入ったノードのほうが減った量より大きく、ファイルがかえって大きくなります。 採点ツールは、皆さんが書き出した数字を信じません。毎回異なるシードとシェイプでモデルを新しく作り、皆さんのツールを実際に実行して、DynamicQuantizeLinear演算子の実際の結果、そして採点ツールが自分で動かした推論結果と照合します。

ステップ

  1. /root/onnxq-dyn/gen_model.pyを作成して実行し、ファイルを作ってください(出力先: /root/onnxq-dyn/fp32.onnx)。
  2. /root/onnxq-dyn/dyntool.pyにquantizeを作り、ファイルを作ってください(出力先: /root/onnxq-dyn/dyn.onnx)。
  3. sizesを追加し、initializerごとのバイト数を数えて書いてください(書き込み先: /root/onnxq-dyn/size.json)。
  4. dqparamsを追加し、DynamicQuantizeLinearが推論のたびに行う計算を自分で実装してください。
  5. compareを追加し、入力の倍率を変えながら誤差を測って書いてください(書き込み先: /root/onnxq-dyn/batches.json)。
  6. outlierを追加し、外れ値1行がバッチを揺らすかどうかを測って書いてください(書き込み先: /root/onnxq-dyn/outlier.json)。
  7. ごく小さなモデルを作って量子化し、結果を書いてください(作成スクリプト: /root/onnxq-dyn/gen_small.py、書き込み先: /root/onnxq-dyn/unfit.json)。
  8. 1枚にまとめてください(出力先: /root/onnxq-dyn/report.json、/root/onnxq-dyn/report.md)。

参考

測る対象を手に入れる

/root/onnxq-dyn/gen_model.pyを作成して実行し、ファイルを作ってください(出力先: /root/onnxq-dyn/fp32.onnx)。バッチ軸は名前だけの動的軸で、行列2つとバイアス2つを持つMLPです。

onnx.helperでグラフを組み立て、onnx.checkerで確認してから保存してください。入力シェイプの最初の軸は、数字ではなく名前(「N」)にします。initializerは、numpy_helper.from_arrayで作ります。作ったあとで、onnxruntimeで一度動かしてみると確実です。

コマンド1行で重みだけを固定する

/root/onnxq-dyn/dyntool.pyにquantize <입력.onnx> <출력.onnx>を作り、ファイルを作ってください(出力先: /root/onnxq-dyn/dyn.onnx。プレースホルダーは入力ファイルと出力ファイルです)。応答には、変わったあとの演算子数と、消えた演算子、新しく入った演算子が必要です。

onnxruntime.quantizationのquantize_dynamicに、weight_typeをQInt8として渡せばよいです。キャリブレーションデータは入れません。入れる場所がありません。変わったグラフは、onnx.loadで開いて、nodeのop_typeを数えれば見えます。MatMulがどこへ行ったかを確認してみてください。

どこが減ったかを名前で言う

sizes <모델.onnx>を追加して、initializerごとにデータ型・要素数・バイト数を数えさせ、2つのモデルを比べた結果を、fp32_bytes・dyn_bytes・shrunk・kept_float・weight_bytes_before・weight_bytes_afterとして書いてください(書き込み先: /root/onnxq-dyn/size.json。プレースホルダーはモデルファイルです)。

onnx.loadのgraph.initializerを順に見て、numpy_helper.to_arrayで変換すれば、dtypeとnbytesが出ます。shrunkは、fp32にはあるのに動的モデルでは名前が消えたinitializerで、kept_floatは、そのままfloat32で残ったものです。バイアスがどちらにあるかを見てください。

推論のたびに物差しを作り直す計算

dqparams <입력.npy> <출력.npy>を追加し、DynamicQuantizeLinearが推論のたびに行う計算を自分で実装してください(プレースホルダーは入力ファイルと出力ファイルです)。スケール・ゼロポイント・範囲を出力し、量子化したuint8配列を保存します。

範囲には、必ず0を入れます。最小値が正なら0に下げ、最大値が負なら0に上げます。uint8なので目盛りは255個で、ゼロポイントは、実数の0.0が収まる整数の位置です。すべて正の配列と、すべて負の配列を入れてみると、ゼロポイントが両端に行くのが見えます。

入力を1000倍まで振ってみる

compare <fp32.onnx> <int8.onnx> <씨앗> <행> <배율>を追加し、シード20260917・行数32で、倍率0.05・1.0・50.0を測って、runsとrelative_spreadとして書いてください(書き込み先: /root/onnxq-dyn/batches.json。プレースホルダーは、シード、行数、倍率です)。

バッチを作る規則は、参考のセクションに固定されています。採点ツールが同じバッチを作り直す必要があるので、そのとおりに従ってください。relative_spreadは、3つのrelativeの値の最大値を最小値で割った値です。絶対誤差は入力の大きさに従って大きくなりますが、相対誤差はどうなるかを見てください。

隣の1行がバッチを揺らす

outlier <fp32.onnx> <int8.onnx> <씨앗> <행> <튀는값>を追加し、シード20260917・行数12・外れ値60.0で測って書いてください(書き込み先: /root/onnxq-dyn/outlier.json。プレースホルダーは、シード、行数、外れ値です)。

同じ12行を2回入れます。1回はそのまま、もう1回は下に外れ値の行を付けて。誤差は、最初の12行だけで測ります。外れ値の行そのものの誤差は数えません。そうしてこそ、「隣のせいで悪くなった」という言い方が成り立ちます。

小さなモデルではかえって大きくなる

ごく小さなモデルを作って量子化し(作成スクリプト: /root/onnxq-dyn/gen_small.py、小さなモデル: /root/onnxq-dyn/small.onnx、量子化後: /root/onnxq-dyn/small_dyn.onnx)、結果をsmall_fp32_bytes・small_dyn_bytes・grew・growth_bytes・dynamic_quantize_nodes・kept_float_opsとして書いてください(書き込み先: /root/onnxq-dyn/unfit.json)。

重みが数百バイトしかなければ、新しく入ったノードとスケール・ゼロポイントのinitializerのほうが、減った量より大きくなります。kept_float_opsは、fp32モデルと動的モデルの両方にある演算子です。変わらずに残ったものですね。growは文字列ではなく、ブール値です。

得たものと失ったものを1枚にまとめる

/root/onnxq-dyn/report.jsonにgraph_change・file・relative_error_spread・outlier_ratio・small_model_grewを書き、## 그래프가 어떻게 바뀌나、## 어디가 줄고 어디가 안 줄었나、## 보정 자료가 필요 없는 이유、## 동적 양자화가 맞지 않는 경우の4つのセクションでMarkdownのレポートを書いてください(出力先: /root/onnxq-dyn/report.md。韓国語の見出しは、順に「グラフがどう変わるか」「どこが減ってどこが減らなかったか」「キャリブレーションデータが不要な理由」「動的量子化が合わない場合」という意味です)。

前のステップで作ったJSONを読んで組み立てればよいです。fileのratioは、動的モデルのバイト数をfp32のバイト数で割った値です。レポートには、数字と一緒に、その数字が何を指すのかを1行ずつ書いてください。読む人は、皆さんの実験を見ていません。