main
py 488 lines 17.4 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 Mixtral 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 import MixtralConfig
33
34 from vllm.compilation.decorators import support_torch_compile
35 from vllm.config import CacheConfig, VllmConfig, get_current_vllm_config
36 from vllm.distributed import (
37 get_ep_group,
38 get_pp_group,
39 get_tensor_model_parallel_world_size,
40 )
41 from vllm.model_executor.layers.attention import Attention
42 from vllm.model_executor.layers.fused_moe import (
43 FusedMoEFactory,
44 GateLinear,
45 )
46 from vllm.model_executor.layers.layernorm import RMSNorm
47 from vllm.model_executor.layers.linear import (
48 QKVParallelLinear,
49 RowParallelLinear,
50 )
51 from vllm.model_executor.layers.logits_processor import LogitsProcessor
52 from vllm.model_executor.layers.quantization import QuantizationConfig
53 from vllm.model_executor.layers.rotary_embedding import get_rope
54 from vllm.model_executor.layers.vocab_parallel_embedding import (
55 ParallelLMHead,
56 VocabParallelEmbedding,
57 )
58 from vllm.sequence import IntermediateTensors
59
60 from .interfaces import MixtureOfExperts, SupportsLoRA, SupportsPP
61 from .utils import (
62 AutoWeightsLoader,
63 PPMissingLayer,
64 WeightsMapper,
65 make_empty_intermediate_tensors_factory,
66 make_layers,
67 maybe_prefix,
68 )
69
70
71 class MixtralMoE(nn.Module):
72 """A tensor-parallel MoE implementation for Mixtral that shards each expert
73 across all ranks.
74
75 Each expert's weights are sharded across all ranks and a fused MoE
76 kernel is used for the forward pass, and finally we reduce the outputs
77 across ranks.
78 """
79
80 def __init__(
81 self,
82 num_experts: int,
83 top_k: int,
84 hidden_size: int,
85 intermediate_size: int,
86 params_dtype: torch.dtype | None = None,
87 quant_config: QuantizationConfig | None = None,
88 tp_size: int | None = None,
89 dp_size: int | None = None,
90 prefix: str = "",
91 enable_eplb: bool = False,
92 ):
93 super().__init__()
94 self.hidden_size = hidden_size
95
96 self.ep_group = get_ep_group().device_group
97 self.ep_size = self.ep_group.size()
98
99 # Expert Parallelism Load balancing settings.
100 vllm_config = get_current_vllm_config()
101 parallel_config = vllm_config.parallel_config
102 self.enable_eplb = enable_eplb
103
104 self.n_routed_experts = num_experts
105 self.n_logical_experts = num_experts
106 self.n_redundant_experts = parallel_config.eplb_config.num_redundant_experts
107 self.n_physical_experts = self.n_logical_experts + self.n_redundant_experts
108 self.n_local_physical_experts = self.n_physical_experts // self.ep_size
109 # Gate always runs at half / full precision for now.
110
111 self.gate = GateLinear(
112 hidden_size,
113 num_experts,
114 params_dtype=params_dtype,
115 prefix=f"{prefix}.gate",
116 )
117
118 self.experts = FusedMoEFactory(
119 num_experts=num_experts,
120 top_k=top_k,
121 hidden_size=hidden_size,
122 intermediate_size=intermediate_size,
123 params_dtype=params_dtype,
124 renormalize=True,
125 quant_config=quant_config,
126 tp_size=tp_size,
127 dp_size=dp_size,
128 prefix=f"{prefix}.experts",
129 enable_eplb=self.enable_eplb,
130 num_redundant_experts=self.n_redundant_experts,
131 ckpt_names=("w1", "w2", "w3"),
132 )
133
134 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
135 # NOTE: hidden_states can have either 1D or 2D shape.
136 orig_shape = hidden_states.shape
137 hidden_states = hidden_states.view(-1, self.hidden_size)
138 # router_logits: (num_tokens, n_experts)
139 router_logits, _ = self.gate(hidden_states)
140 final_hidden_states = self.experts(hidden_states, router_logits)
141 return final_hidden_states.view(orig_shape)
142
143
144 class MixtralAttention(nn.Module):
145 def __init__(
146 self,
147 config: MixtralConfig,
148 hidden_size: int,
149 num_heads: int,
150 num_kv_heads: int,
151 max_position: int = 4096 * 32,
152 cache_config: CacheConfig | None = None,
153 quant_config: QuantizationConfig | None = None,
154 prefix: str = "",
155 ) -> None:
156 super().__init__()
157 self.hidden_size = hidden_size
158 tp_size = get_tensor_model_parallel_world_size()
159 self.total_num_heads = num_heads
160 assert self.total_num_heads % tp_size == 0
161 self.num_heads = self.total_num_heads // tp_size
162 self.total_num_kv_heads = num_kv_heads
163 if self.total_num_kv_heads >= tp_size:
164 # Number of KV heads is greater than TP size, so we partition
165 # the KV heads across multiple tensor parallel GPUs.
166 assert self.total_num_kv_heads % tp_size == 0
167 else:
168 # Number of KV heads is less than TP size, so we replicate
169 # the KV heads across multiple tensor parallel GPUs.
170 assert tp_size % self.total_num_kv_heads == 0
171 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
172 # MixtralConfig has an optional head_dim argument
173 self.head_dim = getattr(config, "head_dim", None)
174 if self.head_dim is None:
175 self.head_dim = self.hidden_size // self.total_num_heads
176 self.q_size = self.num_heads * self.head_dim
177 self.kv_size = self.num_kv_heads * self.head_dim
178 self.scaling = self.head_dim**-0.5
179
180 self.qkv_proj = QKVParallelLinear(
181 hidden_size,
182 self.head_dim,
183 self.total_num_heads,
184 self.total_num_kv_heads,
185 bias=False,
186 quant_config=quant_config,
187 prefix=f"{prefix}.qkv_proj",
188 )
189 self.o_proj = RowParallelLinear(
190 self.total_num_heads * self.head_dim,
191 hidden_size,
192 bias=False,
193 quant_config=quant_config,
194 prefix=f"{prefix}.o_proj",
195 )
196 self.rotary_emb = get_rope(
197 self.head_dim,
198 max_position=max_position,
199 rope_parameters=config.rope_parameters,
200 is_neox_style=True,
201 )
202 self.attn = Attention(
203 self.num_heads,
204 self.head_dim,
205 self.scaling,
206 num_kv_heads=self.num_kv_heads,
207 cache_config=cache_config,
208 quant_config=quant_config,
209 prefix=f"{prefix}.attn",
210 )
211
212 def forward(
213 self,
214 positions: torch.Tensor,
215 hidden_states: torch.Tensor,
216 ) -> torch.Tensor:
217 qkv, _ = self.qkv_proj(hidden_states)
218 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
219 q, k = self.rotary_emb(positions, q, k)
220 attn_output = self.attn(q, k, v)
221 output, _ = self.o_proj(attn_output)
222 return output
223
224
225 class MixtralDecoderLayer(nn.Module):
226 def __init__(
227 self,
228 config: MixtralConfig,
229 cache_config: CacheConfig | None = None,
230 quant_config: QuantizationConfig | None = None,
231 prefix: str = "",
232 enable_eplb: bool = False,
233 ) -> None:
234 super().__init__()
235 self.hidden_size = config.hidden_size
236 self.self_attn = MixtralAttention(
237 config=config,
238 hidden_size=self.hidden_size,
239 num_heads=config.num_attention_heads,
240 max_position=config.max_position_embeddings,
241 num_kv_heads=config.num_key_value_heads,
242 cache_config=cache_config,
243 quant_config=quant_config,
244 prefix=f"{prefix}.self_attn",
245 )
246 self.block_sparse_moe = MixtralMoE(
247 num_experts=config.num_local_experts,
248 top_k=config.num_experts_per_tok,
249 hidden_size=config.hidden_size,
250 intermediate_size=config.intermediate_size,
251 quant_config=quant_config,
252 prefix=f"{prefix}.block_sparse_moe",
253 enable_eplb=enable_eplb,
254 )
255 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
256 self.post_attention_layernorm = RMSNorm(
257 config.hidden_size, eps=config.rms_norm_eps
258 )
259
260 def forward(
261 self,
262 positions: torch.Tensor,
263 hidden_states: torch.Tensor,
264 residual: torch.Tensor | None,
265 ) -> torch.Tensor:
266 # Self Attention
267 if residual is None:
268 residual = hidden_states
269 hidden_states = self.input_layernorm(hidden_states)
270 else:
271 hidden_states, residual = self.input_layernorm(hidden_states, residual)
272 hidden_states = self.self_attn(
273 positions=positions,
274 hidden_states=hidden_states,
275 )
276
277 # Fully Connected
278 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
279 hidden_states = self.block_sparse_moe(hidden_states)
280 return hidden_states, residual
281
282
283 @support_torch_compile
284 class MixtralModel(nn.Module):
285 hf_to_vllm_mapper = WeightsMapper(
286 orig_to_new_stacked={
287 # weight_name: (param_name, shard_id)
288 ".q_proj": (".qkv_proj", "q"),
289 ".k_proj": (".qkv_proj", "k"),
290 ".v_proj": (".qkv_proj", "v"),
291 },
292 orig_to_new_substr={
293 # W8A8 compressed-tensors checkpoints name experts gate/up/down;
294 # map to the w1/w3/w2 the FusedMoE loader expects (no-op if native).
295 ".gate_proj.": ".w1.",
296 ".up_proj.": ".w3.",
297 ".down_proj.": ".w2.",
298 },
299 )
300
301 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
302 super().__init__()
303
304 config = vllm_config.model_config.hf_config
305 cache_config = vllm_config.cache_config
306 quant_config = vllm_config.quant_config
307
308 parallel_config = vllm_config.parallel_config
309
310 self.config = config
311 self.quant_config = quant_config
312
313 self.vocab_size = config.vocab_size
314 self.org_vocab_size = config.vocab_size
315
316 self.embed_tokens = VocabParallelEmbedding(
317 self.vocab_size,
318 config.hidden_size,
319 )
320
321 self.enable_eplb = parallel_config.enable_eplb
322 self.num_redundant_experts = parallel_config.eplb_config.num_redundant_experts
323
324 self.start_layer, self.end_layer, self.layers = make_layers(
325 config.num_hidden_layers,
326 lambda prefix: MixtralDecoderLayer(
327 config,
328 cache_config,
329 quant_config=quant_config,
330 prefix=prefix,
331 enable_eplb=self.enable_eplb,
332 ),
333 prefix=f"{prefix}.layers",
334 )
335
336 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
337 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
338 ["hidden_states", "residual"], config.hidden_size
339 )
340
341 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
342 return self.embed_tokens(input_ids)
343
344 def forward(
345 self,
346 input_ids: torch.Tensor | None,
347 positions: torch.Tensor,
348 intermediate_tensors: IntermediateTensors | None,
349 inputs_embeds: torch.Tensor | None = None,
350 ) -> torch.Tensor | IntermediateTensors:
351 if get_pp_group().is_first_rank:
352 if inputs_embeds is not None:
353 hidden_states = inputs_embeds
354 else:
355 hidden_states = self.embed_input_ids(input_ids)
356 residual = None
357 else:
358 assert intermediate_tensors is not None
359 hidden_states = intermediate_tensors["hidden_states"]
360 residual = intermediate_tensors["residual"]
361 for layer in islice(self.layers, self.start_layer, self.end_layer):
362 hidden_states, residual = layer(positions, hidden_states, residual)
363 if not get_pp_group().is_last_rank:
364 return IntermediateTensors(
365 {"hidden_states": hidden_states, "residual": residual}
366 )
367 hidden_states, _ = self.norm(hidden_states, residual)
368 return hidden_states
369
370 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
371 loader = AutoWeightsLoader(self)
372 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
373
374
375 class MixtralForCausalLM(nn.Module, SupportsLoRA, SupportsPP, MixtureOfExperts):
376 fall_back_to_pt_during_load = False
377
378 hf_to_vllm_mapper = MixtralModel.hf_to_vllm_mapper
379 packed_modules_mapping = {
380 "qkv_proj": [
381 "q_proj",
382 "k_proj",
383 "v_proj",
384 ],
385 }
386
387 # LoRA specific attributes
388 embedding_modules = {
389 "embed_tokens": "input_embeddings",
390 "lm_head": "output_embeddings",
391 }
392
393 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
394 super().__init__()
395 config = vllm_config.model_config.hf_config
396 quant_config = vllm_config.quant_config
397
398 self.config = config
399
400 self.quant_config = quant_config
401
402 self.model = MixtralModel(
403 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
404 )
405
406 self.lm_head = ParallelLMHead(
407 config.vocab_size,
408 config.hidden_size,
409 quant_config=quant_config,
410 prefix=maybe_prefix(prefix, "lm_head"),
411 )
412 if self.config.tie_word_embeddings:
413 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
414 self.logits_processor = LogitsProcessor(config.vocab_size)
415 self.make_empty_intermediate_tensors = (
416 self.model.make_empty_intermediate_tensors
417 )
418
419 self.moe_layers = []
420 example_moe = None
421
422 for layer in self.model.layers:
423 if isinstance(layer, PPMissingLayer):
424 continue
425 assert isinstance(layer, MixtralDecoderLayer)
426 if hasattr(layer, "block_sparse_moe") and isinstance(
427 layer.block_sparse_moe, MixtralMoE
428 ):
429 example_moe = layer.block_sparse_moe
430 self.moe_layers.append(layer.block_sparse_moe.experts)
431
432 self.num_moe_layers = len(self.moe_layers)
433
434 if example_moe is None:
435 raise RuntimeError("No MixtralMoE layer found in model.layers.")
436
437 self.num_logical_experts = example_moe.n_logical_experts
438 self.num_physical_experts = example_moe.n_physical_experts
439 self.num_local_physical_experts = example_moe.n_local_physical_experts
440 self.num_routed_experts = example_moe.n_routed_experts
441 self.num_redundant_experts = example_moe.n_redundant_experts
442 self.num_expert_groups = 1
443 self.num_shared_experts = 0
444
445 def update_physical_experts_metadata(
446 self,
447 num_physical_experts: int,
448 num_local_physical_experts: int,
449 ) -> None:
450 assert self.num_local_physical_experts == num_local_physical_experts
451 self.num_physical_experts = num_physical_experts
452 self.num_local_physical_experts = num_local_physical_experts
453 self.num_redundant_experts = num_physical_experts - self.num_logical_experts
454 for layer in self.model.layers:
455 if hasattr(layer, "block_sparse_moe") and isinstance(
456 layer.block_sparse_moe, MixtralMoE
457 ):
458 moe = layer.block_sparse_moe
459 moe.n_local_physical_experts = num_local_physical_experts
460 moe.n_physical_experts = num_physical_experts
461 moe.n_redundant_experts = self.num_redundant_experts
462 moe.experts.update_expert_map()
463
464 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
465 return self.model.embed_input_ids(input_ids)
466
467 def forward(
468 self,
469 input_ids: torch.Tensor | None,
470 positions: torch.Tensor,
471 intermediate_tensors: IntermediateTensors | None = None,
472 inputs_embeds: torch.Tensor | None = None,
473 ) -> torch.Tensor | IntermediateTensors:
474 hidden_states = self.model(
475 input_ids, positions, intermediate_tensors, inputs_embeds
476 )
477 return hidden_states
478
479 def compute_logits(
480 self,
481 hidden_states: torch.Tensor,
482 ) -> torch.Tensor | None:
483 logits = self.logits_processor(self.lm_head, hidden_states)
484 return logits
485
486 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
487 loader = AutoWeightsLoader(self)
488 return loader.load_weights(weights)