[Bugfix][Diffusion] Fuse distilled LoRA weights before HSDP sharding (#6948)
Signed-off-by: samithuang <285365963@qq.com> Signed-off-by: Samit <285365963@qq.com>
This commit is contained in:
1 parent
ae70479619
commit
44d3ae100a
9 files changed
+311
-1
No files matched your search
@@ -8,6 +8,7 @@ Tests for the DiffusersPipelineLoader.
|
||||
import json
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
@@ -18,6 +19,7 @@ from vllm.config.load import LoadConfig
|
||||
|
||||
from vllm_omni.diffusion.config import get_current_diffusion_config, get_current_diffusion_config_or_none
|
||||
from vllm_omni.diffusion.data import DiffusionParallelConfig, OmniDiffusionConfig
|
||||
from vllm_omni.diffusion.lora.manager import LoRABackend
|
||||
from vllm_omni.diffusion.model_loader.diffusers_loader import DiffusersPipelineLoader
|
||||
from vllm_omni.diffusion.model_loader.host_weight_plan import (
|
||||
HostWeightPlan,
|
||||
@@ -219,6 +221,48 @@ def test_hwr_cold_publication_and_warm_restore_skip_ordinary_dit_loading(
|
||||
assert len(tuple((store_root / "source-digests-v1" / "entries").glob("*.json"))) == 1
|
||||
|
||||
|
||||
def test_maybe_fuse_distilled_lora_skips_when_hwr_warm_snapshot_present():
|
||||
cfg = SimpleNamespace(
|
||||
lora_backend="distill",
|
||||
lora_path="/path/to/lora.safetensors",
|
||||
lora_scale=1.0,
|
||||
dtype=torch.bfloat16,
|
||||
quantization_config=None,
|
||||
parallel_config=SimpleNamespace(use_hsdp=False),
|
||||
)
|
||||
loader = DiffusersPipelineLoader(LoadConfig(), cfg)
|
||||
loader._hwr_state = {"warm_snapshot": {"transformer.weight": (1234, "hash")}}
|
||||
|
||||
model = nn.Module()
|
||||
model.load_lora_weights = MagicMock()
|
||||
|
||||
loader._maybe_fuse_distilled_lora(model)
|
||||
|
||||
model.load_lora_weights.assert_not_called()
|
||||
assert getattr(model, "lora_is_fused", False) is True
|
||||
|
||||
|
||||
def test_maybe_fuse_distilled_lora_fuses_when_not_warm_snapshot():
|
||||
cfg = SimpleNamespace(
|
||||
lora_backend=LoRABackend.DISTILL,
|
||||
lora_path="/path/to/lora.safetensors",
|
||||
lora_scale=1.0,
|
||||
dtype=torch.bfloat16,
|
||||
quantization_config=None,
|
||||
parallel_config=SimpleNamespace(use_hsdp=False),
|
||||
)
|
||||
loader = DiffusersPipelineLoader(LoadConfig(), cfg)
|
||||
loader._hwr_state = None
|
||||
|
||||
model = nn.Module()
|
||||
model.load_lora_weights = MagicMock()
|
||||
|
||||
loader._maybe_fuse_distilled_lora(model)
|
||||
|
||||
model.load_lora_weights.assert_called_once_with("/path/to/lora.safetensors")
|
||||
assert getattr(model, "lora_is_fused", False) is True
|
||||
|
||||
|
||||
def test_hwr_commit_failure_discards_model_and_reloads_without_hwr_or_mmap(tmp_path: Path, monkeypatch):
|
||||
from vllm_omni.diffusion.model_loader import diffusers_loader as loader_module
|
||||
|
||||
@@ -1038,6 +1082,156 @@ def test_hsdp_processes_quantized_weights_before_sharding(mocker):
|
||||
assert events == ["load", "process", "prepare", "shard"]
|
||||
|
||||
|
||||
def test_hsdp_fuses_distilled_lora_before_sharding(mocker):
|
||||
import vllm_omni.diffusion.model_loader.diffusers_loader as loader_mod
|
||||
from vllm_omni.diffusion.offloader.module_collector import PipelineModules
|
||||
|
||||
od_config = SimpleNamespace(
|
||||
dtype=torch.float32,
|
||||
parallel_config=SimpleNamespace(
|
||||
use_hsdp=True,
|
||||
hsdp_replicate_size=1,
|
||||
hsdp_shard_size=2,
|
||||
),
|
||||
quantization_config=None,
|
||||
lora_backend="distill",
|
||||
lora_path="/path/to/lora.safetensors",
|
||||
lora_scale=1.0,
|
||||
)
|
||||
loader = DiffusersPipelineLoader(LoadConfig(), od_config)
|
||||
loader.quant_config = None
|
||||
|
||||
model = nn.Module()
|
||||
model.transformer = nn.Linear(2, 2, bias=False)
|
||||
events: list[str] = []
|
||||
|
||||
model.load_lora_weights = mocker.Mock(side_effect=lambda path: events.append(f"fuse_lora:{path}"))
|
||||
|
||||
loader._init_from_load_format = mocker.Mock(return_value=model)
|
||||
loader.load_weights = mocker.Mock(side_effect=lambda _model: events.append("load"))
|
||||
loader._process_weights_after_loading = mocker.Mock(side_effect=lambda _model, _device: events.append("process"))
|
||||
mocker.patch.object(
|
||||
loader_mod.ModuleDiscovery,
|
||||
"discover",
|
||||
return_value=PipelineModules(
|
||||
dits=[model.transformer],
|
||||
dit_names=["transformer"],
|
||||
vaes=[],
|
||||
encoders=[],
|
||||
encoder_names=[],
|
||||
resident_modules=[],
|
||||
resident_names=[],
|
||||
),
|
||||
)
|
||||
mocker.patch.object(
|
||||
loader_mod,
|
||||
"apply_hsdp_to_model",
|
||||
side_effect=lambda *_args, **_kwargs: events.append("shard"),
|
||||
)
|
||||
|
||||
loader._load_model_with_hsdp(torch.device("cpu"))
|
||||
|
||||
assert events == ["load", "fuse_lora:/path/to/lora.safetensors", "process", "shard"]
|
||||
assert getattr(model, "lora_is_fused", False) is True
|
||||
|
||||
|
||||
def test_hsdp_fuses_multi_file_distilled_lora_wan22(mocker):
|
||||
import vllm_omni.diffusion.model_loader.diffusers_loader as loader_mod
|
||||
from vllm_omni.diffusion.offloader.module_collector import PipelineModules
|
||||
|
||||
od_config = SimpleNamespace(
|
||||
dtype=torch.float32,
|
||||
parallel_config=SimpleNamespace(
|
||||
use_hsdp=True,
|
||||
hsdp_replicate_size=1,
|
||||
hsdp_shard_size=2,
|
||||
),
|
||||
quantization_config=None,
|
||||
lora_backend="distill",
|
||||
lora_path=["/path/to/high.safetensors", "/path/to/low.safetensors"],
|
||||
lora_scale=1.0,
|
||||
)
|
||||
loader = DiffusersPipelineLoader(LoadConfig(), od_config)
|
||||
loader.quant_config = None
|
||||
|
||||
model = nn.Module()
|
||||
model.transformer = nn.Linear(2, 2, bias=False)
|
||||
model.transformer_2 = nn.Linear(2, 2, bias=False)
|
||||
events: list[str] = []
|
||||
|
||||
model.load_lora_weights = mocker.Mock(side_effect=lambda paths: events.append(f"fuse_lora:{paths}"))
|
||||
|
||||
loader._init_from_load_format = mocker.Mock(return_value=model)
|
||||
loader.load_weights = mocker.Mock(side_effect=lambda _model: events.append("load"))
|
||||
loader._process_weights_after_loading = mocker.Mock(side_effect=lambda _model, _device: events.append("process"))
|
||||
mocker.patch.object(
|
||||
loader_mod.ModuleDiscovery,
|
||||
"discover",
|
||||
return_value=PipelineModules(
|
||||
dits=[model.transformer, model.transformer_2],
|
||||
dit_names=["transformer", "transformer_2"],
|
||||
vaes=[],
|
||||
encoders=[],
|
||||
encoder_names=[],
|
||||
resident_modules=[],
|
||||
resident_names=[],
|
||||
),
|
||||
)
|
||||
mocker.patch.object(
|
||||
loader_mod,
|
||||
"apply_hsdp_to_model",
|
||||
side_effect=lambda *_args, **_kwargs: events.append("shard"),
|
||||
)
|
||||
|
||||
loader._load_model_with_hsdp(torch.device("cpu"))
|
||||
|
||||
assert events == [
|
||||
"load",
|
||||
"fuse_lora:['/path/to/high.safetensors', '/path/to/low.safetensors']",
|
||||
"process",
|
||||
"shard",
|
||||
"shard",
|
||||
]
|
||||
assert getattr(model, "lora_is_fused", False) is True
|
||||
|
||||
|
||||
def test_load_model_fuses_distilled_lora_non_hsdp(mocker):
|
||||
od_config = SimpleNamespace(
|
||||
dtype=torch.float32,
|
||||
parallel_config=SimpleNamespace(
|
||||
use_hsdp=False,
|
||||
tensor_parallel_size=1,
|
||||
data_parallel_size=1,
|
||||
sequence_parallel_size=1,
|
||||
),
|
||||
enable_cpu_offload=False,
|
||||
quantization_config=None,
|
||||
lora_backend="distill",
|
||||
lora_path="/path/to/lora.safetensors",
|
||||
lora_scale=1.0,
|
||||
host_weight_runtime_mode="disabled",
|
||||
)
|
||||
loader = DiffusersPipelineLoader(LoadConfig(), od_config)
|
||||
loader._force_canonical_load = True
|
||||
|
||||
model = nn.Module()
|
||||
model.transformer = nn.Linear(2, 2, bias=False)
|
||||
events: list[str] = []
|
||||
|
||||
model.load_lora_weights = mocker.Mock(side_effect=lambda path: events.append(f"fuse_lora:{path}"))
|
||||
|
||||
loader._init_from_load_format = mocker.Mock(return_value=model)
|
||||
loader.load_weights = mocker.Mock(side_effect=lambda _model: events.append("load"))
|
||||
loader._process_weights_after_loading = mocker.Mock(side_effect=lambda _model, _device: events.append("process"))
|
||||
loader._apply_skip_softmax_calibration = mocker.Mock()
|
||||
|
||||
res = loader.load_model(load_device="cpu", device=torch.device("cpu"))
|
||||
|
||||
assert res is model
|
||||
assert events == ["load", "fuse_lora:/path/to/lora.safetensors", "process"]
|
||||
assert getattr(model, "lora_is_fused", False) is True
|
||||
|
||||
|
||||
def test_get_all_weights(prefetch_helios_model, mock_tp_group):
|
||||
"""Ensure that get all weights on a tiny model resolves to nonempty weights."""
|
||||
od_config = OmniDiffusionConfig(
|
||||
|
||||
Reference in new issue
Block a user