main
py 405 lines 13.7 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 """Inference-only OLMoE model compatible with HuggingFace weights."""
16
17 from collections.abc import Iterable
18 from functools import partial
19 from itertools import islice
20
21 import torch
22 from torch import nn
23
24 from vllm.compilation.decorators import support_torch_compile
25 from vllm.config import VllmConfig
26 from vllm.distributed import (
27 get_pp_group,
28 get_tensor_model_parallel_rank,
29 get_tensor_model_parallel_world_size,
30 tensor_model_parallel_all_gather,
31 )
32 from vllm.distributed.utils import split_tensor_along_last_dim
33 from vllm.logger import init_logger
34 from vllm.model_executor.layers.attention import Attention
35 from vllm.model_executor.layers.fused_moe import (
36 FusedMoEFactory,
37 GateLinear,
38 )
39 from vllm.model_executor.layers.layernorm import RMSNorm
40 from vllm.model_executor.layers.linear import (
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
53 from .interfaces import SupportsLoRA, SupportsPP
54 from .utils import (
55 AutoWeightsLoader,
56 WeightsMapper,
57 make_empty_intermediate_tensors_factory,
58 make_layers,
59 maybe_prefix,
60 )
61
62 logger = init_logger(__name__)
63
64
65 class OlmoeMoE(nn.Module):
66 """A tensor-parallel MoE implementation for Olmoe that shards each expert
67 across all ranks.
68
69 Each expert's weights are sharded across all ranks and a fused MoE
70 kernel is used for the forward pass, and finally we reduce the outputs
71 across ranks.
72 """
73
74 def __init__(
75 self,
76 num_experts: int,
77 top_k: int,
78 hidden_size: int,
79 intermediate_size: int,
80 params_dtype: torch.dtype | None = None,
81 quant_config: QuantizationConfig | None = None,
82 tp_size: int | None = None,
83 prefix: str = "",
84 ):
85 super().__init__()
86 self.hidden_size = hidden_size
87
88 # Gate always runs at half / full precision for now.
89 self.gate = GateLinear(
90 hidden_size,
91 num_experts,
92 prefix=f"{prefix}.gate",
93 )
94
95 self.experts = FusedMoEFactory(
96 num_experts=num_experts,
97 top_k=top_k,
98 hidden_size=hidden_size,
99 intermediate_size=intermediate_size,
100 renormalize=False,
101 quant_config=quant_config,
102 tp_size=tp_size,
103 prefix=f"{prefix}.experts",
104 )
105
106 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
107 # NOTE: hidden_states can have either 1D or 2D shape.
108 orig_shape = hidden_states.shape
109 hidden_dim = hidden_states.shape[-1]
110 hidden_states = hidden_states.view(-1, hidden_dim)
111 # router_logits: (num_tokens, n_experts)
112 router_logits, _ = self.gate(hidden_states)
113 final_hidden_states = self.experts(
114 hidden_states=hidden_states, router_logits=router_logits
115 )
116 return final_hidden_states.view(orig_shape)
117
118
119 class OlmoeAttention(nn.Module):
120 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
121 super().__init__()
122
123 config = vllm_config.model_config.hf_config
124 cache_config = vllm_config.cache_config
125 quant_config = vllm_config.quant_config
126
127 self.hidden_size = config.hidden_size
128 max_position_embeddings = getattr(config, "max_position_embeddings", 4096)
129
130 num_heads = config.num_attention_heads
131 num_kv_heads = config.num_key_value_heads
132
133 tp_size = get_tensor_model_parallel_world_size()
134 self.total_num_heads = num_heads
135 assert self.total_num_heads % tp_size == 0
136 self.num_heads = self.total_num_heads // tp_size
137 self.total_num_kv_heads = num_kv_heads
138 if self.total_num_kv_heads >= tp_size:
139 # Number of KV heads is greater than TP size, so we partition
140 # the KV heads across multiple tensor parallel GPUs.
141 assert self.total_num_kv_heads % tp_size == 0
142 else:
143 # Number of KV heads is less than TP size, so we replicate
144 # the KV heads across multiple tensor parallel GPUs.
145 assert tp_size % self.total_num_kv_heads == 0
146 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
147 self.head_dim = self.hidden_size // self.total_num_heads
148 self.q_size = self.num_heads * self.head_dim
149 self.kv_size = self.num_kv_heads * self.head_dim
150 self.scaling = self.head_dim**-0.5
151 self.max_position_embeddings = max_position_embeddings
152
153 self.qkv_proj = QKVParallelLinear(
154 self.hidden_size,
155 self.head_dim,
156 self.total_num_heads,
157 self.total_num_kv_heads,
158 bias=False,
159 quant_config=quant_config,
160 prefix=f"{prefix}.qkv_proj",
161 )
162 self.tp_size = tp_size
163 self.tp_rank = get_tensor_model_parallel_rank()
164 self.q_norm = RMSNorm(self.total_num_heads * self.head_dim, eps=1e-5)
165 self.k_norm = RMSNorm(self.total_num_kv_heads * self.head_dim, eps=1e-5)
166 self.o_proj = RowParallelLinear(
167 self.total_num_heads * self.head_dim,
168 self.hidden_size,
169 bias=False,
170 quant_config=quant_config,
171 prefix=f"{prefix}.o_proj",
172 )
173
174 self.rotary_emb = get_rope(
175 self.head_dim,
176 max_position=max_position_embeddings,
177 rope_parameters=config.rope_parameters,
178 is_neox_style=True,
179 )
180 self.attn = Attention(
181 self.num_heads,
182 self.head_dim,
183 self.scaling,
184 num_kv_heads=self.num_kv_heads,
185 cache_config=cache_config,
186 quant_config=quant_config,
187 prefix=f"{prefix}.attn",
188 )
189
190 def _apply_qk_norm(
191 self, q: torch.Tensor, k: torch.Tensor
192 ) -> tuple[torch.Tensor, torch.Tensor]:
193 if self.tp_size > 1:
194 q = tensor_model_parallel_all_gather(q.contiguous())
195 k = tensor_model_parallel_all_gather(k.contiguous())
196 q = self.q_norm(q)
197 k = self.k_norm(k)
198 if self.tp_size > 1:
199 splitter = partial(split_tensor_along_last_dim, num_partitions=self.tp_size)
200 q = splitter(q)[self.tp_rank]
201 k = splitter(k)[self.tp_rank]
202 return q, k
203
204 def forward(
205 self,
206 positions: torch.Tensor,
207 hidden_states: torch.Tensor,
208 ) -> torch.Tensor:
209 qkv, _ = self.qkv_proj(hidden_states)
210 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
211 q, k = self._apply_qk_norm(q, k)
212 q, k = self.rotary_emb(positions, q, k)
213 attn_output = self.attn(q, k, v)
214 output, _ = self.o_proj(attn_output)
215 return output
216
217
218 class OlmoeDecoderLayer(nn.Module):
219 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
220 super().__init__()
221 config = vllm_config.model_config.hf_config
222 quant_config = vllm_config.quant_config
223
224 self.hidden_size = config.hidden_size
225
226 self.self_attn = OlmoeAttention(
227 vllm_config=vllm_config,
228 prefix=f"{prefix}.self_attn",
229 )
230
231 self.mlp = OlmoeMoE(
232 num_experts=config.num_experts,
233 top_k=config.num_experts_per_tok,
234 hidden_size=config.hidden_size,
235 intermediate_size=config.intermediate_size,
236 quant_config=quant_config,
237 prefix=f"{prefix}.mlp",
238 )
239 self.input_layernorm = RMSNorm(config.hidden_size, eps=1e-5)
240 self.post_attention_layernorm = RMSNorm(config.hidden_size, eps=1e-5)
241
242 def forward(
243 self,
244 positions: torch.Tensor,
245 hidden_states: torch.Tensor,
246 residual: torch.Tensor | None,
247 ) -> torch.Tensor:
248 # Self Attention
249 if residual is None:
250 residual = hidden_states
251 hidden_states = self.input_layernorm(hidden_states)
252 else:
253 hidden_states, residual = self.input_layernorm(hidden_states, residual)
254
255 hidden_states = self.self_attn(
256 positions=positions,
257 hidden_states=hidden_states,
258 )
259
260 # Fully Connected
261 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
262 hidden_states = self.mlp(hidden_states)
263 return hidden_states, residual
264
265
266 @support_torch_compile
267 class OlmoeModel(nn.Module):
268 def __init__(
269 self,
270 *,
271 vllm_config: VllmConfig,
272 prefix: str = "",
273 layer_type: type[nn.Module] = OlmoeDecoderLayer,
274 ):
275 super().__init__()
276
277 config = vllm_config.model_config.hf_config
278 quant_config = vllm_config.quant_config
279
280 self.vocab_size = config.vocab_size
281 self.config = config
282 self.embed_tokens = VocabParallelEmbedding(
283 config.vocab_size,
284 config.hidden_size,
285 quant_config=quant_config,
286 )
287 self.start_layer, self.end_layer, self.layers = make_layers(
288 config.num_hidden_layers,
289 lambda prefix: layer_type(vllm_config=vllm_config, prefix=prefix),
290 prefix=f"{prefix}.layers",
291 )
292 self.norm = RMSNorm(config.hidden_size, eps=1e-5)
293
294 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
295 ["hidden_states", "residual"], config.hidden_size
296 )
297
298 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
299 return self.embed_tokens(input_ids)
300
301 def forward(
302 self,
303 input_ids: torch.Tensor | None,
304 positions: torch.Tensor,
305 intermediate_tensors: IntermediateTensors | None,
306 inputs_embeds: torch.Tensor | None = None,
307 ) -> torch.Tensor | IntermediateTensors:
308 if get_pp_group().is_first_rank:
309 if inputs_embeds is not None:
310 hidden_states = inputs_embeds
311 else:
312 hidden_states = self.embed_input_ids(input_ids)
313 residual = None
314 else:
315 assert intermediate_tensors is not None
316 hidden_states = intermediate_tensors["hidden_states"]
317 residual = intermediate_tensors["residual"]
318
319 for layer in islice(self.layers, self.start_layer, self.end_layer):
320 hidden_states, residual = layer(
321 positions,
322 hidden_states,
323 residual,
324 )
325
326 if not get_pp_group().is_last_rank:
327 return IntermediateTensors(
328 {"hidden_states": hidden_states, "residual": residual}
329 )
330
331 if residual is not None:
332 hidden_states, _ = self.norm(hidden_states, residual)
333 else:
334 hidden_states = self.norm(hidden_states)
335 return hidden_states
336
337
338 class OlmoeForCausalLM(nn.Module, SupportsPP, SupportsLoRA):
339 hf_to_vllm_mapper = WeightsMapper(
340 orig_to_new_stacked={
341 # weight_name: (param_name, shard_id)
342 ".q_proj": (".qkv_proj", "q"),
343 ".k_proj": (".qkv_proj", "k"),
344 ".v_proj": (".qkv_proj", "v"),
345 }
346 )
347 packed_modules_mapping = {
348 "qkv_proj": [
349 "q_proj",
350 "k_proj",
351 "v_proj",
352 ]
353 }
354
355 def __init__(
356 self,
357 *,
358 vllm_config: VllmConfig,
359 prefix: str = "",
360 layer_type: type[nn.Module] = OlmoeDecoderLayer,
361 ):
362 super().__init__()
363 config = vllm_config.model_config.hf_config
364 quant_config = vllm_config.quant_config
365 self.config = config
366 self.quant_config = quant_config
367 self.model = OlmoeModel(
368 vllm_config=vllm_config,
369 prefix=maybe_prefix(prefix, "model"),
370 layer_type=layer_type,
371 )
372 self.lm_head = ParallelLMHead(
373 config.vocab_size,
374 config.hidden_size,
375 quant_config=quant_config,
376 prefix=maybe_prefix(prefix, "lm_head"),
377 )
378 self.logits_processor = LogitsProcessor(config.vocab_size)
379
380 self.make_empty_intermediate_tensors = (
381 self.model.make_empty_intermediate_tensors
382 )
383
384 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
385 return self.model.embed_input_ids(input_ids)
386
387 def forward(
388 self,
389 input_ids: torch.Tensor | None,
390 positions: torch.Tensor,
391 intermediate_tensors: IntermediateTensors | None = None,
392 inputs_embeds: torch.Tensor | None = None,
393 ) -> torch.Tensor | IntermediateTensors:
394 hidden_states = self.model(
395 input_ids, positions, intermediate_tensors, inputs_embeds
396 )
397 return hidden_states
398
399 def compute_logits(self, hidden_states: torch.Tensor) -> torch.Tensor:
400 logits = self.logits_processor(self.lm_head, hidden_states)
401 return logits
402
403 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
404 loader = AutoWeightsLoader(self)
405 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)