main
py 388 lines 13.5 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/gpt2/modeling_gpt2.py
6 # Copyright 2023 The vLLM team.
7 # Copyright 2018 The OpenAI Team Authors and HuggingFace Inc. team.
8 # Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
9 #
10 # Licensed under the Apache License, Version 2.0 (the "License");
11 # you may not use this file except in compliance with the License.
12 # You may obtain a copy of the License at
13 #
14 # http://www.apache.org/licenses/LICENSE-2.0
15 #
16 # Unless required by applicable law or agreed to in writing, software
17 # distributed under the License is distributed on an "AS IS" BASIS,
18 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 # See the License for the specific language governing permissions and
20 # limitations under the License.
21 """Inference-only GPT-2 model compatible with HuggingFace weights."""
22
23 from collections.abc import Iterable
24 from itertools import islice
25
26 import torch
27 from torch import nn
28 from transformers import GPT2Config
29
30 from vllm.compilation.decorators import support_torch_compile
31 from vllm.config import CacheConfig, VllmConfig
32 from vllm.distributed.parallel_state import (
33 get_pp_group,
34 get_tensor_model_parallel_world_size,
35 )
36 from vllm.model_executor.layers.activation import get_act_fn
37 from vllm.model_executor.layers.attention import Attention
38 from vllm.model_executor.layers.layernorm import LayerNorm
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.pooler import DispatchPooler
46 from vllm.model_executor.layers.quantization import QuantizationConfig
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 SupportsCrossEncoding, SupportsPP
54 from .utils import (
55 AutoWeightsLoader,
56 WeightsMapper,
57 make_empty_intermediate_tensors_factory,
58 make_layers,
59 maybe_prefix,
60 )
61
62
63 class GPT2Attention(nn.Module):
64 def __init__(
65 self,
66 config: GPT2Config,
67 cache_config: CacheConfig | None = None,
68 quant_config: QuantizationConfig | None = None,
69 prefix: str = "",
70 ):
71 super().__init__()
72 self.hidden_size = config.hidden_size
73 total_num_heads = config.num_attention_heads
74 tensor_model_parallel_world_size = get_tensor_model_parallel_world_size()
75 assert total_num_heads % tensor_model_parallel_world_size == 0
76 self.num_heads = total_num_heads // tensor_model_parallel_world_size
77 self.head_dim = self.hidden_size // total_num_heads
78 self.scale = self.head_dim**-0.5
79
80 self.c_attn = QKVParallelLinear(
81 self.hidden_size,
82 self.head_dim,
83 total_num_heads,
84 bias=True,
85 quant_config=quant_config,
86 prefix=f"{prefix}.c_attn",
87 )
88 self.c_proj = RowParallelLinear(
89 self.hidden_size,
90 self.hidden_size,
91 bias=True,
92 quant_config=quant_config,
93 prefix=f"{prefix}.c_proj",
94 )
95 self.attn = Attention(
96 self.num_heads,
97 self.head_dim,
98 scale=self.scale,
99 cache_config=cache_config,
100 quant_config=quant_config,
101 prefix=f"{prefix}.attn",
102 )
103
104 def forward(
105 self,
106 hidden_states: torch.Tensor,
107 ) -> torch.Tensor:
108 qkv, _ = self.c_attn(hidden_states)
109 q, k, v = qkv.chunk(chunks=3, dim=-1)
110 attn_output = self.attn(q, k, v)
111 attn_output, _ = self.c_proj(attn_output)
112 return attn_output
113
114
115 class GPT2MLP(nn.Module):
116 def __init__(
117 self,
118 intermediate_size: int,
119 config: GPT2Config,
120 quant_config: QuantizationConfig | None = None,
121 prefix: str = "",
122 ):
123 super().__init__()
124 hidden_size = config.hidden_size
125 self.c_fc = ColumnParallelLinear(
126 hidden_size,
127 intermediate_size,
128 bias=True,
129 quant_config=quant_config,
130 prefix=f"{prefix}.c_fc",
131 )
132 self.c_proj = RowParallelLinear(
133 intermediate_size,
134 hidden_size,
135 bias=True,
136 quant_config=quant_config,
137 prefix=f"{prefix}.c_proj",
138 )
139 self.act = get_act_fn(config.activation_function)
140
141 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
142 hidden_states, _ = self.c_fc(hidden_states)
143 hidden_states = self.act(hidden_states)
144 hidden_states, _ = self.c_proj(hidden_states)
145 return hidden_states
146
147
148 class GPT2Block(nn.Module):
149 def __init__(
150 self,
151 config: GPT2Config,
152 cache_config: CacheConfig | None = None,
153 quant_config: QuantizationConfig | None = None,
154 prefix: str = "",
155 ):
156 super().__init__()
157 hidden_size = config.hidden_size
158 inner_dim = config.n_inner if config.n_inner is not None else 4 * hidden_size
159
160 self.ln_1 = LayerNorm(hidden_size, eps=config.layer_norm_epsilon)
161 self.attn = GPT2Attention(
162 config, cache_config, quant_config, prefix=f"{prefix}.attn"
163 )
164 self.ln_2 = LayerNorm(hidden_size, eps=config.layer_norm_epsilon)
165 self.mlp = GPT2MLP(inner_dim, config, quant_config, prefix=f"{prefix}.mlp")
166
167 def forward(
168 self,
169 hidden_states: torch.Tensor,
170 ) -> torch.Tensor:
171 residual = hidden_states
172 hidden_states = self.ln_1(hidden_states)
173 attn_output = self.attn(hidden_states=hidden_states)
174 # residual connection
175 hidden_states = attn_output + residual
176
177 residual = hidden_states
178 hidden_states = self.ln_2(hidden_states)
179 feed_forward_hidden_states = self.mlp(hidden_states)
180 # residual connection
181 hidden_states = residual + feed_forward_hidden_states
182 return hidden_states
183
184
185 @support_torch_compile
186 class GPT2Model(nn.Module):
187 # Drop attention mask buffers; NOTE: "c_attn.bias" must not be dropped.
188 hf_to_vllm_mapper = WeightsMapper(
189 orig_to_new_substr={".attn.bias": None, ".attn.masked_bias": None}
190 )
191
192 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
193 super().__init__()
194
195 config = vllm_config.model_config.hf_config
196 cache_config = vllm_config.cache_config
197 quant_config = vllm_config.quant_config
198
199 self.config = config
200 assert not config.add_cross_attention
201 assert not config.scale_attn_by_inverse_layer_idx
202 assert not config.reorder_and_upcast_attn
203 self.embed_dim = config.hidden_size
204 self.wte = VocabParallelEmbedding(
205 config.vocab_size,
206 self.embed_dim,
207 quant_config=quant_config,
208 prefix=f"{prefix}.wte",
209 )
210 self.wpe = nn.Embedding(config.max_position_embeddings, self.embed_dim)
211 self.start_layer, self.end_layer, self.h = make_layers(
212 config.num_hidden_layers,
213 lambda prefix: GPT2Block(config, cache_config, quant_config, prefix=prefix),
214 prefix=f"{prefix}.h",
215 )
216 self.ln_f = LayerNorm(self.embed_dim, eps=config.layer_norm_epsilon)
217 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
218 ["hidden_states"], config.n_embd
219 )
220
221 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
222 return self.wte(input_ids)
223
224 def forward(
225 self,
226 input_ids: torch.Tensor | None,
227 position_ids: torch.Tensor,
228 intermediate_tensors: IntermediateTensors | None,
229 inputs_embeds: torch.Tensor | None,
230 ) -> torch.Tensor | IntermediateTensors:
231 if get_pp_group().is_first_rank:
232 if inputs_embeds is None:
233 inputs_embeds = self.embed_input_ids(input_ids)
234 position_embeds = self.wpe(position_ids)
235 hidden_states = inputs_embeds + position_embeds
236 else:
237 assert intermediate_tensors is not None
238 hidden_states = intermediate_tensors["hidden_states"]
239
240 for layer in islice(self.h, self.start_layer, self.end_layer):
241 hidden_states = layer(hidden_states)
242
243 if not get_pp_group().is_last_rank:
244 return IntermediateTensors({"hidden_states": hidden_states})
245
246 hidden_states = self.ln_f(hidden_states)
247 return hidden_states
248
249 def _transpose_conv1d(
250 self, weights: Iterable[tuple[str, torch.Tensor]]
251 ) -> Iterable[tuple[str, torch.Tensor]]:
252 # HF's GPT-2 uses Conv1D instead of Linear, so its 2D weights are
253 # stored transposed relative to what vLLM expects.
254 # Note(zhuohan): the logic below might break quantized models.
255 for name, loaded_weight in weights:
256 if name.endswith(".weight") and any(
257 proj in name for proj in ("c_attn", "c_proj", "c_fc")
258 ):
259 loaded_weight = loaded_weight.t()
260 yield name, loaded_weight
261
262 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
263 loader = AutoWeightsLoader(self)
264 return loader.load_weights(
265 self._transpose_conv1d(weights), mapper=self.hf_to_vllm_mapper
266 )
267
268
269 class GPT2LMHeadModel(nn.Module, SupportsPP):
270 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
271 super().__init__()
272 config = vllm_config.model_config.hf_config
273 quant_config = vllm_config.quant_config
274 self.config = config
275 self.quant_config = quant_config
276 self.transformer = GPT2Model(
277 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "transformer")
278 )
279 self.lm_head = ParallelLMHead(
280 self.config.vocab_size,
281 self.config.hidden_size,
282 quant_config=quant_config,
283 prefix=f"{prefix}.lm_head",
284 )
285 if self.config.tie_word_embeddings:
286 self.lm_head = self.lm_head.tie_weights(self.transformer.wte)
287
288 self.logits_processor = LogitsProcessor(config.vocab_size)
289 self.make_empty_intermediate_tensors = (
290 self.transformer.make_empty_intermediate_tensors
291 )
292
293 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
294 return self.transformer.embed_input_ids(input_ids)
295
296 def forward(
297 self,
298 input_ids: torch.Tensor | None,
299 positions: torch.Tensor,
300 intermediate_tensors: IntermediateTensors | None = None,
301 inputs_embeds: torch.Tensor | None = None,
302 ) -> torch.Tensor | IntermediateTensors:
303 hidden_states = self.transformer(
304 input_ids, positions, intermediate_tensors, inputs_embeds
305 )
306 return hidden_states
307
308 def compute_logits(
309 self,
310 hidden_states: torch.Tensor,
311 ) -> torch.Tensor | None:
312 logits = self.logits_processor(self.lm_head, hidden_states)
313 return logits
314
315 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
316 loader = AutoWeightsLoader(self)
317 weights = _add_transformer_prefix(weights)
318 return loader.load_weights(weights)
319
320
321 class GPT2ForSequenceClassification(nn.Module, SupportsCrossEncoding):
322 """GPT2 Model for sequence classification.
323
324 This class expands GPT2Model with pooling and score functions - last token
325 is being used for classification.
326
327 Attributes:
328 transformer: An instance of GPT2Model used for forward operations.
329 score: A layer for calculating logits.
330 _pooler: An instance of Pooler used for pooling operations.
331
332 """
333
334 is_pooling_model = True
335
336 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
337 super().__init__()
338 config = vllm_config.model_config.hf_config
339 self.transformer = GPT2Model(
340 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "transformer")
341 )
342 self.score = nn.Linear(
343 config.n_embd,
344 config.num_labels,
345 bias=False,
346 dtype=vllm_config.model_config.head_dtype,
347 )
348
349 pooler_config = vllm_config.model_config.pooler_config
350 assert pooler_config is not None
351
352 self.pooler = DispatchPooler.for_seq_cls(pooler_config, classifier=self.score)
353
354 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
355 return self.transformer.embed_input_ids(input_ids)
356
357 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
358 loader = AutoWeightsLoader(self)
359 weights = _add_transformer_prefix(weights)
360 return loader.load_weights(weights)
361
362 def forward(
363 self,
364 input_ids: torch.Tensor | None,
365 positions: torch.Tensor,
366 intermediate_tensors: IntermediateTensors | None = None,
367 inputs_embeds: torch.Tensor | None = None,
368 ) -> torch.Tensor:
369 hidden_states = self.transformer(
370 input_ids=input_ids,
371 position_ids=positions,
372 inputs_embeds=inputs_embeds,
373 intermediate_tensors=intermediate_tensors,
374 )
375 return hidden_states
376
377
378 def _add_transformer_prefix(
379 weights: Iterable[tuple[str, torch.Tensor]],
380 ) -> Iterable[tuple[str, torch.Tensor]]:
381 for name, tensor in weights:
382 if (
383 not name.startswith("transformer.")
384 and not name.startswith("lm_head.")
385 and not name.startswith("score.")
386 ):
387 name = "transformer." + name
388 yield name, tensor