main
py 561 lines 19.9 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 # ruff: noqa: E501
4
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 """Inference-only K-EXAONE-236B-A22B model compatible with HuggingFace weights."""
17
18 from collections.abc import Iterable
19 from itertools import islice
20
21 import torch
22 from torch import nn
23 from transformers import PreTrainedConfig
24
25 from vllm.compilation.decorators import support_torch_compile
26 from vllm.config import CacheConfig, VllmConfig, get_current_vllm_config
27 from vllm.distributed import (
28 get_ep_group,
29 get_pp_group,
30 get_tensor_model_parallel_world_size,
31 )
32 from vllm.model_executor.layers.attention import Attention
33 from vllm.model_executor.layers.fused_moe import (
34 FusedMoEFactory,
35 GateLinear,
36 )
37 from vllm.model_executor.layers.layernorm import RMSNorm
38 from vllm.model_executor.layers.linear import (
39 QKVParallelLinear,
40 RowParallelLinear,
41 )
42 from vllm.model_executor.layers.logits_processor import LogitsProcessor
43 from vllm.model_executor.layers.quantization import QuantizationConfig
44 from vllm.model_executor.layers.rotary_embedding import get_rope
45 from vllm.model_executor.layers.vocab_parallel_embedding import (
46 ParallelLMHead,
47 VocabParallelEmbedding,
48 )
49 from vllm.sequence import IntermediateTensors
50 from vllm.transformers_utils.config import set_default_rope_theta
51
52 from .exaone4 import Exaone4GatedMLP as ExaoneMoeGatedMLP
53 from .interfaces import SupportsLoRA, SupportsPP
54 from .utils import (
55 AutoWeightsLoader,
56 PPMissingLayer,
57 WeightsMapper,
58 extract_layer_index,
59 make_empty_intermediate_tensors_factory,
60 make_layers,
61 maybe_prefix,
62 )
63
64
65 class ExaoneMoe(nn.Module):
66 def __init__(
67 self,
68 config: PreTrainedConfig,
69 swiglu_limit: float | None = None,
70 quant_config: QuantizationConfig | None = None,
71 prefix: str = "",
72 enable_eplb: bool = False,
73 ):
74 super().__init__()
75 self.tp_size = get_tensor_model_parallel_world_size()
76
77 self.routed_scaling_factor = config.routed_scaling_factor
78
79 self.ep_group = get_ep_group().device_group
80 self.ep_size = self.ep_group.size()
81 self.n_routed_experts = config.num_experts
82
83 if self.tp_size > config.num_experts:
84 raise ValueError(
85 f"Tensor parallel size {self.tp_size} is greater than "
86 f"the number of experts {config.num_experts}."
87 )
88
89 self.gate = GateLinear(
90 config.hidden_size,
91 config.num_experts,
92 prefix=f"{prefix}.gate",
93 )
94
95 self.e_score_correction_bias = nn.Parameter(
96 torch.empty(config.num_experts, dtype=torch.float32)
97 )
98
99 # Load balancing settings.
100 vllm_config = get_current_vllm_config()
101 eplb_config = vllm_config.parallel_config.eplb_config
102 self.enable_eplb = enable_eplb
103
104 self.n_logical_experts = self.n_routed_experts
105 eplb_config.num_redundant_experts = (
106 eplb_config.num_redundant_experts
107 if eplb_config.num_redundant_experts is not None
108 else 0
109 )
110 self.n_redundant_experts = eplb_config.num_redundant_experts
111 self.n_physical_experts = self.n_logical_experts + self.n_redundant_experts
112 self.n_local_physical_experts = self.n_physical_experts // self.ep_size
113
114 self.shared_experts: ExaoneMoeGatedMLP | None
115 if getattr(config, "num_shared_experts", 0) > 0:
116 intermediate_size = config.moe_intermediate_size * config.num_shared_experts
117 self.shared_experts = ExaoneMoeGatedMLP(
118 hidden_size=config.hidden_size,
119 intermediate_size=intermediate_size,
120 hidden_act=config.hidden_act,
121 quant_config=quant_config,
122 reduce_results=False,
123 swiglu_limit=swiglu_limit,
124 prefix=f"{prefix}.shared_experts",
125 )
126 else:
127 self.shared_experts = None
128
129 self.experts = FusedMoEFactory(
130 shared_experts=self.shared_experts,
131 gate=self.gate,
132 num_experts=self.n_routed_experts,
133 top_k=config.num_experts_per_tok,
134 hidden_size=config.hidden_size,
135 intermediate_size=config.moe_intermediate_size,
136 renormalize=config.norm_topk_prob,
137 quant_config=quant_config,
138 activation="silu",
139 swiglu_limit=swiglu_limit,
140 use_grouped_topk=True,
141 num_expert_group=config.n_group,
142 topk_group=config.topk_group,
143 prefix=f"{prefix}.experts",
144 scoring_func="sigmoid",
145 routed_scaling_factor=self.routed_scaling_factor,
146 e_score_correction_bias=self.e_score_correction_bias,
147 enable_eplb=self.enable_eplb,
148 num_redundant_experts=self.n_redundant_experts,
149 )
150
151 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
152 # NOTE: hidden_states can have either 1D or 2D shape.
153 orig_shape = hidden_states.shape
154 hidden_dim = hidden_states.shape[-1]
155 hidden_states = hidden_states.view(-1, hidden_dim)
156
157 final_hidden_states = self.experts(
158 hidden_states=hidden_states, router_logits=hidden_states
159 )
160
161 return final_hidden_states.view(orig_shape)
162
163
164 class ExaoneMoeAttention(nn.Module):
165 def __init__(
166 self,
167 config,
168 hidden_size: int,
169 num_heads: int,
170 num_kv_heads: int,
171 max_position_embeddings: int = 8192,
172 quant_config: QuantizationConfig | None = None,
173 bias: bool = False,
174 cache_config: CacheConfig | None = None,
175 prefix: str = "",
176 is_mtp: bool = False,
177 ) -> None:
178 super().__init__()
179 self.hidden_size = hidden_size
180 tp_size = get_tensor_model_parallel_world_size()
181 self.total_num_heads = num_heads
182 assert self.total_num_heads % tp_size == 0
183 self.num_heads = self.total_num_heads // tp_size
184 self.total_num_kv_heads = num_kv_heads
185 if self.total_num_kv_heads >= tp_size:
186 # Number of KV heads is greater than TP size, so we partition
187 # the KV heads across multiple tensor parallel GPUs.
188 assert self.total_num_kv_heads % tp_size == 0
189 else:
190 # Number of KV heads is less than TP size, so we replicate
191 # the KV heads across multiple tensor parallel GPUs.
192 assert tp_size % self.total_num_kv_heads == 0
193 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
194 # MistralConfig has an optional head_dim introduced by Mistral-Nemo
195 self.head_dim = getattr(config, "head_dim", None)
196 if self.head_dim is None:
197 self.head_dim = self.hidden_size // self.total_num_heads
198 self.q_size = self.num_heads * self.head_dim
199 self.kv_size = self.num_kv_heads * self.head_dim
200 self.scaling = self.head_dim**-0.5
201 self.max_position_embeddings = max_position_embeddings
202
203 self.qkv_proj = QKVParallelLinear(
204 hidden_size=hidden_size,
205 head_size=self.head_dim,
206 total_num_heads=self.total_num_heads,
207 total_num_kv_heads=self.total_num_kv_heads,
208 bias=bias,
209 quant_config=quant_config,
210 prefix=f"{prefix}.qkv_proj",
211 )
212
213 self.o_proj = RowParallelLinear(
214 input_size=self.total_num_heads * self.head_dim,
215 output_size=hidden_size,
216 bias=bias,
217 quant_config=quant_config,
218 prefix=f"{prefix}.o_proj",
219 )
220
221 self.q_norm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
222 self.k_norm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
223
224 is_neox_style = True
225 if quant_config is not None and quant_config.get_name() == "gguf":
226 is_neox_style = False
227
228 layer_idx = extract_layer_index(prefix)
229
230 if config.sliding_windows is not None:
231 self.sliding_window_size = config.sliding_windows[layer_idx]
232
233 if config.layer_types[layer_idx] == "full_attention":
234 self.sliding_window_size = None
235
236 if is_mtp:
237 self.sliding_window = (
238 config.mtp_layer_types[layer_idx] == "sliding_attention"
239 )
240 if config.mtp_sliding_windows is not None:
241 self.sliding_window_size = config.mtp_sliding_windows[layer_idx]
242
243 if config.mtp_layer_types[layer_idx] == "full_attention":
244 self.sliding_window_size = None
245
246 # apply rotary embeddings to every layer in full attention models
247 self.apply_rope_all_layers = "sliding_attention" not in config.layer_types
248
249 set_default_rope_theta(config, default_theta=1000000)
250 self.rotary_emb = get_rope(
251 self.head_dim,
252 max_position=max_position_embeddings,
253 rope_parameters=config.rope_parameters,
254 is_neox_style=is_neox_style,
255 )
256 self.attn = Attention(
257 self.num_heads,
258 self.head_dim,
259 self.scaling,
260 num_kv_heads=self.num_kv_heads,
261 cache_config=cache_config,
262 quant_config=quant_config,
263 per_layer_sliding_window=self.sliding_window_size,
264 prefix=f"{prefix}.attn",
265 )
266
267 def forward(
268 self,
269 positions: torch.Tensor,
270 hidden_states: torch.Tensor,
271 ) -> torch.Tensor:
272 qkv, _ = self.qkv_proj(hidden_states)
273 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
274
275 q = q.unflatten(-1, (self.num_heads, self.head_dim))
276 q = self.q_norm(q)
277 q = q.flatten(-2, -1)
278 k = k.unflatten(-1, (self.num_kv_heads, self.head_dim))
279 k = self.k_norm(k)
280 k = k.flatten(-2, -1)
281
282 if self.sliding_window_size or self.apply_rope_all_layers:
283 q, k = self.rotary_emb(positions, q, k)
284 attn_output = self.attn(q, k, v)
285 output, _ = self.o_proj(attn_output)
286 return output
287
288
289 class ExaoneMoeDecoderLayer(nn.Module):
290 def __init__(
291 self,
292 config: PreTrainedConfig,
293 cache_config: CacheConfig | None = None,
294 quant_config: QuantizationConfig | None = None,
295 is_mtp: bool = False,
296 prefix: str = "",
297 ) -> None:
298 super().__init__()
299 layer_idx = extract_layer_index(prefix)
300 self.hidden_size = config.hidden_size
301 max_position_embeddings = getattr(config, "max_position_embeddings", 8192)
302 # Support abacusai/Smaug-72B-v0.1 with attention_bias
303 attention_bias = getattr(config, "attention_bias", False) or getattr(
304 config, "bias", False
305 )
306
307 self.self_attn = ExaoneMoeAttention(
308 config=config,
309 hidden_size=self.hidden_size,
310 num_heads=config.num_attention_heads,
311 num_kv_heads=getattr(
312 config, "num_key_value_heads", config.num_attention_heads
313 ),
314 max_position_embeddings=max_position_embeddings,
315 quant_config=quant_config,
316 bias=attention_bias,
317 cache_config=cache_config,
318 prefix=f"{prefix}.self_attn",
319 is_mtp=is_mtp,
320 )
321
322 swiglu_limits = getattr(config, "swiglu_limits", None)
323 if swiglu_limits is not None:
324 swiglu_limit = swiglu_limits[layer_idx]
325 swiglu_limit = swiglu_limit if swiglu_limit > 0 else None
326 else:
327 swiglu_limit = None
328
329 if config.mlp_layer_types[layer_idx] == "sparse" and not is_mtp:
330 self.mlp = ExaoneMoe(
331 config=config,
332 swiglu_limit=swiglu_limit,
333 quant_config=quant_config,
334 prefix=f"{prefix}.mlp",
335 )
336 else:
337 self.mlp = ExaoneMoeGatedMLP(
338 hidden_size=self.hidden_size,
339 intermediate_size=config.intermediate_size,
340 hidden_act=config.hidden_act,
341 swiglu_limit=swiglu_limit,
342 quant_config=quant_config,
343 bias=getattr(config, "mlp_bias", False),
344 prefix=f"{prefix}.mlp",
345 )
346 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
347 self.post_attention_layernorm = RMSNorm(
348 config.hidden_size, eps=config.rms_norm_eps
349 )
350
351 def forward(
352 self,
353 positions: torch.Tensor,
354 hidden_states: torch.Tensor,
355 residual: torch.Tensor | None,
356 ) -> tuple[torch.Tensor, torch.Tensor]:
357 if residual is None:
358 residual = hidden_states
359 hidden_states = self.input_layernorm(hidden_states)
360 else:
361 hidden_states, residual = self.input_layernorm(hidden_states, residual)
362
363 # Self Attention
364 hidden_states = self.self_attn(
365 positions=positions,
366 hidden_states=hidden_states,
367 )
368
369 # Fully Connected
370 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
371 hidden_states = self.mlp(hidden_states)
372
373 return hidden_states, residual
374
375
376 @support_torch_compile
377 class ExaoneMoeModel(nn.Module):
378 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
379 super().__init__()
380
381 config = vllm_config.model_config.hf_config
382 cache_config = vllm_config.cache_config
383 quant_config = vllm_config.quant_config
384 self.num_redundant_experts = (
385 vllm_config.parallel_config.eplb_config.num_redundant_experts
386 )
387
388 self.config = config
389 self.quant_config = quant_config
390 self.vocab_size = config.vocab_size
391 if get_pp_group().is_first_rank or (
392 config.tie_word_embeddings and get_pp_group().is_last_rank
393 ):
394 self.embed_tokens = VocabParallelEmbedding(
395 self.vocab_size,
396 config.hidden_size,
397 quant_config=quant_config,
398 )
399 else:
400 self.embed_tokens = PPMissingLayer()
401 self.start_layer, self.end_layer, self.layers = make_layers(
402 config.num_hidden_layers,
403 lambda prefix: ExaoneMoeDecoderLayer(
404 config=config,
405 cache_config=cache_config,
406 quant_config=quant_config,
407 prefix=prefix,
408 ),
409 prefix=f"{prefix}.layers",
410 )
411 if get_pp_group().is_last_rank:
412 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
413 else:
414 self.norm = PPMissingLayer()
415
416 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
417 ["hidden_states", "residual"], config.hidden_size
418 )
419
420 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
421 return self.embed_tokens(input_ids)
422
423 def forward(
424 self,
425 input_ids: torch.Tensor | None,
426 positions: torch.Tensor,
427 intermediate_tensors: IntermediateTensors | None,
428 inputs_embeds: torch.Tensor | None = None,
429 ) -> torch.Tensor | IntermediateTensors:
430 if get_pp_group().is_first_rank:
431 if inputs_embeds is not None:
432 hidden_states = inputs_embeds
433 else:
434 hidden_states = self.embed_input_ids(input_ids)
435 residual = None
436 else:
437 assert intermediate_tensors is not None
438 hidden_states = intermediate_tensors["hidden_states"]
439 residual = intermediate_tensors["residual"]
440 for layer in islice(self.layers, self.start_layer, self.end_layer):
441 hidden_states, residual = layer(
442 positions,
443 hidden_states,
444 residual,
445 )
446 if not get_pp_group().is_last_rank:
447 return IntermediateTensors(
448 {"hidden_states": hidden_states, "residual": residual}
449 )
450
451 hidden_states, _ = self.norm(hidden_states, residual)
452 return hidden_states
453
454
455 class ExaoneMoeForCausalLM(nn.Module, SupportsLoRA, SupportsPP):
456 hf_to_vllm_mapper = WeightsMapper(
457 orig_to_new_stacked={
458 # weight_name: (param_name, shard_id)
459 ".q_proj": (".qkv_proj", "q"),
460 ".k_proj": (".qkv_proj", "k"),
461 ".v_proj": (".qkv_proj", "v"),
462 # .experts.gate_up_proj must be handled by MoERunner.load_weights for EP
463 ".mlp.gate_proj": (".mlp.gate_up_proj", 0),
464 ".mlp.up_proj": (".mlp.gate_up_proj", 1),
465 ".shared_experts.gate_proj": (".shared_experts.gate_up_proj", 0),
466 ".shared_experts.up_proj": (".shared_experts.gate_up_proj", 1),
467 },
468 orig_to_new_prefix={"mtp.": None},
469 )
470 packed_modules_mapping = {
471 "qkv_proj": [
472 "q_proj",
473 "k_proj",
474 "v_proj",
475 ],
476 "gate_up_proj": [
477 "gate_proj",
478 "up_proj",
479 ],
480 }
481
482 # LoRA specific attributes
483 embedding_modules = {
484 "embed_tokens": "input_embeddings",
485 "lm_head": "output_embeddings",
486 }
487 embedding_padding_modules = ["lm_head"]
488
489 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
490 super().__init__()
491 config = vllm_config.model_config.hf_config.get_text_config()
492 quant_config = vllm_config.quant_config
493
494 self.config = config
495 self.quant_config = quant_config
496
497 self.model = ExaoneMoeModel(
498 vllm_config=vllm_config,
499 prefix=maybe_prefix(prefix, "model"),
500 )
501 if get_pp_group().is_last_rank:
502 self.unpadded_vocab_size = config.vocab_size
503 self.lm_head = ParallelLMHead(
504 self.unpadded_vocab_size,
505 config.hidden_size,
506 quant_config=quant_config,
507 )
508 if config.tie_word_embeddings:
509 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
510
511 logit_scale = getattr(config, "logit_scale", 1.0)
512 self.logits_processor = LogitsProcessor(
513 self.unpadded_vocab_size, config.vocab_size, logit_scale
514 )
515 else:
516 self.lm_head = PPMissingLayer()
517
518 self.make_empty_intermediate_tensors = (
519 self.model.make_empty_intermediate_tensors
520 )
521
522 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
523 return self.model.embed_input_ids(input_ids)
524
525 def forward(
526 self,
527 input_ids: torch.Tensor | None,
528 positions: torch.Tensor,
529 intermediate_tensors: IntermediateTensors | None = None,
530 inputs_embeds: torch.Tensor | None = None,
531 ) -> torch.Tensor | IntermediateTensors:
532 model_output = self.model(
533 input_ids, positions, intermediate_tensors, inputs_embeds
534 )
535 return model_output
536
537 def compute_logits(
538 self,
539 hidden_states: torch.Tensor,
540 ) -> torch.Tensor | None:
541 logits = self.logits_processor(self.lm_head, hidden_states)
542 return logits
543
544 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
545 loader = AutoWeightsLoader(
546 self,
547 # Skip loading extra parameters for GPTQ/modelopt models.
548 ignore_unexpected_suffixes=[
549 ".bias",
550 "_bias",
551 ".k_scale",
552 "_k_scale",
553 ".v_scale",
554 "_v_scale",
555 ".weight_scale",
556 "_weight_scale",
557 ".input_scale",
558 "_input_scale",
559 ],
560 )
561 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)