Repository navigation
Save the calibrated weights from the final optimiser step - #594
Merged
Merged
Conversation
calibrate_local_areas saved and returned the weights only when epoch % 10 == 0, so a 512-epoch build shipped the state after step 511 and a 32-epoch build the state after step 31, dropping the last update (#456). Save at the last epoch too, in both the verbose and the non-verbose loop, so the saved file, the returned dataset and the logged fit are the final optimiser state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On every logged row, and so on the last one that describes the shipped weights, training_loss, validation_loss and the two pct_close columns come from the dropout-perturbed weights before the optimiser step, while the saved weights, saved_weights_loss and the per-target columns come after it. Note that saved_weights_loss is the consistent loss to read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The save test now checks, for every epoch count from 1 to 40 in both the verbose and the non-verbose loop (a Hypothesis property, with the TESTING build's 32 verbose epochs pinned), that: - the saved weight file is exactly exp(trained parameters) times the country mask; - the returned household weights are its column sums; - the log CSV records every tenth epoch and the last, so its last row is epoch epochs - 1, and that row's saved_weights_loss equals the training loss of the saved weights, recomputed independently in float64. The toy now uses a two-country mask with zeros, so exp(parameters) no longer equals the saved weights and a save that dropped the mask fails. The module docstring describes both save bugs, and the docstrings and changelog number epochs from 0, as the log and #456 do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… of #594) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 18, 2026
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.
Do not merge on its own. Once approved on its gates, this lands with the d833 uk-data batch on Max's go. It is part of workstream W3 of the Autumn Budget 2026 CI plan (the final-fit gate measures the shipped weights, so they must be the final ones).
Fixes the "weights ship from epoch 510 of 512" item in #456.
Why
Epochs and optimiser steps are numbered from 0 here, as in the calibration log's
epochcolumn: epochetakes stepe, so a 512-epoch run takes steps 0–511.calibrate_local_areassaved the weight file, set the returned dataset'shousehold_weightand logged performance only whenepoch % 10 == 0. The weights it shipped were therefore the state after the last step whose epoch is a multiple of 10, and the steps after that were dropped:What changes
log_performancesays which log columns describe which weights (see "Reading the last log row").Effect on the data
Releases now ship the state after step 511 instead of 510, and TESTING builds the state after step 31 instead of 30. So each area's log-weights take one more Adam update (learning rate 0.1). The effect on aggregates has not been measured locally; it needs a full build. Expect small diffs in calibration outputs in the batch. The batch's build-and-test run and its comparison will show them.
Reading the last log row
On every logged row,
training_loss,validation_lossand the twopct_closecolumns come from the dropout-perturbed weights before that epoch's optimiser step. The saved weights,saved_weights_lossand the per-target columns come after it. So on the last row, the one people read for the shipped fit,saved_weights_lossis the consistent loss. Main already logs this way; the PR adds the comment and doesn't change what is logged.Invariants (tested)
test_saved_returned_and_logged_weights_are_the_final_optimiser_stateis a Hypothesis property. It draws epoch counts from 1 to 40 and either loop: 20 examples, plus the TESTING build's 32 verbose epochs, which are pinned. The toy uses a two-country mask with zeros, so the saved weights differ from exp(parameters). For every draw:epoch == epochs - 1.saved_weights_lossequals the training loss of the saved weights, recomputed independently in float64 (relative tolerance 1e-5).Checked outside the suite, with scripts kept in the reviews folder (
w3-uk-data-gates-2026-10-11/594-checks/), not committed:calibrate.py:* rdropped from the saved weights in both loops, in the verbose loop only, or in the non-verbose loop only;saved_weights_loss;* rdropped in both loops.Interactions
#507 (seeded dropout) edits the same function. It changes the imports, the signature and
dropout_weights. This PR changes the save conditions and adds a comment inlog_performance.git merge-treeof this head with #507's head (60a87ff7) is clean, and the merged tree passespytest policyengine_uk_data/tests/test_calibrat*.py: 28 passed.Tests
From runs at
e13f22b8, Python 3.13, serial:pytest policyengine_uk_data/tests/test_calibrat*.py: 18 passed. The count was 33 at677f8039. It fell because the 16 parametrized cases are now one property test.pytest policyengine_uk_data/tests/test_changelog_fragment_check.py: 88 passed.ruff format --check .: 264 files already formatted.axiom: n/a: calibration build; no policy change
🤖 Generated with Claude Code