Download decider/fp8.py from hayriyigit/decider-4b-tr-rag: direct link, hf CLI and curl.
- Browser
- Download file 2.59 kB
-
https://huggingface.co/hayriyigit/decider-4b-tr-rag/resolve/main/decider/fp8.py
- Command line
-
hf download hf://hayriyigit/decider-4b-tr-rag/decider/fp8.py
-
curl -L -o fp8.py https://huggingface.co/hayriyigit/decider-4b-tr-rag/resolve/main/decider/fp8.py
2.59 kB
| """FP8 (e4m3) linear layers for Hopper via torch._scaled_mm. | |
| Weights: per-output-channel scales, quantised once. Activations: per-token dynamic scales. | |
| Under torch.compile the quantisation ops fuse into the surrounding elementwise work.""" | |
| import torch, torch.nn as nn | |
| E4M3_MAX = 448.0 | |
| def _quant_rowwise(x): | |
| s = x.abs().amax(dim=-1, keepdim=True).float().clamp(min=1e-12) / E4M3_MAX | |
| return (x.float() / s).clamp(-E4M3_MAX, E4M3_MAX).to(torch.float8_e4m3fn), s | |
| class FP8Linear(nn.Module): | |
| def __init__(self, lin: nn.Linear): | |
| super().__init__() | |
| wq, sw = _quant_rowwise(lin.weight.detach()) # [N,K] fp8, [N,1] | |
| self.register_buffer("wq", wq.contiguous()) # [N,K]; passed as wq.t() -> [K,N] column-major, as _scaled_mm wants | |
| self.register_buffer("sw_t", sw.t().contiguous()) # [1,N] | |
| self.bias = None if lin.bias is None else nn.Parameter(lin.bias.detach().clone(), requires_grad=False) | |
| self.in_features, self.out_features = lin.in_features, lin.out_features | |
| self.out_dtype = lin.weight.dtype | |
| def forward(self, x): | |
| shp = x.shape[:-1] | |
| x2 = x.reshape(-1, self.in_features) | |
| xq, sx = _quant_rowwise(x2) | |
| y = torch._scaled_mm(xq, self.wq.t(), scale_a=sx, scale_b=self.sw_t, bias=self.bias, out_dtype=self.out_dtype) | |
| return y.reshape(*shp, self.out_features) | |
| def convert_to_fp8(model, skip=("lm_head",), min_dim=1024): | |
| """Replace nn.Linear (with in/out >= min_dim) by FP8Linear in place. Returns count.""" | |
| n = 0 | |
| for name, mod in list(model.named_modules()): | |
| for cname, child in list(mod.named_children()): | |
| full = f"{name}.{cname}" if name else cname | |
| if isinstance(child, nn.Linear) and not any(s in full for s in skip) and min(child.in_features, child.out_features) >= min_dim: | |
| setattr(mod, cname, FP8Linear(child)); n += 1 | |
| return n | |
| if __name__ == "__main__": | |
| import time | |
| lin = nn.Linear(2048, 6144, bias=False).cuda().to(torch.bfloat16) | |
| f8 = FP8Linear(lin) | |
| x = torch.randn(8192, 2048, device="cuda", dtype=torch.bfloat16) | |
| ref = lin(x); got = f8(x) | |
| print("rel err", ((ref.float() - got.float()).abs().mean() / ref.float().abs().mean()).item()) | |
| for f, name in [(lin, "bf16 linear"), (f8, "fp8 linear (eager)"), (torch.compile(f8), "fp8 linear (compiled)")]: | |
| for _ in range(3): f(x) | |
| torch.cuda.synchronize(); t = time.time() | |
| for _ in range(20): f(x) | |
| torch.cuda.synchronize(); print(f"{name:24s} {(time.time()-t)/20*1000:.3f} ms") | |