Files
wehub-resource-sync 770d92cb1f
Lint / lint (push) Has been cancelled
Build Docs / Deploy Docs (push) Has been cancelled
Windows CI / Windows (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:23:58 +08:00

153 lines
5.9 KiB
Python

"""Standard HuggingFace loader mapping helpers."""
from __future__ import annotations
import functools
from collections.abc import Iterable, Sequence
from typing import Callable, Optional, Type # noqa: UP035
import numpy as np
from tvm.relax.frontend import nn
from mlc_llm.loader import ExternMapping
from mlc_llm.quantization import Quantization
NameTransform = Callable[[str], str]
ExportSpecGetter = Callable[[nn.Module], object]
def _default_export_spec(model: nn.Module) -> object:
return model.get_default_spec()
def make_standard_hf_loader(
*,
model_cls: Type[nn.Module], # noqa: UP006
layer_prefix: str = "model.layers",
qkv_names: Sequence[str] = ("q_proj", "k_proj", "v_proj"),
qkv_concat_axis: int = 0,
qkv_target_name: str = "qkv_proj",
add_qkv_bias: bool = False,
qkv_bias_optional: bool = False,
gate_up_names: Sequence[str] = ("gate_proj", "up_proj"),
gate_up_concat_axis: int = 0,
gate_up_target_name: str = "gate_up_proj",
include_qkv: bool = True,
include_gate_up: bool = True,
add_unused: Optional[Iterable[str]] = None, # noqa: UP045
hf_prefix: str = "model.",
name_transform: Optional[NameTransform] = None, # noqa: UP045
export_spec_getter: Optional[ExportSpecGetter] = None, # noqa: UP045
num_layers_getter: Optional[Callable[[object], int]] = None, # noqa: UP045
) -> Callable[[object, Quantization], ExternMapping]:
"""Create a standard loader for HuggingFace weights.
This handles the common QKV concatenation, gate+up concatenation, optional
QKV bias mapping, and passes through remaining parameters 1:1.
"""
if not qkv_names:
include_qkv = False
if not gate_up_names:
include_gate_up = False
if not include_qkv:
qkv_names = ()
if not include_gate_up:
gate_up_names = ()
def _default_name_transform(name: str) -> str:
# When hf_prefix is empty, strip the "model." prefix so models that
# expose bare top-level weights (no "model." namespace) still load.
if hf_prefix == "":
return name[6:] if name.startswith("model.") else name
return name
name_transform_fn = name_transform or _default_name_transform
spec_getter = export_spec_getter or _default_export_spec
unused_names = tuple(add_unused or ())
def huggingface(
model_config: object,
quantization: Quantization,
) -> ExternMapping:
model = model_cls(model_config)
if quantization is not None:
model.to(quantization.model_dtype)
_, _named_params, _ = model.export_tvm(
spec=spec_getter(model),
allow_extern=True,
)
named_parameters = dict(_named_params)
mapping = ExternMapping()
if include_qkv or include_gate_up or unused_names:
if num_layers_getter is None:
num_layers = model_config.num_hidden_layers
else:
num_layers = num_layers_getter(model_config)
for i in range(num_layers):
attn = f"{layer_prefix}.{i}.self_attn"
if include_qkv:
mlc_qkv_name = f"{attn}.{qkv_target_name}.weight"
mlc_param = named_parameters[mlc_qkv_name]
mapping.add_mapping(
mlc_qkv_name,
[name_transform_fn(f"{attn}.{name}.weight") for name in qkv_names],
functools.partial(
lambda q, k, v, dtype: np.concatenate(
[q, k, v], axis=qkv_concat_axis
).astype(dtype),
dtype=mlc_param.dtype,
),
)
if add_qkv_bias:
mlc_bias_name = f"{attn}.{qkv_target_name}.bias"
if (not qkv_bias_optional) or mlc_bias_name in named_parameters:
mlc_param = named_parameters[mlc_bias_name]
mapping.add_mapping(
mlc_bias_name,
[name_transform_fn(f"{attn}.{name}.bias") for name in qkv_names],
functools.partial(
lambda q, k, v, dtype: np.concatenate(
[q, k, v], axis=qkv_concat_axis
).astype(dtype),
dtype=mlc_param.dtype,
),
)
if include_gate_up:
mlp = f"{layer_prefix}.{i}.mlp"
mlc_gate_up_name = f"{mlp}.{gate_up_target_name}.weight"
if gate_up_names:
mlc_param = named_parameters[mlc_gate_up_name]
mapping.add_mapping(
mlc_gate_up_name,
[name_transform_fn(f"{mlp}.{name}.weight") for name in gate_up_names],
functools.partial(
lambda gate, up, dtype: np.concatenate(
[gate, up], axis=gate_up_concat_axis
).astype(dtype),
dtype=mlc_param.dtype,
),
)
for unused_name in unused_names:
mapping.add_unused(name_transform_fn(f"{attn}.{unused_name}"))
for mlc_name, mlc_param in named_parameters.items():
if mlc_name not in mapping.param_map:
mapping.add_mapping(
mlc_name,
[name_transform_fn(mlc_name)],
functools.partial(
lambda x, dtype: x.astype(dtype),
dtype=mlc_param.dtype,
),
)
return mapping
return huggingface