zzzzzeee commited on
Commit
46ebef3
·
verified ·
1 Parent(s): c441492

Upload dinov2.py

Browse files
Files changed (1) hide show
  1. dinov2.py +396 -0
dinov2.py ADDED
@@ -0,0 +1,396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ #
3
+ # This source code is licensed under the Apache License, Version 2.0
4
+ # found in the LICENSE file in the root directory of this source tree.
5
+
6
+ # References:
7
+ # https://github.com/facebookresearch/dino/blob/main/vision_transformer.py
8
+ # https://github.com/rwightman/pytorch-image-models/tree/master/timm/models/vision_transformer.py
9
+
10
+ from functools import partial
11
+ import math
12
+ import logging
13
+ from typing import Sequence, Tuple, Union, Callable
14
+
15
+ import torch
16
+ import torch.nn as nn
17
+ import torch.utils.checkpoint
18
+ from torch.nn.init import trunc_normal_
19
+
20
+ from dinov2.layers import Mlp, PatchEmbed, SwiGLUFFNFused, MemEffAttention, NestedTensorBlock as Block
21
+
22
+
23
+ logger = logging.getLogger("dinov2")
24
+
25
+
26
+ def named_apply(fn: Callable, module: nn.Module, name="", depth_first=True, include_root=False) -> nn.Module:
27
+ if not depth_first and include_root:
28
+ fn(module=module, name=name)
29
+ for child_name, child_module in module.named_children():
30
+ child_name = ".".join((name, child_name)) if name else child_name
31
+ named_apply(fn=fn, module=child_module, name=child_name, depth_first=depth_first, include_root=True)
32
+ if depth_first and include_root:
33
+ fn(module=module, name=name)
34
+ return module
35
+
36
+
37
+ class BlockChunk(nn.ModuleList):
38
+ def forward(self, x):
39
+ for b in self:
40
+ x = b(x)
41
+ return x
42
+
43
+
44
+ class DinoVisionTransformer(nn.Module):
45
+ def __init__(
46
+ self,
47
+ img_size=224,
48
+ patch_size=16,
49
+ in_chans=3,
50
+ embed_dim=768,
51
+ depth=12,
52
+ num_heads=12,
53
+ mlp_ratio=4.0,
54
+ qkv_bias=True,
55
+ ffn_bias=True,
56
+ proj_bias=True,
57
+ drop_path_rate=0.0,
58
+ drop_path_uniform=False,
59
+ init_values=None, # for layerscale: None or 0 => no layerscale
60
+ embed_layer=PatchEmbed,
61
+ act_layer=nn.GELU,
62
+ block_fn=Block,
63
+ ffn_layer="mlp",
64
+ block_chunks=1,
65
+ num_register_tokens=0,
66
+ interpolate_antialias=False,
67
+ interpolate_offset=0.1,
68
+ ):
69
+ """
70
+ Args:
71
+ img_size (int, tuple): input image size
72
+ patch_size (int, tuple): patch size
73
+ in_chans (int): number of input channels
74
+ embed_dim (int): embedding dimension
75
+ depth (int): depth of transformer
76
+ num_heads (int): number of attention heads
77
+ mlp_ratio (int): ratio of mlp hidden dim to embedding dim
78
+ qkv_bias (bool): enable bias for qkv if True
79
+ proj_bias (bool): enable bias for proj in attn if True
80
+ ffn_bias (bool): enable bias for ffn if True
81
+ drop_path_rate (float): stochastic depth rate
82
+ drop_path_uniform (bool): apply uniform drop rate across blocks
83
+ weight_init (str): weight init scheme
84
+ init_values (float): layer-scale init values
85
+ embed_layer (nn.Module): patch embedding layer
86
+ act_layer (nn.Module): MLP activation layer
87
+ block_fn (nn.Module): transformer block class
88
+ ffn_layer (str): "mlp", "swiglu", "swiglufused" or "identity"
89
+ block_chunks: (int) split block sequence into block_chunks units for FSDP wrap
90
+ num_register_tokens: (int) number of extra cls tokens (so-called "registers")
91
+ interpolate_antialias: (str) flag to apply anti-aliasing when interpolating positional embeddings
92
+ interpolate_offset: (float) work-around offset to apply when interpolating positional embeddings
93
+ """
94
+ super().__init__()
95
+ norm_layer = partial(nn.LayerNorm, eps=1e-6)
96
+
97
+ self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models
98
+ self.num_tokens = 1
99
+ self.n_blocks = depth
100
+ self.num_heads = num_heads
101
+ self.patch_size = patch_size
102
+ self.num_register_tokens = num_register_tokens
103
+ self.interpolate_antialias = interpolate_antialias
104
+ self.interpolate_offset = interpolate_offset
105
+
106
+ self.patch_embed = embed_layer(img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim)
107
+ num_patches = self.patch_embed.num_patches
108
+
109
+ self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
110
+ self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + self.num_tokens, embed_dim))
111
+ assert num_register_tokens >= 0
112
+ self.register_tokens = (
113
+ nn.Parameter(torch.zeros(1, num_register_tokens, embed_dim)) if num_register_tokens else None
114
+ )
115
+
116
+ if drop_path_uniform is True:
117
+ dpr = [drop_path_rate] * depth
118
+ else:
119
+ dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule
120
+
121
+ if ffn_layer == "mlp":
122
+ logger.info("using MLP layer as FFN")
123
+ ffn_layer = Mlp
124
+ elif ffn_layer == "swiglufused" or ffn_layer == "swiglu":
125
+ logger.info("using SwiGLU layer as FFN")
126
+ ffn_layer = SwiGLUFFNFused
127
+ elif ffn_layer == "identity":
128
+ logger.info("using Identity layer as FFN")
129
+
130
+ def f(*args, **kwargs):
131
+ return nn.Identity()
132
+
133
+ ffn_layer = f
134
+ else:
135
+ raise NotImplementedError
136
+
137
+ blocks_list = [
138
+ block_fn(
139
+ dim=embed_dim,
140
+ num_heads=num_heads,
141
+ mlp_ratio=mlp_ratio,
142
+ qkv_bias=qkv_bias,
143
+ proj_bias=proj_bias,
144
+ ffn_bias=ffn_bias,
145
+ drop_path=dpr[i],
146
+ norm_layer=norm_layer,
147
+ act_layer=act_layer,
148
+ ffn_layer=ffn_layer,
149
+ init_values=init_values,
150
+ )
151
+ for i in range(depth)
152
+ ]
153
+ if block_chunks > 0:
154
+ self.chunked_blocks = True
155
+ chunked_blocks = []
156
+ chunksize = depth // block_chunks
157
+ for i in range(0, depth, chunksize):
158
+ # this is to keep the block index consistent if we chunk the block list
159
+ chunked_blocks.append([nn.Identity()] * i + blocks_list[i : i + chunksize])
160
+ self.blocks = nn.ModuleList([BlockChunk(p) for p in chunked_blocks])
161
+ else:
162
+ self.chunked_blocks = False
163
+ self.blocks = nn.ModuleList(blocks_list)
164
+
165
+ self.norm = norm_layer(embed_dim)
166
+ self.head = nn.Identity()
167
+
168
+ self.mask_token = nn.Parameter(torch.zeros(1, embed_dim))
169
+
170
+ self.init_weights()
171
+
172
+ def init_weights(self):
173
+ trunc_normal_(self.pos_embed, std=0.02)
174
+ nn.init.normal_(self.cls_token, std=1e-6)
175
+ if self.register_tokens is not None:
176
+ nn.init.normal_(self.register_tokens, std=1e-6)
177
+ named_apply(init_weights_vit_timm, self)
178
+
179
+ def interpolate_pos_encoding(self, x, w, h):
180
+ previous_dtype = x.dtype
181
+ npatch = x.shape[1] - 1
182
+ N = self.pos_embed.shape[1] - 1
183
+ if npatch == N and w == h:
184
+ return self.pos_embed
185
+ pos_embed = self.pos_embed.float()
186
+ class_pos_embed = pos_embed[:, 0]
187
+ patch_pos_embed = pos_embed[:, 1:]
188
+ dim = x.shape[-1]
189
+ w0 = w // self.patch_size
190
+ h0 = h // self.patch_size
191
+ M = int(math.sqrt(N)) # Recover the number of patches in each dimension
192
+ assert N == M * M
193
+ kwargs = {}
194
+ if self.interpolate_offset:
195
+ # Historical kludge: add a small number to avoid floating point error in the interpolation, see https://github.com/facebookresearch/dino/issues/8
196
+ # Note: still needed for backward-compatibility, the underlying operators are using both output size and scale factors
197
+ sx = float(w0 + self.interpolate_offset) / M
198
+ sy = float(h0 + self.interpolate_offset) / M
199
+ kwargs["scale_factor"] = (sx, sy)
200
+ else:
201
+ # Simply specify an output size instead of a scale factor
202
+ kwargs["size"] = (w0, h0)
203
+ patch_pos_embed = nn.functional.interpolate(
204
+ patch_pos_embed.reshape(1, M, M, dim).permute(0, 3, 1, 2),
205
+ mode="bicubic",
206
+ antialias=self.interpolate_antialias,
207
+ **kwargs,
208
+ )
209
+ assert (w0, h0) == patch_pos_embed.shape[-2:]
210
+ patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
211
+ return torch.cat((class_pos_embed.unsqueeze(0), patch_pos_embed), dim=1).to(previous_dtype)
212
+
213
+ def prepare_tokens_with_masks(self, x, masks=None):
214
+ B, nc, w, h = x.shape
215
+ x = self.patch_embed(x)
216
+ if masks is not None:
217
+ x = torch.where(masks.unsqueeze(-1), self.mask_token.to(x.dtype).unsqueeze(0), x)
218
+
219
+ x = torch.cat((self.cls_token.expand(x.shape[0], -1, -1), x), dim=1)
220
+ x = x + self.interpolate_pos_encoding(x, w, h)
221
+
222
+ if self.register_tokens is not None:
223
+ x = torch.cat(
224
+ (
225
+ x[:, :1],
226
+ self.register_tokens.expand(x.shape[0], -1, -1),
227
+ x[:, 1:],
228
+ ),
229
+ dim=1,
230
+ )
231
+
232
+ return x
233
+
234
+ def forward_features_list(self, x_list, masks_list):
235
+ x = [self.prepare_tokens_with_masks(x, masks) for x, masks in zip(x_list, masks_list)]
236
+ for blk in self.blocks:
237
+ x = blk(x)
238
+
239
+ all_x = x
240
+ output = []
241
+ for x, masks in zip(all_x, masks_list):
242
+ x_norm = self.norm(x)
243
+ output.append(
244
+ {
245
+ "x_norm_clstoken": x_norm[:, 0],
246
+ "x_norm_regtokens": x_norm[:, 1 : self.num_register_tokens + 1],
247
+ "x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1 :],
248
+ "x_prenorm": x,
249
+ "masks": masks,
250
+ }
251
+ )
252
+ return output
253
+
254
+ def forward_features(self, x, masks=None):
255
+ if isinstance(x, list):
256
+ return self.forward_features_list(x, masks)
257
+
258
+ x = self.prepare_tokens_with_masks(x, masks)
259
+
260
+ for blk in self.blocks:
261
+ x = blk(x)
262
+
263
+ x_norm = self.norm(x)
264
+ return {
265
+ "x_norm_clstoken": x_norm[:, 0],
266
+ "x_norm_regtokens": x_norm[:, 1 : self.num_register_tokens + 1],
267
+ "x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1 :],
268
+ "x_prenorm": x,
269
+ "masks": masks,
270
+ }
271
+
272
+ def _get_intermediate_layers_not_chunked(self, x, n=1):
273
+ x = self.prepare_tokens_with_masks(x)
274
+ # If n is an int, take the n last blocks. If it's a list, take them
275
+ output, total_block_len = [], len(self.blocks)
276
+ blocks_to_take = range(total_block_len - n, total_block_len) if isinstance(n, int) else n
277
+ for i, blk in enumerate(self.blocks):
278
+ x = blk(x)
279
+ if i in blocks_to_take:
280
+ output.append(x)
281
+ assert len(output) == len(blocks_to_take), f"only {len(output)} / {len(blocks_to_take)} blocks found"
282
+ return output
283
+
284
+ def _get_intermediate_layers_chunked(self, x, n=1):
285
+ x = self.prepare_tokens_with_masks(x)
286
+ output, i, total_block_len = [], 0, len(self.blocks[-1])
287
+ # If n is an int, take the n last blocks. If it's a list, take them
288
+ blocks_to_take = range(total_block_len - n, total_block_len) if isinstance(n, int) else n
289
+ for block_chunk in self.blocks:
290
+ for blk in block_chunk[i:]: # Passing the nn.Identity()
291
+ x = blk(x)
292
+ if i in blocks_to_take:
293
+ output.append(x)
294
+ i += 1
295
+ assert len(output) == len(blocks_to_take), f"only {len(output)} / {len(blocks_to_take)} blocks found"
296
+ return output
297
+
298
+ def get_intermediate_layers(
299
+ self,
300
+ x: torch.Tensor,
301
+ n: Union[int, Sequence] = 1, # Layers or n last layers to take
302
+ reshape: bool = False,
303
+ return_class_token: bool = False,
304
+ norm=True,
305
+ ) -> Tuple[Union[torch.Tensor, Tuple[torch.Tensor]]]:
306
+ if self.chunked_blocks:
307
+ outputs = self._get_intermediate_layers_chunked(x, n)
308
+ else:
309
+ outputs = self._get_intermediate_layers_not_chunked(x, n)
310
+ if norm:
311
+ outputs = [self.norm(out) for out in outputs]
312
+ class_tokens = [out[:, 0] for out in outputs]
313
+ outputs = [out[:, 1 + self.num_register_tokens :] for out in outputs]
314
+ if reshape:
315
+ B, _, w, h = x.shape
316
+ outputs = [
317
+ out.reshape(B, w // self.patch_size, h // self.patch_size, -1).permute(0, 3, 1, 2).contiguous()
318
+ for out in outputs
319
+ ]
320
+ if return_class_token:
321
+ return tuple(zip(outputs, class_tokens))
322
+ return tuple(outputs)
323
+
324
+ def forward(self, *args, is_training=False, **kwargs):
325
+ ret = self.forward_features(*args, **kwargs)
326
+ if is_training:
327
+ return ret
328
+ else:
329
+ return self.head(ret["x_norm_clstoken"])
330
+
331
+
332
+ def init_weights_vit_timm(module: nn.Module, name: str = ""):
333
+ """ViT weight initialization, original timm impl (for reproducibility)"""
334
+ if isinstance(module, nn.Linear):
335
+ trunc_normal_(module.weight, std=0.02)
336
+ if module.bias is not None:
337
+ nn.init.zeros_(module.bias)
338
+
339
+
340
+ def vit_small(patch_size=16, num_register_tokens=0, **kwargs):
341
+ model = DinoVisionTransformer(
342
+ patch_size=patch_size,
343
+ embed_dim=384,
344
+ depth=12,
345
+ num_heads=6,
346
+ mlp_ratio=4,
347
+ block_fn=partial(Block, attn_class=MemEffAttention),
348
+ num_register_tokens=num_register_tokens,
349
+ **kwargs,
350
+ )
351
+ return model
352
+
353
+
354
+ def vit_base(patch_size=16, num_register_tokens=0, **kwargs):
355
+ model = DinoVisionTransformer(
356
+ patch_size=patch_size,
357
+ embed_dim=768,
358
+ depth=12,
359
+ num_heads=12,
360
+ mlp_ratio=4,
361
+ block_fn=partial(Block, attn_class=MemEffAttention),
362
+ num_register_tokens=num_register_tokens,
363
+ **kwargs,
364
+ )
365
+ return model
366
+
367
+
368
+ def vit_large(patch_size=16, num_register_tokens=0, **kwargs):
369
+ model = DinoVisionTransformer(
370
+ patch_size=patch_size,
371
+ embed_dim=1024,
372
+ depth=24,
373
+ num_heads=16,
374
+ mlp_ratio=4,
375
+ block_fn=partial(Block, attn_class=MemEffAttention),
376
+ num_register_tokens=num_register_tokens,
377
+ **kwargs,
378
+ )
379
+ return model
380
+
381
+
382
+ def vit_giant2(patch_size=16, num_register_tokens=0, **kwargs):
383
+ """
384
+ Close to ViT-giant, with embed-dim 1536 and 24 heads => embed-dim per head 64
385
+ """
386
+ model = DinoVisionTransformer(
387
+ patch_size=patch_size,
388
+ embed_dim=1536,
389
+ depth=40,
390
+ num_heads=24,
391
+ mlp_ratio=4,
392
+ block_fn=partial(Block, attn_class=MemEffAttention),
393
+ num_register_tokens=num_register_tokens,
394
+ **kwargs,
395
+ )
396
+ return model