Skip to content

Emit affine maps from AutoTP layers - #8519

Open
Achyuthan-S wants to merge 2 commits into
deepspeedai:masterfrom
Achyuthan-S:step4-emit-affine-map
Open

Achyuthan-S wants to merge 2 commits into
deepspeedai:masterfrom
Achyuthan-S:step4-emit-affine-map

Conversation

@Achyuthan-S

@Achyuthan-S Achyuthan-S commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Step 4 of the staging plan in #8252: AutoTP layers now emit an affine map, and the converter reads it on a path a real job takes rather than one a test supplies.

Follows #8385 (the IR, the lowering constructors, the converter branch) and #8477 (the scale fix). Scope here is contiguous splits and replicated parameters; the fused QKV and Yuan shared-QK layouts are next and still carry unsupported_reason, so they emit no map and are unaffected.

Where a map is built, and why it is not where you would expect

The producer cannot build a map from what collect_autotp_universal_checkpoint_info sees. The conversion metadata carries eight fields and per-rank partition sizes are not among them — those were deliberately restore-only. But the layer does have them: _freeze_partition_sizes resolves them via get_shard_size_list while the layer is built, and they are not recoverable later from a shape alone.

So the map is derived at mark time, in _set_param_uc_meta, which already receives partition_sizes, sub_param_shard_widths, logical_shape and replicated, and has tp_world_size in scope. That puts the derivation in one place rather than in each of the seven _mark_uc_metadata implementations, and collect_... then gathers what the layers produced.

A layout that is not describable yet returns None, so conversion falls back to the categories.

The map and the categories have to coexist

A checkpoint carrying an affine map also carries the existing pattern lists, so a converter predating the map still reads it — that is the additivity §6.3 promises.

The consequence is that the converter which prefers the map never consults those branches, which leaves their patterns looking unused and fails a strict conversion. They are superseded, not unused, so the affine branch now marks them consumed. This is the compatibility question raised as "one boundary for review" in #8385; it turns out both have to be present and the reader has to account for the other.

This only surfaced end to end. The producer's output was correct in isolation, and the emitted maps matched a layout already verified by a passing resume — the failure was a strict-mode assert on rank 0 during conversion, which presented as rank 1 blocking on the following barrier.

Tests

TestAffineMapProducer builds a real AutoTP engine at TP2 and requires the producer to emit exactly the four maps the resume fixture previously supplied by hand. Those maps are not a guess: a full train → save → convert → resume cycle reproduces uninterrupted training through them, so matching them is evidence rather than self-consistency.

affine_resume_checkpoint now takes its layout from the producer instead of injecting one, so the four save-convert-resume cases exercise the metadata a real job writes. All four pass — TP2 → TP1 and TP2 → TP2, through both the legacy and affine paths, compared against uninterrupted training on logits, losses, gradients, FP32 weights, both Adam moments and step counters.

Validated on CPU/gloo (DS_ACCELERATOR=cpu LOCAL_SIZE=4): 45 passed across the resume matrix, the producer test and the affine unit suite. The resume harness is @0z5a's, from #8474.

Related: #8252, #8230.

cc @delock

Derive a parameter's affine map where the layer already knows its per-rank
extents. The conversion metadata does not carry them -- they are resolved by
_freeze_partition_sizes while the layer is built and are not recoverable from a
shape alone -- so the map is built at mark time rather than at collection.

Publish the maps alongside the existing pattern lists, so a converter that
predates them still reads the checkpoint through the categories. The converter
that prefers a map therefore has to mark those patterns as superseded, or a
strict conversion rejects them as unused.

The resume fixture now takes its layout from the producer instead of supplying
one, so the four save-convert-resume cases exercise the metadata a real job
writes.

Signed-off-by: Achyuthan Sivasankar <achyuthan.sivasankar@gmail.com>
Copilot AI lite review requested due to automatic review settings September 14, 2026 23:49

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 129087a461

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread deepspeed/module_inject/layers.py Outdated
'original_shape': _normalize_uc_shape(original_shape),
'is_bias': is_bias,
'replicated': replicated,
'affine_map': affine_map,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the conversion metadata test in sync

Adding affine_map unconditionally changes every _build_param_uc_restore_meta() conversion dictionary, but tests/unit/runtime/tensor_parallel/test_autotp_universal_checkpoint.py:235-244 still compares that dictionary exactly without this key, so the existing unit test now fails even when the argument uses its default. Update that test—preferably to assert only the stable fields rather than the entire private dictionary—or omit the key when no map is supplied.

AGENTS.md reference: AGENTS.md:L30-L32

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Schema compatibility and embedding-path coverage issues remain, and the producer test does not enforce exact output.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds affine-map emission for AutoTP checkpoint metadata and uses producer-generated maps during conversion while preserving legacy categories.

Changes:

  • Derives maps for supported contiguous and replicated layouts.
  • Updates converter compatibility handling.
  • Expands producer and resume test coverage.
File summaries
File Summary Final review comments
tests/unit/checkpoint/test_autotp_uc_checkpoint.py Tests producer-generated layouts and resume behavior. Nit (2 votes): Assert the emitted pattern set exactly matches expectations.
deepspeed/module_inject/layers.py Produces and collects affine maps. Moderate (2 votes): affine_map=None changes the metadata schema and breaks an existing test. Moderate (1 vote): The embedding path does not provide affine maps for contiguous row-sharded layouts.
deepspeed/checkpoint/ds_to_universal.py Consumes affine maps alongside legacy patterns. No final comments.
Review details

Suppressed comments (1)

deepspeed/module_inject/layers.py:713

  • This collector only serializes conversion_meta['affine_map'], but the AutoTP embedding path still constructs metadata directly in auto_tp.py:_slice_embedding without supplying an affine map. Consequently a contiguous row-sharded embedding continues through the category fallback and this producer does not cover all contiguous AutoTP layouts described by the PR; derive and pass the map for that path as well.
            affine_map = conversion_meta.get('affine_map')
            if affine_map is not None:
                affine_maps[pattern] = affine_map.to_dict()
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread deepspeed/module_inject/layers.py Outdated
'original_shape': _normalize_uc_shape(original_shape),
'is_bias': is_bias,
'replicated': replicated,
'affine_map': affine_map,
Comment on lines +1760 to +1763
for pattern, want in expected.items():
assert pattern in maps, f"producer emitted no affine map for {pattern}"
assert maps[pattern] == want, (f"emitted map for {pattern} differs from the layout the resume "
f"fixture verifies:\n emitted {maps[pattern]}\n expected {want}")
@delock
delock self-requested a review September 15, 2026 00:02
Adding the key unconditionally changed the conversion schema for every
parameter, including layouts with no map to describe. Emit it only where there
is one, so an existing layer publishes exactly what it published before, and
store it as plain scalars like the rest of the schema.

Require the producer test to match the expected pattern set exactly, so an
unintended extra map is a failure rather than something the test ignores.

Signed-off-by: Achyuthan Sivasankar <achyuthan.sivasankar@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants