状態:ドラフト(2026-10-16)、一部実測値。 GPUの測定は、ホームラボの8GBクラスのノートPC用GPU1枚で使い捨てPodを使用して行いました(ウォームアップ後の繰り返しの中央値)。精度比較は公開データセット OpenTTGames のテストマッチ1試合(マーカー付き60fpsフレーム388枚)です。INT8は同PodのCPU(4コア制限)でPyTorchの事後量子化を用いて行いました。形式と較正の原理はノートPCのCPUの例(
code/02_quant_error.py、1秒前後)で確認しました。 シリーズ「ビジョンモデルを読む 3」の第2部。レイテンシーバジェットは 第1部にあります。
目次
- 1. なぜビット数を減らすと高速になるのか
- 2. 16ビットの2種類:FP16とBF16
- 3. INT8:実数を目盛りに合わせる
- 4. 事後量子化(PTQ)と較正データ
- 5. 量子化認識学習(QAT)
- 6. 自前の実測:ボールモデルのFP16
- 7. 自前の実測:ボールモデルのINT8事後量子化(CPU)
- 8. 限界
- 9. 自前のプロジェクトとのつながり
- 参考文献
1. なぜビット数を減らすと高速になるのか
ニューラルネットワークの推論にかかる時間は、大半が2つの要素に起因します。メモリから数値を読み込む時間と、乗算および加算を行う時間です。数値を1つ、32ビットから16ビットに減らすと、読み込むバイト数が半分になります。さらに、最近のGPUやモバイルチップには、16ビットや8ビットの乗算を一度に大量に行える専用プロセッサが搭載されています。NVIDIAの2018年世代のGPU仕様では、INT8の演算スループットはFP32の16倍、FP16は8倍です(Wu他 2020、表1 — 仕様上の比率であり実測値ではありません)。
その代償となるのが精度です。数値を粗く表現すると丸め誤差が生じ、範囲が狭いと大きな数値がオーバーフローします。
2. 16ビットの2種類:FP16とBF16
浮動小数点数は、符号・指数・仮数に分かれます。指数は「どれほど大きまたは小さな数値まで表せるか」、仮数は「どれほど細かく表せるか」を決定します。
| 形式 | 符号・指数・仮数 | 最大値 | 1の次の値との間隔(eps) |
|---|---|---|---|
| FP32 | 1 · 8 · 23 | 3.4×10³⁸ | 1.19×10⁻⁷ |
| FP16 | 1 · 5 · 10 | 65,504 | 9.77×10⁻⁴ |
| BF16 | 1 · 8 · 7 | 3.4×10³⁸ | 7.81×10⁻³ |
(code/02_quant_error.out.txt におけるPyTorchの値。)FP16は精度が高いものの範囲が狭く、BF16はFP32と範囲が同じである代わりに精度が粗いです。実際に代入してみると、70,000はFP16ではinfになり、BF16では70,144になり、1e-8はFP16では0になり、BF16では1.00117e-8になります。256×256の行列乗算の相対誤差は、FP16が3.6×10⁻⁴、BF16が2.9×10⁻³でした。
学習においては、この範囲の問題が大きくなります。Micikevicius他による「Mixed Precision Training」(2018)では、FP16において2⁻²⁴より小さい勾配が0になってしまう問題を、損失に大きな数を掛けた後に割り算を行う ロススケーリングによって解決し、重みのFP32の複製を別途保持しました。これにより、ResNet-50のImageNet top-1精度は、FP32の75.92と混合精度での76.04で同等でした(表1)。BF16は範囲がFP32と同じであるためロススケーリングなしで使用でき、Kalamkar他(2019)はResNet-50をBF16で学習させ、FP32と同等の74.7を達成しました(同一の評価手法同士での比較、4.1.2節)。
推論ではすでに学習済みの重みを使用するため、範囲の問題は小さくなります。活性化値が65,504を超えない限り、多くの場合FP16で十分です。
3. INT8:実数を目盛りに合わせる
8ビット整数には256個の枠しかありません。Jacob他(2018)のアフィン量子化は、実数 r を次のように記述します。
r = S · (q − Z)
S: 눈금 한 칸의 크기(실수), Z: 실수 0 에 해당하는 정수(영점), q: 0–255 의 정수
범위 [최솟값, 상한] 을 정하면 S = (상한 − 최솟값) / 255, Z = round(−최솟값 / S)
実数の0が正確に1つの整数として表されるように Z を配置することがポイントです。0で埋めるパディングやReLUの背後にある0が誤差なく表現されます。乗算は uint8 × uint8 を int32 で加算した後に最後に一度だけ倍率を掛け直して再び8ビットに縮小するため、推論全体を整数のみで実行できます。同論文では、バッチ正規化を畳み込みにあらかじめ畳み込んだ(第3部)グラフを量子化しています。
| 結果(Jacob他 2018) | 実数 | INT8 | 条件 |
|---|---|---|---|
| ResNet-50 ImageNet top-1 | 76.4 | 74.9 | 表4.1 |
| MobileNet SSD(幅50%) COCO mAP · レイテンシー | 16.7 · 121ms | 16.6 · 61ms | 2017年のモバイル向け大コア、単一スレッド(表4.4) |
| 顔検出(幅25%)レイテンシー | 44ms(約23fps) | 28ms(約36fps) | 同一のモバイル向け大コア1つ(表4.6) |
同じ33msのバジェット内であれば、INT8モデルの方が実数モデルよりも約10%精度が高かったというのがこの論文の要点です(4.2.1節、モバイルの小さなコア)。精度を下げて浮いた時間で、より大きなモデルを実行できるためです。
目盛りをいくつ設けるか:テンソル全体かチャンネルごとルか
重みテンソル全体に S を1つだけ使用すると、特定のチャンネルの値が大きいだけで目盛りがそのチャンネルに合わせて広がり、残りが潰れてしまいます。出力チャンネルごとに S を個別に設けると(per-channel)、この問題が解消されます。実際に試してみたところ(256×256行列乗算、重みのみINT8)、
| 重み | テンソル全体 | チャンネルごと |
|---|---|---|
| 均一な重み | 1.06×10⁻² | 7.13×10⁻³ |
| 1つのチャンネルだけ20倍 | 7.25×10⁻²(その他のチャンネル 1.13×10⁻¹) | 6.32×10⁻³ |
となります。実際のモデルでも同様の現象が発生します。Krishnamoorthi(2018)のホワイトペーパーでは、MobileNet-v1を学習なしで8ビットに変更した場合、テンソル全体の目盛りではtop-1が0.001(完全に崩壊)、チャンネルごとの目盛りでは0.703(実数は0.709)でした(表3)。深度方向別畳み込み(第4部)はチャンネルごとに値の大きさが大きく異なるため、この問題に特に脆弱です。
4. 事後量子化(PTQ)と較正データ
事後量子化は、学習が完了したモデルに代表的な入力データを数百枚流し込み、レイヤーごとに活性化値の範囲を測定し(較正、calibration)、その範囲から S と Z を決定します。学習は不要です。Nagel他(2021)のホワイトペーパーでは、ラベルなし画像が500〜1,000枚あれば通常は十分であると述べられており、標準的な手順に従えば W8A8(重み・活性化値ともに8ビット)で扱ったすべてのモデルにおいてFP32の0.7%の範囲内であったと報告されています。
難しいのは 範囲をどこまで設定するかです。最小値と最大値をそのまま使用すると、稀にある1つの大きな値のせいで目盛りが粗くなってしまいます。上部を少し切り詰める(例:99.99パーセンタイル)ことで範囲を狭めると、目盛りは細かくなりますが、切り捨てられた値が不正確になります。2種類の活性化値を用いて実際に試してみました(較正用サンプルとテスト用サンプルは別途抽出)。
| 活性化値 | 較正バッチ | 最小値〜最大値 | 99.99パーセンタイル | 99.9パーセンタイル |
|---|---|---|---|---|
| (ア)裾野の長い分布 | 1 | 上限 23.1、誤差 0.057 | 21.0, 0.067 | 11.0, 0.158 |
| (ア) | 64 | 上限 215.1、誤差 0.144 | 22.7, 0.059 | 10.3, 0.170 |
| (イ)稀にある大きな値がシグナルとなる場合 | 64 | 上限 109.4、誤差 0.076 | 51.0, 0.180 | 4.1, 0.707 |
- (ア)において、最小値〜最大値は 較正データを増やすほど悪化しました。 サンプルが多いほど、より大きな極端な値に遭遇し、範囲が 23 → 215 に拡大したためです。99.99パーセンタイルはサンプル数に関係なく安定していました。
- (イ)ではその逆となります。稀にある大きな値が結果を作り出す場合、それを切り捨てると大きく狂ってしまいます。言語モデルの「外れ値特徴」がまさにその例であり、Dettmers他(2022)はその0.1%の値を削除するとモデルが崩壊することを示しました。
そのため、較正方法はモデルごとに検証する必要があります。Wu他(2020)の表によると、EfficientNet-B0に対してPTQを適用した場合、最小値〜最大値が22.3%、エントロピーが72.1%、99.9パーセンタイルが70.9%であり(FP32は76.9%)、Inception v4では最小値〜最大値が0.1%、99.99パーセンタイルが79.6%でした(FP32は79.7%)(ImageNet top-1、較正画像1,024枚、表5)。
5. 量子化認識学習(QAT)
PTQでは不十分な場合、学習の段階から量子化を模倣します。順伝播において、重みと活性化値を「量子化してから元に戻した」値(偽量子化値)として計算し、逆伝播では丸め処理が存在しないかのように勾配をそのまま流します。モデルが目盛りに合わせて重みを調整するように学習させる仕組みです。Krishnamoorthiのホワイトペーパーでは、テンソル全体の目盛りで崩壊していたMobileNet-v1(0.001)が、QATによって0.70まで回復し(表4)、Wu他ではEfficientNet-B0がQATにより76.95%となり、PTQの最良値(72.1%)を上回りました(元の学習スケジュールの約10%分をファインチューニング、表7)。その代償として、学習パイプラインとデータが必要になる点があげられます。
6. 自前の実測:ボールモデルのFP16
速度
TT Scoreのボールモデル(BlurBall、3フレームウィンドウ、512×288)を実験用GPUで測定しました(バッチ1、ウォームアップ20回後の250回の中央値)。
| 精度 | 単一ウィンドウ |
|---|---|
| FP32(運用方式) | 15.09ms |
| FP16、autocast(演算ごとに自動で16ビット化) | 9.77ms(1.54倍) |
| FP16、モデル全体をhalf化 | 9.49ms(1.59倍) |
同じGPU上で、トランスフォーマーエンコーダー(EfficientTAM-S、第4部)は3.1倍でした。ボールモデルは解像度を最後まで維持する畳み込みネットワークであるため、16ビット専用プロセッサの恩恵が相対的に小さく、バッチ正規化やReLUといった要素ごとの演算が残っているためです。第3部でこれらをコンパイラによって統合したところ、4.3msまで低下しました。
精度:ボールの位置が変わるか
OpenTTGamesのテストマッチ1試合において、ボールの位置マーカーが付いた60fpsフレーム388枚(すべてボールが存在)を対象に、運用時と同じ方式(最後のフレームのヒートマップ、閾値0.5でのブロブ検出)でFP32とFP16を同じ入力に対して実行しました。正解から5px(512×288基準)の範囲内であれば正解とみなします。同じフレームをJPEG品質70で圧縮・展開した入力(ブラウザが送信するものと同一の条件)に対してもFP32を実行しました。
| 入力・精度 | 正解 / 誤検知 / 検出漏れ | 精度 · 再現率 | FP32と検出有無が同じフレーム | ヒートマップの最大値位置が動いたフレーム |
|---|---|---|---|---|
| オリジナル · FP32 | 384 / 2 / 4 | 0.995 · 0.990 | — | — |
| オリジナル · FP16(autocast) | 384 / 2 / 4 | 0.995 · 0.990 | 100% | 4.6% (最大2px) |
| オリジナル · FP16(half) | 384 / 2 / 4 | 0.995 · 0.990 | 100% | 0.8% (最大1px) |
| JPEG 70 · FP32 | 377 / 1 / 11 | 0.997 · 0.972 | 97.9% | 34.3% |
FP16はこの388枚の中で判定を1つも変えませんでした。 ヒートマップの値の差は中央値で0.0001水準でした。一方、JPEG圧縮は検出漏れのボールを4個から11個に増やし、最大値の位置をフレームの3分の1ほど動かしました。精度を半分に減らすことよりも、 入力を圧縮する方がはるかに大きな誤差でした。運用環境ではすでにJPEGを受け取っているため、FP16を有効にしても現在と大きく変わることはないと考えています。ただし、これは1つの試合・条件における結果であり、劣化条件(暗い・かすんでいる)や運用GPUでは測定していません(未計測)。
7. 実測結果:ボールモデルのINT8事後量子化(CPU)
GPUでのINT8はTensorRTのようなランタイムが必要となるため、今回は測定できませんでした(パート3)。代わりにPyTorchの事後量子化(FXグラフ方式、x86整数エンジン)を用いて、同じモデルをCPU上でINT8に変更しました。キャリブレーションフレームは試合の前半、評価フレームは後半から重複しないように選択しました。
評価フレーム220枚(ボールあり215枚)、キャリブレーション候補146枚、CPU 4コア制限の条件で、4枚ずつまとめて処理した際の時間の中央値です。
| 方式 | キャリブレーションフレーム | 正解 / 誤検知 / 検出漏れ | 再現率 | ヒートマップの最大値の差(対FP32、中央値) | CPU処理時間/枚 |
|---|---|---|---|---|---|
| FP32 | — | 208 / 3 / 7 | 0.967 | — | 519.9ms |
| INT8、デフォルト(アクティベーションヒストグラム、重みはチャネルごと) | 1 | 206 / 3 / 9 | 0.958 | 0.064 | 72.2ms |
| 同等 | 8 | 207 / 3 / 8 | 0.963 | 0.056 | 71.3ms |
| 同等 | 64 | 208 / 3 / 7 | 0.967 | 0.057 | 63.1ms |
| INT8、最小値-最大値、重みテンソルごと | 8 | 207 / 3 / 8 | 0.963 | 0.086 | 71.4ms |
| INT8、最小値-最大値、重みはチャネルごと | 8 | 208 / 2 / 7 | 0.967 | 0.084 | 72.5ms |
- キャリブレーションフレームが64枚の場合、INT8はFP32と同じ判定数(正解208)を出し、1枚の場合はさらに2枚多く検出を逃しました。このモデルは、1枚のキャリブレーションでも破綻しないほど安定していました。
- ヒートマップの値の差はFP16(0.001水準)よりも50倍ほど大きかったのですが、閾値0.5での判定はほとんど変わりませんでした。ヒートマップ上のボールの位置の値が、大体において閾値から十分離れているためです。
- CPU時間はFP32の約7分の1でした。ただし、この倍率のすべてが8ビット化の恩恵だとは言い切れません。同じCPU上でFP32のパスがこのモデル(解像度を最後まで維持する畳み込み層)に対して異様に遅かった可能性があり、その原因は別途確認していません(未計測)。この差を差し引いても、同じモデルがGPUのFP16で10msであるため、CPUのINT8における72msはリアルタイムのサーバーパスの代替案にはなりません。スマートフォンCPUでの値は測定していません。
8. 制限事項
- 精度比較は公開された1つの試合、サイドカメラ、クリーンな条件に基づいています。ボールがかすんでいたり暗いフレームにおいて、16ビットや8ビットの誤差がさらに大きくなるかどうかは測定していません。
- 「判定が同じ」とは、閾値0.5に基づく領域検出の基準によるものです。閾値に近いボール(信頼度が0.5付近)は、わずかな誤差でも判定が覆る可能性があります。
- 論文の数値はそれぞれのデータおよびハードウェアにおける結果であり、その倍率をそのまま私たちのモデルに適用することはできません。
9. 本プロジェクトとの関連性
- 運用担当者の環境では現在FP32を使用しています。セクション6の結果から、FP16は判定を変えることなく処理時間を約35%削減します。有効化する前に、複数の試合・劣化条件・運用GPUにおいて同様の比較をもう一度行う予定です。
- スマートフォンへ移行するモデル(パート7)では、8ビットがデフォルトの選択肢となります。この際、キャリブレーションデータは実際のスマートフォンの画面(JPEG、多様な照明)から選択する必要があります。セクション4のように、キャリブレーションデータの性質によって精度範囲が決まるためです。
次の記事では、精度を維持したまま 実行方式を変更するコンパイラとランタイムについて解説します。
TTの一言。 数値を半分に減らしてもボールの位置はそのままでした。むしろ写真を圧縮した時の方が大きくブレました。どこで誤差が生じるのか実際に測定してみるまで分かりません!
参考文献
- Micikevicius, P. et al. (2018). Mixed Precision Training. ICLR 2018. https://arxiv.org/abs/1710.03740
- Kalamkar, D. et al. (2019). A Study of BFLOAT16 for Deep Learning Training. arXiv. https://arxiv.org/abs/1905.12322
- Jacob, B. et al. (2018). Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference. CVPR 2018. https://arxiv.org/abs/1712.05877
- Krishnamoorthi, R. (2018). Quantizing deep convolutional networks for efficient inference: A whitepaper. arXiv. https://arxiv.org/abs/1806.08342
- Nagel, M. et al. (2021). A White Paper on Neural Network Quantization. arXiv. https://arxiv.org/abs/2106.08295
- Wu, H. et al. (2020). Integer Quantization for Deep Learning Inference: Principles and Empirical Evaluation. arXiv. https://arxiv.org/abs/2004.09602
- Dettmers, T. et al. (2022). LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale. arXiv. https://arxiv.org/abs/2208.07339
- 公開データ: OpenTTGames (CC BY-NC-SA 4.0)
- サンプルコードと出力: このフォルダの
code/02_quant_error.py,code/02_quant_error.out.txt