← All experiments THE RESEARCH RECORD / lightning-training

Lightning training

Can stochastic variable-depth subtree sampling (Lightning L3 mass-weighted walk) match or beat the deterministic per-root-child sweep at the same number of optimizer steps?

experimentconcludednegativeattentioneval: legacy
Opened Updated
THE ANSWER SO FAR

No, not at matched steps. At d=8, L3 averaged 18.71-20.46 legacy PPL against 17.99 for the deterministic sweep (L2 19.54, L1 28.55), and mass-proportional LR scaling hurt. With 4-10× more steps and 10-30× lower LR, individual runs reached the baseline but means stayed 0.6-1.5 PPL worse. The bf16 and mass-1 compact KV caches built here made d=32 global radix feasible (L3 12.07 at 780 steps).

d=8 deterministic per-root-child, 65×3 steps 17.99 legacy PPL (bin/perplexity, data/input.txt) pre-loss-fixpre-race-fixtruncated-ancestor-gradientlegacy-eval
d=8 L3 p_stop=0.5, 65×3 steps (mean of 3) 20.09 legacy PPL (bin/perplexity, data/input.txt) pre-loss-fixpre-race-fixtruncated-ancestor-gradientlegacy-eval
d=32 global radix L3, 260×3 steps, lr=2e-4 12.07 legacy PPL (bin/perplexity, data/input.txt) pre-loss-fixpre-race-fixtruncated-ancestor-gradientlegacy-eval
TOPICS
RELATED
SUPERSEDED BY
CODE
THE FULL RECORD

Experiment notes

Open directory on GitHub ↗

Lightning Training — empirical

Trainer note: pre-fix, needs reassessment.

Most quantitative claims in this file were produced before the Wk / Wv / bias gradient fix in commit 1c858c0 (2026-04-23). The experiment ideas and failure modes remain useful, but the reported PPL rankings should be treated as provisional until rechecked on the corrected trainer.

Design doc: notes/trainer/lightning-training.md.

Hypothesis

Stochastic variable-depth subtree sampling (L3 mass-weighted walk) can match or beat the deterministic per-root-child sweep at matched total optimizer steps. L3's stop-probability p_stop controls the expected sample depth: small p_stop → deep/narrow samples, large p_stop → shallow/broad samples.

Reference baselines

config PPL mean source
d=8, 3 SE, 65 steps/SE, per-root-child, lr=3e-3 RMSProp 17.99 rnd/radix-saturation/logs
d=16, 3 SE, 65 steps/SE, per-root-child, lr=3e-3 RMSProp 14.59 rnd/radix-saturation/logs
d=32, 3 SE, 65 steps/SE, per-subtree-file, lr=3e-3 RMSProp 13.40 rnd/radix-saturation/logs

d=16 global-radix won't fit KV cache (9.5GB). Initial sweeps live at d=8 where one full run takes ~5 sec. d=16 work will require adopting the per-subtree file format (deferred — each subtree view is self-contained so L3 within a subtree still works).

Experiments

script what
run_d8_pstop_sweep.sh L3 p_stop ∈ {0.2, 0.3, 0.5}, 65 steps × 3 SE matched to baseline

Result: matched-budget p_stop sweep (commit 20e8121)

config mean PPL min max spread
L3 p_stop=0.2, 65×3 20.46 19.26 22.03 2.76
L3 p_stop=0.3, 65×3 20.12 18.40 22.87 4.47
L3 p_stop=0.5, 65×3 20.09 19.41 20.96 1.55
baseline det. 65×3 17.99 — — —

All Lightning configs lose to the deterministic baseline by ~2.1 PPL at matched step count. Consistent with the design doc's hypothesis: stochastic sampling at matched budget without LR retuning underperforms.

Observations:

  • Variance anti-correlates with p_stop. Smaller p_stop = deeper, lower-mass samples → flakier stochastic corpus coverage.
  • All runs train (no divergence). Infrastructure verified.
  • Best single run (p_stop=0.3, seed=43): 18.40 PPL — within 0.4 of baseline. Suggests LR tuning or a higher step budget could close the gap.

Result: extended step-budget + L1/L2 comparators (same commit)

config mean PPL min max spread
baseline det. 65×3 17.99 — — —
L2 uniform-rc s65×3 19.54 18.59 21.12 2.53
L3 p_stop=0.5 s65×3 20.09 19.41 20.96 1.55
L3 p_stop=0.5 s130×3 25.83 21.01 31.17 10.16
L3 p_stop=0.5 s260×3 26.13 24.71 28.45 3.74
L1 uniform-all s65×3 28.55 28.37 28.90 0.53

Key findings:

  • L2 is the closest Lightning variant to baseline (19.54 vs 17.99). L2 samples root-children uniformly with replacement — essentially the deterministic sweep with stochastic noise. Expected.
  • L1 is disastrous (28.55). Uniform sampling across 845k radix nodes skews heavily toward deep, low-mass leaves.
  • More L3 steps at the same LR makes PPL WORSE, not better. 2× steps → 25.83 (from 20.09 at matched), 4× → 26.13. At fixed lr=3e-3 the extra stochastic steps are over-rotating the weights. LR has to come down.
  • L3 was design-motivated by mass weighting; at this LR it lands between L1 (bad) and L2 (close), but can't beat baseline.

Next direction: LR sweep for L3 at higher step budgets — the "many small steps need a smaller lr" story from earlier bigram work predicts lr≈3e-4..1e-3 at 260 steps/SE. If L3 can't close the gap even with tuned LR, the claim "mass-weighted stochastic sampling beats deterministic uniform-over-root-children" is probably wrong.

Result: per-sample mass-lr scaling (new --lightning-mass-lr flag)

Scales each Lightning sample's step_lr by compress(subtree_mass)/mean so high-mass samples move weights proportionally more. This is a LR scale, not a gradient scale — necessary because under RMSProp/Adam the weight cancels out in the adaptive divisor, so gradient scaling would be a no-op at steady state.

L3 p_stop=0.3, 65×3 matched budget:

config mean PPL min max
baseline deterministic 17.99 — —
L3 p_stop=0.3 mass-off 18.71 17.69 20.69
L3 p_stop=0.3 mass-log 19.75 18.89 21.02
L3 p_stop=0.5 mass-log 23.35 20.22 27.65
L3 p_stop=0.3 mass-sqrt 27.04 22.84 31.85
L3 p_stop=0.3 mass-linear 35.43 25.80 54.08

Key finding: mass-off wins; two runs hit 17.69 / 17.74 — matching baseline within GPU-reduction noise. Mass-lr scaling, even with log compression, hurts. Read: RMSProp's second-moment accumulator already handles the "low-mass = noisy gradient" problem; an external LR multiplier just perturbs the running averages without adding signal. Linear and sqrt blow up as predicted (one huge-mass sample dominates with ratio ≥ 100×).

So #4 from the design discussion (mass-weighted step) is theoretically sound but empirically unhelpful on top of RMSProp. Flag is kept — may matter for SGD where gradient scaling and LR scaling ARE equivalent and there's no adaptive divisor to absorb the imbalance.

Result: step-budget × LR sweep (beats baseline on individual runs)

L3 p_stop=0.3, mass-off, 3 SE:

config mean PPL min max spread
baseline det. 65×3 lr=3e-3 17.99 — — —
L3 s=260 lr=3e-4 18.56 18.42 18.69 0.27
L3 s=650 lr=1e-4 18.57 17.94 19.22 1.28
L3 s=260 lr=1e-4 19.22 18.88 19.46 0.59
L3 s=260 lr=1e-3 21.88 20.48 23.45 2.97
L3 s=650 lr=3e-4 22.24 20.54 24.21 3.67

Two breakthroughs:

  1. s=650 lr=1e-4 seed=43 hits 17.94 PPL — beats baseline. First individual Lightning run to cross that line.
  2. s=260 lr=3e-4 has 0.27 PPL spread across 3 seeds — Lightning's stochastic-variance problem largely goes away at the right LR.

Rule of thumb from this sweep:

  • Baseline budget (65 samples/SE): lr=3e-3
  • 4× budget (260 samples/SE): lr=3e-4 (10× smaller)
  • 10× budget (650 samples/SE): lr=1e-4 (30× smaller)

Stronger-than-sqrt LR reduction — consistent with "many stochastic steps need a smaller lr to absorb noise." RMSProp β=0.999 alone doesn't absorb the full variance at matched step counts; LR compensates.

Next: push to s=1300 or higher with matching LR cuts, or run enough seeds at s=260 lr=3e-4 to statistically verify the gap to baseline.

Result: d=16 Lightning via per-subtree files

Wired Lightning into run_per_subtree_training so d=16 becomes tractable: each root-child subtree file is loaded with its own bounded KV cache (~150 MB - 1.3 GB), samples are bucketed across root-children weighted by total_edge_chars. Peak memory use is the single largest root-child's KV, not the 9.5 GB global cache that would need to fit for direct global-radix Lightning at d=16.

L3 p_stop=0.3 mass-off, 3 SE:

config mean PPL min max spread
deterministic d=16 3 SE × 65 ~14.59 — — —
L3 s=65 lr=3e-3 (matched) 16.04 14.60 17.26 2.67
L3 s=260 lr=3e-4 (4×) 16.06 15.80 16.44 0.64
L3 s=650 lr=1e-4 (10×) 15.66 14.72 16.73 2.01

Same pattern as d=8: individual best runs reach baseline (14.60 matches within noise, 14.72 is close), mean is ~1-1.5 PPL worse. s=260 lr=3e-4 again has tightest variance (0.64). s=650 lr=1e-4 has best mean AND highest ceiling individual run. The step-budget×LR trade mirrors d=8 — further LR sweeping at d=16 s=260 might close more of the gap.

Result: finer LR sweep at d=16 s=260

config mean PPL min max spread
baseline det 65×3 14.59 — — —
L3 s=260 lr=5e-4 18.28 17.23 19.02 1.78
L3 s=260 lr=3e-4 16.06 15.80 16.44 0.64
L3 s=260 lr=2e-4 15.31 14.78 15.69 0.91
L3 s=260 lr=1e-4 15.47 15.22 15.79 0.57
L3 s=260 lr=5e-5 17.75 17.12 18.43 1.31

lr=2e-4 is the d=16 sweet spot: gap to baseline narrows from ~1.5 (at lr=3e-4) down to ~0.7. Classic U-shape — lr=5e-4 too aggressive (noise overwhelms signal), lr=5e-5 too slow (hasn't converged). Best run 14.78 is within 0.2 of baseline.

Result: BF16 K/V cache (halves memory)

KV cache stored as bf16 instead of fp32, with conversion at scatter/gather boundaries. Packed attention buffers remain fp32.

  • d=8 Lightning: 791 MB (was 1582 MB). PPL 16.97 on smoke test — within noise of prior fp32 runs (mean 18.71, min 17.69).
  • d=16 GLOBAL radix: 4.77 GB (was 9.5 GB, was REFUSED before). First time global-radix d=16 Lightning runs. Eliminates the per-subtree-file workaround — can go either way now.
  • Next: d=32 global radix becomes borderline-feasible at 19 GB bf16 via unified memory paging (was 38 GB fp32).

Result: mass=1 compact cache (skips 83-96% of char positions)

Radix edges with edge_mass == 1 are leaves (a single corpus position). Nothing ever attends to their K/V as an ancestor, so they don't need cache storage. Own-edge K/V for the current query reads directly from the fresh d_k/d_v forward buffer (no round-trip through cache). Cache size drops dramatically.

depth mass=1 fraction fp32 orig bf16 only bf16 + compact
d=8 83% 1582 MB 791 MB 274 MB
d=16 89% 9500 MB 4770 MB 532 MB
d=32 96% ~38 GB ~19 GB 570 MB

d=32 is barely larger than d=16 because the extra depth is all mass=1 tails. Empirical confirmation of the theoretical claim that branching structure saturates around d≈16 for Shakespeare.

Result: d=32 global-radix becomes tractable — new project best PPL

With the compact cache, d=32 global-radix Lightning fits comfortably. 3 SE × 260 samples, lr=2e-4, L3 p_stop=0.3, mass-off:

depth PPL optimizer steps
d=16 15.38 780
d=32 12.07 780

d=32 Lightning beats:

  • paper's d=32 baseline (13.17, per-subtree 3 SE × 195 steps)
  • previously recorded project best (13.36, d=32 linear mass-weight)

Timing: d=32 ≈ 220 s/run (3 SE), d=16 ≈ 24 s/run. 7× slower but unlocks deeper branching supervision.

Run

rnd/lightning-training/run_d8_pstop_sweep.sh

Each cell dumps a config JSON (*.json), per-run logs (*_r{N}.log), and prints label run N PPL = X time = Ys to stdout.