Files
2026-07-13 13:18:33 +08:00

128 lines
4.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPDX-License-Identifier: Apache-2.0
# DeepSpeed Team
"""Test that partition_config receives correct full hierarchical module paths.
The bug: AutoTP._replace_module built ``full_name`` from ``prev_name`` (the
immediate parent only) instead of ``class_name`` (the accumulated hierarchical
path). Patterns like ``model.layers.0.self_attn.q_proj`` never matched
because the name was just ``0.self_attn.q_proj``.
"""
import pytest
import torch.nn as nn
from deepspeed.module_inject.auto_tp import AutoTP, AutoTPConfig, PartitionType, TPLayerSpec
class SubAttn(nn.Module):
def __init__(self):
super().__init__()
self.q_proj = nn.Linear(32, 32, bias=False)
self.k_proj = nn.Linear(32, 32, bias=False)
self.v_proj = nn.Linear(32, 32, bias=False)
self.o_proj = nn.Linear(32, 32, bias=False)
class DecoderLayer(nn.Module):
def __init__(self):
super().__init__()
self.self_attn = SubAttn()
self.mlp = nn.Sequential(nn.Linear(32, 64), nn.GELU(), nn.Linear(64, 32))
class DummyModel(nn.Module):
def __init__(self, num_layers=2):
super().__init__()
self.embed = nn.Embedding(100, 32)
self.layers = nn.ModuleList([DecoderLayer() for _ in range(num_layers)])
self.head = nn.Linear(32, 100, bias=False)
def _build_config():
"""Partition config that matches q_proj and o_proj via regex."""
return AutoTPConfig(layer_specs=[
TPLayerSpec(patterns=[r".*\.self_attn\.q_proj"], partition_type=PartitionType.COLUMN),
TPLayerSpec(patterns=[r".*\.self_attn\.o_proj"], partition_type=PartitionType.ROW),
])
def _capture_matched_names(model, config):
"""Run _replace_module and capture full_name values that match a spec."""
matched_names = []
original = AutoTP._replace_with_config
def capture(self, child, full_name):
# Only capture if a spec actually matches
param_name = full_name + ".weight"
model_type = self._get_model_type() if hasattr(self, '_get_model_type') else None
spec = config.find_matching_spec(param_name, model_type)
if spec is not None:
matched_names.append(full_name)
return None
AutoTP._replace_with_config = capture
try:
autotp = AutoTP(
module=model,
all_reduce_linears=[],
prefix="model",
state_dict=None,
linear_layer_setting=None,
orig_layer_impl=None,
partition_config=config,
)
autotp._replace_module(model)
finally:
AutoTP._replace_with_config = original
return matched_names
def test_partition_config_receives_full_path():
"""Verify that pattern matching sees the full hierarchical path."""
model = DummyModel(num_layers=2)
config = _build_config()
matched_names = _capture_matched_names(model, config)
for layer_idx in range(2):
assert f"layers.{layer_idx}.self_attn.q_proj" in matched_names, \
f"Expected 'layers.{layer_idx}.self_attn.q_proj', got: {matched_names}"
assert f"layers.{layer_idx}.self_attn.o_proj" in matched_names, \
f"Expected 'layers.{layer_idx}.self_attn.o_proj', got: {matched_names}"
def test_no_truncated_paths():
"""Ensure paths are never truncated to just the immediate parent prefix."""
model = DummyModel(num_layers=3)
config = _build_config()
matched_names = _capture_matched_names(model, config)
for name in matched_names:
assert name.startswith("layers."), \
f"Path should start with 'layers.', got: {name}"
assert ".self_attn." in name, \
f"Path should contain '.self_attn.', got: {name}"
assert name.count(".") >= 3, \
f"Path should have at least 3 dots (layers.N.self_attn.X_proj), got: {name}"
def test_nested_depth_correct():
"""Verify correct count and paths at 3 layers deep."""
model = DummyModel(num_layers=3)
config = _build_config()
matched_names = _capture_matched_names(model, config)
expected_count = 3 * 2 # 3 layers × (q_proj + o_proj)
assert len(matched_names) == expected_count, \
f"Expected {expected_count} matches, got {len(matched_names)}: {matched_names}"
for layer_idx in range(3):
assert f"layers.{layer_idx}.self_attn.q_proj" in matched_names
assert f"layers.{layer_idx}.self_attn.o_proj" in matched_names
if __name__ == "__main__":
pytest.main([__file__, "-v"])