|
|
|
|
|
|
|
|
|
|
| import math
|
|
|
| from functools import partial
|
| from typing import Sequence, Tuple, Union, Callable
|
|
|
| import torch
|
| import torch.nn as nn
|
| import torch.utils.checkpoint
|
|
|
| from torch.nn.init import trunc_normal_
|
| from torch.nn.functional import interpolate
|
|
|
| from hf_src.layers import (
|
| Mlp,
|
| PatchEmbed,
|
| SwiGLUFFNFused,
|
| MemEffAttention,
|
| NestedTensorBlock as Block,
|
| LayerScale,
|
| RMSNorm,
|
| )
|
|
|
|
|
| def named_apply(
|
| fn: Callable, module: nn.Module, name="", depth_first=True, include_root=False
|
| ) -> nn.Module:
|
| if not depth_first and include_root:
|
| fn(module=module, name=name)
|
| for child_name, child_module in module.named_children():
|
| child_name = ".".join((name, child_name)) if name else child_name
|
| named_apply(
|
| fn=fn,
|
| module=child_module,
|
| name=child_name,
|
| depth_first=depth_first,
|
| include_root=True,
|
| )
|
| if depth_first and include_root:
|
| fn(module=module, name=name)
|
| return module
|
|
|
|
|
| class BlockChunk(nn.ModuleList):
|
| def forward(self, x, return_attention=False):
|
|
|
| for i, b in enumerate(self):
|
| if i < len(self) - 1:
|
| x = b(x)
|
| else:
|
| return b(x, return_attention=return_attention)
|
| return x
|
|
|
|
|
| class ViTv2(nn.Module):
|
| def __init__(
|
| self,
|
| *,
|
| img_size=518,
|
| patch_size=16,
|
| in_chans=3,
|
| embed_dim=768,
|
| depth=12,
|
| num_heads=12,
|
| mlp_ratio=4.0,
|
| qkv_bias=True,
|
| ffn_bias=True,
|
| proj_bias=True,
|
| drop_path_rate=0.0,
|
| drop_path_uniform=True,
|
| init_values=None,
|
| embed_layer=PatchEmbed,
|
| act_layer=nn.GELU,
|
| block_fn=Block,
|
| ffn_layer="mlp",
|
| block_chunks=0,
|
| num_register_tokens=0,
|
| interpolate_antialias=False,
|
| interpolate_offset=0.1,
|
| num_classes=None,
|
| **ignored_kwargs,
|
| ):
|
| """
|
| Args:
|
| img_size (int, tuple): input image size
|
| patch_size (int, tuple): patch size
|
| in_chans (int): number of input channels
|
| embed_dim (int): embedding dimension
|
| depth (int): depth of transformer
|
| num_heads (int): number of attention heads
|
| mlp_ratio (int): ratio of mlp hidden dim to embedding dim
|
| qkv_bias (bool): enable bias for qkv if True
|
| proj_bias (bool): enable bias for proj in attn if True
|
| ffn_bias (bool): enable bias for ffn if True
|
| drop_path_rate (float): stochastic depth rate
|
| drop_path_uniform (bool): apply uniform drop rate across blocks
|
| weight_init (str): weight init scheme
|
| init_values (float): layer-scale init values
|
| embed_layer (nn.Module): patch embedding layer
|
| act_layer (nn.Module): MLP activation layer
|
| block_fn (nn.Module): transformer block class
|
| ffn_layer (str): "mlp", "swiglu", "swiglufused" or "identity"
|
| block_chunks: (int) split block sequence into block_chunks units for FSDP wrap
|
| num_register_tokens: (int) number of extra cls tokens (so-called "registers")
|
| interpolate_antialias: (str) flag to apply anti-aliasing when interpolating positional embeddings
|
| interpolate_offset: (float) work-around offset to apply when interpolating positional embeddings
|
| """
|
| super().__init__(**ignored_kwargs)
|
|
|
| norm_layer = partial(nn.LayerNorm, eps=1e-6)
|
| self.img_size = img_size
|
|
|
| self.num_features = self.embed_dim = embed_dim
|
|
|
| self.num_tokens = 1
|
| self.n_blocks = depth
|
| self.num_heads = num_heads
|
| self.patch_size = patch_size
|
| self.num_register_tokens = num_register_tokens
|
| self.interpolate_antialias = interpolate_antialias
|
| self.interpolate_offset = interpolate_offset
|
|
|
| self.patch_embed = embed_layer(
|
| img_size=img_size,
|
| patch_size=patch_size,
|
| in_chans=in_chans,
|
| embed_dim=embed_dim,
|
| )
|
| num_patches = self.patch_embed.num_patches
|
|
|
| self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
|
| self.pos_embed = nn.Parameter(
|
| torch.zeros(1, num_patches + self.num_tokens, embed_dim)
|
| )
|
| assert num_register_tokens >= 0
|
| self.register_tokens = (
|
| nn.Parameter(torch.zeros(1, num_register_tokens, embed_dim))
|
| if num_register_tokens
|
| else None
|
| )
|
|
|
| if drop_path_uniform is True:
|
| dpr = [drop_path_rate] * depth
|
| else:
|
| dpr = [
|
| x.item() for x in torch.linspace(0, drop_path_rate, depth)
|
| ]
|
|
|
| if ffn_layer == "mlp":
|
| ffn_layer = Mlp
|
| elif ffn_layer == "swiglufused" or ffn_layer == "swiglu":
|
| ffn_layer = SwiGLUFFNFused
|
| elif ffn_layer == "identity":
|
|
|
| def f(*args, **kwargs):
|
| return nn.Identity()
|
|
|
| ffn_layer = f
|
| else:
|
| raise NotImplementedError
|
|
|
| blocks_list = [
|
| block_fn(
|
| dim=embed_dim,
|
| num_heads=num_heads,
|
| mlp_ratio=mlp_ratio,
|
| qkv_bias=qkv_bias,
|
| proj_bias=proj_bias,
|
| ffn_bias=ffn_bias,
|
| drop_path=dpr[i],
|
| norm_layer=norm_layer,
|
| act_layer=act_layer,
|
| ffn_layer=ffn_layer,
|
| init_values=init_values,
|
| )
|
| for i in range(depth)
|
| ]
|
| if block_chunks > 0:
|
| self.chunked_blocks = True
|
| chunked_blocks = []
|
| chunksize = depth // block_chunks
|
| for i in range(0, depth, chunksize):
|
|
|
| chunked_blocks.append(
|
| [nn.Identity()] * i + blocks_list[i : i + chunksize]
|
| )
|
| self.blocks = nn.ModuleList([BlockChunk(p) for p in chunked_blocks])
|
| else:
|
| self.chunked_blocks = False
|
| self.blocks = nn.ModuleList(blocks_list)
|
|
|
| self.mask_token = None
|
| self.norm = norm_layer(embed_dim)
|
| self.norm_patch = None
|
|
|
| self.head = (
|
| nn.Identity() if num_classes is None else nn.Linear(embed_dim, num_classes)
|
| )
|
|
|
|
|
| self.init_weights()
|
|
|
| def init_weights(self):
|
| trunc_normal_(self.pos_embed, std=0.02)
|
| nn.init.normal_(self.cls_token, std=1e-6)
|
| if self.register_tokens is not None:
|
| nn.init.normal_(self.register_tokens, std=1e-6)
|
| if self.mask_token is not None:
|
| nn.init.zeros_(self.mask_token)
|
| named_apply(init_weights_vit, self)
|
|
|
| def interpolate_pos_encoding(self, x, w, h):
|
| previous_dtype = x.dtype
|
| npatch = x.shape[1] - 1
|
| N = self.pos_embed.shape[1] - 1
|
| if npatch == N and w == h:
|
| return self.pos_embed
|
| pos_embed = self.pos_embed.float()
|
| class_pos_embed = pos_embed[:, 0]
|
| patch_pos_embed = pos_embed[:, 1:]
|
| dim = x.shape[-1]
|
| w0 = w // self.patch_size
|
| h0 = h // self.patch_size
|
|
|
|
|
| w0, h0 = w0 + self.interpolate_offset, h0 + self.interpolate_offset
|
|
|
| sqrt_N = math.sqrt(N)
|
| sx, sy = float(w0) / sqrt_N, float(h0) / sqrt_N
|
| patch_pos_embed = interpolate(
|
| patch_pos_embed.reshape(1, int(sqrt_N), int(sqrt_N), dim).permute(
|
| 0, 3, 1, 2
|
| ),
|
| scale_factor=(sx, sy),
|
| mode="bicubic",
|
|
|
| )
|
|
|
| assert int(w0) == patch_pos_embed.shape[-2]
|
| assert int(h0) == patch_pos_embed.shape[-1]
|
| patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
|
| return torch.cat((class_pos_embed.unsqueeze(0), patch_pos_embed), dim=1).to(
|
| previous_dtype
|
| )
|
|
|
| def prepare_tokens_with_masks(self, x, masks=None):
|
| B, nc, w, h = x.shape
|
| x = self.patch_embed(x)
|
| if masks is not None:
|
| x = torch.where(
|
| masks.unsqueeze(-1), self.mask_token.to(x.dtype).unsqueeze(0), x
|
| )
|
|
|
| x = torch.cat((self.cls_token.expand(x.shape[0], -1, -1), x), dim=1)
|
| x = x + self.interpolate_pos_encoding(x, w, h)
|
|
|
| if self.register_tokens is not None:
|
| x = torch.cat(
|
| (
|
| x[:, :1],
|
| self.register_tokens.expand(x.shape[0], -1, -1),
|
| x[:, 1:],
|
| ),
|
| dim=1,
|
| )
|
|
|
| return x
|
|
|
| def forward_features_list(self, x_list, masks_list):
|
| x = [
|
| self.prepare_tokens_with_masks(x, masks)
|
| for x, masks in zip(x_list, masks_list)
|
| ]
|
| for blk in self.blocks:
|
| x = blk(x)
|
|
|
| all_x = x
|
| output = []
|
| for x, masks in zip(all_x, masks_list):
|
| cls_tokens = self.norm(x[:, : self.num_register_tokens + 1])
|
|
|
| if self.norm_patch is None:
|
| patch_tokens = self.norm(x[:, self.num_register_tokens + 1 :])
|
| else:
|
| patch_tokens = self.norm_patch(x[:, self.num_register_tokens + 1 :])
|
|
|
| output.append(
|
| {
|
| "latent": cls_tokens[:, 0],
|
| "patch_latent": patch_tokens,
|
| "raw_latent": x[:, 0],
|
| }
|
| )
|
| return output
|
|
|
| def forward_features(self, x, masks=None, last_self_attention=False):
|
| if isinstance(x, list):
|
| return self.forward_features_list(x, masks)
|
|
|
| x = self.prepare_tokens_with_masks(x, masks)
|
|
|
| for i, blk in enumerate(self.blocks):
|
| if i < len(self.blocks) - 1:
|
| x = blk(x)
|
| else:
|
| x = blk(x, return_attention=last_self_attention)
|
|
|
| attn = None
|
| if last_self_attention:
|
| x, attn = x
|
|
|
|
|
| attn = attn[:, :, 0, self.num_register_tokens + 1 :]
|
|
|
| cls_tokens = self.norm(x[:, : self.num_register_tokens + 1])
|
|
|
| if self.norm_patch is None:
|
| patch_tokens = self.norm(x[:, self.num_register_tokens + 1 :])
|
| else:
|
| patch_tokens = self.norm_patch(x[:, self.num_register_tokens + 1 :])
|
|
|
| return {
|
| "latent": cls_tokens[:, 0],
|
| "patch_latent": patch_tokens,
|
| "raw_latent": x[:, 0],
|
| "last_self_attention": attn,
|
| "logits": self.head(cls_tokens[:, 0]),
|
| }
|
|
|
| def forward_head(self, x):
|
|
|
| x = self.projection_head(x)
|
| if self.l2_norm:
|
| x = nn.functional.normalize(x, dim=1, p=2)
|
| return x
|
|
|
| def _get_intermediate_layers_not_chunked(self, x, n=1):
|
| x = self.prepare_tokens_with_masks(x)
|
|
|
| output, total_block_len = [], len(self.blocks)
|
| blocks_to_take = (
|
| range(total_block_len - n, total_block_len) if isinstance(n, int) else n
|
| )
|
| for i, blk in enumerate(self.blocks):
|
| x = blk(x)
|
| if i in blocks_to_take:
|
| output.append(x)
|
| assert len(output) == len(
|
| blocks_to_take
|
| ), f"only {len(output)} / {len(blocks_to_take)} blocks found"
|
| return output
|
|
|
| def _get_intermediate_layers_chunked(self, x, n=1):
|
| x = self.prepare_tokens_with_masks(x)
|
| output, i, total_block_len = [], 0, len(self.blocks[-1])
|
|
|
| blocks_to_take = (
|
| range(total_block_len - n, total_block_len) if isinstance(n, int) else n
|
| )
|
| for block_chunk in self.blocks:
|
| for blk in block_chunk[i:]:
|
| x = blk(x)
|
| if i in blocks_to_take:
|
| output.append(x)
|
| i += 1
|
| assert len(output) == len(
|
| blocks_to_take
|
| ), f"only {len(output)} / {len(blocks_to_take)} blocks found"
|
| return output
|
|
|
| def get_intermediate_layers(
|
| self,
|
| x: torch.Tensor,
|
| n: Union[int, Sequence] = 1,
|
| reshape: bool = False,
|
| return_class_token: bool = False,
|
| norm=True,
|
| ) -> Tuple[Union[torch.Tensor, Tuple[torch.Tensor]]]:
|
| if self.chunked_blocks:
|
| outputs = self._get_intermediate_layers_chunked(x, n)
|
| else:
|
| outputs = self._get_intermediate_layers_not_chunked(x, n)
|
|
|
| class_tokens = [
|
| (
|
| out[:, 0]
|
| if not norm
|
| else self.norm(out[:, : 1 + self.num_register_tokens])[:, 0]
|
| )
|
| for out in outputs
|
| ]
|
| outputs = [
|
| (
|
| out[:, 1 + self.num_register_tokens :]
|
| if not norm
|
| else (
|
| self.norm(out[:, self.num_register_tokens + 1 :])
|
| if self.norm_patch is None
|
| else self.norm_patch(out[:, self.num_register_tokens + 1 :])
|
| )
|
| )
|
| for out in outputs
|
| ]
|
|
|
| if reshape:
|
| B, _, w, h = x.shape
|
| outputs = [
|
| out.reshape(B, w // self.patch_size, h // self.patch_size, -1)
|
| .permute(0, 3, 1, 2)
|
| .contiguous()
|
| for out in outputs
|
| ]
|
| if return_class_token:
|
| return tuple(zip(outputs, class_tokens))
|
| return tuple(outputs)
|
|
|
| def forward(self, xs, masks=None, last_self_attention=False, **kwargs):
|
| if not (isinstance(xs, list) or isinstance(xs, tuple)):
|
| return self.forward_features(xs, masks, last_self_attention)
|
|
|
| if masks is None:
|
| masks = [None] * len(xs)
|
|
|
| return self.forward_features_list(xs, masks)
|
|
|
| def forward_backbone(self, x, last_self_attention=False):
|
| out_dict = self.forward_features(x, last_self_attention=last_self_attention)
|
| cls_token = out_dict["latent"]
|
| x = out_dict["patch_latent"]
|
|
|
| x = torch.cat((cls_token.unsqueeze(1), x), dim=1)
|
| if last_self_attention:
|
| return x, out_dict["last_self_attention"]
|
| return x
|
|
|
| def get_last_selfattention(self, x, masks=None):
|
| """
|
| Adapted from https://gitlab.com/ziegleto-machine-learning/dino/-/tree/main/
|
| """
|
| if isinstance(x, list):
|
| raise NotImplementedError("Not implemented for list of inputs")
|
|
|
|
|
| x = self.prepare_tokens_with_masks(x, masks)
|
|
|
|
|
| for i, blk in enumerate(self.blocks):
|
| if i < len(self.blocks) - 1:
|
| x = blk(x)
|
| else:
|
| _, attn = blk(x, return_attention=True)
|
| return attn
|
|
|
|
|
| def init_weights_vit(module: nn.Module, name: str = ""):
|
| if isinstance(module, nn.Linear):
|
| torch.nn.init.trunc_normal_(module.weight, std=0.02)
|
| if module.bias is not None:
|
| nn.init.zeros_(module.bias)
|
| if hasattr(module, "bias_mask") and module.bias_mask is not None:
|
| o = module.out_features
|
| module.bias_mask.fill_(1)
|
| module.bias_mask[o // 3 : 2 * o // 3].fill_(0)
|
| if isinstance(module, nn.LayerNorm):
|
| module.reset_parameters()
|
| if isinstance(module, LayerScale):
|
| module.reset_parameters()
|
| if isinstance(module, PatchEmbed):
|
| module.reset_parameters()
|
| if isinstance(module, RMSNorm):
|
| module.reset_parameters()
|
|
|