|
|
|
|
|
|
|
|
|
|
|
from collections import OrderedDict |
|
from functools import partial |
|
|
|
import torch |
|
import torch.nn as nn |
|
from timm.layers import trunc_normal_ |
|
|
|
from mmaudio.ext.synchformer import vit_helper |
|
|
|
|
|
class VisionTransformer(nn.Module): |
|
""" Vision Transformer with support for patch or hybrid CNN input stage """ |
|
|
|
def __init__(self, cfg): |
|
super().__init__() |
|
self.img_size = cfg.DATA.TRAIN_CROP_SIZE |
|
self.patch_size = cfg.VIT.PATCH_SIZE |
|
self.in_chans = cfg.VIT.CHANNELS |
|
if cfg.TRAIN.DATASET == "Epickitchens": |
|
self.num_classes = [97, 300] |
|
else: |
|
self.num_classes = cfg.MODEL.NUM_CLASSES |
|
self.embed_dim = cfg.VIT.EMBED_DIM |
|
self.depth = cfg.VIT.DEPTH |
|
self.num_heads = cfg.VIT.NUM_HEADS |
|
self.mlp_ratio = cfg.VIT.MLP_RATIO |
|
self.qkv_bias = cfg.VIT.QKV_BIAS |
|
self.drop_rate = cfg.VIT.DROP |
|
self.drop_path_rate = cfg.VIT.DROP_PATH |
|
self.head_dropout = cfg.VIT.HEAD_DROPOUT |
|
self.video_input = cfg.VIT.VIDEO_INPUT |
|
self.temporal_resolution = cfg.VIT.TEMPORAL_RESOLUTION |
|
self.use_mlp = cfg.VIT.USE_MLP |
|
self.num_features = self.embed_dim |
|
norm_layer = partial(nn.LayerNorm, eps=1e-6) |
|
self.attn_drop_rate = cfg.VIT.ATTN_DROPOUT |
|
self.head_act = cfg.VIT.HEAD_ACT |
|
self.cfg = cfg |
|
|
|
|
|
self.patch_embed = vit_helper.PatchEmbed(img_size=224, |
|
patch_size=self.patch_size, |
|
in_chans=self.in_chans, |
|
embed_dim=self.embed_dim) |
|
|
|
|
|
self.patch_embed_3d = vit_helper.PatchEmbed3D(img_size=self.img_size, |
|
temporal_resolution=self.temporal_resolution, |
|
patch_size=self.patch_size, |
|
in_chans=self.in_chans, |
|
embed_dim=self.embed_dim, |
|
z_block_size=self.cfg.VIT.PATCH_SIZE_TEMP) |
|
self.patch_embed_3d.proj.weight.data = torch.zeros_like( |
|
self.patch_embed_3d.proj.weight.data) |
|
|
|
|
|
if self.video_input: |
|
num_patches = self.patch_embed.num_patches * self.temporal_resolution |
|
else: |
|
num_patches = self.patch_embed.num_patches |
|
self.num_patches = num_patches |
|
|
|
|
|
self.cls_token = nn.Parameter(torch.zeros(1, 1, self.embed_dim)) |
|
trunc_normal_(self.cls_token, std=.02) |
|
|
|
|
|
self.pos_embed = nn.Parameter( |
|
torch.zeros(1, self.patch_embed.num_patches + 1, self.embed_dim)) |
|
self.pos_drop = nn.Dropout(p=cfg.VIT.POS_DROPOUT) |
|
trunc_normal_(self.pos_embed, std=.02) |
|
|
|
if self.cfg.VIT.POS_EMBED == "joint": |
|
self.st_embed = nn.Parameter(torch.zeros(1, num_patches + 1, self.embed_dim)) |
|
trunc_normal_(self.st_embed, std=.02) |
|
elif self.cfg.VIT.POS_EMBED == "separate": |
|
self.temp_embed = nn.Parameter(torch.zeros(1, self.temporal_resolution, self.embed_dim)) |
|
|
|
|
|
dpr = [x.item() for x in torch.linspace(0, self.drop_path_rate, self.depth)] |
|
if self.cfg.VIT.ATTN_LAYER == "divided": |
|
self.blocks = nn.ModuleList([ |
|
vit_helper.DividedSpaceTimeBlock( |
|
attn_type=cfg.VIT.ATTN_LAYER, |
|
dim=self.embed_dim, |
|
num_heads=self.num_heads, |
|
mlp_ratio=self.mlp_ratio, |
|
qkv_bias=self.qkv_bias, |
|
drop=self.drop_rate, |
|
attn_drop=self.attn_drop_rate, |
|
drop_path=dpr[i], |
|
norm_layer=norm_layer, |
|
) for i in range(self.depth) |
|
]) |
|
else: |
|
self.blocks = nn.ModuleList([ |
|
vit_helper.Block(attn_type=cfg.VIT.ATTN_LAYER, |
|
dim=self.embed_dim, |
|
num_heads=self.num_heads, |
|
mlp_ratio=self.mlp_ratio, |
|
qkv_bias=self.qkv_bias, |
|
drop=self.drop_rate, |
|
attn_drop=self.attn_drop_rate, |
|
drop_path=dpr[i], |
|
norm_layer=norm_layer, |
|
use_original_code=self.cfg.VIT.USE_ORIGINAL_TRAJ_ATTN_CODE) |
|
for i in range(self.depth) |
|
]) |
|
self.norm = norm_layer(self.embed_dim) |
|
|
|
|
|
if self.use_mlp: |
|
hidden_dim = self.embed_dim |
|
if self.head_act == 'tanh': |
|
|
|
act = nn.Tanh() |
|
elif self.head_act == 'gelu': |
|
|
|
act = nn.GELU() |
|
else: |
|
|
|
act = nn.ReLU() |
|
self.pre_logits = nn.Sequential( |
|
OrderedDict([ |
|
('fc', nn.Linear(self.embed_dim, hidden_dim)), |
|
('act', act), |
|
])) |
|
else: |
|
self.pre_logits = nn.Identity() |
|
|
|
|
|
self.head_drop = nn.Dropout(p=self.head_dropout) |
|
if isinstance(self.num_classes, (list, )) and len(self.num_classes) > 1: |
|
for a, i in enumerate(range(len(self.num_classes))): |
|
setattr(self, "head%d" % a, nn.Linear(self.embed_dim, self.num_classes[i])) |
|
else: |
|
self.head = nn.Linear(self.embed_dim, |
|
self.num_classes) if self.num_classes > 0 else nn.Identity() |
|
|
|
|
|
self.apply(self._init_weights) |
|
|
|
def _init_weights(self, m): |
|
if isinstance(m, nn.Linear): |
|
trunc_normal_(m.weight, std=.02) |
|
if isinstance(m, nn.Linear) and m.bias is not None: |
|
nn.init.constant_(m.bias, 0) |
|
elif isinstance(m, nn.LayerNorm): |
|
nn.init.constant_(m.bias, 0) |
|
nn.init.constant_(m.weight, 1.0) |
|
|
|
@torch.jit.ignore |
|
def no_weight_decay(self): |
|
if self.cfg.VIT.POS_EMBED == "joint": |
|
return {'pos_embed', 'cls_token', 'st_embed'} |
|
else: |
|
return {'pos_embed', 'cls_token', 'temp_embed'} |
|
|
|
def get_classifier(self): |
|
return self.head |
|
|
|
def reset_classifier(self, num_classes, global_pool=''): |
|
self.num_classes = num_classes |
|
self.head = (nn.Linear(self.embed_dim, num_classes) if num_classes > 0 else nn.Identity()) |
|
|
|
def forward_features(self, x): |
|
|
|
|
|
B = x.shape[0] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
x = self.patch_embed_3d(x) |
|
tok_mask = None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cls_tokens = self.cls_token.expand(B, -1, -1) |
|
x = torch.cat((cls_tokens, x), dim=1) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
new_pos_embed = self.pos_embed |
|
npatch = self.patch_embed.num_patches |
|
|
|
|
|
if self.video_input: |
|
if self.cfg.VIT.POS_EMBED == "separate": |
|
cls_embed = self.pos_embed[:, 0, :].unsqueeze(1) |
|
tile_pos_embed = new_pos_embed[:, 1:, :].repeat(1, self.temporal_resolution, 1) |
|
tile_temporal_embed = self.temp_embed.repeat_interleave(npatch, 1) |
|
total_pos_embed = tile_pos_embed + tile_temporal_embed |
|
total_pos_embed = torch.cat([cls_embed, total_pos_embed], dim=1) |
|
x = x + total_pos_embed |
|
elif self.cfg.VIT.POS_EMBED == "joint": |
|
x = x + self.st_embed |
|
else: |
|
|
|
x = x + new_pos_embed |
|
|
|
|
|
x = self.pos_drop(x) |
|
|
|
|
|
for i, blk in enumerate(self.blocks): |
|
x = blk(x, |
|
seq_len=npatch, |
|
num_frames=self.temporal_resolution, |
|
approx=self.cfg.VIT.APPROX_ATTN_TYPE, |
|
num_landmarks=self.cfg.VIT.APPROX_ATTN_DIM, |
|
tok_mask=tok_mask) |
|
|
|
|
|
|
|
|
|
|
|
return x, tok_mask |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|