main
py 399 lines 14 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 #
4 # RNJ-1 model: Gemma3-based architecture with chunked (block-local) attention.
5 # Chunked attention restricts local layers to attend within aligned blocks,
6 # with lookback to one previous block.
7 from collections.abc import Iterable
8 from itertools import islice
9
10 import torch
11 from torch import nn
12 from transformers import Gemma3TextConfig
13
14 from vllm.compilation.decorators import support_torch_compile
15 from vllm.config import CacheConfig, VllmConfig
16 from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
17 from vllm.logger import init_logger
18 from vllm.model_executor.layers.activation import GeluAndMul
19 from vllm.model_executor.layers.attention import Attention
20 from vllm.model_executor.layers.layernorm import GemmaRMSNorm
21 from vllm.model_executor.layers.linear import (
22 MergedColumnParallelLinear,
23 QKVParallelLinear,
24 RowParallelLinear,
25 )
26 from vllm.model_executor.layers.logits_processor import LogitsProcessor
27 from vllm.model_executor.layers.quantization import QuantizationConfig
28 from vllm.model_executor.layers.rotary_embedding import get_rope
29 from vllm.model_executor.layers.vocab_parallel_embedding import (
30 ParallelLMHead,
31 VocabParallelEmbedding,
32 )
33 from vllm.sequence import IntermediateTensors
34 from vllm.v1.attention.backend import AttentionType
35
36 from .interfaces import SupportsLoRA, SupportsPP
37 from .utils import (
38 AutoWeightsLoader,
39 WeightsMapper,
40 extract_layer_index,
41 make_empty_intermediate_tensors_factory,
42 make_layers,
43 maybe_prefix,
44 )
45
46 logger = init_logger(__name__)
47
48
49 class Rnj1MLP(nn.Module):
50 def __init__(
51 self,
52 hidden_size: int,
53 intermediate_size: int,
54 hidden_activation: str,
55 quant_config: QuantizationConfig | None = None,
56 prefix: str = "",
57 ) -> None:
58 super().__init__()
59 self.gate_up_proj = MergedColumnParallelLinear(
60 hidden_size,
61 [intermediate_size] * 2,
62 bias=False,
63 quant_config=quant_config,
64 prefix=f"{prefix}.gate_up_proj",
65 )
66 self.down_proj = RowParallelLinear(
67 intermediate_size,
68 hidden_size,
69 bias=False,
70 quant_config=quant_config,
71 prefix=f"{prefix}.down_proj",
72 )
73 if hidden_activation != "gelu_pytorch_tanh":
74 raise ValueError(
75 "RNJ-1 uses `gelu_pytorch_tanh` as the hidden activation "
76 "function. Please set `hidden_act` and `hidden_activation` to "
77 "`gelu_pytorch_tanh`."
78 )
79 self.act_fn = GeluAndMul(approximate="tanh")
80
81 def forward(self, x: torch.Tensor) -> torch.Tensor:
82 gate_up, _ = self.gate_up_proj(x)
83 x = self.act_fn(gate_up)
84 x, _ = self.down_proj(x)
85 return x
86
87
88 class Rnj1Attention(nn.Module):
89 def __init__(
90 self,
91 config: Gemma3TextConfig,
92 hidden_size: int,
93 num_heads: int,
94 num_kv_heads: int,
95 head_dim: int,
96 max_position_embeddings: int,
97 cache_config: CacheConfig | None = None,
98 quant_config: QuantizationConfig | None = None,
99 attn_logits_soft_cap: float | None = None,
100 prefix: str = "",
101 ) -> None:
102 super().__init__()
103 self.config = config
104 self.hidden_size = hidden_size
105 tp_size = get_tensor_model_parallel_world_size()
106 self.total_num_heads = num_heads
107 assert self.total_num_heads % tp_size == 0
108 self.num_heads = self.total_num_heads // tp_size
109 self.total_num_kv_heads = num_kv_heads
110 if self.total_num_kv_heads >= tp_size:
111 assert self.total_num_kv_heads % tp_size == 0
112 else:
113 assert tp_size % self.total_num_kv_heads == 0
114 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
115 self.head_dim = head_dim
116 self.q_size = self.num_heads * self.head_dim
117 self.kv_size = self.num_kv_heads * self.head_dim
118 self.scaling = config.query_pre_attn_scalar**-0.5
119
120 self.qkv_proj = QKVParallelLinear(
121 hidden_size,
122 self.head_dim,
123 self.total_num_heads,
124 self.total_num_kv_heads,
125 bias=config.attention_bias,
126 quant_config=quant_config,
127 prefix=f"{prefix}.qkv_proj",
128 )
129 self.o_proj = RowParallelLinear(
130 self.total_num_heads * self.head_dim,
131 hidden_size,
132 bias=config.attention_bias,
133 quant_config=quant_config,
134 prefix=f"{prefix}.o_proj",
135 )
136
137 self.q_norm = GemmaRMSNorm(self.head_dim, eps=config.rms_norm_eps)
138 self.k_norm = GemmaRMSNorm(self.head_dim, eps=config.rms_norm_eps)
139
140 layer_idx = extract_layer_index(prefix)
141 layer_type = config.layer_types[layer_idx]
142 self.is_chunked = layer_type == "chunked_attention"
143 self.chunk_lookback = 1 if self.is_chunked else -1
144 sliding_window = config.sliding_window if self.is_chunked else None
145
146 # Initialize the rotary embedding.
147 # Expects v5-style rope_parameters keyed by layer type.
148 if layer_type in config.rope_parameters:
149 rope_parameters = config.rope_parameters[layer_type]
150 else:
151 rope_parameters = config.rope_parameters
152
153 self.rotary_emb = get_rope(
154 self.head_dim,
155 max_position=max_position_embeddings,
156 rope_parameters=rope_parameters,
157 is_neox_style=True,
158 )
159
160 self.attn = Attention(
161 self.num_heads,
162 self.head_dim,
163 self.scaling,
164 num_kv_heads=self.num_kv_heads,
165 cache_config=cache_config,
166 quant_config=quant_config,
167 attn_type=AttentionType.DECODER,
168 logits_soft_cap=attn_logits_soft_cap,
169 per_layer_sliding_window=sliding_window,
170 chunk_lookback=self.chunk_lookback,
171 prefix=f"{prefix}.attn",
172 )
173
174 def forward(
175 self,
176 positions: torch.Tensor,
177 hidden_states: torch.Tensor,
178 **kwargs,
179 ) -> torch.Tensor:
180 qkv, _ = self.qkv_proj(hidden_states)
181 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
182
183 q = q.unflatten(-1, (self.num_heads, self.head_dim))
184 q = self.q_norm(q)
185 q = q.flatten(-2, -1)
186 k = k.unflatten(-1, (self.num_kv_heads, self.head_dim))
187 k = self.k_norm(k)
188 k = k.flatten(-2, -1)
189
190 q, k = self.rotary_emb(positions, q, k)
191 attn_output = self.attn(q, k, v)
192 output, _ = self.o_proj(attn_output)
193 return output
194
195
196 class Rnj1DecoderLayer(nn.Module):
197 def __init__(
198 self,
199 config: Gemma3TextConfig,
200 cache_config: CacheConfig | None = None,
201 quant_config: QuantizationConfig | None = None,
202 prefix: str = "",
203 ) -> None:
204 super().__init__()
205 self.hidden_size = config.hidden_size
206 self.self_attn = Rnj1Attention(
207 config=config,
208 hidden_size=self.hidden_size,
209 num_heads=config.num_attention_heads,
210 num_kv_heads=config.num_key_value_heads,
211 head_dim=config.head_dim,
212 max_position_embeddings=config.max_position_embeddings,
213 cache_config=cache_config,
214 quant_config=quant_config,
215 attn_logits_soft_cap=None,
216 prefix=f"{prefix}.self_attn",
217 )
218 self.hidden_size = config.hidden_size
219 self.mlp = Rnj1MLP(
220 hidden_size=self.hidden_size,
221 intermediate_size=config.intermediate_size,
222 hidden_activation=config.hidden_activation,
223 quant_config=quant_config,
224 prefix=f"{prefix}.mlp",
225 )
226 self.input_layernorm = GemmaRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
227 self.post_attention_layernorm = GemmaRMSNorm(
228 config.hidden_size, eps=config.rms_norm_eps
229 )
230 self.pre_feedforward_layernorm = GemmaRMSNorm(
231 config.hidden_size, eps=config.rms_norm_eps
232 )
233 self.post_feedforward_layernorm = GemmaRMSNorm(
234 config.hidden_size, eps=config.rms_norm_eps
235 )
236
237 def forward(
238 self,
239 positions: torch.Tensor,
240 hidden_states: torch.Tensor,
241 residual: torch.Tensor | None,
242 **kwargs,
243 ) -> tuple[torch.Tensor, torch.Tensor]:
244 if residual is None:
245 residual = hidden_states
246 hidden_states = self.input_layernorm(hidden_states)
247 else:
248 hidden_states, residual = self.input_layernorm(hidden_states, residual)
249 hidden_states = self.self_attn(
250 positions=positions,
251 hidden_states=hidden_states,
252 **kwargs,
253 )
254 hidden_states = self.post_attention_layernorm(hidden_states)
255
256 hidden_states, residual = self.pre_feedforward_layernorm(
257 hidden_states, residual
258 )
259 hidden_states = self.mlp(hidden_states)
260 hidden_states = self.post_feedforward_layernorm(hidden_states)
261 return hidden_states, residual
262
263
264 @support_torch_compile
265 class Rnj1Model(nn.Module):
266 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
267 super().__init__()
268 config = vllm_config.model_config.hf_config
269 cache_config = vllm_config.cache_config
270 quant_config = vllm_config.quant_config
271 self.config = config
272 self.quant_config = quant_config
273
274 self.embed_tokens = VocabParallelEmbedding(
275 config.vocab_size,
276 config.hidden_size,
277 quant_config=quant_config,
278 prefix=f"{prefix}.embed_tokens",
279 )
280 self.start_layer, self.end_layer, self.layers = make_layers(
281 config.num_hidden_layers,
282 lambda prefix: Rnj1DecoderLayer(
283 config, cache_config, quant_config, prefix=prefix
284 ),
285 prefix=f"{prefix}.layers",
286 )
287 self.norm = GemmaRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
288
289 normalizer = self.config.hidden_size**0.5
290 self.register_buffer("normalizer", torch.tensor(normalizer), persistent=False)
291 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
292 ["hidden_states", "residual"], config.hidden_size
293 )
294
295 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
296 return self.embed_tokens(input_ids) * self.normalizer
297
298 def forward(
299 self,
300 input_ids: torch.Tensor | None,
301 positions: torch.Tensor,
302 intermediate_tensors: IntermediateTensors | None,
303 inputs_embeds: torch.Tensor | None = None,
304 **kwargs,
305 ) -> torch.Tensor | IntermediateTensors:
306 if get_pp_group().is_first_rank:
307 if inputs_embeds is not None:
308 hidden_states = inputs_embeds
309 else:
310 hidden_states = self.embed_input_ids(input_ids)
311 residual = None
312 else:
313 assert intermediate_tensors is not None
314 hidden_states = intermediate_tensors["hidden_states"]
315 residual = intermediate_tensors["residual"]
316 for layer in islice(self.layers, self.start_layer, self.end_layer):
317 hidden_states, residual = layer(
318 positions,
319 hidden_states,
320 residual,
321 **kwargs,
322 )
323 if not get_pp_group().is_last_rank:
324 return IntermediateTensors(
325 {"hidden_states": hidden_states, "residual": residual}
326 )
327 hidden_states, _ = self.norm(hidden_states, residual)
328 return hidden_states
329
330
331 class Rnj1ForCausalLM(nn.Module, SupportsLoRA, SupportsPP):
332 hf_to_vllm_mapper = WeightsMapper(
333 orig_to_new_stacked={
334 # weight_name: (param_name, shard_id)
335 ".q_proj": (".qkv_proj", "q"),
336 ".k_proj": (".qkv_proj", "k"),
337 ".v_proj": (".qkv_proj", "v"),
338 ".gate_proj": (".gate_up_proj", 0),
339 ".up_proj": (".gate_up_proj", 1),
340 }
341 )
342 packed_modules_mapping = {
343 "qkv_proj": ["q_proj", "k_proj", "v_proj"],
344 "gate_up_proj": ["gate_proj", "up_proj"],
345 }
346
347 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
348 config = vllm_config.model_config.hf_config
349 quant_config = vllm_config.quant_config
350
351 super().__init__()
352 self.config = config
353 self.quant_config = quant_config
354 self.model = Rnj1Model(
355 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
356 )
357
358 self.lm_head = ParallelLMHead(
359 config.vocab_size,
360 config.hidden_size,
361 quant_config=quant_config,
362 prefix=maybe_prefix(prefix, "lm_head"),
363 )
364 if config.tie_word_embeddings:
365 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
366
367 self.logits_processor = LogitsProcessor(
368 config.vocab_size, soft_cap=config.final_logit_softcapping
369 )
370 self.make_empty_intermediate_tensors = (
371 self.model.make_empty_intermediate_tensors
372 )
373
374 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
375 return self.model.embed_input_ids(input_ids)
376
377 def forward(
378 self,
379 input_ids: torch.Tensor | None,
380 positions: torch.Tensor,
381 intermediate_tensors: IntermediateTensors | None = None,
382 inputs_embeds: torch.Tensor | None = None,
383 **kwargs,
384 ) -> torch.Tensor | IntermediateTensors:
385 hidden_states = self.model(
386 input_ids, positions, intermediate_tensors, inputs_embeds, **kwargs
387 )
388 return hidden_states
389
390 def compute_logits(
391 self,
392 hidden_states: torch.Tensor,
393 ) -> torch.Tensor | None:
394 logits = self.logits_processor(self.lm_head, hidden_states)
395 return logits
396
397 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
398 loader = AutoWeightsLoader(self)
399 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)