main
py 473 lines 16.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 Nemotron model compatible with HuggingFace weights."""
26
27 from collections.abc import Iterable
28 from itertools import islice
29
30 import torch
31 from torch import nn
32
33 from vllm.compilation.decorators import support_torch_compile
34 from vllm.config import CacheConfig, VllmConfig
35 from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
36 from vllm.model_executor.custom_op import CustomOp
37 from vllm.model_executor.layers.activation import get_act_fn
38 from vllm.model_executor.layers.attention import Attention
39 from vllm.model_executor.layers.linear import (
40 ColumnParallelLinear,
41 QKVParallelLinear,
42 RowParallelLinear,
43 )
44 from vllm.model_executor.layers.logits_processor import LogitsProcessor
45 from vllm.model_executor.layers.quantization import QuantizationConfig
46 from vllm.model_executor.layers.rotary_embedding import get_rope
47 from vllm.model_executor.layers.vocab_parallel_embedding import (
48 ParallelLMHead,
49 VocabParallelEmbedding,
50 )
51 from vllm.sequence import IntermediateTensors
52 from vllm.transformers_utils.configs.nemotron import NemotronConfig
53
54 from .interfaces import SupportsLoRA, SupportsPP
55 from .utils import (
56 AutoWeightsLoader,
57 PPMissingLayer,
58 WeightsMapper,
59 make_empty_intermediate_tensors_factory,
60 make_layers,
61 maybe_prefix,
62 )
63
64 # The architecture is pretty similar to Llama, with these changes:
65 # - There is no gate_proj, just up_proj
66 # - Normal LayerNorm (with a +1 to the weights) instead of RMSNorm
67 # - Squared ReLU instead of SwiGLU
68 # - Adds a partial_rotary_factor to RoPE
69
70
71 def _cast_if_autocast_enabled(device_type: str, *args):
72 if not torch.is_autocast_enabled(device_type):
73 return args
74 return torch.amp.autocast_mode._cast(
75 args,
76 device_type=device_type,
77 dtype=torch.get_autocast_dtype(device_type),
78 )
79
80
81 @CustomOp.register("nemotron_layer_norm")
82 class NemotronLayerNorm1P(CustomOp):
83 """LayerNorm variant used by Nemotron: `x * (1 + w)` instead of `x * w`."""
84
85 def __init__(self, hidden_size: int, eps: float = 1e-5) -> None:
86 super().__init__()
87 self.normalized_shape = (hidden_size,)
88 self.eps = eps
89 self.weight = nn.Parameter(torch.zeros(hidden_size))
90 self.bias = nn.Parameter(torch.zeros(hidden_size))
91
92 def forward_native(
93 self,
94 x: torch.Tensor,
95 residual: torch.Tensor | None = None,
96 ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
97 if residual is not None:
98 x = x + residual
99 residual = x
100 device_type = x.device.type
101 args = _cast_if_autocast_enabled(
102 device_type, x, self.normalized_shape, self.weight + 1, self.bias, self.eps
103 )
104 with torch.amp.autocast(device_type, enabled=False):
105 x = torch.nn.functional.layer_norm(*args)
106 return x if residual is None else (x, residual)
107
108 def forward_cuda(
109 self,
110 x: torch.Tensor,
111 residual: torch.Tensor | None = None,
112 ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
113 return self.forward_native(x, residual)
114
115 def forward_xpu(
116 self,
117 x: torch.Tensor,
118 residual: torch.Tensor | None = None,
119 ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
120 import vllm._xpu_ops # noqa: F401 registers torch.ops.vllm.xpu_nemotron_layer_norm
121
122 if not hasattr(torch.ops._C, "nemotron_layer_norm"):
123 return self.forward_native(x, residual)
124 if residual is not None:
125 if not hasattr(torch.ops._C, "fused_add_nemotron_layer_norm"):
126 return self.forward_native(x, residual)
127 torch.ops.vllm.xpu_fused_add_nemotron_layer_norm(
128 x, residual, self.weight, self.bias, self.eps
129 )
130 return x, residual
131 # empty_like preserves x's strides, but the kernel requires a
132 # contiguous out (unlike x, which it can handle non-contiguous).
133 out = torch.empty(x.shape, device=x.device, dtype=x.dtype)
134 torch.ops.vllm.xpu_nemotron_layer_norm(out, x, self.weight, self.bias, self.eps)
135 return out
136
137
138 class NemotronMLP(nn.Module):
139 def __init__(
140 self,
141 hidden_size: int,
142 intermediate_size: int,
143 hidden_act: str,
144 quant_config: QuantizationConfig | None = None,
145 bias: bool = False,
146 prefix: str = "",
147 ) -> None:
148 super().__init__()
149 self.up_proj = ColumnParallelLinear(
150 input_size=hidden_size,
151 output_size=intermediate_size,
152 bias=bias,
153 quant_config=quant_config,
154 prefix=f"{prefix}.up_proj",
155 )
156 self.down_proj = RowParallelLinear(
157 input_size=intermediate_size,
158 output_size=hidden_size,
159 bias=bias,
160 quant_config=quant_config,
161 prefix=f"{prefix}.down_proj",
162 )
163 self.act_fn = get_act_fn(hidden_act)
164
165 def forward(self, x):
166 up, _ = self.up_proj(x)
167 x = self.act_fn(up)
168 x, _ = self.down_proj(x)
169 return x
170
171
172 class NemotronAttention(nn.Module):
173 def __init__(
174 self,
175 config: NemotronConfig,
176 hidden_size: int,
177 num_heads: int,
178 num_kv_heads: int,
179 max_position_embeddings: int = 8192,
180 quant_config: QuantizationConfig | None = None,
181 bias: bool = False,
182 cache_config: CacheConfig | None = None,
183 prefix: str = "",
184 ) -> None:
185 super().__init__()
186 self.hidden_size = hidden_size
187 tp_size = get_tensor_model_parallel_world_size()
188 self.total_num_heads = num_heads
189 assert self.total_num_heads % tp_size == 0
190 self.num_heads = self.total_num_heads // tp_size
191 self.total_num_kv_heads = num_kv_heads
192 if self.total_num_kv_heads >= tp_size:
193 # Number of KV heads is greater than TP size, so we partition
194 # the KV heads across multiple tensor parallel GPUs.
195 assert self.total_num_kv_heads % tp_size == 0
196 else:
197 # Number of KV heads is less than TP size, so we replicate
198 # the KV heads across multiple tensor parallel GPUs.
199 assert tp_size % self.total_num_kv_heads == 0
200 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
201 # MistralConfig has an optional head_dim introduced by Mistral-Nemo
202 self.head_dim = getattr(config, "head_dim", None)
203 if self.head_dim is None:
204 self.head_dim = self.hidden_size // self.total_num_heads
205 self.q_size = self.num_heads * self.head_dim
206 self.kv_size = self.num_kv_heads * self.head_dim
207 self.scaling = self.head_dim**-0.5
208 self.max_position_embeddings = max_position_embeddings
209
210 self.qkv_proj = QKVParallelLinear(
211 hidden_size=hidden_size,
212 head_size=self.head_dim,
213 total_num_heads=self.total_num_heads,
214 total_num_kv_heads=self.total_num_kv_heads,
215 bias=bias,
216 quant_config=quant_config,
217 prefix=f"{prefix}.qkv_proj",
218 )
219 self.o_proj = RowParallelLinear(
220 input_size=self.total_num_heads * self.head_dim,
221 output_size=hidden_size,
222 bias=bias,
223 quant_config=quant_config,
224 prefix=f"{prefix}.o_proj",
225 )
226
227 self.rotary_emb = get_rope(
228 self.head_dim,
229 max_position=max_position_embeddings,
230 rope_parameters=config.rope_parameters,
231 )
232 self.attn = Attention(
233 self.num_heads,
234 self.head_dim,
235 self.scaling,
236 num_kv_heads=self.num_kv_heads,
237 cache_config=cache_config,
238 quant_config=quant_config,
239 prefix=f"{prefix}.attn",
240 )
241
242 def forward(
243 self,
244 positions: torch.Tensor,
245 hidden_states: torch.Tensor,
246 ) -> torch.Tensor:
247 qkv, _ = self.qkv_proj(hidden_states)
248 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
249 q, k = self.rotary_emb(positions, q, k)
250 attn_output = self.attn(q, k, v)
251 output, _ = self.o_proj(attn_output)
252 return output
253
254
255 class NemotronDecoderLayer(nn.Module):
256 def __init__(
257 self,
258 config: NemotronConfig,
259 cache_config: CacheConfig | None = None,
260 quant_config: QuantizationConfig | None = None,
261 prefix: str = "",
262 ) -> None:
263 super().__init__()
264 self.hidden_size = config.hidden_size
265 max_position_embeddings = getattr(config, "max_position_embeddings", 8192)
266 # Support abacusai/Smaug-72B-v0.1 with attention_bias
267 attention_bias = getattr(config, "attention_bias", False) or getattr(
268 config, "bias", False
269 )
270 self.self_attn = NemotronAttention(
271 config=config,
272 hidden_size=self.hidden_size,
273 num_heads=config.num_attention_heads,
274 num_kv_heads=getattr(
275 config, "num_key_value_heads", config.num_attention_heads
276 ),
277 max_position_embeddings=max_position_embeddings,
278 quant_config=quant_config,
279 bias=attention_bias,
280 cache_config=cache_config,
281 prefix=f"{prefix}.self_attn",
282 )
283 self.mlp = NemotronMLP(
284 hidden_size=self.hidden_size,
285 intermediate_size=config.intermediate_size,
286 hidden_act=config.hidden_act,
287 quant_config=quant_config,
288 bias=getattr(config, "mlp_bias", False),
289 prefix=f"{prefix}.mlp",
290 )
291 self.input_layernorm = NemotronLayerNorm1P(
292 config.hidden_size, eps=config.norm_eps
293 )
294 self.post_attention_layernorm = NemotronLayerNorm1P(
295 config.hidden_size, eps=config.norm_eps
296 )
297
298 def forward(
299 self,
300 positions: torch.Tensor,
301 hidden_states: torch.Tensor,
302 residual: torch.Tensor | None,
303 ) -> tuple[torch.Tensor, torch.Tensor]:
304 # Self Attention
305 if residual is None:
306 residual = hidden_states
307 hidden_states = self.input_layernorm(hidden_states)
308 else:
309 hidden_states, residual = self.input_layernorm(hidden_states, residual)
310 hidden_states = self.self_attn(
311 positions=positions,
312 hidden_states=hidden_states,
313 )
314
315 # Fully Connected
316 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
317 hidden_states = self.mlp(hidden_states)
318 return hidden_states, residual
319
320
321 @support_torch_compile
322 class NemotronModel(nn.Module):
323 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
324 super().__init__()
325
326 config = vllm_config.model_config.hf_config
327 cache_config = vllm_config.cache_config
328 quant_config = vllm_config.quant_config
329
330 self.config = config
331 self.quant_config = quant_config
332
333 self.vocab_size = config.vocab_size
334
335 if get_pp_group().is_first_rank or (
336 config.tie_word_embeddings and get_pp_group().is_last_rank
337 ):
338 self.embed_tokens = VocabParallelEmbedding(
339 self.vocab_size,
340 config.hidden_size,
341 )
342 else:
343 self.embed_tokens = PPMissingLayer()
344 self.start_layer, self.end_layer, self.layers = make_layers(
345 config.num_hidden_layers,
346 lambda prefix: NemotronDecoderLayer(
347 config=config,
348 cache_config=cache_config,
349 quant_config=quant_config,
350 prefix=prefix,
351 ),
352 prefix=f"{prefix}.layers",
353 )
354 if get_pp_group().is_last_rank:
355 self.norm = NemotronLayerNorm1P(config.hidden_size, eps=config.norm_eps)
356 else:
357 self.norm = PPMissingLayer()
358 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
359 ["hidden_states", "residual"], config.hidden_size
360 )
361
362 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
363 return self.embed_tokens(input_ids)
364
365 def forward(
366 self,
367 input_ids: torch.Tensor | None,
368 positions: torch.Tensor,
369 intermediate_tensors: IntermediateTensors | None,
370 inputs_embeds: torch.Tensor | None = None,
371 ) -> torch.Tensor | IntermediateTensors:
372 if get_pp_group().is_first_rank:
373 if inputs_embeds is not None:
374 hidden_states = inputs_embeds
375 else:
376 hidden_states = self.embed_input_ids(input_ids)
377 residual = None
378 else:
379 assert intermediate_tensors is not None
380 hidden_states = intermediate_tensors["hidden_states"]
381 residual = intermediate_tensors["residual"]
382
383 for layer in islice(self.layers, self.start_layer, self.end_layer):
384 hidden_states, residual = layer(positions, hidden_states, residual)
385
386 if not get_pp_group().is_last_rank:
387 return IntermediateTensors(
388 {"hidden_states": hidden_states, "residual": residual}
389 )
390
391 hidden_states, _ = self.norm(hidden_states, residual)
392 return hidden_states
393
394
395 class NemotronForCausalLM(nn.Module, SupportsLoRA, SupportsPP):
396 hf_to_vllm_mapper = WeightsMapper(
397 orig_to_new_stacked={
398 # weight_name: (param_name, shard_id)
399 ".q_proj": (".qkv_proj", "q"),
400 ".k_proj": (".qkv_proj", "k"),
401 ".v_proj": (".qkv_proj", "v"),
402 }
403 )
404 packed_modules_mapping = {
405 "qkv_proj": ["q_proj", "k_proj", "v_proj"],
406 }
407
408 # LoRA specific attributes
409 embedding_modules = {
410 "embed_tokens": "input_embeddings",
411 "lm_head": "output_embeddings",
412 }
413
414 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
415 super().__init__()
416 config = vllm_config.model_config.hf_config
417 quant_config = vllm_config.quant_config
418
419 assert isinstance(config, NemotronConfig)
420
421 self.config = config
422
423 self.quant_config = quant_config
424
425 self.model = NemotronModel(
426 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
427 )
428 if get_pp_group().is_last_rank:
429 self.lm_head = ParallelLMHead(
430 config.vocab_size,
431 config.hidden_size,
432 quant_config=quant_config,
433 prefix=maybe_prefix(prefix, "lm_head"),
434 )
435 if config.tie_word_embeddings:
436 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
437
438 logit_scale = getattr(config, "logit_scale", 1.0)
439 self.logits_processor = LogitsProcessor(
440 config.vocab_size, scale=logit_scale
441 )
442 else:
443 self.lm_head = PPMissingLayer()
444
445 self.make_empty_intermediate_tensors = (
446 self.model.make_empty_intermediate_tensors
447 )
448
449 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
450 return self.model.embed_input_ids(input_ids)
451
452 def forward(
453 self,
454 input_ids: torch.Tensor | None,
455 positions: torch.Tensor,
456 intermediate_tensors: IntermediateTensors | None = None,
457 inputs_embeds: torch.Tensor | None = None,
458 ) -> torch.Tensor | IntermediateTensors:
459 model_output = self.model(
460 input_ids, positions, intermediate_tensors, inputs_embeds
461 )
462 return model_output
463
464 def compute_logits(
465 self,
466 hidden_states: torch.Tensor,
467 ) -> torch.Tensor | None:
468 logits = self.logits_processor(self.lm_head, hidden_states)
469 return logits
470
471 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
472 loader = AutoWeightsLoader(self)
473 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)