Skip to content

Describe the GPTBigCode and Yuan shared-QK layouts - #8575

Open
Achyuthan-S wants to merge 3 commits into
deepspeedai:masterfrom
Achyuthan-S:step4b-fused-yuan
Open

Achyuthan-S wants to merge 3 commits into
deepspeedai:masterfrom
Achyuthan-S:step4b-fused-yuan

Conversation

@Achyuthan-S

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

Copy link
Copy Markdown
Contributor

Three of the four layouts in AUTOTP_UNSUPPORTED_PARAMETER_PATTERNS now describe themselves, so universal checkpoint conversion stops refusing them. Follows #8519, which made AutoTP layers emit affine maps for the layouts a generic rule already covered.

codegentype is not included — its reshape-and-interleave needs more than a segment list or a block selection, and it is the next piece of work.

What was actually missing

Both refusals came from the same limit: a parameter carries one partition dimension, which cannot say that one block of it is sharded while the next is held whole, nor that a rank takes blocks which are not adjacent.

GPTBigCode splits the query rows across ranks and hands every rank the whole key/value block. segmented_map describes a parameter as an ordered list of segments, each split or replicated, so the two end up as pieces that differ in which ranks hold them rather than in kind. That is the case the per-piece location set was introduced for in #8385.

Yuan shared-QK gives a rank the value heads that pair with its query heads, plus the matching second-half heads. Those land in two runs rather than one span, so block_gather_map takes a selection of equal-sized blocks and merges consecutive ids into single pieces.

Why the tests are worth reading

A layer builds its map analytically, from head counts and the tp degree. The test harness derives one independently by running the real partition function on a marker tensor and reading back where each slice came from. The tests require the two to agree, so what ships is checked against what the partition code actually does rather than against a reading of it.

I confirmed they fail when the map is wrong: corrupting segmented_map so the replicated segment claims a single owner produces two failures rather than passing quietly.

shared_qk_value_head_ids is extracted from shard_value_with_share_qk and used by both the partition and the map, so the description cannot drift from the layout it describes.

What stays unsupported, deliberately

  • A layout with no head counts. Describing Yuan needs num_kv_heads; without it the layer publishes the refusal rather than guessing, and there is a test for that.
  • The Yuan o_proj bias. Its partition pre-divides by the world size and replicates, which needs a scaled piece — and that code path does not currently produce a usable bias, so there is no working layout to describe.

Validation

Validated on CPU/gloo (DS_ACCELERATOR=cpu LOCAL_SIZE=4): 90 passed across the affine suite, the producer and coverage tests, the resume matrix and tests/unit/runtime/tensor_parallel/.

Related: #8252, #8230.

cc @delock

Copilot AI lite review requested due to automatic review settings September 17, 2026 15:25

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

Critical partition and map-width mismatches can cause incorrect or lost data.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds affine checkpoint maps for GPTBigCode and Yuan shared-QK layouts, enabling universal checkpoint conversion for previously unsupported weights.

Changes:

  • Adds segmented and block-gather affine map constructors.
  • Integrates GPTBigCode/Yuan metadata generation.
  • Adds shared head-selection logic and validation tests.
File summaries
File Description
tests/unit/checkpoint/test_affine_shard_map.py Tests map correctness and fallback behavior.
deepspeed/module_inject/layers.py Publishes GPTBigCode and Yuan maps.
deepspeed/module_inject/fusedqkv_utils.py Shares Yuan head-selection logic.
deepspeed/checkpoint/affine.py Adds affine map constructors.
Review details
  • Files reviewed: 4/4 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 on lines +227 to +235
head_per_rank = num_heads // world_size
q_head_start = rank * head_per_rank
v_head_ids = []
index = 0
while index < head_per_rank:
v_head_ids.append(q_head_start // 2)
q_head_start += 2
index += 2
v_head_ids.extend([head + num_heads // 2 for head in v_head_ids])
Comment thread deepspeed/module_inject/layers.py Outdated
n_embd = self.tp_meta.n_embd
if not shape or n_embd is None or n_embd >= shape[0]:
return None
return segmented_map(shape, [(n_embd, False), (shape[0] - n_embd, True)], 0, self.tp_world_size)
@delock
delock self-requested a review September 18, 2026 01:05

@delock delock left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the PR — the shared-source design (shared_qk_value_head_ids used by both the partition and the map) and the marker-tensor verification methodology are excellent.

One blocking item (missing GPTBigCode wiring test) and three non-blocking suggestions below.

if self._subparam_shard_widths is not None:
set_fused_qkv_shard_state(self.fused_module.module, self._subparam_shard_widths, self.tp_index)

def _segmented_affine_map(self, shape):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Missing wiring test for GPTBigCode

test_yuan_weight_is_no_longer_refused_by_conversion verifies the full wiring for Yuan (the layer actually publishes AFFINE_MAP_PARAMS and is removed from AUTOTP_UNSUPPORTED_PARAMETER_PATTERNS), but there is no equivalent for bigcodetype. The geometry tests validate segmented_map itself, but nothing fails if fused_LinearLayer._segmented_affine_map never gets called (e.g. a hook-name typo or get_fused_qkv_type returning something else in that context) — the layout would silently stay refused while all tests stay green.

Could you mirror the Yuan test for a fused_LinearLayer with a GPTBigCodeBlock module? The pattern should transfer directly.

Comment thread deepspeed/module_inject/layers.py Outdated
_unsupported_uc_reason = ("Yuan shared-QK tensor parallelism selects noncontiguous head groups that universal "
"checkpoint conversion cannot currently describe")

def _shared_qk_affine_map(self, shape):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Duplicated _shared_qk_affine_map in the two Yuan classes (non-blocking)

Yuan_LinearAllreduce and Yuan_LinearLayer carry near-identical copies of _shared_qk_affine_map (guards, completeness check, and its comment included); the only real difference is the partition axis. This is essential rather than incidental duplication — both copies encode the same invariant (the head selection must partition the head set) and will need to change together. The len(shape) <= 0 leftover in the other copy shows how they can drift. A shared helper taking partition_dim would collapse them to one-liners at each call site.

Comment thread deepspeed/checkpoint/affine.py Outdated
scale=entry.get('scale', 1.0))


def segmented_map(shape, segments, partition_dim, tp_degree, split_widths=None):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Consider making split_widths required (non-blocking)

The only production caller (fused_LinearLayer._segmented_affine_map) already passes explicit widths; the split_widths=None fallback is only exercised by one test. Since an inferred split can read the wrong rows while still covering the tensor (the exact bug the second commit fixed), removing the fallback would close that door for future callers. _even_split_sizes has no other callers, so it can be deleted along with the fallback — the one test call site just passes explicit widths.

Comment thread deepspeed/module_inject/layers.py Outdated
_unsupported_uc_reason = ("Yuan shared-QK tensor parallelism selects noncontiguous head groups that universal "
"checkpoint conversion cannot currently describe")

def _shared_qk_affine_map(self, shape):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Dead condition in Yuan_LinearLayer._shared_qk_affine_map (non-blocking)

len(shape) <= 0 is always false for a tensor shape (ndim >= 1). It looks like a copy-paste leftover from the len(shape) <= 1 guard in Yuan_LinearAllreduce (which protects the shape[1] access). Harmless here since shape[0] is valid for 1-D, but the check as written does nothing — either drop it or restore the intended bound.

@delock

delock commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Hi @Achyuthan-S , thanks for the PR. Being able to handle GPTBigCode and Yuan really show the power of this work. The comments is left above. Thanks!

These were refused by conversion because a single partition dimension cannot say
that one block of a parameter is sharded while the next is held whole, nor that
a rank takes head blocks which are not adjacent.

Both are describable as pieces. GPTBigCode splits its query rows and replicates
the key/value block, so its pieces differ in which ranks hold them. Yuan selects
the value heads pairing with a rank's query heads, which fall into two runs.

The layers build these from head counts and the tp degree, and the tests require
that to agree with what the partition functions actually produce. A layout
without the head counts to describe itself stays unsupported rather than
guessing.

Signed-off-by: Achyuthan Sivasankar <achyuthan.sivasankar@gmail.com>
The query rows of a GPTBigCode weight are not divided evenly: the widths follow
the head count and the grain size, so a map built by dividing reads the wrong
rows while still covering the tensor. Segments now carry the widths the
partition itself computed, and a width list that does not add up is refused.

Yuan's head pairing only partitions the heads when each rank takes an even
number of them. Otherwise two ranks hold the same head, and a piece naming a
single owner contradicts the one beside it, so publish no map instead.

Signed-off-by: Achyuthan Sivasankar <achyuthan.sivasankar@gmail.com>
Add the GPTBigCode equivalent of the Yuan wiring test. The geometry tests cover
the constructor, so a hook that never fires would leave the layout refused with
everything still green; renaming the hook now fails this test.

Collapse the two copies of the shared-QK map builder into one helper taking the
partition axis. They encoded the same invariant and had already drifted -- one
copy guarded len(shape) <= 0, which is never true.

Require the split widths rather than falling back to an even division, since an
inferred split can read the wrong rows while still covering the tensor.

Signed-off-by: Achyuthan Sivasankar <achyuthan.sivasankar@gmail.com>
@Achyuthan-S

Achyuthan-S commented Sep 18, 2026

Copy link
Copy Markdown
Contributor Author

@delock All four addressed in f8b9022.

The wiring test — added and confirmed it earns its place: renaming _segmented_affine_map makes it fail while the geometry tests stay green, which is the scenario you described.

The duplication — collapsed into one module-level helper taking partition_dim. Your point about it being essential rather than incidental was borne out: the len(shape) <= 0 leftover disappeared as a consequence of merging them, rather than needing its own fix.

split_widths required — done, and _even_split_sizes deleted with it. Agreed that leaving the fallback open invites exactly the bug the second commit fixed.

90 passed across the affine suite, the producer and coverage tests, the resume matrix and tests/unit/runtime/tensor_parallel/.

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.

3 participants