| |
| |
| |
| |
|
|
| |
| |
| |
|
|
| import math |
| from typing import Callable, List, Sequence, Tuple, Union |
| import numpy as np |
| import torch |
| import torch.nn as nn |
| import torch.utils.checkpoint |
| from einops import rearrange |
|
|
| from ...utils.logger import logger |
|
|
| from .layers import LayerScale |
| from .layers import Mlp |
| from .layers import ( |
| Block, |
| PatchEmbed, |
| PositionGetter, |
| RotaryPositionEmbedding2D, |
| SwiGLUFFNFused, |
| ) |
| from ...model.reference_view_selector import ( |
| RefViewStrategy, |
| select_reference_view, |
| reorder_by_reference, |
| restore_original_order, |
| ) |
| from ...utils.constants import THRESH_FOR_REF_SELECTION |
|
|
| |
|
|
|
|
| def get_1d_sincos_pos_embed_from_grid(embed_dim, pos): |
| """ |
| embed_dim: output dimension for each position |
| pos: a list of positions to be encoded: size (M,) |
| out: (M, D) |
| """ |
| assert embed_dim % 2 == 0 |
| omega = np.arange(embed_dim // 2, dtype=float) |
| omega /= embed_dim / 2.0 |
| omega = 1.0 / 10000**omega |
|
|
| pos = pos.reshape(-1) |
| out = np.einsum("m,d->md", pos, omega) |
|
|
| emb_sin = np.sin(out) |
| emb_cos = np.cos(out) |
|
|
| emb = np.concatenate([emb_sin, emb_cos], axis=1) |
| return emb |
|
|
|
|
| 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): |
| for b in self: |
| x = b(x) |
| return x |
|
|
|
|
| class DinoVisionTransformer(nn.Module): |
| def __init__( |
| self, |
| img_size=224, |
| 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=False, |
| init_values=1.0, |
| embed_layer=PatchEmbed, |
| act_layer=nn.GELU, |
| block_fn=Block, |
| ffn_layer="mlp", |
| block_chunks=1, |
| num_register_tokens=0, |
| interpolate_antialias=False, |
| interpolate_offset=0.1, |
| alt_start=-1, |
| qknorm_start=-1, |
| rope_start=-1, |
| rope_freq=100, |
| plus_cam_token=False, |
| cat_token=True, |
| ): |
| """ |
| 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 |
| 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__() |
| self.patch_start_idx = 1 |
| norm_layer = nn.LayerNorm |
| self.num_features = self.embed_dim = ( |
| embed_dim |
| ) |
| self.alt_start = alt_start |
| self.qknorm_start = qknorm_start |
| self.rope_start = rope_start |
| self.cat_token = cat_token |
| 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)) |
| if self.alt_start != -1: |
| self.camera_token = nn.Parameter(torch.randn(1, 2, 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": |
| logger.info("using MLP layer as FFN") |
| ffn_layer = Mlp |
| elif ffn_layer == "swiglufused" or ffn_layer == "swiglu": |
| logger.info("using SwiGLU layer as FFN") |
| ffn_layer = SwiGLUFFNFused |
| elif ffn_layer == "identity": |
| logger.info("using Identity layer as FFN") |
|
|
| def f(*args, **kwargs): |
| return nn.Identity() |
|
|
| ffn_layer = f |
| else: |
| raise NotImplementedError |
|
|
| if self.rope_start != -1: |
| self.rope = RotaryPositionEmbedding2D(frequency=rope_freq) if rope_freq > 0 else None |
| self.position_getter = PositionGetter() if self.rope is not None else None |
| else: |
| self.rope = None |
| 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, |
| qk_norm=i >= qknorm_start if qknorm_start != -1 else False, |
| rope=self.rope if i >= rope_start and rope_start != -1 else None, |
| ) |
| for i in range(depth) |
| ] |
| self.blocks = nn.ModuleList(blocks_list) |
| self.norm = norm_layer(embed_dim) |
|
|
| 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.to(dtype=previous_dtype) |
| 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 |
| M = int(math.sqrt(N)) |
| assert N == M * M |
| kwargs = {} |
| if self.interpolate_offset: |
| |
| |
| |
| |
| sx = float(w0 + self.interpolate_offset) / M |
| sy = float(h0 + self.interpolate_offset) / M |
| kwargs["scale_factor"] = (sx, sy) |
| else: |
| |
| kwargs["size"] = (w0, h0) |
| patch_pos_embed = nn.functional.interpolate( |
| patch_pos_embed.reshape(1, M, M, dim).permute(0, 3, 1, 2), |
| mode="bicubic", |
| antialias=self.interpolate_antialias, |
| **kwargs, |
| ) |
| assert (w0, h0) == patch_pos_embed.shape[-2:] |
| 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_cls_token(self, B, S, dtype): |
| cls_token = self.cls_token.to(dtype=dtype).expand(B, S, -1) |
| cls_token = cls_token.reshape(B * S, -1, self.embed_dim) |
| return cls_token |
|
|
| def prepare_tokens_with_masks(self, x, masks=None, cls_token=None, **kwargs): |
| B, S, nc, w, h = x.shape |
| x = rearrange(x, "b s c h w -> (b s) c h w") |
| 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) |
| cls_token = self.prepare_cls_token(B, S, dtype=x.dtype) |
| x = torch.cat((cls_token, x), dim=1) |
| x.add_(self.interpolate_pos_encoding(x, w, h)) |
| if self.register_tokens is not None: |
| x = torch.cat( |
| ( |
| x[:, :1], |
| self.register_tokens.to(dtype=x.dtype).expand(x.shape[0], -1, -1), |
| x[:, 1:], |
| ), |
| dim=1, |
| ) |
| x = rearrange(x, "(b s) n c -> b s n c", b=B, s=S) |
| return x |
|
|
| def _prepare_rope(self, B, S, H, W, device): |
| pos = None |
| pos_nodiff = None |
| if self.rope is not None: |
| pos = self.position_getter( |
| B * S, H // self.patch_size, W // self.patch_size, device=device |
| ) |
| pos = rearrange(pos, "(b s) n c -> b s n c", b=B) |
| pos_nodiff = torch.zeros_like(pos).to(pos.dtype) |
| if self.patch_start_idx > 0: |
| pos = pos + 1 |
| pos_special = torch.zeros(B * S, self.patch_start_idx, 2).to(device).to(pos.dtype) |
| pos_special = rearrange(pos_special, "(b s) n c -> b s n c", b=B) |
| pos = torch.cat([pos_special, pos], dim=2) |
| pos_nodiff = pos_nodiff + 1 |
| pos_nodiff = torch.cat([pos_special, pos_nodiff], dim=2) |
| return pos, pos_nodiff |
|
|
| def _get_intermediate_layers_not_chunked(self, x, n=1, export_feat_layers=[], **kwargs): |
| B, S, _, H, W = x.shape |
| x = self.prepare_tokens_with_masks(x) |
| output, total_block_len, aux_output = [], len(self.blocks), [] |
| blocks_to_take = range(total_block_len - n, total_block_len) if isinstance(n, int) else n |
| blocks_to_take = set(blocks_to_take) |
| export_feat_layers = set(export_feat_layers) |
| pos, pos_nodiff = self._prepare_rope(B, S, H, W, x.device) |
| local_x = None |
| b_idx = None |
| cam_token_arg = kwargs.get("cam_token", None) |
| attn_mask = kwargs.get("attn_mask", None) |
| ref_view_strategy = kwargs.get("ref_view_strategy", "saddle_balanced") |
|
|
| for i, blk in enumerate(self.blocks): |
| if i < self.rope_start or self.rope is None: |
| g_pos, l_pos = None, None |
| else: |
| g_pos = pos_nodiff |
| l_pos = pos |
|
|
| if self.alt_start != -1 and (i == self.alt_start - 1) and x.shape[1] >= THRESH_FOR_REF_SELECTION and cam_token_arg is None: |
| |
| logger.info(f"Selecting reference view using strategy: {ref_view_strategy}") |
| b_idx = select_reference_view(x, strategy=ref_view_strategy) |
| |
| x = reorder_by_reference(x, b_idx) |
| if local_x is not None: |
| local_x = reorder_by_reference(local_x, b_idx) |
|
|
| if self.alt_start != -1 and i == self.alt_start: |
| if cam_token_arg is not None: |
| logger.info("Using camera conditions provided by the user") |
| cam_token = cam_token_arg |
| else: |
| ref_token = self.camera_token[:, :1].expand(B, -1, -1) |
| src_token = self.camera_token[:, 1:].expand(B, S - 1, -1) |
| cam_token = torch.cat([ref_token, src_token], dim=1) |
| del ref_token, src_token |
| x[:, :, 0] = cam_token |
| del cam_token |
|
|
| is_global = self.alt_start != -1 and i >= self.alt_start and i % 2 == 1 |
| if is_global: |
| x_list = [x] |
| del x |
| x = self.process_attention(x_list, blk, "global", pos=g_pos, attn_mask=attn_mask) |
| else: |
| x_list = [x] |
| del x |
| x = self.process_attention(x_list, blk, "local", pos=l_pos) |
| local_x = x |
|
|
| if i in blocks_to_take: |
| out_x = torch.cat([local_x, x], dim=-1) if self.cat_token else x |
| |
| if x.shape[1] >= THRESH_FOR_REF_SELECTION and self.alt_start != -1 and b_idx is not None: |
| out_x = restore_original_order(out_x, b_idx) |
| output.append((out_x[:, :, 0], out_x)) |
| del out_x |
| if i in export_feat_layers: |
| aux_output.append(x) |
| if is_global: |
| local_x = None |
| del g_pos, l_pos |
| return output, aux_output |
|
|
| def process_attention(self, x, block, attn_type="global", pos=None, attn_mask=None): |
| if isinstance(x, list): |
| x_list = x |
| x = x_list[0] |
| x_list.clear() |
| b, s, n = x.shape[:3] |
| if attn_type == "local": |
| x = rearrange(x, "b s n c -> (b s) n c") |
| if pos is not None: |
| pos = rearrange(pos, "b s n c -> (b s) n c") |
| elif attn_type == "global": |
| x = rearrange(x, "b s n c -> b (s n) c") |
| if pos is not None: |
| pos = rearrange(pos, "b s n c -> b (s n) c") |
| else: |
| raise ValueError(f"Invalid attention type: {attn_type}") |
|
|
| x_list = [x] |
| del x |
| x = block(x_list, pos=pos, attn_mask=attn_mask) |
| del pos |
|
|
| if attn_type == "local": |
| x = rearrange(x, "(b s) n c -> b s n c", b=b, s=s) |
| elif attn_type == "global": |
| x = rearrange(x, "b (s n) c -> b s n c", b=b, s=s) |
| return x |
|
|
| def _normalize_intermediate_output(self, out: torch.Tensor, allow_inplace: bool = True) -> torch.Tensor: |
| allow_inplace = allow_inplace and not torch.is_grad_enabled() |
| if out.shape[-1] == self.embed_dim: |
| normed = self.norm(out) |
| if allow_inplace: |
| out.copy_(normed) |
| del normed |
| return out |
| return normed |
| if out.shape[-1] == (self.embed_dim * 2): |
| if allow_inplace: |
| right = out[..., self.embed_dim :] |
| normed = self.norm(right) |
| right.copy_(normed) |
| del normed, right |
| return out |
| return torch.cat([out[..., : self.embed_dim], self.norm(out[..., self.embed_dim :])], dim=-1) |
| raise ValueError(f"Invalid output shape: {out.shape}") |
|
|
| def get_intermediate_layers( |
| self, |
| x: torch.Tensor, |
| n: Union[int, Sequence] = 1, |
| export_feat_layers: List[int] = [], |
| **kwargs, |
| ) -> Tuple[Union[torch.Tensor, Tuple[torch.Tensor]]]: |
| outputs, aux_outputs = self._get_intermediate_layers_not_chunked( |
| x, n, export_feat_layers=export_feat_layers, **kwargs |
| ) |
| camera_tokens, processed_outputs = [], [] |
| crop_start = 1 + self.num_register_tokens |
| aux_storage_ptrs = {out.untyped_storage().data_ptr() for out in aux_outputs} |
| for idx, (camera_token, out) in enumerate(outputs): |
| camera_tokens.append(camera_token.clone()) |
| allow_inplace = out.untyped_storage().data_ptr() not in aux_storage_ptrs |
| out = self._normalize_intermediate_output(out, allow_inplace=allow_inplace) |
| processed_outputs.append(out[..., crop_start:, :]) |
| outputs[idx] = None |
| outputs.clear() |
|
|
| for idx, out in enumerate(aux_outputs): |
| out = self._normalize_intermediate_output(out) |
| aux_outputs[idx] = out[..., crop_start:, :] |
| return tuple(zip(processed_outputs, camera_tokens)), aux_outputs |
|
|
|
|
| def vit_small(patch_size=16, num_register_tokens=0, depth=12, **kwargs): |
| model = DinoVisionTransformer( |
| patch_size=patch_size, |
| embed_dim=384, |
| depth=depth, |
| num_heads=6, |
| mlp_ratio=4, |
| |
| num_register_tokens=num_register_tokens, |
| **kwargs, |
| ) |
| return model |
|
|
|
|
| def vit_base(patch_size=16, num_register_tokens=0, depth=12, **kwargs): |
| model = DinoVisionTransformer( |
| patch_size=patch_size, |
| embed_dim=768, |
| depth=depth, |
| num_heads=12, |
| mlp_ratio=4, |
| |
| num_register_tokens=num_register_tokens, |
| **kwargs, |
| ) |
| return model |
|
|
|
|
| def vit_large(patch_size=16, num_register_tokens=0, depth=24, **kwargs): |
| model = DinoVisionTransformer( |
| patch_size=patch_size, |
| embed_dim=1024, |
| depth=depth, |
| num_heads=16, |
| mlp_ratio=4, |
| |
| num_register_tokens=num_register_tokens, |
| **kwargs, |
| ) |
| return model |
|
|
|
|
| def vit_giant2(patch_size=16, num_register_tokens=0, depth=40, **kwargs): |
| """ |
| Close to ViT-giant, with embed-dim 1536 and 24 heads => embed-dim per head 64 |
| """ |
| model = DinoVisionTransformer( |
| patch_size=patch_size, |
| embed_dim=1536, |
| depth=depth, |
| num_heads=24, |
| mlp_ratio=4, |
| num_register_tokens=num_register_tokens, |
| **kwargs, |
| ) |
| return model |
|
|