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

AIダイエット失敗事件

スケールとゼロ点 — 量子化とは物差しを一本選ぶこと

TT Labで続きを見る

一言でいうと

量子化とは、実数の軸に目盛りの間隔(scale)と0の位置(zero_point)を決める作業です。その2つが決まれば、あとは割り算と丸めだけです。

なぜこれを知る必要があるのか

変換ツールを呼ぶとint8モデルが出てきます。精度が落ちたら、何を直せばよいのでしょうか。キャリブレーションデータを増やすのか、チャンネルごとに分けるのか、活性化値だけをfloatのまま残すのか。これらの選択肢はすべて、スケールがどう決まるのかについての選択です。内部でどんな算術が回っているかを知らなければ選べず、試行錯誤だけが残ります。

そして、実際に間違える場所があります。いちばん多いのは、0が0に戻ってこない場合です。観察範囲が[0.4, 7.0]だからとそのままスケールを決めると、実数0.0は表現できる目盛りの外に出てしまいます。パディングとReLUが作った0が、0でない値によみがえり、その誤差が層を伝って膨らみます。そのため、規格に従う実装は範囲に必ず0を含めます。

どう動くのか

ONNXは、2つの演算子でこれを行います。QuantizeLinearは実数を整数に下げ、DequantizeLinearは整数を実数に上げます。定義は短いものです。

QuantizeLinear     y = saturate(round(x / y_scale) + y_zero_point)
DequantizeLinear   x = (q - x_zero_point) * x_scale

roundは偶数側に寄せる丸めです。0.5は0に、1.5は2に、2.5は2になります。saturateはデータ型の両端で切ることで、uint8なら0と255、int8なら-128と127です。

スケールとゼロポイントは、観察範囲[lo, hi]から決まります。2つの方式があります。

実数の軸を整数の目盛りに折りたたむ2種類の物差し。非対称uint8は、観察範囲のマイナス0.5から2.0までにコード0から255を割り振り、実数0が落ちる位置がゼロポイントの51になります。対称int8は0を真ん中に固定するのでゼロポイントが0で、データが0以上しか出ないと、左半分の127目盛りが使われません

ここから、重要な性質が1つ導かれます。往復誤差は、スケールの半分を超えません。量子化は、値をスケールの倍数の格子の最も近い点に移す作業で、格子の間隔がスケールなので、いちばん離れていても半分です。ただし、範囲の中にあるときだけそうなります。外に出た値は端に張り付いてしまうので、誤差はいくらでも大きくなります。誤差がスケールの半分を大きく超えたなら、丸めの問題ではなく範囲の問題です。

現場での姿

1つ目は、活性化値を対称で扱うと半分を捨てることです。ReLUの後ろの活性化値は、すべて0以上です。ここに対称int8を適用すると、負の側の127個の目盛りがどんな値も受け取りません。残る目盛りは半分なので、スケールは2倍粗くなり、誤差もちょうど2倍になります。そのため、重みは対称、活性化値は非対称がデフォルトである場合が多いです。

2つ目は、外れ値が1つあるとテンソル全体が台無しになることです。重み行列で1か所だけ値が大きいと、テンソル全体のスケールがその値に合わせて大きくなり、残りの要素はすべて、数少ない目盛りの上に集中します。このとき答えになるのは、チャンネルごとのスケール(per-channel)です。出力チャンネルごとにスケールを別々に与えると、被害は外れ値のあるチャンネルの中に閉じ込められます。QuantizeLinearのaxis属性と1次元のスケールテンソルが、これを表現します。

3つ目は、スケールをfloat64で計算するとランタイムとずれることです。ランタイムはfloat32で割ります。Pythonのfloatで割った値が境界にかかると、丸めが別の側に行き、要素の1%ほどで1目盛りの差が出ます。大きな問題ではありませんが、「自分の計算とモデルの出力がなぜ違うのか」で時間を失う場所です。

4つ目は、誤差を測るには基準が必要なことです。「精度が1%落ちた」はデプロイの判断で、「往復誤差がスケールの半分を超えた」は原因の判断です。後者は層1つを見るだけでわかり、直す場所をすぐに指し示します。

実務で本当に大切なこと

次のラボですること

qmath.pyを1ステップずつ育てて、スケールとゼロポイントの計算、量子化、逆量子化、往復誤差、チャンネルごとのスケールを自分で実装します。採点ツールは、皆さんが書き出した数字を信じません。毎回違うシードで配列を作って皆さんのツールを実際に実行し、同じ値をONNXのQuantizeLinear・DequantizeLinear演算子にそのまま通して得られた結果と照合します。