gradual warmup

This commit is contained in:
wassname
2026-01-16 09:47:35 +00:00
parent 08d54c7095
commit ddcd830a4b
2 changed files with 133 additions and 43 deletions
+109 -32
View File
@@ -40,7 +40,7 @@ class TrainingConfig:
seed: int = 42
"""Random seed for reproducibility (layer selection, dim selection, training dynamics)."""
init_n_samples: int = 1000
init_n_samples: int = 2000
"""Number of samples for WANDA-style dimension selection and subspace computation.
Higher = more stable activation statistics, but slower init.
@@ -52,10 +52,10 @@ class TrainingConfig:
data_seed: int = 42
"""Fixed seed for data selection (which suffixes are used)."""
model_name: str = "Qwen/Qwen3-4B-Instruct-2507"
model_name: str = "google/gemma-3-12b-it"
quantization_type: Literal["4bit", "8bit", "none"] = "none"
n_modules: int = 512
n_modules: int = 256
"""Total number of layer×module combinations to select (by gradient importance).
Examples with n_modules=5:
@@ -64,7 +64,7 @@ class TrainingConfig:
The selection is sparse: each layer×module is ranked by gradient, top-k are selected.
Not Cartesian product - can have multiple modules at one layer, none at another.
Default 42 (≈14 layers × 3 modules for typical 36-layer model).
Default 256.
"""
target_modules: List[str] = ["residual-writers"]
@@ -80,23 +80,25 @@ class TrainingConfig:
Explicit list: ["down_proj", "o_proj"] - only these module suffixes are candidates.
"""
bs: int = 8
bs: int = 14
"""Batch size"""
n_epochs: int = 20
n_epochs: int = 30
lr: float = 5e-4
"""Learning rate. Sweep findings: 1e-4 too low (-18 F1), 3e-5 too low (-17 F1).
For Cayley: 4e-4 to 6e-4. For LoRA/DoRA: ~10x lower (3e-5 to 6e-5).
lr: float = 0.002
"""Learning rate.
Empirically, Cayley rotations tend to tolerate higher LR than LoRA/DoRA.
This repo's default matches a strong run on Qwen3-14B.
"""
wd: float = 1e-8
wd: float = 1e-4
"""Weight decay"""
n_logs: int = 10
"""Log this many times per training"""
val_every_n_samples: int = 512
val_every_n_samples: int = 1024
"""Validate every N training samples (independent of logging)."""
effective_bs: int = 32
@@ -108,7 +110,7 @@ class TrainingConfig:
val_split: float = 0.15
"""Fraction of data for validation"""
early_stop_patience: int = 11
early_stop_patience: int = 16
"""Stop if val loss doesn't improve for N validation checks. 0 = disabled (recommended with one-cycle scheduler)."""
early_stop_min_delta: float = 0.00001
@@ -171,7 +173,7 @@ class TrainingConfig:
- taskdiff_x_write_x_notlogits: Task ∩ write ∩ (lm_head^⊥)
"""
loss_subspace_rank: Optional[int] = 8
loss_subspace_rank: Optional[int] = 4
"""Rank (top-k) for loss subspace.
If None (default), select rank automatically via `loss_subspace_energy_frac`
@@ -187,12 +189,12 @@ class TrainingConfig:
k such that cumulative energy >= this fraction. 60% was used in MSRS paper
"""
loss_layer_frac: float = 0.9
loss_layer_frac: float = 0.5
"""Depth fraction (0-1) at which to apply representation loss.
The loss is computed at a single layer: int(loss_layer_frac * num_hidden_layers).
Default 0.8 (80% depth) is in the "planning zone" where Fisher ratio and
Default 0.5 (50% depth) is a simple mid-depth choice; prior sweeps often found a "planning zone" where Fisher ratio and
cross-sample consistency peak across tested architectures (Qwen, Gemma). Also suppurted by supported by e.g 2024-Gurnee-Universal-Neurons-in-GPT2-Language-Models.md
Rationale: gradient-based layer selection was circular (gradients flow FROM
@@ -220,7 +222,7 @@ class TrainingConfig:
dataset_name: str = "honest"
max_samples: Optional[int] = 800
max_samples: Optional[int] = 3000
"""Max training samples (None = all)"""
n_last_tokens: int = 3
@@ -274,15 +276,15 @@ class TrainingConfig:
Projection loss naturally creates ordering; mono is a safety rail, not driver.
"""
mono_margin: float = 0.4
mono_margin: float = 0.5
"""Monotonic threshold_frac: fraction of √H_ref for minimum separation.
Threshold = threshold_frac × √H_ref + threshold_floor.
With H_ref=4 nats (typical), threshold_frac=0.4, floor=0.04: threshold ≈ 0.84 nats.
With H_ref=4 nats (typical), threshold_frac=0.5, floor=0.04: threshold ≈ 1.04 nats.
Sweep findings (2026-01-07, gemma1b):
| margin | F1 |
| 0.4 | 23.6 | ← current default
| 0.5 | (default)
| 0.2 | 17.2 |
| 0.25 | 0.0 | (collapsed)
@@ -300,7 +302,7 @@ class TrainingConfig:
Prevents division issues and provides small stable deadzone.
"""
mono_weight: float = 20.0
mono_weight: float = 30.0
"""Monotonicity loss scaling.
WARNING: Values ≥100 trap adapters in bad init - can't learn "no change" at c=0.
@@ -310,35 +312,36 @@ class TrainingConfig:
"""
mono_warmup_frac: float = -2
"""Constraint warmup using -N syntax (binary: off during warmup, on after).
"""Constraint warmup using -N syntax (gradual linear ramp from 0 to full weight).
- Negative (-N): N × warmup_pct (e.g., -2 = 2× LR warmup = 20% at default)
- Zero: No warmup, active from start
- Zero: No warmup, full weight from start
- Positive: Explicit fraction (e.g., 0.3 = 30% of training)
Default -2: constraints activate at 2× LR warmup. Lets projection establish
direction before constraints kick in.
Default -2: mono ramps up over 2× LR warmup. Lets projection establish
direction before constraints reach full strength.
"""
coh_warmup_frac: float = -2
"""Constraint warmup using -N syntax (binary: off during warmup, on after).
"""Constraint warmup using -N syntax (gradual linear ramp from 0 to full weight).
- Negative (-N): N × warmup_pct (e.g., -2 = 2× LR warmup = 20% at default)
- Zero: No warmup, active from start
- Zero: No warmup, full weight from start
- Positive: Explicit fraction (e.g., 0.3 = 30% of training)
Default -2: synchronized with mono_warmup_frac.
Default -2: coh ramps up over 2× LR warmup. Prevents coh from fighting
projection loss early when deltas are large.
"""
conc_warmup_frac: float = -2
"""Concentration weighting warmup using -N syntax (binary: off during warmup, on after).
"""Concentration (focus) weighting warmup using -N syntax (gradual linear ramp).
- Negative (-N): N × warmup_pct (e.g., -2 = 2× LR warmup = 20% at default)
- Zero: No warmup, active from start
- Zero: No warmup, full weight from start
- Positive: Explicit fraction (e.g., 0.3 = 30% of training)
During warmup, delta_*_norm_full=None disables subspace focus weighting.
Default -2: synchronized with other constraints.
During ramp-up, focus weighting interpolates from 1.0 (no penalty for out-of-subspace)
to the configured focus_softness. Default -2: synchronized with other constraints.
"""
orth_weight: float = 0
@@ -647,12 +650,86 @@ default_configs = {
bs=64,
),
),
# Qwen/Qwen3-32B
"q32b-80gb": (
"Qwen 32B on 80GB GPU (maximum size)",
TrainingConfig(
model_name="Qwen/Qwen3-32B",
bs=4,
),
),
"q14b-80gb": (
"Qwen 14B on 80GB GPU (production quality)",
TrainingConfig(
model_name="Qwen/Qwen3-14B",
bs=12,
),
),
"q14b-goodrun": (
"Qwen 14B best run (fisher, coh+mono, r64)",
TrainingConfig(
model_name="Qwen/Qwen3-14B",
quantization_type="none",
n_modules=256,
target_modules=["residual-writers"],
bs=12,
n_epochs=20,
lr=0.002,
wd=1e-4,
n_logs=10,
val_every_n_samples=512,
effective_bs=32,
quick=False,
val_split=0.15,
early_stop_patience=14,
early_stop_min_delta=1e-5,
warmup_pct=0.1,
r=64,
rot_u=False,
rot_v=True,
dim_select_method="wanda_svd_l1_trip",
max_rotation_angle=pi / 4,
loss_subspace="taskdiff_x_suppressed_x_write",
loss_subspace_rank=4,
loss_subspace_energy_frac=0.6,
loss_layer_frac=0.5,
min_adapter_layer_frac=0.1,
dataset_name="honest",
max_samples=3000,
n_last_tokens=3,
coh=True,
coh_weight=10.0,
coh_thresh=0.9,
coh_barrier_mode="log1p_squared",
coh_lse_temperature=3.0,
mono=True,
mono_margin=0.5,
mono_threshold_floor=0.04,
mono_weight=30.0,
mono_warmup_frac=-2,
coh_warmup_frac=-2,
orth_weight=0,
antisym_margin=0.0,
fisher_var_floor_frac=0.1,
fisher_abs_std_floor=0.05,
fisher_detach_std=True,
eval_max_dilemmas=None,
eval_max_tokens=288,
use_wandb=True,
wandb_project="AntiPaSTO",
wandb_tags=None,
verbose=1,
PROMPT=PROMPT,
PERSONAS=PERSONAS,
),
),
# add gemma4b
"gemma12b-80gb": (
"Gemma 3 12B on 80GB GPU",
TrainingConfig(
model_name="google/gemma-3-12b-it",
bs=4,
bs=14,
),
),
+24 -11
View File
@@ -126,10 +126,19 @@ def compute_batch_loss(
coh_warmup_steps = resolve_warmup(config.coh_warmup_frac)
conc_warmup_steps = resolve_warmup(config.conc_warmup_frac)
# Binary switch: constraints off during warmup, on after
effective_mono_weight = config.mono_weight if step >= mono_warmup_steps else 0.0
enable_coherence_effective = config.coh and (step >= coh_warmup_steps)
enable_concentration = step >= conc_warmup_steps
# Gradual linear ramp: 0 → full weight over warmup period
def ramp_weight(weight: float, warmup_steps: int) -> float:
if warmup_steps <= 0:
return weight
progress = min(1.0, step / warmup_steps)
return weight * progress
effective_mono_weight = ramp_weight(config.mono_weight, mono_warmup_steps)
effective_coh_weight = ramp_weight(config.coh_weight, coh_warmup_steps) if config.coh else 0.0
enable_coherence_effective = config.coh # Always enabled if config.coh=True, weight controls strength
# Focus warmup: interpolate focus_softness from 1.0 (disabled) to configured value
focus_warmup_progress = min(1.0, step / conc_warmup_steps) if conc_warmup_steps > 0 else 1.0
effective_focus_softness = 1.0 + (config.focus_softness - 1.0) * focus_warmup_progress
attention_mask = batch["attention_mask"]
mask_cho = attention_mask[::2]
@@ -258,7 +267,7 @@ def compute_batch_loss(
delta_neg_norm_full = delta_neg_agg.norm(dim=-1) # [b]
# Antisymmetric loss (Fisher + align + delta_full)
# Disable concentration during warmup (delta_norm_full=None)
# Focus now uses gradual ramp via effective_focus_softness (1.0 = disabled, <1 = enabled)
loss_dict = contrastive_steering_loss_with_ref(
s_ref_cho=s_ref_cho,
s_ref_rej=s_ref_rej,
@@ -270,9 +279,9 @@ def compute_batch_loss(
last_n_tokens=config.n_last_tokens,
orth_weight=config.orth_weight,
antisym_margin=config.antisym_margin,
focus_softness=config.focus_softness,
delta_pos_norm_full=delta_pos_norm_full if enable_concentration else None,
delta_neg_norm_full=delta_neg_norm_full if enable_concentration else None,
focus_softness=effective_focus_softness,
delta_pos_norm_full=delta_pos_norm_full,
delta_neg_norm_full=delta_neg_norm_full,
fisher_var_floor_frac=config.fisher_var_floor_frac,
fisher_abs_std_floor=config.fisher_abs_std_floor,
fisher_detach_std=config.fisher_detach_std,
@@ -324,7 +333,7 @@ def compute_batch_loss(
ref_label_logp=ref_coherence,
pi_label_logp=pi_coherence,
mask=mask_logp,
scale=config.coh_weight,
scale=effective_coh_weight,
ref_logits=ref_logits,
pi_logits=pi_logits,
coh_thresh_frac=config.coh_thresh,
@@ -1034,8 +1043,12 @@ def train_epoch(
# Enable ONLY when coherence + monotonic + focus are ON, and only after
# all warmups are finished (LR warmup + coh/mono warmups).
warmup_steps = int(total_steps * config.warmup_pct) if total_steps else 0
mono_warmup_frac = config.mono_warmup_frac if config.mono_warmup_frac >= 0 else config.warmup_pct
coh_warmup_frac = config.coh_warmup_frac if config.coh_warmup_frac >= 0 else config.warmup_pct
# resolve_warmup: -N → N × warmup_pct, else explicit
def resolve_warmup_frac(frac: float) -> float:
return (-frac) * config.warmup_pct if frac < 0 else frac
mono_warmup_frac = resolve_warmup_frac(config.mono_warmup_frac)
coh_warmup_frac = resolve_warmup_frac(config.coh_warmup_frac)
mono_warmup_steps = int(total_steps * mono_warmup_frac) if total_steps else 0
coh_warmup_steps = int(total_steps * coh_warmup_frac) if total_steps else 0