main
py 245 lines 7.71 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 # A modified implementation of the AIMv2 Transformer
5 # inserted here also the image tokenizer used by Ovis2
6 from collections.abc import Iterable
7
8 import torch
9 import torch.nn as nn
10
11 from vllm.distributed import get_tensor_model_parallel_world_size
12 from vllm.distributed.utils import divide
13 from vllm.model_executor.layers.activation import SiluAndMul
14 from vllm.model_executor.layers.attention import MMEncoderAttention
15 from vllm.model_executor.layers.conv import Conv2dLayer
16 from vllm.model_executor.layers.layernorm import RMSNorm
17 from vllm.model_executor.layers.linear import (
18 MergedColumnParallelLinear,
19 QKVParallelLinear,
20 RowParallelLinear,
21 )
22 from vllm.model_executor.layers.quantization import QuantizationConfig
23 from vllm.model_executor.models.utils import AutoWeightsLoader, WeightsMapper
24 from vllm.transformers_utils.configs.ovis import AIMv2Config
25
26
27 class AIMv2SwiGLUFFN(nn.Module):
28 def __init__(
29 self,
30 config: AIMv2Config,
31 quant_config: QuantizationConfig | None,
32 prefix: str,
33 ):
34 super().__init__()
35 hidden_features = config.intermediate_size
36 in_features = config.hidden_size
37 bias = config.use_bias
38
39 self.fc13 = MergedColumnParallelLinear(
40 in_features,
41 [hidden_features] * 2,
42 bias=bias,
43 quant_config=quant_config,
44 prefix=f"{prefix}.fc13",
45 )
46 self.fc2 = RowParallelLinear(
47 input_size=hidden_features,
48 output_size=in_features,
49 bias=bias,
50 quant_config=quant_config,
51 prefix=f"{prefix}.fc2",
52 )
53 self.act_fn = SiluAndMul()
54
55 def forward(self, x: torch.Tensor) -> torch.Tensor:
56 x, _ = self.fc13(x)
57 x = self.act_fn(x)
58 x, _ = self.fc2(x)
59 return x
60
61
62 class AIMv2PatchEmbed(nn.Module):
63 def __init__(self, config: AIMv2Config):
64 super().__init__()
65 self.proj = Conv2dLayer(
66 config.num_channels,
67 config.hidden_size,
68 kernel_size=(config.patch_size, config.patch_size),
69 stride=(config.patch_size, config.patch_size),
70 )
71 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
72
73 def forward(self, x: torch.Tensor) -> torch.Tensor:
74 x = self.proj(x).flatten(2).transpose(1, 2)
75 x = self.norm.forward_native(x)
76 return x
77
78
79 class AIMv2ViTPreprocessor(nn.Module):
80 def __init__(self, config: AIMv2Config):
81 super().__init__()
82 num_patches = (config.image_size // config.patch_size) ** 2
83
84 self.patchifier = AIMv2PatchEmbed(config)
85 self.pos_embed = nn.Parameter(torch.zeros((1, num_patches, config.hidden_size)))
86
87 def forward(self, x: torch.Tensor) -> torch.Tensor:
88 tokens = self.patchifier(x)
89 _, N, _ = tokens.shape
90 pos_embed = self.pos_embed.to(tokens.device)
91 tokens = tokens + pos_embed[:, :N]
92 return tokens
93
94
95 class AIMv2Attention(nn.Module):
96 def __init__(
97 self,
98 config: AIMv2Config,
99 quant_config: QuantizationConfig | None,
100 prefix: str,
101 ):
102 super().__init__()
103 self.config = config
104 self.embed_dim = config.hidden_size
105 self.num_heads = config.num_attention_heads
106 self.head_dim = self.embed_dim // self.num_heads
107 if self.head_dim * self.num_heads != self.embed_dim:
108 raise ValueError(
109 "embed_dim must be divisible by num_heads "
110 f"(got `embed_dim`: {self.embed_dim} and `num_heads`:"
111 f" {self.num_heads})."
112 )
113 self.scale = self.head_dim**-0.5
114
115 self.qkv = QKVParallelLinear(
116 hidden_size=self.embed_dim,
117 head_size=self.head_dim,
118 total_num_heads=self.num_heads,
119 bias=config.qkv_bias,
120 quant_config=quant_config,
121 prefix=f"{prefix}.qkv",
122 )
123
124 self.proj = RowParallelLinear(
125 input_size=self.embed_dim,
126 output_size=self.embed_dim,
127 bias=config.use_bias,
128 quant_config=quant_config,
129 prefix=f"{prefix}.proj",
130 )
131
132 self.tp_size = get_tensor_model_parallel_world_size()
133 self.num_heads_per_partition = divide(self.num_heads, self.tp_size)
134
135 self.attn = MMEncoderAttention(
136 self.num_heads_per_partition,
137 self.head_dim,
138 self.scale,
139 prefix=f"{prefix}.attn",
140 )
141
142 def forward(self, x: torch.Tensor) -> torch.Tensor:
143 qkv, _ = self.qkv(x)
144 q, k, v = qkv.chunk(3, dim=-1)
145
146 x = self.attn(q, k, v)
147 x, _ = self.proj(x)
148 return x
149
150
151 class AIMv2Block(nn.Module):
152 def __init__(
153 self,
154 config: AIMv2Config,
155 quant_config: QuantizationConfig | None,
156 prefix: str,
157 ):
158 super().__init__()
159 self.attn = AIMv2Attention(
160 config, quant_config=quant_config, prefix=f"{prefix}.attn"
161 )
162 self.norm_1 = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
163 self.mlp = AIMv2SwiGLUFFN(
164 config, quant_config=quant_config, prefix=f"{prefix}.mlp"
165 )
166 self.norm_2 = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
167
168 def forward(self, x: torch.Tensor) -> torch.Tensor:
169 x = x + self.attn(self.norm_1.forward_native(x))
170 x = x + self.mlp(self.norm_2.forward_native(x))
171 return x
172
173
174 class AIMv2Transformer(nn.Module):
175 def __init__(
176 self,
177 config: AIMv2Config,
178 quant_config: QuantizationConfig | None,
179 *,
180 require_post_norm: bool | None = None,
181 prefix: str = "",
182 ):
183 super().__init__()
184
185 self.blocks = nn.ModuleList(
186 [
187 AIMv2Block(config, quant_config, prefix=f"{prefix}.blocks.{i}")
188 for i in range(config.num_hidden_layers)
189 ]
190 )
191 if require_post_norm:
192 self.post_trunk_norm: RMSNorm | None = RMSNorm(
193 config.hidden_size, eps=config.rms_norm_eps
194 )
195 else:
196 self.post_trunk_norm = None
197
198 def forward(self, tokens: torch.Tensor) -> torch.Tensor:
199 # they take the -1 as the ref embeddings, like a clip skip
200 for block in self.blocks:
201 tokens = block(tokens)
202 if self.post_trunk_norm is not None:
203 tokens = self.post_trunk_norm(tokens)
204 return tokens
205
206
207 class AIMv2Model(torch.nn.Module):
208 hf_to_vllm_mapper = WeightsMapper(
209 orig_to_new_stacked={
210 ".fc1": (".fc13", 0),
211 ".fc3": (".fc13", 1),
212 }
213 )
214
215 def __init__(
216 self,
217 config: AIMv2Config,
218 quant_config: QuantizationConfig | None,
219 *,
220 require_post_norm: bool | None = None,
221 prefix: str = "",
222 ):
223 super().__init__()
224 self.preprocessor = AIMv2ViTPreprocessor(config)
225 self.trunk = AIMv2Transformer(
226 config,
227 quant_config=quant_config,
228 require_post_norm=require_post_norm,
229 prefix=f"{prefix}.trunk",
230 )
231 # post_trunk_norm is optional (absent for clip-skip backbones).
232 if self.trunk.post_trunk_norm is None:
233 self.hf_to_vllm_mapper = self.hf_to_vllm_mapper | WeightsMapper(
234 orig_to_new_prefix={"trunk.post_trunk_norm.": None}
235 )
236
237 def forward(self, pixel_values: torch.Tensor) -> torch.Tensor:
238 x = self.preprocessor(pixel_values)
239 x = self.trunk(x)
240
241 return x
242
243 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
244 loader = AutoWeightsLoader(self)
245 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)