toilaluan commited on
Commit
8a6e75e
·
verified ·
1 Parent(s): e93816e

Upload folder using huggingface_hub

Browse files
config.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "FineViTModel"
4
+ ],
5
+ "auto_map": {
6
+ "AutoConfig": "configuration_finevit.FineViTConfig",
7
+ "AutoModel": "modeling_finevit.FineViTModel"
8
+ },
9
+ "backbone_name": "dinov2_base_reg",
10
+ "backbone_patch_size": 14,
11
+ "backbone_pool_size": 2,
12
+ "backbone_pretrained": true,
13
+ "decoder_head_dim": 64,
14
+ "decoder_num_attention_heads": 12,
15
+ "decoder_num_layers": 8,
16
+ "decoder_position_grid_size": 16,
17
+ "dropout": 0.0,
18
+ "dtype": "float32",
19
+ "encoder_head_dim": 64,
20
+ "encoder_num_attention_heads": 12,
21
+ "encoder_num_layers": 8,
22
+ "encoder_position_grid_size": 16,
23
+ "intermediate_size": 3072,
24
+ "model_type": "finevit",
25
+ "prefix_alignment_mlp_ratio": 4.0,
26
+ "prefix_alignment_num_attention_heads": 12,
27
+ "prefix_alignment_qk_norm": true,
28
+ "training_mask_ratio": 0.75,
29
+ "training_noise_gamma": 0.0,
30
+ "transformers_version": "5.8.1"
31
+ }
configuration_finevit.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from transformers import PretrainedConfig
3
+
4
+
5
+ class FineViTConfig(PretrainedConfig):
6
+ model_type = "finevit"
7
+ has_no_defaults_at_init = True
8
+
9
+ def __init__(
10
+ self,
11
+ encoder_head_dim: int,
12
+ encoder_num_attention_heads: int,
13
+ decoder_head_dim: int,
14
+ decoder_num_attention_heads: int,
15
+ prefix_alignment_num_attention_heads: int | None = None,
16
+ prefix_alignment_qk_norm: bool = True,
17
+ prefix_alignment_mlp_ratio: float = 4.0,
18
+ backbone_patch_size: int = 14,
19
+ encoder_num_layers: int = 12,
20
+ decoder_num_layers: int = 4,
21
+ intermediate_size: int = 3072,
22
+ backbone_name: str = "dinov2_base_reg",
23
+ backbone_pool_size: int = 2,
24
+ encoder_position_grid_size: int | None = None,
25
+ decoder_position_grid_size: int = 16,
26
+ training_mask_ratio: float = 0.7,
27
+ training_noise_gamma: float = 3.0,
28
+ dropout: float = 0.0,
29
+ backbone_pretrained: bool = False,
30
+ **kwargs,
31
+ ):
32
+ legacy_keys = {
33
+ "num_encoder_perceiver_tokens",
34
+ "perceiver_num_iterations",
35
+ } & set(kwargs)
36
+ if legacy_keys:
37
+ keys = ", ".join(sorted(legacy_keys))
38
+ raise ValueError(f"Perceiver config keys are no longer supported: {keys}.")
39
+ super().__init__(**kwargs)
40
+
41
+ self.backbone_name = backbone_name
42
+ self.backbone_pool_size = int(backbone_pool_size)
43
+ self.encoder_position_grid_size = int(
44
+ encoder_position_grid_size
45
+ if encoder_position_grid_size is not None
46
+ else decoder_position_grid_size
47
+ )
48
+ self.decoder_position_grid_size = int(decoder_position_grid_size)
49
+ self.training_mask_ratio = float(training_mask_ratio)
50
+ self.training_noise_gamma = float(training_noise_gamma)
51
+ self.backbone_patch_size = int(backbone_patch_size)
52
+ self.encoder_num_layers = int(encoder_num_layers)
53
+ self.decoder_num_layers = int(decoder_num_layers)
54
+ self.encoder_head_dim = encoder_head_dim
55
+ self.encoder_num_attention_heads = encoder_num_attention_heads
56
+ self.decoder_head_dim = decoder_head_dim
57
+ self.decoder_num_attention_heads = decoder_num_attention_heads
58
+ self.prefix_alignment_num_attention_heads = int(
59
+ prefix_alignment_num_attention_heads
60
+ if prefix_alignment_num_attention_heads is not None
61
+ else encoder_num_attention_heads
62
+ )
63
+ self.prefix_alignment_qk_norm = bool(prefix_alignment_qk_norm)
64
+ self.prefix_alignment_mlp_ratio = float(prefix_alignment_mlp_ratio)
65
+ self.intermediate_size = int(intermediate_size)
66
+ self.dropout = float(dropout)
67
+ self.backbone_pretrained = bool(backbone_pretrained)
68
+ self.architectures = ["FineViTModel"]
69
+ self.auto_map = {
70
+ "AutoConfig": "configuration_finevit.FineViTConfig",
71
+ "AutoModel": "modeling_finevit.FineViTModel",
72
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7515ecc536fca2f995fed063dfc72a0e3f0f81b38daf4e82823a9f6a9c055a4b
3
+ size 866112016
modeling_finevit.py ADDED
@@ -0,0 +1,929 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from dataclasses import dataclass
3
+
4
+ import torch
5
+ import torch.nn as nn
6
+ import torch.nn.functional as F
7
+ from timm.layers.attention_pool import AttentionPoolLatent
8
+ from transformers import PreTrainedModel
9
+ from transformers.utils import ModelOutput
10
+
11
+ from .configuration_finevit import FineViTConfig
12
+ from .vision_encoder import build_encoder
13
+
14
+
15
+ def _run_layer(layer, *inputs, training: bool):
16
+ # if training:
17
+ # return checkpoint(layer, *inputs, use_reentrant=False)
18
+ return layer(*inputs)
19
+
20
+
21
+ def _square_grid_side(length: int, name: str) -> int:
22
+ side = math.isqrt(length)
23
+ if side * side != length:
24
+ raise ValueError(f"{name} length must be a square grid, got {length}.")
25
+ return side
26
+
27
+
28
+ def rotate_half(x: torch.Tensor) -> torch.Tensor:
29
+ """Rotate adjacent feature pairs for rotary position embedding."""
30
+ if x.shape[-1] % 2 != 0:
31
+ raise ValueError(f"RoPE requires an even head dimension, got {x.shape[-1]}.")
32
+ x = x.view(*x.shape[:-1], -1, 2)
33
+ x1, x2 = x.unbind(dim=-1)
34
+ return torch.stack((-x2, x1), dim=-1).flatten(-2)
35
+
36
+
37
+ def apply_rotary_emb(x: torch.Tensor, freqs_cis: torch.Tensor) -> torch.Tensor:
38
+ """Apply rotary position embedding to attention states shaped [B, H, S, D]."""
39
+ if freqs_cis.shape[-1] != x.shape[-1] * 2:
40
+ raise ValueError(
41
+ "RoPE tensor width must be twice the attention head dimension, got "
42
+ f"{freqs_cis.shape[-1]} and {x.shape[-1]}."
43
+ )
44
+ freqs_cos, freqs_sin = freqs_cis.to(device=x.device, dtype=x.dtype).chunk(2, dim=-1)
45
+ freqs_cos = freqs_cos.view(1, 1, freqs_cos.shape[0], freqs_cos.shape[1])
46
+ freqs_sin = freqs_sin.view(1, 1, freqs_sin.shape[0], freqs_sin.shape[1])
47
+ return x * freqs_cos + rotate_half(x) * freqs_sin
48
+
49
+
50
+ def get_rope_tensor(
51
+ dim: int, seq_h: int, seq_w: int, max_freq: float = 7.0, min_freq: float = 7e-4
52
+ ) -> torch.Tensor:
53
+ """Generate rotary position embedding tensor for 2D sequences."""
54
+ if dim % 4 != 0:
55
+ raise ValueError(f"2D RoPE head dimension must be divisible by 4, got {dim}.")
56
+ freqs_1d = max_freq * (max_freq / min_freq) ** torch.linspace(0, -1, dim // 4)
57
+ freqs_1d = torch.cat([freqs_1d, freqs_1d])
58
+ freqs_2d = torch.zeros(2, dim)
59
+ freqs_2d[0, : dim // 2] = freqs_1d
60
+ freqs_2d[1, -dim // 2 :] = freqs_1d
61
+ freqs_2d = freqs_2d * 2 * torch.pi
62
+ coord_x = torch.linspace(0, 1, seq_h)
63
+ coord_y = torch.linspace(0, 1, seq_w)
64
+ coords_all = torch.cartesian_prod(coord_x, coord_y)
65
+ angle = coords_all @ freqs_2d
66
+ return torch.cat([angle.cos(), angle.sin()], dim=-1)
67
+
68
+
69
+ class GatedMLP(nn.Module):
70
+ def __init__(
71
+ self,
72
+ input_dim: int,
73
+ hidden_dim: int,
74
+ output_dim: int,
75
+ bias: bool = False,
76
+ dropout: float = 0.0,
77
+ ):
78
+ super().__init__()
79
+ self.fc_in = nn.Linear(input_dim, 2 * hidden_dim, bias=bias)
80
+ self.dropout = nn.Dropout(dropout)
81
+ self.fc_out = nn.Linear(hidden_dim, output_dim, bias=bias)
82
+
83
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
84
+ gate, value = self.fc_in(hidden_states).chunk(2, dim=-1)
85
+ hidden_states = F.silu(gate) * value
86
+ hidden_states = self.dropout(hidden_states)
87
+ hidden_states = self.fc_out(hidden_states)
88
+ return hidden_states
89
+
90
+
91
+ class MLP(nn.Module):
92
+ def __init__(
93
+ self,
94
+ input_dim: int,
95
+ hidden_dim: int,
96
+ output_dim: int,
97
+ bias: bool = False,
98
+ dropout: float = 0.0,
99
+ ):
100
+ super().__init__()
101
+ self.fc_in = nn.Linear(input_dim, hidden_dim, bias=bias)
102
+ self.dropout = nn.Dropout(dropout)
103
+ self.fc_out = nn.Linear(hidden_dim, output_dim, bias=bias)
104
+
105
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
106
+ hidden_states = F.silu(self.fc_in(hidden_states))
107
+ hidden_states = self.dropout(hidden_states)
108
+ hidden_states = self.fc_out(hidden_states)
109
+ return hidden_states
110
+
111
+
112
+ class Attention(nn.Module):
113
+ def __init__(
114
+ self,
115
+ hidden_size: int,
116
+ head_dim: int,
117
+ num_attention_heads: int,
118
+ dropout: float = 0.0,
119
+ ):
120
+ super().__init__()
121
+ self.head_dim = head_dim
122
+ self.num_attention_heads = num_attention_heads
123
+ self.dropout = dropout
124
+ self.q_norm = nn.LayerNorm(head_dim, eps=1e-6)
125
+ self.k_norm = nn.LayerNorm(head_dim, eps=1e-6)
126
+ self.to_qkv = nn.Linear(
127
+ hidden_size, 3 * head_dim * num_attention_heads, bias=False
128
+ )
129
+ self.to_out = nn.Linear(head_dim * num_attention_heads, hidden_size, bias=False)
130
+
131
+ def forward(
132
+ self,
133
+ hidden_states: torch.Tensor,
134
+ rope_tensor: torch.Tensor | None = None,
135
+ ) -> torch.Tensor:
136
+ batch, seq, _ = hidden_states.shape
137
+ qkv = self.to_qkv(hidden_states).view(
138
+ batch, seq, 3, self.num_attention_heads, self.head_dim
139
+ )
140
+ query, key, value = qkv.permute(0, 2, 3, 1, 4).contiguous().unbind(dim=1)
141
+ query = self.q_norm(query)
142
+ key = self.k_norm(key)
143
+ if rope_tensor is not None:
144
+ if rope_tensor.shape[0] != seq:
145
+ raise ValueError(
146
+ "RoPE sequence length must match hidden_states sequence length, "
147
+ f"got {rope_tensor.shape[0]} and {seq}."
148
+ )
149
+ query = apply_rotary_emb(query, rope_tensor)
150
+ key = apply_rotary_emb(key, rope_tensor)
151
+ attn = (
152
+ F.scaled_dot_product_attention(
153
+ query,
154
+ key,
155
+ value,
156
+ dropout_p=self.dropout if self.training else 0.0,
157
+ )
158
+ .transpose(1, 2)
159
+ .contiguous()
160
+ .view(batch, seq, -1)
161
+ )
162
+ return self.to_out(attn)
163
+
164
+
165
+ class CrossAttention(nn.Module):
166
+ def __init__(
167
+ self,
168
+ hidden_size: int,
169
+ head_dim: int,
170
+ num_attention_heads: int,
171
+ dropout: float = 0.0,
172
+ attention_scale: float | None = None,
173
+ ):
174
+ super().__init__()
175
+ self.head_dim = head_dim
176
+ self.num_attention_heads = num_attention_heads
177
+ self.dropout = dropout
178
+ self.attention_scale = attention_scale
179
+ inner_dim = head_dim * num_attention_heads
180
+ self.q_norm = nn.LayerNorm(head_dim, eps=1e-6)
181
+ self.k_norm = nn.LayerNorm(head_dim, eps=1e-6)
182
+ self.to_q = nn.Linear(hidden_size, inner_dim, bias=False)
183
+ self.to_k = nn.Linear(hidden_size, inner_dim, bias=False)
184
+ self.to_v = nn.Linear(hidden_size, inner_dim, bias=False)
185
+ self.to_out = nn.Linear(inner_dim, hidden_size, bias=False)
186
+
187
+ def forward(
188
+ self,
189
+ query_states: torch.Tensor,
190
+ key_states: torch.Tensor,
191
+ value_states: torch.Tensor,
192
+ return_weights: bool = False,
193
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
194
+ batch, q_seq, _ = query_states.shape
195
+ key_seq = key_states.shape[1]
196
+ value_seq = value_states.shape[1]
197
+ if key_seq != value_seq:
198
+ raise ValueError(
199
+ "key_states and value_states sequence lengths must match, got "
200
+ f"{key_seq} and {value_seq}."
201
+ )
202
+
203
+ query = (
204
+ self.to_q(query_states)
205
+ .view(batch, q_seq, self.num_attention_heads, self.head_dim)
206
+ .transpose(1, 2)
207
+ .contiguous()
208
+ )
209
+ key = (
210
+ self.to_k(key_states)
211
+ .view(batch, key_seq, self.num_attention_heads, self.head_dim)
212
+ .transpose(1, 2)
213
+ .contiguous()
214
+ )
215
+ value = (
216
+ self.to_v(value_states)
217
+ .view(batch, value_seq, self.num_attention_heads, self.head_dim)
218
+ .transpose(1, 2)
219
+ .contiguous()
220
+ )
221
+ query = self.q_norm(query)
222
+ key = self.k_norm(key)
223
+
224
+ if return_weights:
225
+ scale = self.attention_scale or query.shape[-1] ** -0.5
226
+ scores = (query * scale) @ key.transpose(-2, -1)
227
+ weights = scores.softmax(dim=-1)
228
+ if self.training and self.dropout > 0.0:
229
+ attn = F.dropout(weights, p=self.dropout) @ value
230
+ else:
231
+ attn = weights @ value
232
+ else:
233
+ attn = F.scaled_dot_product_attention(
234
+ query,
235
+ key,
236
+ value,
237
+ scale=self.attention_scale,
238
+ dropout_p=self.dropout if self.training else 0.0,
239
+ )
240
+ weights = None
241
+
242
+ attn = attn.transpose(1, 2).contiguous().view(batch, q_seq, -1)
243
+ out = self.to_out(attn)
244
+ return (out, weights) if return_weights else out
245
+
246
+
247
+ class TransformerBlock(nn.Module):
248
+ def __init__(
249
+ self,
250
+ hidden_size: int,
251
+ head_dim: int,
252
+ num_attention_heads: int,
253
+ mlp_hidden_dim: int,
254
+ dropout: float = 0.0,
255
+ ):
256
+ super().__init__()
257
+ self.attn_norm = nn.LayerNorm(hidden_size, eps=1e-6, bias=False)
258
+ self.attention = Attention(hidden_size, head_dim, num_attention_heads, dropout)
259
+ self.attn_dropout = nn.Dropout(dropout)
260
+
261
+ self.mlp_norm = nn.LayerNorm(hidden_size, eps=1e-6, bias=False)
262
+ self.mlp = MLP(
263
+ hidden_size,
264
+ mlp_hidden_dim,
265
+ hidden_size,
266
+ dropout=dropout,
267
+ )
268
+ self.mlp_dropout = nn.Dropout(dropout)
269
+
270
+ def forward(
271
+ self,
272
+ hidden_states: torch.Tensor,
273
+ rope_tensor: torch.Tensor | None = None,
274
+ ) -> torch.Tensor:
275
+ hidden_states = hidden_states + self.attn_dropout(
276
+ self.attention(self.attn_norm(hidden_states), rope_tensor)
277
+ )
278
+ hidden_states = hidden_states + self.mlp_dropout(
279
+ self.mlp(self.mlp_norm(hidden_states))
280
+ )
281
+ return hidden_states
282
+
283
+
284
+ class CrossAttentionBlock(nn.Module):
285
+ def __init__(
286
+ self,
287
+ hidden_size: int,
288
+ head_dim: int,
289
+ num_attention_heads: int,
290
+ mlp_hidden_dim: int,
291
+ fused_kv: bool = False,
292
+ dropout: float = 0.0,
293
+ ):
294
+ super().__init__()
295
+ self.fused_kv = fused_kv
296
+
297
+ self.q_norm = nn.LayerNorm(hidden_size, eps=1e-6, bias=False)
298
+ self.kv_norm = nn.LayerNorm(hidden_size, eps=1e-6, bias=False)
299
+ self.attention = CrossAttention(
300
+ hidden_size, head_dim, num_attention_heads, dropout
301
+ )
302
+ self.attn_dropout = nn.Dropout(dropout)
303
+
304
+ self.mlp_norm = nn.LayerNorm(hidden_size, eps=1e-6, bias=False)
305
+ self.mlp = MLP(
306
+ hidden_size,
307
+ mlp_hidden_dim,
308
+ hidden_size,
309
+ dropout=dropout,
310
+ )
311
+ self.mlp_dropout = nn.Dropout(dropout)
312
+
313
+ def forward(
314
+ self,
315
+ query_states: torch.Tensor,
316
+ kv_states: torch.Tensor,
317
+ return_weights: bool = False,
318
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
319
+ kv_input = (
320
+ torch.cat([query_states, kv_states], dim=1) if self.fused_kv else kv_states
321
+ )
322
+ kv_input = self.kv_norm(kv_input)
323
+
324
+ attn_out = self.attention(
325
+ self.q_norm(query_states),
326
+ kv_input,
327
+ kv_input,
328
+ return_weights=return_weights,
329
+ )
330
+
331
+ weights = None
332
+ if return_weights:
333
+ attn_out, weights = attn_out
334
+
335
+ query_states = query_states + self.attn_dropout(attn_out)
336
+ query_states = query_states + self.mlp_dropout(
337
+ self.mlp(self.mlp_norm(query_states))
338
+ )
339
+
340
+ return (query_states, weights) if return_weights else query_states
341
+
342
+
343
+ class PatchPooler(nn.Module):
344
+ def __init__(
345
+ self,
346
+ hidden_size: int,
347
+ pool_size: int,
348
+ head_dim: int,
349
+ num_attention_heads: int,
350
+ mlp_hidden_dim: int,
351
+ dropout: float = 0.0,
352
+ ) -> None:
353
+ super().__init__()
354
+ self.pool_size = int(pool_size)
355
+ self.num_attention_heads = int(num_attention_heads)
356
+ if self.pool_size <= 0:
357
+ raise ValueError(f"pool_size must be positive, got {self.pool_size}.")
358
+
359
+ self.pool_attention = CrossAttention(
360
+ hidden_size=hidden_size,
361
+ head_dim=head_dim,
362
+ num_attention_heads=num_attention_heads,
363
+ dropout=dropout,
364
+ )
365
+
366
+ def forward(
367
+ self,
368
+ features: torch.Tensor,
369
+ return_attention_weights: bool = False,
370
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
371
+ batch, length, dim = features.shape
372
+ grid_side = _square_grid_side(length, "backbone feature")
373
+ if grid_side % self.pool_size != 0:
374
+ raise ValueError(
375
+ "backbone feature grid side must be divisible by backbone_pool_size, "
376
+ f"got grid_side={grid_side} and pool_size={self.pool_size}."
377
+ )
378
+
379
+ pooled_side = grid_side // self.pool_size
380
+ num_windows = pooled_side * pooled_side
381
+ pool = self.pool_size
382
+ windows = (
383
+ features.view(batch, pooled_side, pool, pooled_side, pool, dim)
384
+ .permute(0, 1, 3, 2, 4, 5)
385
+ .contiguous()
386
+ .flatten(3, 4)
387
+ ).view(batch * num_windows, pool * pool, dim)
388
+
389
+ query = windows.mean(dim=1, keepdim=True)
390
+ if not return_attention_weights:
391
+ pooled = self.pool_attention(query, windows, windows)
392
+ return pooled.view(batch, num_windows, dim), None
393
+
394
+ pooled, weights = self.pool_attention(
395
+ query, windows, windows, return_weights=True
396
+ )
397
+ weights = weights.squeeze(2).view(
398
+ batch,
399
+ num_windows,
400
+ self.num_attention_heads,
401
+ pool * pool,
402
+ )
403
+ return pooled.view(batch, num_windows, dim), weights.permute(
404
+ 0, 2, 1, 3
405
+ ).contiguous()
406
+
407
+
408
+ class PatchUnpooler(nn.Module):
409
+ def __init__(
410
+ self,
411
+ hidden_size: int,
412
+ pool_size: int,
413
+ intermediate_size: int,
414
+ dropout: float = 0.0,
415
+ ) -> None:
416
+ super().__init__()
417
+ self.hidden_size = hidden_size
418
+ self.pool_size = int(pool_size)
419
+ if self.pool_size <= 0:
420
+ raise ValueError(f"pool_size must be positive, got {self.pool_size}.")
421
+
422
+ self.up = nn.Linear(
423
+ hidden_size, hidden_size * self.pool_size * self.pool_size, bias=False
424
+ )
425
+
426
+ def forward(
427
+ self,
428
+ pooled_tokens: torch.Tensor,
429
+ output_grid_side: int,
430
+ ) -> torch.Tensor:
431
+ batch, pooled_length, dim = pooled_tokens.shape
432
+ if dim != self.hidden_size:
433
+ raise ValueError(
434
+ "pooled_tokens hidden size must match unpooler hidden size, got "
435
+ f"{dim} and {self.hidden_size}."
436
+ )
437
+ if output_grid_side <= 0:
438
+ raise ValueError(
439
+ f"output_grid_side must be positive, got {output_grid_side}."
440
+ )
441
+ if output_grid_side % self.pool_size != 0:
442
+ raise ValueError(
443
+ "output_grid_side must be divisible by pool_size, got "
444
+ f"{output_grid_side} and {self.pool_size}."
445
+ )
446
+
447
+ pooled_side = output_grid_side // self.pool_size
448
+ if pooled_side * pooled_side != pooled_length:
449
+ raise ValueError(
450
+ "pooled_tokens length must match output_grid_side and pool_size, got "
451
+ f"length={pooled_length}, output_grid_side={output_grid_side}, "
452
+ f"pool_size={self.pool_size}."
453
+ )
454
+
455
+ pool = self.pool_size
456
+ patches = self.up(pooled_tokens).view(batch, pooled_length, pool, pool, dim)
457
+ patches = patches + pooled_tokens[:, :, None, None, :]
458
+ patches = patches.view(batch, pooled_side, pooled_side, pool, pool, dim)
459
+ return (
460
+ patches.permute(0, 1, 3, 2, 4, 5)
461
+ .contiguous()
462
+ .view(batch, output_grid_side * output_grid_side, dim)
463
+ )
464
+
465
+
466
+ class PositionEmbedding(nn.Module):
467
+ def __init__(self, grid_size: int, hidden_size: int) -> None:
468
+ super().__init__()
469
+ self.grid_size = int(grid_size)
470
+ self.hidden_size = hidden_size
471
+ if self.grid_size <= 0:
472
+ raise ValueError(f"grid_size must be positive, got {self.grid_size}.")
473
+
474
+ self.weight = nn.Parameter(
475
+ torch.empty(1, self.grid_size * self.grid_size, hidden_size)
476
+ )
477
+ self.reset_parameters()
478
+
479
+ def reset_parameters(self) -> None:
480
+ nn.init.trunc_normal_(self.weight, std=0.02)
481
+
482
+ def forward(
483
+ self,
484
+ grid_side: int,
485
+ *,
486
+ device: torch.device,
487
+ dtype: torch.dtype,
488
+ ) -> torch.Tensor:
489
+ if grid_side <= 0:
490
+ raise ValueError(f"grid_side must be positive, got {grid_side}.")
491
+
492
+ position = self.weight
493
+ if grid_side != self.grid_size:
494
+ position = position.view(
495
+ 1,
496
+ self.grid_size,
497
+ self.grid_size,
498
+ self.hidden_size,
499
+ ).permute(0, 3, 1, 2)
500
+ position = F.interpolate(
501
+ position.contiguous(),
502
+ size=(grid_side, grid_side),
503
+ mode="bicubic",
504
+ align_corners=False,
505
+ )
506
+ position = (
507
+ position.permute(0, 2, 3, 1)
508
+ .contiguous()
509
+ .view(1, grid_side * grid_side, self.hidden_size)
510
+ )
511
+ return position.to(device=device, dtype=dtype)
512
+
513
+
514
+ class FineViTBackbone(nn.Module):
515
+ def __init__(self, config: FineViTConfig):
516
+ super().__init__()
517
+ self.encoder = build_encoder(
518
+ config.backbone_name,
519
+ pretrained=config.backbone_pretrained,
520
+ )
521
+ self.hidden_size = self.encoder.hidden_size
522
+ self.patch_size = self.encoder.patch_size
523
+
524
+ if self.patch_size != config.backbone_patch_size:
525
+ raise ValueError(
526
+ "Configured backbone_patch_size does not match encoder patch size: "
527
+ f"{config.backbone_patch_size} != {self.patch_size}."
528
+ )
529
+
530
+ def forward(
531
+ self,
532
+ pixel_values: torch.Tensor,
533
+ return_prefix_tokens: bool = False,
534
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
535
+ return self.encoder(
536
+ pixel_values,
537
+ return_prefix_tokens=return_prefix_tokens,
538
+ )
539
+
540
+
541
+ class FineViTEncoder(nn.Module):
542
+ def __init__(self, config: FineViTConfig, hidden_size: int):
543
+ super().__init__()
544
+ self.config = config
545
+ self.hidden_size = hidden_size
546
+ self.num_prefix_tokens = 0
547
+ self.training_mask_ratio = float(config.training_mask_ratio)
548
+ if config.encoder_head_dim % 4 != 0:
549
+ raise ValueError(
550
+ "encoder_head_dim must be divisible by 4 for 2D RoPE, got "
551
+ f"{config.encoder_head_dim}."
552
+ )
553
+
554
+ self.patch_positions = PositionEmbedding(
555
+ config.encoder_position_grid_size,
556
+ hidden_size,
557
+ )
558
+ self.input_norm = nn.LayerNorm(hidden_size, eps=1e-6, bias=False)
559
+ self.layers = nn.ModuleList(
560
+ TransformerBlock(
561
+ hidden_size=hidden_size,
562
+ head_dim=config.encoder_head_dim,
563
+ num_attention_heads=config.encoder_num_attention_heads,
564
+ mlp_hidden_dim=config.intermediate_size,
565
+ dropout=config.dropout,
566
+ )
567
+ for _ in range(config.encoder_num_layers)
568
+ )
569
+
570
+ self.pooler_norm = nn.LayerNorm(hidden_size, eps=1e-6, bias=False)
571
+ self.patch_pooler = PatchPooler(
572
+ hidden_size=hidden_size,
573
+ pool_size=config.backbone_pool_size,
574
+ head_dim=config.encoder_head_dim,
575
+ num_attention_heads=config.encoder_num_attention_heads,
576
+ mlp_hidden_dim=config.intermediate_size,
577
+ dropout=config.dropout,
578
+ )
579
+ self.head_norm = nn.LayerNorm(hidden_size, eps=1e-6, bias=False)
580
+ self.head_proj = MLP(hidden_size, config.intermediate_size, hidden_size)
581
+ self.apply(self._init_weights)
582
+
583
+ def _init_weights(self, module: nn.Module) -> None:
584
+ if isinstance(module, nn.Linear):
585
+ nn.init.trunc_normal_(module.weight, std=0.02)
586
+ if module.bias is not None:
587
+ nn.init.zeros_(module.bias)
588
+
589
+ elif isinstance(module, nn.LayerNorm):
590
+ nn.init.ones_(module.weight)
591
+ if module.bias is not None:
592
+ nn.init.zeros_(module.bias)
593
+
594
+ elif isinstance(module, nn.Embedding):
595
+ nn.init.trunc_normal_(module.weight, std=0.02)
596
+
597
+ def _sample_mask_ratio(self, device: torch.device) -> torch.Tensor:
598
+ return (
599
+ torch.empty((), device=device)
600
+ .uniform_(-0.1, self.training_mask_ratio)
601
+ .clamp_min(0.0)
602
+ )
603
+
604
+ def _apply_masking(
605
+ self,
606
+ backbone_features: torch.Tensor,
607
+ mask_token: torch.Tensor | None,
608
+ ) -> torch.Tensor:
609
+ if not self.training:
610
+ return backbone_features
611
+ if mask_token is None:
612
+ raise ValueError(
613
+ "mask_token is required for encoder masking during training."
614
+ )
615
+
616
+ mask_token = mask_token.to(
617
+ device=backbone_features.device,
618
+ dtype=backbone_features.dtype,
619
+ )
620
+ if mask_token.shape[-1] != backbone_features.shape[-1]:
621
+ raise ValueError(
622
+ "mask_token hidden size must match backbone feature hidden size, got "
623
+ f"{mask_token.shape[-1]} and {backbone_features.shape[-1]}."
624
+ )
625
+
626
+ mask_ratio = self._sample_mask_ratio(backbone_features.device)
627
+ anchor = mask_token.sum() * 0.0
628
+ token_mask = torch.rand(
629
+ backbone_features.shape[0],
630
+ backbone_features.shape[1],
631
+ 1,
632
+ device=backbone_features.device,
633
+ ).lt(mask_ratio)
634
+ return (
635
+ torch.where(
636
+ token_mask,
637
+ mask_token.expand_as(backbone_features),
638
+ backbone_features,
639
+ )
640
+ + anchor
641
+ )
642
+
643
+ def forward(
644
+ self,
645
+ backbone_features: torch.Tensor,
646
+ mask_token: torch.Tensor | None = None,
647
+ return_attention_weights: bool = False,
648
+ ) -> tuple[
649
+ torch.Tensor | None,
650
+ torch.Tensor,
651
+ ]:
652
+ grid_side = _square_grid_side(backbone_features.shape[1], "backbone feature")
653
+ hidden_states = backbone_features
654
+ hidden_states = hidden_states + self.patch_positions(
655
+ grid_side,
656
+ device=hidden_states.device,
657
+ dtype=hidden_states.dtype,
658
+ )
659
+
660
+ hidden_states = self.input_norm(hidden_states)
661
+ rope_tensor = get_rope_tensor(
662
+ self.config.encoder_head_dim,
663
+ grid_side,
664
+ grid_side,
665
+ ).to(device=hidden_states.device, dtype=hidden_states.dtype)
666
+ for layer in self.layers:
667
+ hidden_states = _run_layer(
668
+ layer,
669
+ hidden_states,
670
+ rope_tensor,
671
+ training=self.training,
672
+ )
673
+ pooled_tokens, pool_attention_weights = self.patch_pooler(
674
+ self.pooler_norm(hidden_states),
675
+ return_attention_weights=return_attention_weights,
676
+ )
677
+ encoder_hidden_states = self._apply_masking(pooled_tokens, mask_token)
678
+ encoder_hidden_states = self.head_proj(self.head_norm(encoder_hidden_states))
679
+
680
+ return (
681
+ pool_attention_weights,
682
+ encoder_hidden_states,
683
+ )
684
+
685
+
686
+ class FineViTFeatureDecoder(nn.Module):
687
+ def __init__(self, config: FineViTConfig, hidden_size: int):
688
+ super().__init__()
689
+ self.config = config
690
+ self.hidden_size = hidden_size
691
+ if config.decoder_head_dim % 4 != 0:
692
+ raise ValueError(
693
+ "decoder_head_dim must be divisible by 4 for 2D RoPE, got "
694
+ f"{config.decoder_head_dim}."
695
+ )
696
+
697
+ self.input_norm_1 = nn.LayerNorm(hidden_size, eps=1e-6, bias=False)
698
+ self.input_norm_2 = nn.LayerNorm(hidden_size, eps=1e-6, bias=False)
699
+
700
+ self.patch_positions = PositionEmbedding(
701
+ config.decoder_position_grid_size,
702
+ hidden_size,
703
+ )
704
+ self.patch_unpooler = PatchUnpooler(
705
+ hidden_size=hidden_size,
706
+ pool_size=config.backbone_pool_size,
707
+ intermediate_size=config.intermediate_size,
708
+ dropout=config.dropout,
709
+ )
710
+ self.layers = nn.ModuleList(
711
+ TransformerBlock(
712
+ hidden_size=hidden_size,
713
+ head_dim=config.decoder_head_dim,
714
+ num_attention_heads=config.decoder_num_attention_heads,
715
+ mlp_hidden_dim=config.intermediate_size,
716
+ dropout=config.dropout,
717
+ )
718
+ for _ in range(config.decoder_num_layers)
719
+ )
720
+
721
+ self.head_norm = nn.LayerNorm(hidden_size, eps=1e-6, bias=False)
722
+ self.head_proj = nn.Linear(hidden_size, hidden_size)
723
+
724
+ self.apply(self._init_weights)
725
+
726
+ def _init_weights(self, module: nn.Module) -> None:
727
+ if isinstance(module, nn.Linear):
728
+ nn.init.trunc_normal_(module.weight, std=0.02)
729
+ if module.bias is not None:
730
+ nn.init.zeros_(module.bias)
731
+
732
+ elif isinstance(module, nn.LayerNorm):
733
+ nn.init.ones_(module.weight)
734
+ if module.bias is not None:
735
+ nn.init.zeros_(module.bias)
736
+
737
+ elif isinstance(module, nn.Embedding):
738
+ nn.init.trunc_normal_(module.weight, std=0.02)
739
+
740
+ def forward(
741
+ self,
742
+ encoder_hidden_states: torch.Tensor,
743
+ backbone_grid_side: int,
744
+ ) -> torch.Tensor:
745
+ if encoder_hidden_states.shape[-1] != self.hidden_size:
746
+ raise ValueError(
747
+ "encoder_hidden_states hidden size must match decoder hidden size, got "
748
+ f"{encoder_hidden_states.shape[-1]} and {self.hidden_size}."
749
+ )
750
+
751
+ encoder_hidden_states = self.input_norm_1(encoder_hidden_states)
752
+
753
+ hidden = self.patch_unpooler(
754
+ encoder_hidden_states,
755
+ output_grid_side=backbone_grid_side,
756
+ )
757
+ hidden = hidden + self.patch_positions(
758
+ backbone_grid_side,
759
+ device=encoder_hidden_states.device,
760
+ dtype=encoder_hidden_states.dtype,
761
+ )
762
+ hidden = self.input_norm_2(hidden)
763
+ rope_tensor = get_rope_tensor(
764
+ self.config.decoder_head_dim,
765
+ backbone_grid_side,
766
+ backbone_grid_side,
767
+ ).to(device=hidden.device, dtype=hidden.dtype)
768
+
769
+ for layer in self.layers:
770
+ hidden = _run_layer(
771
+ layer,
772
+ hidden,
773
+ rope_tensor,
774
+ training=self.training,
775
+ )
776
+
777
+ return self.head_proj(self.head_norm(hidden))
778
+
779
+
780
+ @dataclass
781
+ class FineViTOutput(ModelOutput):
782
+ encoder_hidden_states: torch.Tensor | None = None
783
+ backbone_features: torch.Tensor | None = None
784
+ backbone_prefix_tokens: torch.Tensor | None = None
785
+ aligned_prefix_tokens: torch.Tensor | None = None
786
+ decoded_patches: torch.Tensor | None = None
787
+ pool_attention_weights: torch.Tensor | None = None
788
+
789
+
790
+ class FineViTModel(PreTrainedModel):
791
+ config_class = FineViTConfig
792
+ base_model_prefix = "finevit"
793
+ main_input_name = "pixel_values"
794
+ _no_split_modules = ["TransformerBlock", "CrossAttentionBlock"]
795
+ MODEL_FILENAME = "model.safetensors"
796
+ CONFIG_FILENAME = "config.json"
797
+ all_tied_weights_keys = {}
798
+
799
+ def __init__(self, config: FineViTConfig):
800
+ super().__init__(config)
801
+ self.backbone = FineViTBackbone(config)
802
+ hidden_size = self.backbone.hidden_size
803
+ self.encoder = FineViTEncoder(config, hidden_size)
804
+ self.decoder = FineViTFeatureDecoder(config, hidden_size)
805
+ self.mask_token = nn.Parameter(torch.empty(1, 1, hidden_size))
806
+ nn.init.trunc_normal_(self.mask_token, std=0.02)
807
+ self.backbone_num_prefix_tokens = int(self.backbone.encoder.num_prefix_tokens)
808
+ self.encoder_num_prefix_tokens = int(self.encoder.num_prefix_tokens)
809
+ self.num_prefix_tokens = self.encoder_num_prefix_tokens
810
+ self.prefix_aligner = AttentionPoolLatent(
811
+ in_features=hidden_size,
812
+ out_features=hidden_size,
813
+ embed_dim=hidden_size,
814
+ num_heads=config.prefix_alignment_num_attention_heads,
815
+ latent_len=self.backbone_num_prefix_tokens,
816
+ qk_norm=config.prefix_alignment_qk_norm,
817
+ mlp_ratio=config.prefix_alignment_mlp_ratio,
818
+ pool_type="",
819
+ norm_layer=nn.LayerNorm,
820
+ drop=config.dropout,
821
+ )
822
+
823
+ def _prepare_encoder_hidden_states(self, encoded: FineViTOutput) -> torch.Tensor:
824
+ if encoded.encoder_hidden_states is None:
825
+ raise ValueError("encoded must contain encoder_hidden_states.")
826
+
827
+ if self.training:
828
+ encoder_hidden_states = encoded.encoder_hidden_states
829
+ gamma = float(self.config.training_noise_gamma)
830
+ if gamma < 0.0:
831
+ raise ValueError(
832
+ f"training_noise_gamma must be non-negative, got {gamma}."
833
+ )
834
+ if gamma > 0.0:
835
+ tau = torch.rand(
836
+ encoder_hidden_states.shape[0],
837
+ 1,
838
+ 1,
839
+ device=encoder_hidden_states.device,
840
+ dtype=encoder_hidden_states.dtype,
841
+ )
842
+ encoder_hidden_states = torch.lerp(
843
+ encoder_hidden_states,
844
+ torch.randn_like(encoder_hidden_states) * gamma,
845
+ tau,
846
+ )
847
+ return encoder_hidden_states
848
+
849
+ return encoded.encoder_hidden_states
850
+
851
+ def encode(
852
+ self,
853
+ pixel_values: torch.Tensor,
854
+ return_attention_weights: bool = False,
855
+ output_backbone_prefix_tokens: bool = False,
856
+ output_aligned_prefix_tokens: bool = False,
857
+ ) -> FineViTOutput:
858
+ backbone_prefix_tokens = None
859
+ backbone_output = self.backbone(
860
+ pixel_values,
861
+ return_prefix_tokens=output_backbone_prefix_tokens,
862
+ )
863
+ if output_backbone_prefix_tokens:
864
+ backbone_features, backbone_prefix_tokens = backbone_output
865
+ else:
866
+ backbone_features = backbone_output
867
+ (
868
+ pool_attention_weights,
869
+ encoder_hidden_states,
870
+ ) = self.encoder(
871
+ backbone_features,
872
+ mask_token=self.mask_token,
873
+ return_attention_weights=return_attention_weights,
874
+ )
875
+ aligned_prefix_tokens = (
876
+ self.prefix_aligner(encoder_hidden_states)
877
+ if output_aligned_prefix_tokens
878
+ else None
879
+ )
880
+ return FineViTOutput(
881
+ encoder_hidden_states=encoder_hidden_states,
882
+ backbone_features=backbone_features,
883
+ backbone_prefix_tokens=backbone_prefix_tokens,
884
+ aligned_prefix_tokens=aligned_prefix_tokens,
885
+ pool_attention_weights=pool_attention_weights,
886
+ )
887
+
888
+ def decode_encoder_hidden_states(
889
+ self,
890
+ encoder_hidden_states: torch.Tensor,
891
+ ) -> torch.Tensor:
892
+ pooled_grid_side = _square_grid_side(
893
+ encoder_hidden_states.shape[1],
894
+ "encoder hidden states",
895
+ )
896
+ backbone_grid_side = pooled_grid_side * int(self.config.backbone_pool_size)
897
+ return self.decoder(encoder_hidden_states, backbone_grid_side)
898
+
899
+ def decode(
900
+ self,
901
+ encoded: FineViTOutput,
902
+ ) -> torch.Tensor:
903
+ return self.decode_encoder_hidden_states(
904
+ self._prepare_encoder_hidden_states(encoded)
905
+ )
906
+
907
+ def forward(
908
+ self,
909
+ pixel_values: torch.Tensor,
910
+ output_decoded_patches: bool = True,
911
+ output_backbone_prefix_tokens: bool = False,
912
+ output_aligned_prefix_tokens: bool = False,
913
+ return_attention_weights: bool = True,
914
+ ) -> FineViTOutput:
915
+ output = self.encode(
916
+ pixel_values,
917
+ return_attention_weights=return_attention_weights,
918
+ output_backbone_prefix_tokens=output_backbone_prefix_tokens,
919
+ output_aligned_prefix_tokens=output_aligned_prefix_tokens,
920
+ )
921
+ if output_decoded_patches:
922
+ output.decoded_patches = self.decode(output)
923
+ return output
924
+
925
+
926
+ FineViT = FineViTModel
927
+
928
+ FineViTConfig.register_for_auto_class()
929
+ FineViTModel.register_for_auto_class("AutoModel")
vision_encoder.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import timm
4
+
5
+
6
+ class TimmVisionEncoder(nn.Module):
7
+ def __init__(self, pretrained_encoder_name: str, load_pretrained: bool = True):
8
+ super().__init__()
9
+ self.model = timm.create_model(
10
+ pretrained_encoder_name,
11
+ pretrained=load_pretrained,
12
+ dynamic_img_size=True,
13
+ )
14
+ self.hidden_size = self.model.embed_dim
15
+ self.model.norm = nn.LayerNorm(self.hidden_size, elementwise_affine=False)
16
+ self.num_prefix_tokens = self.model.num_prefix_tokens
17
+ patch_size = self.model.patch_embed.patch_size
18
+ self.patch_size = int(
19
+ patch_size[0] if isinstance(patch_size, tuple) else patch_size
20
+ )
21
+ if self.patch_size == 14:
22
+ input_size = 224
23
+ else:
24
+ input_size = 256
25
+ self.model.set_input_size(input_size, patch_size)
26
+
27
+ data_config = timm.data.resolve_model_data_config(self.model)
28
+ self.register_buffer(
29
+ "pixel_mean",
30
+ torch.tensor(data_config["mean"])[None, :, None, None],
31
+ persistent=True,
32
+ )
33
+ self.register_buffer(
34
+ "pixel_std",
35
+ torch.tensor(data_config["std"])[None, :, None, None],
36
+ persistent=True,
37
+ )
38
+
39
+ def forward(
40
+ self,
41
+ pixel_values: torch.Tensor,
42
+ return_prefix_tokens: bool = False,
43
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
44
+ x = pixel_values.float() / 255.0
45
+ weight_dtype = self.model.patch_embed.proj.weight.dtype
46
+ x = ((x - self.pixel_mean) / self.pixel_std).to(dtype=weight_dtype)
47
+ output = self.model.forward_features(x)
48
+ if isinstance(output, dict):
49
+ output = output["x"]
50
+ prefix_tokens = output[:, : self.num_prefix_tokens, :]
51
+ patch_tokens = output[:, self.num_prefix_tokens :, :]
52
+ if return_prefix_tokens:
53
+ return patch_tokens, prefix_tokens
54
+ return patch_tokens
55
+
56
+
57
+ class DinoV2Encoder(TimmVisionEncoder):
58
+ pass
59
+
60
+
61
+ class DinoV3Encoder(TimmVisionEncoder):
62
+ pass
63
+
64
+
65
+ ALL_ENCODERS = {
66
+ "dinov3_small": (DinoV3Encoder, "vit_small_patch16_dinov3.lvd1689m"),
67
+ "dinov3_base": (DinoV3Encoder, "vit_base_patch16_dinov3.lvd1689m"),
68
+ "dinov3_large": (DinoV3Encoder, "vit_large_patch16_dinov3.lvd1689m"),
69
+ "dinov2_small_reg": (DinoV2Encoder, "vit_small_patch14_reg4_dinov2.lvd142m"),
70
+ "dinov2_base_reg": (DinoV2Encoder, "vit_base_patch14_reg4_dinov2.lvd142m"),
71
+ "dinov2_large_reg": (DinoV2Encoder, "vit_large_patch14_reg4_dinov2.lvd142m"),
72
+ }
73
+
74
+
75
+ def build_encoder(encoder_name: str, pretrained: bool = False):
76
+ if encoder_name not in ALL_ENCODERS:
77
+ raise ValueError(
78
+ f"Unknown encoder {encoder_name!r}. Available: {list(ALL_ENCODERS)}"
79
+ )
80
+ model_cls, model_id = ALL_ENCODERS[encoder_name]
81
+ return model_cls(model_id, load_pretrained=pretrained)
82
+
83
+
84
+ __all__ = [
85
+ "ALL_ENCODERS",
86
+ "DinoV2Encoder",
87
+ "DinoV3Encoder",
88
+ "TimmVisionEncoder",
89
+ "build_encoder",
90
+ ]