BFloat16 (BF16)
BFloat16(BF16)は、FP32と同じ8ビットの指数部と7ビットの仮数部を持つ16ビット浮動小数点形式で、メモリ使用量を半分にしながらFP32と同じ数値範囲を保ちます。FP16との違いを解説します。
BFloat16(脳浮動小数点、BF16)は、符号ビット1個、指数部8ビット、仮数部7ビットを持つ16ビット浮動小数点形式です。標準的な32ビット浮動小数点形式(FP32)と指数部の幅が同じため、約1.2 × 10⁻³⁸から3.4 × 10³⁸までの同じ値域をカバーしながら、各数値を半分のメモリで格納できます。その代償は精度であり、仮数部が7ビットのため、有効桁数は10進数でわずか2~3桁です。ディープラーニングではこの丸め誤差を十分許容でき、この形式はGoogle BrainでGoogleのTPU向けに考案されて以来、大規模モデルを最新のGPUやアクセラレーターで学習する際の標準的な16ビット形式となっています。
BF16による数値の表現方法#
BF16の値はFP32の上位半分そのものです。符号と指数部は同じで、仮数部が23ビットから7ビットに短縮されています。そのため、FP32からBF16への変換では下位16ビットを丸めて取り除くだけで、逆変換ではゼロを追加します。このため、ハードウェアで形式を低コストで実装できます。もう一つの一般的な16ビット形式であるIEEE FP16、つまり半精度では、ビットの割り当てが異なり、指数部が5ビット、仮数部が10ビットです。これにより値域を犠牲にして精度を高めています。同じ値を3つの形式で比較すると、次のような違いがあります。
import torch
# Largest value, smallest normal value, and spacing just above 1.0 for each format
for dtype in [torch.float32, torch.float16, torch.bfloat16]:
info = torch.finfo(dtype)
print(f"{dtype!s:15} max={info.max:.3g} tiny={info.tiny:.3g} eps={info.eps:.3g}")
# A large activation, a tiny gradient, and a value that needs fine precision
x = torch.tensor([70000.0, 1e-8, 1.001953125])
print("FP16:", x.half().tolist())
print("BF16:", x.bfloat16().tolist())BF16の最大値は3.39e38で、FP16の65,504を上回ります。また、最小正規化数の1.18e-38はFP32と同じです。FP16では70,000が無限大になり、1e-8はゼロになりますが、BF16ではどちらもそれぞれ70,144と約1.0e-8として保持されます。FP16は間隔がより細かく(epsは0.000977、BF16は0.00781)、1.001953125を正確に保持できますが、BF16では1.0に丸められます。
学習におけるBF16とFP16の比較#
学習では、精度よりも値域が重要です。勾配はFP16の最小値を下回って消失することがよくあります。そのため、FP16の混合精度学習では、逆伝播の前に損失へスケール係数を掛け、勾配がオーバーフローするたびにその係数を小さくします。この手法は損失スケーリングと呼ばれます。BF16はFP32と同じ値域を持つため、損失スケーリングは不要です。また、BF16による学習は、ビジョン、音声、言語、レコメンデーションの各モデルで、同じ反復回数ならFP32と同等の結果を得られます。ハイパーパラメーターを変更する必要もありません。混合精度学習では、重みのマスターコピーをFP32で保持し、行列乗算をBF16で実行してFP32で累積します。BF16で仮数部7ビットの積を多数加算すると、精度が大きく失われるためです。2種類の16ビット形式の選択は、主にハードウェアに左右されます。
PyTorchでは、torch.autocast(device_type="cuda", dtype=torch.bfloat16)を使った自動混合精度でBF16を適用できます。Ultralytics YOLOでは、CUDA上でネイティブBF16をサポートするGPUを対象に、同じ切り替えを学習で利用できます。amp="bf16"を使うと、フォワードパスをBF16のautocastで実行し、デフォルトのFP16モードで使われる勾配スケーラーを無効にします。
ハードウェアのサポート#
Google Cloud TPUの第2世代と第3世代は、行列ユニット内でBF16の乗算を行い、FP32で累積します。その後のTPU世代でもこの形式が採用されています。NVIDIA GPUはAmpere世代以降のTensor CoreでBF16を実行でき、A100、H100、Blackwell GPUも含まれます。また、Advanced Matrix Extensions(AMX)を搭載したIntel Xeonプロセッサーは、CPU上でBF16を高速化します。NVIDIA A100では、BF16とFP16のTensor Core演算はいずれも最大312 テラFLOPSに達し、標準FP32演算でのGPU性能19.5テラFLOPSの16倍です。
BF16と関連形式の比較#
- 半精度(FP16): IEEEの16ビット形式で、精度は高い一方、値域は大幅に狭くなります。推論では一般的に使われ続けています。推論時には活性化値が値域内に収まり、追加の精度が役立つためです。
- TF32: BF16と同じ8ビットの指数部と、FP16と同じ10ビットの仮数部を持つNVIDIA Tensor Coreの演算モードです。Ampere以降のGPUでは、テンソルを32ビットストレージに保持したまま、ライブラリがFP32の行列乗算に利用できます。
- FP8: E4M3やE5M2などの8ビット浮動小数点形式です。HopperおよびBlackwell GPUでサポートされており、最大規模のモデルの学習と推論で、メモリ使用量をさらに半分にできます。
- モデル量子化: 学習済みの重みを、INT8やINT4などの整数形式に変換してデプロイします。
大規模言語モデルにおけるBF16#
オープンウェイトの大規模言語モデル(LLM)の多くは、BF16混合精度で学習され、BF16の重みとともに公開されています。これらの重みは、量子化されたFP8版やINT4版の評価基準になります。パラメーターあたり2バイトを使うため、700億パラメーターのモデルの重みはBF16で140 GBとなり、単体のNVIDIA H100の80 GBを超えます。そのため、BF16でこのようなモデルをサービングするには、少なくとも2基のGPUに分割する必要があります。










