| |
| """AutoRound(auto_round:auto_gptq, mixed 2/3/4/8-bit, v1) -> vLLM quant config. |
| |
| Reuses OneComp's MixedGPTQConfig + its numerically-validated v1 kernels UNCHANGED |
| (grouped_moe / fused_dq_gemm / mixed_moe). The ONLY adaptation is parsing: AutoRound |
| stores per-module bits in a flat `extra_config` keyed by full module path + a global |
| `bits`(=16 "unquantized unless overridden"), `group_size`, `sym`; we convert that to |
| the plugin's per-layer `quantization_bits` list-of-dicts {suffix: {bits, method}}. |
| Non-overridden (global-16) modules are omitted -> dispatch returns UnquantizedLinear |
| (BF16) for router gate / indexer / norms / embed / lm_head. |
| |
| Verified (see VLLM_M3_PORT_SPEC.md): all non-expert quantized modules are 4/8-bit |
| (stock AutoGPTQ Marlin), only routed experts are mixed 2/3/4/8 (MixedGPTQMoEMethod). |
| v1 +1 dequant convention is identical between mixed_gptq and auto_round:auto_gptq, so |
| the kernels are reused with zero numeric change -> no quantization-side degradation. |
| """ |
| import os |
| import re |
| import sys |
|
|
| sys.path.insert(0, os.environ.get("ONECOMP_PATH", "OneCompression")) |
|
|
| from vllm.model_executor.layers.quantization import register_quantization_config |
| from vllm.model_executor.layers.linear import LinearBase, UnquantizedLinearMethod |
| from vllm_plugins.gptq.vllm_plugin import MixedGPTQConfig |
|
|
| |
| |
| |
| |
| from vllm_plugins.utils import module as _oc_module |
| _oc_module._FUSED_TO_CONSTITUENTS.pop("gate_up_proj", None) |
|
|
| _LAYER_RE = re.compile(r"\.layers\.(\d+)\.(.+)$") |
|
|
|
|
| def autoround_extra_config_to_quantization_bits(extra_config: dict) -> list: |
| """Flat AutoRound extra_config -> per-layer list[ {suffix: {bits, method}} ].""" |
| num_layers = 0 |
| parsed = [] |
| for full_key, v in extra_config.items(): |
| bits = v.get("bits") if isinstance(v, dict) else None |
| if bits is None or bits >= 16: |
| continue |
| m = _LAYER_RE.search(full_key) |
| if not m: |
| continue |
| layer_idx, suffix = int(m.group(1)), m.group(2) |
| num_layers = max(num_layers, layer_idx + 1) |
| parsed.append((layer_idx, suffix, int(bits))) |
| qbits = [dict() for _ in range(num_layers)] |
| for layer_idx, suffix, bits in parsed: |
| cfg = {"bits": bits, "method": "gptq"} |
| qbits[layer_idx][suffix] = cfg |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if suffix.startswith("mlp.shared_experts."): |
| alias = "block_sparse_moe.shared_experts." + suffix[len("mlp.shared_experts."):] |
| qbits[layer_idx][alias] = cfg |
| return qbits |
|
|
|
|
| @register_quantization_config("autoround_mixed") |
| class AutoRoundMixedConfig(MixedGPTQConfig): |
| """MixedGPTQConfig fed from an AutoRound quantization_config dict.""" |
|
|
| @classmethod |
| def get_name(cls) -> str: |
| return "autoround_mixed" |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| def get_quant_method(self, layer, prefix: str): |
| import os |
| |
| |
| |
| |
| |
| |
| |
| |
| if (os.environ.get("M3_OFFICIAL_PORT") == "1" |
| and isinstance(layer, LinearBase) |
| and prefix.endswith(".self_attn.qkv_proj")): |
| return UnquantizedLinearMethod() |
| return super().get_quant_method(layer, prefix) |
|
|
| @classmethod |
| def from_config(cls, config: dict) -> "AutoRoundMixedConfig": |
| extra = config.get("extra_config", {}) or {} |
| qbits = autoround_extra_config_to_quantization_bits(extra) |
| return cls( |
| quantization_bits=qbits, |
| group_size=config.get("group_size", 128), |
| desc_act=False, |
| sym=config.get("sym", True), |
| lm_head_quantized=False, |
| checkpoint_format="gptq", |
| ) |
|
|
|
|
| |
| if __name__ == "__main__": |
| import json |
| cfgp = os.path.join(os.environ.get("M3_CKPT", os.path.dirname(os.path.abspath(__file__))), "quantization_config.json") |
| cfg = json.load(open(cfgp)) |
| qb = autoround_extra_config_to_quantization_bits(cfg.get("extra_config", {})) |
| print(f"layers in quantization_bits: {len(qb)}") |
| |
| def show(L, needle): |
| hit = {k: v for k, v in qb[L].items() if needle in k} |
| |
| sample = dict(list(hit.items())[:2]) |
| print(f" L{L} [{needle}]: n={len(hit)} sample={sample}") |
| show(0, "self_attn.q_proj") |
| show(0, "mlp.gate_up_proj") |
| show(3, "self_attn.q_proj") |
| show(3, "self_attn.o_proj") |
| show(3, "mlp.experts.0.") |
| show(3, "mlp.shared_experts") |
| show(59, "self_attn.o_proj") |
| |
| tot = sum(len(d) for d in qb) |
| from collections import Counter |
| bitc = Counter(v["bits"] for d in qb for v in d.values()) |
| print(f"total quantized modules: {tot} | bits histogram: {dict(sorted(bitc.items()))}") |
| print("M3_QUANT_SELFTEST_OK") |
|
|