Upload 4 files
Browse files- gguf.py +2 -1
- patch_llamacpp_hpcq_seg.py +20 -2
- segmentation.py +151 -33
gguf.py
CHANGED
|
@@ -1057,7 +1057,8 @@ def write_quantized_gguf(
|
|
| 1057 |
t_q3_start = time.time()
|
| 1058 |
q3_labels: list[np.ndarray] = []
|
| 1059 |
|
| 1060 |
-
|
|
|
|
| 1061 |
if stop_event is not None and stop_event.is_set():
|
| 1062 |
raise InterruptedError("Quantization cancelled by user")
|
| 1063 |
imp_chunk = expand_importance(raw_imp, x_chunk.size, row) if has_imat else None
|
|
|
|
| 1057 |
t_q3_start = time.time()
|
| 1058 |
q3_labels: list[np.ndarray] = []
|
| 1059 |
|
| 1060 |
+
q3_max_elems = ti.n_elements if seg_active else 8_388_608
|
| 1061 |
+
for x_chunk, row, _r0, _r1 in reader.iter_tensor_row_chunks(ti.name, max_elements=q3_max_elems):
|
| 1062 |
if stop_event is not None and stop_event.is_set():
|
| 1063 |
raise InterruptedError("Quantization cancelled by user")
|
| 1064 |
imp_chunk = expand_importance(raw_imp, x_chunk.size, row) if has_imat else None
|
patch_llamacpp_hpcq_seg.py
CHANGED
|
@@ -78,11 +78,17 @@ struct hpcq_seg_entry {
|
|
| 78 |
uintptr_t end; // payload end (exclusive)
|
| 79 |
int64_t block_bytes; // 84 for Q2_K, 110 for Q3_K
|
| 80 |
int64_t n_blocks; // number of 256-weight superblocks
|
| 81 |
-
uint8_t enc; // 0 = 4-bit nibbles
|
| 82 |
-
uint8_t * packed; // compact packed label stream
|
| 83 |
void * dev_packed; // lazily allocated device copy for CUDA (or NULL)
|
| 84 |
};
|
| 85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
GGML_API void ggml_hpcq_seg_bind_raw(
|
| 87 |
const void * payload, int64_t n_bytes,
|
| 88 |
const uint8_t * hqs1, size_t hqs1_len, int64_t block_bytes);
|
|
@@ -131,6 +137,18 @@ static inline HPCQ_HD void hpcq_seg_make_order_block(
|
|
| 131 |
}
|
| 132 |
}
|
| 133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
#ifdef __cplusplus
|
| 135 |
}
|
| 136 |
#endif
|
|
|
|
| 78 |
uintptr_t end; // payload end (exclusive)
|
| 79 |
int64_t block_bytes; // 84 for Q2_K, 110 for Q3_K
|
| 80 |
int64_t n_blocks; // number of 256-weight superblocks
|
| 81 |
+
uint8_t enc; // 0 = 4-bit nibbles, 1 = 8-bit, 2 = 256-entry static table + 1B/blk
|
| 82 |
+
uint8_t * packed; // compact packed label stream
|
| 83 |
void * dev_packed; // lazily allocated device copy for CUDA (or NULL)
|
| 84 |
};
|
| 85 |
|
| 86 |
+
static inline HPCQ_HD size_t hpcq_seg_packed_bytes(int64_t n_blocks, uint8_t enc) {
|
| 87 |
+
if (enc == 2) return 32768u + (size_t) n_blocks;
|
| 88 |
+
if (enc == 0) return (size_t) n_blocks * 128u;
|
| 89 |
+
return (size_t) n_blocks * 256u;
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
GGML_API void ggml_hpcq_seg_bind_raw(
|
| 93 |
const void * payload, int64_t n_bytes,
|
| 94 |
const uint8_t * hqs1, size_t hqs1_len, int64_t block_bytes);
|
|
|
|
| 137 |
}
|
| 138 |
}
|
| 139 |
|
| 140 |
+
static inline HPCQ_HD void hpcq_seg_make_order_at(
|
| 141 |
+
const uint8_t * packed_base, uint8_t enc, int64_t blk_idx, uint16_t * order) {
|
| 142 |
+
if (enc == 2) {
|
| 143 |
+
const uint8_t m = packed_base[32768u + (size_t) blk_idx];
|
| 144 |
+
hpcq_seg_make_order_block(packed_base + (size_t) m * 128u, 0, order);
|
| 145 |
+
} else if (enc == 0) {
|
| 146 |
+
hpcq_seg_make_order_block(packed_base + (size_t) blk_idx * 128u, 0, order);
|
| 147 |
+
} else {
|
| 148 |
+
hpcq_seg_make_order_block(packed_base + (size_t) blk_idx * 256u, 1, order);
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
#ifdef __cplusplus
|
| 153 |
}
|
| 154 |
#endif
|
segmentation.py
CHANGED
|
@@ -113,17 +113,40 @@ def scatter_to_original(xh_slots: np.ndarray, order: np.ndarray) -> np.ndarray:
|
|
| 113 |
# ---- label channel codec (in-format persistence inside a GGUF KV) --------
|
| 114 |
#
|
| 115 |
# Layout: magic "HQS1" | uint8 k | uint8 enc | uint32 n_labels (little-endian)
|
| 116 |
-
# | payload.
|
| 117 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
|
| 119 |
import struct as _struct
|
| 120 |
|
| 121 |
SEG_MAGIC = b"HQS1"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
|
| 123 |
|
| 124 |
def label_channel_bytes(n: int, k: int) -> int:
|
| 125 |
"""Exact content size of the packed label channel."""
|
| 126 |
if int(k) <= 16:
|
|
|
|
|
|
|
| 127 |
return 10 + (n + 1) // 2
|
| 128 |
return 10 + n
|
| 129 |
|
|
@@ -135,12 +158,33 @@ def pack_label_channel(labels: np.ndarray, k: int) -> bytes:
|
|
| 135 |
if k <= 16:
|
| 136 |
if flat.size and int(flat.max()) > 15:
|
| 137 |
raise ValueError(f"label {int(flat.max())} does not fit nibble packing (k={k})")
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
else:
|
| 145 |
enc = 1
|
| 146 |
if flat.size and (int(flat.max()) > 255 or int(flat.min()) < 0):
|
|
@@ -157,15 +201,53 @@ def unpack_label_channel(content: bytes) -> tuple[int, np.ndarray]:
|
|
| 157 |
n = _struct.unpack("<I", content[6:10])[0]
|
| 158 |
data = content[10:]
|
| 159 |
if enc == 0:
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
else:
|
| 165 |
out = np.frombuffer(data, dtype=np.uint8).copy()
|
| 166 |
return k, out[:n]
|
| 167 |
|
| 168 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
def segment_stage(
|
| 170 |
X: np.ndarray,
|
| 171 |
W: np.ndarray | None,
|
|
@@ -175,31 +257,67 @@ def segment_stage(
|
|
| 175 |
) -> tuple[np.ndarray, np.ndarray, int]:
|
| 176 |
"""Run the label fixed-point loop.
|
| 177 |
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
re-derivation would see.
|
| 185 |
"""
|
| 186 |
Xo = np.asarray(X, dtype=np.float64)
|
| 187 |
Wo = None if W is None else np.asarray(W, dtype=np.float64)
|
| 188 |
k = max(1, int(k))
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
|
| 204 |
|
| 205 |
def q2k_proxy_cfg(clamp_val: float, cfg: HPCConfig):
|
|
|
|
| 113 |
# ---- label channel codec (in-format persistence inside a GGUF KV) --------
|
| 114 |
#
|
| 115 |
# Layout: magic "HQS1" | uint8 k | uint8 enc | uint32 n_labels (little-endian)
|
| 116 |
+
# | payload.
|
| 117 |
+
# enc=0: packs labels 0..15 as nibbles (2 labels per byte; used for n <= 65536)
|
| 118 |
+
# enc=1: stores raw uint8 labels (k > 16 only)
|
| 119 |
+
# enc=2: generalized 256-entry permutation table (32 KB nibble table + 1 byte
|
| 120 |
+
# index per 256-weight superblock = +0.03125 bpw; used for n > 65536,
|
| 121 |
+
# k <= 16, n % 256 == 0).
|
| 122 |
|
| 123 |
import struct as _struct
|
| 124 |
|
| 125 |
SEG_MAGIC = b"HQS1"
|
| 126 |
+
SEG_TABLE_SIZE = 256
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def _pack_nibbles_1d(flat: np.ndarray) -> bytes:
|
| 130 |
+
lo = flat[0::2].astype(np.uint8)
|
| 131 |
+
hi = flat[1::2].astype(np.uint8)
|
| 132 |
+
if hi.size < lo.size:
|
| 133 |
+
hi = np.concatenate([hi, np.zeros(1, dtype=np.uint8)])
|
| 134 |
+
return (lo | (hi << 4)).astype(np.uint8).tobytes()
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def _unpack_nibbles_1d(data: bytes, n: int) -> np.ndarray:
|
| 138 |
+
raw = np.frombuffer(data, dtype=np.uint8)
|
| 139 |
+
out = np.empty(raw.size * 2, dtype=np.uint8)
|
| 140 |
+
out[0::2] = raw & np.uint8(0x0F)
|
| 141 |
+
out[1::2] = raw >> np.uint8(4)
|
| 142 |
+
return out[:n]
|
| 143 |
|
| 144 |
|
| 145 |
def label_channel_bytes(n: int, k: int) -> int:
|
| 146 |
"""Exact content size of the packed label channel."""
|
| 147 |
if int(k) <= 16:
|
| 148 |
+
if n > SEG_TABLE_SIZE * 256 and n % 256 == 0:
|
| 149 |
+
return 10 + SEG_TABLE_SIZE * 128 + (n // 256)
|
| 150 |
return 10 + (n + 1) // 2
|
| 151 |
return 10 + n
|
| 152 |
|
|
|
|
| 158 |
if k <= 16:
|
| 159 |
if flat.size and int(flat.max()) > 15:
|
| 160 |
raise ValueError(f"label {int(flat.max())} does not fit nibble packing (k={k})")
|
| 161 |
+
if n > SEG_TABLE_SIZE * 256 and n % 256 == 0:
|
| 162 |
+
enc = 2
|
| 163 |
+
n_blocks = n // 256
|
| 164 |
+
rows = flat.reshape(n_blocks, 256).astype(np.uint8)
|
| 165 |
+
lo = rows[:, 0::2]
|
| 166 |
+
hi = rows[:, 1::2]
|
| 167 |
+
packed_rows = (lo | (hi << 4)).astype(np.uint8) # (n_blocks, 128)
|
| 168 |
+
row_dt = np.dtype((np.void, 128))
|
| 169 |
+
row_views = np.ascontiguousarray(packed_rows).view(row_dt).ravel()
|
| 170 |
+
uniq_views, inv_idx, counts = np.unique(
|
| 171 |
+
row_views, return_inverse=True, return_counts=True
|
| 172 |
+
)
|
| 173 |
+
uniq_rows = uniq_views.view(np.uint8).reshape(-1, 128)
|
| 174 |
+
table = np.zeros((SEG_TABLE_SIZE, 128), dtype=np.uint8)
|
| 175 |
+
if uniq_rows.shape[0] <= SEG_TABLE_SIZE:
|
| 176 |
+
table[: uniq_rows.shape[0]] = uniq_rows
|
| 177 |
+
block_idx = inv_idx.astype(np.uint8)
|
| 178 |
+
else:
|
| 179 |
+
top_u = np.argsort(-counts)[:SEG_TABLE_SIZE]
|
| 180 |
+
table[:] = uniq_rows[top_u]
|
| 181 |
+
remap = np.zeros(uniq_rows.shape[0], dtype=np.uint8)
|
| 182 |
+
remap[top_u] = np.arange(SEG_TABLE_SIZE, dtype=np.uint8)
|
| 183 |
+
block_idx = remap[inv_idx]
|
| 184 |
+
data = table.tobytes() + block_idx.tobytes()
|
| 185 |
+
else:
|
| 186 |
+
enc = 0
|
| 187 |
+
data = _pack_nibbles_1d(flat)
|
| 188 |
else:
|
| 189 |
enc = 1
|
| 190 |
if flat.size and (int(flat.max()) > 255 or int(flat.min()) < 0):
|
|
|
|
| 201 |
n = _struct.unpack("<I", content[6:10])[0]
|
| 202 |
data = content[10:]
|
| 203 |
if enc == 0:
|
| 204 |
+
out = _unpack_nibbles_1d(data, n)
|
| 205 |
+
elif enc == 2:
|
| 206 |
+
n_blocks = n // 256
|
| 207 |
+
tbytes = SEG_TABLE_SIZE * 128
|
| 208 |
+
table = _unpack_nibbles_1d(data[:tbytes], SEG_TABLE_SIZE * 256).reshape(
|
| 209 |
+
SEG_TABLE_SIZE, 256
|
| 210 |
+
)
|
| 211 |
+
idx = np.frombuffer(data[tbytes : tbytes + n_blocks], dtype=np.uint8)
|
| 212 |
+
out = table[idx].reshape(-1)
|
| 213 |
else:
|
| 214 |
out = np.frombuffer(data, dtype=np.uint8).copy()
|
| 215 |
return k, out[:n]
|
| 216 |
|
| 217 |
|
| 218 |
+
def _order_to_canonical_labels(ord_row: np.ndarray, k: int = 16) -> np.ndarray:
|
| 219 |
+
lab = np.empty(256, dtype=np.int64)
|
| 220 |
+
step = 256 // k
|
| 221 |
+
for g in range(k):
|
| 222 |
+
s0 = g * step
|
| 223 |
+
s1 = 256 if g == k - 1 else (g + 1) * step
|
| 224 |
+
sub = np.sort(ord_row[s0:s1])
|
| 225 |
+
lab[sub] = g
|
| 226 |
+
return lab
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
def _segment_stage_dense(
|
| 230 |
+
Xo: np.ndarray,
|
| 231 |
+
Wo: np.ndarray | None,
|
| 232 |
+
cfg: HPCConfig,
|
| 233 |
+
proxy,
|
| 234 |
+
k: int,
|
| 235 |
+
) -> tuple[np.ndarray, np.ndarray, int]:
|
| 236 |
+
labels = band_labels(Xo, k)
|
| 237 |
+
order = make_order(labels)
|
| 238 |
+
rounds = 0
|
| 239 |
+
for _ in range(max(1, int(cfg.segment_iters))):
|
| 240 |
+
rounds += 1
|
| 241 |
+
xg = apply_order(Xo, order)
|
| 242 |
+
wgg = None if Wo is None else apply_order(Wo, order)
|
| 243 |
+
xh = proxy(xg, wgg)
|
| 244 |
+
labels_slot = band_labels(xh, k)
|
| 245 |
+
labels = np.empty_like(labels_slot)
|
| 246 |
+
np.put_along_axis(labels, order, labels_slot, axis=1)
|
| 247 |
+
order = make_order(labels)
|
| 248 |
+
return order, labels, rounds
|
| 249 |
+
|
| 250 |
+
|
| 251 |
def segment_stage(
|
| 252 |
X: np.ndarray,
|
| 253 |
W: np.ndarray | None,
|
|
|
|
| 257 |
) -> tuple[np.ndarray, np.ndarray, int]:
|
| 258 |
"""Run the label fixed-point loop.
|
| 259 |
|
| 260 |
+
For tensors with ``n_blocks <= 256`` (or ``k > 16``), refines a per-block
|
| 261 |
+
label vector directly. For larger tensors (``n_blocks > 256, k <= 16``),
|
| 262 |
+
constructs a 256-entry generalized permutation table (Entry 0 = natural
|
| 263 |
+
order, Entries 1..2 = column RMS/abs profiles, Entries 3..255 = prototype
|
| 264 |
+
blocks refined by ``_segment_stage_dense``) and assigns each superblock its
|
| 265 |
+
optimal 1-byte table index (+0.03125 bpw static+index storage tax).
|
|
|
|
| 266 |
"""
|
| 267 |
Xo = np.asarray(X, dtype=np.float64)
|
| 268 |
Wo = None if W is None else np.asarray(W, dtype=np.float64)
|
| 269 |
k = max(1, int(k))
|
| 270 |
+
n_blocks, B = Xo.shape
|
| 271 |
+
if n_blocks <= SEG_TABLE_SIZE or B != 256 or k > 16:
|
| 272 |
+
return _segment_stage_dense(Xo, Wo, cfg, proxy, k)
|
| 273 |
+
|
| 274 |
+
M = SEG_TABLE_SIZE
|
| 275 |
+
table_labels = np.empty((M, 256), dtype=np.int64)
|
| 276 |
+
# Entry 0: natural order (monotone labels so make_order is identity [0..255])
|
| 277 |
+
table_labels[0] = _order_to_canonical_labels(np.arange(256, dtype=np.int64), k)
|
| 278 |
+
# Entry 1 & 2: column-wide RMS and abs profiles
|
| 279 |
+
col_rms = np.sqrt(np.mean(Xo * Xo, axis=0))
|
| 280 |
+
table_labels[1] = _order_to_canonical_labels(np.argsort(col_rms, kind="stable"), k)
|
| 281 |
+
col_abs = np.mean(np.abs(Xo), axis=0)
|
| 282 |
+
table_labels[2] = _order_to_canonical_labels(np.argsort(col_abs, kind="stable"), k)
|
| 283 |
+
|
| 284 |
+
# Entries 3..M-1: prototype blocks refined with the proxy fixed-point loop
|
| 285 |
+
proto_idx = np.linspace(0, n_blocks - 1, M - 3, dtype=np.int64)
|
| 286 |
+
Wp = None if Wo is None else Wo[proto_idx]
|
| 287 |
+
ord_p, _, rounds = _segment_stage_dense(Xo[proto_idx], Wp, cfg, proxy, k)
|
| 288 |
+
for i in range(M - 3):
|
| 289 |
+
table_labels[3 + i] = _order_to_canonical_labels(ord_p[i], k)
|
| 290 |
+
|
| 291 |
+
table_orders = make_order(table_labels) # (M, 256)
|
| 292 |
+
|
| 293 |
+
# Prescreen top-8 table entries per block via between-subblock variance GEMM
|
| 294 |
+
P = np.zeros((256, M * 16), dtype=np.float32)
|
| 295 |
+
for m in range(M):
|
| 296 |
+
for g in range(16):
|
| 297 |
+
P[table_orders[m, 16 * g : 16 * g + 16], m * 16 + g] = 0.25
|
| 298 |
+
Xf = Xo.astype(np.float32)
|
| 299 |
+
scores = ((Xf @ P) ** 2).reshape(n_blocks, M, 16).sum(axis=2) + 0.5 * (
|
| 300 |
+
(np.abs(Xf) @ P) ** 2
|
| 301 |
+
).reshape(n_blocks, M, 16).sum(axis=2)
|
| 302 |
+
topK = 8
|
| 303 |
+
top_idx = np.argpartition(-scores, topK, axis=1)[:, :topK]
|
| 304 |
+
top_idx[:, 0] = 0 # always include identity (Entry 0) so no block ever regresses
|
| 305 |
+
|
| 306 |
+
best_sse = np.full(n_blocks, np.inf, dtype=np.float64)
|
| 307 |
+
best_m = np.zeros(n_blocks, dtype=np.int64)
|
| 308 |
+
for k_i in range(topK):
|
| 309 |
+
m_col = top_idx[:, k_i]
|
| 310 |
+
ords = table_orders[m_col]
|
| 311 |
+
Xk = apply_order(Xo, ords)
|
| 312 |
+
Wk = None if Wo is None else apply_order(Wo, ords)
|
| 313 |
+
xh = proxy(Xk, Wk)
|
| 314 |
+
err = Xk - xh
|
| 315 |
+
sse_k = np.sum(err * err if Wk is None else Wk * err * err, axis=1)
|
| 316 |
+
better = sse_k < best_sse
|
| 317 |
+
best_sse = np.where(better, sse_k, best_sse)
|
| 318 |
+
best_m = np.where(better, m_col, best_m)
|
| 319 |
+
|
| 320 |
+
return table_orders[best_m], table_labels[best_m], rounds
|
| 321 |
|
| 322 |
|
| 323 |
def q2k_proxy_cfg(clamp_val: float, cfg: HPCConfig):
|