Make the observer LR gate two-channel (entropy + drift)#670
Merged
Conversation
observer_matrix_scale throttled a weight matrix to 0.5x whenever its entropy channel was quiet. But that entropy is computed over a single SGD step's worth of weight change, which sits far below thresholds calibrated for scalar convergence loops -- so the "quiet" test was effectively always true. Instrumented over 2,597 steps x 42 matrices, the gate classified "stable" for 100.0% of matrix-steps and emitted the 1.0x full-rate verdict zero times. An adaptive gate that never adapts is a constant 0.5x drag, and it was applying that drag while the loss was plainly still falling (6.09 -> 0.54). The observer buffer here carries only entropy-derived state (entropy, dH, last_entropy, obs_age, prev_dH) -- there is no value channel at all, so slow-but-steady motion is invisible to it. This adds that channel as relative drift ||grad|| / ||w|| and releases the "stable" verdict when the weights are still moving, leaving the oscillating (0.3x) and diverging (0.1x) verdicts untouched since those are genuine instability signals the entropy channel reads correctly. The two channels disagreed on 100% of matrix-steps, and ground truth says drift was right every time: the gate now releases 99.6% and throttles only real oscillation (0.4%). Three-way A/B, fixed checkpoint, 400 windows, identical seeds: step entropy-only gate off two-channel 100 1.554582 1.205151 1.237571 200 1.405466 1.012344 0.975602 300 0.728710 0.542757 0.553253 Two-channel recovers ~all of the gate-off gain (~25% better loss than the status quo) while retaining the ability to throttle a matrix that genuinely settles -- which "off" permanently gives up. Note the A/B cannot separate off from two-channel here: during active learning "release everything" and "off" are the same policy, so the distinction is structural and would only show in a long run where something actually settles. All three arms stay selectable for future A/B: EIGS_OBS_ENTROPY_ONLY=1 (old single-channel), EIGS_OBS_SCALE_OFF=1 (no gating), EIGS_OBS_DRIFT_EPS (release threshold, default 1e-3). Suite: 3164/3164 on the http+model build, [47c] gradcheck still pins the batched path to the per-position oracle. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
The defect
observer_matrix_scale— the production learning-rate gate in the transformer trainer — throttled a weight matrix to0.5xwhenever its entropy channel was quiet. That entropy is computed over a single SGD step's worth of weight change, which sits far below thresholds calibrated for scalar convergence loops, so the "quiet" test was effectively always true.Instrumented over 2,597 steps x 42 matrices:
0.5x"stable"0.3xoscillating1.0xfull rateAn adaptive gate that never adapts is a constant
0.5xdrag — and it was applying that drag while the loss was plainly still falling (6.09 -> 0.54).Why the observer couldn't see it
The observer buffer here carries only entropy-derived state —
entropy, dH, last_entropy, obs_age, prev_dH. There is no value channel at all, so slow-but-steady motion is invisible to it. This is the observer's own failure mode: a meter blind to slow motion certifying a moving system as converged.The fix
Add the missing channel as relative drift
||grad|| / ||w||, and release the "stable" verdict when the weights are still moving. The0.3xoscillating and0.1xdiverging verdicts are left untouched — those are genuine instability signals the entropy channel reads correctly.The two channels disagreed on 100% of matrix-steps, and ground truth says drift was right every time. The gate now releases 99.6% and throttles only real oscillation (0.4%).
Three-way A/B
Fixed checkpoint, 400 windows, identical seeds:
Two-channel recovers ~all of the gate-off gain (~25% better loss than the status quo) while retaining the ability to throttle a matrix that genuinely settles — which "off" permanently gives up.
Honest limit: this A/B cannot separate off from two-channel. During active learning "release everything" and "off" are the same policy, so the distinction is structural and would only show in a long run where something actually settles. What it does establish decisively is that the status quo is the worst of the three.
Controls retained
All three arms stay selectable for future A/B:
EIGS_OBS_ENTROPY_ONLY=1(old single-channel),EIGS_OBS_SCALE_OFF=1(no gating),EIGS_OBS_DRIFT_EPS(release threshold, default 1e-3).Testing
Suite 3164/3164 on the http+model build;
[47c]gradcheck still pins the batched path to the per-position oracle.🤖 Generated with Claude Code