[diffusion] fix: fix z-Image online fp8 quantization crash with dit_cpu_offload (#29903)
Co-authored-by: Mick <mickjagger19@icloud.com>
This commit is contained in:
co-authored by
Mick
parent
6dd0cefb2a
commit
b7c3709f33
@@ -364,6 +364,11 @@ class ZImagePipelineConfig(ZImageRolloutPipelineMixin, ImagePipelineConfig):
|
|||||||
Batched prompts use stored text lengths. SP mode builds image caches for
|
Batched prompts use stored text lengths. SP mode builds image caches for
|
||||||
the local spatial shard.
|
the local spatial shard.
|
||||||
"""
|
"""
|
||||||
|
if rotary_emb is None:
|
||||||
|
raise ValueError(
|
||||||
|
"Z-Image transformer has no `rotary_emb`. It likely loaded via the "
|
||||||
|
"native diffusers fallback; check the load logs for the real error."
|
||||||
|
)
|
||||||
|
|
||||||
def create_coordinate_grid(size, start=None, device=None):
|
def create_coordinate_grid(size, start=None, device=None):
|
||||||
if start is None:
|
if start is None:
|
||||||
|
|||||||
+10
-1
@@ -85,7 +85,12 @@ class TransformerLoader(ComponentLoader):
|
|||||||
component_server_args = _server_args_for_transformer_component(
|
component_server_args = _server_args_for_transformer_component(
|
||||||
server_args, component_name
|
server_args, component_name
|
||||||
)
|
)
|
||||||
return component_server_args.transformer_weights_path is not None
|
# Don't let a quantized load quietly fall back to the unquantized native
|
||||||
|
# model. That would drop the requested precision and bury the real error.
|
||||||
|
return (
|
||||||
|
component_server_args.transformer_weights_path is not None
|
||||||
|
or component_server_args.quantization is not None
|
||||||
|
)
|
||||||
|
|
||||||
def load_customized(
|
def load_customized(
|
||||||
self, component_model_path: str, server_args: ServerArgs, component_name: str
|
self, component_model_path: str, server_args: ServerArgs, component_name: str
|
||||||
@@ -165,6 +170,10 @@ class TransformerLoader(ComponentLoader):
|
|||||||
reduce_dtype=torch.float32,
|
reduce_dtype=torch.float32,
|
||||||
output_dtype=None,
|
output_dtype=None,
|
||||||
strict=False,
|
strict=False,
|
||||||
|
defer_cpu_offload_until_after_weight_processing=(
|
||||||
|
component_server_args.dit_cpu_offload
|
||||||
|
and quant_spec.requires_device_weight_processing
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# post-hooks (e.g., patch scales (nunchaku))
|
# post-hooks (e.g., patch scales (nunchaku))
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ def maybe_load_fsdp_model(
|
|||||||
output_dtype: torch.dtype | None = None,
|
output_dtype: torch.dtype | None = None,
|
||||||
pin_cpu_memory: bool = True,
|
pin_cpu_memory: bool = True,
|
||||||
strict: bool = True,
|
strict: bool = True,
|
||||||
|
defer_cpu_offload_until_after_weight_processing: bool = False,
|
||||||
) -> torch.nn.Module:
|
) -> torch.nn.Module:
|
||||||
"""Load a model with optional FSDP (Fully Sharded Data Parallel) support.
|
"""Load a model with optional FSDP (Fully Sharded Data Parallel) support.
|
||||||
|
|
||||||
@@ -206,6 +207,9 @@ def maybe_load_fsdp_model(
|
|||||||
- Weight loading and casting
|
- Weight loading and casting
|
||||||
reduce_dtype: Data type for gradient reduction in FSDP mixed precision.
|
reduce_dtype: Data type for gradient reduction in FSDP mixed precision.
|
||||||
strict: If True, enforce strict state dict loading (all keys must match).
|
strict: If True, enforce strict state dict loading (all keys must match).
|
||||||
|
defer_cpu_offload_until_after_weight_processing: If True, keep weights
|
||||||
|
on device until process_weights_after_loading completes, then apply
|
||||||
|
non-FSDP CPU offload.
|
||||||
"""
|
"""
|
||||||
# NOTE(will): cast_forward_inputs=True shouldn't be needed as we are
|
# NOTE(will): cast_forward_inputs=True shouldn't be needed as we are
|
||||||
# manually casting the inputs to the model
|
# manually casting the inputs to the model
|
||||||
@@ -232,6 +236,17 @@ def maybe_load_fsdp_model(
|
|||||||
use_fsdp = False
|
use_fsdp = False
|
||||||
logger.info("Disabling FSDP for MPS platform as it's not compatible")
|
logger.info("Disabling FSDP for MPS platform as it's not compatible")
|
||||||
|
|
||||||
|
defer_cpu_offload = bool(
|
||||||
|
cpu_offload and defer_cpu_offload_until_after_weight_processing
|
||||||
|
)
|
||||||
|
if defer_cpu_offload and use_fsdp:
|
||||||
|
logger.warning(
|
||||||
|
"Ignoring deferred CPU offload for FSDP loading; keeping the existing "
|
||||||
|
"FSDP offload policy."
|
||||||
|
)
|
||||||
|
defer_cpu_offload = False
|
||||||
|
load_cpu_offload = cpu_offload and not defer_cpu_offload
|
||||||
|
|
||||||
if use_fsdp:
|
if use_fsdp:
|
||||||
model._pre_fsdp_weight_loader_params = {
|
model._pre_fsdp_weight_loader_params = {
|
||||||
n: p
|
n: p
|
||||||
@@ -251,7 +266,7 @@ def maybe_load_fsdp_model(
|
|||||||
)
|
)
|
||||||
shard_model(
|
shard_model(
|
||||||
model,
|
model,
|
||||||
cpu_offload=cpu_offload,
|
cpu_offload=load_cpu_offload,
|
||||||
reshard_after_forward=True,
|
reshard_after_forward=True,
|
||||||
mp_policy=mp_policy,
|
mp_policy=mp_policy,
|
||||||
mesh=device_mesh,
|
mesh=device_mesh,
|
||||||
@@ -280,7 +295,7 @@ def maybe_load_fsdp_model(
|
|||||||
device,
|
device,
|
||||||
param_dtype,
|
param_dtype,
|
||||||
strict=strict,
|
strict=strict,
|
||||||
cpu_offload=cpu_offload,
|
cpu_offload=load_cpu_offload,
|
||||||
param_names_mapping=param_names_mapping_fn,
|
param_names_mapping=param_names_mapping_fn,
|
||||||
)
|
)
|
||||||
if bnb_quant_states:
|
if bnb_quant_states:
|
||||||
@@ -301,6 +316,8 @@ def maybe_load_fsdp_model(
|
|||||||
if _is_npu:
|
if _is_npu:
|
||||||
torch.npu.empty_cache()
|
torch.npu.empty_cache()
|
||||||
model.post_load_weights()
|
model.post_load_weights()
|
||||||
|
if defer_cpu_offload:
|
||||||
|
model.to("cpu")
|
||||||
|
|
||||||
for n, p in chain(model.named_parameters(), model.named_buffers()):
|
for n, p in chain(model.named_parameters(), model.named_buffers()):
|
||||||
if p.is_meta:
|
if p.is_meta:
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ class TransformerQuantLoadSpec:
|
|||||||
quant_config: Optional[QuantizationConfig]
|
quant_config: Optional[QuantizationConfig]
|
||||||
nunchaku_config: Optional[NunchakuConfig]
|
nunchaku_config: Optional[NunchakuConfig]
|
||||||
param_dtype: Optional[torch.dtype]
|
param_dtype: Optional[torch.dtype]
|
||||||
|
requires_device_weight_processing: bool = False
|
||||||
post_load_hooks: list[PostLoadHook] = field(default_factory=list)
|
post_load_hooks: list[PostLoadHook] = field(default_factory=list)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -255,6 +256,7 @@ class _ModelOptFp8OffloadAdapter(_TransformerQuantAdapter):
|
|||||||
|
|
||||||
quant_name_getter = getattr(type(quant_config), "get_name", None)
|
quant_name_getter = getattr(type(quant_config), "get_name", None)
|
||||||
quant_name = quant_name_getter() if callable(quant_name_getter) else None
|
quant_name = quant_name_getter() if callable(quant_name_getter) else None
|
||||||
|
|
||||||
if quant_name != "modelopt_fp8":
|
if quant_name != "modelopt_fp8":
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -480,10 +482,25 @@ def resolve_transformer_quant_load_spec(
|
|||||||
quant_config=quant_config,
|
quant_config=quant_config,
|
||||||
nunchaku_config=nunchaku_config,
|
nunchaku_config=nunchaku_config,
|
||||||
param_dtype=param_dtype,
|
param_dtype=param_dtype,
|
||||||
|
requires_device_weight_processing=_requires_device_weight_processing(
|
||||||
|
quant_config
|
||||||
|
),
|
||||||
post_load_hooks=post_load_hooks,
|
post_load_hooks=post_load_hooks,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _requires_device_weight_processing(
|
||||||
|
quant_config: Optional[QuantizationConfig],
|
||||||
|
) -> bool:
|
||||||
|
"""Return whether post-load weight processing needs CUDA/NPU tensors."""
|
||||||
|
quant_name = _get_quant_config_name(quant_config)
|
||||||
|
if quant_name == "fp8":
|
||||||
|
return not getattr(quant_config, "is_checkpoint_fp8_serialized", False)
|
||||||
|
if quant_name == "mxfp4":
|
||||||
|
return not getattr(quant_config, "is_checkpoint_mxfp4_serialized", False)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _build_transformer_quant_adapters(
|
def _build_transformer_quant_adapters(
|
||||||
*,
|
*,
|
||||||
cls_name: str,
|
cls_name: str,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from types import SimpleNamespace
|
|||||||
|
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
|
from sglang.multimodal_gen.runtime.layers.quantization.fp8 import Fp8Config
|
||||||
from sglang.multimodal_gen.runtime.layers.quantization.modelopt_quant import (
|
from sglang.multimodal_gen.runtime.layers.quantization.modelopt_quant import (
|
||||||
ModelOptFp8Config,
|
ModelOptFp8Config,
|
||||||
)
|
)
|
||||||
@@ -296,6 +297,21 @@ def test_modelopt_fp8_adapter_keeps_layerwise_offload_enabled():
|
|||||||
assert server_args.dit_layerwise_offload is True
|
assert server_args.dit_layerwise_offload is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_modelopt_fp8_adapter_does_not_change_online_fp8_offload():
|
||||||
|
server_args = SimpleNamespace(
|
||||||
|
dit_cpu_offload=True,
|
||||||
|
dit_layerwise_offload=False,
|
||||||
|
quantization="fp8",
|
||||||
|
)
|
||||||
|
|
||||||
|
_ModelOptFp8OffloadAdapter._maybe_disable_incompatible_dit_offload_modes(
|
||||||
|
server_args=server_args,
|
||||||
|
quant_config=Fp8Config(),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert server_args.dit_cpu_offload is True
|
||||||
|
|
||||||
|
|
||||||
def test_layerwise_capability_selects_layerwise_strategy_for_any_component():
|
def test_layerwise_capability_selects_layerwise_strategy_for_any_component():
|
||||||
module = _LayerwiseComponent(enabled=True)
|
module = _LayerwiseComponent(enabled=True)
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ from sglang.multimodal_gen.runtime.layers.linear import UnquantizedLinearMethod
|
|||||||
from sglang.multimodal_gen.runtime.layers.quantization.configs.nunchaku_config import (
|
from sglang.multimodal_gen.runtime.layers.quantization.configs.nunchaku_config import (
|
||||||
NunchakuConfig,
|
NunchakuConfig,
|
||||||
)
|
)
|
||||||
|
from sglang.multimodal_gen.runtime.layers.quantization.fp8 import Fp8Config
|
||||||
from sglang.multimodal_gen.runtime.layers.quantization.modelopt_quant import (
|
from sglang.multimodal_gen.runtime.layers.quantization.modelopt_quant import (
|
||||||
ModelOptFp4Config,
|
ModelOptFp4Config,
|
||||||
_prepare_nvfp4_weight_bytes,
|
_prepare_nvfp4_weight_bytes,
|
||||||
@@ -56,6 +57,7 @@ from sglang.multimodal_gen.runtime.layers.quantization.modelopt_quant import (
|
|||||||
from sglang.multimodal_gen.runtime.loader.transformer_load_utils import (
|
from sglang.multimodal_gen.runtime.loader.transformer_load_utils import (
|
||||||
_filter_duplicate_precision_variant_safetensors,
|
_filter_duplicate_precision_variant_safetensors,
|
||||||
_Flux2Nvfp4FallbackAdapter,
|
_Flux2Nvfp4FallbackAdapter,
|
||||||
|
_requires_device_weight_processing,
|
||||||
resolve_transformer_quant_load_spec,
|
resolve_transformer_quant_load_spec,
|
||||||
resolve_transformer_safetensors_to_load,
|
resolve_transformer_safetensors_to_load,
|
||||||
)
|
)
|
||||||
@@ -182,6 +184,14 @@ class TestTransformerQuantHelpers(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(resolved, files)
|
self.assertEqual(resolved, files)
|
||||||
|
|
||||||
|
def test_online_fp8_requires_device_weight_processing(self):
|
||||||
|
self.assertTrue(_requires_device_weight_processing(Fp8Config()))
|
||||||
|
self.assertFalse(
|
||||||
|
_requires_device_weight_processing(
|
||||||
|
Fp8Config(is_checkpoint_fp8_serialized=True)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
"sglang.multimodal_gen.runtime.loader.transformer_load_utils.build_nvfp4_config_from_safetensors_list",
|
"sglang.multimodal_gen.runtime.loader.transformer_load_utils.build_nvfp4_config_from_safetensors_list",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
|
|||||||
Reference in New Issue
Block a user