Precision weighting + NGD trainer for predictive coding (ResNet-18/CIFAR-10) - #34
Open
aprotsenko24 wants to merge 1 commit into
Open
Precision weighting + NGD trainer for predictive coding (ResNet-18/CIFAR-10)#34aprotsenko24 wants to merge 1 commit into
aprotsenko24 wants to merge 1 commit into
Conversation
…8/CIFAR-10)
Per-node diagonal precision (inverse error variance) for the Gaussian energy, plus an
online-precision natural-gradient trainer, benchmarked on the ResNet-18 / CIFAR-10 muPC demo.
Core
- GaussianEnergy: per-channel diagonal precision (scalar path unchanged) + optional report-only
log-precision term.
- NodeState.precision: optional per-node precision carried in state (default None ->
bitwise-identical to prior behaviour, no extra pytree leaves). energy_functional reads it, so a
dynamic precision reaches both inference and the local weight grads with no node
forward()/inference changes. muPC weight_grad_scale=1.0 -> no double application.
- core/precision.py: probe_residual_precision (one-pass diagonal Pi = 1/Var(residual)).
- training/ngd_trainer.py: train_ngd (online EMA precision; NGD via precision-weighted SGD),
probe_latent_propagation (per-layer signal-propagation diagnostic).
Demo / experiments
- resnet18_cifar10_demo.py: --precision {none,diag_probe,online}, --optimizer {adamw,ngd},
--momentum, --seed, --probe_latents; machine-readable [RESULT] line.
- precision_experiment.py: paired multi-seed sweep driver (CSV checkpoint, --resume, per-epoch
progress capture, retries, optimizer/momentum-keyed paired-stats report).
Results (CIFAR-10, muPC ResNet-18):
- diag_probe (frozen diagonal precision, AdamW): consistently beats the Pi=1 baseline in EARLY
training (2 epochs: +1.6/+2.8/+3.1 pts across seeds, lower energy), but the gain washes out at
convergence (30 epochs, n=3): none 42.03+/-0.23% vs diag_probe 42.01+/-1.15%, paired Delta -0.02
pts (p=0.98, not significant); energy marginally lower. A convergence-speed/conditioning effect,
not a converged-accuracy change.
- online/NGD (per-step EMA precision, plain SGD): trains far slower than AdamW; does not reach the
AdamW baseline (negative result).
Precision is a conditioning/robustness lever, not a path to backprop-level accuracy.
Tests: test_precision_weighting.py, test_ngd_precision.py (full suite 229 passed).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Precision weighting + natural-gradient (NGD) for predictive coding
Adds uncertainty-aware (precision-weighted) predictive coding to FabricPC and benchmarks it on the
ResNet-18 / CIFAR-10 muPC demo. Precision ≈ inverse error covariance ≈ Fisher information, so this is
a mechanism-grounded conditioning lever (complementary to muPC scaling).
What's added
GaussianEnergy: per-channel diagonal precision (scalar path unchanged) + optional report-onlylog-precision term.
NodeState.precision: optional per-node precision carried in state (defaultNone⇒bitwise-identical to prior behaviour, no extra pytree leaves). Consumed at the single
energy_functionalchoke point, so a dynamic precision reaches both inference and the localweight gradients with no node
forward()/ inference changes. muPCweight_grad_scale=1.0⇒ nodouble application.
core/precision.py::probe_residual_precision— one-pass diagonal Π = 1/Var(residual).training/ngd_trainer.py—train_ngd(online EMA precision; NGD via precision-weighted SGD),probe_latent_propagation(per-layer signal-propagation diagnostic).resnet18_cifar10_demo.py—--precision {none,diag_probe,online},--optimizer {adamw,ngd},--momentum,--seed,--probe_latents; machine-readable[RESULT]line.precision_experiment.py— paired multi-seed sweep driver (CSV checkpoint,--resume, per-epochprogress capture, retries, optimizer/momentum-keyed paired-stats report).
tests/test_precision_weighting.py,tests/test_ngd_precision.py.Results (CIFAR-10, muPC ResNet-18) — two separate modes
Mode A —
diag_probe(frozen diagonal precision, AdamW). Π=1/Var estimated once pre-training,then fixed.
Consistently better in EARLY training (2 epochs, every seed): +1.57 / +2.84 / +3.14 pts over
the Π=1 baseline, with lower train energy in all three.
But the gain washes out at convergence. 30 epochs, 3 paired seeds:
none 42.03 ± 0.23% vs diag_probe 42.01 ± 1.15% → paired Δ = −0.02 pts, p = 0.977 (not
significant); train energy marginally lower for diag_probe (2/3 seeds).
Interpretation: precision weighting is a convergence-speed / conditioning effect — it
accelerates early training but does not change the converged accuracy. (Likely the frozen Π,
estimated from the untrained net, goes stale as the error structure evolves.)
Mode B —
online/ NGD (per-step EMA precision, plain SGD). Π updated every step; SGD so precisionis the per-channel adaptive LR. Finding: SGD/NGD trains far slower than AdamW here (narrow stable-LR
window; momentum helps but doesn't match Adam in equal epochs) and does not reach the AdamW baseline —
reported as a negative result.
Honest framing: precision weighting is a conditioning/robustness/convergence-speed lever. It is
not a path to backprop-level accuracy — PC-ResNet-18 plateaus ~42% (backprop ResNet-18 ≈90%); that
ceiling is a property of PC training on this task, which precision does not move.
Tests
Full suite 229 passed (the
NodeStatepytree change is a clean no-op regression); the 2 new filesadd 15 tests (diagonal/scalar energy,
NodeState.precisionbackward-compat, precision reaching theweight gradients, online EMA update, normalize-after-clip invariant, latent probe). Run with the
venv (CUDA→CPU fallback is normal):
PYTHONPATH=. python -m pytest tests/ -q.Reproduce
Follow-ups (not in this PR)
online/NGD (Mode B) paired sweep at a tuned SGD LR.