Skip to content

compressed-tensors MoE decompression: weight_shape sharded away under TP/EP, fallback formula unrecoverable for 3/5/6/7-bit #47956

Description

@verma8076

Context

PR #47315 fixed DecompressExperts.convert (src/transformers/integrations/compressed_tensors.py) to prefer the checkpoint's stored per-expert weight_shape over reconstructing the unpacked in-dim as packed_cols * (32 // num_bits), since that formula only recovers the true in-dim when num_bits divides 32 (4-bit, 8-bit). For 3/5/6/7-bit it's wrong.

That PR's own review thread already surfaced a follow-up gap that never got its own issue, so filing it here to make it trackable. Credit for the analysis below goes to @ErenAta16 in #47315 (comment) (their comment on that PR) — reproducing it here since it was never filed separately.

The gap

Under TP/EP sharding, weight_shape is empty on most ranks (only whichever rank owns full checkpoint metadata has it), so the fallback formula still runs there. Per @ErenAta16's analysis, this isn't just a formula bug that a better formula can fix — the packed shape is genuinely ambiguous once sharded:

  • compressed_tensors.compressors.pack_quantized.helpers.pack_to_int32 packs 32 // num_bits values per int32 with padding (the "dense/native" layout).
  • AutoAWQ-style checkpoints instead pack bit-continuously: ceil(cols * num_bits / 32) int32s, no padding.
  • For in_dim=512, 3-bit: native packing gives packed_cols=52 (formula recovers 520, over-counts by 8), AWQ/dense packing gives packed_cols=48 (formula recovers 480, under-counts by 32).
  • Given only packed_cols, the true in-dim can't be distinguished between these two layouts, and the native-layout padding means its exact value isn't recoverable even in principle (only bounded within pack_factor - 1).

So no formula fix is possible here — weight_shape is the only exact source, and it needs to actually reach every rank. It's a 2-element tensor, so per @ErenAta16 the fix is likely to stop sharding it (or broadcast it) rather than reconstruct it. In quantizer_compressed_tensors.py it's currently pulled in as a plain source pattern alongside weight_packed/weight_scale, so it inherits their sharding even though it carries no per-rank data.

Why this matters

This is precisely the deployment scenario that motivates low-bit MoE quantization in the first place — large MoE models needing multi-GPU TP/EP sharding. Today, non-4/8-bit MoE checkpoints under TP/EP still hit wrong decompressed shapes (and the resulting torch._grouped_mm contraction-dimension crash that #47315 was meant to eliminate), just not for the single-GPU case #47315 already fixed.

Suggested direction

Stop letting the small weight_shape metadata (2 ints per expert) get sharded away by the TP/EP loading path — broadcast it to all ranks or otherwise keep it outside the sharding split applied to weight_packed/weight_scale. Validating this needs a real multi-GPU box, which is why it wasn't included in #47315.

cc @SunMarc @zucchini-nlp — flagged in the original thread as having context on why weight_shape ends up empty per-rank.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions