Download python/bhc/xlit_q.py from sourojitdotcom/AksharaLP-1M-v2: direct link, hf CLI and curl.
- Browser
- Download file 3.65 kB
-
https://huggingface.co/sourojitdotcom/AksharaLP-1M-v2/resolve/main/python/bhc/xlit_q.py
- Command line
-
hf download hf://sourojitdotcom/AksharaLP-1M-v2/python/bhc/xlit_q.py
-
curl -L -o xlit_q.py https://huggingface.co/sourojitdotcom/AksharaLP-1M-v2/resolve/main/python/bhc/xlit_q.py
3.65 kB
| """Low-bit weight storage for the char Transformer: matrices -> b-bit (4 or 8) symmetric integers with one fp16 | |
| scale per group of G consecutive input weights (per output row); vectors -> fp16. De-quantised to fp32 at load.""" | |
| import io, json | |
| import numpy as np, torch | |
| from bhc.xlit_model import XlitTransformer | |
| def _q(v, bits, G): | |
| r, c = v.shape; pad = (-c) % G; vp = np.pad(v, ((0, 0), (0, pad))).reshape(r, -1, G) | |
| L = 2 ** (bits - 1) - 1; base = np.abs(vp).max(2, keepdims=True) / L; base[base == 0] = 1 | |
| best_err = None | |
| for al in ((1.0,) if bits >= 8 else (1.0, 0.95, 0.9, 0.85, 0.8, 0.75, 0.7, 0.65)): # MSE-optimal clipping per group | |
| s_ = (base * al).astype(np.float16).astype(np.float32); q_ = np.clip(np.round(vp / s_), -L, L) | |
| e = ((q_ * s_ - vp) ** 2).sum(2, keepdims=True) | |
| if best_err is None: best_err, sc, q = e, s_, q_ | |
| else: m = e < best_err; best_err = np.where(m, e, best_err); sc = np.where(m, s_, sc); q = np.where(m, q_, q) | |
| q = q.astype(np.int8) | |
| return q.reshape(r, -1)[:, :c], sc[..., 0].astype(np.float16) | |
| def export_q(model, path, bits=4, G=32, emb_bits=8): | |
| """bits for Linear weights; emb_bits for the (small, outlier-prone) embedding/position tables.""" | |
| sd = model.state_dict(); meta = {"cfg": model.cfg, "bits": bits, "G": G, "tensors": []}; buf = io.BytesIO() | |
| for k, v in sd.items(): | |
| v = v.float().cpu().numpy() | |
| if v.ndim == 2: | |
| b_ = emb_bits if ("emb" in k or "pos" in k) else bits | |
| q, sc = _q(v, b_, G); flat = q.reshape(-1) | |
| if b_ == 4: | |
| u = (flat + 8).astype(np.uint8); u = np.r_[u, np.zeros(len(u) % 2, np.uint8)]; data = (u[0::2] << 4 | u[1::2]).astype(np.uint8).tobytes() | |
| else: data = flat.astype(np.int8).tobytes() | |
| meta["tensors"].append([k, list(v.shape), "q%d" % b_, buf.tell(), len(data)]); buf.write(data); buf.write(sc.tobytes()) | |
| else: | |
| meta["tensors"].append([k, list(v.shape), "f16", buf.tell(), 0]); buf.write(v.astype(np.float16).tobytes()) | |
| hdr = json.dumps(meta, separators=(",", ":")).encode() | |
| with open(path, "wb") as f: f.write(len(hdr).to_bytes(4, "little")); f.write(hdr); f.write(buf.getvalue()) | |
| def load_q(path): | |
| b = open(path, "rb").read(); n = int.from_bytes(b[:4], "little"); meta = json.loads(b[4:4 + n]); data = b[4 + n:] | |
| bits, G = meta["bits"], meta["G"]; m = XlitTransformer(**meta["cfg"]); sd = {} | |
| for k, shape, kind, off, ln in meta["tensors"]: | |
| cnt = int(np.prod(shape)) | |
| if kind.startswith("q"): | |
| bits = int(kind[1:]) if len(kind) > 1 else meta["bits"] | |
| raw = np.frombuffer(data, np.uint8 if bits == 4 else np.int8, ln, off) | |
| if bits == 4: q = (np.stack([raw >> 4, raw & 15], 1).reshape(-1)[:cnt].astype(np.int16) - 8) | |
| else: q = raw.astype(np.int16) | |
| r, c = shape; ng = (c + G - 1) // G | |
| sc = np.frombuffer(data, np.float16, r * ng, off + ln).astype(np.float32).reshape(r, ng) | |
| w = q.reshape(r, c).astype(np.float32) * np.repeat(sc, G, 1)[:, :c] | |
| sd[k] = torch.from_numpy(w) | |
| else: | |
| sd[k] = torch.from_numpy(np.frombuffer(data, np.float16, cnt, off).astype(np.float32).reshape(shape).copy()) | |
| m.load_state_dict(sd); m.eval(); return m | |
| def load_any(path): | |
| if path.endswith(".int8"): | |
| from bhc.xlit_model import load_int8; return load_int8(path) | |
| if path.endswith(".pt"): | |
| ck = torch.load(path, weights_only=False, map_location="cpu"); m = XlitTransformer(**ck["cfg"]); m.load_state_dict(ck["model"]); m.eval(); return m | |
| return load_q(path) | |