main
py 585 lines 19.3 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 # Adapted from
5 # https://github.com/huggingface/transformers/blob/v4.28.0/src/transformers/models/llama/modeling_llama.py
6 # Copyright 2023 The vLLM team.
7 # Copyright 2022 EleutherAI and the HuggingFace Inc. team. All rights reserved.
8 #
9 # This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
10 # and OPT implementations in this library. It has been modified from its
11 # original forms to accommodate minor architectural differences compared
12 # to GPT-NeoX and OPT used by the Meta AI team that trained the model.
13 #
14 # Licensed under the Apache License, Version 2.0 (the "License");
15 # you may not use this file except in compliance with the License.
16 # You may obtain a copy of the License at
17 #
18 # http://www.apache.org/licenses/LICENSE-2.0
19 #
20 # Unless required by applicable law or agreed to in writing, software
21 # distributed under the License is distributed on an "AS IS" BASIS,
22 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 # See the License for the specific language governing permissions and
24 # limitations under the License.
25 """Inference-only PhiMoE model."""
26
27 from collections.abc import Iterable
28 from itertools import islice
29
30 import torch
31 from torch import nn
32 from transformers.configuration_utils import PreTrainedConfig
33
34 from vllm.compilation.decorators import support_torch_compile
35 from vllm.config import CacheConfig, VllmConfig
36 from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
37 from vllm.model_executor.layers.attention import Attention
38 from vllm.model_executor.layers.fused_moe import (
39 FusedMoEFactory,
40 GateLinear,
41 )
42 from vllm.model_executor.layers.linear import (
43 QKVParallelLinear,
44 RowParallelLinear,
45 )
46 from vllm.model_executor.layers.logits_processor import LogitsProcessor
47 from vllm.model_executor.layers.quantization import QuantizationConfig
48 from vllm.model_executor.layers.rotary_embedding import get_rope
49 from vllm.model_executor.layers.vocab_parallel_embedding import (
50 ParallelLMHead,
51 VocabParallelEmbedding,
52 )
53 from vllm.sequence import IntermediateTensors
54
55 from .interfaces import SupportsLoRA, SupportsPP
56 from .utils import (
57 AutoWeightsLoader,
58 WeightsMapper,
59 make_empty_intermediate_tensors_factory,
60 make_layers,
61 maybe_prefix,
62 )
63
64
65 class PhiMoEConfig(PreTrainedConfig):
66 model_type = "phimoe"
67 keys_to_ignore_at_inference = ["past_key_values"]
68
69 def __init__(
70 self,
71 vocab_size=32000,
72 hidden_size=4096,
73 intermediate_size=14336,
74 num_hidden_layers=32,
75 num_attention_heads=32,
76 num_key_value_heads=8,
77 head_dim=None,
78 hidden_act="silu",
79 max_position_embeddings=4096 * 32,
80 initializer_range=0.02,
81 rms_norm_eps=1e-5,
82 use_cache=True,
83 pad_token_id=None,
84 bos_token_id=1,
85 eos_token_id=2,
86 tie_word_embeddings=False,
87 rope_parameters=None,
88 sliding_window=None,
89 attention_dropout=0.0,
90 num_experts_per_tok=2,
91 num_local_experts=16,
92 output_router_logits=False,
93 router_aux_loss_coef=0.001,
94 router_jitter_noise=0.0,
95 attention_bias=False,
96 lm_head_bias=False,
97 **kwargs,
98 ):
99 self.vocab_size = vocab_size
100 self.max_position_embeddings = max_position_embeddings
101 self.hidden_size = hidden_size
102 self.intermediate_size = intermediate_size
103 self.num_hidden_layers = num_hidden_layers
104 self.num_attention_heads = num_attention_heads
105 self.sliding_window = sliding_window
106 self.attention_bias = attention_bias
107 self.lm_head_bias = lm_head_bias
108 # for backward compatibility
109 if num_key_value_heads is None:
110 num_key_value_heads = num_attention_heads
111 if head_dim is None:
112 head_dim = hidden_size // num_attention_heads
113
114 self.num_key_value_heads = num_key_value_heads
115 self.head_dim = head_dim
116 self.hidden_act = hidden_act
117 self.initializer_range = initializer_range
118 self.rms_norm_eps = rms_norm_eps
119 self.use_cache = use_cache
120 if rope_parameters is None:
121 rope_theta = kwargs.pop("rope_theta", 1e6)
122 rope_parameters = {"rope_type": "default", "rope_theta": rope_theta}
123 self.attention_dropout = attention_dropout
124
125 self.num_experts_per_tok = num_experts_per_tok
126 self.num_local_experts = num_local_experts
127 self.output_router_logits = output_router_logits
128 self.router_aux_loss_coef = router_aux_loss_coef
129 self.router_jitter_noise = router_jitter_noise
130 super().__init__(
131 pad_token_id=pad_token_id,
132 bos_token_id=bos_token_id,
133 eos_token_id=eos_token_id,
134 tie_word_embeddings=tie_word_embeddings,
135 **kwargs,
136 )
137
138
139 class mp(torch.autograd.Function):
140 @staticmethod
141 def forward(
142 ctx,
143 scores: torch.Tensor,
144 multiplier: torch.Tensor,
145 selected_experts: torch.Tensor,
146 masked_gates: torch.Tensor,
147 mask_for_one: torch.Tensor,
148 ):
149 ctx.save_for_backward(multiplier, selected_experts, masked_gates)
150 return multiplier * mask_for_one
151
152 @staticmethod
153 def backward(
154 ctx,
155 grad_at_output: torch.Tensor,
156 ):
157 multiplier, selected_experts, masked_gates = ctx.saved_tensors
158
159 grad_at_output = grad_at_output * multiplier
160
161 grad_at_scores_expanded = masked_gates * grad_at_output.mul(-1)
162 grad_at_scores_expanded.scatter_add_(
163 dim=-1,
164 index=selected_experts,
165 src=grad_at_output,
166 )
167
168 return (
169 grad_at_scores_expanded,
170 None,
171 None,
172 None,
173 None,
174 )
175
176
177 def sparsemixer(scores, jitter_eps=0.01):
178 ################ first expert ################
179
180 with torch.no_grad():
181 # compute mask for sparsity
182 mask_logits_threshold, max_ind = scores.max(dim=-1, keepdim=True)
183 factor = scores.abs().clamp(min=mask_logits_threshold)
184 mask_logits_threshold = ((mask_logits_threshold - scores) / factor) > (
185 2 * jitter_eps
186 )
187
188 # apply mask
189 masked_gates = scores.masked_fill(mask_logits_threshold, float("-inf"))
190 selected_experts = max_ind
191
192 # compute scores for gradients
193 masked_gates = torch.softmax(masked_gates, dim=-1)
194 multiplier_o = masked_gates.gather(dim=-1, index=selected_experts)
195
196 multiplier = multiplier_o
197
198 # masked out first expert
199 masked_scores = torch.scatter(
200 scores,
201 -1,
202 selected_experts,
203 float("-inf"),
204 )
205 with torch.no_grad():
206 # compute mask for sparsity
207 mask_logits_threshold, max_ind = masked_scores.max(dim=-1, keepdim=True)
208 factor = scores.abs().clamp(min=mask_logits_threshold)
209 mask_logits_threshold = ((mask_logits_threshold - scores) / factor) > (
210 2 * jitter_eps
211 )
212
213 # apply mask
214 masked_gates_top2 = masked_scores.masked_fill(mask_logits_threshold, float("-inf"))
215 selected_experts_top2 = max_ind
216 # compute scores for gradients
217 masked_gates_top2 = torch.softmax(masked_gates_top2, dim=-1)
218 multiplier_top2 = masked_gates_top2.gather(dim=-1, index=selected_experts_top2)
219
220 multiplier = torch.concat((multiplier, multiplier_top2), dim=-1)
221 selected_experts = torch.concat((selected_experts, selected_experts_top2), dim=-1)
222
223 return (
224 multiplier,
225 selected_experts,
226 )
227
228
229 def phimoe_routing_function(
230 hidden_states: torch.Tensor,
231 gating_output: torch.Tensor,
232 topk: int,
233 renormalize: bool,
234 ):
235 assert hidden_states.shape[0] == gating_output.shape[0], "Number of tokens mismatch"
236 assert topk == 2, "Only top-2 routing is supported"
237 assert renormalize is False, "Renormalization is not supported"
238
239 topk_weights, topk_ids = sparsemixer(gating_output)
240 return topk_weights, topk_ids
241
242
243 class PhiMoE(nn.Module):
244 """A tensor-parallel MoE implementation for PhiMoE that shards each expert
245 across all ranks.
246
247 Each expert's weights are sharded across all ranks and a fused MoE
248 kernel is used for the forward pass, and finally we reduce the outputs
249 across ranks.
250 """
251
252 def __init__(
253 self,
254 num_experts: int,
255 top_k: int,
256 hidden_size: int,
257 intermediate_size: int,
258 params_dtype: torch.dtype | None = None,
259 quant_config: QuantizationConfig | None = None,
260 tp_size: int | None = None,
261 prefix: str = "",
262 ):
263 super().__init__()
264 self.hidden_size = hidden_size
265
266 # Gate always runs at half / full precision for now.
267 self.gate = GateLinear(
268 hidden_size,
269 num_experts,
270 params_dtype=params_dtype,
271 prefix=f"{prefix}.gate",
272 )
273
274 self.experts = FusedMoEFactory(
275 num_experts=num_experts,
276 top_k=top_k,
277 hidden_size=hidden_size,
278 intermediate_size=intermediate_size,
279 params_dtype=params_dtype,
280 renormalize=False,
281 quant_config=quant_config,
282 tp_size=tp_size,
283 custom_routing_function=phimoe_routing_function,
284 prefix=f"{prefix}.experts",
285 ckpt_names=("w1", "w2", "w3"),
286 )
287
288 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
289 # NOTE: hidden_states can have either 1D or 2D shape.
290 orig_shape = hidden_states.shape
291 hidden_states = hidden_states.view(-1, self.hidden_size)
292 # router_logits: (num_tokens, n_experts)
293 router_logits, _ = self.gate(hidden_states)
294 final_hidden_states = self.experts(hidden_states, router_logits)
295 return final_hidden_states.view(orig_shape)
296
297
298 class PhiMoEAttention(nn.Module):
299 def __init__(
300 self,
301 hidden_size: int,
302 num_heads: int,
303 num_kv_heads: int,
304 rope_parameters: dict,
305 head_dim: int | None = None,
306 max_position: int = 4096 * 32,
307 cache_config: CacheConfig | None = None,
308 quant_config: QuantizationConfig | None = None,
309 prefix: str = "",
310 ) -> None:
311 super().__init__()
312 self.hidden_size = hidden_size
313 tp_size = get_tensor_model_parallel_world_size()
314 self.total_num_heads = num_heads
315 assert self.total_num_heads % tp_size == 0
316 self.num_heads = self.total_num_heads // tp_size
317 self.total_num_kv_heads = num_kv_heads
318 if self.total_num_kv_heads >= tp_size:
319 # Number of KV heads is greater than TP size, so we partition
320 # the KV heads across multiple tensor parallel GPUs.
321 assert self.total_num_kv_heads % tp_size == 0
322 else:
323 # Number of KV heads is less than TP size, so we replicate
324 # the KV heads across multiple tensor parallel GPUs.
325 assert tp_size % self.total_num_kv_heads == 0
326 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
327 if head_dim is None:
328 head_dim = hidden_size // num_heads
329 self.head_dim = head_dim
330 self.q_size = self.num_heads * self.head_dim
331 self.kv_size = self.num_kv_heads * self.head_dim
332 self.scaling = self.head_dim**-0.5
333
334 self.qkv_proj = QKVParallelLinear(
335 hidden_size,
336 self.head_dim,
337 self.total_num_heads,
338 self.total_num_kv_heads,
339 bias=True,
340 quant_config=quant_config,
341 prefix=f"{prefix}.qkv_proj",
342 )
343 self.o_proj = RowParallelLinear(
344 self.total_num_heads * self.head_dim,
345 hidden_size,
346 bias=True,
347 quant_config=quant_config,
348 prefix=f"{prefix}.o_proj",
349 )
350 self.rotary_emb = get_rope(
351 self.head_dim,
352 max_position=max_position,
353 rope_parameters=rope_parameters,
354 is_neox_style=True,
355 )
356 self.attn = Attention(
357 self.num_heads,
358 self.head_dim,
359 self.scaling,
360 num_kv_heads=self.num_kv_heads,
361 cache_config=cache_config,
362 quant_config=quant_config,
363 prefix=f"{prefix}.attn",
364 )
365
366 def forward(
367 self,
368 positions: torch.Tensor,
369 hidden_states: torch.Tensor,
370 ) -> torch.Tensor:
371 qkv, _ = self.qkv_proj(hidden_states)
372 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
373 q, k = self.rotary_emb(positions, q, k)
374 attn_output = self.attn(q, k, v)
375 output, _ = self.o_proj(attn_output)
376 return output
377
378
379 class PhiMoEDecoderLayer(nn.Module):
380 def __init__(
381 self,
382 config: PhiMoEConfig,
383 cache_config: CacheConfig | None = None,
384 quant_config: QuantizationConfig | None = None,
385 prefix: str = "",
386 ) -> None:
387 super().__init__()
388 self.hidden_size = config.hidden_size
389 # Requires transformers > 4.32.0
390 self.self_attn = PhiMoEAttention(
391 hidden_size=self.hidden_size,
392 num_heads=config.num_attention_heads,
393 max_position=config.max_position_embeddings,
394 num_kv_heads=config.num_key_value_heads,
395 head_dim=getattr(
396 config, "head_dim", self.hidden_size // config.num_attention_heads
397 ),
398 cache_config=cache_config,
399 quant_config=quant_config,
400 rope_parameters=config.rope_parameters,
401 prefix=f"{prefix}.self_attn",
402 )
403 self.block_sparse_moe = PhiMoE(
404 num_experts=config.num_local_experts,
405 top_k=config.num_experts_per_tok,
406 hidden_size=config.hidden_size,
407 intermediate_size=config.intermediate_size,
408 quant_config=quant_config,
409 prefix=f"{prefix}.block_sparse_moe",
410 )
411 self.input_layernorm = nn.LayerNorm(
412 config.hidden_size, eps=config.rms_norm_eps, elementwise_affine=True
413 )
414 self.post_attention_layernorm = nn.LayerNorm(
415 config.hidden_size, eps=config.rms_norm_eps, elementwise_affine=True
416 )
417
418 def forward(
419 self,
420 positions: torch.Tensor,
421 hidden_states: torch.Tensor,
422 residual: torch.Tensor | None,
423 ) -> torch.Tensor:
424 residual = hidden_states
425
426 # Self Attention
427 hidden_states = self.input_layernorm(hidden_states)
428
429 hidden_states = self.self_attn(
430 positions=positions,
431 hidden_states=hidden_states,
432 )
433 hidden_states = hidden_states + residual
434
435 # Fully Connected
436 residual = hidden_states
437 hidden_states = self.post_attention_layernorm(hidden_states)
438 hidden_states = self.block_sparse_moe(hidden_states)
439
440 hidden_states = hidden_states + residual
441 return hidden_states, residual
442
443
444 @support_torch_compile
445 class PhiMoEModel(nn.Module):
446 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
447 super().__init__()
448
449 config = vllm_config.model_config.hf_config
450 cache_config = vllm_config.cache_config
451 quant_config = vllm_config.quant_config
452
453 self.vocab_size = config.vocab_size
454
455 self.config = config
456 self.quant_config = quant_config
457
458 self.embed_tokens = VocabParallelEmbedding(
459 self.vocab_size,
460 config.hidden_size,
461 )
462 self.start_layer, self.end_layer, self.layers = make_layers(
463 config.num_hidden_layers,
464 lambda prefix: PhiMoEDecoderLayer(
465 config, cache_config, quant_config, prefix=prefix
466 ),
467 prefix=f"{prefix}.layers",
468 )
469 self.norm = nn.LayerNorm(
470 config.hidden_size, eps=config.rms_norm_eps, elementwise_affine=True
471 )
472
473 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
474 ["hidden_states", "residual"], config.hidden_size
475 )
476
477 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
478 return self.embed_tokens(input_ids)
479
480 def forward(
481 self,
482 input_ids: torch.Tensor | None,
483 positions: torch.Tensor,
484 intermediate_tensors: IntermediateTensors | None,
485 inputs_embeds: torch.Tensor | None = None,
486 ) -> torch.Tensor | IntermediateTensors:
487 if get_pp_group().is_first_rank:
488 if inputs_embeds is not None:
489 hidden_states = inputs_embeds
490 else:
491 hidden_states = self.embed_input_ids(input_ids)
492 residual = None
493 else:
494 assert intermediate_tensors is not None
495 hidden_states = intermediate_tensors["hidden_states"]
496 residual = intermediate_tensors["residual"]
497
498 for layer in islice(self.layers, self.start_layer, self.end_layer):
499 hidden_states, residual = layer(
500 positions,
501 hidden_states,
502 residual,
503 )
504
505 if not get_pp_group().is_last_rank:
506 return IntermediateTensors(
507 {"hidden_states": hidden_states, "residual": residual}
508 )
509
510 hidden_states = self.norm(hidden_states)
511 return hidden_states
512
513
514 class PhiMoEForCausalLM(nn.Module, SupportsLoRA, SupportsPP):
515 fall_back_to_pt_during_load = False
516
517 hf_to_vllm_mapper = WeightsMapper(
518 orig_to_new_stacked={
519 # weight_name: (param_name, shard_id)
520 ".q_proj": (".qkv_proj", "q"),
521 ".k_proj": (".qkv_proj", "k"),
522 ".v_proj": (".qkv_proj", "v"),
523 }
524 )
525 packed_modules_mapping = {
526 "qkv_proj": [
527 "q_proj",
528 "k_proj",
529 "v_proj",
530 ],
531 }
532
533 # LoRA specific attributes
534 embedding_modules = {
535 "embed_tokens": "input_embeddings",
536 "lm_head": "output_embeddings",
537 }
538
539 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
540 super().__init__()
541 config = vllm_config.model_config.hf_config
542
543 self.config = config
544
545 self.quant_config = vllm_config.quant_config
546
547 self.model = PhiMoEModel(
548 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
549 )
550
551 self.lm_head = ParallelLMHead(
552 config.vocab_size,
553 config.hidden_size,
554 quant_config=None,
555 bias=True,
556 prefix=maybe_prefix(prefix, "lm_head"),
557 )
558 self.logits_processor = LogitsProcessor(config.vocab_size)
559
560 self.make_empty_intermediate_tensors = (
561 self.model.make_empty_intermediate_tensors
562 )
563
564 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
565 return self.model.embed_input_ids(input_ids)
566
567 def forward(
568 self,
569 input_ids: torch.Tensor | None,
570 positions: torch.Tensor,
571 intermediate_tensors: IntermediateTensors | None = None,
572 inputs_embeds: torch.Tensor | None = None,
573 ) -> torch.Tensor | IntermediateTensors:
574 hidden_states = self.model(
575 input_ids, positions, intermediate_tensors, inputs_embeds
576 )
577 return hidden_states
578
579 def compute_logits(self, hidden_states: torch.Tensor) -> torch.Tensor:
580 logits = self.logits_processor(self.lm_head, hidden_states)
581 return logits
582
583 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
584 loader = AutoWeightsLoader(self)
585 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)