chore: import upstream snapshot with attribution
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,769 @@
|
||||
# Copied and adapted from: https://github.com/hao-ai-lab/FastVideo
|
||||
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Adapted from torchtune
|
||||
# Copyright 2024 The TorchTune Authors.
|
||||
# Copyright 2025 The sglang-diffusion Authors.
|
||||
|
||||
from collections import Counter, defaultdict
|
||||
from collections.abc import Callable, Generator
|
||||
from itertools import chain
|
||||
from typing import Any
|
||||
|
||||
import torch
|
||||
from torch import nn
|
||||
from torch.distributed import DeviceMesh, init_device_mesh
|
||||
from torch.distributed._tensor import distribute_tensor
|
||||
from torch.distributed.fsdp import (
|
||||
CPUOffloadPolicy,
|
||||
FSDPModule,
|
||||
MixedPrecisionPolicy,
|
||||
fully_shard,
|
||||
)
|
||||
from torch.nn.modules.module import _IncompatibleKeys
|
||||
|
||||
from sglang.multimodal_gen.configs.models.fsdp import is_module_list_entry_in
|
||||
from sglang.multimodal_gen.runtime.layers.linear import UnquantizedLinearMethod
|
||||
from sglang.multimodal_gen.runtime.layers.quantization.bitsandbytes import (
|
||||
attach_bitsandbytes_4bit_quant_states,
|
||||
build_bitsandbytes_4bit_quant_states,
|
||||
split_bitsandbytes_4bit_state,
|
||||
)
|
||||
from sglang.multimodal_gen.runtime.loader.utils import (
|
||||
get_param_names_mapping,
|
||||
hf_to_custom_state_dict,
|
||||
set_default_torch_dtype,
|
||||
)
|
||||
from sglang.multimodal_gen.runtime.loader.weight_load_plan import WeightLoadPlan
|
||||
from sglang.multimodal_gen.runtime.loader.weight_utils import (
|
||||
safetensors_weights_iterator,
|
||||
)
|
||||
from sglang.multimodal_gen.runtime.platforms import current_platform
|
||||
from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger
|
||||
from sglang.multimodal_gen.utils import set_mixed_precision_policy
|
||||
from sglang.srt.utils import is_npu
|
||||
|
||||
_is_npu = is_npu()
|
||||
|
||||
logger = init_logger(__name__)
|
||||
|
||||
_QUANTIZED_DTYPES = (
|
||||
torch.uint8,
|
||||
torch.float8_e4m3fn,
|
||||
torch.float8_e5m2,
|
||||
torch.int8,
|
||||
)
|
||||
_DTYPE_MISMATCH_EXAMPLE_LIMIT = 3
|
||||
|
||||
|
||||
def _is_bitsandbytes_quant_config(quant_config: Any | None) -> bool:
|
||||
if quant_config is None:
|
||||
return False
|
||||
quant_name_getter = getattr(type(quant_config), "get_name", None)
|
||||
return bool(callable(quant_name_getter) and quant_name_getter() == "bitsandbytes")
|
||||
|
||||
|
||||
def _format_dtype_mismatch_summary(
|
||||
mismatch_counts: Counter[tuple[torch.dtype, torch.dtype]],
|
||||
mismatch_examples: dict[tuple[torch.dtype, torch.dtype], list[str]],
|
||||
) -> str:
|
||||
parts: list[str] = []
|
||||
for (checkpoint_dtype, target_dtype), count in mismatch_counts.items():
|
||||
examples = mismatch_examples[(checkpoint_dtype, target_dtype)]
|
||||
part = f"{checkpoint_dtype}->{target_dtype} x{count}"
|
||||
if examples:
|
||||
part += f" (e.g. {', '.join(examples)})"
|
||||
parts.append(part)
|
||||
return "; ".join(parts)
|
||||
|
||||
|
||||
def _make_param_like(
|
||||
actual_param: torch.nn.Parameter, tensor: torch.Tensor
|
||||
) -> torch.nn.Parameter:
|
||||
cls = actual_param.__class__
|
||||
# nn.Parameter defaults to requires_grad=True, which is illegal for non-floating/complex dtypes (e.g., int8/FP8
|
||||
# quantized weights).
|
||||
try:
|
||||
new_param = cls.__new__(cls, tensor, requires_grad=False)
|
||||
except TypeError:
|
||||
try:
|
||||
new_param = cls.__new__(cls, tensor)
|
||||
except TypeError:
|
||||
new_param = nn.Parameter(tensor, requires_grad=False)
|
||||
new_param.__dict__.update(actual_param.__dict__)
|
||||
new_param.requires_grad = False
|
||||
return new_param
|
||||
|
||||
|
||||
def _get_param_for_weight_loading(
|
||||
model: torch.nn.Module,
|
||||
param_dict: dict[str, torch.nn.Parameter],
|
||||
param_name: str,
|
||||
) -> torch.nn.Parameter | None:
|
||||
actual_param = param_dict.get(param_name)
|
||||
if actual_param is not None and getattr(actual_param, "weight_loader", None):
|
||||
return actual_param
|
||||
|
||||
pre_fsdp_weight_loader_params = getattr(model, "_pre_fsdp_weight_loader_params", {})
|
||||
pre_fsdp_param = pre_fsdp_weight_loader_params.get(param_name)
|
||||
if pre_fsdp_param is not None:
|
||||
return pre_fsdp_param
|
||||
|
||||
return actual_param
|
||||
|
||||
|
||||
def _make_class_name_shard_condition(class_names: set[str]):
|
||||
def shard_condition(n: str, m: nn.Module) -> bool:
|
||||
return type(m).__name__ in class_names
|
||||
|
||||
return shard_condition
|
||||
|
||||
|
||||
def _is_common_numbered_block(n: str, m: nn.Module) -> bool:
|
||||
return is_module_list_entry_in(
|
||||
n,
|
||||
(
|
||||
"blocks",
|
||||
"layers",
|
||||
"double_blocks",
|
||||
"single_blocks",
|
||||
"refiner_blocks",
|
||||
"noise_refiner",
|
||||
"context_refiner",
|
||||
"transformer_blocks",
|
||||
"single_transformer_blocks",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _resolve_fsdp_shard_conditions(
|
||||
model: torch.nn.Module,
|
||||
fsdp_shard_conditions: list[Callable[[str, nn.Module], bool]] | None,
|
||||
) -> tuple[list[Callable[[str, nn.Module], bool]], str]:
|
||||
if fsdp_shard_conditions:
|
||||
return fsdp_shard_conditions, "explicit"
|
||||
|
||||
block_class_names = set(getattr(model, "_repeated_blocks", []) or [])
|
||||
block_class_names.update(getattr(model, "_no_split_modules", []) or [])
|
||||
if block_class_names:
|
||||
return [_make_class_name_shard_condition(block_class_names)], "block-class"
|
||||
|
||||
return [_is_common_numbered_block], "common-numbered-block"
|
||||
|
||||
|
||||
def _maybe_dequantize_fp8(
|
||||
full_tensor: torch.Tensor,
|
||||
target_dtype: torch.dtype,
|
||||
target_param_name: str,
|
||||
param_sd: dict[str, torch.Tensor],
|
||||
) -> torch.Tensor:
|
||||
"""Auto-dequantize an FP8 checkpoint weight when the model parameter expects a higher-precision type.
|
||||
|
||||
Some modules (e.g. AdaLayerNormZero) don't accept quant_config, so their
|
||||
parameters remain in higher precision even when the checkpoint stores FP8
|
||||
weights. In that case we multiply by the per-tensor weight_scale to
|
||||
recover the original unquantized value.
|
||||
"""
|
||||
if not (
|
||||
full_tensor.dtype == torch.float8_e4m3fn and target_dtype != torch.float8_e4m3fn
|
||||
):
|
||||
return full_tensor
|
||||
|
||||
scale_key = target_param_name.rsplit(".", 1)[0] + ".weight_scale"
|
||||
scale_tensor = param_sd.get(scale_key)
|
||||
if scale_tensor is not None:
|
||||
full_tensor = full_tensor.to(torch.float32) * scale_tensor.float()
|
||||
logger.debug(
|
||||
"Auto-dequantized FP8 weight %s using %s",
|
||||
target_param_name,
|
||||
scale_key,
|
||||
)
|
||||
return full_tensor
|
||||
|
||||
|
||||
# TODO(PY): add compile option
|
||||
def maybe_load_fsdp_model(
|
||||
model_cls: type[nn.Module],
|
||||
init_params: dict[str, Any],
|
||||
weight_dir_list: list[str],
|
||||
device: torch.device,
|
||||
hsdp_replicate_dim: int,
|
||||
hsdp_shard_dim: int,
|
||||
param_dtype: torch.dtype,
|
||||
reduce_dtype: torch.dtype,
|
||||
cpu_offload: bool = False,
|
||||
fsdp_inference: bool = False,
|
||||
output_dtype: torch.dtype | None = None,
|
||||
pin_cpu_memory: bool = True,
|
||||
strict: bool = True,
|
||||
weight_load_plan: WeightLoadPlan | None = None,
|
||||
) -> torch.nn.Module:
|
||||
"""Load a model with optional FSDP (Fully Sharded Data Parallel) support.
|
||||
|
||||
Args:
|
||||
param_dtype: Data type for model parameters, also used for:
|
||||
- Model initialization context (set_default_torch_dtype)
|
||||
- FSDP mixed precision policy
|
||||
- Weight loading and casting
|
||||
reduce_dtype: Data type for gradient reduction in FSDP mixed precision.
|
||||
strict: If True, enforce strict state dict loading (all keys must match).
|
||||
weight_load_plan: Optional checkpoint/postprocess device plan for this load.
|
||||
"""
|
||||
# NOTE(will): cast_forward_inputs=True shouldn't be needed as we are
|
||||
# manually casting the inputs to the model
|
||||
|
||||
# 1. prepare for loading
|
||||
default_torch_dtype = param_dtype if param_dtype else torch.bfloat16
|
||||
mp_policy = MixedPrecisionPolicy(
|
||||
default_torch_dtype, reduce_dtype, output_dtype, cast_forward_inputs=False
|
||||
)
|
||||
|
||||
set_mixed_precision_policy(
|
||||
param_dtype=default_torch_dtype,
|
||||
reduce_dtype=reduce_dtype,
|
||||
output_dtype=output_dtype,
|
||||
mp_policy=mp_policy,
|
||||
)
|
||||
|
||||
with set_default_torch_dtype(default_torch_dtype), torch.device("meta"):
|
||||
model = model_cls(**init_params)
|
||||
|
||||
# Check if we should use FSDP
|
||||
use_fsdp = fsdp_inference
|
||||
|
||||
# Disable FSDP for MPS as it's not compatible
|
||||
if current_platform.is_mps():
|
||||
use_fsdp = False
|
||||
logger.info("Disabling FSDP for MPS platform as it's not compatible")
|
||||
|
||||
weight_load_plan = weight_load_plan or WeightLoadPlan(checkpoint_load_device=device)
|
||||
defer_cpu_offload = bool(
|
||||
cpu_offload and weight_load_plan.defer_component_cpu_offload
|
||||
)
|
||||
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 = bool(cpu_offload and not defer_cpu_offload)
|
||||
weight_postprocess_device = weight_load_plan.weight_postprocess_device
|
||||
if use_fsdp and weight_postprocess_device is not None:
|
||||
logger.warning("Ignoring weight postprocess device override for FSDP loading.")
|
||||
weight_postprocess_device = None
|
||||
|
||||
if use_fsdp:
|
||||
model._pre_fsdp_weight_loader_params = {
|
||||
n: p
|
||||
for n, p in model.named_parameters()
|
||||
if getattr(p, "weight_loader", None)
|
||||
}
|
||||
world_size = hsdp_replicate_dim * hsdp_shard_dim
|
||||
if not fsdp_inference:
|
||||
hsdp_replicate_dim = world_size
|
||||
hsdp_shard_dim = 1
|
||||
|
||||
device_mesh = init_device_mesh(
|
||||
current_platform.device_type,
|
||||
# (Replicate(), Shard(dim=0))
|
||||
mesh_shape=(hsdp_replicate_dim, hsdp_shard_dim),
|
||||
mesh_dim_names=("replicate", "shard"),
|
||||
)
|
||||
shard_model(
|
||||
model,
|
||||
cpu_offload=load_cpu_offload,
|
||||
reshard_after_forward=True,
|
||||
mp_policy=mp_policy,
|
||||
mesh=device_mesh,
|
||||
fsdp_shard_conditions=getattr(model, "_fsdp_shard_conditions", None),
|
||||
pin_cpu_memory=pin_cpu_memory,
|
||||
)
|
||||
|
||||
param_names_mapping_fn = get_param_names_mapping(model.param_names_mapping)
|
||||
|
||||
# 2. load model from disk
|
||||
weight_iterator = safetensors_weights_iterator(weight_dir_list)
|
||||
preprocess_loaded_state_dict = getattr(model, "preprocess_loaded_state_dict", None)
|
||||
if preprocess_loaded_state_dict is not None:
|
||||
weight_iterator = preprocess_loaded_state_dict(weight_iterator)
|
||||
bnb_quant_states = None
|
||||
if _is_bitsandbytes_quant_config(init_params.get("quant_config")):
|
||||
normal_weights, raw_quant_state = split_bitsandbytes_4bit_state(weight_iterator)
|
||||
bnb_quant_states = build_bitsandbytes_4bit_quant_states(
|
||||
[name for name, _ in normal_weights],
|
||||
raw_quant_state,
|
||||
device,
|
||||
param_names_mapping_fn,
|
||||
)
|
||||
weight_iterator = iter(normal_weights)
|
||||
load_model_from_full_model_state_dict(
|
||||
model,
|
||||
weight_iterator,
|
||||
weight_load_plan.checkpoint_load_device,
|
||||
param_dtype,
|
||||
strict=strict,
|
||||
cpu_offload=load_cpu_offload,
|
||||
param_names_mapping=param_names_mapping_fn,
|
||||
)
|
||||
if bnb_quant_states:
|
||||
attach_bitsandbytes_4bit_quant_states(
|
||||
dict(model.named_parameters()), bnb_quant_states
|
||||
)
|
||||
|
||||
# 3. postprocessing
|
||||
if weight_postprocess_device is not None:
|
||||
# move to device to perform postprocessing
|
||||
model.to(weight_postprocess_device)
|
||||
|
||||
for _, module in model.named_modules():
|
||||
quant_method = getattr(module, "quant_method", None)
|
||||
if quant_method is not None and hasattr(
|
||||
quant_method, "process_weights_after_loading"
|
||||
):
|
||||
if _is_npu and not isinstance(quant_method, UnquantizedLinearMethod):
|
||||
# Activate the NZ format for storing weights,
|
||||
# which is a specific optimization for Ascend NPU
|
||||
torch.npu.config.allow_internal_format = True
|
||||
quant_method.process_weights_after_loading(module)
|
||||
if _is_npu:
|
||||
torch.npu.empty_cache()
|
||||
model.post_load_weights()
|
||||
|
||||
for n, p in chain(model.named_parameters(), model.named_buffers()):
|
||||
if p.is_meta:
|
||||
raise RuntimeError(f"Unexpected param or buffer {n} on meta device.")
|
||||
# Avoid unintended computation graph accumulation during inference
|
||||
if isinstance(p, torch.nn.Parameter):
|
||||
p.requires_grad = False
|
||||
|
||||
# 4. deferred cpu offload
|
||||
if defer_cpu_offload:
|
||||
model.to("cpu")
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def shard_model(
|
||||
model,
|
||||
*,
|
||||
cpu_offload: bool,
|
||||
reshard_after_forward: bool = True,
|
||||
mp_policy: MixedPrecisionPolicy | None = MixedPrecisionPolicy(), # noqa
|
||||
mesh: DeviceMesh | None = None,
|
||||
fsdp_shard_conditions: list[Callable[[str, nn.Module], bool]] | None = None,
|
||||
pin_cpu_memory: bool = True,
|
||||
) -> None:
|
||||
"""
|
||||
Utility to shard a model with FSDP using the PyTorch Distributed fully_shard API.
|
||||
|
||||
This method will over the model's named modules from the bottom-up and apply shard modules
|
||||
based on whether they meet any of the criteria from shard_conditions.
|
||||
|
||||
Args:
|
||||
model (TransformerDecoder): Model to shard with FSDP.
|
||||
cpu_offload (bool): If set to True, FSDP will offload parameters, gradients, and optimizer
|
||||
states to CPU.
|
||||
reshard_after_forward (bool): Whether to reshard parameters and buffers after
|
||||
the forward pass. Setting this to True corresponds to the FULL_SHARD sharding strategy
|
||||
from FSDP1, while setting it to False corresponds to the SHARD_GRAD_OP sharding strategy.
|
||||
mesh (Optional[DeviceMesh]): Device mesh to use for FSDP sharding under multiple parallelism.
|
||||
Default to None.
|
||||
fsdp_shard_conditions (List[Callable[[str, nn.Module], bool]]): A list of functions to determine
|
||||
which modules to shard with FSDP.
|
||||
pin_cpu_memory (bool): If set to True, FSDP will pin the CPU memory of the offloaded parameters.
|
||||
|
||||
"""
|
||||
fsdp_shard_conditions, condition_source = _resolve_fsdp_shard_conditions(
|
||||
model, fsdp_shard_conditions
|
||||
)
|
||||
if condition_source != "explicit":
|
||||
logger.warning(
|
||||
"Using %s FSDP shard condition fallback for %s",
|
||||
condition_source,
|
||||
type(model).__name__,
|
||||
)
|
||||
|
||||
fsdp_kwargs = {
|
||||
"reshard_after_forward": reshard_after_forward,
|
||||
"mesh": mesh,
|
||||
"mp_policy": mp_policy,
|
||||
}
|
||||
if cpu_offload:
|
||||
fsdp_kwargs["offload_policy"] = CPUOffloadPolicy(pin_memory=pin_cpu_memory)
|
||||
|
||||
# iterating in reverse to start with
|
||||
# lowest-level modules first
|
||||
num_layers_sharded = 0
|
||||
# TODO(will): don't reshard after forward for the last layer to save on the
|
||||
# all-gather that will immediately happen Shard the model with FSDP,
|
||||
for n, m in reversed(list(model.named_modules())):
|
||||
if any([shard_condition(n, m) for shard_condition in fsdp_shard_conditions]): # type: ignore
|
||||
fully_shard(m, **fsdp_kwargs)
|
||||
num_layers_sharded += 1
|
||||
|
||||
if num_layers_sharded == 0:
|
||||
raise ValueError(
|
||||
f"No layer modules were sharded in {type(model).__name__}. "
|
||||
f"FSDP shard condition source: {condition_source}."
|
||||
)
|
||||
|
||||
# Finally shard the entire model to account for any stragglers
|
||||
fully_shard(model, **fsdp_kwargs)
|
||||
logger.info(
|
||||
"Applied FSDP to %d submodules in %s using %s shard conditions",
|
||||
num_layers_sharded,
|
||||
type(model).__name__,
|
||||
condition_source,
|
||||
)
|
||||
|
||||
|
||||
# TODO(mick): need refactor, to move out checkpoint-specific adjustments
|
||||
def load_model_from_full_model_state_dict(
|
||||
model: FSDPModule | torch.nn.Module,
|
||||
full_sd_iterator: Generator[tuple[str, torch.Tensor], None, None],
|
||||
checkpoint_load_device: torch.device,
|
||||
param_dtype: torch.dtype | None,
|
||||
strict: bool = False,
|
||||
cpu_offload: bool = False,
|
||||
param_names_mapping: Callable[[str], tuple[str, Any, Any]] | None = None,
|
||||
) -> _IncompatibleKeys:
|
||||
"""
|
||||
Converting full state dict into a sharded state dict
|
||||
and loading it into FSDP model (if training) or normal huggingface model
|
||||
Args:
|
||||
model (Union[FSDPModule, torch.nn.Module]): Model to generate fully qualified names for cpu_state_dict
|
||||
full_sd_iterator (Generator): an iterator yielding (param_name, tensor) pairs
|
||||
checkpoint_load_device (torch.device): device used to move full state dict tensors
|
||||
param_dtype (torch.dtype): dtype used to move full state dict tensors. If none, respect original dtype from checkpoint
|
||||
strict (bool): flag to check if to load the model in strict mode
|
||||
cpu_offload (bool): flag to check if FSDP offload is enabled
|
||||
param_names_mapping (Optional[Callable[[str], str]]): a function that maps full param name to sharded param name
|
||||
Returns:
|
||||
``NamedTuple`` with ``missing_keys`` and ``unexpected_keys`` fields:
|
||||
* **missing_keys** is a list of str containing the missing keys
|
||||
* **unexpected_keys** is a list of str containing the unexpected keys
|
||||
|
||||
"""
|
||||
meta_sd = model.state_dict()
|
||||
param_dict = dict(model.named_parameters())
|
||||
|
||||
# map names from checkpoint to customized names
|
||||
custom_param_sd, reverse_param_names_mapping = hf_to_custom_state_dict(
|
||||
full_sd_iterator,
|
||||
param_names_mapping,
|
||||
valid_target_names=set(meta_sd.keys()),
|
||||
) # type: ignore
|
||||
|
||||
is_fsdp_model = isinstance(model, FSDPModule) or any(
|
||||
hasattr(p, "device_mesh") for p in meta_sd.values()
|
||||
)
|
||||
|
||||
# sort parameter names to ensure all ranks process parameters in the same order
|
||||
sorted_param_names = sorted(custom_param_sd.keys())
|
||||
|
||||
sharded_sd = {}
|
||||
skipped_checkpoint_keys: list[str] = []
|
||||
non_quantized_dtype_mismatch_counts: Counter[tuple[torch.dtype, torch.dtype]] = (
|
||||
Counter()
|
||||
)
|
||||
non_quantized_dtype_mismatch_examples: dict[
|
||||
tuple[torch.dtype, torch.dtype], list[str]
|
||||
] = defaultdict(list)
|
||||
quantized_dtype_mismatch_counts: Counter[tuple[torch.dtype, torch.dtype]] = (
|
||||
Counter()
|
||||
)
|
||||
quantized_dtype_mismatch_examples: dict[
|
||||
tuple[torch.dtype, torch.dtype], list[str]
|
||||
] = defaultdict(list)
|
||||
|
||||
# shard from loaded state_dict, custom_param_sd -> sharded_sd
|
||||
for target_param_name in sorted_param_names:
|
||||
full_tensor = custom_param_sd[target_param_name]
|
||||
meta_sharded_param = meta_sd.get(target_param_name)
|
||||
|
||||
if meta_sharded_param is None:
|
||||
# For FSDP models, ensure all ranks process parameters consistently
|
||||
if strict or is_fsdp_model:
|
||||
raise ValueError(
|
||||
f"Parameter {target_param_name} not found in custom model state dict. The hf to custom mapping may be incorrect."
|
||||
)
|
||||
else:
|
||||
skipped_checkpoint_keys.append(target_param_name)
|
||||
continue
|
||||
|
||||
# use meta param dtype so quantized params (e.g. FP8) keep their dtype;
|
||||
# for non-quantized models meta dtype equals param_dtype anyway
|
||||
if meta_sharded_param is None:
|
||||
# for nunchaku, some scales are patched later
|
||||
target_dtype = full_tensor.dtype
|
||||
else:
|
||||
target_dtype = meta_sharded_param.dtype
|
||||
|
||||
full_tensor = _maybe_dequantize_fp8(
|
||||
full_tensor, target_dtype, target_param_name, custom_param_sd
|
||||
)
|
||||
|
||||
if full_tensor.dtype != target_dtype:
|
||||
mismatch_key = (full_tensor.dtype, target_dtype)
|
||||
if (
|
||||
full_tensor.dtype in _QUANTIZED_DTYPES
|
||||
or target_dtype in _QUANTIZED_DTYPES
|
||||
):
|
||||
quantized_dtype_mismatch_counts[mismatch_key] += 1
|
||||
if (
|
||||
len(quantized_dtype_mismatch_examples[mismatch_key])
|
||||
< _DTYPE_MISMATCH_EXAMPLE_LIMIT
|
||||
):
|
||||
quantized_dtype_mismatch_examples[mismatch_key].append(
|
||||
target_param_name
|
||||
)
|
||||
else:
|
||||
non_quantized_dtype_mismatch_counts[mismatch_key] += 1
|
||||
if (
|
||||
len(non_quantized_dtype_mismatch_examples[mismatch_key])
|
||||
< _DTYPE_MISMATCH_EXAMPLE_LIMIT
|
||||
):
|
||||
non_quantized_dtype_mismatch_examples[mismatch_key].append(
|
||||
target_param_name
|
||||
)
|
||||
|
||||
if not hasattr(meta_sharded_param, "device_mesh"):
|
||||
full_tensor = full_tensor.to(
|
||||
device=checkpoint_load_device, dtype=target_dtype
|
||||
)
|
||||
actual_param = _get_param_for_weight_loading(
|
||||
model, param_dict, target_param_name
|
||||
)
|
||||
weight_loader = (
|
||||
getattr(actual_param, "weight_loader", None)
|
||||
if actual_param is not None
|
||||
else None
|
||||
)
|
||||
if weight_loader is not None:
|
||||
assert actual_param is not None
|
||||
sharded_tensor = torch.empty_like(
|
||||
meta_sharded_param,
|
||||
device=checkpoint_load_device,
|
||||
dtype=target_dtype,
|
||||
)
|
||||
# Preserve requires_grad flag to avoid errors with non-floating dtypes
|
||||
requires_grad = getattr(meta_sharded_param, "requires_grad", False)
|
||||
temp_param = _make_param_like(actual_param, sharded_tensor)
|
||||
if not (
|
||||
sharded_tensor.is_floating_point() or sharded_tensor.is_complex()
|
||||
):
|
||||
requires_grad = False
|
||||
temp_param.requires_grad = requires_grad
|
||||
try:
|
||||
weight_loader(temp_param, full_tensor)
|
||||
except AssertionError as exc:
|
||||
raise AssertionError(
|
||||
"Failed to shard/load parameter "
|
||||
f"{target_param_name}: full_tensor.shape={tuple(full_tensor.shape)}, "
|
||||
f"meta_sharded_param.shape={tuple(meta_sharded_param.shape)}, "
|
||||
f"temp_param.shape={tuple(temp_param.shape)}, "
|
||||
f"param_cls={type(actual_param).__name__}"
|
||||
) from exc
|
||||
sharded_tensor = temp_param.data
|
||||
else:
|
||||
# In cases where parts of the model aren't sharded, some parameters will be plain tensors
|
||||
sharded_tensor = full_tensor
|
||||
|
||||
# Important: `cpu_offload` is intended for FSDP-managed parameter movement.
|
||||
# If a parameter is not sharded into a DTensor (i.e., no `device_mesh`), FSDP
|
||||
# will NOT manage it. Offloading it here would leave CPU parameters that
|
||||
# later participate in GPU kernels (e.g., conv/embedding), causing device/dtype
|
||||
# mismatches like "Input type (CUDABFloat16Type) and weight type (CPUBFloat16Type)".
|
||||
#
|
||||
# Therefore:
|
||||
# - For non-FSDP models, keep the historical behavior (allow CPU offload).
|
||||
# - For FSDP models, do NOT offload non-sharded parameters here.
|
||||
if cpu_offload and not is_fsdp_model:
|
||||
sharded_tensor = sharded_tensor.cpu()
|
||||
else:
|
||||
full_tensor = full_tensor.to(
|
||||
device=checkpoint_load_device, dtype=target_dtype
|
||||
)
|
||||
actual_param = _get_param_for_weight_loading(
|
||||
model, param_dict, target_param_name
|
||||
)
|
||||
weight_loader = (
|
||||
getattr(actual_param, "weight_loader", None)
|
||||
if actual_param is not None
|
||||
else None
|
||||
)
|
||||
if weight_loader is not None:
|
||||
assert actual_param is not None
|
||||
tp_sharded_tensor = torch.empty(
|
||||
tuple(actual_param.shape),
|
||||
device=checkpoint_load_device,
|
||||
dtype=target_dtype,
|
||||
)
|
||||
temp_param = _make_param_like(actual_param, tp_sharded_tensor)
|
||||
if not (
|
||||
tp_sharded_tensor.is_floating_point()
|
||||
or tp_sharded_tensor.is_complex()
|
||||
):
|
||||
temp_param.requires_grad = False
|
||||
try:
|
||||
weight_loader(temp_param, full_tensor)
|
||||
except AssertionError as exc:
|
||||
raise AssertionError(
|
||||
"Failed to TP-shard/load FSDP parameter "
|
||||
f"{target_param_name}: full_tensor.shape={tuple(full_tensor.shape)}, "
|
||||
f"meta_sharded_param.shape={tuple(meta_sharded_param.shape)}, "
|
||||
f"temp_param.shape={tuple(temp_param.shape)}, "
|
||||
f"param_cls={type(actual_param).__name__}"
|
||||
) from exc
|
||||
full_tensor = temp_param.data
|
||||
sharded_tensor = distribute_tensor(
|
||||
full_tensor,
|
||||
meta_sharded_param.device_mesh,
|
||||
meta_sharded_param.placements,
|
||||
)
|
||||
if cpu_offload:
|
||||
sharded_tensor = sharded_tensor.to("cpu")
|
||||
|
||||
actual_param = param_dict.get(target_param_name)
|
||||
if actual_param is not None:
|
||||
sharded_sd[target_param_name] = _make_param_like(
|
||||
actual_param, sharded_tensor
|
||||
)
|
||||
else:
|
||||
sharded_sd[target_param_name] = nn.Parameter(
|
||||
sharded_tensor, requires_grad=False
|
||||
)
|
||||
|
||||
model.reverse_param_names_mapping = reverse_param_names_mapping
|
||||
|
||||
if non_quantized_dtype_mismatch_counts:
|
||||
logger.debug(
|
||||
"Casting checkpoint tensors to target dtype during load: %s",
|
||||
_format_dtype_mismatch_summary(
|
||||
non_quantized_dtype_mismatch_counts,
|
||||
non_quantized_dtype_mismatch_examples,
|
||||
),
|
||||
main_process_only=True,
|
||||
local_main_process_only=True,
|
||||
)
|
||||
|
||||
if quantized_dtype_mismatch_counts:
|
||||
logger.warning(
|
||||
"Dtype mismatches detected for quantized parameters during load: %s",
|
||||
_format_dtype_mismatch_summary(
|
||||
quantized_dtype_mismatch_counts,
|
||||
quantized_dtype_mismatch_examples,
|
||||
),
|
||||
main_process_only=True,
|
||||
local_main_process_only=True,
|
||||
)
|
||||
|
||||
if skipped_checkpoint_keys:
|
||||
logger.warning(
|
||||
"Checkpoint keys not loaded (no matching model parameter) %s",
|
||||
(
|
||||
skipped_checkpoint_keys[:20]
|
||||
if len(skipped_checkpoint_keys) > 20
|
||||
else skipped_checkpoint_keys
|
||||
),
|
||||
)
|
||||
if len(skipped_checkpoint_keys) > 20:
|
||||
logger.warning(
|
||||
"... and %d more skipped keys.",
|
||||
len(skipped_checkpoint_keys) - 20,
|
||||
)
|
||||
|
||||
# parameters in nn.Module that doesn't exist in safetensor files
|
||||
unused_keys = set(meta_sd.keys()) - set(sharded_sd.keys())
|
||||
if unused_keys:
|
||||
logger.warning("Found unloaded parameters in meta state dict: %s", unused_keys)
|
||||
|
||||
# Legacy allowlist for parameter families synthesized after loading.
|
||||
# New formats should declare missing_param_init on the parameter instead.
|
||||
LEGACY_ALLOWED_NEW_PARAM_PATTERNS = [
|
||||
"gate_compress",
|
||||
"wcscales",
|
||||
"wtscale",
|
||||
"input_scale",
|
||||
"weight_scale",
|
||||
"bias",
|
||||
"norm_q",
|
||||
"norm_k",
|
||||
"weight_scale",
|
||||
]
|
||||
for new_param_name in unused_keys:
|
||||
meta_sharded_param = meta_sd.get(new_param_name)
|
||||
meta_sharded_param_dtype = meta_sharded_param.dtype
|
||||
actual_param = param_dict.get(new_param_name)
|
||||
missing_param_init = (
|
||||
getattr(actual_param, "missing_param_init", None)
|
||||
if actual_param is not None
|
||||
else None
|
||||
)
|
||||
|
||||
if missing_param_init == "error":
|
||||
raise ValueError(
|
||||
f"Required checkpoint parameter '{new_param_name}' was not loaded. "
|
||||
"This usually indicates a checkpoint/model-arch mismatch or a "
|
||||
"broken weight-name mapping."
|
||||
)
|
||||
|
||||
if missing_param_init is None and not any(
|
||||
pattern in new_param_name for pattern in LEGACY_ALLOWED_NEW_PARAM_PATTERNS
|
||||
):
|
||||
logger.error(
|
||||
"Unsupported new parameter: %s. Allowed legacy patterns: %s",
|
||||
new_param_name,
|
||||
LEGACY_ALLOWED_NEW_PARAM_PATTERNS,
|
||||
)
|
||||
raise ValueError(
|
||||
f"New parameter '{new_param_name}' is not supported. "
|
||||
"Checkpoint-specific synthesized parameters should either match "
|
||||
f"{LEGACY_ALLOWED_NEW_PARAM_PATTERNS} or declare missing_param_init."
|
||||
)
|
||||
|
||||
if missing_param_init == "ones" or any(
|
||||
p in new_param_name
|
||||
for p in (
|
||||
"wcscales",
|
||||
"wtscale",
|
||||
"input_scale",
|
||||
"weight_scale",
|
||||
"norm_q",
|
||||
"norm_k",
|
||||
)
|
||||
):
|
||||
init_like = torch.ones_like
|
||||
elif missing_param_init == "zeros" or missing_param_init is None:
|
||||
init_like = torch.zeros_like
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Unsupported missing_param_init={missing_param_init!r} for {new_param_name}"
|
||||
)
|
||||
|
||||
if not hasattr(meta_sharded_param, "device_mesh"):
|
||||
sharded_tensor = init_like(
|
||||
meta_sharded_param,
|
||||
device=checkpoint_load_device,
|
||||
dtype=meta_sharded_param_dtype,
|
||||
)
|
||||
if cpu_offload and not is_fsdp_model:
|
||||
sharded_tensor = sharded_tensor.cpu()
|
||||
else:
|
||||
full_tensor = init_like(
|
||||
meta_sharded_param,
|
||||
device=checkpoint_load_device,
|
||||
dtype=meta_sharded_param_dtype,
|
||||
)
|
||||
sharded_tensor = distribute_tensor(
|
||||
full_tensor,
|
||||
meta_sharded_param.device_mesh,
|
||||
meta_sharded_param.placements,
|
||||
)
|
||||
if cpu_offload:
|
||||
sharded_tensor = sharded_tensor.cpu()
|
||||
sharded_sd[new_param_name] = nn.Parameter(sharded_tensor)
|
||||
|
||||
# choose `assign=True` since we cannot call `copy_` on meta tensor
|
||||
return model.load_state_dict(sharded_sd, strict=strict, assign=True)
|
||||
Reference in New Issue
Block a user