Spaces:
Running on Zero
Running on Zero
File size: 1,958 Bytes
1811aab | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | """FP8 linears for sampling (Hopper): the large bias-free linears of every DiT block -- attention qkv / out
and the MLP -- with e4m3 weights scaled per output channel and activations quantized per row on the fly,
multiplied by torch._scaled_mm into bf16. Inference only (no gradient), on a model whose LoRA is merged;
approximate (FP8 rounding), so its effect is measured, not assumed (tests/test_fp8.py).
"""
from __future__ import annotations
import torch
from torch import nn
E4M3_MAX = 448.0
class FP8Linear(nn.Module):
def __init__(self, linear: nn.Linear):
super().__init__()
assert linear.bias is None
w = linear.weight.detach().float() # [out, in]
scale = (w.abs().amax(dim=1, keepdim=True) / E4M3_MAX).clamp_min(1e-12)
self.register_buffer("weight_fp8", (w / scale).to(torch.float8_e4m3fn), persistent=False)
self.register_buffer("weight_scale", scale.t().contiguous(), persistent=False) # [1, out]
self.out_features = linear.out_features
def forward(self, x):
shape = x.shape
x2 = x.reshape(-1, shape[-1])
scale = (x2.abs().amax(dim=1, keepdim=True).float() / E4M3_MAX).clamp_min(1e-12) # [rows, 1]
xq = (x2.float() / scale).to(torch.float8_e4m3fn)
y = torch._scaled_mm(xq, self.weight_fp8.t(), scale_a=scale, scale_b=self.weight_scale,
out_dtype=torch.bfloat16)
return y.reshape(*shape[:-1], self.out_features)
def quantize_blocks(model) -> int:
"""Swap the DiT blocks' qkv / out / fc1 / fc2 linears for FP8Linear in place; returns how many."""
n = 0
for block in model.blocks:
for parent, names in ((block.attn, ("qkv_proj", "out_proj")), (block.mlp, ("fc1", "fc2"))):
for name in names:
setattr(parent, name, FP8Linear(getattr(parent, name)))
n += 1
torch.cuda.empty_cache()
return n
|