main
py 457 lines 16.5 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Inference-only PLaMo3 model."""
4
5 from collections.abc import Iterable
6 from itertools import islice
7 from typing import TYPE_CHECKING, Any
8
9 import torch
10 from torch import nn
11 from transformers import PreTrainedConfig
12
13 from vllm.compilation.decorators import support_torch_compile
14 from vllm.config import VllmConfig
15 from vllm.distributed import get_tensor_model_parallel_world_size
16 from vllm.distributed.parallel_state import get_pp_group
17 from vllm.model_executor.layers.activation import SiluAndMul
18 from vllm.model_executor.layers.attention import Attention
19 from vllm.model_executor.layers.layernorm import RMSNorm
20 from vllm.model_executor.layers.linear import (
21 MergedColumnParallelLinear,
22 QKVParallelLinear,
23 RowParallelLinear,
24 )
25 from vllm.model_executor.layers.logits_processor import LogitsProcessor
26 from vllm.model_executor.layers.quantization import QuantizationConfig
27 from vllm.model_executor.layers.rotary_embedding import get_rope
28 from vllm.model_executor.layers.vocab_parallel_embedding import (
29 DEFAULT_VOCAB_PADDING_SIZE,
30 ParallelLMHead,
31 VocabParallelEmbedding,
32 )
33 from vllm.model_executor.model_loader.weight_utils import (
34 LoaderFunction,
35 composed_weight_loader,
36 default_weight_loader,
37 )
38 from vllm.model_executor.models.interfaces import (
39 EagleModelMixin,
40 SupportsEagle3,
41 SupportsLoRA,
42 SupportsPP,
43 )
44 from vllm.model_executor.models.utils import (
45 AutoWeightsLoader,
46 extract_layer_index,
47 make_empty_intermediate_tensors_factory,
48 make_layers,
49 maybe_prefix,
50 )
51 from vllm.model_executor.utils import set_weight_attrs
52 from vllm.sequence import IntermediateTensors
53
54 # Only used for type hinting.
55 if TYPE_CHECKING:
56
57 class Plamo3Config(PreTrainedConfig): # type: ignore
58 model_type: str = "plamo3"
59
60 hidden_size: int
61 num_hidden_layers: int
62 rms_norm_eps: float
63 # Attention
64 num_attention_heads: int
65 head_dim: int
66 num_key_value_heads: int
67 # vllm rename `sliding_window` attr to `interleaved_sliding_window`
68 # if `sliding_window` is list
69 interleaved_sliding_window: list[int | None]
70 sliding_window_pattern: int
71 rope_parameters: dict[str, Any]
72 rope_local_theta: int
73 # MLP
74 intermediate_size: int
75 # Tokenizer
76 vocab_size: int
77
78
79 def rms_norm_weight_loader(offset: float) -> LoaderFunction:
80 return composed_weight_loader(
81 default_weight_loader,
82 lambda x: x + offset,
83 )
84
85
86 class DenseMLP(nn.Module):
87 def __init__(
88 self,
89 config: "Plamo3Config",
90 quant_config: QuantizationConfig | None = None,
91 prefix: str = "",
92 ) -> None:
93 super().__init__()
94 self.hidden_size = config.hidden_size
95 self.intermediate_size = config.intermediate_size
96 self.gate_up_proj = MergedColumnParallelLinear(
97 self.hidden_size,
98 [self.intermediate_size] * 2,
99 bias=False,
100 prefix=f"{prefix}.gate_up_proj",
101 quant_config=quant_config,
102 return_bias=False,
103 )
104 self.act = SiluAndMul()
105 self.down_proj = RowParallelLinear(
106 self.intermediate_size,
107 self.hidden_size,
108 bias=False,
109 prefix=f"{prefix}.down_proj",
110 quant_config=quant_config,
111 return_bias=False,
112 )
113
114 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
115 h = self.gate_up_proj(hidden_states)
116 h = self.act(h)
117 return self.down_proj(h)
118
119
120 class Plamo3AttentionMixer(nn.Module):
121 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "", **kwargs) -> None:
122 super().__init__()
123 config = vllm_config.model_config.hf_config
124 quant_config = vllm_config.quant_config
125
126 self.hidden_size = config.hidden_size
127 tp_size = get_tensor_model_parallel_world_size()
128 self.total_num_heads = config.num_attention_heads
129 assert self.total_num_heads % tp_size == 0
130 self.num_heads = self.total_num_heads // tp_size
131 self.total_num_kv_heads = config.num_key_value_heads
132 if self.total_num_kv_heads >= tp_size:
133 # Number of KV heads is greater than TP size, so we partition
134 # the KV heads across multiple tensor parallel GPUs.
135 assert self.total_num_kv_heads % tp_size == 0
136 else:
137 # Number of KV heads is less than TP size, so we replicate
138 # the KV heads across multiple tensor parallel GPUs.
139 assert tp_size % self.total_num_kv_heads == 0
140 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
141 self.head_dim = config.head_dim
142 self.q_size = self.num_heads * self.head_dim
143 self.kv_size = self.num_kv_heads * self.head_dim
144 self.scaling = self.head_dim**-0.5
145
146 self.qkv_proj = QKVParallelLinear(
147 config.hidden_size,
148 self.head_dim,
149 self.total_num_heads,
150 self.total_num_kv_heads,
151 bias=False,
152 quant_config=quant_config,
153 prefix=f"{prefix}.qkv_proj",
154 )
155 self.o_proj = RowParallelLinear(
156 self.total_num_heads * self.head_dim,
157 config.hidden_size,
158 bias=False,
159 quant_config=quant_config,
160 prefix=f"{prefix}.o_proj",
161 )
162
163 layer_idx = extract_layer_index(prefix)
164 layer_type = config.layer_types[layer_idx]
165 is_sliding = layer_type == "sliding_attention"
166
167 # Initialize the rotary embedding.
168 if layer_type in config.rope_parameters:
169 # Transformers v5 rope config.
170 rope_parameters = config.rope_parameters[layer_type]
171 else:
172 # Transformers v4 rope config.
173 # Global attention. Use the values in config.json.
174 rope_parameters = config.rope_parameters
175 # Local attention. Override the values in config.json.
176 if is_sliding:
177 rope_parameters = dict(
178 rope_type="default", rope_theta=config.rope_local_theta
179 )
180 max_position = config.max_position_embeddings
181 if hasattr(vllm_config.model_config, "max_model_len") and isinstance(
182 vllm_config.model_config.max_model_len, int
183 ):
184 max_position = min(max_position, vllm_config.model_config.max_model_len)
185
186 self.rotary_emb = get_rope(
187 self.head_dim,
188 max_position=max_position,
189 rope_parameters=rope_parameters,
190 )
191 self.q_norm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
192 set_weight_attrs(
193 self.q_norm.weight, {"weight_loader": rms_norm_weight_loader(offset=1.0)}
194 )
195 self.k_norm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
196 set_weight_attrs(
197 self.k_norm.weight, {"weight_loader": rms_norm_weight_loader(offset=1.0)}
198 )
199 self.attn = Attention(
200 self.num_heads,
201 self.head_dim,
202 self.scaling,
203 num_kv_heads=self.num_kv_heads,
204 cache_config=vllm_config.cache_config,
205 per_layer_sliding_window=config.interleaved_sliding_window[layer_idx],
206 prefix=f"{prefix}.attn",
207 )
208
209 def forward(
210 self,
211 positions: torch.Tensor,
212 hidden_states: torch.Tensor,
213 residual: torch.Tensor | None,
214 **kwargs: Any,
215 ) -> torch.Tensor:
216 qkv, _ = self.qkv_proj(hidden_states)
217 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
218
219 q_shape = q.shape
220 q = q.reshape(q_shape[:-1] + (q_shape[-1] // self.head_dim, self.head_dim))
221 normalized_q = self.q_norm.forward_native(q)
222 assert isinstance(normalized_q, torch.Tensor)
223 q = normalized_q.reshape(q_shape)
224 k_shape = k.shape
225 k = k.reshape(k_shape[:-1] + (k_shape[-1] // self.head_dim, self.head_dim))
226 normalized_k = self.k_norm.forward_native(k)
227 assert isinstance(normalized_k, torch.Tensor)
228 k = normalized_k.reshape(k_shape)
229
230 q, k = self.rotary_emb(positions, q, k)
231 attn_output = self.attn(q, k, v)
232 output, _ = self.o_proj(attn_output)
233 return output
234
235
236 class Plamo3DecoderLayer(nn.Module):
237 def __init__(
238 self, vllm_config: VllmConfig, prefix: str = "", **kwargs: Any
239 ) -> None:
240 super().__init__()
241 config = vllm_config.model_config.hf_config
242 quant_config = vllm_config.quant_config
243
244 self.mixer = Plamo3AttentionMixer(
245 vllm_config=vllm_config,
246 prefix=f"{prefix}.mixer",
247 )
248
249 self.mlp = DenseMLP(
250 config=config, quant_config=quant_config, prefix=f"{prefix}.mlp"
251 )
252 self.pre_mixer_norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
253 set_weight_attrs(
254 self.pre_mixer_norm.weight,
255 {"weight_loader": rms_norm_weight_loader(offset=1.0)},
256 )
257 self.post_mixer_norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
258 set_weight_attrs(
259 self.post_mixer_norm.weight,
260 {"weight_loader": rms_norm_weight_loader(offset=1.0 / 5)},
261 )
262 self.pre_mlp_norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
263 set_weight_attrs(
264 self.pre_mlp_norm.weight,
265 {"weight_loader": rms_norm_weight_loader(offset=1.0)},
266 )
267 self.post_mlp_norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
268 set_weight_attrs(
269 self.post_mlp_norm.weight,
270 {"weight_loader": rms_norm_weight_loader(offset=1.0 / (5**1.5))},
271 )
272
273 def forward(
274 self,
275 positions: torch.Tensor,
276 hidden_states: torch.Tensor,
277 residual: torch.Tensor | None,
278 **kwargs: Any,
279 ) -> tuple[torch.Tensor, torch.Tensor | None]:
280 if residual is None:
281 residual = hidden_states
282 hidden_states = self.pre_mixer_norm(hidden_states)
283 else:
284 hidden_states, residual = self.pre_mixer_norm(hidden_states, residual)
285
286 hidden_states = self.mixer(
287 positions=positions, hidden_states=hidden_states, residual=residual
288 )
289 hidden_states = self.post_mixer_norm(hidden_states)
290 # Fully Connected
291 hidden_states, residual = self.pre_mlp_norm(hidden_states, residual)
292 hidden_states = self.mlp(hidden_states)
293 hidden_states = self.post_mlp_norm(hidden_states)
294 return hidden_states, residual
295
296
297 class Plamo3Decoder(torch.nn.Module, EagleModelMixin):
298 def __init__(self, vllm_config: VllmConfig, prefix: str = "") -> None:
299 super().__init__()
300 num_hidden_layers = vllm_config.model_config.hf_config.num_hidden_layers
301
302 self.start_layer, self.end_layer, self.layers = make_layers(
303 num_hidden_layers,
304 lambda prefix: Plamo3DecoderLayer(vllm_config, prefix=prefix),
305 prefix=f"{prefix}.layers",
306 )
307
308 def __len__(self) -> int:
309 return len(self.layers)
310
311 def forward(
312 self,
313 positions: torch.Tensor,
314 hidden_states: torch.Tensor,
315 residual: torch.Tensor | None,
316 ) -> tuple[torch.Tensor, torch.Tensor | None, list[torch.Tensor]]:
317 aux_hidden_states = self._maybe_add_hidden_state([], 0, hidden_states, residual)
318 for idx, layer in enumerate(
319 islice(self.layers, self.start_layer, self.end_layer)
320 ):
321 hidden_states, residual = layer(
322 positions=positions,
323 hidden_states=hidden_states,
324 residual=residual,
325 )
326 self._maybe_add_hidden_state(
327 aux_hidden_states, idx + 1, hidden_states, residual
328 )
329 return hidden_states, residual, aux_hidden_states
330
331
332 @support_torch_compile
333 class Plamo3Model(nn.Module, EagleModelMixin):
334 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
335 super().__init__()
336 config = vllm_config.model_config.hf_config
337
338 self.config = config
339 self.vocab_size = config.vocab_size
340 self.org_vocab_size = config.vocab_size
341
342 self.embed_tokens = VocabParallelEmbedding(
343 self.vocab_size,
344 config.hidden_size,
345 org_num_embeddings=config.vocab_size,
346 prefix=f"{prefix}.embed_tokens",
347 )
348 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
349 ["hidden_states", "residual"], config.hidden_size
350 )
351 self.layers = Plamo3Decoder(vllm_config, prefix=f"{prefix}.layers")
352 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
353 set_weight_attrs(
354 self.norm.weight,
355 {"weight_loader": rms_norm_weight_loader(offset=1.0)},
356 )
357
358 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
359 return self.embed_tokens(input_ids)
360
361 def _set_aux_hidden_state_layers(self, layers: tuple[int, ...]) -> None:
362 self.layers._set_aux_hidden_state_layers(layers)
363
364 def forward(
365 self,
366 input_ids: torch.Tensor | None,
367 positions: torch.Tensor,
368 intermediate_tensors: IntermediateTensors | None = None,
369 inputs_embeds: torch.Tensor | None = None,
370 ) -> torch.Tensor | IntermediateTensors | tuple[torch.Tensor, list[torch.Tensor]]:
371 if get_pp_group().is_first_rank:
372 if inputs_embeds is not None:
373 hidden_states = inputs_embeds
374 else:
375 hidden_states = self.embed_input_ids(input_ids)
376 residual = None
377 else:
378 assert intermediate_tensors is not None
379 hidden_states = intermediate_tensors["hidden_states"]
380 residual = intermediate_tensors["residual"]
381
382 hidden_states, residual, aux_hidden_states = self.layers(
383 positions=positions, hidden_states=hidden_states, residual=residual
384 )
385 if not get_pp_group().is_last_rank:
386 return IntermediateTensors(
387 {"hidden_states": hidden_states, "residual": residual}
388 )
389 hidden_states, _ = self.norm(hidden_states, residual)
390 if len(aux_hidden_states) > 0:
391 return hidden_states, aux_hidden_states
392 return hidden_states
393
394
395 class Plamo3ForCausalLM(nn.Module, SupportsLoRA, SupportsPP, SupportsEagle3):
396 packed_modules_mapping = {
397 "qkv_proj": ["qkv_proj"],
398 "gate_up_proj": ["gate_up_proj"],
399 }
400
401 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
402 super().__init__()
403 self.config = vllm_config.model_config.hf_config
404 self.vllm_config = vllm_config
405 self.model_config = vllm_config.model_config
406 self.scheduler_config = vllm_config.scheduler_config
407
408 self.model = Plamo3Model(
409 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
410 )
411
412 self.vocab_size = self.config.vocab_size
413 self.unpadded_vocab_size = self.config.vocab_size
414
415 num_embeddings = ((self.vocab_size + 15) // 16) * 16
416 self.lm_head = ParallelLMHead(
417 num_embeddings,
418 self.config.hidden_size,
419 org_num_embeddings=self.config.vocab_size,
420 padding_size=DEFAULT_VOCAB_PADDING_SIZE,
421 prefix=f"{prefix}.lm_head",
422 )
423 if self.config.tie_word_embeddings:
424 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
425
426 self.logits_processor = LogitsProcessor(
427 self.unpadded_vocab_size, self.config.vocab_size
428 )
429 self.make_empty_intermediate_tensors = (
430 self.model.make_empty_intermediate_tensors
431 )
432
433 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
434 return self.model.embed_input_ids(input_ids)
435
436 def forward(
437 self,
438 input_ids: torch.Tensor | None,
439 positions: torch.Tensor,
440 intermediate_tensors: IntermediateTensors | None = None,
441 inputs_embeds: torch.Tensor | None = None,
442 ) -> torch.Tensor | IntermediateTensors | tuple[torch.Tensor, list[torch.Tensor]]:
443 hidden_states = self.model(
444 input_ids, positions, intermediate_tensors, inputs_embeds
445 )
446 return hidden_states
447
448 def compute_logits(
449 self,
450 hidden_states: torch.Tensor,
451 ) -> torch.Tensor | None:
452 logits = self.logits_processor(self.lm_head, hidden_states)
453 return logits
454
455 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
456 loader = AutoWeightsLoader(self)
457 return loader.load_weights(weights)