main
py 663 lines 25.2 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from typing import Any
5
6 import torch
7
8 from vllm.distributed import get_tensor_model_parallel_rank, get_tp_group
9 from vllm.model_executor.layers.fused_moe import (
10 FusedMoEConfig,
11 FusedMoEMethodBase,
12 FusedMoeWeightScaleSupported,
13 RoutedExperts,
14 SharedExperts,
15 )
16 from vllm.model_executor.layers.fused_moe.config import (
17 FusedMoEQuantConfig,
18 )
19 from vllm.model_executor.layers.fused_moe.oracle.int_wna16 import (
20 WNA16MoEBackend,
21 convert_to_wna16_moe_kernel_format,
22 make_wna16_moe_kernel,
23 make_wna16_moe_quant_config,
24 select_wna16_moe_backend,
25 )
26 from vllm.model_executor.layers.fused_moe.unquantized_fused_moe_method import (
27 UnquantizedFusedMoEMethod,
28 )
29 from vllm.model_executor.layers.linear import LinearBase, UnquantizedLinearMethod
30 from vllm.model_executor.layers.quantization import QuantizationMethods
31 from vllm.model_executor.layers.quantization.base_config import (
32 QuantizationConfig,
33 QuantizeMethodBase,
34 )
35 from vllm.model_executor.layers.quantization.utils.gptq_utils import (
36 normalize_and_validate_gptq_desc_act,
37 )
38 from vllm.model_executor.layers.quantization.utils.quant_utils import (
39 INT4_DTYPE,
40 INT8_DTYPE,
41 QuantKey,
42 kInt4Static32GroupScale,
43 kInt4StaticGroupScale,
44 kInt8StaticGroupScale,
45 )
46 from vllm.model_executor.utils import replace_parameter, set_weight_attrs
47 from vllm.platforms import current_platform
48
49
50 class MoeWNA16Config(QuantizationConfig):
51 """Config class for MOE WNA16 (W8A16/W4A16) quantization."""
52
53 def __init__(
54 self,
55 linear_quant_method: str,
56 weight_bits: int,
57 group_size: int,
58 has_zp: bool,
59 lm_head_quantized: bool,
60 modules_to_not_convert: list[str] | None,
61 full_config: dict[str, Any],
62 ) -> None:
63 super().__init__()
64 self.weight_bits = weight_bits
65 self.group_size = group_size
66 self.has_zp = has_zp
67 self.bit8_pack_factor = 8 // self.weight_bits
68 self.lm_head_quantized = lm_head_quantized
69 self.linear_quant_method = linear_quant_method
70 self.full_config = full_config
71 # Avoid circular import
72 from vllm.model_executor.layers.quantization.auto_awq import AutoAWQConfig
73
74 if self.linear_quant_method == "gptq":
75 normalize_and_validate_gptq_desc_act(
76 full_config.get("desc_act", False),
77 group_size,
78 full_config.get("dynamic") or {},
79 )
80 elif self.linear_quant_method in ("awq", "awq_marlin"):
81 capability_tuple = current_platform.get_device_capability()
82 device_capability = (
83 -1 if capability_tuple is None else capability_tuple.to_int()
84 )
85 awq_min_capability = AutoAWQConfig.get_min_capability()
86 if device_capability < awq_min_capability:
87 raise ValueError(
88 "The quantization method moe_wna16 + awq is not supported "
89 "for the current GPU. "
90 f"Minimum capability: {awq_min_capability}. "
91 f"Current capability: {device_capability}."
92 )
93 else:
94 raise ValueError("moe_wna16 only support gptq and awq.")
95
96 if modules_to_not_convert is None:
97 self.modules_to_not_convert = []
98 else:
99 self.modules_to_not_convert = modules_to_not_convert
100
101 @classmethod
102 def get_name(cls) -> QuantizationMethods:
103 return "moe_wna16"
104
105 @classmethod
106 def get_supported_act_dtypes(cls) -> list[torch.dtype]:
107 return [torch.bfloat16, torch.half]
108
109 @classmethod
110 def get_min_capability(cls) -> int:
111 return 70
112
113 @classmethod
114 def get_config_filenames(cls) -> list[str]:
115 return ["quantize_config.json"]
116
117 @classmethod
118 def from_config(cls, config: dict[str, Any]) -> "MoeWNA16Config":
119 linear_quant_method = cls.get_from_keys(config, ["quant_method"])
120 weight_bits = cls.get_from_keys(config, ["bits"])
121 group_size = cls.get_from_keys(config, ["group_size"])
122 lm_head_quantized = cls.get_from_keys_or(config, ["lm_head"], default=False)
123 if linear_quant_method == "gptq":
124 has_zp = not cls.get_from_keys(config, ["sym"])
125 modules_to_not_convert = []
126 elif linear_quant_method in ("awq", "awq_marlin"):
127 has_zp = cls.get_from_keys(config, ["zero_point"])
128 modules_to_not_convert = cls.get_from_keys_or(
129 config, ["modules_to_not_convert"], None
130 )
131 else:
132 raise ValueError("moe_wna16 only support gptq and awq.")
133
134 return cls(
135 linear_quant_method,
136 weight_bits,
137 group_size,
138 has_zp,
139 lm_head_quantized,
140 modules_to_not_convert,
141 config,
142 )
143
144 @classmethod
145 def override_quantization_method(
146 cls, hf_quant_cfg, user_quant, hf_config=None
147 ) -> QuantizationMethods | None:
148 can_convert = cls.is_moe_wna16_compatible(hf_quant_cfg)
149 if can_convert and user_quant == "moe_wna16":
150 return cls.get_name()
151 return None
152
153 @classmethod
154 def is_moe_wna16_compatible(cls, quant_config: dict[str, Any]):
155 # Extract data from quant config.
156 quant_method = quant_config.get("quant_method", "").lower()
157 num_bits = quant_config.get("bits")
158 desc_act = quant_config.get("desc_act", False)
159 try:
160 normalize_and_validate_gptq_desc_act(
161 desc_act,
162 quant_config.get("group_size"),
163 quant_config.get("dynamic") or {},
164 )
165 supported_act_order = True
166 except ValueError:
167 supported_act_order = False
168
169 capability_tuple = current_platform.get_device_capability()
170 device_capability = (
171 -1 if capability_tuple is None else capability_tuple.to_int()
172 )
173 # Avoid circular import
174 from vllm.model_executor.layers.quantization.auto_awq import AutoAWQConfig
175
176 awq_min_capability = AutoAWQConfig.get_min_capability()
177
178 gptq_compatible = (
179 quant_method == "gptq" and supported_act_order and num_bits in [4, 8]
180 )
181 awq_compatible = (
182 quant_method == "awq"
183 and num_bits == 4
184 and device_capability >= awq_min_capability
185 )
186
187 return gptq_compatible or awq_compatible
188
189 def get_quant_method(
190 self, layer: torch.nn.Module, prefix: str
191 ) -> "QuantizeMethodBase | None":
192 if is_layer_skipped_quant(prefix, self.modules_to_not_convert):
193 if isinstance(layer, RoutedExperts):
194 return UnquantizedFusedMoEMethod(layer.moe_config)
195 return UnquantizedLinearMethod()
196 elif isinstance(layer, LinearBase):
197 # Avoid circular import
198 from vllm.model_executor.layers.quantization.auto_awq import AutoAWQConfig
199 from vllm.model_executor.layers.quantization.auto_gptq import (
200 AutoGPTQConfig,
201 )
202
203 linear_config: QuantizationConfig
204 if self.linear_quant_method == "gptq":
205 linear_config = AutoGPTQConfig.from_config(self.full_config)
206 elif self.linear_quant_method in ("awq", "awq_marlin"):
207 linear_config = AutoAWQConfig.from_config(self.full_config)
208 else:
209 raise ValueError("moe_wna16 only support gptq and awq.")
210
211 # The delegate is rebuilt from the raw HF quantization dict, which
212 # lists shard names and never fused ones. Without the mapping it
213 # resolves fused layers (qkv_proj, gate_up_proj) to
214 # UnquantizedLinearMethod and the checkpoint's qweight has nowhere
215 # to load.
216 linear_config.packed_modules_mapping = self.packed_modules_mapping
217 return linear_config.get_quant_method(layer, prefix)
218 elif isinstance(layer, RoutedExperts):
219 return MoeWNA16Method(self, layer.moe_config)
220 return None
221
222
223 def is_layer_skipped_quant(prefix: str, modules_to_not_convert: list[str]):
224 return any(module_name in prefix for module_name in modules_to_not_convert)
225
226
227 class MoeWNA16Method(FusedMoEMethodBase):
228 """Linear method for MOE WNA16 (W8A16/W4A16) quantization.
229
230 Args:
231 quant_config: The MOE WNA16 (W8A16/W4A16) quantization config.
232
233 """
234
235 def __init__(self, quant_config: MoeWNA16Config, moe: "FusedMoEConfig") -> None:
236 super().__init__(moe)
237 self.quant_config = quant_config
238
239 num_bits = self.quant_config.weight_bits
240 group_size = self.quant_config.group_size
241
242 if num_bits == 4:
243 quant_type = INT4_DTYPE
244 if group_size == 32:
245 scale = kInt4Static32GroupScale
246 else:
247 scale = kInt4StaticGroupScale
248 elif num_bits == 8:
249 quant_type = INT8_DTYPE
250 scale = kInt8StaticGroupScale
251 else:
252 raise ValueError("MoeWNA16Method only supports int4 and int8 now.")
253
254 weight_key = QuantKey(quant_type, scale)
255
256 # Select WNA16 MoE backend via oracle.
257 # handle ZP?
258 self.wna16_backend, self.experts_cls = select_wna16_moe_backend(
259 config=self.moe,
260 weight_key=weight_key,
261 quant_config=self.quant_config,
262 may_have_zp=self.quant_config.has_zp,
263 may_have_bias=False,
264 )
265
266 def create_weights(
267 self,
268 layer: RoutedExperts,
269 num_experts: int,
270 hidden_size: int,
271 intermediate_size_per_partition: int,
272 params_dtype: torch.dtype,
273 **extra_weight_attrs,
274 ):
275 layer.quant_config = self.quant_config
276 bit8_pack_factor = self.quant_config.bit8_pack_factor
277 group_size = self.quant_config.group_size
278 group_size_div_factor = 1
279
280 # make intermediate_size and hidden_size divisible by group_size
281 # we reduce the group size to ensure that
282 # and we would repeat the loaded_weight later
283 while intermediate_size_per_partition % group_size or hidden_size % group_size:
284 group_size = group_size // 2
285 group_size_div_factor *= 2
286 assert group_size >= 32
287 layer.group_size = group_size
288 layer.group_size_div_factor = group_size_div_factor
289
290 strategy = FusedMoeWeightScaleSupported.GROUP.value
291 extra_weight_attrs.update({"quant_method": strategy, "is_transposed": False})
292
293 assert "weight_loader" in extra_weight_attrs
294 weight_loader = extra_weight_attrs["weight_loader"]
295 wrapped_weight_loader = MoeWNA16Method.get_weight_loader(layer, weight_loader)
296 extra_weight_attrs["weight_loader"] = wrapped_weight_loader
297
298 # Fused gate_up_proj (column parallel)
299 w13_qweight = torch.nn.Parameter(
300 torch.empty(
301 num_experts,
302 self.moe.w13_num_shards * intermediate_size_per_partition,
303 hidden_size // bit8_pack_factor,
304 dtype=torch.uint8,
305 ),
306 requires_grad=False,
307 )
308 layer.register_parameter("w13_qweight", w13_qweight)
309 set_weight_attrs(w13_qweight, extra_weight_attrs)
310
311 # down_proj (row parallel)
312 w2_qweight = torch.nn.Parameter(
313 torch.empty(
314 num_experts,
315 hidden_size,
316 intermediate_size_per_partition // bit8_pack_factor,
317 dtype=torch.uint8,
318 ),
319 requires_grad=False,
320 )
321 layer.register_parameter("w2_qweight", w2_qweight)
322 set_weight_attrs(w2_qweight, extra_weight_attrs)
323
324 w13_scales = torch.nn.Parameter(
325 torch.zeros(
326 num_experts,
327 self.moe.w13_num_shards * intermediate_size_per_partition,
328 hidden_size // group_size,
329 dtype=params_dtype,
330 ),
331 requires_grad=False,
332 )
333 layer.register_parameter("w13_scales", w13_scales)
334 set_weight_attrs(w13_scales, extra_weight_attrs)
335
336 w2_scales = torch.nn.Parameter(
337 torch.zeros(
338 num_experts,
339 hidden_size,
340 intermediate_size_per_partition // group_size,
341 dtype=params_dtype,
342 ),
343 requires_grad=False,
344 )
345 layer.register_parameter("w2_scales", w2_scales)
346 set_weight_attrs(w2_scales, extra_weight_attrs)
347
348 if self.quant_config.has_zp:
349 w13_qzeros = torch.nn.Parameter(
350 torch.zeros(
351 num_experts,
352 self.moe.w13_num_shards
353 * intermediate_size_per_partition
354 // bit8_pack_factor,
355 hidden_size // group_size,
356 dtype=torch.uint8,
357 ),
358 requires_grad=False,
359 )
360 layer.register_parameter("w13_qzeros", w13_qzeros)
361 set_weight_attrs(w13_qzeros, extra_weight_attrs)
362
363 w2_qzeros = torch.nn.Parameter(
364 torch.zeros(
365 num_experts,
366 hidden_size // bit8_pack_factor,
367 intermediate_size_per_partition // group_size,
368 dtype=torch.uint8,
369 ),
370 requires_grad=False,
371 )
372 layer.register_parameter("w2_qzeros", w2_qzeros)
373 set_weight_attrs(w2_qzeros, extra_weight_attrs)
374
375 if self.quant_config.linear_quant_method == "gptq":
376 # some param are unused, but we need to init them in order to
377 # load weights
378 invalid_param_keys: list[str] = []
379 if not self.quant_config.has_zp:
380 invalid_param_keys += ["w13_qzeros", "w2_qzeros"]
381 for key in invalid_param_keys:
382 param = torch.nn.Parameter(
383 torch.empty((0,), dtype=torch.int32), requires_grad=False
384 )
385 layer.register_parameter(key, param)
386 set_weight_attrs(param, extra_weight_attrs)
387
388 def get_fused_moe_quant_config(
389 self, layer: RoutedExperts
390 ) -> FusedMoEQuantConfig | None:
391 if self.wna16_backend == WNA16MoEBackend.HUMMING:
392 from vllm.model_executor.layers.quantization.utils.humming import (
393 get_humming_moe_quant_config,
394 )
395
396 return get_humming_moe_quant_config(
397 layer,
398 gemm1_alpha=getattr(layer, "swiglu_alpha", None),
399 gemm1_beta=getattr(layer, "swiglu_beta", None),
400 gemm1_clamp_limit=getattr(layer, "swiglu_limit", None),
401 )
402
403 has_zp = self.quant_config.has_zp
404 return make_wna16_moe_quant_config(
405 w1_scale=layer.w13_scales,
406 w2_scale=layer.w2_scales,
407 w1_zp=layer.w13_qzeros if has_zp else None,
408 w2_zp=layer.w2_qzeros if has_zp else None,
409 group_size=layer.group_size,
410 num_bits=self.quant_config.weight_bits,
411 )
412
413 def _setup_kernel(self, layer: RoutedExperts):
414 assert self.experts_cls is not None
415 self.moe_quant_config = self.get_fused_moe_quant_config(layer)
416 assert self.moe_quant_config is not None
417 self.moe_kernel = make_wna16_moe_kernel(
418 moe_quant_config=self.moe_quant_config,
419 moe_config=self.moe,
420 experts_cls=self.experts_cls,
421 backend=self.wna16_backend,
422 routing_tables=layer._expert_routing_tables(),
423 )
424
425 def process_weights_after_loading(self, layer: RoutedExperts) -> None:
426 has_zp = self.quant_config.has_zp
427 converted = convert_to_wna16_moe_kernel_format(
428 backend=self.wna16_backend,
429 layer=layer,
430 quant_config=self.quant_config,
431 input_dtype=None,
432 w13=layer.w13_qweight,
433 w2=layer.w2_qweight,
434 w13_scale=layer.w13_scales,
435 w2_scale=layer.w2_scales,
436 w13_qzeros=layer.w13_qzeros if has_zp else None,
437 w2_qzeros=layer.w2_qzeros if has_zp else None,
438 )
439
440 if converted is None:
441 # Backend rewrote the layer's params in place (e.g. Humming).
442 self._setup_kernel(layer)
443 return
444
445 (
446 w13_qweight,
447 w2_qweight,
448 w13_scales,
449 w2_scales,
450 w13_qzeros,
451 w2_qzeros,
452 w13_input_global_scale,
453 w2_input_global_scale,
454 _, # w13_bias
455 _, # w2_bias
456 ) = converted
457
458 # Replace common parameters
459 replace_parameter(layer, "w13_qweight", w13_qweight)
460 replace_parameter(layer, "w2_qweight", w2_qweight)
461 replace_parameter(layer, "w13_scales", w13_scales)
462 replace_parameter(layer, "w2_scales", w2_scales)
463 layer.w13_weight = layer.w13_qweight
464 layer.w2_weight = layer.w2_qweight
465
466 if has_zp:
467 assert w13_qzeros is not None and w2_qzeros is not None
468 replace_parameter(layer, "w13_qzeros", w13_qzeros)
469 replace_parameter(layer, "w2_qzeros", w2_qzeros)
470
471 # Marlin-specific parameters (not needed for Flashinfer)
472 if self.wna16_backend != WNA16MoEBackend.FLASHINFER_TRTLLM:
473 # Register input global scales if present
474 if w13_input_global_scale is not None:
475 layer.register_parameter(
476 "w13_input_global_scale",
477 torch.nn.Parameter(w13_input_global_scale, requires_grad=False),
478 )
479 if w2_input_global_scale is not None:
480 layer.register_parameter(
481 "w2_input_global_scale",
482 torch.nn.Parameter(w2_input_global_scale, requires_grad=False),
483 )
484
485 self._setup_kernel(layer)
486
487 def apply(
488 self,
489 layer: RoutedExperts,
490 x: torch.Tensor,
491 topk_weights: torch.Tensor,
492 topk_ids: torch.Tensor,
493 shared_experts: SharedExperts | None,
494 shared_experts_input: torch.Tensor | None,
495 ) -> torch.Tensor:
496 assert not self.is_monolithic
497 assert self.moe_kernel is not None
498 return self.moe_kernel.apply(
499 x,
500 layer.w13_weight,
501 layer.w2_weight,
502 topk_weights=topk_weights,
503 topk_ids=topk_ids,
504 activation=layer.activation,
505 global_num_experts=layer.global_num_experts,
506 expert_map=layer.expert_map,
507 apply_router_weight_on_input=layer.apply_router_weight_on_input,
508 shared_experts=shared_experts,
509 shared_experts_input=shared_experts_input,
510 )
511
512 def apply_monolithic(
513 self,
514 layer: RoutedExperts,
515 x: torch.Tensor,
516 router_logits: torch.Tensor,
517 input_ids: torch.Tensor | None = None,
518 ) -> torch.Tensor:
519 assert self.is_monolithic
520 assert self.moe_kernel is not None
521 return self.moe_kernel.apply_monolithic(
522 x,
523 layer.w13_weight,
524 layer.w2_weight,
525 router_logits,
526 activation=layer.activation,
527 global_num_experts=layer.global_num_experts,
528 expert_map=layer.expert_map,
529 apply_router_weight_on_input=layer.apply_router_weight_on_input,
530 num_expert_group=layer.num_expert_group,
531 topk_group=layer.topk_group,
532 e_score_correction_bias=layer.e_score_correction_bias,
533 routed_scaling_factor=layer.routed_scaling_factor,
534 )
535
536 @staticmethod
537 def get_weight_loader(layer, weight_loader):
538 def convert_awq_tensor(tensor, tensor_type):
539 # convert awq qweight/qzeros to a standard format (assume int4)
540 # qweight: (k, n // pack_factor_bit32) -> (n, k // pack_factor_bit8)
541 # qzeros: (k // group_size, n // pack_factor_bit32) ->
542 # (n // pack_factor_bit8, k // group_size)
543 # pack_factor_bit32 = 32 // weight_bits
544 # pack_factor_bit8 = 8 // weight_bits
545
546 # 0. suppose origin shape (a, b), dtype int32
547 # 1. convert to uint8, shape (a, b) -> (a, 4 * b)
548 size0 = tensor.size(0)
549 tensor = tensor.view(torch.uint8)
550
551 # 2. unpack to uint4 (only when weight_bits == 4)
552 # shape (a, 4 * b) -> (a, 4 * b, 2)
553 shifter = torch.tensor([0, 4], dtype=torch.uint8, device=tensor.device)
554 tensor = (tensor[:, :, None] >> shifter) & 0xF
555
556 # 3. change order, see
557 # https://github.com/casper-hansen/AutoAWQ/blob/v0.2.8/awq/utils/quant_utils.py
558 # shape -> (a, 4 * b * pack_factor_bit8)
559 reverse_awq_pack_order = [0, 4, 1, 5, 2, 6, 3, 7]
560 tensor = tensor.view(-1, 8)[:, reverse_awq_pack_order]
561 tensor = tensor.view(size0, -1)
562
563 # 4. transpose, shape -> (4 * b * pack_factor_bit8, a)
564 tensor = tensor.T.contiguous()
565
566 # 5. repack (only when weight_bits == 4)
567 # qweight shape -> (4 * b * pack_factor_bit8, a // pack_factor_bit8)
568 # qzeros shape -> (4 * b, a)
569
570 if tensor_type == "qweight":
571 tensor = tensor[:, 1::2] * 16 + tensor[:, ::2]
572 elif tensor_type == "qzeros":
573 tensor = tensor[1::2, :] * 16 + tensor[::2, :]
574 return tensor
575
576 def convert_gptq_int4_qzeros(tensor):
577 tensor = tensor.view(torch.uint8)
578 shifter = torch.tensor([0, 4], dtype=torch.uint8, device=tensor.device)
579 tensor = (tensor[:, :, None] >> shifter) & 0xF
580 tensor = tensor + 1
581 tensor = tensor[:, :, 0] + tensor[:, :, 1] * 16
582 return tensor
583
584 def moe_wna16_weight_loader(
585 param: torch.nn.Parameter,
586 loaded_weight: torch.Tensor,
587 weight_name: str,
588 shard_id: str,
589 expert_id: int,
590 return_success: bool = False,
591 ):
592 if not layer.quant_config.has_zp and "qzeros" in weight_name:
593 return False if return_success else None
594
595 device = get_tp_group().device
596 tp_rank = get_tensor_model_parallel_rank()
597 loaded_weight = loaded_weight.to(device)
598 shard_size = layer.intermediate_size_per_partition
599
600 # convert gptq and awq weight to a standard format
601 # awq_marlin uses the same weight format as awq
602 if layer.quant_config.linear_quant_method in ("awq", "awq_marlin"):
603 assert layer.quant_config.weight_bits == 4
604 if "weight" in weight_name:
605 loaded_weight = convert_awq_tensor(loaded_weight, "qweight")
606 elif "zeros" in weight_name:
607 loaded_weight = convert_awq_tensor(loaded_weight, "qzeros")
608 else:
609 loaded_weight = loaded_weight.T
610 elif layer.quant_config.linear_quant_method == "gptq":
611 assert layer.quant_config.weight_bits in [4, 8]
612 if "weight" in weight_name:
613 loaded_weight = loaded_weight.T.contiguous().view(torch.uint8)
614 elif "zeros" in weight_name:
615 # add 1 to gptq qzeros to align with awq
616 loaded_weight = loaded_weight.view(torch.uint8)
617 if layer.quant_config.weight_bits == 4:
618 loaded_weight = convert_gptq_int4_qzeros(loaded_weight).T
619 else:
620 loaded_weight = loaded_weight.T + 1
621 else:
622 loaded_weight = loaded_weight.T
623
624 # repeat the qzeros/scales to fit new group size
625 if (
626 layer.group_size_div_factor > 1
627 and "qzeros" in weight_name
628 or "scales" in weight_name
629 ):
630 loaded_weight = loaded_weight.repeat_interleave(
631 layer.group_size_div_factor, 1
632 )
633
634 if "w13_qzeros" in weight_name:
635 tensor = loaded_weight.view(
636 layer.moe_config.tp_size, -1, loaded_weight.size(1)
637 )[tp_rank]
638 # qzeros are packed along the intermediate dim by
639 # bit8_pack_factor, so the w1/w3 boundary is in packed units
640 # (equals shard_size // 2 only for 4-bit).
641 shard_zp_size = shard_size // layer.quant_config.bit8_pack_factor
642 if shard_id == "w1":
643 param.data[expert_id, :shard_zp_size] = tensor
644 else:
645 param.data[expert_id, shard_zp_size:] = tensor
646 return True if return_success else None
647 elif "w2_qzeros" in weight_name:
648 param.data[expert_id] = loaded_weight.view(
649 loaded_weight.size(0), layer.moe_config.tp_size, -1
650 )[:, tp_rank]
651 return True if return_success else None
652 else:
653 # Delegate to the original loader, passing return_success
654 return weight_loader(
655 param,
656 loaded_weight,
657 weight_name,
658 shard_id,
659 expert_id,
660 return_success=return_success,
661 )
662
663 return moe_wna16_weight_loader