Files
2026-07-13 12:31:40 +08:00

79 lines
2.7 KiB
Python

# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES
#
# Licensed under the Apache License, Version 2.0 (the "License");
# You may not use this file except in compliance with the License.
# To view a copy of this license, visit http://www.apache.org/licenses/LICENSE-2.0
#
# No warranties are given. The work is provided "AS IS", without warranty of any kind, express or implied.
#
# SPDX-License-Identifier: Apache-2.0
"""TorchAO FP8 post-training quantization helpers."""
import torch
import torch.nn as nn
# Small conditioning/output projections are both numerically sensitive and too
# small to amortize dynamic activation quantization on H100.
_BF16_MODULES = {
"text_embedding.0",
"text_embedding.2",
"time_embedding.0",
"time_embedding.2",
"time_projection.1",
"head.head",
}
def quantize_model_fp8(model: nn.Module, *, verbose: bool = False) -> int:
"""Quantize compatible BF16 linear layers to row-wise dynamic FP8 in-place."""
if not torch.cuda.is_available():
raise RuntimeError("TorchAO FP8 inference requires a CUDA GPU.")
device = next(model.parameters()).device
if device.type != "cuda":
raise ValueError("Move the BF16 model to CUDA before applying FP8 quantization.")
if torch.cuda.get_device_capability(device) < (8, 9):
raise RuntimeError("TorchAO FP8 inference requires compute capability 8.9 or newer.")
try:
from torchao.quantization import (
Float8DynamicActivationFloat8WeightConfig,
PerRow,
quantize_,
)
except ImportError as exc:
raise ImportError(
"FP8 inference requires a TorchAO version compatible with the installed PyTorch."
) from exc
quantized_names = []
skipped_names = []
def filter_fn(module: nn.Module, fqn: str) -> bool:
if not isinstance(module, nn.Linear):
return False
if fqn in _BF16_MODULES:
skipped_names.append(fqn)
return False
if module.weight.dtype != torch.bfloat16:
raise TypeError(f"FP8 layer {fqn!r} must be BF16, got {module.weight.dtype}.")
out_features, in_features = module.weight.shape
if in_features % 16 or out_features % 16:
skipped_names.append(fqn)
return False
quantized_names.append(fqn)
return True
quantize_(
model,
Float8DynamicActivationFloat8WeightConfig(granularity=PerRow()),
filter_fn=filter_fn,
)
if verbose:
print(
f"[FP8] TorchAO W8A8 quantized {len(quantized_names)} linear layers "
f"with row-wise scaling; kept {len(skipped_names)} layers in BF16"
)
return len(quantized_names)