Download tensor_roll/quant.py from Snapkitty/tensor-roll: direct link, hf CLI and curl.
- Browser
- Download file 15.9 kB
-
https://huggingface.co/Snapkitty/tensor-roll/resolve/main/tensor_roll/quant.py
- Command line
-
hf download hf://Snapkitty/tensor-roll/tensor_roll/quant.py
-
curl -L -o quant.py https://huggingface.co/Snapkitty/tensor-roll/resolve/main/tensor_roll/quant.py
15.9 kB
| # Tensor Roll — Recursive CUDA-Q Model Quantizer | |
| # Copyright (C) 2026 SnapKitty Collective | |
| # SPDX-License-Identifier: AGPL-3.0-or-later | |
| """Quantization formats: FP16 / BF16 / INT8 / INT4. | |
| All quantizers are implemented here from first principles (no external | |
| quantizer libraries). Every quantize() returns (payload, meta) and every | |
| meta carries enough information for an exact dequantize() round-trip. | |
| """ | |
| from __future__ import annotations | |
| import numpy as np | |
| def to_fp16(W: np.ndarray): | |
| W = np.asarray(W, dtype=np.float32) | |
| return W.astype(np.float16), {"fmt": "fp16"} | |
| def from_fp16(q: np.ndarray) -> np.ndarray: | |
| return np.asarray(q, dtype=np.float32) | |
| def to_bf16(W: np.ndarray): | |
| """Round-to-nearest-even float32 -> bfloat16 (stored as uint16).""" | |
| a = np.asarray(W, dtype=np.float32) | |
| i = a.view(np.int32).copy() | |
| finite = np.isfinite(a) | |
| lsb = (i >> 16) & 1 | |
| i[finite] += (0x7FFF + lsb[finite]).astype(np.int32) | |
| return (i >> 16).astype(np.uint16), {"fmt": "bf16"} | |
| def from_bf16(q: np.ndarray) -> np.ndarray: | |
| return (np.asarray(q, dtype=np.int32) << 16).view(np.float32) | |
| def quantize_int8(W: np.ndarray): | |
| """Asymmetric per-tensor INT8.""" | |
| W = np.asarray(W, dtype=np.float64) | |
| wmin, wmax = float(W.min()), float(W.max()) | |
| if wmax == wmin: | |
| return np.zeros(W.shape, dtype=np.int8), { | |
| "fmt": "int8", "scale": 1.0, "zero": 0.0} | |
| scale = (wmax - wmin) / 255.0 | |
| q = np.clip(np.round((W - wmin) / scale), 0, 255).astype(np.int64) - 128 | |
| return q.astype(np.int8), {"fmt": "int8", "scale": scale, "zero": wmin} | |
| def dequantize_int8(q: np.ndarray, meta: dict) -> np.ndarray: | |
| return (q.astype(np.float64) + 128.0) * meta["scale"] + meta["zero"] | |
| def quantize_int4(W: np.ndarray, group: int = 32): | |
| """Group-wise asymmetric INT4, two nibbles packed per byte (low nibble | |
| first). Groups run along the last axis; ragged tail is zero-padded and | |
| the pad is recorded so dequantize() restores the exact shape.""" | |
| W = np.asarray(W, dtype=np.float64) | |
| shape = list(W.shape) | |
| last = shape[-1] | |
| rows = int(np.prod(shape[:-1])) if len(shape) > 1 else 1 | |
| flat = W.reshape(rows, last) | |
| pad = (-last) % group | |
| if pad: | |
| flat = np.concatenate([flat, np.zeros((rows, pad))], axis=1) | |
| G = flat.reshape(-1, group) | |
| gmin = G.min(axis=1) | |
| gmax = G.max(axis=1) | |
| scale = np.maximum(gmax - gmin, 1e-300) / 15.0 | |
| q = np.clip(np.round((G - gmin[:, None]) / scale[:, None]), 0, 15).astype(np.uint8) | |
| packed = (q[:, 0::2] | (q[:, 1::2] << 4)).astype(np.uint8) | |
| meta = {"fmt": "int4", "group": group, "shape": shape, | |
| "scales": scale.astype(np.float64), "zeros": gmin.astype(np.float64), | |
| "rows": rows, "last": last} | |
| return packed, meta | |
| def dequantize_int4(packed: np.ndarray, meta: dict) -> np.ndarray: | |
| group, rows, last = meta["group"], meta["rows"], meta["last"] | |
| p = np.asarray(packed).reshape(-1, group // 2) | |
| q = np.empty((p.shape[0], group), dtype=np.float64) | |
| q[:, 0::2] = p & 0x0F | |
| q[:, 1::2] = (p >> 4) & 0x0F | |
| G = q * np.asarray(meta["scales"])[:, None] + np.asarray(meta["zeros"])[:, None] | |
| flat = G.reshape(rows, -1)[:, :last] | |
| return flat.reshape(meta["shape"]).astype(np.float32) | |
| def quantize_tensor(W: np.ndarray, fmt: str): | |
| """Dispatch: fmt in {fp32, fp16, bf16, int8, int4}.""" | |
| if fmt == "fp32": | |
| return np.asarray(W, dtype=np.float32), {"fmt": "fp32"} | |
| if fmt == "fp16": | |
| return to_fp16(W) | |
| if fmt == "bf16": | |
| return to_bf16(W) | |
| if fmt == "int8": | |
| return quantize_int8(W) | |
| if fmt == "int4": | |
| return quantize_int4(W) | |
| raise ValueError(f"unknown format {fmt!r}") | |
| def dequantize_tensor(q: np.ndarray, meta: dict) -> np.ndarray: | |
| fmt = meta["fmt"] | |
| if fmt == "fp32": | |
| return np.asarray(q, dtype=np.float32) | |
| if fmt == "fp16": | |
| return from_fp16(q) | |
| if fmt == "bf16": | |
| return from_bf16(q) | |
| if fmt == "int8": | |
| return dequantize_int8(q, meta).astype(np.float32) | |
| if fmt == "int4": | |
| return dequantize_int4(q, meta) | |
| raise ValueError(f"unknown format {fmt!r}") | |
| def payload_nbytes(q: np.ndarray, fmt: str) -> int: | |
| return int(np.asarray(q).nbytes) | |
| # --------------------------------------------------------------------------- | |
| # INT4 failure resolution (F6): per-tensor error instrumentation, advanced | |
| # schemes, mixed precision, and quantization-aware recovery. | |
| # --------------------------------------------------------------------------- | |
| def quantize_int4_per_tensor(W: np.ndarray): | |
| """Whole-tensor asymmetric INT4: ONE scale/zero over the entire tensor.""" | |
| W = np.asarray(W, dtype=np.float64) | |
| shape = list(W.shape) | |
| flat = W.ravel() | |
| gmin, gmax = float(flat.min()), float(flat.max()) | |
| scale = max(gmax - gmin, 1e-300) / 15.0 | |
| q = np.clip(np.round((flat - gmin) / scale), 0, 15).astype(np.uint8) | |
| if q.size % 2: | |
| q = np.concatenate([q, np.zeros(1, dtype=np.uint8)]) | |
| packed = (q[0::2] | (q[1::2] << 4)).astype(np.uint8) | |
| meta = {"fmt": "int4", "scheme": "per-tensor", "shape": shape, | |
| "scales": np.array([scale]), "zeros": np.array([gmin]), | |
| "size": flat.size} | |
| return packed, meta | |
| def dequantize_int4_per_tensor(packed: np.ndarray, meta: dict) -> np.ndarray: | |
| q = np.asarray(packed) | |
| flat = np.empty(q.size * 2, dtype=np.float64) | |
| flat[0::2] = q & 0x0F | |
| flat[1::2] = (q >> 4) & 0x0F | |
| flat = flat[:meta["size"]] | |
| W = flat * float(meta["scales"][0]) + float(meta["zeros"][0]) | |
| return W.reshape(meta["shape"]).astype(np.float32) | |
| def quantize_int4_per_channel(W: np.ndarray): | |
| """Per-channel (per-row) asymmetric INT4: one scale/zero per row instead | |
| of per group-of-32. Returns (packed, meta) with the same nibble layout | |
| as quantize_int4 so dequantize_int4-style unpacking applies.""" | |
| W = np.asarray(W, dtype=np.float64) | |
| shape = list(W.shape) | |
| last = shape[-1] | |
| rows = int(np.prod(shape[:-1])) if len(shape) > 1 else 1 | |
| flat = W.reshape(rows, last) | |
| gmin = flat.min(axis=1) | |
| gmax = flat.max(axis=1) | |
| scale = np.maximum(gmax - gmin, 1e-300) / 15.0 | |
| q = np.clip(np.round((flat - gmin[:, None]) / scale[:, None]), 0, 15) | |
| q = q.astype(np.uint8) | |
| # pad last dim to even for nibble packing | |
| if last % 2: | |
| q = np.concatenate([q, np.zeros((rows, 1), dtype=np.uint8)], axis=1) | |
| packed = (q[:, 0::2] | (q[:, 1::2] << 4)).astype(np.uint8) | |
| meta = {"fmt": "int4", "scheme": "per-channel", "shape": shape, | |
| "scales": scale.astype(np.float64), | |
| "zeros": gmin.astype(np.float64), | |
| "rows": rows, "last": last} | |
| return packed, meta | |
| def dequantize_int4_per_channel(packed: np.ndarray, meta: dict) -> np.ndarray: | |
| rows, last = meta["rows"], meta["last"] | |
| p = np.asarray(packed).reshape(rows, -1) | |
| q = np.empty((rows, p.shape[1] * 2), dtype=np.float64) | |
| q[:, 0::2] = p & 0x0F | |
| q[:, 1::2] = (p >> 4) & 0x0F | |
| q = q[:, :last] | |
| W = q * np.asarray(meta["scales"])[:, None] + np.asarray(meta["zeros"])[:, None] | |
| return W.reshape(meta["shape"]).astype(np.float32) | |
| def search_group_size(W: np.ndarray, | |
| groups: tuple = (16, 32, 64, 128)) -> dict: | |
| """Search the INT4 group size by MEASURED reconstruction MSE — the | |
| winner is the argmin of measured error, never a hard-coded default.""" | |
| W = np.asarray(W, dtype=np.float64) | |
| results = [] | |
| for g in groups: | |
| if g < 2: | |
| continue | |
| q, meta = quantize_int4(W, group=g) | |
| back = dequantize_int4(q, meta).astype(np.float64) | |
| err = float(np.mean((W - back) ** 2)) | |
| results.append({"group": g, "mse": err, | |
| "bytes": int(np.asarray(q).nbytes)}) | |
| results.sort(key=lambda r: r["mse"]) | |
| return {"results": results, "best_group": results[0]["group"], | |
| "best_mse": results[0]["mse"]} | |
| def quant_error_map(W: np.ndarray, Wd: np.ndarray) -> dict: | |
| """Instrument quantization error per tensor. All values measured.""" | |
| W = np.asarray(W, dtype=np.float64) | |
| Wd = np.asarray(Wd, dtype=np.float64) | |
| err = W - Wd | |
| flat = err.ravel() | |
| sd = float(flat.std()) if flat.size else 0.0 | |
| per_channel = [] | |
| if W.ndim == 2: | |
| for r in range(W.shape[0]): | |
| e = err[r] | |
| per_channel.append(float(np.mean(e ** 2))) | |
| # per-group error at group=32 along the last axis | |
| per_group = [] | |
| if W.ndim >= 1 and W.shape[-1] >= 32: | |
| G = err.reshape(-1, 32) | |
| per_group = [float(np.mean(g ** 2)) for g in G] | |
| denom = float(np.linalg.norm(W)) | |
| return { | |
| "min": float(err.min()) if err.size else 0.0, | |
| "max": float(err.max()) if err.size else 0.0, | |
| "mean": float(err.mean()) if err.size else 0.0, | |
| "variance": float(err.var()) if err.size else 0.0, | |
| "std": sd, | |
| "outlier_frac": float(np.mean(np.abs(flat) > 3 * sd)) if sd > 0 else 0.0, | |
| "mse": float(np.mean(flat ** 2)) if flat.size else 0.0, | |
| "cosine_sim": float(np.dot(W.ravel(), Wd.ravel()) | |
| / (np.linalg.norm(W.ravel()) | |
| * np.linalg.norm(Wd.ravel()) + 1e-300)), | |
| "rel_err": float(np.linalg.norm(err) / (denom + 1e-300)), | |
| "per_channel_mse": per_channel, | |
| "per_group_mse": per_group, | |
| } | |
| def int4_scheme_report(W: np.ndarray) -> dict: | |
| """Benchmark per-tensor / per-channel / group-wise INT4 on one tensor. | |
| Returns measured MSE + byte size for each scheme; the caller picks the | |
| winner from measurements.""" | |
| W = np.asarray(W, dtype=np.float64) | |
| out = {} | |
| qt, mt = quantize_int4_per_tensor(W) | |
| backt = dequantize_int4_per_tensor(qt, mt).astype(np.float64) | |
| out["per-tensor"] = {"mse": float(np.mean((W - backt) ** 2)), | |
| "bytes": int(np.asarray(qt).nbytes), | |
| "meta_bytes": 16} | |
| qp, mp = quantize_int4_per_channel(W) | |
| backp = dequantize_int4_per_channel(qp, mp).astype(np.float64) | |
| out["per-channel"] = {"mse": float(np.mean((W - backp) ** 2)), | |
| "bytes": int(np.asarray(qp).nbytes), | |
| "meta_bytes": int(mp["scales"].nbytes | |
| + mp["zeros"].nbytes)} | |
| gs = search_group_size(W) | |
| out["group-wise"] = {"mse": gs["best_mse"], | |
| "bytes": next(r["bytes"] for r in gs["results"] | |
| if r["group"] == gs["best_group"]), | |
| "best_group": gs["best_group"], | |
| "all": gs["results"]} | |
| # int8 reference for the mixed-precision gain computation | |
| qi, mi = quantize_int8(W) | |
| backi = dequantize_int8(qi, mi).astype(np.float64) | |
| out["int8"] = {"mse": float(np.mean((W - backi) ** 2)), | |
| "bytes": int(np.asarray(qi).nbytes)} | |
| return out | |
| def mixed_precision_plan(params: dict, size_budget_bytes: int) -> dict: | |
| """Assign INT4 vs INT8 per tensor under a physical size budget. | |
| Tensors where INT8 buys the most error reduction per extra byte stay | |
| INT8; the rest go INT4 (best measured scheme per tensor). Sensitive | |
| tensors are thereby protected by measurement, not by fiat. | |
| """ | |
| rows = [] | |
| for k, v in params.items(): | |
| v = np.asarray(v) | |
| if v.ndim != 2 or v.size < 64: | |
| rows.append({"tensor": k, "fmt": "fp32", | |
| "bytes": int(v.nbytes), "gain_per_byte": 0.0, | |
| "int4_mse": 0.0, "int8_mse": 0.0}) | |
| continue | |
| rep = int4_scheme_report(v) | |
| best4 = min(rep["per-tensor"], rep["per-channel"], | |
| rep["group-wise"], key=lambda r: r["mse"]) | |
| b4 = best4["bytes"] + best4.get("meta_bytes", 0) | |
| b8 = rep["int8"]["bytes"] | |
| gain = max(0.0, best4["mse"] - rep["int8"]["mse"]) | |
| gpb = gain / max(1, b8 - b4) | |
| rows.append({"tensor": k, "fmt": "int4", "bytes": b4, | |
| "gain_per_byte": gpb, "int4_mse": best4["mse"], | |
| "int8_mse": rep["int8"]["mse"], | |
| "int4_scheme": "per-tensor" | |
| if best4 is rep["per-tensor"] else | |
| ("per-channel" if best4 is rep["per-channel"] | |
| else f"group-{rep['group-wise']['best_group']}")}) | |
| # start all-int4, upgrade highest gain/byte to int8 while budget allows | |
| total4 = sum(r["bytes"] for r in rows) | |
| cands = sorted([r for r in rows if r["fmt"] == "int4" | |
| and r["gain_per_byte"] > 0], | |
| key=lambda r: -r["gain_per_byte"]) | |
| plan, used = {}, total4 | |
| for r in rows: | |
| plan[r["tensor"]] = "int4" | |
| for r in cands: | |
| v = np.asarray(params[r["tensor"]]) | |
| qi, _ = quantize_int8(v) | |
| b8 = int(np.asarray(qi).nbytes) | |
| if used - r["bytes"] + b8 <= size_budget_bytes: | |
| plan[r["tensor"]] = "int8" | |
| used = used - r["bytes"] + b8 | |
| return {"plan": plan, "total_bytes": used, | |
| "budget_bytes": int(size_budget_bytes), | |
| "within_budget": used <= size_budget_bytes, | |
| "n_int8": sum(1 for v in plan.values() if v == "int8"), | |
| "n_int4": sum(1 for v in plan.values() if v == "int4"), | |
| "rows": rows} | |
| def dequantize_dispatch(q: np.ndarray, meta: dict) -> np.ndarray: | |
| """Dequantize honoring per-tensor / per-channel int4 metadata.""" | |
| if meta.get("fmt") == "int4" and meta.get("scheme") == "per-tensor": | |
| return dequantize_int4_per_tensor(q, meta) | |
| if meta.get("fmt") == "int4" and meta.get("scheme") == "per-channel": | |
| return dequantize_int4_per_channel(q, meta) | |
| return dequantize_tensor(q, meta) | |
| def recovery_train(s_qparams: dict, s_cfg, t_params: dict, t_cfg, | |
| *, steps: int = 30, batch: int = 64, lr: float = 1e-3, | |
| seed: int = 0) -> dict: | |
| """Quantization-aware recovery (spec section 9): | |
| INT4-dequantized student -> distillation (task + logit KL) | |
| vs the teacher -> requantize INT4 -> evaluate. | |
| Returns measured ppl before/after and the gap delta. Improvement is | |
| reported ONLY if measured; otherwise the (non-)result stands. | |
| """ | |
| from .model import (Adam, backward, ce_loss, eval_metrics, forward, | |
| gen_data, kl_loss) | |
| rng = np.random.default_rng(seed) | |
| # start from the dequantized INT4 weights (float working copy) | |
| params = {k: v.astype(np.float32) for k, v in s_qparams.items()} | |
| before = eval_metrics(params, s_cfg, batches=4, batch=batch, seed=911) | |
| opt = Adam(params, lr=lr) | |
| for _ in range(steps): | |
| ids = gen_data(rng, batch, s_cfg) | |
| tl, _ = forward(t_params, t_cfg, ids[:, :-1]) | |
| sl, cache = forward(params, s_cfg, ids[:, :-1]) | |
| lt, dl_t = ce_loss(sl, ids[:, 1:]) | |
| lk, dl_k = kl_loss(sl, tl, tau=2.0) | |
| grads = backward(params, s_cfg, cache, dl_t + 0.5 * dl_k) | |
| opt.step(params, grads) | |
| after = eval_metrics(params, s_cfg, batches=4, batch=batch, seed=911) | |
| # requantize and measure the real INT4 artifact quality | |
| re_q = {} | |
| for k, v in params.items(): | |
| if v.ndim == 2 and v.size >= 64: | |
| q, m = quantize_int4(v) | |
| re_q[k] = dequantize_int4(q, m).astype(np.float32) | |
| else: | |
| re_q[k] = v | |
| re_eval = eval_metrics(re_q, s_cfg, batches=4, batch=batch, seed=911) | |
| return { | |
| "steps": steps, | |
| "ppl_before": float(np.exp(before["loss"])), | |
| "ppl_after_train": float(np.exp(after["loss"])), | |
| "ppl_after_requant": float(np.exp(re_eval["loss"])), | |
| "gap_before": float(np.exp(before["loss"]) - np.exp(after["loss"])), | |
| "gap_delta": float(np.exp(before["loss"]) | |
| - np.exp(re_eval["loss"])), | |
| "improved": bool(np.exp(re_eval["loss"]) < np.exp(before["loss"])), | |
| } | |