Build models from a checkpoint without the random parameter initialisation - #1257
Build models from a checkpoint without the random parameter initialisation#1257Innixma wants to merge 2 commits into
Conversation
|
hey! Sorry for the drive-by comment, I just happened to be discussing this in the office yesterday and then spotted your PR. I'm just wondering if you also tried PyTorch's suggested approach of using the meta device? That won't affect other threads in the process and won't miss future initialisation functions, but maybe it doesn't work for us. |
|
Below is from Claude, but TLDR it was actually a bit slower (and adds 750ms cold start time) and also unsafe to do without adding guards around it. Do we have concerns about other threads in the process for this scenario? I'm unsure how we would test all the cases. Claude: Thanks, good pointer. I tried it. Same 52 small datasets (23 binary, 16 multiclass, 13 regression), a single estimator fit once per dataset through the AutoGluon wrapper on one GPU, every model build timed in-process:
Predictions are bit-identical between skip-init and the guarded meta build on all 52 datasets. The build is about 2 s of the 15 s either way, and the 0.7 s gap between the two runs is within run-to-run noise (a repeat of the skip-init run the day before came out at 14.6 s). The bare meta variant fails with In a microbenchmark the meta build was ~2x faster (about 15 ms vs 30 ms per build), but that did not survive the real path. I agree with both of your points in favour of meta (no process-wide patch, no list of init functions to keep current); given no end-to-end gain and the two new failure modes, I'll stick with skip-init here. Worth revisiting if host memory during builds becomes the constraint, since assign avoids the second copy of the weights. |
|
ah thank you this is very useful! If we don't need to get this in urgently, I suggest we fix the architecture so it works with the meta device. From a very quick look, I think we need to make this line an For me, I'd prefer to use the recommended approach even if it is a bit slower, as I'm worried that patching creates several tricky latent bugs:
For people who want to go really fast, we can help them use the What do you think? |
…ation Every parameter and persistent buffer of a model built in `_build_model` is overwritten by the strict `load_state_dict` that follows, so the layers' `reset_parameters` drew random weights only to discard them: about a quarter of a second per build on a full-size checkpoint, paid at every fit. The `torch.nn.init` functions are no-ops while the architecture is constructed and restored afterwards. A test checks that the loaded model's parameters and buffers equal the checkpoint's exactly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ELdutHiUqkvynzEP7EnPsi
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ELdutHiUqkvynzEP7EnPsi
186aeca to
ef09456
Compare
Stacked on #1255.
_build_modelconstructs the architecture and then loads the checkpoint with a strictload_state_dict, which overwrites every parameter and persistent buffer. The construction still ran every layer'sreset_parameters, drawing random weights only to discard them: about a quarter of a second per build on a full-size checkpoint, paid at everyfitand, under bagging, at every child.The architecture is now built inside a small context manager that turns the
torch.nn.initfunctions into no-ops for the duration and restores them afterwards, so the tensors are allocated but not filled before the load. The result is the same model: the architectures register no non-persistent buffers, and the tensors they initialise themselves are parameters, so all of them come from the checkpoint. A test loads small v2 and v3 checkpoints and checks that every named parameter and buffer equals the source model's exactly; a second test checks the patch is scoped to the block.On an 83.5M-parameter checkpoint the build goes from 287 ms to 19 ms, most of the remainder being the state-dict copy; the reference-prediction consistency tests pass unchanged. One caveat for reviewers: the context manager patches module-level functions, so a model constructed concurrently in another thread of the same process during that window would also skip its initialisation. The library builds models on the calling thread, and the parallel paths use separate processes, so no such window exists in the code as it stands.
🤖 Generated with Claude Code
https://claude.ai/code/session_01ELdutHiUqkvynzEP7EnPsi