Upload segmentation.py
Browse files- segmentation.py +106 -43
segmentation.py
CHANGED
|
@@ -269,65 +269,128 @@ def segment_stage(
|
|
| 269 |
) -> tuple[np.ndarray, np.ndarray, int]:
|
| 270 |
"""Run the label fixed-point loop.
|
| 271 |
|
| 272 |
-
For tensors with ``n_blocks <=
|
| 273 |
-
label vector directly. For larger tensors (``n_blocks >
|
| 274 |
-
constructs
|
| 275 |
-
order, Entries 1..2 = column RMS/abs profiles, Entries 3..
|
| 276 |
-
blocks refined by ``_segment_stage_dense``) and assigns each
|
| 277 |
-
|
| 278 |
"""
|
| 279 |
-
Xo = np.asarray(X, dtype=np.float64)
|
| 280 |
-
Wo = None if W is None else np.asarray(W, dtype=np.float64)
|
| 281 |
k = max(1, int(k))
|
| 282 |
-
n_blocks, B =
|
| 283 |
M = int(getattr(cfg, "segment_table_size", SEG_TABLE_SIZE))
|
| 284 |
if M <= 0 or n_blocks <= M or B != 256 or k > 16:
|
|
|
|
|
|
|
| 285 |
return _segment_stage_dense(Xo, Wo, cfg, proxy, k)
|
| 286 |
|
| 287 |
-
|
|
|
|
| 288 |
# Entry 0: natural order (monotone labels so make_order is identity [0..255])
|
| 289 |
-
table_labels[0] = _order_to_canonical_labels(np.arange(256, dtype=np.int64), k)
|
| 290 |
-
# Entry 1 & 2: column-wide RMS and abs profiles
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
table_labels[
|
|
|
|
|
|
|
| 295 |
|
| 296 |
# Entries 3..M-1: prototype blocks refined with the proxy fixed-point loop
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
|
|
|
|
|
|
| 302 |
|
| 303 |
-
table_orders = make_order(table_labels) # (M, 256)
|
| 304 |
|
| 305 |
-
#
|
| 306 |
P = np.zeros((256, M * 16), dtype=np.float32)
|
| 307 |
for m in range(M):
|
| 308 |
for g in range(16):
|
| 309 |
P[table_orders[m, 16 * g : 16 * g + 16], m * 16 + g] = 0.25
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
(np.abs(Xf) @ P) ** 2
|
| 313 |
-
).reshape(n_blocks, M, 16).sum(axis=2)
|
| 314 |
-
topK = 8
|
| 315 |
-
top_idx = np.argpartition(-scores, topK, axis=1)[:, :topK]
|
| 316 |
-
top_idx[:, 0] = 0 # always include identity (Entry 0) so no block ever regresses
|
| 317 |
-
|
| 318 |
-
best_sse = np.full(n_blocks, np.inf, dtype=np.float64)
|
| 319 |
best_m = np.zeros(n_blocks, dtype=np.int64)
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
|
| 332 |
return table_orders[best_m], table_labels[best_m], rounds
|
| 333 |
|
|
|
|
| 269 |
) -> tuple[np.ndarray, np.ndarray, int]:
|
| 270 |
"""Run the label fixed-point loop.
|
| 271 |
|
| 272 |
+
For tensors with ``n_blocks <= M`` (or ``k > 16``), refines a per-block
|
| 273 |
+
label vector directly. For larger tensors (``n_blocks > M, k <= 16``),
|
| 274 |
+
constructs an ``M``-entry generalized permutation lookup table (Entry 0 =
|
| 275 |
+
natural order, Entries 1..2 = column RMS/abs profiles, Entries 3..M-1 =
|
| 276 |
+
prototype blocks refined by ``_segment_stage_dense``) and assigns each
|
| 277 |
+
superblock its optimal table index in chunked float32 / CUDA batches.
|
| 278 |
"""
|
|
|
|
|
|
|
| 279 |
k = max(1, int(k))
|
| 280 |
+
n_blocks, B = X.shape
|
| 281 |
M = int(getattr(cfg, "segment_table_size", SEG_TABLE_SIZE))
|
| 282 |
if M <= 0 or n_blocks <= M or B != 256 or k > 16:
|
| 283 |
+
Xo = np.asarray(X, dtype=np.float64)
|
| 284 |
+
Wo = None if W is None else np.asarray(W, dtype=np.float64)
|
| 285 |
return _segment_stage_dense(Xo, Wo, cfg, proxy, k)
|
| 286 |
|
| 287 |
+
Xf = np.ascontiguousarray(X, dtype=np.float32)
|
| 288 |
+
table_labels = np.empty((M, 256), dtype=np.uint8)
|
| 289 |
# Entry 0: natural order (monotone labels so make_order is identity [0..255])
|
| 290 |
+
table_labels[0] = _order_to_canonical_labels(np.arange(256, dtype=np.int64), k).astype(np.uint8)
|
| 291 |
+
# Entry 1 & 2: column-wide RMS and abs profiles (sampled across up to 4096 blocks)
|
| 292 |
+
s_step = max(1, n_blocks // 4096)
|
| 293 |
+
X_sample = Xf[::s_step].astype(np.float64)
|
| 294 |
+
col_rms = np.sqrt(np.mean(X_sample * X_sample, axis=0))
|
| 295 |
+
table_labels[1] = _order_to_canonical_labels(np.argsort(col_rms, kind="stable"), k).astype(np.uint8)
|
| 296 |
+
col_abs = np.mean(np.abs(X_sample), axis=0)
|
| 297 |
+
table_labels[2] = _order_to_canonical_labels(np.argsort(col_abs, kind="stable"), k).astype(np.uint8)
|
| 298 |
|
| 299 |
# Entries 3..M-1: prototype blocks refined with the proxy fixed-point loop
|
| 300 |
+
n_proto = M - 3
|
| 301 |
+
proto_idx = np.linspace(0, n_blocks - 1, n_proto, dtype=np.int64)
|
| 302 |
+
Xp = Xf[proto_idx].astype(np.float64)
|
| 303 |
+
Wp = None if W is None else np.asarray(W[proto_idx], dtype=np.float64)
|
| 304 |
+
ord_p, _, rounds = _segment_stage_dense(Xp, Wp, cfg, proxy, k)
|
| 305 |
+
for i in range(n_proto):
|
| 306 |
+
table_labels[3 + i] = _order_to_canonical_labels(ord_p[i], k).astype(np.uint8)
|
| 307 |
|
| 308 |
+
table_orders = make_order(table_labels) # (M, 256) int64
|
| 309 |
|
| 310 |
+
# Build sub-block projection matrix P: (256, M * 16)
|
| 311 |
P = np.zeros((256, M * 16), dtype=np.float32)
|
| 312 |
for m in range(M):
|
| 313 |
for g in range(16):
|
| 314 |
P[table_orders[m, 16 * g : 16 * g + 16], m * 16 + g] = 0.25
|
| 315 |
+
|
| 316 |
+
topK = min(4, M)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
best_m = np.zeros(n_blocks, dtype=np.int64)
|
| 318 |
+
nmax_sub = 3.0 if str(getattr(cfg, "quant_format", "q2_k")) != "q3_k" else 7.0
|
| 319 |
+
|
| 320 |
+
use_torch_cuda = False
|
| 321 |
+
if getattr(cfg, "device", "auto") != "cpu":
|
| 322 |
+
try:
|
| 323 |
+
import torch
|
| 324 |
+
use_torch_cuda = torch.cuda.is_available()
|
| 325 |
+
except Exception:
|
| 326 |
+
use_torch_cuda = False
|
| 327 |
+
|
| 328 |
+
if use_torch_cuda:
|
| 329 |
+
import torch
|
| 330 |
+
dev_str = "cuda" if cfg.device == "auto" else cfg.device
|
| 331 |
+
dev = torch.device(dev_str)
|
| 332 |
+
P_t = torch.from_numpy(P).to(dev)
|
| 333 |
+
orders_t = torch.from_numpy(table_orders).to(dev)
|
| 334 |
+
chunk = 16384
|
| 335 |
+
with torch.no_grad():
|
| 336 |
+
for s in range(0, n_blocks, chunk):
|
| 337 |
+
e = min(s + chunk, n_blocks)
|
| 338 |
+
xb = torch.from_numpy(Xf[s:e]).to(dev)
|
| 339 |
+
sc = (xb @ P_t).square().view(-1, M, 16).sum(dim=2) + 0.5 * (
|
| 340 |
+
(xb.abs() @ P_t).square().view(-1, M, 16).sum(dim=2)
|
| 341 |
+
)
|
| 342 |
+
top_idx = torch.topk(sc, k=topK, dim=1).indices
|
| 343 |
+
top_idx[:, 0] = 0
|
| 344 |
+
ords = orders_t[top_idx]
|
| 345 |
+
u = torch.gather(xb.unsqueeze(1).expand(-1, topK, -1), 2, ords).view(
|
| 346 |
+
-1, topK, 16, 16
|
| 347 |
+
)
|
| 348 |
+
umin = torch.clamp_max(u.amin(dim=3, keepdim=True), 0.0)
|
| 349 |
+
umax = u.amax(dim=3, keepdim=True)
|
| 350 |
+
step = torch.clamp_min((umax - umin) * (1.0 / nmax_sub), 1.0e-30)
|
| 351 |
+
q = torch.clamp(torch.round((u - umin) / step), 0.0, nmax_sub)
|
| 352 |
+
err = u - (umin + step * q)
|
| 353 |
+
if W is not None:
|
| 354 |
+
wb = torch.from_numpy(np.ascontiguousarray(W[s:e], dtype=np.float32)).to(dev)
|
| 355 |
+
wg = torch.gather(wb.unsqueeze(1).expand(-1, topK, -1), 2, ords).view(
|
| 356 |
+
-1, topK, 16, 16
|
| 357 |
+
)
|
| 358 |
+
sse = (wg * err.square()).sum(dim=(2, 3))
|
| 359 |
+
else:
|
| 360 |
+
sse = err.square().sum(dim=(2, 3))
|
| 361 |
+
best_local = sse.argmin(dim=1)
|
| 362 |
+
best_m[s:e] = (
|
| 363 |
+
top_idx[torch.arange(e - s, device=dev), best_local].cpu().numpy()
|
| 364 |
+
)
|
| 365 |
+
else:
|
| 366 |
+
chunk = 8192
|
| 367 |
+
for s in range(0, n_blocks, chunk):
|
| 368 |
+
e = min(s + chunk, n_blocks)
|
| 369 |
+
xb = Xf[s:e]
|
| 370 |
+
sc = ((xb @ P) ** 2).reshape(e - s, M, 16).sum(axis=2) + 0.5 * (
|
| 371 |
+
(np.abs(xb) @ P) ** 2
|
| 372 |
+
).reshape(e - s, M, 16).sum(axis=2)
|
| 373 |
+
top_idx = np.argpartition(-sc, topK - 1, axis=1)[:, :topK]
|
| 374 |
+
top_idx[:, 0] = 0
|
| 375 |
+
ords = table_orders[top_idx]
|
| 376 |
+
u = np.take_along_axis(xb[:, None, :], ords, axis=2).reshape(
|
| 377 |
+
e - s, topK, 16, 16
|
| 378 |
+
)
|
| 379 |
+
umin = np.minimum(u.min(axis=3, keepdims=True), 0.0)
|
| 380 |
+
umax = u.max(axis=3, keepdims=True)
|
| 381 |
+
step = np.maximum((umax - umin) * (1.0 / nmax_sub), 1.0e-30)
|
| 382 |
+
q = np.clip(np.rint((u - umin) / step), 0.0, nmax_sub)
|
| 383 |
+
err = u - (umin + step * q)
|
| 384 |
+
if W is not None:
|
| 385 |
+
wb = np.ascontiguousarray(W[s:e], dtype=np.float32)
|
| 386 |
+
wg = np.take_along_axis(wb[:, None, :], ords, axis=2).reshape(
|
| 387 |
+
e - s, topK, 16, 16
|
| 388 |
+
)
|
| 389 |
+
sse = np.sum(wg * err * err, axis=(2, 3))
|
| 390 |
+
else:
|
| 391 |
+
sse = np.sum(err * err, axis=(2, 3))
|
| 392 |
+
best_local = np.argmin(sse, axis=1)
|
| 393 |
+
best_m[s:e] = top_idx[np.arange(e - s), best_local]
|
| 394 |
|
| 395 |
return table_orders[best_m], table_labels[best_m], rounds
|
| 396 |
|