main
py 654 lines 22.8 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 MiniCPM model compatible with HuggingFace weights."""
26
27 import math
28 from collections.abc import Iterable
29 from itertools import islice
30 from typing import Any
31
32 import torch
33 from torch import nn
34 from transformers import PreTrainedConfig
35
36 from vllm.compilation.decorators import support_torch_compile
37 from vllm.config import CacheConfig, VllmConfig
38 from vllm.distributed import (
39 get_pp_group,
40 get_tensor_model_parallel_rank,
41 get_tensor_model_parallel_world_size,
42 tensor_model_parallel_all_reduce,
43 )
44 from vllm.model_executor.layers.activation import FatreluAndMul, SiluAndMul
45 from vllm.model_executor.layers.attention import Attention
46 from vllm.model_executor.layers.fused_moe import (
47 GateLinear,
48 fused_experts,
49 fused_topk,
50 )
51 from vllm.model_executor.layers.layernorm import RMSNorm
52 from vllm.model_executor.layers.linear import (
53 MergedColumnParallelLinear,
54 QKVParallelLinear,
55 RowParallelLinear,
56 )
57 from vllm.model_executor.layers.logits_processor import LogitsProcessor
58 from vllm.model_executor.layers.quantization import QuantizationConfig
59 from vllm.model_executor.layers.rotary_embedding import get_rope
60 from vllm.model_executor.layers.vocab_parallel_embedding import (
61 ParallelLMHead,
62 VocabParallelEmbedding,
63 )
64 from vllm.model_executor.model_loader.weight_utils import default_weight_loader
65 from vllm.model_executor.utils import set_weight_attrs
66 from vllm.platforms import current_platform
67 from vllm.sequence import IntermediateTensors
68
69 from .interfaces import (
70 EagleModelMixin,
71 SupportsEagle,
72 SupportsEagle3,
73 SupportsLoRA,
74 SupportsPP,
75 )
76 from .utils import (
77 AutoWeightsLoader,
78 is_pp_missing_parameter,
79 make_empty_intermediate_tensors_factory,
80 make_layers,
81 maybe_prefix,
82 )
83
84
85 class MiniCPMMoE(nn.Module):
86 """A tensor-parallel MoE implementation that shards each expert
87 across all ranks.
88
89 Each expert's weights are sharded across all ranks and a fused MoE
90 kernel is used for the forward pass, and finally we reduce the outputs
91 across ranks.
92 """
93
94 def __init__(
95 self,
96 num_experts: int,
97 top_k: int,
98 hidden_size: int,
99 intermediate_size: int,
100 params_dtype: torch.dtype | None = None,
101 tp_size: int | None = None,
102 prefix: str = "",
103 ):
104 super().__init__()
105 self.tp_size = tp_size or get_tensor_model_parallel_world_size()
106 self.num_total_experts = num_experts
107 self.top_k = top_k
108 self.hidden_size = hidden_size
109 self.intermediate_size = intermediate_size // self.tp_size
110
111 if params_dtype is None:
112 params_dtype = torch.get_default_dtype()
113 self.params_dtype = params_dtype
114
115 self.gate = GateLinear(
116 self.hidden_size,
117 self.num_total_experts,
118 params_dtype=self.params_dtype,
119 prefix=f"{prefix}.gate",
120 )
121
122 self.ws = nn.Parameter(
123 torch.empty(
124 self.num_total_experts,
125 2 * self.intermediate_size,
126 self.hidden_size,
127 device=current_platform.device_type,
128 dtype=self.params_dtype,
129 )
130 )
131 self.w2s = nn.Parameter(
132 torch.empty(
133 self.num_total_experts,
134 self.hidden_size,
135 self.intermediate_size,
136 device=current_platform.device_type,
137 dtype=self.params_dtype,
138 )
139 )
140
141 set_weight_attrs(
142 self.ws,
143 {
144 "weight_loader": self.weight_loader,
145 },
146 )
147 set_weight_attrs(
148 self.w2s,
149 {
150 "weight_loader": self.weight_loader,
151 },
152 )
153
154 def weight_loader(
155 self,
156 param: nn.Parameter,
157 loaded_weight: torch.Tensor,
158 weight_name: str,
159 expert_id: int,
160 ):
161 tp_rank = get_tensor_model_parallel_rank()
162 param_data = param.data
163 shard_size = self.intermediate_size
164 shard = slice(tp_rank * shard_size, (tp_rank + 1) * shard_size)
165 if weight_name.endswith("w1.weight"):
166 param_data[expert_id, 0:shard_size, :] = loaded_weight[shard, :]
167 if weight_name.endswith("w3.weight"):
168 param_data[expert_id, shard_size : 2 * shard_size, :] = loaded_weight[
169 shard, :
170 ]
171 if weight_name.endswith("w2.weight"):
172 param_data[expert_id, :, :] = loaded_weight[:, shard]
173
174 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
175 num_tokens, hidden_size = hidden_states.shape
176 hidden_states = hidden_states.view(-1, self.hidden_size)
177 # router_logits: (num_tokens, n_experts)
178 router_logits, _ = self.gate(hidden_states)
179
180 topk_weights, topk_ids, _ = fused_topk(
181 hidden_states, router_logits, self.top_k, renormalize=True
182 )
183
184 final_hidden_states = fused_experts(
185 hidden_states, self.ws, self.w2s, topk_weights, topk_ids
186 )
187
188 if self.tp_size > 1:
189 final_hidden_states = tensor_model_parallel_all_reduce(final_hidden_states)
190
191 return final_hidden_states.view(num_tokens, hidden_size)
192
193
194 class MiniCPMMLP(nn.Module):
195 def __init__(
196 self,
197 hidden_size: int,
198 intermediate_size: int,
199 hidden_act: str,
200 hidden_act_param: float,
201 quant_config: QuantizationConfig | None = None,
202 prefix: str = "",
203 ) -> None:
204 super().__init__()
205 self.gate_up_proj = MergedColumnParallelLinear(
206 hidden_size,
207 [intermediate_size] * 2,
208 bias=False,
209 quant_config=quant_config,
210 prefix=f"{prefix}.gate_up_proj",
211 )
212 self.down_proj = RowParallelLinear(
213 intermediate_size,
214 hidden_size,
215 bias=False,
216 quant_config=quant_config,
217 prefix=f"{prefix}.down_proj",
218 )
219 if hidden_act == "silu":
220 self.act_fn = SiluAndMul()
221 elif hidden_act == "fatrelu":
222 self.act_fn = FatreluAndMul(threshold=hidden_act_param)
223 else:
224 raise ValueError(
225 f"Unsupported activation: {hidden_act}. "
226 "Only silu and fatrelu are supported for now."
227 )
228
229 def forward(self, x):
230 gate_up, _ = self.gate_up_proj(x)
231 x = self.act_fn(gate_up)
232 x, _ = self.down_proj(x)
233 return x
234
235
236 class MiniCPMAttention(nn.Module):
237 def __init__(
238 self,
239 hidden_size: int,
240 num_heads: int,
241 num_kv_heads: int,
242 rope_parameters: dict[str, Any] | None = None,
243 max_position_embeddings: int = 8192,
244 cache_config: CacheConfig | None = None,
245 quant_config: QuantizationConfig | None = None,
246 prefix: str = "",
247 ) -> None:
248 super().__init__()
249 self.hidden_size = hidden_size
250 tp_size = get_tensor_model_parallel_world_size()
251 self.total_num_heads = num_heads
252 assert self.total_num_heads % tp_size == 0
253 self.num_heads = self.total_num_heads // tp_size
254 self.total_num_kv_heads = num_kv_heads
255 if self.total_num_kv_heads >= tp_size:
256 # Number of KV heads is greater than TP size, so we partition
257 # the KV heads across multiple tensor parallel GPUs.
258 assert self.total_num_kv_heads % tp_size == 0
259 else:
260 # Number of KV heads is less than TP size, so we replicate
261 # the KV heads across multiple tensor parallel GPUs.
262 assert tp_size % self.total_num_kv_heads == 0
263 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
264 self.head_dim = hidden_size // self.total_num_heads
265 self.q_size = self.num_heads * self.head_dim
266 self.kv_size = self.num_kv_heads * self.head_dim
267 self.scaling = self.head_dim**-0.5
268 self.max_position_embeddings = max_position_embeddings
269
270 self.qkv_proj = QKVParallelLinear(
271 hidden_size,
272 self.head_dim,
273 self.total_num_heads,
274 self.total_num_kv_heads,
275 bias=False,
276 quant_config=quant_config,
277 prefix=f"{prefix}.qkv_proj",
278 )
279 self.o_proj = RowParallelLinear(
280 self.total_num_heads * self.head_dim,
281 hidden_size,
282 bias=False,
283 quant_config=quant_config,
284 prefix=f"{prefix}.o_proj",
285 )
286
287 self.rotary_emb = get_rope(
288 self.head_dim,
289 max_position=max_position_embeddings,
290 rope_parameters=rope_parameters,
291 )
292
293 self.attn = Attention(
294 self.num_heads,
295 self.head_dim,
296 self.scaling,
297 num_kv_heads=self.num_kv_heads,
298 cache_config=cache_config,
299 quant_config=quant_config,
300 prefix=f"{prefix}.attn",
301 )
302
303 def forward(
304 self,
305 positions: torch.Tensor,
306 hidden_states: torch.Tensor,
307 ) -> torch.Tensor:
308 qkv, _ = self.qkv_proj(hidden_states)
309 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
310 q, k = self.rotary_emb(positions, q, k)
311 attn_output = self.attn(q, k, v)
312 output, _ = self.o_proj(attn_output)
313 return output
314
315
316 class MiniCPMDecoderLayer(nn.Module):
317 def __init__(
318 self,
319 config: PreTrainedConfig,
320 cache_config: CacheConfig | None = None,
321 quant_config: QuantizationConfig | None = None,
322 prefix: str = "",
323 ) -> None:
324 super().__init__()
325 self.config = config
326 self.cache_config = cache_config
327 self.quant_config = quant_config
328 self.hidden_size = config.hidden_size
329 self.max_position_embeddings = getattr(config, "max_position_embeddings", 8192)
330 self.prefix = prefix
331 self._init_attn_block()
332 self._init_ffn_block()
333
334 def _init_attn_block(self):
335 self.input_layernorm = RMSNorm(
336 self.config.hidden_size, eps=self.config.rms_norm_eps
337 )
338 self.self_attn = MiniCPMAttention(
339 hidden_size=self.hidden_size,
340 num_heads=self.config.num_attention_heads,
341 num_kv_heads=self.config.num_key_value_heads,
342 rope_parameters=self.config.rope_parameters,
343 max_position_embeddings=self.max_position_embeddings,
344 cache_config=self.cache_config,
345 quant_config=self.quant_config,
346 prefix=f"{self.prefix}.self_attn",
347 )
348
349 def _init_ffn_block(self):
350 self.post_attention_layernorm = RMSNorm(
351 self.config.hidden_size, eps=self.config.rms_norm_eps
352 )
353 self.num_experts = getattr(self.config, "num_experts", 0)
354 if self.num_experts == 0:
355 self.mlp = MiniCPMMLP(
356 hidden_size=self.hidden_size,
357 intermediate_size=self.config.intermediate_size,
358 hidden_act=self.config.hidden_act,
359 hidden_act_param=getattr(self.config, "hidden_act_param", 0.0),
360 quant_config=self.quant_config,
361 prefix=f"{self.prefix}.mlp",
362 )
363 else:
364 self.mlp = MiniCPMMoE(
365 num_experts=self.config.num_experts,
366 top_k=self.config.num_experts_per_tok,
367 hidden_size=self.config.hidden_size,
368 intermediate_size=self.config.intermediate_size,
369 prefix=f"{self.prefix}.mlp",
370 )
371
372 def forward(
373 self,
374 positions: torch.Tensor,
375 hidden_states: torch.Tensor,
376 residual: torch.Tensor | None,
377 ) -> tuple[torch.Tensor, torch.Tensor]:
378 # Self Attention
379 residual = hidden_states
380 hidden_states = self.input_layernorm(hidden_states)
381 hidden_states = self.self_attn(
382 positions=positions,
383 hidden_states=hidden_states,
384 )
385 hidden_states = residual + hidden_states * (
386 self.config.scale_depth / math.sqrt(self.config.num_hidden_layers)
387 )
388
389 # Fully Connected
390 residual = hidden_states
391 hidden_states = self.post_attention_layernorm(hidden_states)
392 hidden_states = self.mlp(hidden_states)
393 hidden_states = residual + hidden_states * (
394 self.config.scale_depth / math.sqrt(self.config.num_hidden_layers)
395 )
396
397 return hidden_states, None
398
399
400 @support_torch_compile
401 class MiniCPMModel(nn.Module, EagleModelMixin):
402 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
403 super().__init__()
404
405 config = vllm_config.model_config.hf_config
406 cache_config = vllm_config.cache_config
407 quant_config = vllm_config.quant_config
408
409 self.config = config
410 self.cache_config = cache_config
411 self.quant_config = quant_config
412
413 self.vocab_size = config.vocab_size
414
415 self.embed_tokens = VocabParallelEmbedding(
416 self.vocab_size,
417 config.hidden_size,
418 )
419 self.num_experts = getattr(self.config, "num_experts", 0)
420 self._init_layers(prefix, config, cache_config, quant_config)
421 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
422
423 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
424 ["hidden_states", "residual"], self.config.hidden_size
425 )
426
427 def _init_layers(
428 self,
429 prefix: str,
430 config: PreTrainedConfig,
431 cache_config: CacheConfig | None,
432 quant_config: QuantizationConfig | None,
433 ):
434 self.start_layer, self.end_layer, self.layers = make_layers(
435 config.num_hidden_layers,
436 lambda prefix: MiniCPMDecoderLayer(
437 config, cache_config, quant_config, prefix=prefix
438 ),
439 prefix=f"{prefix}.layers",
440 )
441
442 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
443 embedding = self.embed_tokens(input_ids)
444 return embedding * self.config.scale_emb
445
446 def forward(
447 self,
448 input_ids: torch.Tensor | None,
449 positions: torch.Tensor,
450 intermediate_tensors: IntermediateTensors | None = None,
451 inputs_embeds: torch.Tensor | None = None,
452 ) -> torch.Tensor | IntermediateTensors | tuple[torch.Tensor, list[torch.Tensor]]:
453 if get_pp_group().is_first_rank:
454 if inputs_embeds is not None:
455 hidden_states = inputs_embeds
456 else:
457 hidden_states = self.embed_input_ids(input_ids)
458 residual = None
459 else:
460 assert intermediate_tensors is not None
461 hidden_states = intermediate_tensors["hidden_states"]
462 residual = intermediate_tensors["residual"]
463
464 aux_hidden_states = self._maybe_add_hidden_state([], 0, hidden_states, residual)
465 for idx, layer in enumerate(
466 islice(self.layers, self.start_layer, self.end_layer)
467 ):
468 hidden_states, residual = layer(
469 positions,
470 hidden_states,
471 residual,
472 )
473 self._maybe_add_hidden_state(
474 aux_hidden_states, idx + 1, hidden_states, residual
475 )
476
477 if not get_pp_group().is_last_rank:
478 return IntermediateTensors(
479 {"hidden_states": hidden_states, "residual": residual}
480 )
481
482 hidden_states = self.norm(hidden_states)
483
484 if len(aux_hidden_states) > 0:
485 return hidden_states, aux_hidden_states
486 return hidden_states
487
488 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
489 stacked_params_mapping = [
490 # (param_name, shard_name, shard_id)
491 ("qkv_proj", "q_proj", "q"),
492 ("qkv_proj", "k_proj", "k"),
493 ("qkv_proj", "v_proj", "v"),
494 ("gate_up_proj", "gate_proj", 0),
495 ("gate_up_proj", "up_proj", 1),
496 ]
497 expert_params_mapping = [
498 # (param_name, weight_name, expert_id)
499 (
500 "ws" if weight_name in ["w1", "w3"] else "w2s",
501 f"experts.{expert_id}.{weight_name}.weight",
502 expert_id,
503 )
504 for expert_id in range(self.num_experts)
505 for weight_name in ["w1", "w2", "w3"]
506 ]
507 params_dict = dict(self.named_parameters())
508
509 loaded_params: set[str] = set()
510 for name, loaded_weight in weights:
511 if "rotary_emb.inv_freq" in name:
512 continue
513 if "rotary_emb.cos_cached" in name or "rotary_emb.sin_cached" in name:
514 # Models trained using ColossalAI may include these tensors in
515 # the checkpoint. Skip them.
516 continue
517 for param_name, weight_name, shard_id in stacked_params_mapping:
518 if weight_name not in name:
519 continue
520 name = name.replace(weight_name, param_name)
521 # Skip loading extra bias for GPTQ models.
522 if name.endswith(".bias") and name not in params_dict:
523 continue
524 if is_pp_missing_parameter(name, self):
525 continue
526 param = params_dict[name]
527 weight_loader = param.weight_loader
528 weight_loader(param, loaded_weight, shard_id)
529 break
530 else:
531 for param_name, weight_name, expert_id in expert_params_mapping:
532 if weight_name not in name:
533 continue
534 name = name.replace(weight_name, param_name)
535 if is_pp_missing_parameter(name, self):
536 continue
537 param = params_dict[name]
538 weight_loader = param.weight_loader
539 weight_loader(
540 param, loaded_weight, weight_name, expert_id=expert_id
541 )
542 break
543 else:
544 # Skip loading extra bias for GPTQ models.
545 if name.endswith(".bias") and name not in params_dict:
546 continue
547 if is_pp_missing_parameter(name, self):
548 continue
549 param = params_dict[name]
550 weight_loader = getattr(
551 param, "weight_loader", default_weight_loader
552 )
553 weight_loader(param, loaded_weight)
554 loaded_params.add(name)
555 return loaded_params
556
557
558 class MiniCPMForCausalLM(
559 nn.Module, SupportsLoRA, SupportsPP, SupportsEagle, SupportsEagle3
560 ):
561 packed_modules_mapping = {
562 "qkv_proj": [
563 "q_proj",
564 "k_proj",
565 "v_proj",
566 ],
567 "gate_up_proj": [
568 "gate_proj",
569 "up_proj",
570 ],
571 }
572
573 # LoRA specific attributes
574 embedding_modules = {
575 "embed_tokens": "input_embeddings",
576 "lm_head": "output_embeddings",
577 }
578
579 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
580 super().__init__()
581 config = vllm_config.model_config.hf_config
582 cache_config = vllm_config.cache_config
583 quant_config = vllm_config.quant_config
584
585 parallel_config = vllm_config.parallel_config
586
587 self.prefix = prefix
588 self.vllm_config = vllm_config
589 self.config = config
590
591 self.cache_config = cache_config
592 self.quant_config = quant_config
593
594 self.model = self._init_model(
595 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
596 )
597
598 self.lm_head = ParallelLMHead(
599 config.vocab_size,
600 config.hidden_size,
601 quant_config=quant_config,
602 prefix=maybe_prefix(prefix, "lm_head"),
603 )
604 if config.tie_word_embeddings:
605 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
606 self.scale_width = self.config.hidden_size / self.config.dim_model_base
607
608 self.logits_processor = LogitsProcessor(config.vocab_size)
609 self.make_empty_intermediate_tensors = (
610 self.model.make_empty_intermediate_tensors
611 )
612 if parallel_config.enable_eplb and getattr(config, "num_experts", 0) > 0:
613 raise NotImplementedError("EPLB is not supported for MiniCPM yet.")
614
615 def _init_model(self, *, vllm_config: VllmConfig, prefix: str = ""):
616 return MiniCPMModel(vllm_config=vllm_config, prefix=prefix)
617
618 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
619 return self.model.embed_input_ids(input_ids)
620
621 def forward(
622 self,
623 input_ids: torch.Tensor | None,
624 positions: torch.Tensor,
625 intermediate_tensors: IntermediateTensors | None = None,
626 inputs_embeds: torch.Tensor | None = None,
627 ) -> torch.Tensor | IntermediateTensors | tuple[torch.Tensor, list[torch.Tensor]]:
628 model_output = self.model(
629 input_ids, positions, intermediate_tensors, inputs_embeds
630 )
631
632 if isinstance(model_output, tuple) and len(model_output) == 2:
633 # Aux hidden states are present.
634 hidden_states, aux_hidden_states = model_output
635 hidden_states = hidden_states / self.scale_width
636 return hidden_states, aux_hidden_states
637 else:
638 # Only hidden states or IntermediateTensors
639 if isinstance(model_output, IntermediateTensors):
640 return model_output
641 else:
642 hidden_states = model_output / self.scale_width
643 return hidden_states
644
645 def compute_logits(
646 self,
647 hidden_states: torch.Tensor,
648 ) -> torch.Tensor | None:
649 logits = self.logits_processor(self.lm_head, hidden_states)
650 return logits
651
652 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
653 loader = AutoWeightsLoader(self)
654 return loader.load_weights(weights)