main
py 218 lines 7.93 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 # Copyright 2025 The Baidu team.
5 # Copyright 2023 The vLLM team.
6 # Copyright 2022 EleutherAI and the HuggingFace Inc. team. All rights reserved.
7 #
8 # This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
9 # and OPT implementations in this library. It has been modified from its
10 # original forms to accommodate minor architectural differences compared
11 # to GPT-NeoX and OPT used by the Meta AI team that trained the model.
12 #
13 # Licensed under the Apache License, Version 2.0 (the "License");
14 # you may not use this file except in compliance with the License.
15 # You may obtain a copy of the License at
16 #
17 # http://www.apache.org/licenses/LICENSE-2.0
18 #
19 # Unless required by applicable law or agreed to in writing, software
20 # distributed under the License is distributed on an "AS IS" BASIS,
21 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 # See the License for the specific language governing permissions and
23 # limitations under the License.
24 """Inference-only Ernie-MTP model."""
25
26 from collections.abc import Iterable
27
28 import torch
29 import torch.nn as nn
30
31 from vllm.config import VllmConfig
32 from vllm.model_executor.layers.layernorm import RMSNorm
33 from vllm.model_executor.layers.logits_processor import LogitsProcessor
34 from vllm.model_executor.layers.vocab_parallel_embedding import (
35 ParallelLMHead,
36 VocabParallelEmbedding,
37 )
38 from vllm.sequence import IntermediateTensors
39
40 from .llama import LlamaDecoderLayer
41 from .utils import AutoWeightsLoader, WeightsMapper, maybe_prefix
42
43
44 class ErnieMultiTokenPredictorLayer(nn.Module):
45 def __init__(
46 self,
47 vllm_config: VllmConfig,
48 prefix: str,
49 ) -> None:
50 super().__init__()
51 config = vllm_config.model_config.hf_config
52
53 self.mtp_emb_norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
54 self.mtp_hidden_norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
55 self.mtp_linear_proj = nn.Linear(
56 config.hidden_size * 2, config.hidden_size, bias=False
57 )
58 self.mtp_block = LlamaDecoderLayer(vllm_config, prefix)
59
60 def forward(
61 self,
62 inputs_embeds: torch.Tensor,
63 positions: torch.Tensor,
64 previous_hidden_states: torch.Tensor,
65 spec_step_index: int = 0,
66 ) -> torch.Tensor:
67 assert inputs_embeds is not None
68 # masking inputs at position 0, as not needed by MTP
69 inputs_embeds[positions == 0] = 0
70
71 inputs_embeds = self.mtp_emb_norm(inputs_embeds)
72 previous_hidden_states = self.mtp_hidden_norm(previous_hidden_states)
73
74 hidden_states = self.mtp_linear_proj(
75 torch.cat([inputs_embeds, previous_hidden_states], dim=-1)
76 )
77
78 hidden_states, residual = self.mtp_block(
79 positions=positions, hidden_states=hidden_states, residual=None
80 )
81 hidden_states = residual + hidden_states
82
83 return hidden_states
84
85
86 class ErnieMultiTokenPredictor(nn.Module):
87 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
88 super().__init__()
89
90 config = vllm_config.model_config.hf_config
91 self.mtp_start_layer_idx = config.num_hidden_layers
92 self.num_mtp_layers = config.num_nextn_predict_layers
93 # to map the exact layer index from weights
94 self.layers = torch.nn.ModuleDict(
95 {
96 str(idx): ErnieMultiTokenPredictorLayer(
97 vllm_config,
98 f"{prefix}.layers.{idx}",
99 )
100 for idx in range(
101 self.mtp_start_layer_idx,
102 self.mtp_start_layer_idx + self.num_mtp_layers,
103 )
104 }
105 )
106 self.embed_tokens = VocabParallelEmbedding(
107 config.vocab_size,
108 config.hidden_size,
109 )
110 self.logits_processor = LogitsProcessor(config.vocab_size)
111
112 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
113 return self.embed_tokens(input_ids)
114
115 def forward(
116 self,
117 input_ids: torch.Tensor,
118 positions: torch.Tensor,
119 previous_hidden_states: torch.Tensor,
120 inputs_embeds: torch.Tensor | None = None,
121 spec_step_idx: int = 0,
122 ) -> torch.Tensor:
123 if inputs_embeds is None:
124 inputs_embeds = self.embed_tokens(input_ids)
125 return self.layers[str(self.mtp_start_layer_idx + spec_step_idx)](
126 inputs_embeds,
127 positions,
128 previous_hidden_states,
129 spec_step_idx,
130 )
131
132 def compute_logits(
133 self,
134 hidden_states: torch.Tensor,
135 lm_head: ParallelLMHead,
136 spec_step_idx: int = 0,
137 ) -> torch.Tensor:
138 self.layers[str(self.mtp_start_layer_idx + spec_step_idx)]
139 logits = self.logits_processor(lm_head, hidden_states)
140 return logits
141
142
143 class ErnieMTP(nn.Module):
144 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
145 super().__init__()
146
147 self.config = vllm_config.model_config.hf_config
148 # MTP weights are stored under a flat `mtp_*.0.` block in the
149 # checkpoint; rewrite them into `model.layers.{spec_layer}.*`.
150 spec_layer = self.config.num_hidden_layers
151 self.hf_to_vllm_mapper = WeightsMapper(
152 orig_to_new_substr={
153 "model.mtp_emb_norm.0.": f"model.layers.{spec_layer}.mtp_emb_norm.",
154 "model.mtp_hidden_norm.0.": (
155 f"model.layers.{spec_layer}.mtp_hidden_norm."
156 ),
157 "model.mtp_linear_proj.0.": (
158 f"model.layers.{spec_layer}.mtp_linear_proj."
159 ),
160 "model.mtp_block.0.": f"model.layers.{spec_layer}.mtp_block.",
161 },
162 orig_to_new_stacked={
163 ".q_proj": (".qkv_proj", "q"),
164 ".k_proj": (".qkv_proj", "k"),
165 ".v_proj": (".qkv_proj", "v"),
166 ".gate_proj": (".gate_up_proj", 0),
167 ".up_proj": (".gate_up_proj", 1),
168 },
169 )
170 self.model = ErnieMultiTokenPredictor(
171 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
172 )
173 self.lm_head = ParallelLMHead(
174 self.config.vocab_size,
175 self.config.hidden_size,
176 prefix=maybe_prefix(prefix, "lm_head"),
177 )
178
179 if self.config.tie_word_embeddings:
180 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
181
182 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
183 return self.model.embed_input_ids(input_ids)
184
185 def forward(
186 self,
187 input_ids: torch.Tensor | None,
188 positions: torch.Tensor,
189 hidden_states: torch.Tensor,
190 intermediate_tensors: IntermediateTensors | None = None,
191 inputs_embeds: torch.Tensor | None = None,
192 spec_step_idx: int = 0,
193 ) -> torch.Tensor:
194 assert spec_step_idx == 0, "ernie_mtp only support predict one token"
195 hidden_states = self.model(
196 input_ids, positions, hidden_states, inputs_embeds, spec_step_idx
197 )
198 return hidden_states
199
200 def compute_logits(
201 self,
202 hidden_states: torch.Tensor,
203 spec_step_idx: int = 0,
204 ) -> torch.Tensor | None:
205 return self.model.compute_logits(hidden_states, self.lm_head, spec_step_idx)
206
207 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
208 # Checkpoint bundles the full base model; only MTP, embed_tokens and
209 # lm_head weights belong to this module.
210 def _filter(
211 weights: Iterable[tuple[str, torch.Tensor]],
212 ) -> Iterable[tuple[str, torch.Tensor]]:
213 for name, weight in weights:
214 if any(k in name for k in ("mtp", "embed_tokens", "lm_head")):
215 yield name, weight
216
217 loader = AutoWeightsLoader(self)
218 return loader.load_weights(_filter(weights), mapper=self.hf_to_vllm_mapper)