eyad-silx commited on
Commit
c9e7d1f
路
verified 路
1 Parent(s): e653712

push artifacts/scripts/scale_refit.py

Browse files
Files changed (1) hide show
  1. artifacts/scripts/scale_refit.py +158 -0
artifacts/scripts/scale_refit.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Challenger v1: the king with every PQ2_0 block's fp16 scale refit by least squares against the
2
+ teacher's rotated weights (codes untouched). Works entirely in the stored (rotated) layout, where
3
+ stored = W_hf 路 signs 路 H per block for both folded weights and latent embeddings.
4
+
5
+ .venv/bin/python scale_refit.py [--out /root/models/chal-v1]
6
+ """
7
+ import argparse
8
+ import shutil
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ sys.path.insert(0, "/root/ingot/src")
13
+ import numpy as np
14
+
15
+ from common.gguf import read_header
16
+ from common.gguf.dequant import hadamard, rotation
17
+ from common.gguf.types import PQ2_0, TYPES
18
+ from model_dedup.gguf_checkpoint import GgufCheckpoint, Heads
19
+
20
+ ap = argparse.ArgumentParser()
21
+ ap.add_argument("--king", default="/root/models/king-ix")
22
+ ap.add_argument("--teacher", default="/root/models/teacher")
23
+ ap.add_argument("--out", default="/root/models/chal-v1")
24
+ ap.add_argument("--max-tensors", type=int, default=0, help="limit for testing")
25
+ args = ap.parse_args()
26
+
27
+ ck = GgufCheckpoint(args.king, "cpu")
28
+ g = ck.g
29
+ heads = ck._heads
30
+ block_size = int(g.kv["prism.hadamard.block_size"].value)
31
+ H = hadamard(block_size)
32
+ print("block_size", block_size, "heads", heads)
33
+
34
+ teacher_shards = {}
35
+ import json
36
+
37
+ idx = json.loads((Path(args.teacher) / "model.safetensors.index.json").read_text())
38
+ weight_map = idx["weight_map"]
39
+
40
+ def teacher_tensor(hf_name: str) -> np.ndarray:
41
+ raw_name = (
42
+ hf_name
43
+ if hf_name == "lm_head.weight"
44
+ else "model.language_model." + hf_name[len("model.") :]
45
+ )
46
+ shard = weight_map[raw_name]
47
+ if shard not in teacher_shards:
48
+ import safetensors
49
+
50
+ teacher_shards[shard] = safetensors.safe_open(str(Path(args.teacher) / shard), "pt")
51
+ t = teacher_shards[shard].get_tensor(raw_name)
52
+ return t.to(dtype=torch.float32).numpy()
53
+
54
+ import torch # noqa: E402
55
+
56
+
57
+ def tile(w: np.ndarray, dim: int, head: int) -> np.ndarray:
58
+ """Inverse of GgufCheckpoint._untile: HF grouped [n_k, rep, head] -> stored tiled [rep, n_k, head]."""
59
+ shape = list(w.shape)
60
+ x = w.reshape(*shape[:dim], heads.n_k, heads.rep, head, *shape[dim + 1 :])
61
+ return x.transpose(dim, dim + 1).reshape(shape)
62
+
63
+
64
+ def hf_to_stored(name: str, w: np.ndarray) -> np.ndarray:
65
+ """Teacher tensor, HF layout -> the king's stored layout (row order, then rotation)."""
66
+ if name.startswith("layers.") and heads.rep > 1:
67
+ kind = name.split(".")[2]
68
+ if kind == "in_proj_qkv" and name.endswith("in_proj_qkv.weight"):
69
+ qk = 2 * heads.n_k * heads.k_dim
70
+ w = np.concatenate([w[:qk], tile(w[qk:], 0, heads.v_dim)])
71
+ elif kind == "in_proj_z":
72
+ w = tile(w, 0, heads.v_dim)
73
+ elif kind in ("in_proj_a", "in_proj_b"):
74
+ w = tile(w, 0, 1)
75
+ elif kind == "out_proj" and not (rot_signs(name) is not None and ck._grouped):
76
+ w = tile(w.T, 0, heads.v_dim).T
77
+ signs = rot_signs(name)
78
+ if signs is None:
79
+ return w
80
+ rows, width = w.shape
81
+ nblk = width // block_size
82
+ # stored = (W 路 signs) 路 H per col block, as get()'s inverse: W = (stored 路 H) 路 signs
83
+ w = (w.reshape(rows, nblk, block_size) * signs.reshape(nblk, block_size)) @ H
84
+ return w.reshape(rows, width)
85
+
86
+
87
+ def rot_signs(name: str) -> np.ndarray | None:
88
+ kv = g.kv
89
+ listed = [
90
+ kv["prism.hadamard." + key].value
91
+ for key in ("weight_names", "inverse_weight_names")
92
+ if "prism.hadamard." + key in kv
93
+ ]
94
+ if not any(name in names for names in listed):
95
+ return None
96
+ widths = kv["prism.hadamard.sign_widths"].value
97
+ values = kv["prism.hadamard.sign_values"].value
98
+ width = g.tensors[name].shape[0]
99
+ at = 0
100
+ for w_ in widths:
101
+ if w_ == width:
102
+ return np.asarray(values[at : at + w_], np.float32)
103
+ at += w_
104
+ raise ValueError(f"no sign vector of width {width} for {name}")
105
+
106
+
107
+ # the gguf tensor name each HF name maps to (dedup canonical, with the .weight suffix)
108
+ from model_dedup.gguf_checkpoint import canonical as gguf_canonical # noqa: E402
109
+
110
+ out_dir = Path(args.out)
111
+ out_dir.mkdir(parents=True, exist_ok=True)
112
+ src = Path(args.king) / "Ingot-IX-27B-Q2.gguf"
113
+ dst = out_dir / "chal-v1.gguf"
114
+ shutil.copy(src, dst)
115
+
116
+ done = 0
117
+ with open(dst, "r+b") as fh:
118
+ for name, t in g.tensors.items():
119
+ if t.ggml_type != PQ2_0:
120
+ continue
121
+ if args.max_tensors and done >= args.max_tensors:
122
+ break
123
+ canon = gguf_canonical(name)
124
+ if canon is None:
125
+ print("skip (no hf name):", name)
126
+ continue
127
+ hf_name = "model." + canon if not canon.startswith(("lm_head", "norm.")) else canon
128
+ # canonical gives e.g. layers.0.self_attn.q_proj.weight; HF: model.layers.0...
129
+ w_t = teacher_tensor(hf_name)
130
+ w_t = hf_to_stored(name, w_t)
131
+ rows, width = w_t.shape
132
+ nblk = width // 128 # the PQ2_0 block is 128 wide; the Hadamard block is not
133
+ # read raw blocks
134
+ size = rows * nblk * 34
135
+ raw = np.memmap(dst, np.uint8, "r+", g.data_start + t.offset, (size,))
136
+ blocks = raw.reshape(-1, 34)
137
+ qs = blocks[:, 2:]
138
+ codes = np.stack([(qs >> (2 * k)) & 3 for k in range(4)], axis=-1).reshape(rows, nblk, 128)
139
+ if (codes == 3).any():
140
+ raise SystemExit(f"{name}: a code 3 in the king")
141
+ c_vals = codes.astype(np.float32) - 1.0
142
+ w_t_b = w_t.reshape(rows, nblk, 128)
143
+ d_orig = blocks[:, :2].copy().view("<f2").astype(np.float32).reshape(rows, nblk)
144
+ support = (c_vals * c_vals).sum(-1)
145
+ num = (c_vals * w_t_b).sum(-1)
146
+ # a block of mostly-zero codes has no stable least-squares scale: ridge + a support floor,
147
+ # and never more than double the king's own scale (its codes were fit with it)
148
+ d_new = num / (support + 4.0)
149
+ d_new = np.where(support >= 8, d_new, d_orig)
150
+ d_new = np.clip(d_new, -2.0 * np.abs(d_orig), 2.0 * np.abs(d_orig))
151
+ d_new = np.clip(d_new, -5000.0, 5000.0)
152
+ d_new = d_new.astype(np.float16)
153
+ blocks[:, :2] = d_new.astype("<f2").view(np.uint8).reshape(-1, 2)
154
+ done += 1
155
+ if done % 50 == 0:
156
+ print(f"refit {done} tensors ({name})", flush=True)
157
+
158
+ print(f"refit {done} tensors -> {dst}")