Add eigen_eval_loss: forward-only held-out cross-entropy#671
Merged
Conversation
A language model should be selected on held-out loss. That metric did not exist here, so iLambdaAi ranked checkpoints on a generate-and-parse rate instead -- and that rate could not see the model. Three checkpoints scored an IDENTICAL 60/300 on the greedy parse metric while their held-out loss spanned 3.679 to 1.751, a perplexity gap of 39.6 vs 5.76. Greedy decoding collapses onto frequency attractors regardless of how good the model underneath is, so the metric separates trained from untrained (0 vs 200) and then flatlines across every trained candidate. Deterministic is not the same as informative. The sample size compounded it: at n=30 a 20% rate carries a 95% interval of roughly 8%-39%, so an A/B whose arms differed by 14 points of accuracy read as "no difference". A loss gives one datapoint per window on a continuous scale -- 2000 windows where there were 30 binary trials. Forward only: no backward, no weight update, no requantise, no observer state, and g_model_age/g_training_samples untouched, so scoring a checkpoint never mutates it. Log-softmax subtracts the max before exponentiating; without that a large logit overflows float and returns inf, which would silently poison a mean taken over thousands of windows. Suite [47d] pins it against the property that an untrained model must score ln(vocab_size), since a near-uniform output distribution has that cross-entropy whatever the target is. Measured 7.004 against a predicted 7.011 at vocab=1109, and 2.016 against 2.079 on the vocab=8 fixture. One anchor catches a missing max-subtraction, a mis-indexed target, a softmax summed over the wrong axis, and sign errors at once. Also checks out-of-range targets return the -1 sentinel rather than reading past the logits array. Suite: 3168/3168 on the http+model build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Optional 4th argument to eigen_generate. Keeps the smallest set of tokens whose cumulative probability reaches p, so the candidate set adapts to the model's confidence, instead of the fixed top-k=40 that admits 39 near-zero tokens where the distribution is sharp and truncates real choices where it is flat. Absent or outside (0,1) keeps the historical top-k path, so existing callers and every recorded number are unchanged -- decoding policy is opt-in, not a silent default flip. Honest scope: this was written to fix a repetition degeneracy and it does not. The model assigns its repeat-loop continuation p=0.987 (loss 0.0127, alternatives at ~3e-8), so a 0.95 nucleus keeps exactly one token and produces output identical to top-k, token for token. The degeneracy is a genuine self-consistent fixed point in the model, not a decoding artifact. Nucleus sampling is still the better default policy and is worth having; it just is not the fix for that. Testing needed care. Generation draws from a global rand(), so two calls with the SAME policy already disagree on most positions -- measured 3/40 identical. Comparing token sequences therefore proves nothing about the policy, and an earlier version of this check "verified" top-p by observing 0/40 matches, which the control shows is indistinguishable from noise. The robust signal is the statistical signature: on a near-uniform model top-p and top-k emit visibly different numbers of DISTINCT tokens (61 vs 257 in 300 tokens at vocab=1109), which is a property of the candidate set rather than of any rand() draw. Suite [47e] asserts a restrictive nucleus narrows the emitted set and that out-of-range top_p falls back to top-k without emptying the set. qsort comparator is NaN-safe: a NaN comparison yields 0 rather than an inconsistent ordering, which qsort is permitted to fault on. Suite: 3172/3172 on the http+model build. 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.
Why
A language model should be selected on held-out loss. That metric didn't exist in the model extension, so iLambdaAi ranked checkpoints on a generate-and-parse rate — and that rate cannot see the model.
Three checkpoints scored an identical 60/300 on the greedy parse metric while their held-out loss spanned 3.679 to 1.751 (perplexity 39.6 vs 5.76):
Greedy decoding collapses onto frequency attractors regardless of how good the model underneath is, so the metric separates trained from untrained and then flatlines across every trained candidate. Deterministic is not the same as informative.
Sample size compounded it: at n=30 a 20% rate carries a 95% interval of roughly 8%–39%, so an A/B whose arms differed by 14 points of accuracy read as "no difference."
What
eigen_eval_loss of [prompt_ids, target_id]→ cross-entropy in nats.Forward only — no backward, no weight update, no requantise, no observer state, and
g_model_age/g_training_samplesuntouched, so scoring a checkpoint never mutates it.Log-softmax subtracts the max before exponentiating; without it a large logit overflows float and returns
inf, which would silently poison a mean taken over thousands of windows.Testing
Suite
[47d]pins it against the property that an untrained model must scoreln(vocab_size)— a near-uniform output distribution has that cross-entropy whatever the target is:That single anchor catches a missing max-subtraction, a mis-indexed target, a softmax summed over the wrong axis, and sign errors simultaneously. Also verifies out-of-range targets return the
-1sentinel rather than reading past the logits array.Suite: 3168/3168 on the http+model build.
🤖 Generated with Claude Code