[Bug][Quantization] Fix humming is_layer_skipped for compressed-tensors "re:" ignore entries (#48507)

Signed-off-by: Zhongdongming Dai <zhongdongmin@nvidia.com>

Zhongdongming Dai committed Jul 16, 2026 at 07:47 UTC 75bdad40b5e96517c7f7645ce3199a8557817e17
2 files changed +67 -2
tests/quantization/test_humming_ignore.py new
+56
@@ -0,0 +1,56 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +"""Tests for humming's is_layer_skipped handling of compressed-tensors
4 +regex ("re:") ignore entries.
5 +
6 +Regression test: compressed-tensors checkpoints (e.g. Kimi-K2.6) list ignored
7 +layers as regex patterns prefixed with "re:" (e.g. "re:vision_tower.*").
8 +humming previously substring-matched these literals, so ignored layers were
9 +never skipped and got (incorrectly) quantized -- which then diverged between
10 +the real weight-load path (falls back to unquantized) and the dummy-load path
11 +(stays quantized), breaking any consumer that byte-compares the two.
12 +"""
13 +
14 +import pytest
15 +
16 +from vllm.model_executor.layers.quantization.humming import HummingConfig
17 +
18 +# The real ignore list from Kimi-K2.6's compressed-tensors quantization_config.
19 +K26_IGNORE = [
20 + "re:.*self_attn.*",
21 + "re:.*shared_experts.*",
22 + r"re:.*mlp\.(gate|up|gate_up|down)_proj.*",
23 + "re:.*lm_head.*",
24 + "re:vision_tower.*",
25 + "re:mm_projector.*",
26 +]
27 +
28 +
29 +@pytest.mark.parametrize(
30 + "prefix,expected",
31 + [
32 + # ignored layers (must be skipped -> stay unquantized)
33 + ("vision_tower.encoder.blocks.0.mlp.fc0", True),
34 + ("vision_tower.encoder.blocks.16.wo", True),
35 + ("mm_projector.linear_1", True),
36 + ("language_model.model.layers.0.self_attn.o_proj", True),
37 + ("language_model.model.layers.0.mlp.gate_up_proj", True),
38 + ("language_model.model.layers.0.mlp.down_proj", True),
39 + ("language_model.model.layers.3.mlp.shared_experts.gate_proj", True),
40 + ("model.lm_head", True),
41 + # routed experts are NOT ignored -> must be quantized
42 + ("language_model.model.layers.3.mlp.experts.383.up_proj", False),
43 + ("language_model.model.layers.3.mlp.experts.0.down_proj", False),
44 + ],
45 +)
46 +def test_is_layer_skipped_regex_ignore(prefix, expected):
47 + cfg = HummingConfig(full_config={"ignore": K26_IGNORE})
48 + assert cfg.is_layer_skipped({"ignore": K26_IGNORE}, prefix) is expected
49 +
50 +
51 +def test_plain_substring_entries_still_work():
52 + # bitsandbytes-style modules_to_not_convert use plain substrings.
53 + cfg = HummingConfig()
54 + cfg_dict = {"modules_to_not_convert": ["lm_head", "vision"]}
55 + assert cfg.is_layer_skipped(cfg_dict, "model.vision.encoder.fc") is True
56 + assert cfg.is_layer_skipped(cfg_dict, "model.layers.0.mlp.up_proj") is False
vllm/model_executor/layers/quantization/humming.py
+11 -2
@@ -217,8 +217,17 @@ class HummingConfig(QuantizationConfig):
217 if hasattr(self, "hf_to_vllm_mapper"):
218 ignored_layers = self.hf_to_vllm_mapper.apply_list(ignored_layers)
219
220 - if any(module_name in prefix for module_name in ignored_layers):
221 - return True
220 + for module_name in ignored_layers:
221 + # compressed-tensors style entries may be regex patterns prefixed
222 + # with "re:" (e.g. "re:vision_tower.*"). These must be regex-matched;
223 + # plain substring matching never matches the literal "re:..." string,
224 + # so ignored layers get silently quantized. Non-"re:" entries keep the
225 + # existing substring behavior (e.g. bitsandbytes modules_to_not_convert).
226 + if module_name.startswith("re:"):
227 + if re.match(module_name[3:], prefix):
228 + return True
229 + elif module_name in prefix:
230 + return True
231 if "lm_head" in prefix:
232 return True
233