Files
evil_MoE/src/vgrout/tablelog.py
T
wassname 6094568c56 feat: lora2r adapter (rank-2r PiSSA-init LoRA) + SGTM three-way hard routing
Structural-separation arm to disentangle directionality from shrinkage. A rank-2r
PiSSA-init LoRA with A and B both trainable, partitioned into a deployed block [:r]
and a quarantine block [r:] (spectrum-matched via alternated SVD axes). Unlike the
same-basis PiSSA routeV (where deploy-ablation only removes a magnitude slice of one
shared update = shrinkage null), each block has its own input-side A rows and
output-side B columns, so deploy-ablation removes a different FUNCTION.

Routing = SGTM-style three-way hard per-rollout masks from the cosine of the deployed
block's gate-pass gradient to the pair-extracted v_grad: clean (m=0,d=0) trains
deployed only; hack (m=1,d=1) detaches deployed output so only the quarantine updates
(SGTM grad-retain trick); mid (m=1,d=0) trains both (absorption). Gate is no-cheat:
cos to the hand-authored-pair direction, never an oracle label of a live rollout.

verify_lora2r_routing.py gates identity-at-init, the three-way block-grad routing,
per-rollout c-probe recovery, and ablation teeth; wired into smoke-lora2r. Additive:
PiSSA / lora_frozen_b paths untouched.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-10 09:25:58 +00:00

144 lines
7.2 KiB
Python

"""Per-step training-table rendering and run logging.
Two concerns, both pure presentation (no model, no RNG): set up the token-efficient
loguru sinks for a run, and render the per-step metrics table. The renderer is the
single source of truth for column order, width, header, and number format; the
training loop hands it a row dict of raw values and gets back a formatted line.
"""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from loguru import logger
from tqdm import tqdm
LOGS_DIR = Path("logs")
def setup_logging(run_id: str) -> Path:
"""Token-efficient loguru: stdout = 1-char icon + msg; verbose log to file.
See /root/.claude/skills/token-efficient-logging/SKILL.md.
"""
LOGS_DIR.mkdir(exist_ok=True)
verbose_log = LOGS_DIR / f"{datetime.now().strftime('%Y%m%dT%H%M%S')}_{run_id}.log"
logger.remove()
logger.add(
lambda msg: tqdm.write(msg, end=""),
colorize=True,
format="<level>{level.icon}</level> {message}",
level="INFO",
)
logger.add(
verbose_log,
format="{time:HH:mm:ss} | {level} | {message}",
level="DEBUG",
)
logger.level("INFO", icon="I")
logger.level("WARNING", icon="W")
logger.level("ERROR", icon="E")
logger.level("DEBUG", icon="D")
return verbose_log
@dataclass(frozen=True)
class _Col:
"""Declarative column definition for the streamed step table."""
key: str
width: int
header: str
fmt: str | None = None
desc: str = "" # one-line decode for the legend; "" => omitted from legend
def _format_cell(value, fmt: str | None) -> str:
"""Format one cell. NaN renders as 'nan' regardless of spec."""
if value is None:
return "nan"
if fmt == "frac":
n, d = value
return f"{n}/{d}"
if fmt is None:
return str(value)
if isinstance(value, float) and value != value: # NaN
return "nan"
return format(value, fmt)
class StepLogger:
"""Render raw per-step metrics using one canonical column definition."""
def __init__(self, arm: str, modes: list[str], mode_code: dict[str, str],
show_ablate: bool = False) -> None:
# Erase reports projection diagnostics; routeV reports routing diagnostics below.
projects = arm == "projected"
is_route = arm in ("routingV", "routingV_per_token", "routingV_lora2r")
cols: list[_Col] = [
_Col("step", 4, "step", "d", "GRPO step"),
_Col("ref_eq", 6, "ref_eq", ".2f", "vanilla-equiv step (cum_gens/256)"),
_Col("rew", 6, "rew", "+.2f", "mean combined reward"),
_Col("rew_s", 6, "rew_s↑", "+.2f", "student mean reward"),
_Col("gt_s", 6, "gt_s↑", "frac", "student ground-truth passes"),
_Col("gt_t", 6, "gt_t", "frac", "teacher ground-truth passes (sanity)"),
_Col("hack_s", 7, "hack_s?", "frac", "student hack-flagged rollouts (the headline)"),
_Col("hack_t", 7, "hack_t", "frac", "teacher hack-flagged rollouts (sanity: pool hacks)"),
# Held-out deployed evaluation with quarantine ablated; NaN between evaluation steps.
_Col("hack_deployed", 7, "hk_dep", "+.2f", "DEPLOY-eval hack (routeV: quarantine OFF; vanilla/erase: trained model); held-out subset, T=0.7, every eval_ablate_every steps; nan between"),
_Col("solve_deployed", 7, "slv_dep", "+.2f", "DEPLOY-eval solve (same cadence; nan between)"),
]
# Multi-mode runs show current-step hacks per environment; single-mode would duplicate hack_s.
self._modes = modes if len(modes) > 1 else []
for m in self._modes:
cols.append(_Col(f"hk_{mode_code[m]}", 5, f"hk_{mode_code[m]}", "d",
f"student hacks of {m} THIS step (current batch, not cumulative)"))
cols += [
_Col("lp_s", 6, "lp_s↓", "+.2f", "mean student gen_logp (diagnostic)"),
_Col("lp_t", 6, "lp_t↑", "+.2f", "mean teacher gen_logp; off-policy gap = lp_s-lp_t"),
_Col("loss", 7, "loss", "+.2f", "mean GRPO loss"),
_Col("gn", 7, "gn", ".1e", "pre-clip L2 norm of delta_S grads (vs grad_clip)"),
_Col("lr", 7, "lr", ".1e", "scheduled learning rate"),
]
if projects:
cols += [
_Col("cos_pre", 6, "cin", ".2f", "hack-ward grad fraction ||relu(V@g)||/||g|| [0,1] BEFORE proj"),
_Col("cos_pre_s", 6, "cin_s", ".2f", "cin on student-only grad"),
_Col("cos_pre_t", 6, "cin_t", ".2f", "cin on teacher-only grad (want cin_t>cin_s)"),
_Col("cos_post", 6, "cout", ".2f", "hack-ward fraction AFTER projection (want ~0: all removed)"),
_Col("fired", 5, "fired", ".2f", "fraction of modules where projection fired"),
]
# routeV reports unit and energy shares across the routing band plus residual leak.
if is_route:
cols += [
_Col("qmass", 6, "qmass", ".2f", "quarantine energy share ||g_quar||/(||g_keep||+||g_quar||): fraction of the update parked in the throwaway quarantine adapter"),
_Col("keep", 6, "keep", ".2f", "unit share with cos below the band -> kept whole in the deployed adapter (left)"),
_Col("resid", 6, "resid", ".2f", "unit share with cos inside the band -> partially routed (residual middle)"),
_Col("rout", 6, "rout", ".2f", "unit share with cos above the band -> fully routed into quarantine (right)"),
_Col("keepE", 6, "keepE", ".2f", "energy-weighted keep: share of grad ENERGY in the kept zone"),
_Col("residE", 6, "residE", ".2f", "energy-weighted resid: share of grad ENERGY in the partially-routed zone"),
_Col("routE", 6, "routE", ".2f", "energy-weighted rout: grad ENERGY share fully routed (~quarantine mass; the routed total is routE..routE+residE)"),
_Col("leak", 6, "leak", "+.2f", "hack-ward cosine left in the deployed adapter after routing; ~0 = stripped clean, >0 = hack leaked through (under-routed)"),
]
# Show the training-prompt deploy proxy only when an ablated slice exists.
if is_route and show_ablate:
cols += [
_Col("hack_abl", 6, "hk_abl", "frac", "per-step deploy proxy: hack rate on the ablated (deploy-mode) rollout slice; train prompts, noisier than hk_dep"),
_Col("solve_abl", 6, "slv_abl", "frac", "per-step deploy proxy: solve rate on the ablated (deploy-mode) rollout slice; train prompts"),
]
self._cols = cols
def header(self) -> str:
return " ".join(f"{c.header:>{c.width}}" for c in self._cols)
def row(self, cells: dict) -> str:
return " ".join(
f"{_format_cell(cells[c.key], c.fmt):>{c.width}}" for c in self._cols
)
def legend(self) -> str:
"""Decode the (arm-/mode-conditional) columns actually present this run."""
lines = "\n".join(f" {c.header:>8} = {c.desc}" for c in self._cols if c.desc)
return ("table columns (timing gen/fb/t_rew/sec dropped from streaming, kept "
"in the end-of-run dump):\n" + lines)