| |
| """Converged activation-aware AQLM for the cold experts of GLM-5.2. |
| |
| Per (layer, projection), over the layer's cold experts: |
| objective: sum_e || (W_e - What_e) diag(sqrt(h_e)) ||_F^2 |
| with h_e = per-expert diagonal input Hessian E[x^2] from real |
| routed calibration activations (w13: layer input x; w2: the |
| expert's own silu(gate)*up computed with TEACHER weights) |
| variables: shared 65536x8 fp16 codebook (1 book, groups of 8 along the |
| input dim), int16 codes per group, per-expert per-out-channel |
| fp16 scales |
| algorithm: warm start from the existing shipped codes; iterate |
| [weighted coordinate-descent re-encode (batched GEMM argmax |
| under the h-weighted metric) -> closed-form weighted codebook |
| update -> closed-form weighted scale refit] until the relative |
| weighted error improvement < 0.2% or MAX_ITERS. |
| |
| Teacher weights: original NVFP4 per-expert tensors, dequantized to fp32 |
| (from /tmp/glm52-hot-dl2 ranged downloads; layers 3,4,5,8,74-77 from |
| /data/glm52-old-layerwise which stores all 256 experts). |
| |
| Output: /data/glm52-aqlm-conv/layer_N.pt with |
| expert_ids int32 [nC] (ascending), w13_codes int16 [nC,1,4096,768], |
| w13_codebooks fp16 [1,65536,8], w13_scales fp16 [nC,4096], w2c_* likewise |
| ([nC,1,6144,256] codes), plus before/after weighted rel-err metrics. |
| |
| Usage: aqlm_converge.py [--layers a,b,...] [--gpus 0,..7] |
| """ |
|
|
| import argparse |
| import json |
| import os |
| import time |
| from concurrent.futures import ProcessPoolExecutor |
|
|
| ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| CKPT = "/data/glm52" |
| OLD_LAYERWISE = "/data/glm52-old-layerwise" |
| DL = "/tmp/glm52-hot-dl2" |
| ACTS = "/data/glm52-acts" |
| OUT = "/data/glm52-aqlm-conv" |
| LOCAL_TEACHER_LAYERS = {3, 4, 5, 8, 74, 75, 76, 77} |
|
|
| ENTRIES = 65536 |
| G = 8 |
| MAX_ITERS = 6 |
| TOL = 2e-3 |
| CHUNK = 16384 |
| FP4_LUT = [0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, |
| -0.0, -0.5, -1.0, -1.5, -2.0, -3.0, -4.0, -6.0] |
|
|
|
|
| |
| class TeacherReader: |
| """Original NVFP4 per-expert tensors from download regions or the |
| old layer-wise checkpoint.""" |
|
|
| DT = None |
|
|
| def __init__(self): |
| import torch |
|
|
| TeacherReader.DT = {"U8": torch.uint8, "F8_E4M3": torch.uint8, |
| "BF16": torch.bfloat16, "F32": torch.float32} |
| self.headers = json.load(open(f"{DL}/headers.json")) |
| self.wm = {} |
| for shard, h in self.headers.items(): |
| for name in h["header"]: |
| if name != "__metadata__": |
| self.wm[name] = shard |
| self.regions = {} |
| for shard in self.headers: |
| d = f"{DL}/regions/{shard}" |
| regs = [] |
| if os.path.isdir(d): |
| for f in os.listdir(d): |
| p = os.path.join(d, f) |
| regs.append((int(f[:-4]), p, os.path.getsize(p))) |
| self.regions[shard] = sorted(regs) |
| idx = json.load( |
| open(f"{OLD_LAYERWISE}/model.safetensors.index.json")) |
| self.old_wm = idx["weight_map"] |
| self._old_open = {} |
|
|
| def _from_regions(self, name): |
| import torch |
|
|
| shard = self.wm[name] |
| info = self.headers[shard]["header"][name] |
| b, e = info["data_offsets"] |
| for rb, path, sz in self.regions[shard]: |
| if rb <= b and e <= rb + sz: |
| with open(path, "rb") as fh: |
| fh.seek(b - rb) |
| buf = fh.read(e - b) |
| return torch.frombuffer( |
| bytearray(buf), dtype=self.DT[info["dtype"]] |
| ).reshape(info["shape"]) |
| raise KeyError(name) |
|
|
| def _from_old(self, name): |
| from safetensors import safe_open |
|
|
| shard = self.old_wm[name] |
| if shard not in self._old_open: |
| self._old_open[shard] = safe_open( |
| f"{OLD_LAYERWISE}/{shard}", framework="pt") |
| return self._old_open[shard].get_tensor(name) |
|
|
| def get(self, li, e, proj, part): |
| name = f"model.layers.{li}.mlp.experts.{e}.{proj}.{part}" |
| if li in LOCAL_TEACHER_LAYERS: |
| return self._from_old(name) |
| return self._from_regions(name) |
|
|
|
|
| def dequant_teacher(tr, li, e, proj, device, lut): |
| import torch |
|
|
| packed = tr.get(li, e, proj, "weight").to(device) |
| ws = tr.get(li, e, proj, "weight_scale").view(torch.uint8).to(device) |
| ws2 = tr.get(li, e, proj, "weight_scale_2").to(device).float() |
| lo = (packed & 0x0F).long() |
| hi = (packed >> 4).long() |
| vals = torch.empty(packed.shape[0], packed.shape[1] * 2, |
| dtype=torch.float32, device=device) |
| vals[:, 0::2] = lut[lo] |
| vals[:, 1::2] = lut[hi] |
| scale = ws.view(torch.float8_e4m3fn).float().repeat_interleave(16, dim=1) |
| return vals * scale * ws2 |
|
|
|
|
| |
| def weighted_encode(vecs, hw, cent, out_codes): |
| """CD re-encode: for each group i pick argmin_c sum_j hw[i,j]*(v-c)^2. |
| vecs,hw: [N,8] fp32 (hw = per-group diag weights). cent: [K,8] fp32.""" |
| import torch |
|
|
| for s in range(0, vecs.shape[0], CHUNK): |
| v = vecs[s:s + CHUNK] |
| w = hw[s:s + CHUNK] |
| |
| a = w @ (cent.t() ** 2) |
| b = (v * w) @ cent.t() |
| out_codes[s:s + CHUNK] = (a - 2 * b).argmin(-1).to(torch.int32) |
| return out_codes |
|
|
|
|
| def codebook_update(vecs, hw, codes, K): |
| """Closed-form weighted LS per entry per dim: |
| c_kj = sum_{i in k} w_ij v_ij / sum w_ij.""" |
| import torch |
|
|
| num = torch.zeros(K, G, device=vecs.device) |
| den = torch.zeros(K, G, device=vecs.device) |
| idx = codes.long() |
| num.index_add_(0, idx, vecs * hw) |
| den.index_add_(0, idx, hw) |
| cent = num / den.clamp_min(1e-12) |
| dead = den.sum(-1) < 1e-12 |
| return cent, dead |
|
|
|
|
| def fit_projection(w, h, codes0, cb0, scales0, gen, log, tag): |
| """w [E,M,K] fp16 teacher; h [E,K] diag Hessian; warm-start codes/cb/scales. |
| Expert-chunked to bound peak memory. Returns codes int16 [E,1,M,K/8], |
| cb fp16 [1,65536,8], scales fp16 [E,M].""" |
| import torch |
|
|
| E, M, K = w.shape |
| NG = K // G |
| dev = w.device |
| EC = max(1, min(48, E)) |
| scales = scales0.float().clamp_min(1e-8) |
| cent = cb0[0].float().clone() |
| codes = (codes0.reshape(E, M, NG).to(torch.int32) & 0xFFFF).clone() |
|
|
| def chunk_tgt(e0, e1): |
| tgt = (w[e0:e1].float() / scales[e0:e1].unsqueeze(-1)) |
| hh = (h[e0:e1].reshape(e1 - e0, 1, NG, G) |
| .expand(e1 - e0, M, NG, G)) |
| return tgt.reshape(e1 - e0, M, NG, G), hh |
|
|
| def weighted_err(): |
| num = torch.zeros((), device=dev, dtype=torch.float64) |
| den = torch.zeros((), device=dev, dtype=torch.float64) |
| for e0 in range(0, E, 16): |
| e1 = min(e0 + 16, E) |
| tgt, hh = chunk_tgt(e0, e1) |
| dec = cent[codes[e0:e1].long().reshape(-1)].reshape( |
| e1 - e0, M, NG, G) |
| num += ((tgt - dec).pow(2) * hh).sum().double() |
| den += (tgt.pow(2) * hh).sum().double() |
| del tgt, hh, dec |
| return (num / den.clamp_min(1e-12)).item() |
|
|
| e0_err = weighted_err() |
| prev = e0_err |
| for it in range(MAX_ITERS): |
| num_cb = torch.zeros(ENTRIES, G, device=dev) |
| den_cb = torch.zeros(ENTRIES, G, device=dev) |
| for e0 in range(0, E, EC): |
| e1 = min(e0 + EC, E) |
| tgt, hh = chunk_tgt(e0, e1) |
| v = tgt.reshape(-1, G) |
| hwv = hh.reshape(-1, G) |
| cflat = codes[e0:e1].reshape(-1).clone() |
| weighted_encode(v, hwv, cent, cflat) |
| codes[e0:e1] = cflat.reshape(e1 - e0, M, NG) |
| idx = cflat.long() |
| num_cb.index_add_(0, idx, v * hwv) |
| den_cb.index_add_(0, idx, hwv) |
| del tgt, hh, v, hwv, cflat |
| torch.cuda.empty_cache() |
| newc = num_cb / den_cb.clamp_min(1e-12) |
| dead = den_cb.sum(-1) < 1e-12 |
| newc[dead] = cent[dead] |
| cent = newc |
| |
| for e0 in range(0, E, EC): |
| e1 = min(e0 + EC, E) |
| dec = cent[codes[e0:e1].long().reshape(-1)].reshape( |
| e1 - e0, M, K) |
| wf = w[e0:e1].float() |
| hh = h[e0:e1].unsqueeze(1) |
| num = (wf * hh * dec).sum(-1) |
| den = ((dec * dec) * hh).sum(-1).clamp_min(1e-10) |
| scales[e0:e1] = (num / den).clamp(1e-6, None) |
| del dec, wf, hh |
| torch.cuda.empty_cache() |
| cur = weighted_err() |
| log(f" {tag} iter{it}: weighted rel-err {cur:.5f}") |
| if prev - cur < TOL * prev: |
| break |
| prev = cur |
|
|
| codes16 = torch.where(codes >= 32768, codes - 65536, codes).to(torch.int16) |
| return (codes16.reshape(E, 1, M, NG).cpu(), |
| cent.half().unsqueeze(0).cpu(), |
| scales.half().cpu(), e0_err, prev) |
|
|
|
|
| def process_layer(li, gpu): |
| import torch |
|
|
| t0 = time.time() |
| dev = f"cuda:{gpu}" |
| torch.cuda.set_device(gpu) |
| gen = torch.Generator(device=dev) |
| gen.manual_seed(777 + li) |
| lut = torch.tensor(FP4_LUT, dtype=torch.float32, device=dev) |
|
|
| def log(m): |
| print(f"[L{li} gpu{gpu}] {m}", flush=True) |
|
|
| outp = os.path.join(OUT, f"layer_{li}.pt") |
| if os.path.exists(outp): |
| log("done, skip") |
| return li, None |
|
|
| |
| from safetensors import safe_open |
|
|
| idx = json.load(open(f"{CKPT}/model.safetensors.index.json")) |
| wm = idx["weight_map"] |
| p = f"model.layers.{li}.mlp.experts" |
| opened = {} |
|
|
| def ck(name): |
| shard = wm[f"{p}.{name}"] |
| if shard not in opened: |
| opened[shard] = safe_open(f"{CKPT}/{shard}", framework="pt") |
| return opened[shard].get_tensor(f"{p}.{name}") |
|
|
| kind = ck("hyb_kind") |
| cold = (kind == 2).nonzero().flatten().tolist() |
| nC = len(cold) |
|
|
| |
| acts = torch.load(f"{ACTS}/acts_layer{li}.pt", map_location="cpu", |
| weights_only=True) |
| x = acts["x"].to(dev).float() |
| tk_ids = acts["topk_ids"].to(dev).long() |
| h13 = torch.zeros(nC, 6144, device=dev) |
| h2 = torch.zeros(nC, 2048, device=dev) |
| pos = {e: j for j, e in enumerate(cold)} |
|
|
| tr = TeacherReader() |
| log(f"cold={nC}; dequant teachers + Hessians") |
| w13 = torch.empty(nC, 4096, 6144, dtype=torch.float16, device=dev) |
| w2 = torch.empty(nC, 6144, 2048, dtype=torch.float16, device=dev) |
| hits = (tk_ids.unsqueeze(-1) == |
| torch.tensor(cold, device=dev).view(1, 1, -1)) |
| tok_of = hits.any(1) |
| for j, e in enumerate(cold): |
| gate = dequant_teacher(tr, li, e, "gate_proj", dev, lut) |
| up = dequant_teacher(tr, li, e, "up_proj", dev, lut) |
| down = dequant_teacher(tr, li, e, "down_proj", dev, lut) |
| w13[j, :2048] = gate.half() |
| w13[j, 2048:] = up.half() |
| w2[j] = down.half() |
| xe = x[tok_of[:, j]] |
| if xe.shape[0] < 16: |
| h13[j] = x.pow(2).mean(0) |
| mid = torch.nn.functional.silu(x[:512] @ gate.t()) * (x[:512] @ up.t()) |
| else: |
| h13[j] = xe.pow(2).mean(0) |
| mid = torch.nn.functional.silu(xe @ gate.t()) * (xe @ up.t()) |
| h2[j] = mid.float().pow(2).mean(0).clamp_min(1e-10) |
| h13[j] = h13[j].clamp_min(1e-10) |
| del hits, tok_of, x |
| torch.cuda.empty_cache() |
|
|
| |
| c13 = ck("w13_codes").to(dev).view(torch.int16) |
| cb13 = ck("w13_codebooks").to(dev) |
| s13 = ck("w13_scales").to(dev) |
| c2 = ck("w2c_codes").to(dev) |
| cb2 = ck("w2c_codebooks").to(dev) |
| s2 = ck("w2c_scales").to(dev) |
|
|
| log("fit w13") |
| r13 = fit_projection(w13, h13, c13.view(torch.uint16).int(), cb13.float(), |
| s13, gen, log, "w13") |
| del w13 |
| torch.cuda.empty_cache() |
| log("fit w2") |
| r2 = fit_projection(w2, h2, c2.view(torch.uint16).int(), cb2.float(), |
| s2, gen, log, "w2") |
| del w2 |
| torch.cuda.empty_cache() |
|
|
| torch.save({ |
| "layer": li, |
| "expert_ids": torch.tensor(cold, dtype=torch.int32), |
| "w13_codes": r13[0], "w13_codebooks": r13[1], "w13_scales": r13[2], |
| "w2c_codes": r2[0], "w2c_codebooks": r2[1], "w2c_scales": r2[2], |
| "w13_err_before": r13[3], "w13_err_after": r13[4], |
| "w2_err_before": r2[3], "w2_err_after": r2[4], |
| }, outp) |
| log(f"saved ({time.time()-t0:.0f}s) w13 {r13[3]:.4f}->{r13[4]:.4f} " |
| f"w2 {r2[3]:.4f}->{r2[4]:.4f}") |
| return li, (r13[3], r13[4], r2[3], r2[4]) |
|
|
|
|
| def main(): |
| ap = argparse.ArgumentParser() |
| ap.add_argument("--layers", default=None) |
| ap.add_argument("--gpus", default="0,1,2,3,4,5,6,7") |
| args = ap.parse_args() |
| os.makedirs(OUT, exist_ok=True) |
| layers = list(range(3, 78)) |
| if args.layers: |
| want = {int(x) for x in args.layers.split(",")} |
| layers = [li for li in layers if li in want] |
| gpus = [int(g) for g in args.gpus.split(",")] |
| jobs = [(li, gpus[i % len(gpus)]) for i, li in enumerate(layers)] |
| with ProcessPoolExecutor(max_workers=len(gpus)) as ex: |
| for f in [ex.submit(process_layer, *j) for j in jobs]: |
| li, m = f.result() |
| if m: |
| print(f"== L{li}: w13 {m[0]:.4f}->{m[1]:.4f} " |
| f"w2 {m[2]:.4f}->{m[3]:.4f}", flush=True) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|