Skip to content

fix(chaos_optimizer): brake permanent-mutation bug + smooth-threshold generalization - #27

Merged
theomgdev merged 6 commits into
mainfrom
experimental
Aug 7, 2026
Merged

fix(chaos_optimizer): brake permanent-mutation bug + smooth-threshold generalization#27
theomgdev merged 6 commits into
mainfrom
experimental

Conversation

@theomgdev

Copy link
Copy Markdown
Owner

Summary

Two rounds of fixes to ChaosGrad, OdyssNet's zero-config optimizer, both driven by the same standard: fixed constants are fine (AdamW's betas are the precedent) when outcomes are insensitive to them, but not when they silently paper over a problem specific to our own bundled examples while leaving general library users exposed to the same failure mode unprotected.

  • Loss-spike brake permanent-mutation bug (d516d3a): the brake was firing on ordinary per-batch noise (not real divergence) and permanently shrinking the D-adaptation estimator's own state on every trigger, with no recovery path — silently freezing convergence_mnist_record.py training at a ~83-87% plateau. Replaced with a transient brake_ceiling multiplier that throttles only the applied step, decoupled from the estimator's bookkeeping, and relaxes back toward 1.0 every call.
  • Smooth-threshold generalization (16b8d98, 72b6ac7): the traction limit's rms0 include/exclude test was a hard cutoff sitting exactly on micro_quiet_warm's init std (one of this library's own bundled inits) — the same seed produced a 16.8x different cap on CPU vs CUDA purely from RNG noise landing on either side of the cutoff. Replaced with a smooth ramp. Also exposed brake_sigma/brake_ratio/brake_ema_alpha as constructor parameters (previously unreachable class constants tuned against our own batch~16-32 examples) and fixed Neurogenesis.expand() silently dropping a user's brake config on network growth.

An advisor-led review round (72b6ac7) on top of the first smoothing attempt caught two real regressions the 293-test suite had already passed: a wrong-direction cap (raising an init scale inside the fade ramp made training more restricted) and a version of the fix that passed every test but silently moved record.py's own cap 0.006→0.0025 — only caught by re-measuring the actual example configs, not by re-running old tests. Also fixed brake_sigma/ratio/ema_alpha being plain instance attributes that state_dict() doesn't serialize (a save/load round-trip would've silently reverted a customized brake to defaults).

Full detail on every fix, including exact before/after measurements, is in the CHANGELOG.md [2.6.1] entry.

Numbers moved (re-validated against real runs, not assumed)

  • MNIST 98.62%→98.71%, MNIST Revive 98.54%→98.70%, MNIST Tiny 95.15%→95.58%, MNIST Scaled 97.38%→98.01%, MNIST (8k) Embed 94.08%→93.71%, Skill Transfer speedup 3.0x→3.6x.
  • convergence_mnist_record.py: previously froze mid-run (~83-87%), now trains cleanly through all 100 epochs, landing at 87.98% (peak 88.46%) zero-config. The README's 90.14% "WORLD RECORD" banner is deliberately left unchanged — it predates this optimizer entirely (a different, since-removed scheduler/preset pipeline), so it isn't a like-for-like regression target. Only a status note was added beneath it explaining the provenance.
  • convergence_mnist_reverse_record.py moved to zero-config (lr removed) — this was in-flight, uncommitted WIP for most of this work and was finished/committed independently.

Known open issue (not fixed here, documented)

convergence_sine_wave.py shows a late-training loss explosion around epoch ~7900 of a 10000-epoch run, not yet root-caused. EPOCHS was reduced to 6800 as a stopgap to avoid the window, not a fix — flagged in the CHANGELOG for future investigation.

Test plan

  • Full pytest tests/ -q: 297/297 passing (added trust-cap continuity tests, a state_dict round-trip test for the brake config, and a Neurogenesis brake-forwarding test)
  • XOR seeds 42/123: both solve zero-config
  • Delayed adder: 2000 epochs, no divergence, final loss ~0.00017
  • convergence_mnist_record.py 25-epoch diagnostic probe: d_max flat, effective_lr steady with brake recovering after each dip
  • MNIST-3k/6-epoch probe: 89.20% (byte-identical before/after every change in this PR)
  • Author has run the full example suite locally and confirmed results look good

🤖 Generated with Claude Code

theomgdev and others added 5 commits August 7, 2026 00:47
…nary noise

Diagnosed via instrumented probes on convergence_mnist_record.py: the
spike brake was firing every ~150-350 batches on nothing worse than
ordinary per-batch classification noise, unrelated to Hebbian plasticity
(fires *more* often with hebb_type=None). Each trigger permanently
multiplied d_numerator and d_max by brake_factor, so repeated harmless
triggers ratcheted the step scale down with no way back - by epoch 25 on
a 3k-subset probe, eff_lr had collapsed 500x and training had effectively
frozen, matching the ~83-87% accuracy plateau seen in full 100-epoch runs.

Replaced the permanent mutation with a transient brake_ceiling multiplier
applied only at the point of the actual parameter update, leaving
d/d_numerator/d_max bookkeeping untouched so the estimator keeps learning
the true scale while suppressed. The ceiling relaxes geometrically back
toward 1.0 every report_loss call, so an isolated spike heals in tens of
steps while a fast cascade (genuine divergence) keeps compounding down.

Also fixed a secondary bug: the post-spike variance reseed collapsed to
0.0, making the sigma test degenerate for ~20 calls after every fire.

Validated: adder 2000 epochs (no divergence), record.py 25-epoch probe
(d_max no longer decays, eff_lr recovers instead of collapsing), XOR
seeds 42/123, MNIST-3k probe (brake never fires there, unaffected),
full pytest suite (285/285).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Confirms the loss-spike brake fix (d516d3a) at full scale:
convergence_mnist_record.py previously froze around epoch 15-20 (the
83-87% plateau the brake bug caused) and now trains cleanly through
all 100 epochs, landing at 87.98% zero-config (peak 88.46%, epoch 86).
LR set to None. The README's 90.14% "WORLD RECORD" banner predates this
optimizer entirely (different scheduler/preset pipeline, since removed)
and is left as-is with an added status note per explicit decision -
not a regression target, since the script changed too much for a
like-for-like comparison.

Re-ran the rest of the suite under the fixed brake and refreshed
numbers accordingly: MNIST 98.62->98.71%, MNIST Revive 98.54->98.70%,
MNIST Tiny 95.15->95.58%, MNIST Scaled 97.38->98.01%, MNIST Embed
94.08->93.71% (within run-to-run noise), Skill Transfer speedup
3.0x->3.6x. Sine Wave/Latch/Stopwatch log excerpts refreshed.

- convergence_skill_transfer.py: retuned add_epochs 500->250,
  mul_epochs 1500->500 - avoids overfitting the small model before
  transplant, giving a consistent clear win instead of a partial one.
- convergence_sine_wave.py: EPOCHS 10000->6800 as a stopgap for a
  newly-observed late-training instability (loss explodes ~epoch 7900
  under the fixed brake) - not yet root-caused, flagged in CHANGELOG
  as a known issue rather than silently patched over.
- Version bumped to 2.6.1 (odyssnet/__init__.py, pyproject.toml,
  CITATION.cff, CHANGELOG.md) for the brake fix + claim-check fix.

reverse_record.py's zero-config experiment stays uncommitted -
unresolved, not part of this pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…brake constants

The traction limit's rms0 include/exclude test was a hard cutoff sitting
exactly on micro_quiet_warm's init std (1e-3) -- on convergence_mnist_record.py,
the same seed produced a 16.8x different cap on CPU vs CUDA purely from which
side of the cutoff a group's RNG-drawn rms0 landed on. Replaced with a smooth
weight ramp (_anchor_weight) over the same boundary numbers; verified zero cap
change on the CUDA-run configs the READMEs are actually measured on.

Also exposes brake_sigma/brake_ratio/brake_ema_alpha as constructor params
(previously unreachable class constants tuned against our own batch~16-32
examples), adds a bias-correction-style warmup for the brake's loss-variance
estimate, and fixes Neurogenesis.expand() silently dropping a user's brake
config on network growth.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ate_dict gap

Review of the previous commit (16b8d98) caught three real defects the test
suite had not:

1. The rms0/weight formula in the first attempt was non-monotone in the
   wrong direction -- a group's contribution to the traction cap could
   *decrease* as its own rms0 grew, so raising an init scale inside the
   fade ramp made training more restricted, backwards. Replaced with
   max(rms0, blend), which guarantees a partially-trusted group can never
   bind tighter than its own value.

2. That fix's first version blended toward a fixed reference
   (_TRUST_RMS_FLOOR_HIGH), which passed every test but silently moved
   convergence_mnist_record.py's own cap from 0.006 to 0.0025: an excluded
   near-zero group's fallback undercut memory_feedback's real, smaller
   anchor. Caught only by re-measuring the actual example configs, not by
   the suite. Now blends toward the smallest already-fully-trusted group's
   rms0 instead, which reproduces the old cap exactly whenever nothing
   sits mid-ramp (every bundled example, confirmed on both record.py and
   the adder) and disables the cap entirely only when literally every
   group is excluded (a lone all-zero-initialized parameter, no other
   family to anchor against -- test_adaptive_mode_converges_on_quadratic
   caught this one directly when an intermediate version regressed it).

3. brake_sigma/brake_ratio/brake_ema_alpha were exposed as plain instance
   attributes, which torch.optim.Optimizer.state_dict() doesn't
   serialize -- a save/load round trip would silently revert a customized
   brake to defaults. Moved into `defaults` so they ride the param groups
   like brake_factor already does.

Re-validated end to end after these corrections: adder 2000 epochs and
XOR 42/123 byte-identical to the prior run, record.py's 25-epoch probe
identical (d_max flat 0.060141, eff_lr 0.005958), MNIST-3k identical
(89.20%), full pytest 297/297.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 7, 2026 17:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates OdyssNet’s ChaosGrad (zero-config optimizer) to (1) fix a loss-spike brake behavior that could permanently suppress adaptive step scaling, and (2) generalize the traction-limit anchor selection from hard cutoffs to a smooth fade—aiming to remove discontinuities and improve stability across bundled examples and external user configurations.

Changes:

  • Reworked the loss-spike brake to throttle a transient brake_ceiling (applied-step multiplier) instead of mutating the D-adaptation estimator state, and exposed brake tuning parameters (brake_sigma, brake_ratio, brake_ema_alpha).
  • Replaced hard include/exclude logic in the traction-limit anchor (trust_ratio) with a continuous _anchor_weight + blended reference in _trust_cap.
  • Updated tests, examples, READMEs, and release metadata for the 2.6.1 release.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/training/test_chaos_optimizer.py Adds regression and continuity tests for the smoothed traction anchor and the new brake-ceiling behavior.
odyssnet/training/chaos_optimizer.py Implements brake_ceiling, brake warmup/relaxation, exposed brake params, and smooth trust-cap anchoring.
odyssnet/utils/neurogenesis.py Preserves brake configuration (and brake_ceiling) when rebuilding ChaosGrad during expansion.
examples/advanced/convergence_mnist_record.py Switches record example to true zero-config (LR = None).
examples/advanced/convergence_mnist_reverse_record.py Removes explicit lr to use zero-config behavior.
examples/advanced/convergence_sine_wave.py Reduces epoch count as a stopgap for documented late-training instability.
examples/advanced/convergence_skill_transfer.py Retunes epoch counts to reflect updated transfer behavior and runtime expectations.
README.md Refreshes benchmark numbers/log excerpts and adds a status note clarifying the MNIST record provenance vs current optimizer.
README_TR.md Mirrors README benchmark/log updates in Turkish.
CHANGELOG.md Adds 2.6.1 entry describing the optimizer fixes and related example/doc updates.
pyproject.toml Bumps package version to 2.6.1.
odyssnet/init.py Bumps __version__ to 2.6.1.
CITATION.cff Updates citation version to 2.6.1.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread CHANGELOG.md Outdated
Comment thread CITATION.cff
Comment on lines +8 to 9
version: 2.6.1
date-released: 2025-12-12

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's by design. Invention date.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@theomgdev
theomgdev merged commit d53c76b into main Aug 7, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants