Skip to content

Commit 534d46d

Browse files
committed
Fix diffusers HF export crash on configs without torch_dtype
`_resolve_export_dtype` accessed `model.config.torch_dtype` directly, which raises `AttributeError` for a diffusers `FrozenDict` config that has no `torch_dtype` key. This broke `export_hf_checkpoint` for diffusers models (UNet/DiT/Flux) under the minimum transformers/diffusers versions, failing the `tf_min` unit matrix (tests/unit/torch/export/test_export_diffusers.py). Read it via `getattr(..., None)` and fall back to the model's parameter dtype (`next(model.parameters()).dtype`), matching how `diffusers_utils` already resolves a diffusers model's dtype. Behavior is unchanged when the config carries `torch_dtype`. Adds version-independent unit tests for the helper. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
1 parent 913f5e2 commit 534d46d

2 files changed

Lines changed: 46 additions & 3 deletions

File tree

modelopt/torch/export/unified_export_hf.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -826,11 +826,17 @@ def _dispatch_export_handler(name: str, sub_module: nn.Module, ctx: ExportContex
826826

827827
def _resolve_export_dtype(model: nn.Module, dtype: torch.dtype | None) -> torch.dtype:
828828
"""Return the export dtype, defaulting to the model's own and warning on a mismatch."""
829+
# Diffusers models expose their config as a ``FrozenDict`` that need not carry a
830+
# ``torch_dtype`` key, so bare attribute access can raise ``AttributeError``. Fall back
831+
# to the model's parameter dtype in that case (matching ``diffusers_utils``).
832+
model_dtype = getattr(model.config, "torch_dtype", None)
833+
if model_dtype is None:
834+
model_dtype = next(model.parameters()).dtype
829835
if dtype is None:
830-
return model.config.torch_dtype
831-
if dtype != model.config.torch_dtype:
836+
return model_dtype
837+
if dtype != model_dtype:
832838
warnings.warn(
833-
f"Model's original dtype ({model.config.torch_dtype}) differs from target dtype "
839+
f"Model's original dtype ({model_dtype}) differs from target dtype "
834840
f"({dtype}), which may lead to numerical errors."
835841
)
836842
return dtype

tests/unit/torch/export/test_unified_export_hf.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
"""Tests for tied-weight helpers in unified_export_hf."""
1717

18+
from types import SimpleNamespace
19+
1820
import pytest
1921
import torch
2022
from _test_utils.torch.quantization.tied_modules import (
@@ -29,6 +31,7 @@
2931
postprocess_state_dict,
3032
sync_tied_input_amax,
3133
)
34+
from modelopt.torch.export.unified_export_hf import _resolve_export_dtype
3235
from modelopt.torch.quantization.nn import TensorQuantizer
3336

3437

@@ -484,3 +487,37 @@ def test_fuse_prequant_layernorm_fuses_and_removes_pre_quant_scale():
484487
for module in modules:
485488
assert not hasattr(module.input_quantizer, "_pre_quant_scale")
486489
assert module.fused_with_prequant
490+
491+
492+
def _module_with_config(config, param_dtype):
493+
"""A minimal ``nn.Module`` exposing a ``.config`` and a single parameter."""
494+
495+
class _M(torch.nn.Module):
496+
def __init__(self):
497+
super().__init__()
498+
self.linear = torch.nn.Linear(2, 2).to(param_dtype)
499+
self.config = config
500+
501+
return _M()
502+
503+
504+
def test_resolve_export_dtype_uses_config_torch_dtype():
505+
"""A config carrying ``torch_dtype`` is used verbatim."""
506+
model = _module_with_config(SimpleNamespace(torch_dtype=torch.bfloat16), torch.float32)
507+
assert _resolve_export_dtype(model, None) == torch.bfloat16
508+
509+
510+
def test_resolve_export_dtype_falls_back_to_param_dtype_without_torch_dtype():
511+
"""Diffusers-style configs (e.g. a ``FrozenDict``) need not carry ``torch_dtype``.
512+
513+
Regression test: bare ``model.config.torch_dtype`` used to raise ``AttributeError``
514+
during diffusers export under minimum transformers/diffusers versions.
515+
"""
516+
model = _module_with_config(SimpleNamespace(), torch.float16) # no torch_dtype
517+
assert _resolve_export_dtype(model, None) == torch.float16
518+
519+
520+
def test_resolve_export_dtype_returns_explicit_dtype_without_config_dtype():
521+
"""An explicit target dtype is returned, and the mismatch path does not crash."""
522+
model = _module_with_config(SimpleNamespace(), torch.float32)
523+
assert _resolve_export_dtype(model, torch.bfloat16) == torch.bfloat16

0 commit comments

Comments
 (0)