main
py 450 lines 15.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://huggingface.co/LGAI-EXAONE/EXAONE-3.0-7.8B-Instruct/blob/main/modeling_exaone.py
6 # Copyright 2024 The LG U+ CTO AI Tech Lab.
7 # Copyright 2021 The LG AI Research EXAONE Lab
8 # Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
9 #
10 # This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
11 # and OPT implementations in this library. It has been modified from its
12 # original forms to accommodate minor architectural differences compared
13 # to GPT-NeoX and OPT used by the Meta AI team that trained the model.
14 #
15 # Licensed under the Apache License, Version 2.0 (the "License");
16 # you may not use this file except in compliance with the License.
17 # You may obtain a copy of the License at
18 #
19 # http://www.apache.org/licenses/LICENSE-2.0
20 #
21 # Unless required by applicable law or agreed to in writing, software
22 # distributed under the License is distributed on an "AS IS" BASIS,
23 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 # See the License for the specific language governing permissions and
25 # limitations under the License.
26 """Inference-only Exaone model compatible with HuggingFace weights."""
27
28 from collections.abc import Iterable
29 from itertools import islice
30
31 import torch
32 from torch import nn
33 from transformers import PreTrainedConfig
34
35 from vllm.compilation.decorators import support_torch_compile
36 from vllm.config import CacheConfig, VllmConfig
37 from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
38 from vllm.model_executor.layers.activation import SiluAndMul
39 from vllm.model_executor.layers.attention import Attention
40 from vllm.model_executor.layers.layernorm import RMSNorm
41 from vllm.model_executor.layers.linear import (
42 MergedColumnParallelLinear,
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 PPMissingLayer,
59 WeightsMapper,
60 make_empty_intermediate_tensors_factory,
61 make_layers,
62 maybe_prefix,
63 )
64
65
66 class ExaoneGatedMLP(nn.Module):
67 def __init__(
68 self,
69 hidden_size: int,
70 intermediate_size: int,
71 hidden_act: str,
72 quant_config: QuantizationConfig | None = None,
73 bias: bool = False,
74 prefix: str = "",
75 ) -> None:
76 super().__init__()
77 self.gate_up_proj = MergedColumnParallelLinear(
78 input_size=hidden_size,
79 output_sizes=[intermediate_size] * 2,
80 bias=bias,
81 quant_config=quant_config,
82 prefix=f"{prefix}.gate_up_proj",
83 )
84 self.c_proj = RowParallelLinear(
85 input_size=intermediate_size,
86 output_size=hidden_size,
87 bias=bias,
88 quant_config=quant_config,
89 prefix=f"{prefix}.c_proj",
90 )
91 if hidden_act != "silu":
92 raise ValueError(
93 f"Unsupported activation: {hidden_act}. Only silu is supported for now."
94 )
95 self.act_fn = SiluAndMul()
96
97 def forward(self, x):
98 gate_up, _ = self.gate_up_proj(x)
99 x = self.act_fn(gate_up)
100 x, _ = self.c_proj(x)
101 return x
102
103
104 class ExaoneAttention(nn.Module):
105 def __init__(
106 self,
107 config: PreTrainedConfig,
108 hidden_size: int,
109 num_heads: int,
110 num_kv_heads: int,
111 max_position_embeddings: int = 8192,
112 quant_config: QuantizationConfig | None = None,
113 bias: bool = False,
114 cache_config: CacheConfig | None = None,
115 prefix: str = "",
116 ) -> None:
117 super().__init__()
118 self.hidden_size = hidden_size
119 tp_size = get_tensor_model_parallel_world_size()
120 self.total_num_heads = num_heads
121 assert self.total_num_heads % tp_size == 0
122 self.num_heads = self.total_num_heads // tp_size
123 self.total_num_kv_heads = num_kv_heads
124 if self.total_num_kv_heads >= tp_size:
125 # Number of KV heads is greater than TP size, so we partition
126 # the KV heads across multiple tensor parallel GPUs.
127 assert self.total_num_kv_heads % tp_size == 0
128 else:
129 # Number of KV heads is less than TP size, so we replicate
130 # the KV heads across multiple tensor parallel GPUs.
131 assert tp_size % self.total_num_kv_heads == 0
132 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
133 # MistralConfig has an optional head_dim introduced by Mistral-Nemo
134 self.head_dim = getattr(config, "head_dim", None)
135 if self.head_dim is None:
136 self.head_dim = self.hidden_size // self.total_num_heads
137 self.q_size = self.num_heads * self.head_dim
138 self.kv_size = self.num_kv_heads * self.head_dim
139 self.scaling = self.head_dim**-0.5
140 self.max_position_embeddings = max_position_embeddings
141
142 self.qkv_proj = QKVParallelLinear(
143 hidden_size=hidden_size,
144 head_size=self.head_dim,
145 total_num_heads=self.total_num_heads,
146 total_num_kv_heads=self.total_num_kv_heads,
147 bias=bias,
148 quant_config=quant_config,
149 prefix=f"{prefix}.qkv_proj",
150 )
151
152 self.out_proj = RowParallelLinear(
153 input_size=self.total_num_heads * self.head_dim,
154 output_size=hidden_size,
155 bias=bias,
156 quant_config=quant_config,
157 prefix=f"{prefix}.out_proj",
158 )
159
160 is_neox_style = True
161
162 self.rotary_emb = get_rope(
163 self.head_dim,
164 max_position=max_position_embeddings,
165 rope_parameters=config.rope_parameters,
166 is_neox_style=is_neox_style,
167 )
168 self.attn = Attention(
169 self.num_heads,
170 self.head_dim,
171 self.scaling,
172 num_kv_heads=self.num_kv_heads,
173 cache_config=cache_config,
174 quant_config=quant_config,
175 prefix=f"{prefix}.attn",
176 )
177
178 def forward(
179 self,
180 positions: torch.Tensor,
181 hidden_states: torch.Tensor,
182 ) -> torch.Tensor:
183 qkv, _ = self.qkv_proj(hidden_states)
184 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
185 q, k = self.rotary_emb(positions, q, k)
186 attn_output = self.attn(q, k, v)
187 output, _ = self.out_proj(attn_output)
188 return output
189
190
191 class ExaoneBlockAttention(nn.Module):
192 def __init__(
193 self,
194 config: PreTrainedConfig,
195 hidden_size: int,
196 num_heads: int,
197 num_kv_heads: int,
198 max_position_embeddings: int = 8192,
199 quant_config: QuantizationConfig | None = None,
200 bias: bool = False,
201 cache_config: CacheConfig | None = None,
202 prefix: str = "",
203 ) -> None:
204 super().__init__()
205 self.attention = ExaoneAttention(
206 config=config,
207 hidden_size=hidden_size,
208 num_heads=num_heads,
209 num_kv_heads=num_kv_heads,
210 max_position_embeddings=max_position_embeddings,
211 quant_config=quant_config,
212 bias=bias,
213 cache_config=cache_config,
214 prefix=f"{prefix}.attention",
215 )
216
217 def forward(
218 self,
219 positions: torch.Tensor,
220 hidden_states: torch.Tensor,
221 ) -> torch.Tensor:
222 return self.attention(
223 positions=positions,
224 hidden_states=hidden_states,
225 )
226
227
228 class ExaoneDecoderLayer(nn.Module):
229 def __init__(
230 self,
231 config: PreTrainedConfig,
232 cache_config: CacheConfig | None = None,
233 quant_config: QuantizationConfig | None = None,
234 prefix: str = "",
235 ) -> None:
236 super().__init__()
237 self.hidden_size = config.hidden_size
238 max_position_embeddings = getattr(config, "max_position_embeddings", 8192)
239 # Support abacusai/Smaug-72B-v0.1 with attention_bias
240 attention_bias = getattr(config, "attention_bias", False) or getattr(
241 config, "bias", False
242 )
243 self.attn = ExaoneBlockAttention(
244 config=config,
245 hidden_size=self.hidden_size,
246 num_heads=config.num_attention_heads,
247 num_kv_heads=getattr(
248 config, "num_key_value_heads", config.num_attention_heads
249 ),
250 max_position_embeddings=max_position_embeddings,
251 quant_config=quant_config,
252 bias=attention_bias,
253 cache_config=cache_config,
254 prefix=f"{prefix}.attn",
255 )
256 self.mlp = ExaoneGatedMLP(
257 hidden_size=self.hidden_size,
258 intermediate_size=config.intermediate_size,
259 hidden_act=config.activation_function,
260 quant_config=quant_config,
261 bias=getattr(config, "mlp_bias", False),
262 prefix=f"{prefix}.mlp",
263 )
264 self.ln_1 = RMSNorm(config.hidden_size, eps=config.layer_norm_epsilon)
265 self.ln_2 = RMSNorm(config.hidden_size, eps=config.layer_norm_epsilon)
266
267 def forward(
268 self,
269 positions: torch.Tensor,
270 hidden_states: torch.Tensor,
271 residual: torch.Tensor | None,
272 ) -> tuple[torch.Tensor, torch.Tensor]:
273 # Self Attention
274 if residual is None:
275 residual = hidden_states
276 hidden_states = self.ln_1(hidden_states)
277 else:
278 hidden_states, residual = self.ln_1(hidden_states, residual)
279 hidden_states = self.attn(
280 positions=positions,
281 hidden_states=hidden_states,
282 )
283
284 # Fully Connected
285 hidden_states, residual = self.ln_2(hidden_states, residual)
286 hidden_states = self.mlp(hidden_states)
287 return hidden_states, residual
288
289
290 @support_torch_compile
291 class ExaoneModel(nn.Module):
292 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
293 super().__init__()
294
295 config = vllm_config.model_config.hf_config
296 cache_config = vllm_config.cache_config
297 quant_config = vllm_config.quant_config
298
299 self.config = config
300 self.quant_config = quant_config
301
302 self.vocab_size = config.vocab_size
303 self.wte = config.vocab_size
304 if get_pp_group().is_first_rank or (
305 config.tie_word_embeddings and get_pp_group().is_last_rank
306 ):
307 self.wte = VocabParallelEmbedding(
308 self.vocab_size,
309 config.hidden_size,
310 quant_config=quant_config,
311 )
312 else:
313 self.wte = PPMissingLayer()
314 self.start_layer, self.end_layer, self.h = make_layers(
315 config.num_hidden_layers,
316 lambda prefix: ExaoneDecoderLayer(
317 config=config,
318 cache_config=cache_config,
319 quant_config=quant_config,
320 prefix=prefix,
321 ),
322 prefix=f"{prefix}.h",
323 )
324 if get_pp_group().is_last_rank:
325 self.ln_f = RMSNorm(config.hidden_size, eps=config.layer_norm_epsilon)
326 else:
327 self.ln_f = PPMissingLayer()
328
329 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
330 ["hidden_states", "residual"], config.hidden_size
331 )
332
333 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
334 return self.wte(input_ids)
335
336 def forward(
337 self,
338 input_ids: torch.Tensor | None,
339 positions: torch.Tensor,
340 intermediate_tensors: IntermediateTensors | None,
341 inputs_embeds: torch.Tensor | None = None,
342 ) -> torch.Tensor | IntermediateTensors:
343 if get_pp_group().is_first_rank:
344 if inputs_embeds is not None:
345 hidden_states = inputs_embeds
346 else:
347 hidden_states = self.embed_input_ids(input_ids)
348 residual = None
349 else:
350 assert intermediate_tensors is not None
351 hidden_states = intermediate_tensors["hidden_states"]
352 residual = intermediate_tensors["residual"]
353
354 for layer in islice(self.h, self.start_layer, self.end_layer):
355 hidden_states, residual = layer(
356 positions,
357 hidden_states,
358 residual,
359 )
360
361 if not get_pp_group().is_last_rank:
362 return IntermediateTensors(
363 {"hidden_states": hidden_states, "residual": residual}
364 )
365
366 hidden_states, _ = self.ln_f(hidden_states, residual)
367 return hidden_states
368
369
370 class ExaoneForCausalLM(nn.Module, SupportsLoRA, SupportsPP):
371 hf_to_vllm_mapper = WeightsMapper(
372 orig_to_new_stacked={
373 # weight_name: (param_name, shard_id)
374 ".q_proj": (".qkv_proj", "q"),
375 ".k_proj": (".qkv_proj", "k"),
376 ".v_proj": (".qkv_proj", "v"),
377 ".c_fc_0": (".gate_up_proj", 0),
378 ".c_fc_1": (".gate_up_proj", 1),
379 }
380 )
381 packed_modules_mapping = {
382 "qkv_proj": ["q_proj", "k_proj", "v_proj"],
383 "gate_up_proj": ["c_fc_0", "c_fc_1"],
384 }
385
386 # LoRA specific attributes
387 embedding_modules = {
388 "wte": "input_embeddings",
389 "lm_head": "output_embeddings",
390 }
391
392 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
393 super().__init__()
394 config = vllm_config.model_config.hf_config
395 quant_config = vllm_config.quant_config
396
397 self.config = config
398
399 self.quant_config = quant_config
400
401 self.transformer = ExaoneModel(
402 vllm_config=vllm_config,
403 prefix=maybe_prefix(prefix, "model"),
404 )
405 if get_pp_group().is_last_rank:
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 config.tie_word_embeddings:
413 self.lm_head = self.lm_head.tie_weights(self.transformer.wte)
414
415 logit_scale = getattr(config, "logit_scale", 1.0)
416 self.logits_processor = LogitsProcessor(
417 config.vocab_size, scale=logit_scale
418 )
419 else:
420 self.lm_head = PPMissingLayer()
421
422 self.make_empty_intermediate_tensors = (
423 self.transformer.make_empty_intermediate_tensors
424 )
425
426 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
427 return self.model.embed_input_ids(input_ids)
428
429 def forward(
430 self,
431 input_ids: torch.Tensor | None,
432 positions: torch.Tensor,
433 intermediate_tensors: IntermediateTensors | None = None,
434 inputs_embeds: torch.Tensor | None = None,
435 ) -> torch.Tensor | IntermediateTensors:
436 model_output = self.transformer(
437 input_ids, positions, intermediate_tensors, inputs_embeds
438 )
439 return model_output
440
441 def compute_logits(
442 self,
443 hidden_states: torch.Tensor,
444 ) -> torch.Tensor | None:
445 logits = self.logits_processor(self.lm_head, hidden_states)
446 return logits
447
448 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
449 loader = AutoWeightsLoader(self)
450 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)