Skip to content

Commit 386d2bf

Browse files
committed
Refactor AutoTP gathered column tie handling and update related tests for fallback behavior
1 parent e237d7e commit 386d2bf

4 files changed

Lines changed: 56 additions & 26 deletions

File tree

deepspeed/module_inject/auto_tp.py

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ def __init__(self,
215215
self.linear_policies = None
216216
self.conv_linear_layer = False
217217
self.partition_config = partition_config
218-
self._gathered_column_ties_validated = False
218+
self._gathered_column_tie_fallbacks_configured = False
219+
self._tied_gathered_column_module_names = set()
219220
TensorParallel_Layer.set_keep_module_on_host(keep_module_on_host)
220221

221222
def in_module_list(module, module_list):
@@ -494,19 +495,31 @@ def _create_column_parallel_layer(self, module, spec: TPLayerSpec, name: str):
494495
print_dist(f"AutoTP: replacing '{name}' with LinearLayer(gather_output=True)", ranks=[0])
495496
return LinearLayer(module, self.mp_group, name=name, gather_output=spec.gather_output)
496497

497-
def _validate_gathered_column_ties(self):
498-
"""Reject gathered output layers that share their Parameter with an embedding."""
499-
if self._gathered_column_ties_validated or self.partition_config is None:
498+
def _configure_gathered_column_tie_fallbacks(self):
499+
"""Configure a replicated fallback for gathered output layers tied to embeddings."""
500+
if self._gathered_column_tie_fallbacks_configured or self.partition_config is None:
500501
return
501502

502-
embeddings = [(name, module) for name, module in self.module.named_modules()
503+
named_modules = list(self.module.named_modules())
504+
embeddings = [(name, module) for name, module in named_modules
503505
if isinstance(module, nn.Embedding) and hasattr(module, "weight")]
506+
get_input_embeddings = getattr(self.module, "get_input_embeddings", None)
507+
if callable(get_input_embeddings):
508+
input_embedding = get_input_embeddings()
509+
if input_embedding is not None and hasattr(input_embedding, "weight"):
510+
input_embedding_name = next(
511+
(name for name, module in named_modules if module is input_embedding),
512+
None,
513+
)
514+
is_known_embedding = any(module is input_embedding for _, module in embeddings)
515+
if input_embedding_name is not None and not is_known_embedding:
516+
embeddings.append((input_embedding_name, input_embedding))
504517
if not embeddings:
505-
self._gathered_column_ties_validated = True
518+
self._gathered_column_tie_fallbacks_configured = True
506519
return
507520

508521
model_type = self._get_model_type()
509-
for module_name, module in self.module.named_modules():
522+
for module_name, module in named_modules:
510523
if not module_name or isinstance(module, nn.Embedding) or not hasattr(module, "weight"):
511524
continue
512525

@@ -521,13 +534,14 @@ def _validate_gathered_column_ties(self):
521534
if spec is None or spec.partition_type != PartitionType.COLUMN or not spec.gather_output:
522535
continue
523536

524-
raise NotImplementedError(
525-
f"AutoTP cannot apply gathered column parallelism to '{module_name}.weight' because it is tied "
526-
f"to embedding '{tied_embedding_name}.weight' (both reference the same Parameter). Tied output "
527-
"weights require a coupled vocabulary-parallel embedding, which is not supported yet. Untie "
528-
"the weights before enabling gathered column parallelism.")
537+
self._tied_gathered_column_module_names.update((module_name, tied_embedding_name))
538+
print_dist(
539+
f"AutoTP: '{module_name}.weight' is tied to '{tied_embedding_name}.weight'; leaving both modules "
540+
"replicated because coupled vocabulary-parallel embedding is not supported yet.",
541+
ranks=[0],
542+
)
529543

530-
self._gathered_column_ties_validated = True
544+
self._gathered_column_tie_fallbacks_configured = True
531545

532546
def _get_model_type(self) -> Optional[str]:
533547
"""Extract model type from module config or class name."""
@@ -623,7 +637,7 @@ def _replace_autoep_shared_experts(self, autoep_layer, autoep_name):
623637

624638
def _replace_module(self, r_module, prev_name='', prev_class_name=''):
625639
if prev_name == '' and prev_class_name == '':
626-
self._validate_gathered_column_ties()
640+
self._configure_gathered_column_tie_fallbacks()
627641

628642
for name, child in r_module.named_children():
629643
if getattr(child, "_is_autoep_layer", False):
@@ -650,7 +664,9 @@ def _replace_module(self, r_module, prev_name='', prev_class_name=''):
650664
# instead of linear_policies. This keeps all pattern logic centralized here.
651665
if self.partition_config is not None:
652666
full_name = class_name + '.' + name if class_name else name
653-
if isinstance(child, nn.Embedding):
667+
if full_name in self._tied_gathered_column_module_names:
668+
continue
669+
elif isinstance(child, nn.Embedding):
654670
# Check if embedding matches any pattern
655671
param_name = full_name + ".weight"
656672
model_type = self._get_model_type()

docs/_tutorials/autotp-training.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,12 @@ shards so every tensor-parallel rank receives the complete output.
125125

126126
Gathered column parallelism currently supports untied output layers. If an
127127
output layer such as `lm_head` shares the same runtime `Parameter` object with
128-
an embedding, DeepSpeed raises an error before replacing either layer. Preserving
129-
that tie requires a coupled vocabulary-parallel embedding, which is not yet
130-
implemented. This check uses actual `Parameter` identity rather than model
131-
configuration metadata such as `tie_word_embeddings`.
128+
an embedding, DeepSpeed leaves both modules replicated and applies tensor
129+
parallelism to the remaining matched layers. This preserves the tie without
130+
silently cloning the weight, but does not reduce the embedding or output-layer
131+
memory footprint. A coupled vocabulary-parallel embedding is required to shard
132+
the tied weight and is not yet implemented. This fallback uses actual `Parameter`
133+
identity rather than model configuration metadata such as `tie_word_embeddings`.
132134

133135
Additional HuggingFace types such as `local_colwise` and `local_rowwise` are
134136
not yet handled and fall back to AutoTP preset-based partitioning.

tests/unit/model_parallelism/test_tp_plan_real_models.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ def test_qwen2_tp_plan_with_zero2(self):
9393

9494
assert not torch.isnan(outputs.loss)
9595

96-
def test_qwen2_tied_lm_head_is_rejected(self):
97-
"""Test that the gathered LM-head plan rejects an actual Qwen2 Parameter tie."""
96+
def test_qwen2_tied_lm_head_falls_back_to_replicated(self):
97+
"""Test that an actual Qwen2 Parameter tie remains replicated."""
9898
skip_on_device()
9999

100100
try:
@@ -124,10 +124,23 @@ def test_qwen2_tied_lm_head_is_rejected(self):
124124
},
125125
}
126126

127-
with pytest.raises(NotImplementedError, match="coupled vocabulary-parallel embedding"):
128-
deepspeed.initialize(model=model, model_parameters=model.parameters(), config=ds_config)
127+
engine, _, _, _ = deepspeed.initialize(model=model, model_parameters=model.parameters(), config=ds_config)
129128

129+
assert engine.autotp_size() == 2
130+
assert isinstance(model.model.layers[0].self_attn.q_proj, LinearLayer)
131+
assert isinstance(model.model.embed_tokens, torch.nn.Embedding)
132+
assert isinstance(model.lm_head, torch.nn.Linear)
130133
assert model.lm_head.weight is model.model.embed_tokens.weight
134+
assert model.lm_head.weight.shape == (1000, 128)
135+
136+
input_ids = torch.randint(0, 1000, (1, 16)).to(get_accelerator().current_device_name())
137+
dist.broadcast(
138+
input_ids,
139+
src=groups.get_tensor_model_parallel_src_rank(),
140+
group=groups.get_tensor_model_parallel_group(),
141+
)
142+
outputs = engine(input_ids)
143+
assert outputs.logits.shape == (1, 16, 1000)
131144

132145
def test_custom_model_with_custom_tp_plan(self):
133146
"""Test custom model + custom tp_plan"""

tests/unit/module_inject/test_tp_partition_config_path.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,11 @@ def test_gathered_lm_head_uses_column_parallel_layer_when_untied():
165165
assert model.lm_head.gather_output
166166

167167

168-
def test_gathered_lm_head_rejects_runtime_parameter_tie_before_replacement():
168+
def test_gathered_lm_head_falls_back_for_runtime_parameter_tie():
169169
model = OutputModel(tied=True)
170170
assert model.lm_head.weight is model.embed_tokens.weight
171171

172-
with pytest.raises(NotImplementedError, match="coupled vocabulary-parallel embedding"):
173-
_build_gathered_lm_head_autotp(model)._replace_module(model)
172+
_build_gathered_lm_head_autotp(model)._replace_module(model)
174173

175174
assert isinstance(model.embed_tokens, nn.Embedding)
176175
assert isinstance(model.lm_head, nn.Linear)

0 commit comments

Comments
 (0)