Upload 141 files
Browse files- hugging/td_fuse/transport.py +44 -22
hugging/td_fuse/transport.py
CHANGED
|
@@ -394,6 +394,9 @@ def _compute_plans_fallback(
|
|
| 394 |
print("[transport] This may take 2-5 minutes...")
|
| 395 |
sys.stdout.flush()
|
| 396 |
|
|
|
|
|
|
|
|
|
|
| 397 |
for i, (sl, tl) in enumerate(zip(source_layers, target_layers)):
|
| 398 |
S = source_act[sl].numpy()
|
| 399 |
T = target_act[tl].numpy()
|
|
@@ -403,26 +406,45 @@ def _compute_plans_fallback(
|
|
| 403 |
# Neurons already correspond (e.g. DeepSeek) — identity Q
|
| 404 |
Q_matrices[(sl, tl)] = np.eye(S.shape[1]) / S.shape[1]
|
| 405 |
else:
|
| 406 |
-
#
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
#
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 426 |
else:
|
| 427 |
# Different dims -- do lightweight Sinkhorn on this pair only
|
| 428 |
print(f" Layer {i}: dim mismatch ({S.shape[1]} vs {T.shape[1]}), using Sinkhorn...")
|
|
@@ -438,8 +460,8 @@ def _compute_plans_fallback(
|
|
| 438 |
print(f" Matched layer {i + 1}/{n_source}: {sl} -> {tl}")
|
| 439 |
sys.stdout.flush()
|
| 440 |
|
| 441 |
-
# Timeout:
|
| 442 |
-
tracker.check_timeout(timeout_seconds=
|
| 443 |
|
| 444 |
if permutations:
|
| 445 |
print(f"[transport] Computed {len(permutations)} neuron permutations")
|
|
|
|
| 394 |
print("[transport] This may take 2-5 minutes...")
|
| 395 |
sys.stdout.flush()
|
| 396 |
|
| 397 |
+
# Track which block indices already have permutations (avoid computing twice)
|
| 398 |
+
block_perms = {} # block_index -> perm array
|
| 399 |
+
|
| 400 |
for i, (sl, tl) in enumerate(zip(source_layers, target_layers)):
|
| 401 |
S = source_act[sl].numpy()
|
| 402 |
T = target_act[tl].numpy()
|
|
|
|
| 406 |
# Neurons already correspond (e.g. DeepSeek) — identity Q
|
| 407 |
Q_matrices[(sl, tl)] = np.eye(S.shape[1]) / S.shape[1]
|
| 408 |
else:
|
| 409 |
+
# Extract block index (e.g. "model.layers.5.mlp" -> 5)
|
| 410 |
+
block_idx = None
|
| 411 |
+
for part_j, part in enumerate(tl.split(".")):
|
| 412 |
+
if part == "layers":
|
| 413 |
+
try:
|
| 414 |
+
block_idx = int(tl.split(".")[part_j + 1])
|
| 415 |
+
except (ValueError, IndexError):
|
| 416 |
+
pass
|
| 417 |
+
break
|
| 418 |
+
|
| 419 |
+
# Reuse permutation if we already computed it for this block
|
| 420 |
+
if block_idx is not None and block_idx in block_perms:
|
| 421 |
+
perm = block_perms[block_idx]
|
| 422 |
+
permutations[(sl, tl)] = perm
|
| 423 |
+
Q_matrices[(sl, tl)] = np.eye(S.shape[1]) / S.shape[1] # placeholder
|
| 424 |
+
else:
|
| 425 |
+
# Neurons are SCRAMBLED (e.g. MiMo) — find the permutation
|
| 426 |
+
# 1. Compute correlation matrix between source and target neurons
|
| 427 |
+
S_norm = (S - S.mean(0)) / (S.std(0) + 1e-8)
|
| 428 |
+
T_norm = (T - T.mean(0)) / (T.std(0) + 1e-8)
|
| 429 |
+
corr = S_norm.T @ T_norm / S.shape[0] # [hidden_dim, hidden_dim]
|
| 430 |
+
|
| 431 |
+
# 2. Run Sinkhorn on cost matrix to get soft transport plan
|
| 432 |
+
# Use reg=0.1 and 30 iters (faster — we only need argmax, not precision)
|
| 433 |
+
cost = 1.0 - corr
|
| 434 |
+
Q_soft = _sinkhorn(cost, reg=0.1, max_iter=30)
|
| 435 |
+
|
| 436 |
+
# 3. Extract hard permutation: for each source neuron, which target neuron?
|
| 437 |
+
perm = np.argmax(Q_soft, axis=1) # source_neuron -> target_neuron
|
| 438 |
+
|
| 439 |
+
# 4. Check for duplicate assignments (Sinkhorn should avoid this, but be safe)
|
| 440 |
+
if len(set(perm)) < len(perm) * 0.9:
|
| 441 |
+
# Too many collisions — fall back to Hungarian-style greedy
|
| 442 |
+
perm = _greedy_permutation(corr)
|
| 443 |
+
|
| 444 |
+
permutations[(sl, tl)] = perm
|
| 445 |
+
Q_matrices[(sl, tl)] = Q_soft
|
| 446 |
+
if block_idx is not None:
|
| 447 |
+
block_perms[block_idx] = perm
|
| 448 |
else:
|
| 449 |
# Different dims -- do lightweight Sinkhorn on this pair only
|
| 450 |
print(f" Layer {i}: dim mismatch ({S.shape[1]} vs {T.shape[1]}), using Sinkhorn...")
|
|
|
|
| 460 |
print(f" Matched layer {i + 1}/{n_source}: {sl} -> {tl}")
|
| 461 |
sys.stdout.flush()
|
| 462 |
|
| 463 |
+
# Timeout: 90 min (Sinkhorn on 4096x4096 is slow on CPU)
|
| 464 |
+
tracker.check_timeout(timeout_seconds=5400)
|
| 465 |
|
| 466 |
if permutations:
|
| 467 |
print(f"[transport] Computed {len(permutations)} neuron permutations")
|