deepsafe's picture
Add stripped inference-only model code mirror
9e14838 verified
Raw History Blame Contribute Delete
43.1 kB
# -*- coding: utf-8 -*-
# Copyright (c) OpenMMLab. All rights reserved.
import math
from functools import partial
from typing import Dict, Union
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.checkpoint as checkpoint
from einops import rearrange, reduce, repeat
from models.builder import BACKBONES
from timm.models.layers import drop_path, to_2tuple, trunc_normal_
from ..common import BN3D_MOMENTUM
from ..pose_efficientNet import EfficientNet
from .base import BaseBackbone
from .efficientNet import get_model_params
def get_abs_pos(abs_pos, h, w, ori_h, ori_w, has_cls_token=True):
"""
Calculate absolute positional embeddings. If needed, resize embeddings and remove cls_token
dimension for the original embeddings.
Args:
abs_pos (Tensor): absolute positional embeddings with (1, num_position, C).
has_cls_token (bool): If true, has 1 embedding in abs_pos for cls token.
hw (Tuple): size of input image tokens.
Returns:
Absolute positional embeddings after processing with shape (1, H, W, C)
"""
cls_token = None
B, L, C = abs_pos.shape
if has_cls_token:
cls_token = abs_pos[:, 0:1]
abs_pos = abs_pos[:, 1:]
if ori_h != h or ori_w != w:
new_abs_pos = (
F.interpolate(
abs_pos.reshape(1, ori_h, ori_w, -1).permute(0, 3, 1, 2),
size=(h, w),
mode="bicubic",
align_corners=False,
)
.permute(0, 2, 3, 1)
.reshape(B, -1, C)
)
else:
new_abs_pos = abs_pos
if cls_token is not None:
new_abs_pos = torch.cat([cls_token, new_abs_pos], dim=1)
return new_abs_pos
class DropPath(nn.Module):
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks)."""
def __init__(self, drop_prob=None):
super(DropPath, self).__init__()
self.drop_prob = drop_prob
def forward(self, x):
return drop_path(x, self.drop_prob, self.training)
def extra_repr(self):
return "p={}".format(self.drop_prob)
class Mlp(nn.Module):
def __init__(
self,
in_features,
hidden_features=None,
out_features=None,
act_layer=nn.GELU,
drop=0.0,
):
super().__init__()
out_features = out_features or in_features
hidden_features = hidden_features or in_features
self.fc1 = nn.Linear(in_features, hidden_features)
self.act = act_layer()
self.fc2 = nn.Linear(hidden_features, out_features)
self.drop = nn.Dropout(drop)
def forward(self, x):
x = self.fc1(x)
x = self.act(x)
x = self.fc2(x)
x = self.drop(x)
return x
class Attention(nn.Module):
def __init__(
self,
dim,
num_heads=8,
qkv_bias=False,
qk_scale=None,
attn_drop=0.0,
proj_drop=0.0,
attn_head_dim=None,
att_dimension=None,
):
super().__init__()
self.num_heads = num_heads
head_dim = dim // num_heads
self.dim = dim
if attn_head_dim is not None:
head_dim = attn_head_dim
all_head_dim = head_dim * self.num_heads
self.scale = qk_scale or head_dim**-0.5
self.qkv = nn.Linear(dim, all_head_dim * 3, bias=qkv_bias)
self.attn_drop = nn.Dropout(attn_drop)
self.proj = nn.Linear(all_head_dim, dim)
self.proj_drop = nn.Dropout(proj_drop)
self.attn_cam = None
self.attn = None
self.v = None
self.v_cam = None
self.attn_gradients = None
def get_attn(self):
return self.attn
def save_attn(self, attn):
self.attn = attn
def save_attn_cam(self, cam):
self.attn_cam = cam
def get_attn_cam(self):
return self.attn_cam
def get_v(self):
return self.v
def save_v(self, v):
self.v = v
def save_v_cam(self, cam):
self.v_cam = cam
def get_v_cam(self):
return self.v_cam
def save_attn_gradients(self, attn_gradients):
self.attn_gradients = attn_gradients
def get_attn_gradients(self):
return self.attn_gradients
def forward(self, x, b_size, **kwargs):
B, N, C = x.shape
# mask = None
# if 'maskout_pes' in kwargs.keys():
# mask = kwargs['maskout_pes']
# T = mask.shape[1]
# num_tokens = mask.shape[2]*mask.shape[3]
# if num_tokens == (N-1):
# mask = rearrange(mask, 'b t h w -> (b t) (h w)', b=b_size, t=T, h=mask.shape[2], w= mask.shape[3]).unsqueeze(1)
# mask = mask.unsqueeze(3)
# mask = mask.repeat(1, self.num_heads, 1, num_tokens+1)
# mask = torch.cat((torch.ones((b_size*T, self.num_heads, 1, num_tokens+1)).cuda(), mask), 2)
# else:
# mask = rearrange(mask, 'b t h w -> (b h w) t', b=b_size, t=T, h=mask.shape[2], w= mask.shape[3]).unsqueeze(1)
# mask = mask.unsqueeze(3)
# mask = mask.repeat(1, self.num_heads, 1, T+1)
# mask = torch.cat((torch.ones((b_size*num_tokens, self.num_heads, 1, T+1)).cuda(), mask), 2)
qkv = self.qkv(x)
qkv = qkv.reshape(B, N, 3, self.num_heads, -1).permute(
2, 0, 3, 1, 4
) # 3, B (BxT or BxN), H, N, C
q, k, v = (
qkv[0],
qkv[1],
qkv[2],
) # make torchscript happy (cannot use tensor as tuple)
# self.save_v(v)
q = q * self.scale
attn = q @ k.transpose(-2, -1)
# if mask is not None:
# attn = attn.masked_fill(mask == 0, float("-1e20"))
attn = attn.softmax(dim=-1)
attn = self.attn_drop(attn)
# self.save_attn(attn)
# attn.register_hook(self.save_attn_gradients)
x = (attn @ v).transpose(1, 2).reshape(B, N, -1)
x = self.proj(x)
x = self.proj_drop(x)
return x
class CrossAttention(nn.Module):
def __init__(
self,
dim,
num_heads=8,
qkv_bias=False,
qk_scale=None,
attn_drop=0.0,
proj_drop=0.0,
attn_head_dim=None,
att_dimension="spatial",
):
"""
Implementation for cross attention through dimension
"""
super().__init__()
self.num_heads = num_heads
head_dim = dim // num_heads
self.dim = dim
if attn_head_dim is not None:
head_dim = attn_head_dim
all_head_dim = head_dim * self.num_heads
self.scale = qk_scale or head_dim**-0.5
self.qkv = nn.Linear(dim, all_head_dim * 3, bias=qkv_bias)
self.attn_drop = nn.Dropout(attn_drop)
self.proj = nn.Linear(all_head_dim, dim)
self.proj_drop = nn.Dropout(proj_drop)
assert att_dimension in ["spatial", "temporal"]
self.att_dimension = att_dimension
def forward(self, x, b_size):
B, N, C = (
x.shape
) # N can be T or HW // P**2, B can be previous reshaped from self.batch_size * T|B
T = B // b_size
qkv = self.qkv(x)
qkv = qkv.reshape(b_size, T, N, 3, self.num_heads, -1).permute(3, 0, 4, 1, 2, 5)
q, k, v = (
qkv[0],
qkv[1],
qkv[2],
) # make torchscript happy (cannot use tensor as tuple)
# Define the window
window = torch.tensor([-2, -1, 0, 1, 2]).cuda()
# Start computing locally cross self-attention
q_scale = q * self.scale
# Initialize attn weights
attn = torch.zeros((b_size, self.num_heads, T, N, N), dtype=torch.float).cuda()
if self.att_dimension == "spatial":
sqrt_N = int(math.sqrt(N))
for w in window:
i_indices = torch.arange(1, T).cuda()
j_indices = torch.arange(0, N).cuda()
i_valid = (i_indices + w >= 0) & (i_indices + w < T)
j_valid = (j_indices + sqrt_N * w >= 0) & (j_indices + sqrt_N * w < N)
i_indices = i_indices[i_valid]
j_indices = j_indices[j_valid]
attn[
:, :, torch.cat((torch.zeros(1, dtype=int).cuda(), i_indices + w))
][:, :, :, j_indices][..., j_indices + sqrt_N * w] = q_scale[
:, :, torch.cat((torch.zeros(1, dtype=int).cuda(), i_indices))
][
:, :, :, j_indices
] @ k[
:, :, torch.cat((torch.zeros(1, dtype=int).cuda(), i_indices + w))
][
:, :, :, j_indices + sqrt_N * w
].transpose(
-2, -1
)
else:
sqrt_T = int(math.sqrt(T))
for w in window:
i_indices = torch.arange(0, T).cuda()
j_indices = torch.arange(1, N).cuda()
i_valid = (i_indices + sqrt_T * w >= 0) & (i_indices + sqrt_T * w < T)
j_valid = (j_indices + w >= 1) & (j_indices + w < N)
i_indices = i_indices[i_valid]
j_indices = j_indices[j_valid]
attn[:, :, i_indices + sqrt_T * w][
:, :, :, torch.cat((torch.zeros(1, dtype=int).cuda(), j_indices))
][
..., torch.cat((torch.zeros(1, dtype=int).cuda(), j_indices + w))
] = q_scale[
:, :, i_indices
][
:, :, :, torch.cat((torch.zeros(1, dtype=int).cuda(), j_indices))
] @ k[
:, :, i_indices + sqrt_T * w
][
:,
:,
:,
torch.cat((torch.zeros(1, dtype=int).cuda(), j_indices + w)),
].transpose(
-2, -1
)
attn = attn.softmax(dim=-1)
attn = self.attn_drop(attn)
x = (attn @ v).transpose(1, 2).reshape(B, N, -1)
x = self.proj(x)
x = self.proj_drop(x)
return x
class Block(nn.Module):
def __init__(
self,
dim,
num_heads,
mlp_ratio=4.0,
qkv_bias=False,
qk_scale=None,
drop=0.0,
attn_drop=0.0,
drop_path=0.0,
act_layer=nn.GELU,
norm_layer=nn.LayerNorm,
attn_head_dim=None,
):
super().__init__()
self.norm1 = norm_layer(dim)
self.attn = Attention(
dim,
num_heads=num_heads,
qkv_bias=qkv_bias,
qk_scale=qk_scale,
attn_drop=attn_drop,
proj_drop=drop,
attn_head_dim=attn_head_dim,
)
# NOTE: drop path for stochastic depth, we shall see if this is better than dropout here
self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
self.norm2 = norm_layer(dim)
mlp_hidden_dim = int(dim * mlp_ratio)
self.mlp = Mlp(
in_features=dim,
hidden_features=mlp_hidden_dim,
act_layer=act_layer,
drop=drop,
)
def forward(self, x, b_size, **kwargs):
x = x + self.drop_path(self.attn(self.norm1(x), b_size))
x = x + self.drop_path(self.mlp(self.norm2(x)))
return x
class Block2D(nn.Module):
def __init__(
self,
dim,
num_heads,
mlp_ratio=4.0,
qkv_bias=False,
qk_scale=None,
drop=0.0,
attn_drop=0.0,
drop_path=0.1,
act_layer=nn.GELU,
norm_layer=nn.LayerNorm,
attention_type="divided_space_time",
**kwargs,
):
super().__init__()
self.attention_type = attention_type
assert attention_type in [
"divided_space_time",
"space_only",
"joint_space_time",
]
self.register_token = kwargs.get("register_token")
self.temp_token = kwargs.get("temp_token")
self.return_s_cls_token = kwargs.get("return_s_cls_token") or False
self.norm1 = norm_layer(dim)
self.attn = Attention(
dim,
num_heads=num_heads,
qkv_bias=qkv_bias,
qk_scale=qk_scale,
attn_drop=attn_drop,
proj_drop=drop,
)
## Temporal Attention Parameters
if self.attention_type == "divided_space_time":
self.temporal_norm1 = norm_layer(dim)
self.temporal_attn = Attention(
dim,
num_heads=num_heads,
qkv_bias=qkv_bias,
qk_scale=qk_scale,
attn_drop=attn_drop,
proj_drop=drop,
att_dimension="temporal",
)
self.temporal_fc = nn.Linear(dim, dim)
## drop path
self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
self.norm2 = norm_layer(dim)
# self.norm3 = norm_layer(dim)
# self.norm4 = norm_layer(dim)
mlp_hidden_dim = int(dim * mlp_ratio)
self.mlp = Mlp(
in_features=dim,
hidden_features=mlp_hidden_dim,
act_layer=act_layer,
drop=drop,
)
def forward(self, x, B, T, W, **kwargs):
if self.temp_token:
num_spatial_tokens = (x.size(1) - 2) // T
else:
num_spatial_tokens = (x.size(1) - 1) // T
H = num_spatial_tokens // W
if self.attention_type in ["space_only", "joint_space_time"]:
x = x + self.drop_path(self.attn(self.norm1(x)))
x = x + self.drop_path(self.mlp(self.norm2(x)))
return x, None
elif self.attention_type == "divided_space_time":
# init class token
init_cls_token = x[:, 0, :].unsqueeze(1)
if self.temp_token:
init_temp_token = x[:, -1, :].unsqueeze(1)
# init_register_token = x[:, -1, :].unsqueeze(1)
## Temporal
if self.temp_token:
t_cls_token = init_temp_token.repeat(1, num_spatial_tokens, 1)
else:
t_cls_token = init_cls_token.repeat(1, num_spatial_tokens, 1)
t_cls_token = rearrange(
t_cls_token, "b (h w) m -> (b h w) m", b=B, h=H, w=W
).unsqueeze(1)
if self.temp_token:
xt = x[:, 1:-1, :]
else:
xt = x[:, 1:, :]
xt = rearrange(xt, "b (h w t) m -> (b h w) t m", b=B, h=H, w=W, t=T)
xt = torch.cat((xt, t_cls_token), 1)
res_temporal = self.drop_path(
self.temporal_attn(self.temporal_norm1(xt), b_size=B, **kwargs)
) # Processing temporal att.
res_temporal = self.temporal_fc(res_temporal)
res_temporal, t_cls_token = res_temporal[:, :-1, :], res_temporal[:, -1, :]
res_temporal = rearrange(
res_temporal, "(b h w) t m -> b (h w t) m", b=B, h=H, w=W, t=T
)
t_cls_token = rearrange(
t_cls_token, "(b h w) m -> b (h w) m", b=B, h=H, w=W
)
# t_cls_token_avg = torch.mean(t_cls_token, 1, True) ## average for every temporal patch
xt = x[:, 1:-1, :] + res_temporal
## Spatial
cls_token = init_cls_token.repeat(1, T, 1)
cls_token = rearrange(cls_token, "b t m -> (b t) m", b=B, t=T).unsqueeze(1)
# register_token = init_register_token.repeat(1, T, 1)
# register_token = rearrange(register_token, 'b t m -> (b t) m', b=B, t=T).unsqueeze(1)
xs = xt
xs = rearrange(xs, "b (h w t) m -> (b t) (h w) m", b=B, h=H, w=W, t=T)
xs = torch.cat((cls_token, xs), 1)
# xs = torch.cat((xs, register_token), 1)
res_spatial = self.drop_path(self.attn(self.norm1(xs), b_size=B, **kwargs))
### Taking care of TEMP token
t_cls_token_avg = torch.mean(
t_cls_token, 1, True
) ## average for every temporal patch
### Taking care of CLS token
cls_token = res_spatial[:, 0, :]
cls_token = rearrange(cls_token, "(b t) m -> b t m", b=B, t=T)
cls_token_avg = torch.mean(cls_token, 1, True) ## averaging for every frame
# register_token = res_spatial[:, -1, :]
# register_token = rearrange(register_token, '(b t) m -> b t m', b=B, t=T)
# register_token_avg = torch.mean(register_token, 1, True) ## averaging for every frame
res_spatial = res_spatial[:, 1:, :]
res_spatial = rearrange(
res_spatial, "(b t) (h w) m -> b (h w t) m", b=B, h=H, w=W, t=T
)
res = res_spatial
x = xt
## Mlp
# x = torch.cat((t_cls_token_avg, x), 1) + torch.cat((cls_token_avg, res), 1)
xt_ = torch.cat((init_cls_token, x), 1)
if self.temp_token:
xt_ = torch.cat((xt_, t_cls_token_avg), 1)
xs_ = torch.cat((cls_token_avg, res), 1)
if self.temp_token:
xs_ = torch.cat((xs_, init_temp_token), 1)
x = xt_ + xs_
x = x + self.drop_path(self.mlp(self.norm2(x)))
# Adding MLP for spatial_cls_token, ttemp_cls_token
# t_cls_token = self.drop_path(self.mlp(self.norm3(t_cls_token)))
# s_cls_token = self.drop_path(self.mlp(self.norm4(cls_token)))
if self.return_s_cls_token:
return x, cls_token, None
else:
return x, None, None
class PatchEmbed(nn.Module):
"""Image to Patch Embedding"""
def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768, ratio=1):
super().__init__()
img_size = to_2tuple(img_size)
patch_size = to_2tuple(patch_size)
num_patches = (
(img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0]) * (ratio**2)
)
self.patch_shape = (
int(img_size[0] // patch_size[0] * ratio),
int(img_size[1] // patch_size[1] * ratio),
)
self.origin_patch_shape = (
int(img_size[0] // patch_size[0]),
int(img_size[1] // patch_size[1]),
)
self.img_size = img_size
self.patch_size = patch_size
self.num_patches = num_patches
self.proj = nn.Conv2d(
in_chans,
embed_dim,
kernel_size=patch_size,
stride=(patch_size[0] // ratio),
padding=4 + 2 * (ratio // 2 - 1),
)
def forward(self, x, **kwargs):
B, C, H, W = x.shape
x = self.proj(x)
Hp, Wp = x.shape[2], x.shape[3]
x = x.flatten(2).transpose(1, 2)
return x, (Hp, Wp)
class PatchEmbed3D(nn.Module):
"""Images to Patch Embedding"""
def __init__(
self,
img_size=224,
patch_size=16,
in_chans=3,
embed_dim=768,
ratio=1,
low_level=False,
**override_params,
):
super().__init__()
img_size = to_2tuple(img_size)
patch_size = to_2tuple(patch_size)
num_patches = (
(img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0]) * (ratio**2)
)
self.patch_shape = (
int(img_size[0] // patch_size[0] * ratio),
int(img_size[1] // patch_size[1] * ratio),
)
self.origin_patch_shape = (
int(img_size[0] // patch_size[0]),
int(img_size[1] // patch_size[1]),
)
self.img_size = img_size
self.patch_size = patch_size
self.num_patches = num_patches
self.low_level = low_level
if not self.low_level:
self.proj = nn.Conv2d(
in_chans, embed_dim, kernel_size=patch_size, stride=patch_size
)
else:
model_name = "efficientnet-b4"
self.proj = EfficientNet.from_pretrained(
model_name, advprop=True, **override_params
)
self.fc = nn.Linear(160, embed_dim)
def forward(self, x):
B, C, T, H, W = x.shape
x = rearrange(x, "b c t h w -> (b t) c h w")
if not self.low_level:
x = self.proj(x)
else:
endpoints = self.proj.extract_endpoints(x)
x1 = endpoints["reduction_6"]
x2 = endpoints["reduction_5"]
x3 = endpoints["reduction_4"]
x4 = endpoints["reduction_3"]
x5 = endpoints["reduction_2"]
x = x3
x = x.permute(0, 2, 3, 1)
x = self.fc(x)
x = x.permute(0, 3, 1, 2)
Hp, Wp = x.shape[2], x.shape[3]
x = x.flatten(2).transpose(1, 2)
return x, T, (Hp, Wp)
class HybridEmbed(nn.Module):
"""CNN Feature Map Embedding
Extract feature map from CNN, flatten, project to embedding dim.
"""
def __init__(
self, backbone, img_size=224, feature_size=None, in_chans=3, embed_dim=768
):
super().__init__()
assert isinstance(backbone, nn.Module)
img_size = to_2tuple(img_size)
self.img_size = img_size
self.backbone = backbone
if feature_size is None:
with torch.no_grad():
training = backbone.training
if training:
backbone.eval()
o = self.backbone(torch.zeros(1, in_chans, img_size[0], img_size[1]))[
-1
]
feature_size = o.shape[-2:]
feature_dim = o.shape[1]
backbone.train(training)
else:
feature_size = to_2tuple(feature_size)
feature_dim = self.backbone.feature_info.channels()[-1]
self.num_patches = feature_size[0] * feature_size[1]
self.proj = nn.Linear(feature_dim, embed_dim)
def forward(self, x):
x = self.backbone(x)[-1]
x = x.flatten(2).transpose(1, 2)
x = self.proj(x)
return x
@BACKBONES.register_module()
class ViT(BaseBackbone):
def __init__(
self,
img_size=224,
patch_size=16,
in_chans=3,
num_classes=80,
embed_dim=768,
depth=12,
num_heads=12,
mlp_ratio=4.0,
qkv_bias=False,
qk_scale=None,
drop_rate=0.0,
attn_drop_rate=0.0,
drop_path_rate=0.0,
hybrid_backbone=None,
norm_layer=None,
use_checkpoint=False,
frozen_stages=-1,
ratio=1,
last_norm=True,
class_token=True,
attention_type="space",
patch_padding="pad",
freeze_attn=False,
freeze_ffn=False,
**kwargs,
):
# Protect mutable default arguments
super(ViT, self).__init__()
norm_layer = norm_layer or partial(nn.LayerNorm, eps=1e-6)
self.num_classes = num_classes
self.num_features = self.embed_dim = (
embed_dim # num_features for consistency with other models
)
self.frozen_stages = frozen_stages
self.use_checkpoint = use_checkpoint
self.patch_padding = patch_padding
self.freeze_attn = freeze_attn
self.freeze_ffn = freeze_ffn
self.depth = depth
self.attention_type = attention_type
if hybrid_backbone is not None:
self.patch_embed = HybridEmbed(
hybrid_backbone,
img_size=img_size,
in_chans=in_chans,
embed_dim=embed_dim,
)
else:
self.patch_embed = PatchEmbed(
img_size=img_size,
patch_size=patch_size,
in_chans=in_chans,
embed_dim=embed_dim,
ratio=ratio,
)
num_patches = self.patch_embed.num_patches
self.cls_token = (
nn.Parameter(torch.zeros(1, 1, embed_dim)) if class_token else None
)
# since the pretraining model has class token
self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim))
dpr = [
x.item() for x in torch.linspace(0, drop_path_rate, depth)
] # stochastic depth decay rule
self.blocks = nn.ModuleList(
[
Block(
dim=embed_dim,
num_heads=num_heads,
mlp_ratio=mlp_ratio,
qkv_bias=qkv_bias,
qk_scale=qk_scale,
drop=drop_rate,
attn_drop=attn_drop_rate,
drop_path=dpr[i],
norm_layer=norm_layer,
)
for i in range(depth)
]
)
self.norm = norm_layer(embed_dim) if last_norm else nn.Identity()
if self.pos_embed is not None:
trunc_normal_(self.pos_embed, std=0.02)
self._freeze_stages()
def _freeze_stages(self):
"""Freeze parameters."""
if self.frozen_stages >= 0:
self.patch_embed.eval()
for param in self.patch_embed.parameters():
param.requires_grad = False
for i in range(1, self.frozen_stages + 1):
m = self.blocks[i]
m.eval()
for param in m.parameters():
param.requires_grad = False
if self.freeze_attn:
for i in range(0, self.depth):
m = self.blocks[i]
m.attn.eval()
m.norm1.eval()
for param in m.attn.parameters():
param.requires_grad = False
for param in m.norm1.parameters():
param.requires_grad = False
if self.freeze_ffn:
self.pos_embed.requires_grad = False
self.patch_embed.eval()
for param in self.patch_embed.parameters():
param.requires_grad = False
for i in range(0, self.depth):
m = self.blocks[i]
m.mlp.eval()
m.norm2.eval()
for param in m.mlp.parameters():
param.requires_grad = False
for param in m.norm2.parameters():
param.requires_grad = False
def init_weights(self, pretrained=None):
"""Initialize the weights in backbone.
Args:
pretrained (str, optional): Path to pre-trained weights.
Defaults to None.
"""
super().init_weights(pretrained, patch_padding=self.patch_padding)
if pretrained is None:
def _init_weights(m):
if isinstance(m, nn.Linear):
trunc_normal_(m.weight, std=0.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)
if self.cls_token is not None:
nn.init.normal_(self.cls_token, std=1e-6)
self.apply(_init_weights)
def get_num_layers(self):
return len(self.blocks)
@torch.jit.ignore
def no_weight_decay(self):
return {"pos_embed", "cls_token"}
def forward_features(self, x, **kwargs):
B, C, H, W = x.shape
x, (Hp, Wp) = self.patch_embed(x)
if self.cls_token is not None:
cls_token = self.cls_token.expand(x.shape[0], -1, -1)
x = torch.cat((cls_token, x), dim=1)
if self.pos_embed is not None:
# fit for multiple GPU training
# since the first element for pos embed (sin-cos manner) is zero, it will cause no difference
# x = x + self.pos_embed[:, 1:] + self.pos_embed[:, :1]
x = x + self.pos_embed
for blk in self.blocks:
if self.use_checkpoint:
x = checkpoint.checkpoint(blk, x)
else:
x = blk(x, B)
x = self.norm(x)
res = {}
if self.cls_token is not None:
x_cls = x[:, :1]
else:
x_cls = torch.mean(x[:, 1:], 1, False)
res["cls"] = x_cls
xp = x[:, 1:]
xp = xp.permute(0, 2, 1).reshape(B, -1, Hp, Wp).contiguous()
res["embed"] = xp
return res
def forward(self, x, **kwargs) -> Union[torch.tensor, Dict[str, torch.tensor]]:
x = self.forward_features(x, **kwargs)
return x
def train(self, mode=True):
"""Convert the model into training mode."""
super().train(mode)
self._freeze_stages()
@BACKBONES.register_module()
class TimeViT(ViT):
def __init__(
self,
img_size=224,
patch_size=16,
in_chans=3,
num_classes=80,
embed_dim=768,
depth=12,
num_heads=12,
mlp_ratio=4,
qkv_bias=False,
qk_scale=None,
drop_rate=0,
attn_drop_rate=0,
drop_path_rate=0,
hybrid_backbone=None,
norm_layer=None,
use_checkpoint=False,
frozen_stages=-1,
ratio=1,
last_norm=True,
class_token=True,
attention_type="space_only",
patch_padding="pad",
freeze_attn=False,
freeze_ffn=False,
num_frames=4,
**kwargs,
):
super().__init__(
img_size,
patch_size,
in_chans,
num_classes,
embed_dim,
depth,
num_heads,
mlp_ratio,
qkv_bias,
qk_scale,
drop_rate,
attn_drop_rate,
drop_path_rate,
hybrid_backbone,
norm_layer,
use_checkpoint,
frozen_stages,
ratio,
last_norm,
class_token,
attention_type,
patch_padding,
freeze_attn,
freeze_ffn,
**kwargs,
)
norm_layer = norm_layer or partial(nn.LayerNorm, eps=1e-6)
register_token = kwargs.get("register_token") or False
temp_token = kwargs.get("temp_token") or False
self.low_level_enhanced = kwargs.get("low_level_enhanced") or False
self.patch_size = patch_size
# Temporary
# self.patch_embed = PatchEmbed3D(
# img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim, ratio=ratio,
# low_level=True, include_top=False, include_hm_decoder=False)
self.patch_embed = PatchEmbed3D(
img_size=img_size,
patch_size=patch_size,
in_chans=in_chans,
embed_dim=embed_dim,
ratio=ratio,
)
self.register_token = (
nn.Parameter(torch.zeros(1, 1, embed_dim)) if register_token else None
)
self.temp_token = (
nn.Parameter(torch.zeros(1, 1, embed_dim)) if temp_token else None
)
if self.attention_type != "space_only":
if temp_token:
self.time_embed = nn.Parameter(
torch.zeros(1, num_frames + 1, embed_dim)
)
else:
self.time_embed = nn.Parameter(torch.zeros(1, num_frames, embed_dim))
self.time_drop = nn.Dropout(p=drop_rate)
self.norm_s_cls = norm_layer(embed_dim)
self.norm_t_cls = norm_layer(embed_dim)
dpr = [
x.item() for x in torch.linspace(0, drop_path_rate, depth)
] # stochastic depth decay rule
self.blocks = nn.ModuleList(
[
Block2D(
dim=embed_dim,
num_heads=num_heads,
mlp_ratio=mlp_ratio,
qkv_bias=qkv_bias,
qk_scale=qk_scale,
drop=drop_rate,
attn_drop=attn_drop_rate,
drop_path=dpr[i],
norm_layer=norm_layer,
attention_type=self.attention_type,
temp_token=temp_token,
register_token=register_token,
return_s_cls_token=True,
)
for i in range(depth)
]
)
if self.low_level_enhanced:
self.lowconv3d_0 = nn.Conv3d(
3,
embed_dim // 16,
kernel_size=(3, 4, 4),
stride=(1, 4, 4),
padding=(1, 0, 0),
bias=False,
) # B x 48 x 4 x 56 x 56
self.bn_low0 = nn.BatchNorm3d(embed_dim // 16, momentum=BN3D_MOMENTUM)
self.act_low0 = nn.GELU()
self.lowconv3d_01 = nn.Conv3d(
embed_dim // 16,
embed_dim // 4,
kernel_size=(3, 4, 4),
stride=(1, 4, 4),
padding=(1, 0, 0),
bias=False,
) # B x 48 x 4 x 14 x 14
self.bn_low01 = nn.BatchNorm3d(embed_dim // 4, momentum=BN3D_MOMENTUM)
self.act_low01 = nn.GELU()
# self.lowmaxpool3d_0 = nn.MaxPool3d(kernel_size=(1, 4, 4), stride=(1, 4, 4), padding=0) # B x 48 x 4 x 14 x 14
self.lowfc_0 = nn.Linear(embed_dim // 4, embed_dim)
self.lowconv3d_1 = nn.Conv3d(
embed_dim // 16,
embed_dim // 4,
kernel_size=(3, 2, 2),
stride=(1, 2, 2),
padding=(1, 0, 0),
bias=False,
) # B x 192 x 4 x 28 x 28
self.bn_low1 = nn.BatchNorm3d(embed_dim // 4, momentum=BN3D_MOMENTUM)
self.act_low1 = nn.GELU()
self.lowconv3d_11 = nn.Conv3d(
embed_dim // 4,
embed_dim,
kernel_size=(3, 2, 2),
stride=(1, 2, 2),
padding=(1, 0, 0),
bias=False,
) # B x 192 x 4 x 14 x 14
self.bn_low11 = nn.BatchNorm3d(embed_dim, momentum=BN3D_MOMENTUM)
self.act_low11 = nn.GELU()
# self.lowmaxpool3d_1 = nn.MaxPool3d(kernel_size=(1,2,2), stride=(1,2,2), padding=0)
self.lowfc_1 = nn.Linear(embed_dim, embed_dim)
self.lowconv3d_2 = nn.Conv3d(
embed_dim // 4,
embed_dim,
kernel_size=(3, 2, 2),
stride=(1, 2, 2),
padding=(1, 0, 0),
bias=False,
) # B x 768 x 4 x 14 x 14
self.bn_low2 = nn.BatchNorm3d(embed_dim, momentum=BN3D_MOMENTUM)
self.act_low2 = nn.GELU()
self.lowconv3d_21 = nn.Conv3d(
embed_dim,
embed_dim,
kernel_size=(3, 3, 3),
stride=(1, 1, 1),
padding=(1, 1, 1),
bias=False,
) # B x 768 x 4 x 14 x 14
self.bn_low21 = nn.BatchNorm3d(embed_dim, momentum=BN3D_MOMENTUM)
self.act_low21 = nn.GELU()
# self.lowmaxpool3d_2 = nn.MaxPool3d(kernel_size=(1,1,1), stride=(1,1,1), padding=0)
self.lowfc_2 = nn.Linear(embed_dim, embed_dim)
self.lowconv3d_3 = nn.Conv3d(
embed_dim,
embed_dim,
kernel_size=(3, 1, 1),
stride=(1, 1, 1),
padding=(1, 0, 0),
bias=False,
) # B x 768 x 4 x 14 x 14
self.bn_low3 = nn.BatchNorm3d(embed_dim, momentum=BN3D_MOMENTUM)
self.act_low3 = nn.GELU()
self.lowconv3d_31 = nn.Conv3d(
embed_dim,
embed_dim,
kernel_size=(3, 1, 1),
stride=(1, 1, 1),
padding=(1, 0, 0),
bias=False,
) # B x 768 x 4 x 14 x 14
self.bn_low31 = nn.BatchNorm3d(embed_dim, momentum=BN3D_MOMENTUM)
self.act_low31 = nn.GELU()
# self.lowmaxpool3d_3 = nn.MaxPool3d(kernel_size=(1,1,1), stride=(1,1,1), padding=0)
self.lowfc_3 = nn.Linear(embed_dim, embed_dim)
def init_weights(self, pretrained=None):
super().init_weights(pretrained)
## initialization of temporal attention weights
if self.attention_type == "divided_space_time":
print("Initializing weights for temporal FC...")
i = 0
for m in self.blocks.modules():
m_str = str(m)
if "Block" in m_str:
if i > 0:
nn.init.constant_(m.temporal_fc.weight, 0)
nn.init.constant_(m.temporal_fc.bias, 0)
i += 1
if self.temp_token is not None:
nn.init.normal_(self.temp_token, std=1e-6)
@torch.jit.ignore
def no_weight_decay(self):
return {"pos_embed", "cls_token", "time_embed"}
def forward_features(self, x, **kwargs):
B, C, T, H, W = x.shape
if self.low_level_enhanced:
x_low0 = self.lowconv3d_0(x)
x_low0 = self.bn_low0(x_low0)
x_low0 = self.act_low0(x_low0)
# x_low0_ = self.lowmaxpool3d_0(x_low0)
x_low0_ = self.act_low01(self.bn_low01(self.lowconv3d_01(x_low0)))
x_low0_ = x_low0_.flatten(2).transpose(1, 2)
x_low0_ = self.lowfc_0(x_low0_)
x_low0_ = rearrange(
x_low0_,
"b (t h w) m -> (b t) (h w) m",
b=B,
t=T,
h=H // self.patch_size,
w=W // self.patch_size,
)
x_low0_ = x_low0_.sigmoid()
x_prev = x_low0
x, T, (Hp, Wp) = self.patch_embed(x)
if self.low_level_enhanced:
x = x_low0_ * x
if self.cls_token is not None:
cls_token = self.cls_token.expand(x.shape[0], -1, -1)
x = torch.cat((cls_token, x), dim=1)
# if self.register_token is not None:
# register_token = self.register_token.expand(x.shape[0], -1, -1)
# x = torch.cat((x, register_token), dim=1)
if self.pos_embed is not None:
# fit for multiple GPU training
# since the first element for pos embed (sin-cos manner) is zero, it will cause no difference
# x = x + self.pos_embed[:, 1:] + self.pos_embed[:, :1]
if x.size(1) != self.pos_embed.size(1):
# Resizing the pos embeds in case they do not match the input at inference
pos_embed = self.pos_embed
cls_pos_embed = pos_embed[0, 0, :].unsqueeze(0).unsqueeze(1)
other_pos_embed = pos_embed[0, 1:, :].unsqueeze(0).transpose(1, 2)
P = int(other_pos_embed.size(2) ** 0.5)
H = x.size(1) // W
other_pos_embed = other_pos_embed.reshape(1, x.size(2), P, P)
new_pos_embed = F.interpolate(
other_pos_embed, size=(H, W), mode="nearest"
)
new_pos_embed = new_pos_embed.flatten(2)
new_pos_embed = new_pos_embed.transpose(1, 2)
new_pos_embed = torch.cat((cls_pos_embed, new_pos_embed), 1)
x = x + new_pos_embed
else:
x = x + self.pos_embed
## Time Embeddings
if self.attention_type != "space_only":
cls_tokens = x[:B, 0, :].unsqueeze(1)
x = x[:, 1:]
x = rearrange(x, "(b t) n m -> (b n) t m", b=B, t=T)
if self.temp_token is not None:
temp_token = self.temp_token.expand(x.shape[0], -1, -1)
x = torch.cat((x, temp_token), dim=1)
## Resizing time embeddings in case they don't match
if T != self.time_embed.size(1) and self.temp_token is None:
time_embed = self.time_embed.transpose(1, 2)
new_time_embed = F.interpolate(time_embed, size=(T), mode="nearest")
new_time_embed = new_time_embed.transpose(1, 2)
x = x + new_time_embed
else:
x = x + self.time_embed
x = self.time_drop(x)
temp_tokens = x[:B, 0, :].unsqueeze(1)
x = x[:, :-1]
x = rearrange(x, "(b n) t m -> b (n t) m", b=B, t=T)
x = torch.cat((cls_tokens, x), dim=1)
x = torch.cat((x, temp_tokens), dim=1)
for idx, blk in enumerate(self.blocks):
if self.use_checkpoint:
x = checkpoint.checkpoint(blk, x)
else:
x, s_cls_token, t_cls_token = blk(x, B, T, Wp, **kwargs)
if self.low_level_enhanced:
if (idx + 1) % 4 == 0:
x_low = self.__getattr__(f"lowconv3d_{(idx+1)//4}")(x_prev)
# x_low_ = self.__getattr__(f'lowmaxpool3d_{(idx+1)//4}')(x_low)
x_low = self.__getattr__(f"bn_low{(idx+1)//4}")(x_low)
x_low = self.__getattr__(f"act_low{(idx+1)//4}")(x_low)
x_low_ = self.__getattr__(f"lowconv3d_{(idx+1)//4}1")(x_low)
x_low_ = self.__getattr__(f"bn_low{(idx+1)//4}1")(x_low_)
x_low_ = self.__getattr__(f"act_low{(idx+1)//4}1")(x_low_)
x_low_ = x_low_.flatten(2).transpose(1, 2)
x_low_ = self.__getattr__(f"lowfc_{(idx+1)//4}")(x_low_)
x_low_ = x_low_.sigmoid()
x[:, 1:-1, :] = x[:, 1:-1, :].clone() * x_low_
x_prev = x_low
### Predictions for space-only baseline
if self.attention_type == "space_only":
x = rearrange(x, "(b t) n m -> b t n m", b=B, t=T)
x = torch.mean(x, 1) # averaging predictions for every frame
x = self.norm(x)
res = {}
if self.temp_token is not None:
x_cls = x[:, -1:]
elif self.cls_token is not None:
x_cls = x[:, :1]
else:
x_cls = torch.mean(x[:, 1:-1], 1, False)
res["cls"] = x_cls
xp = x[:, 1:-1]
xp = xp.permute(0, 2, 1).reshape(B, -1, Hp, Wp).contiguous()
if self.attention_type != "space_only":
xp = rearrange(xp, "b (m t) h w -> b m t h w", b=B, t=T, h=Hp, w=Wp)
res["embed"] = xp
if s_cls_token is not None:
s_cls_token = self.norm_s_cls(s_cls_token)
res["s_cls_token"] = s_cls_token
if t_cls_token is not None:
t_cls_token = self.norm_t_cls(t_cls_token)
res["t_cls_token"] = t_cls_token
return res