170 lines
6.8 KiB
Python
170 lines
6.8 KiB
Python
# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License").
|
|
# You may not use this file except in compliance with the License.
|
|
# To view a copy of this license, visit http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# No warranties are given. The work is provided "AS IS", without warranty of any kind, express or implied.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Mapping
|
|
|
|
import torch
|
|
from torch import nn
|
|
|
|
|
|
NVFP4_CHECKPOINT_FORMAT = "longlive_generator_nvfp4"
|
|
TE_NVFP4_CHECKPOINT_FORMAT = "longlive_generator_te_nvfp4"
|
|
NVFP4_CHECKPOINT_VERSION = 1
|
|
|
|
|
|
def is_nvfp4_state_dict(state_dict: object) -> bool:
|
|
"""Return True when a state dict contains materialized FourOverSix NVFP4 buffers."""
|
|
if not isinstance(state_dict, Mapping):
|
|
return False
|
|
return any(str(key).endswith("quantized_weight_values") for key in state_dict)
|
|
|
|
|
|
def is_te_nvfp4_checkpoint(checkpoint: object) -> bool:
|
|
"""Return True for checkpoints saved with TransformerEngine module state."""
|
|
return (
|
|
isinstance(checkpoint, Mapping)
|
|
and checkpoint.get("checkpoint_format") == TE_NVFP4_CHECKPOINT_FORMAT
|
|
)
|
|
|
|
|
|
def unwrap_generator_state_dict(checkpoint: object, use_ema: bool = False) -> object:
|
|
"""Extract the generator state dict from common LongLive checkpoint layouts."""
|
|
if not isinstance(checkpoint, Mapping):
|
|
return checkpoint
|
|
if "generator" in checkpoint or "generator_ema" in checkpoint:
|
|
ema_key = "generator_ema" if use_ema and "generator_ema" in checkpoint else "generator"
|
|
return checkpoint[ema_key]
|
|
if "model" in checkpoint:
|
|
return checkpoint["model"]
|
|
return checkpoint
|
|
|
|
|
|
def clean_fsdp_state_dict_keys(state_dict: Mapping[str, torch.Tensor]) -> dict[str, torch.Tensor]:
|
|
"""Remove FSDP wrapper prefixes used by some EMA checkpoints."""
|
|
return {str(key).replace("_fsdp_wrapped_module.", ""): value for key, value in state_dict.items()}
|
|
|
|
|
|
def build_model_quantization_config(config, keep_master_weights: bool = False):
|
|
from utils.quant import ModelQuantizationConfig
|
|
|
|
quant_cfg = ModelQuantizationConfig(
|
|
scale_rule=getattr(config, "model_quant_scale_rule", "static_6"),
|
|
quantize_backend=getattr(config, "model_quant_backend", None),
|
|
activation_scale_rule=getattr(
|
|
config,
|
|
"model_quant_activation_scale_rule",
|
|
getattr(config, "model_quant_scale_rule", "static_6"),
|
|
),
|
|
weight_scale_rule=getattr(config, "model_quant_weight_scale_rule", None),
|
|
gradient_scale_rule=getattr(config, "model_quant_gradient_scale_rule", None),
|
|
)
|
|
quant_cfg.keep_master_weights = keep_master_weights
|
|
return quant_cfg
|
|
|
|
|
|
def _maybe_to_dict(value):
|
|
if value is None:
|
|
return None
|
|
try:
|
|
from omegaconf import OmegaConf
|
|
|
|
if OmegaConf.is_config(value):
|
|
value = OmegaConf.to_container(value, resolve=True)
|
|
except ImportError:
|
|
pass
|
|
return dict(value)
|
|
|
|
|
|
def quantize_model_for_fouroversix_nvfp4(model: nn.Module, config, *, keep_master_weights: bool = False, verbose: bool = True):
|
|
"""Replace eligible modules with FourOverSix NVFP4 modules using the runtime config."""
|
|
from utils.quant import quantize_model_with_filter
|
|
|
|
return quantize_model_with_filter(
|
|
model,
|
|
quant_config=build_model_quantization_config(config, keep_master_weights=keep_master_weights),
|
|
filtered_modules=getattr(config, "model_quant_filtered_modules", None),
|
|
use_default_filtered_modules=getattr(config, "model_quant_use_default_filtered_modules", True),
|
|
cast_model_to_bf16=True,
|
|
materialize_for_inference=False,
|
|
use_transformer_engine=False,
|
|
verbose=verbose,
|
|
)
|
|
|
|
|
|
def quantize_model_for_transformer_engine_nvfp4(
|
|
model: nn.Module,
|
|
config,
|
|
*,
|
|
keep_master_weights: bool = False,
|
|
verbose: bool = True,
|
|
):
|
|
"""Replace eligible modules with TransformerEngine NVFP4 wrappers."""
|
|
from utils.quant import quantize_model_with_filter
|
|
|
|
use_transformer_engine = True
|
|
te_inference_only = bool(getattr(config, "model_quant_te_inference_only", use_transformer_engine))
|
|
te_low_precision_weights = bool(getattr(config, "model_quant_te_low_precision_weights", te_inference_only))
|
|
te_fallback_to_fouroversix = bool(getattr(config, "model_quant_te_fallback_to_fouroversix", False))
|
|
|
|
return quantize_model_with_filter(
|
|
model,
|
|
quant_config=build_model_quantization_config(config, keep_master_weights=keep_master_weights),
|
|
filtered_modules=getattr(config, "model_quant_filtered_modules", None),
|
|
use_default_filtered_modules=getattr(config, "model_quant_use_default_filtered_modules", True),
|
|
cast_model_to_bf16=True,
|
|
materialize_for_inference=False,
|
|
use_transformer_engine=True,
|
|
te_inference_only=te_inference_only,
|
|
te_low_precision_weights=te_low_precision_weights,
|
|
te_recipe_kwargs=_maybe_to_dict(getattr(config, "model_quant_te_recipe_kwargs", None)),
|
|
te_module_kwargs=_maybe_to_dict(getattr(config, "model_quant_te_module_kwargs", None)),
|
|
te_fallback_to_fouroversix=te_fallback_to_fouroversix,
|
|
verbose=verbose,
|
|
)
|
|
|
|
|
|
def drop_fouroversix_master_weights(model: nn.Module) -> list[str]:
|
|
"""Drop high-precision master weights after loading materialized NVFP4 buffers."""
|
|
materialized_modules = []
|
|
for module_name, module in model.named_modules():
|
|
if not hasattr(module, "parameters_to_quantize"):
|
|
continue
|
|
|
|
parameters_to_quantize = getattr(module, "parameters_to_quantize", ())
|
|
if callable(parameters_to_quantize):
|
|
parameters_to_quantize = parameters_to_quantize()
|
|
if not parameters_to_quantize:
|
|
continue
|
|
|
|
dropped_any = False
|
|
for parameter_name in parameters_to_quantize:
|
|
if isinstance(getattr(module, parameter_name, None), nn.Parameter):
|
|
module.register_parameter(parameter_name, None)
|
|
dropped_any = True
|
|
elif hasattr(module, parameter_name):
|
|
setattr(module, parameter_name, None)
|
|
dropped_any = True
|
|
|
|
if not dropped_any:
|
|
continue
|
|
for cache_name in ("_quantized_weight", "_quantized_weight_transposed", "_quantized_weights"):
|
|
if hasattr(module, cache_name):
|
|
delattr(module, cache_name)
|
|
if hasattr(module, "config") and hasattr(module.config, "keep_master_weights"):
|
|
module.config.keep_master_weights = False
|
|
materialized_modules.append(module_name)
|
|
return materialized_modules
|
|
|
|
|
|
def cpu_state_dict(module: nn.Module) -> dict[str, torch.Tensor]:
|
|
"""Return a detached CPU state dict suitable for torch.save."""
|
|
return {key: value.detach().cpu() for key, value in module.state_dict().items()}
|