Don't rewrite packed modules as float weights when resuming - #2386
Shubham-Padkonde wants to merge 3 commits into
Conversation
A run resumed through AR_RESUME_DIR skips the blocks a previous run already quantized, so those modules are still plain `nn.Linear` in the model tree while their packed tensors sit in the shards the crashed run flushed. `finalize()`'s remaining-weights pass deduplicates by exact tensor name, so `<layer>.weight` never matched the saved `<layer>.qweight` and the stale floating-point weight was written on top of the packed one, silently duplicating every module of every skipped block. Two changes: - Adopt the previous run's shards at the start of `finalize()`. Discovery previously ran on the first `_flush_shard()`, which `finalize()` only reaches after the remaining-weights pass, so that pass could not see anything the crashed run had written. - Skip a module's unpacked `weight` when its packed tensors are already saved, and log how many were skipped. Fixes intel#2350 Signed-off-by: Shubham Padkonde <shubhampadkonde12@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
/azp run Unit-Test-CUDA-AutoRound |
|
Azure Pipelines successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved critical resume-detection issues and incomplete shard metadata remain.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 2
Open (2)
What changed in this PR
This PR fixes duplicated floating-point weights when resuming packed shard exports.
Changes:
- Discovers prior shards before finalization.
- Skips stale unpacked weights for packed modules.
- Adds regression coverage.
| File | Summary | Review findings |
|---|---|---|
auto_round/compressors/shard_writer.py |
Adds resume shard discovery and packed-weight filtering. | Critical (3 votes): Compare transformed layer names when detecting resumed packed modules. Moderate (1 vote): Include adopted shards in index size and element statistics. Critical (1 vote): Safely adopt finalized resumable shards to prevent overwriting prior output. |
test/unit/common/utils/test_shard_writer.py |
Adds regression coverage for resumed packed-module handling. | No findings. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@Shubham-Padkonde Could you please address these high-priority comments? |
Signed-off-by: Shubham Padkonde <shubhampadkonde12@gmail.com>
|
Published the signed checkpoint-recovery follow-up in f31a1b2. It handles finalized shards and transformed resume names, with regression coverage. The prepared patch passed 65 tests and pre-commit checks before sign-off; hosted CI still needs to validate the new commit. Prepared with Codex assistance. |
|
/azp run Unit-Test-CUDA-AutoRound |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run Unit-Test-CUDA-AutoRound |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Signed-off-by: Shubham Padkonde <shubhampadkonde12@gmail.com>
|
/azp run Unit-Test-CUDA-AutoRound |
|
Azure Pipelines successfully started running 1 pipeline(s). |

Fixes #2350
Problem
When a run is resumed through
AR_RESUME_DIR, the blocks a previous run already quantized are skipped, so their modules are still plainnn.Linearin the model tree while their packed tensors sit in the shard files the crashed run flushed.ShardWriter.finalize()'s remaining-weights pass deduplicates by exact tensor name, so<layer>.weightnever matches the saved<layer>.qweight: the stale floating-point weight is written on top of the packed one. Every module of every skipped block ends up in the checkpoint twice, with no warning. The issue reports a Qwen3.8-27B W4A16 artifact coming out at 47.4 GB instead of ~17.7 GB, with 318/407 modules duplicated.There is a second half to it: discovery of the previous run's shards (
_discover_existing_shards) is deferred to the first_flush_shard()call, whichfinalize()only reaches after the remaining-weights pass. So that pass ran with an empty_all_savedfor anything the crashed run wrote — even tensors whose names match exactly would have been written twice.Fix
finalize(), before deciding which weights are still missing.output_diris already the final path there, which is the reason the call was deferred out of__init__in the first place.weightwhen packed tensors for that module are already saved (qweight/weight_packed), and log how many were skipped so this is no longer silent.Modules that were never packed are unaffected, and non-resuming runs never reach the new discovery call because it stays gated on
AR_RESUME_DIR.Review follow-up
Resuming an export can leave plain model weights in memory while their packed replacements already exist on disk. Adopt prior temporary or finalized shards before the remaining-weight pass, compare names in the serialized checkpoint namespace, and skip unpacked weights whose packed replacements are already saved.
The review follow-up also includes recovered tensors in the index's total_parameters and total_size. Safetensors counts come from validated header metadata without materializing payloads; PyTorch shards are inspected on the meta device. Ordinary non-resuming behavior is unchanged.
Original validation
The results in this section cover the initial implementation; current statistics validation is recorded below.
test_finalize_skips_unpacked_weight_of_resumed_packed_modulewrites a shard in the crashed run's temp layout containinglinear.qweight/linear.scales/linear.bias, pointsAR_RESUME_DIRat it, and finalizes a writer whose model still holds the plainnn.Linear.Without the change:
With it, all 8 pass and
proj_out.weight— a module that was never packed — is still saved.The same 5 failures (
test_alg_ext,test_layer_config_resolution,test_weight_handler, twoTestIsImmediateSavingModecases) reproduce on an unmodifiedmainwith the same selection — 922 passed there, so this change adds a test and breaks nothing.black,isortandruffare clean.Current statistics validation
007955ef5ee1ba8c702e378d0aaffd32838bdf0c.The published commit is the contributor-signed commit and matches the exact two-file patch tested above.
Disclosure: this change was written by Claude Code (Claude Opus 5) working as my agent, at my direction. The test, lint and reproduction output quoted above comes from real runs in my local environment; I am accountable for what is submitted here and will follow up on review feedback.
🤖 Generated with Claude Code
Codex prepared and tested the checkpoint-recovery and statistics review follow-ups.