---
title: "FP8とは？FP16・BF16・FP4との違いとE4M3/E5M2の仕組み、対応GPU【2026年版】"
url: "https://www.issoh.co.jp/tech/details/5959/"
published: 2025-03-21
updated: 2026-10-13
categories: ["AI"]
publisher: "株式会社一創"
---

# FP8とは？FP16・BF16・FP4との違いとE4M3/E5M2の仕組み、対応GPU【2026年版】

FP8は、1つの数値を8ビットで表す浮動小数点形式です。業界規格（OCP OFP8）では指数と仮数の配分が異なるE4M3とE5M2の2種類が定められています。FP8にした値の容量はFP16の半分で、H100などの対応GPUでは行列演算の理論性能もFP16の2倍です。その代わり、E4M3は448を超える値を表せず、仮数が3ビットしかないため0.1は0.1015625に丸められます。

この記事では、FP16・BF16・FP32・FP4・INT8との違いを比較表と手元の実測で整理し、どのGPUでFP8演算が効くのか、学習・推論・画像生成でどう使うのかを、2026年10月時点のTransformer Engine・vLLM・ComfyUIのソースと公式資料をもとに説明します。

## まとめ：FP8の要点と使いどころ

- **規格上の形式は2種類**：E4M3（指数4・仮数3ビット、最大448）は精度寄り、E5M2（指数5・仮数2ビット、最大57,344）は範囲寄り。規格は[OCPのOFP8](https://www.opencompute.org/documents/ocp-8-bit-floating-point-specification-ofp8-revision-1-0-2023-06-20-pdf)（2023年6月承認）
- **FP16との違い**：FP8にしたテンソルの容量と転送量は半分、H100 SXMの密行列性能は1,979 TFLOPSでFP16（989.5 TFLOPS）の2倍。一方、有効桁は10進で約1桁まで落ちる
- **スケーリングが前提**：範囲外の値はNaN・無限大・最大値への張り付き（形式と変換方法による）に化け、小さい値は0に潰れる。テンソルやブロックごとに倍率を掛けて範囲に収めてから変換する
- **INT8との違い**：本文のテンソル単位・最大絶対値スケーリングの比較では、外れ値を混ぜたデータはFP8、一様分布と正規分布はINT8の復元誤差が小さかった。H100ではINT8とFP8の演算性能は同じだが、Blackwell Ultra（B300）ではINT8がFP8の1/24に削られている
- **演算できるGPU**：NVIDIAはAda Lovelace（compute capability 8.9）以降、AMDはMI300X以降。A100やRTX 30シリーズ（Ampere）は重みをFP8で保存できても演算はFP16/BF16に戻して行う
- **FP4との違い**：要素形式のE2M1は最大6しか表せないため、32要素ごとにスケールを共有するMXFP4や16要素ごとのNVFP4の形で使う。演算はNVIDIAがBlackwell世代、AMDがMI350X・MI355Xから

## FP8の定義：E4M3とE5M2のビット割り当て

浮動小数点数は「符号・指数・仮数」の3つのビット列で値を表します。指数に多く割けば表せる範囲が広がり、仮数に多く割けば細かい値を区別できます。FP8はこの8ビットの配分違いで2つの形式を定めています。仕組みの基礎は[IEEE 754の浮動小数点数の解説](/tech/details/15257/)にまとめています。

| 項目       | E4M3           | E5M2              |
| -------- | -------------- | ----------------- |
| 符号・指数・仮数 | 1・4・3ビット       | 1・5・2ビット          |
| 指数のバイアス  | 7              | 15                |
| 最大値      | 448            | 57,344            |
| 最小の正規化数  | 2\-6（0.015625） | 2\-14（約6.1×10\-5） |
| 1付近の刻み幅  | 0.125          | 0.25              |
| 無限大（Inf） | なし             | あり                |
| 主な用途     | 重み・活性化（順伝播）    | 勾配（逆伝播）           |

E5M2はIEEE 754と同じく無限大とNaNを持ちます。E4M3は無限大を持たず、NaNに割り当てるビットパターンを符号違いの2つだけにして、空いた分を有限値に回しています。これにより最大値が240から448まで伸びています。勾配は値の幅が大きく外れ値も出やすいため範囲の広いE5M2、重みと活性化は刻みの細かいE4M3、という使い分けが基本です。

### 規格の出どころ：2022年の論文とOCP OFP8

2つの形式は、NVIDIA・Arm・Intelの研究者15名による論文「[FP8 Formats for Deep Learning](https://arxiv.org/abs/2209.05433)」（arXiv:2209.05433、2022年9月12日投稿）で提案されました。論文ではCNN・RNN・Transformerの学習を、ハイパーパラメータを16ビット学習から変えずにFP8で回し、最大175Bパラメータの言語モデルを含めて16ビットと同等の結果を報告しています。これを業界団体OCP（Open Compute Project）が「OCP 8-bit Floating Point Specification（OFP8）」として2023年6月20日に承認しました。NVIDIA GPUのFP8やPyTorchの`float8_e4m3fn`・`float8_e5m2`はこの定義と同じビット配置です。ただしAMD InstinctのMI300X・MI325Xは別の変種（FNUZ）を採用しており、InstinctシリーズがOCP形式に揃ったのはMI350X以降です。

## FP8・FP16・BF16・FP32・FP4の比較

深層学習で使う主な浮動小数点形式を並べると、FP8の位置づけが見えます。最大値と最小の正規化数は`ml_dtypes`と`numpy.finfo`で確認した値です。

| 形式       | ビット数 | 符号・指数・仮数 | 最大値        | 最小の正規化数      | 1付近の刻み幅    |
| -------- | ---- | -------- | ---------- | ------------ | ---------- |
| FP32     | 32   | 1・8・23   | 約3.4×1038  | 約1.18×10\-38 | 約1.2×10\-7 |
| BF16     | 16   | 1・8・7    | 約3.39×1038 | 約1.18×10\-38 | 0.0078125  |
| FP16     | 16   | 1・5・10   | 65,504     | 約6.1×10\-5   | 約0.00098   |
| FP8 E5M2 | 8    | 1・5・2    | 57,344     | 約6.1×10\-5   | 0.25       |
| FP8 E4M3 | 8    | 1・4・3    | 448        | 0.015625     | 0.125      |
| FP4 E2M1 | 4    | 1・2・1    | 6          | 1            | 0.5        |

### FP8とFP16の違い：半分のメモリと2倍の演算、失う桁数

FP16からFP8に移すと、FP8にした重みや活性化の容量と転送量が半分になります（スケールや高精度で残す部分があるため、モデル全体がちょうど半分になるわけではありません）。演算も速くなり、[H100 SXMのTensorコア性能](https://www.nvidia.com/ja-jp/data-center/h100/)は密行列でFP16が989.5 TFLOPS、FP8が1,979 TFLOPSです（NVIDIA公式の構造化スパース込み表記はそれぞれ1,979と3,958 TFLOPS）。

代償は精度です。FP16の仮数10ビットは10進で約3桁の有効桁があるのに対し、E4M3の仮数3ビットは約1桁です。E5M2は指数がFP16と同じ5ビットなので表せる範囲はFP16とほぼ同じ（最大57,344と65,504）ですが、刻みはさらに粗くなります。FP16の学習コードの型だけをFP8に置き換えても動かないのはこのためで、後述のスケーリングと、精度を残す演算の切り分けが必要になります。

### BF16とFP16の違い：指数による数値範囲と仮数による精度

同じ16ビットでも、BF16は指数をFP32と同じ8ビットにして仮数を7ビットに削り、FP16は指数5ビット・仮数10ビットに配分しています。上の表のとおり、BF16は約3.39×1038まで表せる代わりに1付近の刻みが0.0078125と粗く、FP16は刻みが約0.00098と8倍細かい代わりに、最大有限値は65,504です。

この差は学習の手間に直結します。FP16学習では、小さな勾配のアンダーフローを抑えるため、通常は損失を拡大する「損失スケーリング」を使います。ただし、モデルの値がFP16の範囲を超える問題を、これだけで解消できるとは限りません。BF16はFP32と同じ範囲を持つので損失スケーリングがほぼ不要で、Ampere（A100・RTX 30シリーズ）以降の大規模学習ではBF16が標準になりました。推論で値の範囲が落ち着いているなら、細かさで勝るFP16でも問題は起きにくい、というのが使い分けの目安です。

### FP8とBF16・FP32の役割分担：行列積入力の8ビット化

FP8学習はBF16を置き換えるものではありません。行列積の入力だけをFP8にし、行列積の累積・重みの原本（マスター重み）・オプティマイザの状態はBF16やFP32で持つ構成が一般的です。「BF16とFP8の違い」は、前者が学習全体の基準精度、後者が行列積を速くするための入力形式、という役割の差と捉えると整理できます。

### FP4（E2M1）とMXFP4・NVFP4：ブロックスケールを含む4ビット形式

FP4の要素形式E2M1は、0を除くと絶対値0.5・1・1.5・2・3・4・6（それぞれ正と負）の値しか表せません。そのため深層学習向けの4ビット浮動小数点は、E2M1の要素に少数要素ごとの共有スケールを組み合わせた形式として規格化されています。

- **MXFP4**：OCPのMicroscaling Formats（MX）仕様v1.0で定義。32要素ごとにE8M0（2のべき乗だけを表す8ビット）のスケールを共有する。同じ仕様にMXFP8もある。[gpt-oss](/tech/details/8080/)はMoE部分の重みをこの形式で配布している
- **NVFP4**：NVIDIAの形式。16要素ごとにFP8 E4M3のスケールを共有し、さらにテンソル全体にFP32のスケールを掛ける。ブロックが半分の大きさで、スケールも2のべき乗に限られないぶん、MXFP4より値の分布に細かく合わせられる

FP8はテンソル単位のスケール1つでも学習・推論が成り立つのに対し、MXFP4・NVFP4はブロックごとのスケールを形式そのものの一部として持つ、というのが実務上の大きな違いです。

## FP8の丸めとオーバーフローのPythonでの確認結果

FP8の挙動は、Googleが公開しているNumPy向けの型拡張`ml_dtypes`で手元のCPUでも再現できます。GPUは不要です。以下はPython 3.9.6・NumPy 2.0.2・ml\_dtypes 0.5.4で実行したコードと結果です（2026年10月13日実行。NumPy 1.26.4でも同じ出力でした）。同じ版は仮想環境で`python -m pip install numpy==2.0.2 ml-dtypes==0.5.4`を実行すると入ります。

```
import numpy as np
import ml_dtypes

E4M3 = ml_dtypes.float8_e4m3fn
E5M2 = ml_dtypes.float8_e5m2

x = np.array([0.1, 3.14159, 300.0, 500.0], dtype=np.float32)
print("E4M3:", x.astype(E4M3).astype(np.float32))
print("E5M2:", x.astype(E5M2).astype(np.float32))

# テンソル単位のスケーリング：最大絶対値(amax)を448に合わせてから変換する
amax = np.abs(x).max()
scale = float(ml_dtypes.finfo(E4M3).max) / amax
q = (x * scale).astype(E4M3)
print("scaled E4M3:", q.astype(np.float32) / scale)

# ブロック単位のスケーリング：2要素ずつ別々のスケールを持たせる
def to_fp8_blockwise(v, block=2):
    out = np.empty_like(v)
    for i in range(0, len(v), block):
        b = v[i:i + block]
        amax = np.abs(b).max()
        if amax == 0:  # すべて0のブロックはそのまま0を返す
            out[i:i + block] = 0
            continue
        s = float(ml_dtypes.finfo(E4M3).max) / amax
        out[i:i + block] = (b * s).astype(E4M3).astype(np.float32) / s
    return out

print("block E4M3:", to_fp8_blockwise(x))
```

```
E4M3: [1.015625e-01 3.250000e+00 2.880000e+02          nan]
E5M2: [9.375e-02 3.000e+00 3.200e+02 5.120e+02]
scaled E4M3: [9.5912382e-02 3.0691962e+00 2.8571426e+02 4.9999997e+02]
block E4M3: [9.8174684e-02 3.1415899e+00 2.8571426e+02 4.9999997e+02]
```

結果から読み取れることは次のとおりです。

- **500はE4M3でNaNになる**：丸めた結果が448を超える値は、飽和（448に張り付く）ではなくNaNに変換された。境界を調べると、464ちょうどまでは448に丸められ、464を少しでも超える値（464.5など）はNaNになる。PyTorch 2.2.2の`tensor.to(torch.float8_e4m3fn)`でも同じ結果で（2026年9月27日実測）、E5M2では70,000が無限大（inf）になった。NaNや無限大は、その要素を参照する行列積の出力に伝わり、後続の層でさらに広がるので、スケーリングなしの変換は危険
- **丸め誤差は数%単位**：3.14159はE4M3で3.25（誤差約3.5%）、E5M2で3.0（約4.5%）。FP16なら3.140625で誤差は0.03%程度
- **外れ値が1つあると小さい値が粗くなる**：テンソル全体を500に合わせてスケールすると、0.1は0.0959まで崩れた。2要素ずつ別スケールにすると0.0982、3.14159はほぼ元の値に戻る

### スケーリングの粒度：Transformer Engineの5つのレシピ

最後の結果が、FP8のスケーリングが細かくなってきた理由です。LLMの活性化には一部のチャネルだけ桁違いに大きい外れ値が出るため、テンソル全体で1つのスケールだと他の値が潰れます。NVIDIAの学習ライブラリTransformer Engineは、[v2.20.2（2026年10月5日公開）のレシピ定義](https://github.com/NVIDIA/TransformerEngine/blob/v2.20.2/transformer%5Fengine/common/recipe/%5F%5Finit%5F%5F.py)で次の方式を用意しています。対応GPUの条件は同じ版の`transformer_engine/pytorch/quantization.py`の判定関数から読み取ったものです。

| レシピ名                 | スケールの単位         | スケールの決め方     | 判定条件                    |
| -------------------- | --------------- | ------------ | ----------------------- |
| DelayedScaling       | テンソル            | 過去の反復のamax履歴 | CC 8.9以上                |
| Float8CurrentScaling | テンソル            | その反復のamax    | CC 8.9以上                |
| Float8BlockScaling   | 1×128・128×128要素 | 2のべき乗（既定）    | CC 9.0以上・CUDA 12.9以上    |
| MXFP8BlockScaling    | 32要素            | E8M0を共有      | CC 10.x                 |
| NVFP4BlockScaling    | 16要素＋テンソル       | E4M3＋FP32の2段 | CC 10.0以上（学習は10.0・10.3） |

表の「CC 8.9以上」のうちAda（8.9）は、CUDA 12.1以上とcuBLASLt 12.1.3.x以上も条件です。DelayedScalingは前の反復の統計を使うので計算が軽い反面、急に大きな値が来ると追いつけません。現在値でスケールを決める方式やブロック単位の方式は、その弱点を計算量と引き換えに埋めるものです。[Float8BlockScalingの公式文書](https://docs.nvidia.com/deeplearning/transformer-engine/features/low%5Fprecision%5Ftraining/fp8%5Fblockwise%5Fscaling/fp8%5Fblockwise%5Fscaling.html)によると、この方式が本来の対象とするのはHopper（SM 9.0）で、Blackwell（SM 10.0以上）ではMXFP8で代替実行され、BlackwellではMXFP8BlockScalingが推奨です。注意したいのはMXFP8の扱いで、v2.20.2はcompute capability 12.0以上（RTX 50シリーズなど）を「MXFP8はまだ全GEMMレイアウトで未対応」として弾きます。レシピを指定しないときの既定値は、MXFP8が使えればMXFP8BlockScaling、12.0以上ならFloat8CurrentScaling、それ以外はDelayedScalingです。同じBlackwellでもB200（10.0）とRTX 5090（12.0）で既定の方式が変わります。

## FP8とINT8の違い：外れ値への強さとGPUの演算性能

同じ8ビットでも、INT8は整数なので値の刻みが等間隔で、FP8は指数を持つので0に近いほど刻みが細かくなります。どちらが正確かは値の分布で変わるため、上と同じ環境でテンソル単位のスケーリングを掛けて10万要素の誤差を比べました（誤差は復元誤差の二乗平均平方根（RMSE）を元データの二乗平均平方根（RMS）で割った相対値）。

```
import numpy as np
import ml_dtypes

E4M3 = ml_dtypes.float8_e4m3fn
rng = np.random.default_rng(0)

def int8_roundtrip(x):
    if not np.any(x):
        return np.zeros_like(x)
    s = np.abs(x).max() / 127          # 最大絶対値を127に合わせる（対称INT8）
    return np.clip(np.round(x / s), -127, 127) * s

def fp8_roundtrip(x):
    if not np.any(x):
        return np.zeros_like(x)
    s = np.abs(x).max() / 448          # 最大絶対値を448に合わせる（E4M3）
    return (x / s).astype(E4M3).astype(np.float32) * s

def rel_err(ref, y):
    return np.sqrt(np.mean((ref - y) ** 2)) / np.sqrt(np.mean(ref ** 2))

uniform = rng.uniform(-1, 1, 100_000).astype(np.float32)
normal = rng.normal(0, 1, 100_000).astype(np.float32)
outlier = np.append(rng.normal(0, 1, 99_990), np.full(10, 100.0)).astype(np.float32)

for name, x in [("一様分布", uniform), ("正規分布", normal)]:
    print(f"{name}: INT8 {rel_err(x, int8_roundtrip(x)):.2%} / FP8 {rel_err(x, fp8_roundtrip(x)):.2%}")

# 外れ値10個（値100）を混ぜ、外れ値以外の99,990要素だけで誤差を測る
body = slice(0, 99_990)
i8, f8 = int8_roundtrip(outlier)[body], fp8_roundtrip(outlier)[body]
print(f"外れ値あり: INT8 {rel_err(outlier[body], i8):.2%} / FP8 {rel_err(outlier[body], f8):.2%}")
print(f"0に潰れた要素: INT8 {np.mean(i8 == 0):.1%} / FP8 {np.mean(f8 == 0):.1%}")
```

```
一様分布: INT8 0.40% / FP8 2.56%
正規分布: INT8 1.04% / FP8 2.64%
外れ値あり: INT8 22.71% / FP8 2.67%
0に潰れた要素: INT8 30.6% / FP8 0.0%
```

値が狭い範囲にそろっている一様分布や正規分布では、INT8の誤差はFP8の1/6〜2/5でした。ところが10万要素中10個だけ100という外れ値を混ぜると、INT8は刻みが100÷127≒0.79まで広がり、正規分布部分の30.6%が0に丸められて誤差が22.71%に跳ね上がりました。FP8は相対的な刻みで値を持つため2.67%のままです。この比較では、テンソル単位の最大絶対値スケーリングで外れ値がある場合にFP8が有利でした。ただし、人工データの復元誤差だけではLLMの出力品質は判断できず、チャネル単位のINT8などとは別途比較が必要です。

もう1つの違いはハードウェアです。[H100 SXM](https://www.nvidia.com/en-us/data-center/h100/)ではFP8が3,958 TFLOPS、INT8が3,958 TOPS（いずれも構造化スパース込み）で同じでした。ところが[NVIDIA HGXの仕様表](https://www.nvidia.com/en-us/data-center/hgx/)では、Blackwell UltraのHGX B300（8 GPU）がFP8 72 PFLOPSに対してINT8は3 POPS（ともに構造化スパース込み）で、HGX B200のINT8 72 POPSから大きく削られています。HGX B300の公称演算性能を踏まえると、B300でW8A8推論を設計する場合はFP8を優先的に評価するのが合理的です。ほかのGPUでは、対応カーネルと対象モデルの精度・スループットを比較して選びます。

## FP8演算に対応するGPU：Hopper・Ada・Blackwellとそれ以前

FP8の行列積をTensorコアで実行できるかは、GPUの世代（compute capability）で決まります。CUDAの公式対応表では、FP8入力はAda Lovelace（8.9）とHopper（9.0）以降が対象で、Ampere（A100の8.0）には含まれていません。

| 世代                          | 代表製品             | FP8演算       | FP4演算     |
| --------------------------- | ---------------- | ----------- | --------- |
| NVIDIA Ampere（8.0/8.6）      | A100・RTX 30シリーズ  | なし          | なし        |
| NVIDIA Ada Lovelace（8.9）    | L4・L40S・RTX 4090 | あり          | なし        |
| NVIDIA Hopper（9.0）          | H100・H200        | あり          | なし        |
| NVIDIA Blackwell（10.x/12.x） | B200・RTX 50シリーズ  | あり（MXFP8含む） | あり（NVFP4） |
| AMD CDNA3                   | MI300X・MI325X    | あり（FNUZ形式）  | なし        |
| AMD CDNA4                   | MI350X・MI355X    | あり（OCP形式）   | あり（MXFP4） |

AMDのMI300XはFP8の密行列性能2.61 PFLOPSをうたいますが、形式はOCP版ではなく「FNUZ」と呼ばれる変種です。FNUZ版のE4M3は最大値が240で（ml\_dtypesの`float8_e4m3fnuz`で確認）、OCP版の448と一致しません。MI300X向けに量子化したチェックポイントをNVIDIA GPUへ持ち込む、あるいはその逆のときは、同じ「E4M3」でも値の解釈が違う点に注意が必要です。GPUの世代とcompute capabilityの読み方は[CUDAとGPU世代の解説](/tech/details/4902/)で扱っています。

### Ampere（RTX 30・A100）でのFP8重み：保存形式と演算形式の違い

RTX 3090などAmpere世代でも、FP8で配布されたモデルは読み込めます。ただし演算はFP8では行われず、重みをFP16/BF16に戻してから計算します。FP8演算器による高速化はありません。重みをFP8のまま保持して計算直前に戻す実装ならVRAMとディスクが減り、メモリ帯域が律速になる場面では読み込み量が減る分だけ速くなる余地があります。

画像生成でよく使われるComfyUIのソース（[v0.39.0のmodel\_management.py](https://github.com/Comfy-Org/ComfyUI/blob/v0.39.0/comfy/model%5Fmanagement.py)、2026年10月5日公開）を読むと、FP8演算を使うかどうかの判定が明記されています。`supports_fp8_compute`は、NVIDIA GPUのcompute capabilityが9以上なら無条件で、8.9ならPyTorch 2.3以上（Windowsは2.4以上）でFP8演算を有効にし、8.8以下では無効にします（このPyTorch版の条件は関数内の判定で、ComfyUI本体が動作に求めるPyTorch版とは別です）。起動オプションの`--fp8_e4m3fn-unet`のヘルプは「Store unet weights in fp8\_e4m3fn」で、あくまで重みの保存形式を指定するものです。

## FP8が使われている場面：学習・推論・画像生成

### 学習：Transformer EngineとDeepSeek-V3のFP8混合精度

PyTorchで学習にFP8を使う代表的な手段が、NVIDIAのTransformer Engineです。`te.Linear`などの層に差し替え、`te.autocast(recipe=...)`の範囲で順伝播を回すと、行列積だけがFP8になります。以前の解説記事でよく見る`fp8_autocast`は、v2.20.2では非推奨の警告を出して`autocast`へ転送するだけの関数になっています。[NVIDIA日本語ブログのBERT例](https://developer.nvidia.com/ja-jp/blog/introduction-to-fp8-training-using-transformer-engine/)では、Transformer Engineの層への置き換えとFP8化を合わせた効果として、H100で最大1.57倍の高速化と21%のメモリ節約が報告されています。

大規模学習の事例として知られるのがDeepSeek-V3（671Bパラメータ）です。[技術報告（arXiv:2412.19437）](https://arxiv.org/abs/2412.19437)によると、線形層の3つの行列積（順伝播・入力勾配・重み勾配）をFP8で実行し、活性化は1×128要素、重みは128×128要素のブロックごとにスケールを持たせています。埋め込み層・出力層・MoEのゲーティング・正規化・Attentionは元のBF16/FP32のままです。外れ値でスケールが崩れる問題を、細かいブロックと精度を残す層の切り分けで避けた構成で、Transformer EngineのFloat8BlockScalingも同じ考え方です。モデル自体の解説は[DeepSeek-V3の仕組みと性能](/tech/details/4789/)を参照してください。

### 推論：vLLMでFP8量子化する手順とKVキャッシュ

FP8量子化とは、BF16/FP16で学習済みのモデルの重み（と活性化）をFP8に変換して動かすことです。[vLLM v0.31.0](https://docs.vllm.ai/en/v0.31.0/features/quantization/online/)（2026年10月5日公開）では、変換済みのチェックポイントを用意しなくても、読み込み時に変換するオンライン量子化が使えます。指定名は`fp8_per_tensor`（テンソル単位）、`fp8_per_block`（重み128×128・活性化1×128のブロック単位）、`mxfp8`（32要素ブロック）です。

```
vllm serve meta-llama/Llama-3.1-8B --quantization fp8_per_block
```

精度を詰めたいときは、vLLMの[FP8 W8A8のドキュメント](https://github.com/vllm-project/vllm/blob/v0.31.0/docs/features/quantization/llm%5Fcompressor/fp8.md)が勧めるllm-compressorで事前に変換します。`FP8_DYNAMIC`方式は重みをチャネル単位で静的に、活性化をトークン単位で動的にスケールするため、校正データが要りません。以下は同ドキュメントの手順を要約したもので、実行にはGPUとモデルの利用許諾が必要です（vLLMとllm-compressorは別の仮想環境に入れるよう案内されています）。

```
from transformers import AutoTokenizer, AutoModelForCausalLM
from llmcompressor import oneshot
from llmcompressor.modifiers.quantization import QuantizationModifier

MODEL_ID = "meta-llama/Meta-Llama-3-8B-Instruct"
model = AutoModelForCausalLM.from_pretrained(MODEL_ID, device_map="auto", dtype="auto")
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)

# Linear層をFP8_DYNAMICで量子化し、出力層(lm_head)は元の精度に残す
recipe = QuantizationModifier(targets="Linear", scheme="FP8_DYNAMIC", ignore=["lm_head"])
oneshot(model=model, recipe=recipe)

SAVE_DIR = "Meta-Llama-3-8B-Instruct-FP8-Dynamic"
model.save_pretrained(SAVE_DIR)
tokenizer.save_pretrained(SAVE_DIR)
```

同ドキュメントによれば、FP8化でモデルのメモリは半分になり、スループットは最大1.6倍です。FP8演算（W8A8）はAda Lovelace・Hopper・Blackwellが対象で、Turing・Ampereでは重みだけをFP8で持つW8A16のMarlinカーネルで動きます。`mxfp8`のW8A8はSM 100以上が条件で、それ以外のGPUではW8A16に落ちます。

長い文脈で効くのがKVキャッシュのFP8化で、`--kv-cache-dtype fp8`を指定すると、KVキャッシュの値の容量はFP16の半分になります。ただし、校正なしではスケールが1.0となるため、精度を確認し、必要に応じてllm-compressorによる校正を行います。明示形式の`fp8_e4m3`はCUDA 11.8以上とROCmで、`fp8_e5m2`はCUDA 11.8以上で使えます。vLLM自体の構成は[vLLMの仕組みと使い方](/tech/details/15786/)、キャッシュの考え方は[KVキャッシュの解説](/tech/details/13786/)、NVIDIA純正の推論エンジンでのFP8は[TensorRT-LLMの解説](/tech/details/16075/)で扱っています。自社GPUでのLLM運用の設計は、当社の[生成AI導入支援](/service/ai/genai/)でもご相談を受けています。

### 画像生成：ComfyUIのFP8・MXFP8・NVFP4とFLUX.1-devの容量

画像生成では、FP8は主にファイルサイズとVRAMを減らす目的で使われています。Hugging Faceで確認すると、[Black Forest Labs公式のFLUX.1-dev](https://huggingface.co/black-forest-labs/FLUX.1-dev)本体（`flux1-dev.safetensors`）は23.8GB、[Kijai/flux-fp8](https://huggingface.co/Kijai/flux-fp8)で配布されるE4M3版（`flux1-dev-fp8-e4m3fn.safetensors`）は11.9GBです。E4M3版とE5M2版が並んで配布されていることが多く、推論用の重みなら刻みの細かいE4M3を選ぶのが基本です。

最近は単純なFP8だけでなく、層ごとの倍率を持つ「fp8\_scaled」や、ブロック単位のMXFP8・NVFP4のチェックポイントも配布されています。[ComfyUI v0.39.0のquant\_ops.py](https://github.com/Comfy-Org/ComfyUI/blob/v0.39.0/comfy/quant%5Fops.py)が扱う主な形式と、`model_management.py`での演算の判定条件は次のとおりです。

| 形式名（QUANT\_ALGOS） | 中身                  | 量子化演算の条件                                          |
| ----------------- | ------------------- | ------------------------------------------------- |
| float8\_e4m3fn    | E4M3＋テンソル単位の倍率      | CC 8.9以上（上記のPyTorch条件あり）                          |
| mxfp8             | E4M3＋32要素ごとの倍率      | CC 10以上・PyTorch 2.10以上                            |
| nvfp4             | FP4＋16要素ごとの倍率       | CC 10以上                                           |
| int8\_tensorwise  | INT8重み＋行単位の動的活性化量子化 | Apple Silicon・Intel XPU・DirectML・Iluvatar CoreX以外 |

条件を満たさないGPUでは、`get_disabled_quant_formats`がその形式の量子化演算を無効にし、重みを計算のたびに元の精度へ戻して通常の行列積で処理します。RTX 40シリーズでMXFP8版やNVFP4版を選んでもVRAMとディスクは減りますが、演算はBF16などに戻るため、低精度演算器による高速化は得られません。処理全体の速度は、重みの転送量削減と復元処理の負荷によって変わります。RTX 40ではfp8\_scaled（float8\_e4m3fn）、RTX 50ではMXFP8やNVFP4が比較候補になります。ただし、v0.39.0の最適化CUDAバックエンドにはCUDA 13.0以上でビルドされたPyTorchも必要で、画質と速度は対象モデルで比較して選びます。

Ampere以前のGPUでは、FP8演算器による高速化は得られません。FP8重みを保持する実装では容量を削減でき、転送量が減ることで処理時間が短くなる場合もあります。VRAM不足でモデルが載らないならFP8版が有効ですが、FP16版が余裕を持って載るならFP16版のほうが画質面の不安は少なくなります。

## FP8を採用すべきでない場面

FP8はどのモデルにも効く高速化ではありません。次の条件に当てはまるなら、BF16/FP16のまま運用するか、別の量子化を選ぶほうが合理的です。

- **Ampere以前のGPUでFP8演算による高速化を求める**：FP8演算器がないため、得られるのは重みの読み込み量が減る分だけ。VRAMを減らしたいだけなら、INT8/INT4の重み量子化（GPTQ・AWQ・GGUFなど）のほうが選択肢が多い
- **エッジ端末の推論**：Qualcommの研究者による論文「[FP8 versus INT8 for efficient deep learning inference](https://arxiv.org/abs/2303.17951)」（arXiv:2303.17951）は、専用ハードウェアではFP形式がINT形式より演算効率で50〜180%劣るとして、推論用にはINT8を推している。データセンターGPUとは逆の結論になる点に注意
- **メモリも演算も律速になっていない小さなモデル**：スケーリングや精度検証の手間に見合う速度差が出にくい
- **学習でスケーリング方式を検証する余裕がない**：損失の発散はFP8化の典型的な失敗。まずは推論側のFP8化か、BF16学習で始める

INT8・INT4を含めた量子化の選び方は[モデル量子化のPTQとQATの解説](/tech/details/15846/)と[LLM量子化とGPTQ・AWQ・GGUFの違い](/tech/details/13791/)で比較しています。

## FP8に関するよくある質問

### FP8とFP16の違いは何ですか？

ビット数が半分で、メモリと転送量も半分になります。対応GPU（H100など）では行列演算の理論性能がFP16の2倍です。一方、仮数がFP16の10ビットに対してE4M3は3ビットしかなく、有効桁は約3桁から約1桁に減り、1つの値の丸め誤差は数%に達します（3.14159がE4M3で3.25）。それでもスケーリングを入れて行列積だけをFP8にし、累積やマスター重みを高精度に残せば、16ビット学習と同等の精度にできることが論文や大規模学習で示されています。

### FP8とINT8の違いは何ですか？

どちらも8ビットですが、INT8は等間隔の整数、FP8は指数を持つ浮動小数点です。本文のテンソル単位・最大絶対値スケーリングの実験では、一様分布と正規分布でINT8、外れ値を混ぜたデータでFP8の復元誤差が小さくなりました。10万要素に外れ値10個を混ぜた実測では、INT8の誤差22.71%に対しFP8は2.67%でした。演算性能はH100では同じですが、HGX B300ではINT8がFP8の1/24です。

### FP8量子化とは何ですか？

BF16/FP16で学習済みのモデルの重みや活性化を、スケールを付けてFP8に変換することです。vLLMなら`--quantization fp8_per_block`のように指定して読み込み時に変換でき、事前に変換するならllm-compressorの`FP8_DYNAMIC`方式が校正データなしで使えます。FP8に変換した値の容量は半分になりますが、モデル全体のメモリ削減率と処理速度は、量子化対象、スケーリングの負荷、GPUやカーネルによって変わります。

### BF16とFP16、FP8の違いは何ですか？

BF16は指数8ビットでFP32と同じ範囲を持ち、FP16は仮数10ビットで刻みが8倍細かい代わりに、最大有限値は65,504です。学習ではBF16が標準です。FP8はこれらを置き換えるものではなく、行列積の入力を軽くするための8ビット形式で、BF16やFP32と組み合わせて使います。

### RTX 3090やA100（Ampere）でFP8は使えますか？

FP8で保存されたモデルは読み込めますが、演算はFP16/BF16に戻して行われます。FP8演算器による高速化はなく、VRAMが減るのはFP8のまま保持する実装（ComfyUIのFP8重みやvLLMのW8A16など）の場合です。FP8演算に対応するのはRTX 40シリーズ・L4・L40S（Ada Lovelace）、H100（Hopper）、Blackwell世代です。

## 関連記事

- [浮動小数点数とは？IEEE 754の仕組みと誤差・型の選び方を実装目線で解説](/tech/details/15257/)
- [量子化（モデル量子化）とは？仕組み・PTQとQATの違いと実装判断を解説【2026年版】](/tech/details/15846/)
- [LLM量子化とは？仕組みとGPTQ・AWQ・GGUFの違い・企業の採用判断を解説【2026年版】](/tech/details/13791/)
- [vLLMとは？PagedAttentionの仕組み・使い方とOllama・TensorRT-LLMとの違いを実装者目線で解説](/tech/details/15786/)
- [TensorRT-LLMとは？NVIDIA GPUでのLLM推論高速化の仕組みと採用判断を解説【2026年版】](/tech/details/16075/)

---

出典: [FP8とは？FP16・BF16・FP4との違いとE4M3/E5M2の仕組み、対応GPU【2026年版】](<https://www.issoh.co.jp/tech/details/5959/>)（株式会社一創）
