main
py 782 lines 27.7 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from copy import deepcopy
5 from typing import Any
6
7 import torch
8 from safetensors.torch import _TYPES as _SAFETENSORS_TO_TORCH_DTYPE
9 from transformers import PreTrainedConfig
10
11 import vllm.model_executor.layers.fused_moe # noqa
12 from vllm.logger import init_logger
13 from vllm.model_executor.kernels.linear import (
14 MPLinearLayerConfig,
15 choose_mp_linear_kernel,
16 )
17 from vllm.model_executor.layers.fused_moe import (
18 FusedMoEConfig,
19 FusedMoEMethodBase,
20 FusedMoEQuantConfig,
21 FusedMoeWeightScaleSupported,
22 RoutedExperts,
23 SharedExperts,
24 UnquantizedFusedMoEMethod,
25 )
26 from vllm.model_executor.layers.fused_moe.oracle.int_wna16 import (
27 WNA16MoEBackend,
28 convert_to_wna16_moe_kernel_format,
29 make_wna16_moe_kernel,
30 select_wna16_moe_backend,
31 )
32 from vllm.model_executor.layers.linear import LinearMethodBase, set_weight_attrs
33 from vllm.model_executor.layers.quantization import QuantizationMethods
34 from vllm.model_executor.layers.quantization.base_config import (
35 QuantizationConfig,
36 QuantizeMethodBase,
37 )
38 from vllm.model_executor.layers.quantization.utils import replace_parameter
39 from vllm.model_executor.layers.quantization.utils.gptq_utils import (
40 get_dynamic_override,
41 get_linear_quant_method,
42 normalize_and_validate_gptq_desc_act,
43 override_config,
44 )
45 from vllm.model_executor.layers.quantization.utils.marlin_utils import (
46 check_moe_marlin_supports_layer,
47 get_marlin_input_dtype,
48 marlin_repeat_scales_on_all_ranks,
49 verify_marlin_supported,
50 )
51 from vllm.model_executor.layers.quantization.utils.quant_utils import (
52 QuantKey,
53 kInt4StaticGroupScale,
54 kInt8StaticGroupScale,
55 )
56 from vllm.model_executor.parameter import (
57 ChannelQuantScaleParameter,
58 GroupQuantScaleParameter,
59 PackedColumnParameter,
60 PackedvLLMParameter,
61 )
62 from vllm.scalar_type import scalar_types
63 from vllm.transformers_utils.config import get_safetensors_params_metadata
64 from vllm.utils.collection_utils import is_list_of
65
66 logger = init_logger(__name__)
67
68
69 def get_moe_quant_method(
70 config: "AutoGPTQConfig",
71 layer: RoutedExperts,
72 prefix: str,
73 moe_method_cls: type,
74 ):
75 cloned_config = deepcopy(config)
76
77 assert isinstance(layer, RoutedExperts)
78 # False = skip module, None = no override, else = Positive match
79 if (
80 get_dynamic_override( # noqa: E712
81 cloned_config, # noqa: E712
82 layer_name=prefix,
83 )
84 == False
85 ): # noqa: E712
86 return UnquantizedFusedMoEMethod(layer.moe_config)
87
88 if prefix:
89 # Dynamic per module/layer rules may override base config
90 override_config(cloned_config, prefix=prefix)
91
92 return moe_method_cls(cloned_config, layer.moe_config)
93
94
95 class AutoGPTQConfig(QuantizationConfig):
96 """Config class for AutoGPTQ quantization using Marlin kernels."""
97
98 # (num_bits, is_sym) -> quant_type
99 TYPE_MAP = {
100 (4, True): scalar_types.uint4b8,
101 (8, True): scalar_types.uint8b128,
102 }
103
104 def __init__(
105 self,
106 weight_bits: int,
107 group_size: int,
108 desc_act: bool,
109 is_sym: bool,
110 lm_head_quantized: bool,
111 dynamic: dict[str, dict[str, int | bool]],
112 full_config: dict[str, Any],
113 modules_in_block_to_quantize: list[str] | None = None,
114 ) -> None:
115 super().__init__()
116 desc_act = normalize_and_validate_gptq_desc_act(desc_act, group_size, dynamic)
117
118 # GPTQModel use `dynamic` config property to allow per module
119 # quantization config so each module can be individually optimized.
120 # Format is dict[str, dict] where key is a regex string that can
121 # perform both positive ("+:" prefixed) or negative ("-:" prefixed)
122 # matching of a module.
123 # Default to positive match, override base quant config mode, if no
124 # prefix is used. Value is in dict format of field key and override
125 # value.
126 # Negative matching will skip quantization init for this module
127 # entirely:
128 # non-quantized inference. More details and quantization examples can be
129 # found at: https://github.com/ModelCloud/GPTQModel
130 # Example:
131 # # last 1/2 of the layers 10-21 has 8bit vs 4bit for 0-9
132 # # last 1/4 of the layers 16-21 has 8bit and group_size 64
133 # dynamic = {
134 # #`.*\.` matches the layers_node prefix
135 # # positive match layer 10-15
136 # r"+:.*\.(?:1[0-5])\..*": {"bits": 8,},
137 # # positive match layer 16-21
138 # r"+:.*\.(?:1[6-9]|20|21)\..*": {"bits": 8, "group_size": 64,},
139 # r"-:.*\.moe\..*": {}, # negative match (skip) all `moe` layers
140 # }
141 self.dynamic = dynamic
142
143 self.weight_bits = weight_bits
144 self.is_sym = is_sym
145
146 self.pack_factor = 32 // weight_bits # packed into int32
147 self.group_size = group_size
148 self.desc_act: bool = desc_act
149 self.lm_head_quantized = lm_head_quantized
150 self.full_config = full_config
151
152 if (weight_bits, is_sym) not in self.TYPE_MAP:
153 raise ValueError(
154 f"Unsupported quantization config: bits={weight_bits}, sym={is_sym}"
155 )
156
157 self.quant_type = self.TYPE_MAP[(weight_bits, is_sym)]
158
159 self.modules_in_block_to_quantize = modules_in_block_to_quantize or []
160 # used to identify GPTQ model quantized by autoround
161 self.autoround_version = full_config.get("autoround_version", "")
162
163 def __repr__(self) -> str:
164 return (
165 f"AutoGPTQConfig(quant_type={self.quant_type}, "
166 f"group_size={self.group_size}, "
167 f"desc_act={self.desc_act}, "
168 f"lm_head_quantized={self.lm_head_quantized}, "
169 f"dynamic={self.dynamic}, "
170 f"modules_in_block_to_quantize={self.modules_in_block_to_quantize})"
171 )
172
173 @classmethod
174 def get_name(cls) -> QuantizationMethods:
175 return "auto_gptq"
176
177 @classmethod
178 def get_supported_act_dtypes(cls) -> list[torch.dtype]:
179 return [torch.half, torch.bfloat16]
180
181 @classmethod
182 def get_min_capability(cls) -> int:
183 return 60
184
185 @classmethod
186 def get_config_filenames(cls) -> list[str]:
187 return ["quantize_config.json"]
188
189 @classmethod
190 def from_config(cls, config: dict[str, Any]) -> "AutoGPTQConfig":
191 dynamic = cls.get_from_keys_or(config, ["dynamic"], default={})
192 dynamic = {} if dynamic is None else dynamic
193
194 weight_bits = cls.get_from_keys(config, ["bits"])
195 group_size = cls.get_from_keys(config, ["group_size"])
196 desc_act = cls.get_from_keys(config, ["desc_act"])
197 is_sym = cls.get_from_keys(config, ["sym"])
198 lm_head_quantized = cls.get_from_keys_or(config, ["lm_head"], default=False)
199 modules_in_block_to_quantize = cls.get_from_keys_or(
200 config, ["modules_in_block_to_quantize"], default=None
201 )
202 return cls(
203 weight_bits,
204 group_size,
205 desc_act,
206 is_sym,
207 lm_head_quantized,
208 dynamic,
209 config,
210 modules_in_block_to_quantize,
211 )
212
213 @classmethod
214 def override_quantization_method(
215 cls, hf_quant_cfg, user_quant, hf_config=None
216 ) -> QuantizationMethods | None:
217 """Override to use AutoGPTQ for compatible GPTQ models."""
218 quant_method = hf_quant_cfg.get("quant_method", "").lower()
219
220 if quant_method != "gptq":
221 return None
222
223 is_valid_user_quant = user_quant is None or user_quant in (
224 "gptq",
225 "gptq_marlin",
226 "auto_gptq",
227 "marlin",
228 )
229
230 if is_valid_user_quant:
231 return cls.get_name()
232
233 return None
234
235 def get_quant_method(
236 self, layer: torch.nn.Module, prefix: str
237 ) -> "QuantizeMethodBase | None":
238 if isinstance(layer, RoutedExperts):
239 from vllm.model_executor.layers.quantization.moe_wna16 import MoeWNA16Config
240
241 if not check_moe_marlin_supports_layer(
242 layer, self.group_size, allow_tile_padding=True
243 ):
244 logger.warning_once(
245 f"Layer '{prefix}' is not supported by GPTQMoeMarlin. "
246 "Falling back to Moe WNA16 kernels."
247 )
248 return MoeWNA16Config.from_config(self.full_config).get_quant_method(
249 layer, prefix
250 )
251 moe_quant_method = get_moe_quant_method(
252 self, layer, prefix, AutoGPTQMoEMethod
253 )
254 if moe_quant_method is None:
255 return None
256 moe_quant_method.input_dtype = get_marlin_input_dtype(prefix)
257 return moe_quant_method
258
259 quant_method = get_linear_quant_method(
260 self, layer, prefix, AutoGPTQLinearMethod
261 )
262 if quant_method is None:
263 return None
264 quant_method.input_dtype = get_marlin_input_dtype(prefix)
265 return quant_method
266
267 def apply_vllm_mapper(self, hf_to_vllm_mapper):
268 if self.modules_in_block_to_quantize is not None:
269 self.modules_in_block_to_quantize = hf_to_vllm_mapper.apply_list(
270 self.modules_in_block_to_quantize
271 )
272
273 def maybe_update_config(
274 self,
275 model_name: str,
276 hf_config: PreTrainedConfig | None = None,
277 revision: str | None = None,
278 ):
279 if self.modules_in_block_to_quantize:
280 if is_list_of(self.modules_in_block_to_quantize, list):
281 # original modules_in_block_to_quantize: list[list[str]]
282 # flatten original modules_in_block_to_quantize
283 self.modules_in_block_to_quantize = [
284 item
285 for sublist in self.modules_in_block_to_quantize
286 for item in sublist
287 ]
288 return
289
290 unquant_dtypes = [torch.float16, torch.bfloat16, torch.float32]
291 metadata = get_safetensors_params_metadata(model_name, revision=revision)
292 quant_layers: set[str] = {
293 param_name.rsplit(".", 1)[0]
294 for param_name, info in metadata.items()
295 if (dtype := info.get("dtype", None))
296 and _SAFETENSORS_TO_TORCH_DTYPE[dtype] not in unquant_dtypes
297 }
298 self.modules_in_block_to_quantize = list(quant_layers)
299
300
301 class AutoGPTQLinearMethod(LinearMethodBase):
302 """Linear method for AutoGPTQ using Marlin kernels.
303
304 Args:
305 quant_config: The AutoGPTQ quantization config.
306
307 """
308
309 _kernel_backends_being_used: set[str] = set()
310
311 def __init__(self, quant_config: AutoGPTQConfig) -> None:
312 self.quant_config = quant_config
313 self.input_dtype = None
314 self.quant_type = self.quant_config.quant_type
315
316 # Verify supported on platform.
317 verify_marlin_supported(
318 quant_type=self.quant_config.quant_type,
319 group_size=self.quant_config.group_size,
320 )
321
322 def create_weights(
323 self,
324 layer: torch.nn.Module,
325 input_size_per_partition: int,
326 output_partition_sizes: list[int],
327 input_size: int,
328 output_size: int,
329 params_dtype: torch.dtype,
330 **extra_weight_attrs,
331 ) -> None:
332 output_size_per_partition = sum(output_partition_sizes)
333 is_row_parallel = input_size != input_size_per_partition
334 weight_loader = extra_weight_attrs.get("weight_loader")
335 input_dtype = self.input_dtype
336
337 mp_linear_kernel_config = MPLinearLayerConfig(
338 full_weight_shape=(input_size, output_size),
339 partition_weight_shape=(
340 input_size_per_partition,
341 output_size_per_partition,
342 ),
343 weight_type=self.quant_config.quant_type,
344 act_type=params_dtype if input_dtype is None else input_dtype,
345 group_size=self.quant_config.group_size,
346 zero_points=False,
347 )
348
349 kernel_type = choose_mp_linear_kernel(mp_linear_kernel_config)
350
351 if kernel_type.__name__ not in self._kernel_backends_being_used:
352 logger.info("Using %s for AutoGPTQLinearMethod", kernel_type.__name__)
353 self._kernel_backends_being_used.add(kernel_type.__name__)
354
355 # Normalize group_size
356 if self.quant_config.group_size != -1:
357 group_size = self.quant_config.group_size
358 else:
359 group_size = input_size
360
361 # Determine sharding
362 if marlin_repeat_scales_on_all_ranks(
363 self.quant_config.group_size, is_row_parallel
364 ):
365 # By setting scale_dim == None, weight_loader will
366 # repeat the scales on each GPU in TP>1 case.
367 scales_and_zp_input_dim = None
368 scales_and_zp_size = input_size // group_size
369 else:
370 # By setting scale_dim == 0, weight_loader will
371 # shard the scales in TP>1 case.
372 scales_and_zp_input_dim = 0
373 scales_and_zp_size = input_size_per_partition // group_size
374
375 # Quantized weights
376 qweight = PackedvLLMParameter(
377 data=torch.empty(
378 input_size_per_partition // self.quant_config.pack_factor,
379 output_size_per_partition,
380 dtype=torch.int32,
381 ),
382 input_dim=0,
383 output_dim=1,
384 packed_dim=0,
385 packed_factor=self.quant_config.pack_factor,
386 weight_loader=weight_loader,
387 )
388
389 qzeros_args = {
390 "data": torch.empty(
391 scales_and_zp_size,
392 output_size_per_partition // self.quant_config.pack_factor,
393 dtype=torch.int32,
394 ),
395 "weight_loader": weight_loader,
396 }
397 weight_scale_args = {
398 "data": torch.empty(
399 scales_and_zp_size,
400 output_size_per_partition,
401 dtype=params_dtype,
402 ),
403 "weight_loader": weight_loader,
404 }
405
406 if scales_and_zp_input_dim is None:
407 scales = ChannelQuantScaleParameter(output_dim=1, **weight_scale_args)
408 qzeros = PackedColumnParameter(
409 output_dim=1,
410 packed_dim=1,
411 packed_factor=self.quant_config.pack_factor,
412 **qzeros_args,
413 )
414
415 else:
416 scales = GroupQuantScaleParameter(
417 output_dim=1, input_dim=0, **weight_scale_args
418 )
419 qzeros = PackedvLLMParameter(
420 input_dim=0,
421 output_dim=1,
422 packed_dim=1,
423 packed_factor=self.quant_config.pack_factor,
424 **qzeros_args,
425 )
426
427 layer.register_parameter("qweight", qweight)
428 layer.register_parameter("scales", scales)
429 layer.register_parameter("qzeros", qzeros)
430
431 self.kernel = kernel_type(
432 mp_linear_kernel_config,
433 w_q_param_name="qweight",
434 w_s_param_name="scales",
435 w_zp_param_name="qzeros",
436 )
437
438 def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
439 self.kernel.process_weights_after_loading(layer)
440
441 def apply(
442 self,
443 layer: torch.nn.Module,
444 x: torch.Tensor,
445 bias: torch.Tensor | None = None,
446 ) -> torch.Tensor:
447 return self.kernel.apply_weights(layer, x, bias)
448
449
450 class AutoGPTQMoEMethod(FusedMoEMethodBase):
451 """MoE Marlin method with quantization."""
452
453 def __init__(
454 self,
455 quant_config: AutoGPTQConfig,
456 moe: FusedMoEConfig,
457 ) -> None:
458 super().__init__(moe)
459 self.quant_config = quant_config
460 if self.quant_config.quant_type.size_bits == 4:
461 quant_type = scalar_types.uint4b8
462 scale = kInt4StaticGroupScale
463 elif self.quant_config.quant_type.size_bits == 8:
464 quant_type = scalar_types.uint8b128
465 scale = kInt8StaticGroupScale
466 else:
467 raise ValueError("AutoGPTQMoEMethod only supports int4 and int8 now.")
468 self.input_dtype = None
469 self.use_marlin = True
470 weight_key = QuantKey(quant_type, scale)
471
472 self.wna16_moe_backend, self.experts_cls = select_wna16_moe_backend(
473 moe,
474 weight_key,
475 quant_config=self.quant_config,
476 may_have_zp=not self.quant_config.is_sym,
477 may_have_bias=True,
478 allow_tile_padding=True,
479 )
480
481 def create_weights(
482 self,
483 layer: RoutedExperts,
484 num_experts: int,
485 hidden_size: int,
486 intermediate_size_per_partition: int,
487 params_dtype: torch.dtype,
488 **extra_weight_attrs,
489 ):
490 layer.input_dtype = self.input_dtype
491 is_a_8bit = self.input_dtype is not None and self.input_dtype.itemsize == 1
492
493 if is_a_8bit:
494 assert self.quant_config.quant_type.size_bits == 8, (
495 "W8A8-INT8 is not supported by marlin kernel."
496 )
497
498 if self.quant_config.group_size != -1:
499 scales_size13 = hidden_size // self.quant_config.group_size
500 w2_scales_size = intermediate_size_per_partition
501 scales_size2 = w2_scales_size // self.quant_config.group_size
502 strategy = FusedMoeWeightScaleSupported.GROUP.value
503 else:
504 scales_size13 = 1
505 scales_size2 = 1
506 strategy = FusedMoeWeightScaleSupported.CHANNEL.value
507
508 layer.num_groups_w13 = scales_size13
509 layer.num_groups_w2 = scales_size2
510
511 extra_weight_attrs.update({"quant_method": strategy, "is_transposed": True})
512 # Fused gate_up_proj (column parallel)
513 w13_qweight = torch.nn.Parameter(
514 torch.empty(
515 num_experts,
516 hidden_size // self.quant_config.pack_factor,
517 self.moe.w13_num_shards * intermediate_size_per_partition,
518 dtype=torch.int32,
519 ),
520 requires_grad=False,
521 )
522 layer.register_parameter("w13_qweight", w13_qweight)
523 set_weight_attrs(w13_qweight, extra_weight_attrs)
524 # down_proj (row parallel)
525 w2_qweight = torch.nn.Parameter(
526 torch.empty(
527 num_experts,
528 intermediate_size_per_partition // self.quant_config.pack_factor,
529 hidden_size,
530 dtype=torch.int32,
531 ),
532 requires_grad=False,
533 )
534 layer.register_parameter("w2_qweight", w2_qweight)
535 set_weight_attrs(w2_qweight, extra_weight_attrs)
536 # up_proj scales
537 w13_scales = torch.nn.Parameter(
538 torch.empty(
539 num_experts,
540 scales_size13,
541 self.moe.w13_num_shards * intermediate_size_per_partition,
542 dtype=params_dtype,
543 ),
544 requires_grad=False,
545 )
546 layer.register_parameter("w13_scales", w13_scales)
547 set_weight_attrs(w13_scales, extra_weight_attrs)
548 # down_proj scales
549 w2_scales = torch.nn.Parameter(
550 torch.empty(num_experts, scales_size2, hidden_size, dtype=params_dtype),
551 requires_grad=False,
552 )
553 layer.register_parameter("w2_scales", w2_scales)
554 set_weight_attrs(w2_scales, extra_weight_attrs)
555 # up_proj zero points
556 w13_qzeros = torch.nn.Parameter(
557 torch.empty(
558 num_experts,
559 scales_size13,
560 self.moe.w13_num_shards
561 * intermediate_size_per_partition
562 // self.quant_config.pack_factor,
563 dtype=torch.int32,
564 ),
565 requires_grad=False,
566 )
567 layer.register_parameter("w13_qzeros", w13_qzeros)
568 set_weight_attrs(w13_qzeros, extra_weight_attrs)
569 # down_proj zero points
570 w2_qzeros = torch.nn.Parameter(
571 torch.empty(
572 num_experts,
573 scales_size2,
574 hidden_size // self.quant_config.pack_factor,
575 dtype=torch.int32,
576 ),
577 requires_grad=False,
578 )
579 layer.register_parameter("w2_qzeros", w2_qzeros)
580 set_weight_attrs(w2_qzeros, extra_weight_attrs)
581
582 # Some GPTQ checkpoints contain expert biases even when the model
583 # architecture does not declare them. Zero initialization keeps
584 # checkpoints without biases equivalent to the bias-free path.
585 w13_bias = torch.nn.Parameter(
586 torch.zeros(
587 num_experts,
588 self.moe.w13_num_shards * intermediate_size_per_partition,
589 dtype=params_dtype,
590 ),
591 requires_grad=False,
592 )
593 layer.register_parameter("w13_bias", w13_bias)
594 set_weight_attrs(w13_bias, extra_weight_attrs)
595 w2_bias = torch.nn.Parameter(
596 torch.zeros(num_experts, hidden_size, dtype=params_dtype),
597 requires_grad=False,
598 )
599 layer.register_parameter("w2_bias", w2_bias)
600 set_weight_attrs(w2_bias, extra_weight_attrs)
601
602 def process_weights_after_loading(self, layer: RoutedExperts) -> None:
603 def replace_or_register(name: str, val: torch.Tensor | None):
604 if val is None:
605 return
606
607 if hasattr(layer, name):
608 replace_parameter(layer, name, val)
609 else:
610 layer.register_parameter(
611 name, torch.nn.Parameter(val, requires_grad=False)
612 )
613
614 is_a_8bit = self.input_dtype is not None and self.input_dtype.itemsize == 1
615
616 assert not is_a_8bit or self.quant_config.quant_type.size_bits == 8, (
617 "W8A8-INT8 is not supported by marlin kernel."
618 )
619
620 w13_bias = getattr(layer, "w13_bias", None)
621 if "w13_bias" not in layer._loaded_expert_biases:
622 layer.register_parameter("w13_bias", None)
623 w13_bias = None
624 w2_bias = getattr(layer, "w2_bias", None)
625 if "w2_bias" not in layer._loaded_expert_biases:
626 layer.register_parameter("w2_bias", None)
627 w2_bias = None
628
629 # Normalize GPTQ K-first layout to canonical N-first format.
630 w13 = layer.w13_qweight.data.transpose(1, 2).contiguous()
631 w2 = layer.w2_qweight.data.transpose(1, 2).contiguous()
632 w13_scale = layer.w13_scales.data.transpose(1, 2).contiguous()
633 w2_scale = layer.w2_scales.data.transpose(1, 2).contiguous()
634 w13_qzeros = getattr(layer, "w13_qzeros", None)
635 w2_qzeros = getattr(layer, "w2_qzeros", None)
636 if w13_qzeros is not None:
637 w13_qzeros = w13_qzeros.data.transpose(1, 2).contiguous()
638 if w2_qzeros is not None:
639 w2_qzeros = w2_qzeros.data.transpose(1, 2).contiguous()
640
641 converted = convert_to_wna16_moe_kernel_format(
642 backend=self.wna16_moe_backend,
643 layer=layer,
644 quant_config=self.quant_config,
645 input_dtype=self.input_dtype,
646 w13=w13,
647 w2=w2,
648 w13_scale=w13_scale,
649 w2_scale=w2_scale,
650 w13_bias=w13_bias,
651 w2_bias=w2_bias,
652 w13_qzeros=w13_qzeros,
653 w2_qzeros=w2_qzeros,
654 )
655
656 if converted is None:
657 # Backend rewrote the layer's params in place (e.g. Humming).
658 self._setup_kernel(layer)
659 return
660
661 (
662 w13,
663 w2,
664 w13_scale,
665 w2_scale,
666 w13_qzeros,
667 w2_qzeros,
668 w13_input_global_scale,
669 w2_input_global_scale,
670 w13_bias,
671 w2_bias,
672 ) = converted
673
674 replace_parameter(layer, "w13_qweight", w13)
675 replace_parameter(layer, "w2_qweight", w2)
676 replace_parameter(layer, "w13_scales", w13_scale)
677 replace_parameter(layer, "w2_scales", w2_scale)
678 replace_or_register("w13_input_global_scale", w13_input_global_scale)
679 replace_or_register("w2_input_global_scale", w2_input_global_scale)
680 replace_or_register("w13_bias", w13_bias)
681 replace_or_register("w2_bias", w2_bias)
682 replace_or_register("w13_qzeros", w13_qzeros)
683 replace_or_register("w2_qzeros", w2_qzeros)
684
685 # The modular kernel reads w13_weight/w2_weight; marlin keeps *_qweight.
686 layer.w13_weight = layer.w13_qweight
687 layer.w2_weight = layer.w2_qweight
688
689 self._setup_kernel(layer)
690
691 def _setup_kernel(self, layer: RoutedExperts) -> None:
692 """Build the FusedMoEKernel for this layer."""
693 self.moe_quant_config = self.get_fused_moe_quant_config(layer)
694 self.moe_kernel = make_wna16_moe_kernel(
695 moe_quant_config=self.moe_quant_config,
696 moe_config=self.moe,
697 experts_cls=self.experts_cls,
698 backend=self.wna16_moe_backend,
699 routing_tables=layer._expert_routing_tables(),
700 )
701
702 def get_fused_moe_quant_config(self, layer: RoutedExperts) -> FusedMoEQuantConfig:
703 if self.wna16_moe_backend == WNA16MoEBackend.HUMMING:
704 from vllm.model_executor.layers.quantization.utils.humming import (
705 get_humming_moe_quant_config,
706 )
707
708 return get_humming_moe_quant_config(
709 layer,
710 gemm1_alpha=getattr(layer, "swiglu_alpha", None),
711 gemm1_beta=getattr(layer, "swiglu_beta", None),
712 gemm1_clamp_limit=getattr(layer, "swiglu_limit", None),
713 )
714
715 from vllm.model_executor.layers.fused_moe.config import (
716 gptq_marlin_moe_quant_config,
717 )
718
719 # CPU fused_experts_cpu requires zero points even for symmetric quant
720 use_zp = (
721 not self.quant_config.is_sym
722 or self.wna16_moe_backend == WNA16MoEBackend.CPU
723 )
724 return gptq_marlin_moe_quant_config(
725 w1_scale=layer.w13_scales,
726 w2_scale=layer.w2_scales,
727 weight_bits=self.quant_config.weight_bits,
728 group_size=self.quant_config.group_size,
729 w1_zp=getattr(layer, "w13_qzeros", None) if use_zp else None,
730 w2_zp=getattr(layer, "w2_qzeros", None) if use_zp else None,
731 w1_bias=getattr(layer, "w13_bias", None),
732 w2_bias=getattr(layer, "w2_bias", None),
733 )
734
735 def apply(
736 self,
737 layer: RoutedExperts,
738 x: torch.Tensor,
739 topk_weights: torch.Tensor,
740 topk_ids: torch.Tensor,
741 shared_experts: SharedExperts | None,
742 shared_experts_input: torch.Tensor | None,
743 ) -> torch.Tensor:
744 assert not self.is_monolithic
745 assert self.moe_kernel is not None
746 return self.moe_kernel.apply(
747 hidden_states=x,
748 w1=layer.w13_weight,
749 w2=layer.w2_weight,
750 topk_weights=topk_weights,
751 topk_ids=topk_ids,
752 activation=layer.activation,
753 global_num_experts=layer.global_num_experts,
754 apply_router_weight_on_input=layer.apply_router_weight_on_input,
755 expert_map=layer.expert_map,
756 shared_experts=shared_experts,
757 shared_experts_input=shared_experts_input,
758 )
759
760 def apply_monolithic(
761 self,
762 layer: RoutedExperts,
763 x: torch.Tensor,
764 router_logits: torch.Tensor,
765 input_ids: torch.Tensor | None = None,
766 ) -> torch.Tensor:
767 assert self.is_monolithic
768 assert self.moe_kernel is not None
769 return self.moe_kernel.apply_monolithic(
770 hidden_states=x,
771 w1=layer.w13_weight,
772 w2=layer.w2_weight,
773 router_logits=router_logits,
774 activation=layer.activation,
775 global_num_experts=layer.global_num_experts,
776 expert_map=layer.expert_map,
777 apply_router_weight_on_input=layer.apply_router_weight_on_input,
778 num_expert_group=layer.num_expert_group,
779 topk_group=layer.topk_group,
780 e_score_correction_bias=layer.e_score_correction_bias,
781 routed_scaling_factor=layer.routed_scaling_factor,
782 )