混合精度計算
テンソルコアの桁違いのスループットを、精度を落とさず取り込む方法が分かります。FP16/BF16/TF32の使い分けと反復改良の原理を押さえれば、倍精度並みの解を数倍速く得られます。
- FP16は表現範囲が狭くオーバーフロー・アンダーフローしやすいのに対し、BF16はFP32と同じ指数8ビットで範囲を保ち精度だけ落とすため、学習でも科学計算でも扱いやすい。
- 反復改良(iterative refinement)は残差計算を高精度で行い、低精度分解の誤差を補正する。条件数が過大でなければ倍精度相当の解を単精度分解の速度で得られる。
- テンソルコアはFP16入力・FP32累算の行列積を専用実行し、加算誤差はFP32側で吸収する。速度はメモリ帯域とテンソルコアのどちらが律速かで決まる。
混合精度が「単なる高速化」ではない理由
GPUやAIアクセラレータでは、半精度(16ビット)の行列積スループットが倍精度(64ビット)の十数倍から数十倍に達します。この差を使い演算の大部分を低精度で行うのが混合精度計算(mixed precision)ですが、要点は「全部を低精度にする」ことではなく、精度が結果を支配する箇所だけ高精度を残し、それ以外を低精度で流すという選択的な設計にあります。素朴に全体を低精度化すると、丸め誤差の蓄積で解が使い物にならなくなるか、そもそも反復が収束しません。
低精度化が効く理由は演算スループットだけではありません。多くのHPCワークロードはメモリ帯域律速(メモリバウンド)であり、データ幅を64ビットから16ビットへ削ればキャッシュ滞在量とメモリ転送量が2〜4倍改善します。演算律速かメモリ律速かはRoofline性能モデルで切り分けられ、混合精度がどこに効くか(演算スループット側かメモリ帯域側か)もこの枠組みで判断できます。
FP16・BF16・TF32 ── 3つの低精度フォーマットの本質的な違い
16ビット・19ビット級の浮動小数点には複数の規格があり、指数部(表現範囲を決める)と仮数部(精度を決める)のビット配分が異なります。この配分の違いが用途を分けます。
横にスクロール
主な低精度フォーマットのビット配分(符号/指数/仮数)
FP64 (double) : 1 / 11 / 52 相対誤差 ε ≈ 1.1e-16
FP32 (float) : 1 / 8 / 23 相対誤差 ε ≈ 6.0e-8
TF32 : 1 / 8 / 10 指数はFP32・仮数は10ビット(NVIDIA独自19bit相当)
FP16 (half) : 1 / 5 / 10 最大値 約6.5e4・相対誤差 ε ≈ 4.9e-4
BF16 (bfloat16): 1 / 8 / 7 最大値 約3.4e38・相対誤差 ε ≈ 3.9e-3
決定的なのは指数部のビット数です。FP16は指数5ビットしかなく、表現できる絶対値の上限が約6.5×10^4、正規化数の下限が約6.1×10^-5と範囲が狭いため、大きな値でオーバーフロー、小さな値でアンダーフロー(ゼロに丸められる)が起きやすい。一方BF16はFP32と同じ指数8ビットを持ち、表現範囲がFP32と同一のまま仮数だけを7ビットに削ります。つまりBF16は「範囲は広いが精度が粗い」フォーマットで、オーバーフロー対策が不要になる代わりに有効桁が約2〜3桁しかありません。
TF32はNVIDIAのテンソルコアが内部で使う形式で、FP32と同じ8ビット指数を保ちつつ仮数を10ビットに切り詰めます。FP32配列をそのまま入力として受け取り、テンソルコア内部でTF32相当に丸めて積和を実行するため、コード変更なしにFP32計算を加速できるのが特徴です。ただし精度はFP32より落ちるため、暗黙にTF32が使われることを把握していないと精度低下の原因を見失います。
| フォーマット | 指数/仮数 | 表現範囲 | 有効桁の目安 | 主用途 |
|---|---|---|---|---|
| FP16 | 5 / 10 | 狭い(±6.5e4) | 約3桁 | 深層学習・帯域削減(要スケーリング) |
| BF16 | 8 / 7 | FP32と同一 | 約2桁 | 学習・範囲重視の低精度演算 |
| TF32 | 8 / 10 | FP32と同一 | 約3桁 | FP32計算の透過的な加速 |
| FP32 | 8 / 23 | 広い | 約7桁 | 残差・累算・基準精度 |
FP16で勾配や中間値がアンダーフローしてゼロに潰れるのを防ぐため、深層学習では損失を定数倍してから逆伝播し、更新前に割り戻すロス・スケーリング(loss scaling)が標準的に使われます。BF16はFP32と同じ範囲を持つためスケーリング不要で導入が容易ですが、仮数7ビットゆえ精度そのものはFP16より粗い点に注意が要ります。
テンソルコア ── FP16入力・FP32累算という非対称構造
テンソルコアは小さな行列ブロック(例えば4×4や16×16)の積和をハードウェアで一括実行する専用ユニットです。混合精度の観点で重要なのは、入力はFP16/BF16/TF32などの低精度でも、内部の累算(積を足し込む部分)はFP32で行われるという非対称構造です。
テンソルコアの積和(概念)
D = A × B + C
A, B : 低精度(FP16 / BF16 / TF32)の入力行列
積 A[i,k]·B[k,j] : 低精度で計算
Σ(総和) : FP32で累算 ← ここが精度を守る
C, D : FP32で保持
→ 個々の積は粗いが、多数の積を足す部分は
FP32なので加算誤差の蓄積を大きく抑えられる
なぜこの設計が効くかは、数値安定性と浮動小数点誤差で扱う「加算誤差はO(n・ε)で蓄積する」性質から理解できます。行列積は内積の集まりであり、k 個の積を足し込む総和部分こそ誤差が積み上がる箇所です。ここをFP32累算にすれば、低精度の積による誤差は残るものの、総和による桁単位の誤差増幅を抑えられます。逆に累算まで低精度で行うと、k が大きい行列で誤差が急増し実用に耐えません。
一方で個々の積がFP16/BF16なら有効桁は数桁しかなく、テンソルコア単体の結果はFP32演算と同一にはなりません。これで十分かは用途次第で、深層学習の順伝播・逆伝播は許容できても、科学技術計算で倍精度相当の解が要る場面ではこの結果をさらに補正する仕組みが必要になります。それが次の反復改良です。
反復改良 ── 低精度分解の誤差を高精度残差で回復する
混合精度をHPCの線形ソルバで活かす中核が反復改良(iterative refinement)です。連立一次方程式 Ax = b の直接解法において、支配的コストであるLU分解を低精度(単精度や半精度)で一度だけ行い、残差の計算と解の補正だけを高精度(倍精度)で反復します。
混合精度反復改良(LU分解ベース、概念)
1. A = LU を低精度で分解 ← 最も重いが一度きり
2. x ← U\(L\b) で初期解を得る (前進・後退代入も低精度可)
繰り返し:
3. r = b − A x を高精度(FP64)で計算 ← 桁落ちを避ける核心
4. A d = r を低精度分解で解く(前計算したL,Uを再利用)
5. x ← x + d で解を補正
収束判定: ||r|| が十分小さくなれば停止
肝は残差 r = b − Ax を高精度で計算する点です。真の解に近づくほど Ax と b は接近し、その差 r は桁落ち(近い値どうしの引き算で有効桁が失われる現象)を強く受けます。ここを低精度で計算すると補正情報そのものが誤差に埋もれるため、残差だけはFP64(あるいはさらに高精度)で評価します。一方、重いLU分解は低精度で一度作れば各反復で再利用できるので、全体の実行時間は低精度分解1回分に近づきます。結果として、速度は低精度分解相当、最終精度は倍精度相当という両取りが成立します。
LU分解は行列Aの構造そのものを一度に因数分解する処理で、Ad = r の解 d はステップ4で前進・後退代入するだけで得られます。低精度のL・Uには分解時の誤差が含まれますが、それは補正方向 d の近似誤差に留まり、外側ループが残差ゼロへ向かって反復するうちに吸収されます。分解を毎回やり直す必要がないことが、反復改良が「安い」根拠です。
収束と数値安定性のトレードオフ ── 条件数という上限
反復改良がいつでも効くわけではありません。収束するか否か、そして最終的に到達できる精度は、行列Aの条件数 κ(A)(最大特異値と最小特異値の比)に強く支配されます。
直感的には、低精度分解が持ち込む誤差はおおよそ κ(A) と低精度の機械イプシロン ε_low の積のオーダーで効きます。反復改良の1ステップは誤差を概ね κ(A)・ε_low 倍に縮小しようとするため、この係数が1未満なら反復ごとに誤差が減り、高精度残差の精度限界(FP64なら約16桁)まで収束します。逆に κ(A)・ε_low が1に近い、あるいは1を超えると補正が縮小として働かず、収束が停滞するか発散します。
反復改良が収束する条件(概念的な目安)
縮小係数 ρ ≈ κ(A) · ε_low が 1 未満なら収束
FP16分解 (ε_low ≈ 4.9e-4) の場合:
κ(A) が 2000 程度を超えると ρ ≳ 1 で収束が怪しくなる
FP32分解 (ε_low ≈ 6.0e-8) の場合:
κ(A) が 1e7 近くまで許容余地がある
→ 悪条件なほど、分解に使える低精度の下限が上がる
つまり低精度を攻めるほど、扱える条件数の上限は下がるというトレードオフです。FP16でLU分解すると速いが、条件数が数千を超える系ではもう補正しきれません。実務ではこれを緩和するため、FP16の初期解をFP32の反復で磨き、さらにFP64残差で仕上げるといった多段の精度階層を組みます。悪条件そのものへの根本対策は前処理(プリコンディショニング)で実効的な条件数を下げることで、反復改良と前処理は競合せず併用されます。反復法(CG・GMRES)に混合精度を組み込む場合も、反復解法の収束判定に使う残差ノルムは高精度で評価するのが定石です。
テンソルコアやTF32は、明示指定せずとも実行時に暗黙で選ばれることがあります。結果が微妙にずれる、反復回数が想定より増える、といった症状が出たときは、どこで低精度が使われているかをまず疑うべきです。特にFP16のアンダーフローはゼロへ静かに丸められ例外も警告も出ないため、収束が突然止まる原因として見落とされがちです。
「FP16とBF16の違いは」と問われたら答えの軸は指数部——BF16はFP32と同じ指数8ビットで範囲を保ち精度だけ落とす、FP16は指数5ビットで範囲が狭くスケーリングが要る。「混合精度で倍精度の答えがなぜ出るか」には反復改良——重いLU分解は低精度で一度、残差だけ高精度で反復補正、と説明する。そして「常に効くのか」には条件数が上限を決める、κ(A)・ε_lowが1未満でなければ収束しない、まで言えると強い。
まとめ
- 混合精度は全体の低精度化ではなく、精度を支配する箇所(累算・残差)だけ高精度を残す選択的設計であり、演算スループットとメモリ帯域の両面で効く。
- FP16は指数5ビットで範囲が狭くスケーリング前提、BF16はFP32同一の指数8ビットで範囲を保ち精度だけ粗い、TF32はFP32を透過的に加速する。用途で使い分ける。
- テンソルコアはFP16/BF16入力とFP32累算という非対称構造で、加算誤差の蓄積をFP32側で吸収することで低精度でも実用精度を保つ。
- 反復改良は低精度LU分解を再利用しつつ残差を高精度で計算・補正することで、速度は低精度分解相当・最終精度は倍精度相当を両立させる。
- 収束と到達精度は条件数が上限を決め、κ(A)・ε_lowが1未満でなければ補正が働かない。悪条件には前処理を併用し、精度階層を多段に組むのが実務の定石。
HPC・科学技術計算の記事ガイド
混合精度計算を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
HPC
比較で見る軸
難易度: advanced / カテゴリ: HPC・科学技術計算 / タグ数: 5
導入後に効く点
反復改良(iterative refinement)は残差計算を高精度で行い、低精度分解の誤差を補正する。条件数が過大でなければ倍精度相当の解を単精度分解の速度で得られる。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- HPC・科学技術計算
- タグ数
- 5
判断チェックリスト
- 自社の用途が「HPC / 数値解析」に近いか確認する。
- 強みである「FP16は表現範囲が狭くオーバーフロー・アンダーフローしやすいのに対し、BF16はFP32と同じ指数8ビットで範囲を保ち精度だけ落とすため、学習でも科学計算でも扱いやすい。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。