Skip to content

spec(ml): eval_hslm -- held-out loss of an HSLM checkpoint, last position and full sequence (Refs #10372) - #10467

Draft
gHashTag wants to merge 7 commits into
masterfrom
feat/eval-hslm
Draft

gHashTag wants to merge 7 commits into
masterfrom
feat/eval-hslm

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Refs #10372 (epic #10024, own_model cell 25). Stacked on #10387 (feat/train-hslm, head 45ea9a9).

What it measures

specs/ml/eval_hslm.t27 is a t27 program that scores a trained HSLM checkpoint on held-out tokens (/tmp/hslm/valid.tok) two ways, on the same windows of 28 tokens (27 inputs, targets are tokens 1..27):

  • last_loss / last_ppl: cross-entropy of the last position only. This is the number train_hslm prints as valid_loss and the way run R5's PPL 2.96 was reported.
  • full_loss / full_ppl: cross-entropy at every position (27 terms per window, mean over all of them).

Position 26's term is the same number in both, so full_loss is what the model does on the 26 positions the trainer never scores.

main() reads /tmp/hslm/eval.cfg (key=value lines: step, windows, stride, check; defaults 0 / 243 / 0 / 0), loads ckpt_<step>.state and valid.tok, and prints and writes to /tmp/hslm/eval_<step>.txt:

eval step <s> windows <n> positions <p> last_loss <x> last_ppl <x> full_loss <x> full_ppl <x>

stride=0 spaces the windows evenly exactly as tr_valid_loss does; a set stride below 28 is raised to 28 so windows never overlap. A window that does not fit is refused and not counted. check=1 also runs the trainer's own tr_valid_loss and prints both numbers and whether they are the same bits. The checkpoint must be the .state file: the MSLH .bin holds only the ternary shadows and biases.

It changes no training path

use ml::train_hslm reaches the forward pass, cross_entropy, tr_load_state, tr_read_tok, tr_valid_loss and the formatters. specs/ml/train_hslm.t27 is not touched: zero lines changed, nothing made pub. The diff is two new files, specs/ml/eval_hslm.t27 and .trinity/seals/ml_eval_hslm.json. What the new module declares itself: the shape consts, the module var state the spliced code binds to (use_resolve splices no var), and two kernels (ternary_matmul, trit_int; train_hslm's speed-1 forms, tested there to give the reference logits to the bit) because the name is declared by both nn/hslm and train_hslm and is not resolved.

Evidence (Railway lab, t27c 0.5.2 /root/dupdecl/target/release/t27c, zig 0.16.0)

Tests: t27c test-report --specs-dir /root/eval-hslm/specs specs/ml/eval_hslm.t27: 10 tests, 10 pass, 0 vacuous. Seal: t27c seal --save (tests 10/10 pass), saved .trinity/seals/ml_eval_hslm.json.

Negative controls (break one line, run test-report, restore; spec hash back to 09c6729a... each time):

Broken line Tests that failed
p + 1 == CONTEXT_LEN -> p + 2 == ... last_loss_equals_the_trainers_valid_loss_on_the_same_windows
target tokens[start + p + 1] -> tokens[start + p] position_26_of_the_full_measure_is_the_last_position_loss, last_loss_equals_the_trainers_valid_loss...
ev_mean_full divides by ev_npos + 1 uniform_logits_give_ln_729_for_the_full_measure, the_full_loss_is_the_mean_of_27_terms_and_not_only_the_last
start + EV_WINDOW > len -> start + CONTEXT_LEN > len a_window_shorter_than_28_tokens_is_refused
default windows 243 -> 242 config_defaults_when_nothing_is_set

Binary: t27c compile specs/ml/eval_hslm.t27 --backend ml (0 UNRESOLVED), then zig build-exe -O ReleaseFast, run under nice -n 19 while the training run stayed live.

Cross-check against /tmp/hslm/run.log (243 evenly spaced windows, check=1):

eval step 2000 windows 243 positions 6561 last_loss 5.6637 last_ppl 288.22 full_loss 6.2018 full_ppl 493.62
check tr_valid_loss 5.663717 last_loss 5.663717 same_bits 1      (run.log step 2000: valid_loss 5.6637 valid_ppl 288.22)
eval step 4000 windows 243 positions 6561 last_loss 4.3820 last_ppl 80.00 full_loss 6.5208 full_ppl 679.15
check tr_valid_loss 4.381989 last_loss 4.381989 same_bits 1      (run.log step 4000: valid_loss 4.3820 valid_ppl 80.00)

Both match run.log to the printed digits, and to the bit against tr_valid_loss.

2000 windows (54000 positions, 126 s for step 2000 on a loaded 48-core box):

eval step 2000 windows 2000 positions 54000 last_loss 5.7455 last_ppl 312.78 full_loss 6.1998 full_ppl 492.65
eval step 4000 windows 2000 positions 54000 last_loss 4.4967 last_ppl 89.72 full_loss 6.5372 full_ppl 690.38

Reading: the last-position loss falls 5.75 -> 4.50 between steps 2000 and 4000, while the full-sequence loss rises 6.20 -> 6.54 (PPL 493 -> 690). The trainer only scores position 26, so the other 26 positions are not being trained on.

Only eval_*.txt and a temporary eval.cfg (removed) were written in /tmp/hslm; no ckpt_* or run.log was touched.

Draft, label no-auto-merge.

🤖 Generated with Claude Code

gHashTag and others added 7 commits October 18, 2026 17:32
… and LAMB, writes MSLH checkpoints (Refs #10372)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the flat vector, xorshift guards zero (Refs #10372)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…efs #10372)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion and full sequence (Refs #10372)

main() reads /tmp/hslm/eval.cfg (step, windows, stride, check), loads
ckpt_<step>.state and valid.tok through train_hslm's own readers, and scores
non-overlapping 28-token windows two ways: the last position only (the
trainer's valid_loss) and every position (27 terms per window). It prints and
writes eval step <s> windows <n> positions <p> last_loss .. last_ppl ..
full_loss .. full_ppl .. to /tmp/hslm/eval_<step>.txt.

train_hslm.t27 is reached through use and is not touched. 10 tests, sealed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-10-18 13:24:17 UTC

Summary

Status Count
Total Open PRs 50
PRs with Failing Checks 50
PRs with All Checks Green 0
READY 0
FAILING 50
PENDING 0
NO CHECKS YET 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=fad12492937a != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

This was referenced Oct 18, 2026
This was referenced Oct 18, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-auto-merge Opt out of the issue gate's default squash auto-merge (issue-gate.yml)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant