main
py 345 lines 11.4 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Mistral adaptation of the LLaMA architecture."""
4
5 from collections.abc import Iterable
6
7 import torch
8 from torch import nn
9 from transformers import LlamaConfig
10
11 from vllm.compilation.decorators import support_torch_compile
12 from vllm.config import CacheConfig, VllmConfig
13 from vllm.model_executor.layers.activation import SiluAndMul
14 from vllm.model_executor.layers.linear import (
15 ColumnParallelLinear,
16 MergedColumnParallelLinear,
17 RowParallelLinear,
18 )
19 from vllm.model_executor.layers.quantization import QuantizationConfig
20 from vllm.model_executor.models.llama import (
21 LlamaAttention,
22 LlamaDecoderLayer,
23 LlamaForCausalLM,
24 LlamaModel,
25 )
26 from vllm.sequence import IntermediateTensors
27 from vllm.v1.attention.backend import AttentionType
28
29 from .utils import AutoWeightsLoader
30
31
32 class MistralMLP(nn.Module):
33 def __init__(
34 self,
35 hidden_size: int,
36 intermediate_size: int,
37 hidden_act: str,
38 quant_config: QuantizationConfig | None = None,
39 bias: bool = False,
40 gate_up_proj_bias: bool | None = None,
41 prefix: str = "",
42 reduce_results: bool = True,
43 disable_tp: bool = False,
44 ) -> None:
45 super().__init__()
46 gate_up_proj_bias = bias if gate_up_proj_bias is None else gate_up_proj_bias
47 self.gate_up_proj = MergedColumnParallelLinear(
48 input_size=hidden_size,
49 output_sizes=[intermediate_size] * 2,
50 bias=gate_up_proj_bias,
51 quant_config=quant_config,
52 disable_tp=disable_tp,
53 prefix=f"{prefix}.gate_up_proj",
54 )
55 self.down_proj = RowParallelLinear(
56 input_size=intermediate_size,
57 output_size=hidden_size,
58 bias=bias,
59 quant_config=quant_config,
60 reduce_results=reduce_results,
61 disable_tp=disable_tp,
62 prefix=f"{prefix}.down_proj",
63 )
64 if hidden_act != "silu":
65 raise ValueError(
66 f"Unsupported activation: {hidden_act}. Only silu is supported for now."
67 )
68 self.act_fn = SiluAndMul()
69
70 def forward(self, x):
71 x, _ = self.gate_up_proj(x)
72 x = self.act_fn(x)
73 x, _ = self.down_proj(x)
74 return x
75
76
77 class MistralAttention(LlamaAttention):
78 def __init__(
79 self,
80 config: LlamaConfig,
81 hidden_size: int,
82 num_heads: int,
83 num_kv_heads: int,
84 max_position_embeddings: int = 8192,
85 quant_config: QuantizationConfig | None = None,
86 bias: bool = False,
87 bias_o_proj: bool = False,
88 cache_config: CacheConfig | None = None,
89 prefix: str = "",
90 attn_type: str = AttentionType.DECODER,
91 ) -> None:
92 super().__init__(
93 config=config,
94 hidden_size=hidden_size,
95 num_heads=num_heads,
96 num_kv_heads=num_kv_heads,
97 max_position_embeddings=max_position_embeddings,
98 quant_config=quant_config,
99 bias=bias,
100 bias_o_proj=bias_o_proj,
101 cache_config=cache_config,
102 prefix=prefix,
103 attn_type=attn_type,
104 )
105
106 llama_4_scaling_config: dict[str, int | float | str] | None = getattr(
107 config, "llama_4_scaling", None
108 )
109 self.do_llama_4_scaling = llama_4_scaling_config is not None
110 if self.do_llama_4_scaling:
111 assert llama_4_scaling_config is not None
112 self.llama_4_scaling_original_max_position_embeddings = (
113 llama_4_scaling_config["original_max_position_embeddings"]
114 )
115 self.llama_4_scaling_beta = llama_4_scaling_config["beta"]
116
117 def _get_llama_4_attn_scale(self, positions: torch.Tensor) -> torch.Tensor:
118 # Llama4 scaling
119 scaling = 1 + self.llama_4_scaling_beta * torch.log(
120 1
121 + torch.floor(
122 positions / self.llama_4_scaling_original_max_position_embeddings
123 )
124 )
125 # Broadcast over head_dim
126 return scaling.unsqueeze(-1)
127
128 def forward(
129 self,
130 positions: torch.Tensor,
131 hidden_states: torch.Tensor,
132 ) -> torch.Tensor:
133 qkv, _ = self.qkv_proj(hidden_states)
134 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
135 q, k = self.rotary_emb(positions, q, k)
136 if self.do_llama_4_scaling:
137 attn_scale = self._get_llama_4_attn_scale(positions)
138 q = (q * attn_scale).to(q.dtype)
139 attn_output = self.attn(q, k, v)
140 output, _ = self.o_proj(attn_output)
141 return output
142
143
144 class MistralDecoderLayer(LlamaDecoderLayer):
145 def __init__(
146 self,
147 vllm_config: VllmConfig,
148 prefix: str = "",
149 config: LlamaConfig | None = None,
150 ) -> None:
151 super().__init__(
152 vllm_config=vllm_config,
153 prefix=prefix,
154 config=config,
155 attn_layer_type=MistralAttention,
156 )
157
158 self.layer_idx = int(prefix.split(sep=".")[-1])
159 config = config or vllm_config.model_config.hf_config
160
161 if getattr(config, "ada_rms_norm_t_cond", False):
162 self.ada_rms_norm_t_cond = nn.Sequential(
163 ColumnParallelLinear(
164 input_size=config.hidden_size,
165 output_size=config.ada_rms_norm_t_cond_dim,
166 bias=False,
167 return_bias=False,
168 ),
169 nn.GELU(),
170 RowParallelLinear(
171 input_size=config.ada_rms_norm_t_cond_dim,
172 output_size=config.hidden_size,
173 bias=False,
174 return_bias=False,
175 ),
176 )
177 else:
178 self.ada_rms_norm_t_cond = None
179
180 def forward(
181 self,
182 positions: torch.Tensor,
183 hidden_states: torch.Tensor,
184 residual: torch.Tensor | None,
185 t_cond: torch.Tensor | None = None,
186 ) -> tuple[torch.Tensor, torch.Tensor]:
187 # Self Attention
188 if residual is None:
189 residual = hidden_states
190 hidden_states = self.input_layernorm(hidden_states)
191 else:
192 hidden_states, residual = self.input_layernorm(hidden_states, residual)
193 hidden_states = self.self_attn(positions=positions, hidden_states=hidden_states)
194
195 # Fully Connected
196 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
197
198 if self.ada_rms_norm_t_cond is not None:
199 assert t_cond is not None
200 hidden_states = hidden_states * (1 + self.ada_rms_norm_t_cond(t_cond))
201
202 hidden_states = self.mlp(hidden_states)
203 return hidden_states, residual
204
205
206 @support_torch_compile
207 class MistralModel(LlamaModel):
208 def __init__(
209 self,
210 *,
211 vllm_config: VllmConfig,
212 prefix: str = "",
213 layer_type: type[nn.Module] = MistralDecoderLayer,
214 ):
215 super().__init__(vllm_config=vllm_config, prefix=prefix, layer_type=layer_type)
216
217 def forward(
218 self,
219 input_ids: torch.Tensor | None,
220 positions: torch.Tensor,
221 intermediate_tensors: IntermediateTensors | None,
222 inputs_embeds: torch.Tensor | None = None,
223 t_cond: torch.Tensor | None = None,
224 **extra_layer_kwargs: object,
225 ) -> torch.Tensor | IntermediateTensors | tuple[torch.Tensor, list[torch.Tensor]]:
226 return super().forward(
227 input_ids,
228 positions,
229 intermediate_tensors,
230 inputs_embeds,
231 t_cond=t_cond,
232 **extra_layer_kwargs,
233 )
234
235
236 class MistralForCausalLM(LlamaForCausalLM):
237 # Mistral: We don't support LoRA on the embedding layers.
238 embedding_modules = {}
239
240 # Mistral/Llama models can also be loaded with --load-format mistral
241 # from consolidated.safetensors checkpoints
242 mistral_mapping = {
243 "layers": "model.layers",
244 "attention": "self_attn",
245 "qscale_act": "input_scale",
246 "qscale_weight": "weight_scale",
247 "kv_fake_quantizer.qscale_act": "kv_scale",
248 "q_fake_quantizer.qscale_act": "attn.q_scale",
249 "k_fake_quantizer.qscale_act": "k_scale",
250 "v_fake_quantizer.qscale_act": "v_scale",
251 "wq": "q_proj",
252 "wk": "k_proj",
253 "wv": "v_proj",
254 "wo": "o_proj",
255 "attention_norm": "input_layernorm",
256 "feed_forward": "mlp",
257 "w1": "gate_proj",
258 "w2": "down_proj",
259 "w3": "up_proj",
260 "ffn_norm": "post_attention_layernorm",
261 "tok_embeddings": "model.embed_tokens",
262 "output": "lm_head",
263 "norm": "model.norm",
264 }
265
266 def __init__(
267 self,
268 *,
269 vllm_config: VllmConfig,
270 prefix: str = "",
271 layer_type: type[nn.Module] = MistralDecoderLayer,
272 ):
273 super().__init__(vllm_config=vllm_config, prefix=prefix, layer_type=layer_type)
274
275 def _init_model(
276 self,
277 vllm_config: VllmConfig,
278 prefix: str = "",
279 layer_type: type[nn.Module] = MistralDecoderLayer,
280 ):
281 return MistralModel(
282 vllm_config=vllm_config, prefix=prefix, layer_type=layer_type
283 )
284
285 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
286 loader = AutoWeightsLoader(self)
287 return loader.load_weights(
288 self.maybe_remap_mistral(name, loaded_weight)
289 for name, loaded_weight in weights
290 )
291
292 def maybe_remap_mistral(
293 self,
294 name: str,
295 loaded_weight: torch.Tensor,
296 ) -> tuple[str, torch.Tensor]:
297 def permute(w: torch.Tensor, n_heads: int, attn_out: int):
298 attn_in = self.config.head_dim * n_heads
299
300 return (
301 w.view(n_heads, attn_in // n_heads // 2, 2, attn_out)
302 .transpose(1, 2)
303 .reshape(attn_in, attn_out)
304 )
305
306 mapping = self.mistral_mapping
307 modules = name.split(".")
308
309 # rotary embeds should be sliced
310 # If using quantized model in mistral format,
311 # quantization scales (qscale_weight) also need to be sliced
312 if "wk" in modules and modules[-1] == "weight":
313 loaded_weight = permute(
314 loaded_weight, self.config.num_key_value_heads, self.config.hidden_size
315 )
316 elif (
317 "wk" in modules
318 and modules[-1] == "qscale_weight"
319 and loaded_weight.numel() > 1
320 ):
321 loaded_weight = permute(loaded_weight, self.config.num_key_value_heads, 1)
322 elif "wq" in modules and modules[-1] == "weight":
323 loaded_weight = permute(
324 loaded_weight, self.config.num_attention_heads, self.config.hidden_size
325 )
326 elif (
327 "wq" in modules
328 and modules[-1] == "qscale_weight"
329 and loaded_weight.numel() > 1
330 ):
331 loaded_weight = permute(loaded_weight, self.config.num_attention_heads, 1)
332
333 num_modules = len(modules)
334 for i in range(num_modules):
335 item = modules[i]
336 next_item = modules[i + 1] if i < num_modules - 1 else None
337
338 combined_item = f"{item}.{next_item}" if next_item is not None else None
339
340 if combined_item in mapping:
341 name = name.replace(combined_item, mapping[combined_item])
342 elif item in mapping and mapping[item] not in name:
343 name = name.replace(item, mapping[item])
344
345 return name, loaded_weight