Upload 149 files
Browse files- hugging/td_fuse/selfimprove.py +278 -76
hugging/td_fuse/selfimprove.py
CHANGED
|
@@ -1,18 +1,19 @@
|
|
| 1 |
"""
|
| 2 |
-
TD Self-Improvement
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
|
|
|
| 16 |
"""
|
| 17 |
|
| 18 |
import torch
|
|
@@ -34,25 +35,25 @@ class SelfImproveConfig:
|
|
| 34 |
model_path: str = "" # Auto-detected below
|
| 35 |
reference_model: str = "Qwen/Qwen3-VL-8B-Instruct"
|
| 36 |
output_dir: str = "td_fuse_outputs/self_improve"
|
|
|
|
| 37 |
|
| 38 |
# How many layers to target
|
| 39 |
top_n_layers: int = 16
|
| 40 |
min_improvement_score: float = 0.05
|
| 41 |
|
| 42 |
-
# LoRA config
|
| 43 |
-
lora_r: int = 64
|
| 44 |
-
lora_alpha: int = 128
|
| 45 |
train_epochs: int = 3
|
| 46 |
train_batch: int = 2
|
| 47 |
train_grad_accum: int = 8
|
| 48 |
learning_rate: float = 1.5e-5
|
| 49 |
-
norm_learning_rate: float = 5e-5
|
| 50 |
-
save_steps: int =
|
| 51 |
num_train_samples: int = 3000
|
| 52 |
|
| 53 |
# Layernorm unfreezing
|
| 54 |
-
|
| 55 |
-
norm_kurtosis_threshold: float = 15.0 # Unfreeze norms with kurtosis above this
|
| 56 |
|
| 57 |
# Scoring weights
|
| 58 |
w_health: float = 0.55
|
|
@@ -60,6 +61,28 @@ class SelfImproveConfig:
|
|
| 60 |
w_coherence: float = 0.20
|
| 61 |
|
| 62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
def auto_detect_model_path() -> Tuple[str, int]:
|
| 64 |
"""Find the best starting model and determine cycle number."""
|
| 65 |
base = Path("td_fuse_outputs/self_improve")
|
|
@@ -441,9 +464,6 @@ def detect_clogged_norms(health_scores: Dict[str, Dict], threshold: float = 15.0
|
|
| 441 |
"""
|
| 442 |
clogged = []
|
| 443 |
for name, h in health_scores.items():
|
| 444 |
-
# Only look at norm layers
|
| 445 |
-
# Qwen3-VL has: input_layernorm, post_attention_layernorm
|
| 446 |
-
# Also check for any norm-like parameter (norm, ln)
|
| 447 |
if any(norm_type in name.lower() for norm_type in ["layernorm", "ln_", "_norm"]):
|
| 448 |
kurt = abs(h["stats"]["kurtosis"])
|
| 449 |
if kurt > threshold:
|
|
@@ -452,6 +472,91 @@ def detect_clogged_norms(health_scores: Dict[str, Dict], threshold: float = 15.0
|
|
| 452 |
return clogged
|
| 453 |
|
| 454 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 455 |
# ============================================================
|
| 456 |
# STEP 6: LoRA + UNFROZEN NORMS IMPROVEMENT
|
| 457 |
# ============================================================
|
|
@@ -486,6 +591,11 @@ def improve_with_lora(model_path: str, target_layers: List[int],
|
|
| 486 |
)
|
| 487 |
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
|
| 488 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 489 |
# Apply LoRA on projection weights
|
| 490 |
lora_config = LoraConfig(
|
| 491 |
r=cfg.lora_r,
|
|
@@ -522,34 +632,28 @@ def improve_with_lora(model_path: str, target_layers: List[int],
|
|
| 522 |
print(f"\n LoRA trainable + {unfrozen_count} unfrozen norm layers ({unfrozen_params:,} params)")
|
| 523 |
model.print_trainable_parameters()
|
| 524 |
|
| 525 |
-
# Load
|
| 526 |
-
|
|
|
|
| 527 |
train_texts = []
|
| 528 |
-
|
| 529 |
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
chat = (f"<|im_start|>user\nContinue this text naturally.<|im_end|>\n"
|
| 540 |
-
f"<|im_start|>assistant\n{text[:800]}<|im_end|>")
|
| 541 |
-
train_texts.append(chat)
|
| 542 |
-
count += 1
|
| 543 |
-
print(f" General text: {count}")
|
| 544 |
-
except Exception as e:
|
| 545 |
-
print(f" WARNING: General text failed: {e}")
|
| 546 |
|
| 547 |
-
#
|
| 548 |
try:
|
| 549 |
ds = load_dataset("openai/gsm8k", "main", split="train")
|
| 550 |
count = 0
|
| 551 |
for row in ds:
|
| 552 |
-
if count >=
|
| 553 |
break
|
| 554 |
q, a = row.get("question", ""), row.get("answer", "")
|
| 555 |
if q and a:
|
|
@@ -557,72 +661,149 @@ def improve_with_lora(model_path: str, target_layers: List[int],
|
|
| 557 |
f"<|im_start|>assistant\n<think>\n{a}\n</think><|im_end|>")
|
| 558 |
train_texts.append(chat)
|
| 559 |
count += 1
|
| 560 |
-
print(f" Math
|
| 561 |
except Exception as e:
|
| 562 |
-
print(f" WARNING:
|
| 563 |
|
| 564 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 565 |
try:
|
| 566 |
ds = load_dataset("sahil2801/CodeAlpaca-20k", split="train")
|
| 567 |
count = 0
|
| 568 |
for row in ds:
|
| 569 |
-
if count >=
|
| 570 |
break
|
| 571 |
-
# CodeAlpaca uses instruction/input/output, NOT prompt/completion
|
| 572 |
inst = row.get("instruction", "")
|
| 573 |
inp = row.get("input", "")
|
| 574 |
out = row.get("output", "")
|
| 575 |
-
if inst and out:
|
| 576 |
prompt = f"{inst}\n{inp}" if inp else inst
|
| 577 |
chat = (f"<|im_start|>user\n{prompt}<|im_end|>\n"
|
| 578 |
f"<|im_start|>assistant\n{out}<|im_end|>")
|
| 579 |
train_texts.append(chat)
|
| 580 |
count += 1
|
| 581 |
-
print(f" Code: {count}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 582 |
except Exception as e:
|
| 583 |
-
print(f" WARNING:
|
| 584 |
|
| 585 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 586 |
try:
|
| 587 |
ds = load_dataset("yahma/alpaca-cleaned", split="train")
|
| 588 |
count = 0
|
| 589 |
for row in ds:
|
| 590 |
-
if count >=
|
| 591 |
break
|
| 592 |
inst = row.get("instruction", "")
|
| 593 |
inp = row.get("input", "")
|
| 594 |
out = row.get("output", "")
|
| 595 |
-
if inst and out:
|
| 596 |
prompt = f"{inst}\n{inp}" if inp else inst
|
| 597 |
chat = (f"<|im_start|>user\n{prompt}<|im_end|>\n"
|
| 598 |
f"<|im_start|>assistant\n{out}<|im_end|>")
|
| 599 |
train_texts.append(chat)
|
| 600 |
count += 1
|
| 601 |
-
print(f" Instructions: {count}")
|
| 602 |
except Exception as e:
|
| 603 |
-
print(f" WARNING:
|
| 604 |
|
| 605 |
-
#
|
| 606 |
try:
|
| 607 |
-
ds = load_dataset("
|
| 608 |
count = 0
|
| 609 |
for row in ds:
|
| 610 |
-
if count >=
|
| 611 |
break
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
|
| 616 |
-
if q and options and 0 <= answer_idx < len(options):
|
| 617 |
-
answer_text = options[answer_idx]
|
| 618 |
-
chat = (f"<|im_start|>user\n{q}<|im_end|>\n"
|
| 619 |
-
f"<|im_start|>assistant\n<think>\nLet me reason through this.\n"
|
| 620 |
-
f"{answer_text}\n</think>\n{answer_text}<|im_end|>")
|
| 621 |
train_texts.append(chat)
|
| 622 |
count += 1
|
| 623 |
-
print(f"
|
| 624 |
except Exception as e:
|
| 625 |
-
print(f" WARNING:
|
| 626 |
|
| 627 |
print(f"\n Total training samples: {len(train_texts)}")
|
| 628 |
|
|
@@ -685,6 +866,7 @@ def improve_with_lora(model_path: str, target_layers: List[int],
|
|
| 685 |
gradient_accumulation_steps=cfg.train_grad_accum,
|
| 686 |
learning_rate=cfg.learning_rate,
|
| 687 |
bf16=True,
|
|
|
|
| 688 |
logging_steps=max(1, total_steps // 20),
|
| 689 |
save_strategy="steps",
|
| 690 |
save_steps=cfg.save_steps,
|
|
@@ -803,13 +985,22 @@ def run_cycle(cfg: SelfImproveConfig = None):
|
|
| 803 |
else:
|
| 804 |
cycle_num = 2 if "improve" in cfg.model_path else 1
|
| 805 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 806 |
start = time.time()
|
|
|
|
| 807 |
print("=" * 60)
|
| 808 |
-
print(f"TD SELF-IMPROVEMENT
|
| 809 |
print(f"Cycle: {cycle_num}")
|
| 810 |
print(f"Model: {cfg.model_path}")
|
| 811 |
-
print(f"LoRA rank: {cfg.lora_r}
|
|
|
|
|
|
|
| 812 |
print(f"Norm training: ENABLED (LR={cfg.norm_learning_rate})")
|
|
|
|
|
|
|
| 813 |
print(f"Started: {time.strftime('%H:%M:%S')}")
|
| 814 |
print("=" * 60)
|
| 815 |
|
|
@@ -842,8 +1033,7 @@ def run_cycle(cfg: SelfImproveConfig = None):
|
|
| 842 |
)
|
| 843 |
ref_analysis = analyze_model_weights(str(ref_path), "Qwen3-VL-8B (reference)")
|
| 844 |
damage_scores = compute_damage_scores(model_analysis, ref_analysis)
|
| 845 |
-
|
| 846 |
-
shutil.rmtree(str(ref_path), ignore_errors=True)
|
| 847 |
gc.collect()
|
| 848 |
|
| 849 |
# STEP 4: Coherence
|
|
@@ -870,6 +1060,18 @@ def run_cycle(cfg: SelfImproveConfig = None):
|
|
| 870 |
else:
|
| 871 |
print(f"\n No clogged norms above threshold {cfg.norm_kurtosis_threshold}")
|
| 872 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 873 |
# Save report
|
| 874 |
report_dir = Path(cfg.output_dir)
|
| 875 |
report_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
| 1 |
"""
|
| 2 |
+
TD Self-Improvement v6 — MEGA CYCLE
|
| 3 |
+
|
| 4 |
+
v5 fixes + MEGA MODE for cycle 3+:
|
| 5 |
+
- 8000+ training samples (was 3000)
|
| 6 |
+
- Priority: code, math, reasoning, creativity (top), then general (second)
|
| 7 |
+
- LoRA r=128 (was 64) — maximum reshape capacity
|
| 8 |
+
- ALL 36 layers targeted (was 16)
|
| 9 |
+
- gradient_checkpointing for speed + VRAM savings
|
| 10 |
+
- batch_size 4 (was 2) — 2x faster per step
|
| 11 |
+
- Norm unclogging continues from cycle 2
|
| 12 |
+
|
| 13 |
+
SPEED OPTIMIZATIONS:
|
| 14 |
+
- gradient_checkpointing = True (saves ~40% VRAM, allows bigger batches)
|
| 15 |
+
- batch_size 4 + grad_accum 4 = effective batch 16 (same quality, faster)
|
| 16 |
+
- Processes 2x more samples per forward pass = ~2x faster wall clock
|
| 17 |
"""
|
| 18 |
|
| 19 |
import torch
|
|
|
|
| 35 |
model_path: str = "" # Auto-detected below
|
| 36 |
reference_model: str = "Qwen/Qwen3-VL-8B-Instruct"
|
| 37 |
output_dir: str = "td_fuse_outputs/self_improve"
|
| 38 |
+
mega_mode: bool = False # Auto-enabled for cycle 3+
|
| 39 |
|
| 40 |
# How many layers to target
|
| 41 |
top_n_layers: int = 16
|
| 42 |
min_improvement_score: float = 0.05
|
| 43 |
|
| 44 |
+
# LoRA config
|
| 45 |
+
lora_r: int = 64
|
| 46 |
+
lora_alpha: int = 128
|
| 47 |
train_epochs: int = 3
|
| 48 |
train_batch: int = 2
|
| 49 |
train_grad_accum: int = 8
|
| 50 |
learning_rate: float = 1.5e-5
|
| 51 |
+
norm_learning_rate: float = 5e-5
|
| 52 |
+
save_steps: int = 100
|
| 53 |
num_train_samples: int = 3000
|
| 54 |
|
| 55 |
# Layernorm unfreezing
|
| 56 |
+
norm_kurtosis_threshold: float = 15.0
|
|
|
|
| 57 |
|
| 58 |
# Scoring weights
|
| 59 |
w_health: float = 0.55
|
|
|
|
| 61 |
w_coherence: float = 0.20
|
| 62 |
|
| 63 |
|
| 64 |
+
def apply_mega_mode(cfg: SelfImproveConfig):
|
| 65 |
+
"""Crank everything up for mega cycles (3+)."""
|
| 66 |
+
print("\n *** MEGA MODE ENABLED ***")
|
| 67 |
+
cfg.mega_mode = True
|
| 68 |
+
cfg.top_n_layers = 36 # ALL layers — leave nothing behind
|
| 69 |
+
cfg.min_improvement_score = 0.0 # Target everything
|
| 70 |
+
cfg.lora_r = 128 # Maximum reshape capacity
|
| 71 |
+
cfg.lora_alpha = 256 # 2x rank
|
| 72 |
+
cfg.train_epochs = 3 # 3 epochs over 8000 samples = plenty
|
| 73 |
+
cfg.train_batch = 4 # 2x bigger batch — 2x faster
|
| 74 |
+
cfg.train_grad_accum = 4 # Effective batch = 16 (same quality)
|
| 75 |
+
cfg.num_train_samples = 8000 # Nearly 3x more data
|
| 76 |
+
cfg.learning_rate = 1e-5 # Slightly lower LR for stability with more data
|
| 77 |
+
cfg.norm_learning_rate = 1e-3 # Much higher — norms need strong signal to move during training
|
| 78 |
+
cfg.save_steps = 200 # Save less often (faster)
|
| 79 |
+
print(f" LoRA: r={cfg.lora_r}, alpha={cfg.lora_alpha}")
|
| 80 |
+
print(f" Layers: ALL {cfg.top_n_layers}")
|
| 81 |
+
print(f" Data: {cfg.num_train_samples} samples")
|
| 82 |
+
print(f" Batch: {cfg.train_batch} × {cfg.train_grad_accum} = {cfg.train_batch * cfg.train_grad_accum}")
|
| 83 |
+
print(f" Speed: gradient_checkpointing + batch 4")
|
| 84 |
+
|
| 85 |
+
|
| 86 |
def auto_detect_model_path() -> Tuple[str, int]:
|
| 87 |
"""Find the best starting model and determine cycle number."""
|
| 88 |
base = Path("td_fuse_outputs/self_improve")
|
|
|
|
| 464 |
"""
|
| 465 |
clogged = []
|
| 466 |
for name, h in health_scores.items():
|
|
|
|
|
|
|
|
|
|
| 467 |
if any(norm_type in name.lower() for norm_type in ["layernorm", "ln_", "_norm"]):
|
| 468 |
kurt = abs(h["stats"]["kurtosis"])
|
| 469 |
if kurt > threshold:
|
|
|
|
| 472 |
return clogged
|
| 473 |
|
| 474 |
|
| 475 |
+
def repair_clogged_norms(model_path: str, clogged_norms: List[str],
|
| 476 |
+
ref_path: str, health_scores: Dict[str, Dict]) -> str:
|
| 477 |
+
"""
|
| 478 |
+
DIRECT SURGICAL REPAIR of clogged layernorms.
|
| 479 |
+
|
| 480 |
+
Training can't fix kurtosis of 2446 — gradient signal too weak for 1D norms.
|
| 481 |
+
Instead: blend broken norms with reference values.
|
| 482 |
+
|
| 483 |
+
Strategy:
|
| 484 |
+
- Kurtosis > 100: 80% reference, 20% current (severely broken)
|
| 485 |
+
- Kurtosis > 50: 60% reference, 40% current
|
| 486 |
+
- Kurtosis > 15: 40% reference, 60% current (mildly clogged)
|
| 487 |
+
|
| 488 |
+
This is like replacing a clogged pipe instead of pushing harder.
|
| 489 |
+
"""
|
| 490 |
+
if not clogged_norms:
|
| 491 |
+
print(" No clogged norms to repair.")
|
| 492 |
+
return model_path
|
| 493 |
+
|
| 494 |
+
print(f"\n{'='*50}")
|
| 495 |
+
print(f"SURGICAL NORM REPAIR — {len(clogged_norms)} clogged norms")
|
| 496 |
+
print(f"{'='*50}")
|
| 497 |
+
|
| 498 |
+
from safetensors.torch import load_file, save_file
|
| 499 |
+
import glob
|
| 500 |
+
|
| 501 |
+
# Load reference norms
|
| 502 |
+
print(" Loading reference norms...")
|
| 503 |
+
ref_norms = {}
|
| 504 |
+
for st_file in sorted(glob.glob(f"{ref_path}/*.safetensors")):
|
| 505 |
+
state = load_file(st_file, device="cpu")
|
| 506 |
+
for name, tensor in state.items():
|
| 507 |
+
if name in clogged_norms:
|
| 508 |
+
ref_norms[name] = tensor.clone()
|
| 509 |
+
del state
|
| 510 |
+
|
| 511 |
+
print(f" Matched {len(ref_norms)}/{len(clogged_norms)} norms in reference")
|
| 512 |
+
|
| 513 |
+
if not ref_norms:
|
| 514 |
+
print(" WARNING: No reference norms found! Skipping repair.")
|
| 515 |
+
return model_path
|
| 516 |
+
|
| 517 |
+
# Load and repair model weights
|
| 518 |
+
repaired = 0
|
| 519 |
+
st_files = sorted(glob.glob(f"{model_path}/*.safetensors"))
|
| 520 |
+
|
| 521 |
+
for st_file in st_files:
|
| 522 |
+
state = load_file(st_file, device="cpu")
|
| 523 |
+
changed = False
|
| 524 |
+
|
| 525 |
+
for name in list(state.keys()):
|
| 526 |
+
if name in ref_norms:
|
| 527 |
+
kurt = abs(health_scores[name]["stats"]["kurtosis"])
|
| 528 |
+
|
| 529 |
+
# Blend ratio based on severity
|
| 530 |
+
if kurt > 100:
|
| 531 |
+
ref_weight = 0.80 # Severely broken — mostly use reference
|
| 532 |
+
elif kurt > 50:
|
| 533 |
+
ref_weight = 0.60
|
| 534 |
+
else:
|
| 535 |
+
ref_weight = 0.40 # Mildly clogged
|
| 536 |
+
|
| 537 |
+
old_tensor = state[name]
|
| 538 |
+
ref_tensor = ref_norms[name].to(old_tensor.dtype)
|
| 539 |
+
|
| 540 |
+
# Blend: new = ref_weight * reference + (1-ref_weight) * current
|
| 541 |
+
state[name] = ref_weight * ref_tensor + (1 - ref_weight) * old_tensor
|
| 542 |
+
|
| 543 |
+
# Compute new kurtosis
|
| 544 |
+
t = state[name].float()
|
| 545 |
+
new_kurt = abs(((t - t.mean()) / max(t.std().item(), 1e-8)).pow(4).mean().item() - 3)
|
| 546 |
+
|
| 547 |
+
print(f" {name}: kurtosis {kurt:.1f} → {new_kurt:.1f} (blend {ref_weight:.0%} ref)")
|
| 548 |
+
repaired += 1
|
| 549 |
+
changed = True
|
| 550 |
+
|
| 551 |
+
if changed:
|
| 552 |
+
save_file(state, st_file)
|
| 553 |
+
del state
|
| 554 |
+
|
| 555 |
+
gc.collect()
|
| 556 |
+
print(f"\n Repaired {repaired} norms via reference blending")
|
| 557 |
+
return model_path
|
| 558 |
+
|
| 559 |
+
|
| 560 |
# ============================================================
|
| 561 |
# STEP 6: LoRA + UNFROZEN NORMS IMPROVEMENT
|
| 562 |
# ============================================================
|
|
|
|
| 591 |
)
|
| 592 |
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
|
| 593 |
|
| 594 |
+
# Enable gradient checkpointing for mega mode (saves ~40% VRAM)
|
| 595 |
+
if cfg.mega_mode:
|
| 596 |
+
model.gradient_checkpointing_enable()
|
| 597 |
+
print(" Gradient checkpointing: ENABLED (saves ~40% VRAM)")
|
| 598 |
+
|
| 599 |
# Apply LoRA on projection weights
|
| 600 |
lora_config = LoraConfig(
|
| 601 |
r=cfg.lora_r,
|
|
|
|
| 632 |
print(f"\n LoRA trainable + {unfrozen_count} unfrozen norm layers ({unfrozen_params:,} params)")
|
| 633 |
model.print_trainable_parameters()
|
| 634 |
|
| 635 |
+
# Load training data — priority-weighted categories
|
| 636 |
+
# MEGA MODE: code/math/reasoning/creativity get 2x allocation
|
| 637 |
+
print("\n Loading training data...")
|
| 638 |
train_texts = []
|
| 639 |
+
total = cfg.num_train_samples
|
| 640 |
|
| 641 |
+
if cfg.mega_mode:
|
| 642 |
+
# Priority split: code=2000, math=2000, reasoning=1500, creativity=1000, instructions=1000, general=500
|
| 643 |
+
alloc = {"code": int(total*0.25), "math": int(total*0.25),
|
| 644 |
+
"reasoning": int(total*0.19), "creativity": int(total*0.12),
|
| 645 |
+
"instructions": int(total*0.12), "general": int(total*0.07)}
|
| 646 |
+
else:
|
| 647 |
+
per = total // 5
|
| 648 |
+
alloc = {"code": per, "math": per, "reasoning": per,
|
| 649 |
+
"creativity": 0, "instructions": per, "general": per}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 650 |
|
| 651 |
+
# 1. MATH (top priority)
|
| 652 |
try:
|
| 653 |
ds = load_dataset("openai/gsm8k", "main", split="train")
|
| 654 |
count = 0
|
| 655 |
for row in ds:
|
| 656 |
+
if count >= alloc["math"]:
|
| 657 |
break
|
| 658 |
q, a = row.get("question", ""), row.get("answer", "")
|
| 659 |
if q and a:
|
|
|
|
| 661 |
f"<|im_start|>assistant\n<think>\n{a}\n</think><|im_end|>")
|
| 662 |
train_texts.append(chat)
|
| 663 |
count += 1
|
| 664 |
+
print(f" Math (GSM8K): {count}")
|
| 665 |
except Exception as e:
|
| 666 |
+
print(f" WARNING: GSM8K failed: {e}")
|
| 667 |
|
| 668 |
+
# Extra math for mega mode — harder competition math
|
| 669 |
+
if cfg.mega_mode and count < alloc["math"]:
|
| 670 |
+
try:
|
| 671 |
+
ds2 = load_dataset("lighteval/MATH", "all", split="train", trust_remote_code=True)
|
| 672 |
+
extra = 0
|
| 673 |
+
for row in ds2:
|
| 674 |
+
if count + extra >= alloc["math"]:
|
| 675 |
+
break
|
| 676 |
+
q = row.get("problem", "")
|
| 677 |
+
a = row.get("solution", "")
|
| 678 |
+
if q and a:
|
| 679 |
+
chat = (f"<|im_start|>user\n{q}\nSolve this step by step.<|im_end|>\n"
|
| 680 |
+
f"<|im_start|>assistant\n<think>\n{a}\n</think><|im_end|>")
|
| 681 |
+
train_texts.append(chat)
|
| 682 |
+
extra += 1
|
| 683 |
+
print(f" Math (MATH hard): {extra}")
|
| 684 |
+
except Exception as e:
|
| 685 |
+
print(f" WARNING: MATH dataset failed: {e}")
|
| 686 |
+
|
| 687 |
+
# 2. CODE (top priority)
|
| 688 |
try:
|
| 689 |
ds = load_dataset("sahil2801/CodeAlpaca-20k", split="train")
|
| 690 |
count = 0
|
| 691 |
for row in ds:
|
| 692 |
+
if count >= alloc["code"]:
|
| 693 |
break
|
|
|
|
| 694 |
inst = row.get("instruction", "")
|
| 695 |
inp = row.get("input", "")
|
| 696 |
out = row.get("output", "")
|
| 697 |
+
if inst and out and len(out) > 20:
|
| 698 |
prompt = f"{inst}\n{inp}" if inp else inst
|
| 699 |
chat = (f"<|im_start|>user\n{prompt}<|im_end|>\n"
|
| 700 |
f"<|im_start|>assistant\n{out}<|im_end|>")
|
| 701 |
train_texts.append(chat)
|
| 702 |
count += 1
|
| 703 |
+
print(f" Code (CodeAlpaca): {count}")
|
| 704 |
+
except Exception as e:
|
| 705 |
+
print(f" WARNING: CodeAlpaca failed: {e}")
|
| 706 |
+
|
| 707 |
+
# Extra code for mega mode
|
| 708 |
+
if cfg.mega_mode and count < alloc["code"]:
|
| 709 |
+
try:
|
| 710 |
+
ds2 = load_dataset("m-a-p/CodeFeedback-Filtered-Instruction", split="train")
|
| 711 |
+
extra = 0
|
| 712 |
+
for row in ds2:
|
| 713 |
+
if count + extra >= alloc["code"]:
|
| 714 |
+
break
|
| 715 |
+
q = row.get("query", "")
|
| 716 |
+
a = row.get("answer", "")
|
| 717 |
+
if q and a and len(a) > 30:
|
| 718 |
+
chat = (f"<|im_start|>user\n{q}<|im_end|>\n"
|
| 719 |
+
f"<|im_start|>assistant\n{a[:1500]}<|im_end|>")
|
| 720 |
+
train_texts.append(chat)
|
| 721 |
+
extra += 1
|
| 722 |
+
print(f" Code (CodeFeedback): {extra}")
|
| 723 |
+
except Exception as e:
|
| 724 |
+
print(f" WARNING: CodeFeedback failed: {e}")
|
| 725 |
+
|
| 726 |
+
# 3. REASONING (top priority)
|
| 727 |
+
try:
|
| 728 |
+
ds = load_dataset("TIGER-Lab/MMLU-Pro", split="test")
|
| 729 |
+
count = 0
|
| 730 |
+
for row in ds:
|
| 731 |
+
if count >= alloc["reasoning"]:
|
| 732 |
+
break
|
| 733 |
+
q = row.get("question", "")
|
| 734 |
+
options = row.get("options", [])
|
| 735 |
+
answer_idx = row.get("answer_index", -1)
|
| 736 |
+
if q and options and 0 <= answer_idx < len(options):
|
| 737 |
+
answer_text = options[answer_idx]
|
| 738 |
+
chat = (f"<|im_start|>user\n{q}<|im_end|>\n"
|
| 739 |
+
f"<|im_start|>assistant\n<think>\nLet me reason through this.\n"
|
| 740 |
+
f"{answer_text}\n</think>\n{answer_text}<|im_end|>")
|
| 741 |
+
train_texts.append(chat)
|
| 742 |
+
count += 1
|
| 743 |
+
print(f" Reasoning (MMLU-Pro): {count}")
|
| 744 |
except Exception as e:
|
| 745 |
+
print(f" WARNING: MMLU-Pro failed: {e}")
|
| 746 |
|
| 747 |
+
# 4. CREATIVITY (mega mode priority)
|
| 748 |
+
if alloc.get("creativity", 0) > 0:
|
| 749 |
+
try:
|
| 750 |
+
ds = load_dataset("yahma/alpaca-cleaned", split="train")
|
| 751 |
+
count = 0
|
| 752 |
+
creative_words = ["write", "story", "poem", "creative", "imagine", "describe",
|
| 753 |
+
"compose", "draft", "narrative", "fiction", "essay", "letter",
|
| 754 |
+
"explain", "summarize", "rewrite", "paraphrase"]
|
| 755 |
+
for row in ds:
|
| 756 |
+
if count >= alloc["creativity"]:
|
| 757 |
+
break
|
| 758 |
+
inst = row.get("instruction", "").lower()
|
| 759 |
+
out = row.get("output", "")
|
| 760 |
+
if any(w in inst for w in creative_words) and out and len(out) > 50:
|
| 761 |
+
inp = row.get("input", "")
|
| 762 |
+
prompt = f"{row['instruction']}\n{inp}" if inp else row["instruction"]
|
| 763 |
+
chat = (f"<|im_start|>user\n{prompt}<|im_end|>\n"
|
| 764 |
+
f"<|im_start|>assistant\n{out}<|im_end|>")
|
| 765 |
+
train_texts.append(chat)
|
| 766 |
+
count += 1
|
| 767 |
+
print(f" Creativity (Alpaca filtered): {count}")
|
| 768 |
+
except Exception as e:
|
| 769 |
+
print(f" WARNING: Creativity data failed: {e}")
|
| 770 |
+
|
| 771 |
+
# 5. INSTRUCTIONS (general)
|
| 772 |
try:
|
| 773 |
ds = load_dataset("yahma/alpaca-cleaned", split="train")
|
| 774 |
count = 0
|
| 775 |
for row in ds:
|
| 776 |
+
if count >= alloc.get("instructions", 0):
|
| 777 |
break
|
| 778 |
inst = row.get("instruction", "")
|
| 779 |
inp = row.get("input", "")
|
| 780 |
out = row.get("output", "")
|
| 781 |
+
if inst and out and len(out) > 30:
|
| 782 |
prompt = f"{inst}\n{inp}" if inp else inst
|
| 783 |
chat = (f"<|im_start|>user\n{prompt}<|im_end|>\n"
|
| 784 |
f"<|im_start|>assistant\n{out}<|im_end|>")
|
| 785 |
train_texts.append(chat)
|
| 786 |
count += 1
|
| 787 |
+
print(f" Instructions (Alpaca): {count}")
|
| 788 |
except Exception as e:
|
| 789 |
+
print(f" WARNING: Alpaca failed: {e}")
|
| 790 |
|
| 791 |
+
# 6. GENERAL TEXT (lowest priority)
|
| 792 |
try:
|
| 793 |
+
ds = load_dataset("neuralmagic/LLM_compression_calibration", split="train")
|
| 794 |
count = 0
|
| 795 |
for row in ds:
|
| 796 |
+
if count >= alloc.get("general", 0):
|
| 797 |
break
|
| 798 |
+
text = row.get("text", "")
|
| 799 |
+
if len(text) > 100:
|
| 800 |
+
chat = (f"<|im_start|>user\nContinue this text naturally.<|im_end|>\n"
|
| 801 |
+
f"<|im_start|>assistant\n{text[:800]}<|im_end|>")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 802 |
train_texts.append(chat)
|
| 803 |
count += 1
|
| 804 |
+
print(f" General text: {count}")
|
| 805 |
except Exception as e:
|
| 806 |
+
print(f" WARNING: General text failed: {e}")
|
| 807 |
|
| 808 |
print(f"\n Total training samples: {len(train_texts)}")
|
| 809 |
|
|
|
|
| 866 |
gradient_accumulation_steps=cfg.train_grad_accum,
|
| 867 |
learning_rate=cfg.learning_rate,
|
| 868 |
bf16=True,
|
| 869 |
+
gradient_checkpointing=cfg.mega_mode, # Saves ~40% VRAM in mega mode
|
| 870 |
logging_steps=max(1, total_steps // 20),
|
| 871 |
save_strategy="steps",
|
| 872 |
save_steps=cfg.save_steps,
|
|
|
|
| 985 |
else:
|
| 986 |
cycle_num = 2 if "improve" in cfg.model_path else 1
|
| 987 |
|
| 988 |
+
# MEGA MODE for cycle 3+
|
| 989 |
+
if cycle_num >= 3 and not cfg.mega_mode:
|
| 990 |
+
apply_mega_mode(cfg)
|
| 991 |
+
|
| 992 |
start = time.time()
|
| 993 |
+
mode_label = "MEGA CYCLE" if cfg.mega_mode else "NORM UNCLOGGING + LoRA"
|
| 994 |
print("=" * 60)
|
| 995 |
+
print(f"TD SELF-IMPROVEMENT v6 — {mode_label}")
|
| 996 |
print(f"Cycle: {cycle_num}")
|
| 997 |
print(f"Model: {cfg.model_path}")
|
| 998 |
+
print(f"LoRA rank: {cfg.lora_r}")
|
| 999 |
+
print(f"Samples: {cfg.num_train_samples}")
|
| 1000 |
+
print(f"Batch: {cfg.train_batch} × {cfg.train_grad_accum} = {cfg.train_batch * cfg.train_grad_accum}")
|
| 1001 |
print(f"Norm training: ENABLED (LR={cfg.norm_learning_rate})")
|
| 1002 |
+
if cfg.mega_mode:
|
| 1003 |
+
print(f"Gradient checkpointing: ENABLED")
|
| 1004 |
print(f"Started: {time.strftime('%H:%M:%S')}")
|
| 1005 |
print("=" * 60)
|
| 1006 |
|
|
|
|
| 1033 |
)
|
| 1034 |
ref_analysis = analyze_model_weights(str(ref_path), "Qwen3-VL-8B (reference)")
|
| 1035 |
damage_scores = compute_damage_scores(model_analysis, ref_analysis)
|
| 1036 |
+
# DON'T delete reference yet — we need it for norm repair
|
|
|
|
| 1037 |
gc.collect()
|
| 1038 |
|
| 1039 |
# STEP 4: Coherence
|
|
|
|
| 1060 |
else:
|
| 1061 |
print(f"\n No clogged norms above threshold {cfg.norm_kurtosis_threshold}")
|
| 1062 |
|
| 1063 |
+
# STEP 5c: REPAIR clogged norms surgically
|
| 1064 |
+
if clogged_norms:
|
| 1065 |
+
print(f"\n{'='*40}")
|
| 1066 |
+
print("STEP 5c/7: SURGICAL NORM REPAIR")
|
| 1067 |
+
print(f"{'='*40}")
|
| 1068 |
+
repair_clogged_norms(cfg.model_path, clogged_norms, str(ref_path), health_scores)
|
| 1069 |
+
|
| 1070 |
+
# Clean up reference now
|
| 1071 |
+
print(f" Cleaning up reference weights...")
|
| 1072 |
+
shutil.rmtree(str(ref_path), ignore_errors=True)
|
| 1073 |
+
gc.collect()
|
| 1074 |
+
|
| 1075 |
# Save report
|
| 1076 |
report_dir = Path(cfg.output_dir)
|
| 1077 |
report_dir.mkdir(parents=True, exist_ok=True)
|