main
py 201 lines 6.87 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Inference-only EXAONE-4_5 MTP model."""
4
5 from collections.abc import Iterable
6
7 import torch
8 from torch import nn
9
10 from vllm.compilation.decorators import support_torch_compile
11 from vllm.config import VllmConfig
12 from vllm.distributed.parallel_state import get_pp_group
13 from vllm.logger import init_logger
14 from vllm.model_executor.layers.layernorm import RMSNorm
15 from vllm.model_executor.layers.linear import ColumnParallelLinear
16 from vllm.model_executor.layers.logits_processor import LogitsProcessor
17 from vllm.model_executor.layers.vocab_parallel_embedding import (
18 ParallelLMHead,
19 VocabParallelEmbedding,
20 )
21 from vllm.model_executor.models.exaone4 import Exaone4DecoderLayer
22 from vllm.model_executor.models.exaone_moe_mtp import (
23 ExaoneMoeMTP,
24 ExaoneMoeMultiTokenPredictor,
25 )
26 from vllm.sequence import IntermediateTensors
27
28 from .interfaces import (
29 MultiModalEmbeddings,
30 SupportsMultiModal,
31 _require_is_multimodal,
32 )
33 from .utils import (
34 AutoWeightsLoader,
35 _merge_multimodal_embeddings,
36 maybe_prefix,
37 )
38
39 logger = init_logger(__name__)
40
41 KVCache = tuple[torch.Tensor, torch.Tensor]
42
43
44 @support_torch_compile
45 class Exaone4_5MultiTokenPredictor(ExaoneMoeMultiTokenPredictor):
46 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
47 nn.Module.__init__(self)
48
49 model_config = vllm_config.model_config
50 quant_config = vllm_config.quant_config
51 config = model_config.hf_config
52 text_config = config.text_config
53
54 self.config = config
55 self.vocab_size = config.vocab_size
56 self.org_vocab_size = config.vocab_size
57
58 self.mtp_start_layer_idx = text_config.num_hidden_layers
59 self.num_mtp_layers = getattr(config, "num_nextn_predict_layers", 1)
60
61 self.embed_tokens = VocabParallelEmbedding(
62 self.vocab_size,
63 text_config.hidden_size,
64 )
65
66 self.fc = ColumnParallelLinear(
67 text_config.hidden_size * 2,
68 text_config.hidden_size,
69 gather_output=True,
70 bias=False,
71 return_bias=False,
72 quant_config=quant_config,
73 prefix=f"{prefix}.fc",
74 )
75 self.layers = nn.ModuleList(
76 Exaone4DecoderLayer(
77 text_config,
78 quant_config=quant_config,
79 prefix=f"{prefix}.layers.{idx}",
80 )
81 for idx in range(self.num_mtp_layers)
82 )
83
84 self.norm = RMSNorm(text_config.hidden_size, eps=text_config.rms_norm_eps)
85 self.pre_fc_norm_hidden = RMSNorm(
86 text_config.hidden_size, eps=text_config.rms_norm_eps
87 )
88 self.pre_fc_norm_embedding = RMSNorm(
89 text_config.hidden_size, eps=text_config.rms_norm_eps
90 )
91
92 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
93 return self.embed_tokens(input_ids)
94
95 def forward(
96 self,
97 input_ids: torch.Tensor,
98 positions: torch.Tensor,
99 hidden_states: torch.Tensor,
100 intermediate_tensors: IntermediateTensors | None = None,
101 inputs_embeds: torch.Tensor | None = None,
102 spec_step_idx: int = 0,
103 ) -> torch.Tensor:
104 if get_pp_group().is_first_rank:
105 if inputs_embeds is None:
106 inputs_embeds = self.get_input_embeddings(input_ids)
107 assert hidden_states.shape[-1] == inputs_embeds.shape[-1]
108 inputs_embeds = self.pre_fc_norm_embedding(inputs_embeds)
109 hidden_states = self.pre_fc_norm_hidden(hidden_states)
110 hidden_states = torch.cat([inputs_embeds, hidden_states], dim=-1)
111 hidden_states = self.fc(hidden_states)
112 residual = None
113 else:
114 assert intermediate_tensors is not None
115 hidden_states = intermediate_tensors["hidden_states"]
116 residual = intermediate_tensors["residual"]
117
118 current_step_idx = spec_step_idx % self.num_mtp_layers
119 hidden_states, residual = self.layers[current_step_idx](
120 positions=positions,
121 hidden_states=hidden_states,
122 residual=residual,
123 )
124
125 if not get_pp_group().is_last_rank:
126 return IntermediateTensors(
127 {"hidden_states": hidden_states, "residual": residual}
128 )
129
130 hidden_states = self.norm(hidden_states)
131 return hidden_states
132
133
134 @support_torch_compile
135 class Exaone4_5_MTP(ExaoneMoeMTP, SupportsMultiModal):
136 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
137 config = vllm_config.model_config.hf_config
138 text_config = config.text_config
139 self.vllm_config = vllm_config
140 self.quant_config = vllm_config.quant_config
141
142 nn.Module.__init__(self)
143 self.config = config
144 self.model = Exaone4_5MultiTokenPredictor(
145 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "mtp")
146 )
147 self.unpadded_vocab_size = config.vocab_size
148 self.lm_head = ParallelLMHead(
149 self.unpadded_vocab_size,
150 text_config.hidden_size,
151 org_num_embeddings=config.vocab_size,
152 prefix=maybe_prefix(prefix, "lm_head"),
153 )
154 if config.tie_word_embeddings:
155 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
156 self.logits_processor = LogitsProcessor(
157 self.unpadded_vocab_size, config.vocab_size
158 )
159
160 def embed_input_ids(
161 self,
162 input_ids: torch.Tensor,
163 multimodal_embeddings: MultiModalEmbeddings | None = None,
164 *,
165 is_multimodal: torch.Tensor | None = None,
166 ) -> torch.Tensor:
167 inputs_embeds = self._embed_text_input_ids(
168 input_ids,
169 self.model.embed_input_ids,
170 is_multimodal=is_multimodal,
171 )
172
173 if multimodal_embeddings is None or len(multimodal_embeddings) == 0:
174 return inputs_embeds
175
176 is_multimodal = _require_is_multimodal(is_multimodal)
177
178 inputs_embeds = _merge_multimodal_embeddings(
179 inputs_embeds=inputs_embeds,
180 multimodal_embeddings=multimodal_embeddings,
181 is_multimodal=is_multimodal,
182 )
183
184 return inputs_embeds
185
186 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
187 shared_weight_names = ["embed_tokens", "lm_head"]
188
189 def remap_weight_names(weights):
190 for name, weight in weights:
191 if name.startswith("mtp."):
192 name = name.replace("mtp.", "model.")
193 elif any(key in name for key in shared_weight_names):
194 if "embed_tokens" in name:
195 name = name.replace("language_model.", "")
196 else:
197 continue
198 yield name, weight
199
200 loader = AutoWeightsLoader(self)
201 return loader.load_weights(remap_weight_names(weights))