Add causal person fusion and MuVAP stage-3 baselines
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# PyTorch models
|
||||
*.pth
|
||||
|
||||
# Miscellaneous
|
||||
*.DS_Store
|
||||
*.swp
|
||||
*.pyc
|
||||
*.pkl
|
||||
|
||||
*.out
|
||||
|
||||
dataset/
|
||||
# Dataset soft links
|
||||
data/
|
||||
|
||||
# Output files
|
||||
output/
|
||||
output_*/
|
||||
|
||||
checkpoints/
|
||||
|
||||
notebooks/
|
||||
wandb/
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
model: 'MambaA'
|
||||
data_name: 'EasyCom_ANTICIPATION'
|
||||
task: 'ANTICIPATION'
|
||||
loss: 'ANTICIPATION' # NONUNIFORM, originally
|
||||
metric: 'AP' # AP or cAP
|
||||
optimizer: 'AdamW'
|
||||
device: 'cuda'
|
||||
feature_pretrained: 'resnet50'
|
||||
root_path: './data/EasyCom'
|
||||
rgb_type: 'rgb_kinetics_resnet50'
|
||||
flow_type: 'audio_wav2vec2'
|
||||
annotation_type: 'target_perframe'
|
||||
video_list_path: 'data_info/data_info.json'
|
||||
output_path: './output_cfg'
|
||||
window_size: 128
|
||||
batch_size: 64
|
||||
test_batch_size: 1
|
||||
# loss_offset: 4 # loss weight a_t when L - {loss_offset} <= t <= L
|
||||
num_epoch: 30
|
||||
lr: 0.0001
|
||||
weight_decay: 0.05
|
||||
num_workers: 4
|
||||
dropout: 0.20
|
||||
num_classes: 3
|
||||
embedding_dim: 2048
|
||||
hidden_dim: 1024
|
||||
num_layers: 1
|
||||
stride: 4
|
||||
anticipation_length: 10
|
||||
actionness: False
|
||||
ignore_index: -100
|
||||
seed: 20
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
model: 'MiniROADA'
|
||||
data_name: 'EasyCom_ANTICIPATION'
|
||||
task: 'ANTICIPATION'
|
||||
loss: 'ANTICIPATION' # NONUNIFORM, originally
|
||||
metric: 'AP' # AP or cAP
|
||||
optimizer: 'AdamW'
|
||||
device: 'cuda'
|
||||
feature_pretrained: 'resnet50'
|
||||
root_path: './data/EasyCom'
|
||||
rgb_type: 'rgb_kinetics_resnet50'
|
||||
flow_type: 'audio_wav2vec2'
|
||||
annotation_type: 'target_perframe'
|
||||
video_list_path: 'data_info/data_info.json'
|
||||
output_path: './output_cfg'
|
||||
window_size: 128
|
||||
batch_size: 64
|
||||
test_batch_size: 1
|
||||
# loss_offset: 4 # loss weight a_t when L - {loss_offset} <= t <= L
|
||||
num_epoch: 30
|
||||
lr: 0.0001
|
||||
weight_decay: 0.05
|
||||
num_workers: 4
|
||||
dropout: 0.20
|
||||
num_classes: 3 # [background, target speaker speaking, other person speaking]
|
||||
embedding_dim: 2048
|
||||
hidden_dim: 1024
|
||||
num_layers: 1
|
||||
stride: 4
|
||||
anticipation_length: 10
|
||||
actionness: False
|
||||
ignore_index: -100
|
||||
seed: 20
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
model: 'MambaA'
|
||||
data_name: 'YTConv_ANTICIPATION'
|
||||
data_info_name: 'shard_0'
|
||||
task: 'ANTICIPATION'
|
||||
loss: 'ANTICIPATION' # NONUNIFORM, originally
|
||||
metric: 'AP' # AP or cAP
|
||||
optimizer: 'AdamW'
|
||||
device: 'cuda'
|
||||
feature_pretrained: 'resnet50'
|
||||
root_path: './data/YTConv/shard_0'
|
||||
rgb_type: 'rgb_kinetics_resnet50'
|
||||
flow_type: 'audio_wav2vec2' # or 'none'?
|
||||
annotation_type: 'target_perframe/case1'
|
||||
video_list_path: 'data_info/YTConv_info.json'
|
||||
output_path: './output_cfg'
|
||||
window_size: 128
|
||||
batch_size: 32
|
||||
test_batch_size: 1
|
||||
# loss_offset: 4 # loss weight a_t when L - {loss_offset} <= t <= L
|
||||
num_epoch: 30
|
||||
lr: 0.0001
|
||||
weight_decay: 0.05
|
||||
num_workers: 4
|
||||
dropout: 0.20
|
||||
num_classes: 2 # for YTConv pretraining, there is no
|
||||
embedding_dim: 2048
|
||||
hidden_dim: 1024
|
||||
num_layers: 1
|
||||
stride: 4
|
||||
anticipation_length: 10
|
||||
actionness: False
|
||||
ignore_index: -100
|
||||
@@ -0,0 +1,32 @@
|
||||
model: 'MiniROADA'
|
||||
data_name: 'YTConv_ANTICIPATION'
|
||||
data_info_name: 'shard_0'
|
||||
task: 'ANTICIPATION'
|
||||
loss: 'ANTICIPATION' # NONUNIFORM, originally
|
||||
metric: 'AP' # AP or cAP
|
||||
optimizer: 'AdamW'
|
||||
device: 'cuda'
|
||||
feature_pretrained: 'resnet50'
|
||||
root_path: './data/YTConv/shard_0'
|
||||
rgb_type: 'rgb_kinetics_resnet50'
|
||||
flow_type: 'audio_wav2vec2'
|
||||
annotation_type: 'target_perframe'
|
||||
video_list_path: 'data_info/YTConv_info.json'
|
||||
output_path: './output_cfg'
|
||||
window_size: 128
|
||||
batch_size: 64
|
||||
test_batch_size: 1
|
||||
# loss_offset: 4 # loss weight a_t when L - {loss_offset} <= t <= L
|
||||
num_epoch: 30
|
||||
lr: 0.0001
|
||||
weight_decay: 0.05
|
||||
num_workers: 4
|
||||
dropout: 0.20
|
||||
num_classes: 2 # for pretraining, there is no target speaker
|
||||
embedding_dim: 2048
|
||||
hidden_dim: 1024
|
||||
num_layers: 1
|
||||
stride: 4
|
||||
anticipation_length: 10
|
||||
actionness: False
|
||||
ignore_index: -100
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
model: 'MambaA'
|
||||
data_name: 'EGO4D_ANTICIPATION'
|
||||
data_info_name: 'EGO4DSHUFFLE'
|
||||
task: 'ANTICIPATION'
|
||||
loss: 'ANTICIPATION' # NONUNIFORM, originally
|
||||
metric: 'AP' # AP or cAP
|
||||
optimizer: 'AdamW'
|
||||
device: 'cuda'
|
||||
feature_pretrained: 'resnet50'
|
||||
root_path: './data/EGO4D'
|
||||
rgb_type: 'rgb_kinetics_resnet50'
|
||||
flow_type: 'audio_wav2vec2'
|
||||
annotation_type: 'target_perframe'
|
||||
video_list_path: 'data_info/data_info.json'
|
||||
output_path: './output_cfg'
|
||||
window_size: 128
|
||||
batch_size: 64
|
||||
test_batch_size: 1
|
||||
# loss_offset: 4 # loss weight a_t when L - {loss_offset} <= t <= L
|
||||
num_epoch: 30
|
||||
lr: 0.0001
|
||||
weight_decay: 0.05
|
||||
num_workers: 4
|
||||
dropout: 0.20
|
||||
num_classes: 3 # including background
|
||||
embedding_dim: 2048
|
||||
hidden_dim: 1024
|
||||
d_state: 16
|
||||
d_conv: 4
|
||||
expand: 2
|
||||
num_layers: 1
|
||||
stride: 4
|
||||
anticipation_length: 10
|
||||
actionness: False
|
||||
ignore_index: -100
|
||||
seed: 20
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
model: 'MiniROADA'
|
||||
data_name: 'EGO4D_ANTICIPATION'
|
||||
data_info_name: 'EGO4DSHUFFLE'
|
||||
task: 'ANTICIPATION'
|
||||
loss: 'ANTICIPATION' # NONUNIFORM, originally
|
||||
metric: 'AP' # AP or cAP
|
||||
optimizer: 'AdamW'
|
||||
device: 'cuda'
|
||||
feature_pretrained: 'resnet50'
|
||||
root_path: './data/EGO4D'
|
||||
rgb_type: 'rgb_kinetics_resnet50'
|
||||
flow_type: 'audio_wav2vec2' # or 'none'?
|
||||
annotation_type: 'target_perframe'
|
||||
video_list_path: 'data_info/data_info.json'
|
||||
output_path: './output_cfg'
|
||||
window_size: 128
|
||||
batch_size: 64
|
||||
test_batch_size: 1
|
||||
# loss_offset: 4 # loss weight a_t when L - {loss_offset} <= t <= L
|
||||
num_epoch: 30
|
||||
lr: 0.0001
|
||||
weight_decay: 0.05
|
||||
num_workers: 4
|
||||
dropout: 0.20
|
||||
num_classes: 3
|
||||
embedding_dim: 2048
|
||||
hidden_dim: 1024
|
||||
num_layers: 1
|
||||
stride: 4
|
||||
anticipation_length: 10
|
||||
actionness: False
|
||||
ignore_index: -100
|
||||
@@ -0,0 +1,26 @@
|
||||
# dimension of features extracted from different models
|
||||
# used in feature_head, dataset, models
|
||||
|
||||
FEATURE_SIZES = {
|
||||
'rgb_anet_resnet50': 2048,
|
||||
'flow_anet_resnet50': 2048,
|
||||
'rgb_kinetics_bninception': 1024,
|
||||
'flow_kinetics_bninception': 1024,
|
||||
'rgb_farl': 512,
|
||||
'rgb_kinetics_resnet50': 2048,
|
||||
'rgb_kinetics_resnet50_debug': 2048,
|
||||
'rgb_kinetics_resnet50_debug_resized': 2048,
|
||||
'flow_kinetics_resnet50': 2048,
|
||||
'flow_nv_kinetics_bninception': 1024,
|
||||
'rgb_kinetics_i3d': 2048,
|
||||
'flow_kinetics_i3d': 2048,
|
||||
'audio_wav2vec2': 5120,
|
||||
'audio_wav2vec2_left': 5120,
|
||||
'audio_wav2vec2_right': 5120,
|
||||
'text_word2vec': 300,
|
||||
'text_fasttext_crawl': 300,
|
||||
'text_gpt2_last_subword_window_size_0': 768,
|
||||
'text_gpt2_last_subword_window_size_20': 768,
|
||||
'text_gpt2_last_subword_window_size_50': 768,
|
||||
'text_gpt2_last_subword_window_size_100': 768,
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
from .loss import OadLoss
|
||||
from .loss_builder import build_criterion
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from criterions.loss_builder import CRITERIONS
|
||||
|
||||
|
||||
@CRITERIONS.register('NONUNIFORM')
|
||||
class OadLoss(nn.Module):
|
||||
|
||||
def __init__(self, cfg, reduction='mean'):
|
||||
super(OadLoss, self).__init__()
|
||||
self.reduction = reduction
|
||||
self.num_classes = cfg['num_classes']
|
||||
self.loss_offset = cfg['loss_offset'] if 'loss_offset' in cfg else 0
|
||||
assert self.loss_offset==0, f'loss_offset is not 0: {self.loss_offset}'
|
||||
self.loss = self.end_loss
|
||||
|
||||
def end_loss(self, out_dict, target):
|
||||
# logits: (B, seq, K) target: (B, seq, K)
|
||||
assert self.loss_offset < out_dict['logits'].shape[1], f'loss_offset is longer than the sequence loss_offset: {self.loss_offset}, logits.shape: {out_dict["logits"].shape}'
|
||||
logits = out_dict['logits']
|
||||
logits = logits[:,-1 - self.loss_offset,:].contiguous()
|
||||
target = target[:,-1 - self.loss_offset,:].contiguous()
|
||||
ce_loss = self.mlce_loss(logits, target)
|
||||
return ce_loss
|
||||
|
||||
def mlce_loss(self, logits, target):
|
||||
'''
|
||||
multi label cross entropy loss.
|
||||
logits: (B, K) target: (B, K)
|
||||
'''
|
||||
logsoftmax = nn.LogSoftmax(dim=-1).to(logits.device)
|
||||
output = torch.sum(-F.normalize(target) * logsoftmax(logits), dim=1) # B
|
||||
if self.reduction == 'mean':
|
||||
loss = torch.mean(output)
|
||||
elif self.reduction == 'sum':
|
||||
loss = torch.sum(output)
|
||||
return loss
|
||||
|
||||
def forward(self, out_dict, target):
|
||||
return self.loss(out_dict, target)
|
||||
|
||||
|
||||
@CRITERIONS.register('UNIFORM')
|
||||
class OadUniformLoss(nn.Module):
|
||||
|
||||
def __init__(self, cfg, reduction='mean'):
|
||||
super(OadUniformLoss, self).__init__()
|
||||
self.reduction = reduction
|
||||
self.num_classes = cfg['num_classes']
|
||||
# self.loss_offset = cfg['loss_offset'] if 'loss_offset' in cfg else 0
|
||||
self.loss = self.end_loss
|
||||
|
||||
def end_loss(self, out_dict, target):
|
||||
# logits: (B, seq, K) target: (B, seq, K)
|
||||
logits = out_dict['logits']
|
||||
# logits = logits[:,-1 - self.loss_offset,:].contiguous()
|
||||
# target = target[:,-1 - self.loss_offset,:].contiguous()
|
||||
ce_loss = self.mlce_loss(logits, target)
|
||||
return ce_loss
|
||||
|
||||
def mlce_loss(self, logits, target):
|
||||
'''
|
||||
multi label cross entropy loss.
|
||||
logits: (B, K) target: (B, K)
|
||||
'''
|
||||
logsoftmax = nn.LogSoftmax(dim=-1).to(logits.device)
|
||||
output = torch.sum(-target * logsoftmax(logits), dim=1) # B
|
||||
# output = torch.sum(-F.normalize(target) * logsoftmax(logits), dim=1) # B
|
||||
if self.reduction == 'mean':
|
||||
loss = torch.mean(output)
|
||||
elif self.reduction == 'sum':
|
||||
loss = torch.sum(output)
|
||||
return loss
|
||||
|
||||
def forward(self, out_dict, target):
|
||||
return self.loss(out_dict, target)
|
||||
|
||||
@CRITERIONS.register('OADTR')
|
||||
class OadTRLoss(nn.Module):
|
||||
|
||||
def __init__(self, cfg, reduction='mean'):
|
||||
super(OadTRLoss, self).__init__()
|
||||
self.reduction = reduction
|
||||
self.num_classes = cfg['num_classes']
|
||||
# self.loss_offset = cfg['loss_offset'] if 'loss_offset' in cfg else 0
|
||||
self.loss = self.end_loss
|
||||
|
||||
def end_loss(self, out_dict, target):
|
||||
# logits: (B, seq, K) target: (B, seq, K)
|
||||
assert self.loss_offset < out_dict['logits'].shape[1], f'loss_offset is longer than the sequence loss_offset: {self.loss_offset}, logits.shape: {out_dict["logits"].shape}'
|
||||
logits = out_dict['logits']
|
||||
# logits = logits[:,-1 - self.loss_offset,:].contiguous()
|
||||
# target = target[:,-1 - self.loss_offset,:].contiguous()
|
||||
ce_loss = self.mlce_loss(logits, target)
|
||||
return ce_loss
|
||||
|
||||
def mlce_loss(self, logits, target):
|
||||
'''
|
||||
multi label cross entropy loss.
|
||||
logits: (B, K) target: (B, K)
|
||||
'''
|
||||
logsoftmax = nn.LogSoftmax(dim=-1).to(logits.device)
|
||||
output = torch.sum(-F.normalize(target) * logsoftmax(logits), dim=1) # B
|
||||
if self.reduction == 'mean':
|
||||
loss = torch.mean(output)
|
||||
elif self.reduction == 'sum':
|
||||
loss = torch.sum(output)
|
||||
return loss
|
||||
|
||||
def forward(self, out_dict, enc_target, dec_target):
|
||||
return self.loss(out_dict, enc_target, dec_target)
|
||||
|
||||
@CRITERIONS.register('ANTICIPATION')
|
||||
class OadAntLoss(nn.Module):
|
||||
|
||||
# reduction was sum, not mean, what if we change to sum?
|
||||
def __init__(self, cfg, reduction='mean'):
|
||||
super(OadAntLoss, self).__init__()
|
||||
self.reduction = reduction
|
||||
self.loss = self.anticipation_loss
|
||||
self.num_classes = cfg['num_classes']
|
||||
|
||||
def anticipation_loss(self, out_dict, target, ant_target):
|
||||
# anticipation_logits: (B, seq, ANTICIPATION_LENGTH, num_class)
|
||||
# ant_target: (B, ANTICIPATION_LENGTH, num_class)
|
||||
anticipation_logits = out_dict['anticipation_logits']
|
||||
# [B, window_size, anticipation_length, num_class]
|
||||
pred_anticipation_logits = anticipation_logits[:,-1,:,:].contiguous().view(-1, self.num_classes)
|
||||
|
||||
anticipation_logit_targets = ant_target.view(-1, self.num_classes)
|
||||
ant_loss = self.mlce_loss(pred_anticipation_logits, anticipation_logit_targets)
|
||||
|
||||
return ant_loss
|
||||
|
||||
def ce_loss(self, out_dict, target):
|
||||
# logits: (B, seq, K) target: (B, seq, K)
|
||||
logits = out_dict['logits']
|
||||
logits = logits[:,-1,:].contiguous()
|
||||
target = target[:,-1,:].contiguous()
|
||||
ce_loss = self.mlce_loss(logits, target)
|
||||
return ce_loss
|
||||
|
||||
def mlce_loss(self, logits, target):
|
||||
'''
|
||||
multi label cross entropy loss.
|
||||
logits: (B, K) target: (B, K)
|
||||
'''
|
||||
logsoftmax = nn.LogSoftmax(dim=-1).to(logits.device)
|
||||
output = torch.sum(-F.normalize(target) * logsoftmax(logits), dim=1) # B
|
||||
if self.reduction == 'mean':
|
||||
loss = torch.mean(output)
|
||||
elif self.reduction == 'sum':
|
||||
loss = torch.sum(output)
|
||||
|
||||
return loss
|
||||
|
||||
def forward(self, out_dict, target, ant_target):
|
||||
return self.loss(out_dict, target, ant_target)
|
||||
|
||||
|
||||
|
||||
@CRITERIONS.register('UNIFORM_ANTICIPATION')
|
||||
class OadUniformAntLoss(nn.Module):
|
||||
|
||||
# reduction was sum, not mean, what if we change to sum?
|
||||
def __init__(self, cfg, reduction='mean'):
|
||||
super(OadUniformAntLoss, self).__init__()
|
||||
self.reduction = reduction
|
||||
self.loss = self.anticipation_loss
|
||||
self.num_classes = cfg['num_classes']
|
||||
|
||||
def anticipation_loss(self, out_dict, target, ant_target):
|
||||
anticipation_logits = out_dict['anticipation_logits']
|
||||
pred_anticipation_logits = anticipation_logits[:,-1,:,:].contiguous().view(-1, self.num_classes)
|
||||
anticipation_logit_targets = ant_target.view(-1, self.num_classes)
|
||||
ant_loss = self.mlce_loss(pred_anticipation_logits, anticipation_logit_targets)
|
||||
return ant_loss
|
||||
|
||||
def ce_loss(self, out_dict, target):
|
||||
# logits: (B, seq, K) target: (B, seq, K)
|
||||
logits = out_dict['logits']
|
||||
logits = logits[:,-1,:].contiguous()
|
||||
target = target[:,-1,:].contiguous()
|
||||
ce_loss = self.mlce_loss(logits, target)
|
||||
return ce_loss
|
||||
|
||||
def mlce_loss(self, logits, target):
|
||||
'''
|
||||
multi label cross entropy loss.
|
||||
logits: (B, K) target: (B, K)
|
||||
'''
|
||||
logsoftmax = nn.LogSoftmax(dim=-1).to(logits.device)
|
||||
output = torch.sum(-target * logsoftmax(logits), dim=1) # B
|
||||
|
||||
# output = torch.sum(-F.normalize(target) * logsoftmax(logits), dim=1) # B
|
||||
if self.reduction == 'mean':
|
||||
loss = torch.mean(output)
|
||||
elif self.reduction == 'sum':
|
||||
loss = torch.sum(output)
|
||||
|
||||
return loss
|
||||
|
||||
def forward(self, out_dict, target, ant_target):
|
||||
return self.loss(out_dict, target, ant_target)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
__all__ = [
|
||||
'build_criterion'
|
||||
]
|
||||
|
||||
from utils import Registry
|
||||
|
||||
CRITERIONS = Registry()
|
||||
|
||||
def build_criterion(cfg, device=None):
|
||||
criterion = CRITERIONS[cfg["loss"]](cfg)
|
||||
return criterion.to(device)
|
||||
@@ -0,0 +1,426 @@
|
||||
{
|
||||
"shard_0": {
|
||||
"class_index": [
|
||||
"background",
|
||||
"camera wearer start to speak"
|
||||
],
|
||||
"train_session_set": [
|
||||
"m5Aoywf51L4",
|
||||
"p99cIB7nHjw",
|
||||
"zGgMzBfPfaI",
|
||||
"O0eCtKU3FxY",
|
||||
"goZQ6iOIFYo",
|
||||
"9Tb-lSyhgoE",
|
||||
"GBxQtGUjAMA",
|
||||
"VXgt7keOC8I",
|
||||
"hZ2tin2J0P4",
|
||||
"Ur-P18sm4tI",
|
||||
"WfYmsNBQwg8",
|
||||
"AvMNaDDfrN0",
|
||||
"fW7pCN3wwdo",
|
||||
"xG6HBdTtS4s",
|
||||
"HcDlqUeDD9Q",
|
||||
"1DE3mKncHUA",
|
||||
"oEMX_quSMZY",
|
||||
"jmS3nWbGV5Y",
|
||||
"qUJRjPRH-u4",
|
||||
"aHsUgWfUcss",
|
||||
"ZfdQOmJkaK0",
|
||||
"4f43nYrfTnI",
|
||||
"qE1FnzkNjrE",
|
||||
"4NsmKhs9flk",
|
||||
"3wDSJ9ABzmo",
|
||||
"sY6_8QwRZsw",
|
||||
"hByfAzEoeKA",
|
||||
"vDEsV0AhcjM",
|
||||
"XGNhX3Nu1RE",
|
||||
"MkVT8K0V57A",
|
||||
"yR8y-wN8Clk",
|
||||
"P1dTOlO2sl8",
|
||||
"6UGC_wxTeB0",
|
||||
"d4Xud2Gqoxk",
|
||||
"IJ1aqePculQ",
|
||||
"o8Oxf5QBitQ",
|
||||
"XVci_V4qxmc",
|
||||
"ofDzhOS1Pv0",
|
||||
"mygTXBuMRF0",
|
||||
"iq6q82Jo8ZQ",
|
||||
"Kzc_ekBJ4QE",
|
||||
"WEeJ9B1i8HI",
|
||||
"rciqpxdCc9Y",
|
||||
"Rh7d2D7JVLI",
|
||||
"Qkx4hSlc8jk",
|
||||
"ixi7sVLFrWM",
|
||||
"n96-scSyoOQ",
|
||||
"YZ5xYioIQdA",
|
||||
"X76QxuWm1oA",
|
||||
"2I7d_LfK75Y",
|
||||
"AeUWBaYFMyo",
|
||||
"lsqXFgymLlI",
|
||||
"Dx_0jYMWN5o",
|
||||
"0raJ1-1BJ7s",
|
||||
"EGX8am5hWh0",
|
||||
"KjtZ_CLY1hQ",
|
||||
"R4FQI2BI3hw",
|
||||
"fAoqqtsNjbQ",
|
||||
"l6RwyA6jUdM",
|
||||
"GJW6dlagRnU",
|
||||
"j_FGvmbp6aM",
|
||||
"V2O15h1VrH8",
|
||||
"P3c70VbDP3E",
|
||||
"epAwpBdUiYU",
|
||||
"6dpv_JeyL8w",
|
||||
"dUKYxsvnuBM",
|
||||
"L2_zn7h8sOQ",
|
||||
"m09qdCkAhmY",
|
||||
"7BB71tIae7A",
|
||||
"6go4oViEkdc",
|
||||
"MYimxshUK1c",
|
||||
"v4eZXWcIRQU",
|
||||
"vGI_w1Ml6S0",
|
||||
"BeFndnkT00c",
|
||||
"9JHRbL1jmq4",
|
||||
"0rBaR5dd-Fs",
|
||||
"EvhkIS_-K-w",
|
||||
"pYVbLx-h5HI",
|
||||
"fVciOXYDlqg",
|
||||
"zWFH4N-CmDE",
|
||||
"Aw4cauzfX5E",
|
||||
"lxVpcQHtcWs",
|
||||
"32dkF-aXgug",
|
||||
"_USorgWOsUk",
|
||||
"1yVMU93nmv8",
|
||||
"aYkF4Ybk-l8",
|
||||
"Vwt18p0BqzM",
|
||||
"oOQM5Pvjut4",
|
||||
"_WWSMNtNYx0",
|
||||
"zzlhwg0IJ5Y",
|
||||
"lJhIffkUhr8",
|
||||
"PbS9nSH3mqM",
|
||||
"8EGVgiFpwhg",
|
||||
"5LhXpv5IFO4",
|
||||
"ibD4APVzWgg",
|
||||
"2nhcSQmopO4",
|
||||
"gri79OfCRvo",
|
||||
"lOwQN3QcRzE",
|
||||
"wQyluRNtqFo",
|
||||
"IZyx8zeiKB0",
|
||||
"mZyNfKyzDHU",
|
||||
"A9eGJWiuIKs",
|
||||
"d37DZogot1w",
|
||||
"7ZUSVabB6N4",
|
||||
"m62GQXq-358",
|
||||
"ArwPVsCZo2c",
|
||||
"QK-BFkpO8s4",
|
||||
"PgfX9bvmEhs",
|
||||
"l1jTUhwWJYA",
|
||||
"S1PGHO4evaI",
|
||||
"M6uPJa5B8Ns",
|
||||
"XXzz19J-1w8",
|
||||
"U4uV1xmUaB0",
|
||||
"skQ0OBpkKqY",
|
||||
"mfW57TDpMtc",
|
||||
"NAwKA75cDwE",
|
||||
"eIEBI2_Y14Q",
|
||||
"eWQGYpSoiYU",
|
||||
"3DPIUWhYtn8",
|
||||
"_elvaRJrhE0",
|
||||
"PAA8IFhWJzM",
|
||||
"1XDKqhq6bb0",
|
||||
"ngi9gy3DzRY",
|
||||
"Y0yONfuimeE",
|
||||
"g9_cAgHa_4Q",
|
||||
"BIG7jEw-j4w",
|
||||
"TFdom0lkd-0",
|
||||
"2zV2N8_DwWg",
|
||||
"kTz7XB_jpB4",
|
||||
"GuoglIuhNjY",
|
||||
"L98U3mO38gQ",
|
||||
"UfWDoRAzpC8",
|
||||
"W6TakaXCIIQ",
|
||||
"i6j1I6pha-M",
|
||||
"issHFC8Gbio",
|
||||
"4EzGoRkd6SI",
|
||||
"QoS0GgF4sI4",
|
||||
"A8hJg3D_TGY",
|
||||
"A3N10suCRCw",
|
||||
"6962tnWRsNY",
|
||||
"lS4p8osXSJs",
|
||||
"wbZ7ZhcZd6o",
|
||||
"fsIvQvIlKwc",
|
||||
"4jQSOSi2DA8",
|
||||
"Vpt6tZBvXQU",
|
||||
"kxOG3kpLmYw",
|
||||
"Mzjlz0nq9AA",
|
||||
"80cuXt2VSko",
|
||||
"VvswhogSiY0",
|
||||
"5Q6ky5VrOIA",
|
||||
"I_XtcUpNRF8",
|
||||
"QmtmTGycK9A",
|
||||
"qp3x8iy7vkA",
|
||||
"xGU8QDtPrQI",
|
||||
"uWWUfqth_BA",
|
||||
"WbM7w8r4VGQ",
|
||||
"bt6Ed1qjulw",
|
||||
"7vymT27scoY",
|
||||
"b9AdV6oVDVg",
|
||||
"nf2zqYUAOwg",
|
||||
"A-LNJc-1Jc0",
|
||||
"yCI-hpPiDVc",
|
||||
"Uu5DME4FqIM",
|
||||
"symjuH9iOTc",
|
||||
"mPf2MTnTBc0",
|
||||
"NDTlQ1KGvH8",
|
||||
"g9vuDzFQSRU",
|
||||
"B6iqnvleve0",
|
||||
"9bbszAD7JL0",
|
||||
"RIyMAuz7Ckg",
|
||||
"mlrQHtBRMhc",
|
||||
"3gaidlW7vig",
|
||||
"DGdV97q9ndk",
|
||||
"qthjlg0lXFg",
|
||||
"gWng5YLPOow",
|
||||
"iaYG-KL9XRM",
|
||||
"385414AVZcA",
|
||||
"7xhBnWGpI7c",
|
||||
"Nh0cmSQUs-E",
|
||||
"Rvtj0QHPulA",
|
||||
"rAtLksYQukw",
|
||||
"S9rB__zbbe0",
|
||||
"SNpMVsOPfHM",
|
||||
"zhF43F4kO2w",
|
||||
"HOUkcInyxN0",
|
||||
"x8ymkfr2Z1o",
|
||||
"Ix6Khtq6O-k",
|
||||
"VHs_JyPhmtE",
|
||||
"zJX3byjYSMc",
|
||||
"zdqX09zZNf8",
|
||||
"uPwlmwNQq8Y",
|
||||
"e4xNTXrk6wE",
|
||||
"P2PNUWN4GMs",
|
||||
"_WVHCYjHBVY",
|
||||
"5Jtv1GpPvHM",
|
||||
"AIZYucKOzhw",
|
||||
"vbsR_VDZXh0",
|
||||
"9Ew0lDHB0kM",
|
||||
"ejpkPVZHuMM",
|
||||
"47HlvCfubE4",
|
||||
"HzPkQSIG2m0",
|
||||
"e7mTRnNnOXg",
|
||||
"eZHx_biEECg",
|
||||
"FvP6WFKNP4Y",
|
||||
"g_TJ5XFEVfY",
|
||||
"8Utg2VEWsJg",
|
||||
"VMdBjL-tpoo",
|
||||
"zihP2Eqeqxk",
|
||||
"ILVqgOlntLM",
|
||||
"Z1BWQI8KPCg",
|
||||
"j8TRVp-7l3Q",
|
||||
"G5gJBZ-3OlY",
|
||||
"o1JUlGSirSE",
|
||||
"OsPdhUQ0snE",
|
||||
"-5uIaW2gKWQ",
|
||||
"03kiFNh-9ME",
|
||||
"w5W6mgDaJJA",
|
||||
"sYsgd_8AFoo",
|
||||
"vLg8kVUTmWU",
|
||||
"OK0J0xsuSWQ",
|
||||
"d284rjoU4Cs",
|
||||
"5lE6V4jrUl4",
|
||||
"bS1OpC9xu48",
|
||||
"ZUxpqU_J_n0",
|
||||
"AUliC0c_YIQ",
|
||||
"nJ58-d_viOo",
|
||||
"uIy3b2kzlrM",
|
||||
"vOTLfb0qjfY",
|
||||
"37_7tPZjC34",
|
||||
"MUwCdP5qceI",
|
||||
"lYvNi_ewd8U",
|
||||
"PDG7SzDBTC4",
|
||||
"QHQzoHolWvI",
|
||||
"YmvOEj00LB8",
|
||||
"fLhkXVx7Yw0",
|
||||
"Uj5nhugVgwo",
|
||||
"aHbrDu2o0WY",
|
||||
"yKW6vVgEPCM",
|
||||
"Q0WUgyqQ-4I",
|
||||
"xw3KOPJ9a3E",
|
||||
"5mrTo7c3bCM",
|
||||
"x0ft032W--4",
|
||||
"UdQxEbrIocc",
|
||||
"EHZHAFyv_Ks",
|
||||
"7DojKgPRLVk",
|
||||
"-1CahRD6lJ0",
|
||||
"f3TuYwl7AZE",
|
||||
"TgGTovCcS9Y",
|
||||
"AwyrJGeIN_g",
|
||||
"RbPJD4Wh-l8",
|
||||
"58bFdwYkDzU",
|
||||
"6gIEKGxL7zo",
|
||||
"BeuzJwTuChQ",
|
||||
"YebZyAzLZuc",
|
||||
"9T74b8EQBVY",
|
||||
"TqSVFjw1uNM",
|
||||
"WUYhCmfE1a0",
|
||||
"vRCVUQFUio4",
|
||||
"w2t9JdMNMgQ",
|
||||
"nn6-x9xXMeE",
|
||||
"FDFVPzLlzFs",
|
||||
"qFhvi4bH29E",
|
||||
"qdpsMXLsUZM",
|
||||
"JREk1QeSc9A",
|
||||
"jOVEs-4r1jc",
|
||||
"v4VZI9Ouzy0",
|
||||
"DMg5UUvf-C4",
|
||||
"26J5zRzQkOg",
|
||||
"YkiJ4iJfJbw",
|
||||
"_d8j8nfTEIk",
|
||||
"wMNb3hQXEUE",
|
||||
"1KI8ex22blk",
|
||||
"eCp2Jp7MtKg",
|
||||
"HNlpHBb5dC4",
|
||||
"C6_pDRdIo1k",
|
||||
"ZRWJJHrgP54",
|
||||
"L7jfx4veKIA",
|
||||
"p1a5_qltICM",
|
||||
"nZ0svM8DoY0",
|
||||
"miEzjCJVaZk",
|
||||
"TpC2tDCxABw",
|
||||
"SZmqoqCX__U",
|
||||
"dZ0bYteaI2Q",
|
||||
"GW-cYEzShmY",
|
||||
"iUYuPhutKVw",
|
||||
"97VSm6T2zMw",
|
||||
"jPiQyi5jr1M",
|
||||
"lrUi4nsPZ9M",
|
||||
"rXLeXke-k_w",
|
||||
"mEO4izUqZ7E",
|
||||
"4qI1LjMXfh0",
|
||||
"mVt6myGMa-0",
|
||||
"VJrnhh6FqEg",
|
||||
"vwCeBWbZvrE",
|
||||
"c08fl8-xmVE",
|
||||
"syZ3bvXIrpA",
|
||||
"i3ysNaulop4",
|
||||
"__t3rU0QjsE",
|
||||
"jT32uQQVGvU",
|
||||
"Fzvmh-R2EWk",
|
||||
"QApJSKrLg6E",
|
||||
"L5y3j8SdGSU",
|
||||
"3t8JGblR__0",
|
||||
"e1F90W40t6g",
|
||||
"j6L8UIa5z28",
|
||||
"Zmg2_C1FuVI",
|
||||
"XIXKJXsiBFc",
|
||||
"NVHB1VdWFo4",
|
||||
"-x7-8f7bsl4",
|
||||
"2FSxSiowkGI",
|
||||
"jgFIBIeFve0",
|
||||
"1BLuXK1M5CE",
|
||||
"zmpx6LUT2ow",
|
||||
"z-QUzJTsFro",
|
||||
"0sS4v13DAK8",
|
||||
"gMIBQ0mbRvM",
|
||||
"9kcaEXlTB5s",
|
||||
"ZYArPGMLS4k",
|
||||
"tejW9GMaucA",
|
||||
"z9r4bmXCWiI",
|
||||
"4Zpsb46iaLc",
|
||||
"52Odc8UbvC8",
|
||||
"x-B6-iYDHPs",
|
||||
"8GinSDc0Is8",
|
||||
"5lwbp5d_w0Y",
|
||||
"I4LpTQLzlHE",
|
||||
"lMl6-XH7ozY",
|
||||
"tOubscNbY1Q",
|
||||
"7gb7AH-oWcQ",
|
||||
"mDerqt9Ib-I",
|
||||
"gKV3FNd9WHE",
|
||||
"kH5L1E_r-3U",
|
||||
"sq5TRFxVp0w",
|
||||
"SrBhj7Xusk8",
|
||||
"RxlxEapsz2E",
|
||||
"aCD3_TziiKo",
|
||||
"_HPVZOkxdkk",
|
||||
"pNCjgVQ53Y8",
|
||||
"iPdT5GUoGvA",
|
||||
"cIrlet1oRw4",
|
||||
"1dyWUMBjMwQ",
|
||||
"XZQB3cq18yc",
|
||||
"i75Mn_AeFBY",
|
||||
"_0VwR9WPS-k",
|
||||
"q1JDjgnRdGY",
|
||||
"1JNEpfvRq0Y",
|
||||
"2FGj6HciQZA",
|
||||
"eLi59TtNwHY",
|
||||
"Z3W8OST7tmA",
|
||||
"F2dNYov8-xw",
|
||||
"kPfyJu_CrSA",
|
||||
"7I4Y2HtAwlY",
|
||||
"us-andJDfWw",
|
||||
"Q0Bzn1teWfo",
|
||||
"pADxBfOrYeQ",
|
||||
"KnA1tIR0qCE",
|
||||
"lBNPfBcb6pw",
|
||||
"zt9MIiBfxF8",
|
||||
"FwX12hPqCws",
|
||||
"6e2LXGrze2M",
|
||||
"0dvclhcFjSo",
|
||||
"UTkuDYOIxKo",
|
||||
"4n044cOTiNg",
|
||||
"VxksG15OgRY",
|
||||
"4193MkWdJcQ",
|
||||
"2bDX4Uzy8Wc",
|
||||
"SkyBNX2auEc",
|
||||
"JrSLspuJ2gk",
|
||||
"GZO7V-CWSJ8",
|
||||
"TYMIy4qlRzc",
|
||||
"jK7q_Li7LU0",
|
||||
"UiTIXDQEkPM"
|
||||
],
|
||||
"test_session_set": [
|
||||
"DTGtC7FC4oI",
|
||||
"CIWvk3gJ_7E",
|
||||
"A1fZ6rlOwxk",
|
||||
"cB8WCrmJEEA",
|
||||
"gGQ_2Ank9-U",
|
||||
"b91abcyMeNY",
|
||||
"9MuaFeyczM0",
|
||||
"rwZxhdK_UVU",
|
||||
"hd_WbrqYnTE",
|
||||
"4BWcqA9cFFk",
|
||||
"MuOkgCkob6E",
|
||||
"OdhFWA3Fgb0",
|
||||
"Pe3ZabrUiUk",
|
||||
"U0c_uv8DU08",
|
||||
"kTqt_3YJBbs",
|
||||
"eWXVVMi4bf8",
|
||||
"RCD-vXumMIs",
|
||||
"DuwiezJCZ3I",
|
||||
"BNgEqjAiKU4",
|
||||
"Cbn0t7eqaEs",
|
||||
"D3M9Q0WwuFY",
|
||||
"oO-Bv4iRZKI",
|
||||
"bBvqtbZdSZQ",
|
||||
"-mnoItCi32Y",
|
||||
"yQPxKfz-bwI",
|
||||
"ITVZ4cx5big",
|
||||
"uY5_e0L7mjw",
|
||||
"J_Pl9ftnNXE",
|
||||
"Jt_pb40MRy4",
|
||||
"E3hAj2niRNo",
|
||||
"kl2xVSxVECQ",
|
||||
"gzebuFs90Xg",
|
||||
"6DQSIzJ9J-g",
|
||||
"pJJlnryTYWg",
|
||||
"X87qsfVLFoc",
|
||||
"W7BIPWU1WHc",
|
||||
"Oj506EVOiQc",
|
||||
"DPEvWvcstxA",
|
||||
"Pk16UT5-zeM",
|
||||
"XMK1KEk5OdU",
|
||||
"PpNm5llsEW8",
|
||||
"sAp3rvablGk"
|
||||
]
|
||||
}
|
||||
}
|
||||
+1898
File diff suppressed because it is too large
Load Diff
+2
@@ -0,0 +1,2 @@
|
||||
from .dataset_builder import build_dataset, build_data_loader
|
||||
from .dataset import THUMOSDataset, FINEACTIONDataset
|
||||
+314
@@ -0,0 +1,314 @@
|
||||
import gc
|
||||
import json
|
||||
import os.path as osp
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
import torch.utils.data as data
|
||||
from configs.feature_sizes import FEATURE_SIZES
|
||||
from datasets.dataset_builder import DATA_LAYERS
|
||||
from utils.util import parse_flow_type, parse_rgb_type
|
||||
|
||||
|
||||
@DATA_LAYERS.register("THUMOS")
|
||||
@DATA_LAYERS.register("TVSERIES")
|
||||
@DATA_LAYERS.register("EasyCom")
|
||||
@DATA_LAYERS.register("EGO4D")
|
||||
@DATA_LAYERS.register("IEMOCAP")
|
||||
@DATA_LAYERS.register("IEMOCAP_DEBUG")
|
||||
@DATA_LAYERS.register("YTConv")
|
||||
class THUMOSDataset(data.Dataset):
|
||||
|
||||
def __init__(self, cfg, mode='train'):
|
||||
self.root_path = cfg['root_path']
|
||||
self.mode = mode
|
||||
self.training = mode == 'train'
|
||||
self.window_size = cfg['window_size']
|
||||
self.enc_steps = cfg['window_size'] if 'window_size' in cfg else None
|
||||
self.dec_steps = cfg['query_num'] if 'query_num' in cfg else None
|
||||
|
||||
self.stride = cfg['stride']
|
||||
data_name = cfg['data_name']
|
||||
self.model = cfg['model']
|
||||
self.vids = json.load(open(cfg['video_list_path']))[data_name][mode + '_session_set']
|
||||
self.num_classes = cfg['num_classes']
|
||||
self.inputs = []
|
||||
|
||||
self._load_features(cfg)
|
||||
self._init_features()
|
||||
|
||||
def _load_features(self, cfg):
|
||||
self.annotation_type = cfg['annotation_type']
|
||||
self.rgb_type = cfg['rgb_type']
|
||||
self.flow_type = cfg['flow_type']
|
||||
self.target_all = {}
|
||||
self.rgb_inputs = {}
|
||||
self.flow_inputs = {}
|
||||
dummy_target = np.zeros((self.window_size-1, self.num_classes))
|
||||
|
||||
rgb_type = parse_rgb_type(cfg['rgb_type'])
|
||||
flow_type = parse_flow_type(cfg['flow_type'])
|
||||
|
||||
dummy_rgb = np.zeros((self.window_size-1, FEATURE_SIZES[rgb_type]))
|
||||
dummy_flow = np.zeros((self.window_size-1, FEATURE_SIZES[flow_type]))
|
||||
for vid in self.vids:
|
||||
target = np.load(osp.join(self.root_path, self.annotation_type, vid + '.npy'))
|
||||
rgb = np.load(osp.join(self.root_path, self.rgb_type, vid + '.npy'))
|
||||
flow = np.load(osp.join(self.root_path, self.flow_type, vid + '.npy'))
|
||||
|
||||
if self.training:
|
||||
self.target_all[vid] = np.concatenate((dummy_target, target), axis=0)
|
||||
self.rgb_inputs[vid] = np.concatenate((dummy_rgb, rgb), axis=0)
|
||||
self.flow_inputs[vid] = np.concatenate((dummy_flow, flow), axis=0)
|
||||
else:
|
||||
self.target_all[vid] = target
|
||||
self.rgb_inputs[vid] = rgb
|
||||
self.flow_inputs[vid] = flow
|
||||
|
||||
def _init_features(self):
|
||||
del self.inputs
|
||||
gc.collect()
|
||||
self.inputs = []
|
||||
if self.model == 'MiniROAD':
|
||||
for vid in self.vids:
|
||||
target = self.target_all[vid]
|
||||
|
||||
|
||||
if self.training:
|
||||
seed = np.random.randint(self.stride)
|
||||
for start, end in zip(range(seed, target.shape[0], self.stride),
|
||||
range(seed + self.window_size, target.shape[0]+1, self.stride)):
|
||||
self.inputs.append([
|
||||
vid, start, end, target[start:end]
|
||||
])
|
||||
|
||||
else:
|
||||
start = 0
|
||||
end = target.shape[0]
|
||||
self.inputs.append([
|
||||
vid, start, end, target[start:end]
|
||||
])
|
||||
|
||||
elif self.model == 'OADTR':
|
||||
for vid in self.vids:
|
||||
target = self.target_all[vid]
|
||||
|
||||
|
||||
seed = np.random.randint(self.stride) if self.training else 0
|
||||
|
||||
for start, end in zip(
|
||||
range(seed, target.shape[0], 1),
|
||||
range(seed + self.window_size, target.shape[0]+1, 1)
|
||||
):
|
||||
enc_target = target[start:end]
|
||||
dec_target = target[end:end + self.dec_steps]
|
||||
distance_target, class_h_target = self.get_distance_target(target[start:end])
|
||||
|
||||
self.inputs.append([
|
||||
vid, start, end, enc_target, distance_target, class_h_target, dec_target
|
||||
])
|
||||
|
||||
else:
|
||||
for vid in self.vids:
|
||||
target = self.target_all[vid]
|
||||
seed = np.random.randint(self.stride) if self.training else 0
|
||||
for start, end in zip(
|
||||
range(seed, target.shape[0], 1),
|
||||
range(seed + self.window_size, target.shape[0]+1, 1)
|
||||
):
|
||||
self.inputs.append([
|
||||
vid, start, end, target[start:end]
|
||||
])
|
||||
|
||||
def get_distance_target(self, target_vector):
|
||||
target_matrix = np.zeros(self.enc_steps - 1)
|
||||
target_argmax = target_vector[self.enc_steps-1].argmax()
|
||||
for i in range(self.enc_steps - 1):
|
||||
if target_vector[i].argmax() == target_argmax:
|
||||
target_matrix[i] = 1.
|
||||
return target_matrix, target_vector[self.enc_steps-1]
|
||||
|
||||
def __getitem__(self, index):
|
||||
if self.model == 'MiniROAD' or self.model == 'Transformer':
|
||||
vid, start, end, target = self.inputs[index]
|
||||
rgb_input = self.rgb_inputs[vid][start:end]
|
||||
flow_input = self.flow_inputs[vid][start:end]
|
||||
rgb_input = torch.tensor(rgb_input.astype(np.float32))
|
||||
flow_input = torch.tensor(flow_input.astype(np.float32))
|
||||
target = torch.tensor(target.astype(np.float32))
|
||||
return vid, rgb_input, flow_input, target
|
||||
|
||||
elif self.model == 'OADTR':
|
||||
vid, start, end, enc_target, distance_target, class_h_target, dec_target = self.inputs[index]
|
||||
rgb_input = self.rgb_inputs[vid][start:end]
|
||||
flow_input = self.flow_inputs[vid][start:end]
|
||||
rgb_input = torch.tensor(rgb_input.astype(np.float32))
|
||||
flow_input = torch.tensor(flow_input.astype(np.float32))
|
||||
enc_target = torch.tensor(enc_target.astype(np.float32))
|
||||
distance_target = torch.tensor(distance_target.astype(np.float32))
|
||||
class_h_target = torch.tensor(class_h_target.astype(np.float32))
|
||||
dec_target = torch.tensor(dec_target.astype(np.float32))
|
||||
return vid, rgb_input, flow_input, enc_target, distance_target, class_h_target, dec_target
|
||||
|
||||
def __len__(self):
|
||||
return len(self.inputs)
|
||||
|
||||
|
||||
@DATA_LAYERS.register("THUMOS_ANTICIPATION")
|
||||
@DATA_LAYERS.register("TVSERIES_ANTICIPATION")
|
||||
@DATA_LAYERS.register("IEMOCAP_ANTICIPATION")
|
||||
@DATA_LAYERS.register("EasyCom_ANTICIPATION")
|
||||
@DATA_LAYERS.register("YTConv_ANTICIPATION")
|
||||
@DATA_LAYERS.register("EGO4D_ANTICIPATION")
|
||||
class THUMOSDataset(data.Dataset):
|
||||
|
||||
def __init__(self, cfg, mode='train'):
|
||||
self.cfg = cfg
|
||||
self.root_path = cfg['root_path']
|
||||
self.mode = mode
|
||||
self.training = mode == 'train'
|
||||
self.window_size = cfg['window_size']
|
||||
self.stride = cfg['stride']
|
||||
self.anticipation_length = cfg['anticipation_length']
|
||||
data_name = cfg["data_name"].split('_')[0]
|
||||
if 'data_info_name' in cfg.keys() and cfg['data_info_name'] is not None:
|
||||
data_name = cfg['data_info_name']
|
||||
|
||||
self.vids = json.load(open(cfg['video_list_path']))[data_name][mode + '_session_set']
|
||||
print(f'data_name: {data_name}')
|
||||
print(f'{mode} session set length: {len(self.vids)}')
|
||||
self.num_classes = cfg['num_classes']
|
||||
self.inputs = []
|
||||
|
||||
self._load_features(cfg)
|
||||
self._init_features()
|
||||
|
||||
def _load_features(self, cfg):
|
||||
self.annotation_type = cfg['annotation_type']
|
||||
self.rgb_type = cfg['rgb_type']
|
||||
self.flow_type = cfg['flow_type']
|
||||
|
||||
self.target_all = {}
|
||||
self.rgb_inputs = {}
|
||||
self.flow_inputs = {}
|
||||
|
||||
dummy_target = np.zeros((self.window_size-1, self.num_classes))
|
||||
dummy_rgb = np.zeros((self.window_size-1, FEATURE_SIZES[cfg['rgb_type']]))
|
||||
dummy_flow = np.zeros((self.window_size-1, FEATURE_SIZES[cfg['flow_type']]))
|
||||
|
||||
for vid in self.vids:
|
||||
target = np.load(osp.join(self.root_path, self.annotation_type, vid + '.npy'))
|
||||
rgb = np.load(osp.join(self.root_path, self.rgb_type, vid + '.npy'))
|
||||
flow = np.load(osp.join(self.root_path, self.flow_type, vid + '.npy'))
|
||||
|
||||
if self.training:
|
||||
self.target_all[vid] = np.concatenate((dummy_target, target), axis=0)
|
||||
self.rgb_inputs[vid] = np.concatenate((dummy_rgb, rgb), axis=0)
|
||||
self.flow_inputs[vid] = np.concatenate((dummy_flow, flow), axis=0)
|
||||
else:
|
||||
self.target_all[vid] = target
|
||||
self.rgb_inputs[vid] = rgb
|
||||
self.flow_inputs[vid] = flow
|
||||
|
||||
def _init_features(self):
|
||||
del self.inputs
|
||||
gc.collect()
|
||||
self.inputs = []
|
||||
|
||||
for vid in self.vids:
|
||||
target = self.target_all[vid]
|
||||
if self.training:
|
||||
seed = np.random.randint(self.stride)
|
||||
for start, end in zip(range(seed, target.shape[0], self.stride),
|
||||
range(seed + self.window_size, target.shape[0]-self.anticipation_length, self.stride)):
|
||||
self.inputs.append([
|
||||
vid, start, end, target[start:end], target[end:end+self.anticipation_length]
|
||||
])
|
||||
else:
|
||||
start = 0
|
||||
end = target.shape[0] - self.anticipation_length
|
||||
ant_target = []
|
||||
|
||||
for s in range(1, target.shape[0] - self.anticipation_length + 1):
|
||||
ant_target.append(target[s:s + self.anticipation_length])
|
||||
|
||||
self.inputs.append([
|
||||
vid, start, end, target[start:end], np.array(ant_target)
|
||||
])
|
||||
|
||||
|
||||
|
||||
def __getitem__(self, index):
|
||||
vid, start, end, target, ant_target = self.inputs[index]
|
||||
|
||||
|
||||
rgb_input = self.rgb_inputs[vid][start:end]
|
||||
flow_input = self.flow_inputs[vid][start:end]
|
||||
|
||||
rgb_input = torch.tensor(rgb_input.astype(np.float32))
|
||||
flow_input = torch.tensor(flow_input.astype(np.float32))
|
||||
|
||||
target = torch.tensor(target.astype(np.float32))
|
||||
ant_target = torch.tensor(ant_target.astype(np.float32))
|
||||
|
||||
return vid, rgb_input, flow_input, target, ant_target
|
||||
|
||||
def __len__(self):
|
||||
return len(self.inputs)
|
||||
|
||||
|
||||
@DATA_LAYERS.register("FINEACTION")
|
||||
class FINEACTIONDataset(data.Dataset):
|
||||
|
||||
def __init__(self, cfg, mode='train'):
|
||||
self.root_path = cfg['root_path']
|
||||
self.mode = mode
|
||||
self.training = mode == 'train'
|
||||
self.window_size = cfg['window_size']
|
||||
self.stride = cfg['stride']
|
||||
data_name = cfg['data_name']
|
||||
self.vids = json.load(open(cfg['video_list_path']))[data_name][mode + '_session_set']
|
||||
self.num_classes = cfg['num_classes']
|
||||
self.inputs = []
|
||||
self._load_features(cfg)
|
||||
self._init_features()
|
||||
|
||||
def _load_features(self, cfg):
|
||||
self.annotation_type = cfg['annotation_type']
|
||||
self.rgb_type = cfg['rgb_type']
|
||||
self.flow_type = cfg['flow_type']
|
||||
|
||||
def _init_features(self, seed=0):
|
||||
|
||||
del self.inputs
|
||||
gc.collect()
|
||||
self.inputs = []
|
||||
for vid in self.vids:
|
||||
target = np.load(osp.join(self.root_path, self.annotation_type, vid + '.npy'))
|
||||
if self.training:
|
||||
seed = np.random.randint(self.stride)
|
||||
for start, end in zip(range(seed, target.shape[0], self.stride),
|
||||
range(seed + self.window_size, target.shape[0]+1, self.stride)):
|
||||
self.inputs.append([
|
||||
vid, start, end
|
||||
])
|
||||
else:
|
||||
start = 0
|
||||
end = target.shape[0]
|
||||
self.inputs.append([
|
||||
vid, start, end
|
||||
])
|
||||
|
||||
def __getitem__(self, index):
|
||||
vid, start, end = self.inputs[index]
|
||||
rgb_input = np.load(osp.join(self.root_path, self.rgb_type, vid + '.npy'), mmap_mode='r')[start:end]
|
||||
flow_input = np.load(osp.join(self.root_path, self.flow_type, vid + '.npy'), mmap_mode='r')[start:end]
|
||||
target = np.load(osp.join(self.root_path, self.annotation_type, vid + '.npy'), mmap_mode='r')[start:end]
|
||||
rgb_input = torch.tensor(rgb_input.astype(np.float32))
|
||||
flow_input = torch.tensor(flow_input.astype(np.float32))
|
||||
target = torch.tensor(target.astype(np.float32))
|
||||
return rgb_input, flow_input, target
|
||||
|
||||
def __len__(self):
|
||||
return len(self.inputs)
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
__all__ = [
|
||||
'build_dataset',
|
||||
'build_data_loader',
|
||||
]
|
||||
|
||||
import torch.utils.data as data
|
||||
from utils import Registry
|
||||
|
||||
DATA_LAYERS = Registry()
|
||||
|
||||
def build_dataset(cfg):
|
||||
data_layer = DATA_LAYERS[f'{cfg["data_name"]}']
|
||||
return data_layer
|
||||
|
||||
def build_data_loader(cfg, mode):
|
||||
data_layer = build_dataset(cfg)
|
||||
data_loader = data.DataLoader(
|
||||
dataset=data_layer(cfg, mode),
|
||||
batch_size=cfg["batch_size"] if mode == 'train' else cfg["test_batch_size"],
|
||||
shuffle=True if mode == 'train' else False,
|
||||
num_workers=cfg["num_workers"],
|
||||
pin_memory=False,
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return data_loader
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
import argparse
|
||||
import os
|
||||
import os.path as osp
|
||||
|
||||
import torch
|
||||
import wandb
|
||||
import yaml
|
||||
from criterions import build_criterion
|
||||
from model import build_model
|
||||
from torch.utils.tensorboard import SummaryWriter
|
||||
from trainer import build_eval, build_trainer
|
||||
from utils import *
|
||||
from utils import get_logger
|
||||
|
||||
from datasets import build_data_loader
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--config", type=str)
|
||||
parser.add_argument("--eval", action="store_true")
|
||||
parser.add_argument("--ckpt", type=str)
|
||||
parser.add_argument("--amp", action="store_true")
|
||||
parser.add_argument("--tensorboard", action="store_true")
|
||||
parser.add_argument("--wandb", action="store_true")
|
||||
parser.add_argument("--lr_scheduler", action="store_true")
|
||||
parser.add_argument("--no_rgb", action="store_true")
|
||||
parser.add_argument("--no_flow", action="store_true")
|
||||
parser.add_argument(
|
||||
"--save-as-cfg",
|
||||
action="store_true",
|
||||
help="Save outputs with config name in the output directory",
|
||||
)
|
||||
parser.add_argument("--debug", action="store_true")
|
||||
parser.add_argument(
|
||||
"--save-preds",
|
||||
action="store_true",
|
||||
help="Save predictions in the output directory",
|
||||
)
|
||||
# parser.add_argument('--model', type=str, default=None)
|
||||
args = parser.parse_args()
|
||||
|
||||
# combine argparse and yaml
|
||||
opt = yaml.load(open(args.config), Loader=yaml.FullLoader)
|
||||
opt.update(vars(args))
|
||||
cfg = opt
|
||||
|
||||
if "checkpoint" in cfg and cfg["checkpoint"] is not None and args.ckpt is None:
|
||||
args.ckpt = cfg["checkpoint"]
|
||||
print(f"checkpoint is given in the config, set args.ckpt to {args.ckpt}")
|
||||
|
||||
seed = 20 if "seed" not in cfg else cfg["seed"]
|
||||
set_seed(seed)
|
||||
|
||||
device = "cuda" if torch.cuda.is_available() else "cpu"
|
||||
|
||||
annotation_type = os.path.basename(cfg["annotation_type"])
|
||||
|
||||
identifier = os.path.basename(cfg["config"])[:-5]
|
||||
identifier += "_"
|
||||
if cfg["no_flow"] == False:
|
||||
identifier += "A"
|
||||
if cfg["no_rgb"] == False:
|
||||
identifier += "V"
|
||||
|
||||
if cfg["save_as_cfg"] == True:
|
||||
output_dir = osp.join("output_cfg", identifier)
|
||||
result_path = create_outdir(output_dir, overwrite=True)
|
||||
else:
|
||||
identifier = get_identifier(cfg)
|
||||
annotation_type = os.path.basename(cfg["annotation_type"])
|
||||
identifier += f"_{annotation_type}"
|
||||
result_path = create_outdir(osp.join(cfg["output_path"], identifier))
|
||||
|
||||
logger = get_logger(result_path)
|
||||
logger.info(f"Identifier: {identifier}")
|
||||
logger.info(f"result_path: {result_path}")
|
||||
logger.info(cfg)
|
||||
|
||||
testloader = build_data_loader(cfg, mode="test")
|
||||
print("testloader loaded")
|
||||
model = build_model(cfg, device)
|
||||
evaluate = build_eval(cfg)
|
||||
|
||||
trainloader = build_data_loader(cfg, mode="train")
|
||||
print("trainloader loaded")
|
||||
|
||||
# evaluation
|
||||
if args.eval == True and cfg["ckpt"] == None:
|
||||
print("Automatically find the best checkpoint")
|
||||
ckpt_root = os.path.join("output_cfg", identifier, "ckpts")
|
||||
ckpt_dirs = os.listdir(ckpt_root)
|
||||
|
||||
ckpt_path = os.path.join(ckpt_root, "best.pth")
|
||||
if os.path.exists(ckpt_path):
|
||||
args.ckpt = ckpt_path
|
||||
print(f"args.ckpt set to {args.ckpt}")
|
||||
|
||||
else:
|
||||
ckpt_path = os.path.join(ckpt_root, "pred", "best.pth")
|
||||
if os.path.exists(ckpt_path):
|
||||
args.ckpt = ckpt_path
|
||||
print(f"args.ckpt set to {args.ckpt}")
|
||||
|
||||
print("Warninig) no ckpt_path found, there can be multiple ckpts")
|
||||
print(f"ckpt_dirs: {ckpt_dirs}")
|
||||
pred_dir_number = 0
|
||||
ckpt_dirs.sort(reverse=True)
|
||||
|
||||
if args.ckpt == None:
|
||||
for ckpt_dir in ckpt_dirs:
|
||||
ckpt_path = os.path.join(ckpt_root, ckpt_dir, "best.pth")
|
||||
|
||||
if os.path.exists(ckpt_path):
|
||||
args.ckpt = ckpt_path
|
||||
print(f"Found ckpt, args.ckpt was None, set to {args.ckpt}")
|
||||
break
|
||||
|
||||
if args.ckpt == None:
|
||||
raise ValueError("No checkpoint found, args.ckpt is None")
|
||||
|
||||
# load checkpoint if args.ckpt is given
|
||||
if args.eval == True:
|
||||
if args.ckpt == None:
|
||||
raise ValueError("Please specify the checkpoint path")
|
||||
checkpoint = torch.load(args.ckpt)
|
||||
model.load_state_dict(checkpoint)
|
||||
|
||||
elif args.eval == False and args.ckpt != None:
|
||||
checkpoint = torch.load(args.ckpt)
|
||||
print(f"Load checkpoint from {args.ckpt}")
|
||||
model_dict = model.state_dict()
|
||||
|
||||
unnecessary_keys = []
|
||||
matched_ckpt = {}
|
||||
# Filter out unnecessary keys
|
||||
for k, v in checkpoint.items():
|
||||
if not (k in model_dict and model_dict[k].shape == checkpoint[k].shape):
|
||||
unnecessary_keys.append(k)
|
||||
elif (
|
||||
"except_classifier" in cfg
|
||||
and cfg["except_classifier"] == True
|
||||
and "classification" in k
|
||||
):
|
||||
print(f"Unnecessary key: {k} is classifier")
|
||||
unnecessary_keys.append(k)
|
||||
else:
|
||||
matched_ckpt[k] = v
|
||||
|
||||
# Overwrite entries in the existing state dict
|
||||
model_dict.update(matched_ckpt)
|
||||
|
||||
# Load the new state dict
|
||||
model.load_state_dict(model_dict)
|
||||
|
||||
print(f"Unnecessary keys: {unnecessary_keys}")
|
||||
|
||||
if cfg["wandb"] == True:
|
||||
# args.tensorboard = True
|
||||
print("wandb init")
|
||||
os.environ["WANDB__SERVICE_WAIT"] = "3000"
|
||||
wandb.init(
|
||||
project="egospeak",
|
||||
tags=[cfg["model"], cfg["data_name"], annotation_type],
|
||||
config=cfg,
|
||||
name=os.path.basename(result_path),
|
||||
)
|
||||
|
||||
if args.eval == True or args.debug == True:
|
||||
if args.eval == True and args.ckpt == None:
|
||||
raise ValueError("Please specify the checkpoint path")
|
||||
|
||||
logger.info(f'Dataset: {cfg["data_name"]}, Model: {cfg["model"]}')
|
||||
logger.info(f"Result path:{result_path}")
|
||||
|
||||
for phase in ["test"]:
|
||||
if phase == "train":
|
||||
raise NotImplementedError("evaluation on train split is not implemented")
|
||||
dataloader = trainloader
|
||||
|
||||
elif phase == "test":
|
||||
dataloader = testloader
|
||||
|
||||
mAP, mcAP, preds = evaluate(
|
||||
model, dataloader, logger, result_path=result_path
|
||||
)
|
||||
|
||||
pred_path = osp.join(result_path, "eval")
|
||||
os.makedirs(pred_path, exist_ok=True)
|
||||
|
||||
if args.save_preds == True:
|
||||
print(f"pred_path: {pred_path}")
|
||||
for k, v in preds.items():
|
||||
np.save(osp.join(pred_path, f"{k}.npy"), v)
|
||||
|
||||
logger.info(f'{phase}) {cfg["task"]} result: {mAP*100:.2f} mAP')
|
||||
logger.info(f'{phase}) {cfg["task"]} result: {mcAP*100:.2f} mcAP')
|
||||
exit()
|
||||
|
||||
criterion = build_criterion(cfg, device)
|
||||
train_one_epoch = build_trainer(cfg)
|
||||
optim = torch.optim.AdamW if cfg["optimizer"] == "AdamW" else torch.optim.Adam
|
||||
optimizer = optim(
|
||||
[{"params": model.parameters(), "initial_lr": cfg["lr"]}],
|
||||
lr=cfg["lr"],
|
||||
weight_decay=cfg["weight_decay"],
|
||||
)
|
||||
|
||||
scheduler = (
|
||||
build_lr_scheduler(cfg, optimizer, len(trainloader))
|
||||
if args.lr_scheduler
|
||||
else None
|
||||
)
|
||||
writer = SummaryWriter(osp.join(result_path, "runs")) if args.tensorboard else None
|
||||
scaler = torch.cuda.amp.GradScaler() if args.amp else None
|
||||
total_params = sum(p.numel() for p in model.parameters())
|
||||
|
||||
logger.info(f'Dataset: {cfg["data_name"]}, Model: {cfg["model"]}')
|
||||
logger.info(
|
||||
f'lr:{cfg["lr"]} | Weight Decay:{cfg["weight_decay"]} | Window Size:{cfg["window_size"]} | Batch Size:{cfg["batch_size"]}'
|
||||
)
|
||||
logger.info(
|
||||
f'Total epoch:{cfg["num_epoch"]} | Total Params:{total_params/1e6:.1f} M | Optimizer: {cfg["optimizer"]}'
|
||||
)
|
||||
logger.info(f"Output Path:{result_path}")
|
||||
|
||||
best_mAP, best_epoch = 0, 0
|
||||
best_cAP, best_cAP_epoch = 0, 0
|
||||
|
||||
pred_save_path = osp.join(result_path, "ckpts", "pred")
|
||||
print(f"pred_save_path: {pred_save_path}")
|
||||
|
||||
# if you want to overwrite, change the code
|
||||
os.makedirs(pred_save_path, exist_ok=True)
|
||||
|
||||
for epoch in range(1, cfg["num_epoch"] + 1):
|
||||
epoch_loss = train_one_epoch(
|
||||
trainloader,
|
||||
model,
|
||||
criterion,
|
||||
optimizer,
|
||||
scaler,
|
||||
epoch,
|
||||
cfg["wandb"],
|
||||
writer,
|
||||
scheduler=scheduler,
|
||||
)
|
||||
trainloader.dataset._init_features()
|
||||
|
||||
mAP, cAP, preds = evaluate(
|
||||
model, testloader, logger, criterion, cfg["wandb"], epoch, writer
|
||||
)
|
||||
|
||||
if mAP > best_mAP:
|
||||
best_mAP = mAP
|
||||
best_epoch = epoch
|
||||
torch.save(model.state_dict(), osp.join(pred_save_path, "best.pth"))
|
||||
|
||||
if args.save_preds == True:
|
||||
for k, v in preds.items():
|
||||
np.save(osp.join(pred_save_path, f"{k}.npy"), v)
|
||||
|
||||
if cAP > best_cAP:
|
||||
best_cAP = cAP
|
||||
best_cAP_epoch = epoch
|
||||
torch.save(model.state_dict(), osp.join(pred_save_path, "best_cAP.pth"))
|
||||
|
||||
logger.info(
|
||||
f'Epoch {epoch} mAP: {mAP*100:.2f} | Best mAP: {best_mAP*100:.2f} at epoch {best_epoch}, iter {epoch*cfg["batch_size"]*len(trainloader)} | train_loss: {epoch_loss/len(trainloader):.4f}, lr: {optimizer.param_groups[0]["lr"]:.7f}'
|
||||
)
|
||||
logger.info(
|
||||
f'Epoch {epoch} cAP: {cAP*100:.2f} | Best cAP: {best_cAP*100:.2f} at epoch {best_epoch}, iter {epoch*cfg["batch_size"]*len(trainloader)} | train_loss: {epoch_loss/len(trainloader):.4f}, lr: {optimizer.param_groups[0]["lr"]:.7f}'
|
||||
)
|
||||
|
||||
|
||||
if cfg["wandb"] == True:
|
||||
logger.info("Wandb logging")
|
||||
wandb.log(
|
||||
{
|
||||
"Epoch": epoch,
|
||||
"Train Loss (Epoch)": epoch_loss / len(trainloader),
|
||||
"Learning Rate": optimizer.param_groups[0]["lr"],
|
||||
"anticipation average mAP": mAP,
|
||||
"anticipation average cAP": cAP,
|
||||
}
|
||||
)
|
||||
|
||||
# os.rename(osp.join(result_path, 'ckpts', 'best.pth'), osp.join(result_path, 'ckpts', f'best_{best_mAP*100:.2f}.pth'))
|
||||
# save the training result and config as txt file
|
||||
with open(osp.join(result_path, "result.txt"), "w") as f:
|
||||
f.write(f"Best mAP: {best_mAP*100:.2f} at epoch {best_epoch}\n")
|
||||
f.write(f"Best cAP: {best_cAP*100:.2f} at epoch {best_cAP_epoch}\n")
|
||||
f.write(f"Config: {cfg}")
|
||||
|
||||
wandb.finish() if args.wandb else None
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
from .mamba import OADMamba
|
||||
from .model_builder import build_model
|
||||
from .rnn import MROAD
|
||||
from .transformer_models import ViTEnc
|
||||
@@ -0,0 +1 @@
|
||||
from .mamba import OADMamba, OADMambaA
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
import sys
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from configs.feature_sizes import FEATURE_SIZES
|
||||
from mamba_ssm import Mamba
|
||||
# from mambapy.mamba import Mamba, MambaConfig
|
||||
from model.model_builder import META_ARCHITECTURES
|
||||
from utils.util import parse_flow_type, parse_rgb_type
|
||||
|
||||
|
||||
@META_ARCHITECTURES.register("Mamba")
|
||||
class OADMamba(nn.Module):
|
||||
|
||||
def __init__(self, cfg):
|
||||
super(OADMamba, self).__init__()
|
||||
self.use_flow = not cfg['no_flow']
|
||||
self.use_rgb = not cfg['no_rgb']
|
||||
|
||||
self.input_dim = 0
|
||||
|
||||
rgb_type = parse_rgb_type(cfg['rgb_type'])
|
||||
flow_type = parse_flow_type(cfg['flow_type'])
|
||||
|
||||
if self.use_rgb:
|
||||
self.input_dim += FEATURE_SIZES[rgb_type]
|
||||
if self.use_flow:
|
||||
self.input_dim += FEATURE_SIZES[flow_type]
|
||||
|
||||
self.hidden_dim = cfg['hidden_dim']
|
||||
self.num_layers = cfg['num_layers']
|
||||
|
||||
# raise NotImplementedError("The following code is not implemented yet")
|
||||
self.out_dim = cfg['num_classes']
|
||||
self.window_size = cfg['window_size']
|
||||
|
||||
self.relu = nn.ReLU()
|
||||
|
||||
self.embedding_dim = cfg['embedding_dim']
|
||||
self.d_state = cfg['d_state']
|
||||
self.d_conv = cfg['d_conv']
|
||||
self.expand = cfg['expand']
|
||||
|
||||
# self.mamba_config = MambaConfig(d_model=self.embedding_dim, n_layers=self.num_layers)
|
||||
# self.mamba = Mamba(self.mamba_config)
|
||||
self.mamba = Mamba(
|
||||
d_model=self.embedding_dim, # Model dimension d_model
|
||||
d_state=self.d_state, # SSM state expansion factor
|
||||
d_conv=self.d_conv, # Local convolution width
|
||||
expand=self.expand, # Block expansion factor
|
||||
)
|
||||
|
||||
# self.gru = nn.GRU(self.embedding_dim, self.hidden_dim, self.num_layers, batch_first=True)
|
||||
self.layer1 = nn.Sequential(
|
||||
nn.Linear(self.input_dim, self.embedding_dim),
|
||||
nn.LayerNorm(self.embedding_dim),
|
||||
nn.ReLU(),
|
||||
nn.Dropout(p=cfg['dropout']),
|
||||
)
|
||||
self.f_classification = nn.Sequential(
|
||||
nn.Linear(self.hidden_dim, self.out_dim)
|
||||
)
|
||||
# self.h0 = torch.nn.Parameter(torch.zeros(1, 1, self.hidden_dim))
|
||||
self.h0 = torch.zeros(self.num_layers, 1, self.hidden_dim)
|
||||
|
||||
def forward(self, rgb_input, flow_input):
|
||||
x = torch.empty(0, device='cuda:0')
|
||||
if self.use_rgb:
|
||||
x = torch.cat((x, rgb_input), dim=2) # Concatenating along the channel dimension
|
||||
if self.use_flow:
|
||||
x = torch.cat((x, flow_input), dim=2)
|
||||
|
||||
x = self.layer1(x)
|
||||
ht = self.mamba(x)
|
||||
|
||||
logits = self.f_classification(ht)
|
||||
out_dict = {}
|
||||
if self.training:
|
||||
out_dict['logits'] = logits
|
||||
else:
|
||||
pred_scores = F.softmax(logits, dim=-1)
|
||||
out_dict['logits'] = pred_scores
|
||||
return out_dict
|
||||
|
||||
@META_ARCHITECTURES.register("MambaA")
|
||||
class OADMambaA(nn.Module):
|
||||
|
||||
def __init__(self, cfg):
|
||||
super(OADMambaA, self).__init__()
|
||||
self.use_flow = not cfg['no_flow']
|
||||
self.use_rgb = not cfg['no_rgb']
|
||||
self.input_dim = 0
|
||||
if self.use_rgb:
|
||||
self.input_dim += FEATURE_SIZES[cfg['rgb_type']]
|
||||
if self.use_flow:
|
||||
self.input_dim += FEATURE_SIZES[cfg['flow_type']]
|
||||
|
||||
self.hidden_dim = cfg['hidden_dim']
|
||||
self.num_layers = cfg['num_layers']
|
||||
self.anticipation_length = cfg["anticipation_length"]
|
||||
self.out_dim = cfg['num_classes']
|
||||
|
||||
self.embedding_dim = cfg['embedding_dim']
|
||||
self.d_state = cfg['d_state'] if 'd_state' in cfg else 16
|
||||
self.d_conv = cfg['d_conv'] if 'd_conv' in cfg else 4
|
||||
self.expand = cfg['expand'] if 'expand' in cfg else 2
|
||||
|
||||
self.layer1 = nn.Sequential(
|
||||
nn.Linear(self.input_dim, self.embedding_dim),
|
||||
nn.LayerNorm(self.embedding_dim),
|
||||
nn.ReLU(),
|
||||
nn.Dropout(p=cfg['dropout'])
|
||||
)
|
||||
self.actionness = cfg['actionness']
|
||||
if self.actionness:
|
||||
self.f_actionness = nn.Sequential(
|
||||
nn.Linear(self.hidden_dim, 1),
|
||||
)
|
||||
self.relu = nn.ReLU()
|
||||
# self.mamba_config = MambaConfig(d_model=self.embedding_dim, n_layers=self.num_layers)
|
||||
# self.mamba = Mamba(self.mamba_config)
|
||||
self.mamba = Mamba(
|
||||
d_model=self.embedding_dim, # Model dimension d_model
|
||||
d_state=self.d_state, # SSM state expansion factor
|
||||
d_conv=self.d_conv, # Local convolution width
|
||||
expand=self.expand, # Block expansion facto
|
||||
)
|
||||
|
||||
# self.gru = nn.GRU(self.embedding_dim, self.hidden_dim, self.num_layers, batch_first=True)
|
||||
self.f_classification = nn.Sequential(
|
||||
nn.Linear(self.embedding_dim, self.out_dim)
|
||||
)
|
||||
self.anticipation_layer = nn.Sequential(
|
||||
nn.Linear(self.embedding_dim, self.anticipation_length*self.embedding_dim),
|
||||
)
|
||||
self.cfg = cfg
|
||||
|
||||
def forward(self, rgb_input, flow_input):
|
||||
x = torch.empty(0, device='cuda:0')
|
||||
if self.use_rgb:
|
||||
# [64, 128, 2048]
|
||||
x = torch.cat((x, rgb_input), dim=2) # Concatenating along the channel dimension
|
||||
if self.use_flow:
|
||||
# [64, 128, 5120]
|
||||
x = torch.cat((x, flow_input), dim=2)
|
||||
|
||||
# audio channel is too huge to directly concat...
|
||||
# to c
|
||||
|
||||
# B, L, D: mamba
|
||||
B, S, feat_dim = x.shape # batch, length, dim
|
||||
#
|
||||
x = self.layer1(x)
|
||||
ht = self.mamba(x)
|
||||
# h0 = torch.zeros(1, B, self.hidden_dim).to(x.device)
|
||||
# ht, _ = self.gru(x, h0)
|
||||
logits = self.f_classification(self.relu(ht))
|
||||
anticipation_ht = self.anticipation_layer(self.relu(ht)).view(B, S, self.anticipation_length, self.embedding_dim)
|
||||
anticipation_logits = self.f_classification(self.relu(anticipation_ht))
|
||||
|
||||
out_dict = {}
|
||||
if self.training:
|
||||
out_dict['logits'] = logits
|
||||
out_dict['anticipation_logits'] = anticipation_logits
|
||||
else:
|
||||
pred_scores = F.softmax(logits, dim=-1)
|
||||
pred_anticipation_scores = F.softmax(anticipation_logits, dim=-1)
|
||||
out_dict['logits'] = pred_scores
|
||||
out_dict['anticipation_logits'] = pred_anticipation_scores
|
||||
|
||||
return out_dict
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
__all__ = ['build_model']
|
||||
|
||||
from utils import Registry
|
||||
|
||||
META_ARCHITECTURES = Registry()
|
||||
|
||||
def build_model(cfg, device=None):
|
||||
model = META_ARCHITECTURES[cfg["model"]](cfg)
|
||||
return model.to(device)
|
||||
+1
@@ -0,0 +1 @@
|
||||
from .rnn import MROAD, MROADA
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
import sys
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from configs.feature_sizes import FEATURE_SIZES
|
||||
from model.model_builder import META_ARCHITECTURES
|
||||
from utils.util import parse_flow_type, parse_rgb_type
|
||||
|
||||
|
||||
@META_ARCHITECTURES.register("MiniROAD")
|
||||
class MROAD(nn.Module):
|
||||
|
||||
def __init__(self, cfg):
|
||||
super(MROAD, self).__init__()
|
||||
self.use_flow = not cfg['no_flow']
|
||||
self.use_rgb = not cfg['no_rgb']
|
||||
|
||||
self.input_dim = 0
|
||||
|
||||
rgb_type = parse_rgb_type(cfg['rgb_type'])
|
||||
flow_type = parse_flow_type(cfg['flow_type'])
|
||||
|
||||
if self.use_rgb:
|
||||
self.input_dim += FEATURE_SIZES[rgb_type]
|
||||
if self.use_flow:
|
||||
self.input_dim += FEATURE_SIZES[flow_type]
|
||||
|
||||
self.hidden_dim = cfg['hidden_dim']
|
||||
self.num_layers = cfg['num_layers']
|
||||
self.out_dim = cfg['num_classes']
|
||||
self.window_size = cfg['window_size']
|
||||
|
||||
self.relu = nn.ReLU()
|
||||
self.embedding_dim = cfg['embedding_dim']
|
||||
self.gru = nn.GRU(self.embedding_dim, self.hidden_dim, self.num_layers, batch_first=True)
|
||||
self.layer1 = nn.Sequential(
|
||||
nn.Linear(self.input_dim, self.embedding_dim),
|
||||
nn.LayerNorm(self.embedding_dim),
|
||||
nn.ReLU(),
|
||||
nn.Dropout(p=cfg['dropout']),
|
||||
)
|
||||
self.f_classification = nn.Sequential(
|
||||
nn.Linear(self.hidden_dim, self.out_dim)
|
||||
)
|
||||
# self.h0 = torch.nn.Parameter(torch.zeros(1, 1, self.hidden_dim))
|
||||
self.h0 = torch.zeros(self.num_layers, 1, self.hidden_dim)
|
||||
|
||||
def forward(self, rgb_input, flow_input):
|
||||
x = torch.empty(0, device='cuda:0')
|
||||
if self.use_rgb:
|
||||
x = torch.cat((x, rgb_input), dim=2) # Concatenating along the channel dimension
|
||||
if self.use_flow:
|
||||
x = torch.cat((x, flow_input), dim=2)
|
||||
|
||||
# if self.use_rgb and self.use_flow:
|
||||
# x = torch.cat((rgb_input, flow_input), 2)
|
||||
# elif self.use_rgb:
|
||||
# x = rgb_input
|
||||
# elif self.use_flow:
|
||||
# x = flow_input
|
||||
|
||||
x = self.layer1(x)
|
||||
B, _, _ = x.shape
|
||||
h0 = self.h0.expand(-1, B, -1).to(x.device)
|
||||
ht, _ = self.gru(x, h0)
|
||||
ht = self.relu(ht)
|
||||
# ht = self.relu(ht + x)
|
||||
logits = self.f_classification(ht)
|
||||
out_dict = {}
|
||||
if self.training:
|
||||
out_dict['logits'] = logits
|
||||
else:
|
||||
pred_scores = F.softmax(logits, dim=-1)
|
||||
out_dict['logits'] = pred_scores
|
||||
return out_dict
|
||||
|
||||
@META_ARCHITECTURES.register("MiniROADA")
|
||||
class MROADA(nn.Module):
|
||||
|
||||
def __init__(self, cfg):
|
||||
super(MROADA, self).__init__()
|
||||
self.use_flow = not cfg['no_flow']
|
||||
self.use_rgb = not cfg['no_rgb']
|
||||
self.input_dim = 0
|
||||
if self.use_rgb:
|
||||
self.input_dim += FEATURE_SIZES[cfg['rgb_type']]
|
||||
if self.use_flow:
|
||||
self.input_dim += FEATURE_SIZES[cfg['flow_type']]
|
||||
|
||||
self.embedding_dim = cfg['embedding_dim']
|
||||
self.hidden_dim = cfg['hidden_dim']
|
||||
self.num_layers = cfg['num_layers']
|
||||
self.anticipation_length = cfg["anticipation_length"]
|
||||
self.out_dim = cfg['num_classes']
|
||||
|
||||
self.layer1 = nn.Sequential(
|
||||
nn.Linear(self.input_dim, self.embedding_dim),
|
||||
nn.LayerNorm(self.embedding_dim),
|
||||
nn.ReLU(),
|
||||
nn.Dropout(p=cfg['dropout'])
|
||||
)
|
||||
|
||||
self.actionness = cfg['actionness']
|
||||
if self.actionness:
|
||||
self.f_actionness = nn.Sequential(
|
||||
nn.Linear(self.hidden_dim, 1),
|
||||
)
|
||||
self.relu = nn.ReLU()
|
||||
self.gru = nn.GRU(self.embedding_dim, self.hidden_dim, self.num_layers, batch_first=True)
|
||||
self.f_classification = nn.Sequential(
|
||||
nn.Linear(self.hidden_dim, self.out_dim)
|
||||
)
|
||||
self.anticipation_layer = nn.Sequential(
|
||||
nn.Linear(self.hidden_dim, self.anticipation_length*self.hidden_dim),
|
||||
)
|
||||
self.cfg = cfg
|
||||
|
||||
def forward(self, rgb_input, flow_input):
|
||||
x = torch.empty(0, device='cuda:0')
|
||||
if self.use_rgb:
|
||||
# [64, 128, 2048]
|
||||
x = torch.cat((x, rgb_input), dim=2) # Concatenating along the channel dimension
|
||||
if self.use_flow:
|
||||
# [64, 128, 5120]
|
||||
x = torch.cat((x, flow_input), dim=2)
|
||||
|
||||
# audio channel is too huge to directly concat...
|
||||
# to c
|
||||
|
||||
B, S, feat_dim = x.shape # batch, length, dim
|
||||
#
|
||||
x = self.layer1(x)
|
||||
h0 = torch.zeros(1, B, self.hidden_dim).to(x.device)
|
||||
ht, _ = self.gru(x, h0)
|
||||
ht = self.relu(ht)
|
||||
|
||||
logits = self.f_classification(self.relu(ht))
|
||||
anticipation_ht = self.anticipation_layer(self.relu(ht)).view(B, S, self.anticipation_length, self.hidden_dim)
|
||||
anticipation_logits = self.f_classification(self.relu(anticipation_ht))
|
||||
|
||||
|
||||
out_dict = {}
|
||||
if self.training:
|
||||
out_dict['logits'] = logits
|
||||
out_dict['anticipation_logits'] = anticipation_logits
|
||||
else:
|
||||
pred_scores = F.softmax(logits, dim=-1)
|
||||
pred_anticipation_scores = F.softmax(anticipation_logits, dim=-1)
|
||||
out_dict['logits'] = pred_scores
|
||||
out_dict['anticipation_logits'] = pred_anticipation_scores
|
||||
|
||||
return out_dict
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
import math
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
|
||||
class SelfAttention(nn.Module):
|
||||
def __init__(
|
||||
self, dim, heads=8, qkv_bias=False, qk_scale=None, dropout_rate=0.0
|
||||
):
|
||||
super().__init__()
|
||||
self.num_heads = heads
|
||||
head_dim = dim // heads
|
||||
self.scale = qk_scale or head_dim ** -0.5
|
||||
|
||||
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
||||
self.attn_drop = nn.Dropout(dropout_rate)
|
||||
self.proj = nn.Linear(dim, dim)
|
||||
self.proj_drop = nn.Dropout(dropout_rate)
|
||||
|
||||
def forward(self, x):
|
||||
B, N, C = x.shape
|
||||
qkv = (
|
||||
self.qkv(x)
|
||||
.reshape(B, N, 3, self.num_heads, C // self.num_heads)
|
||||
.permute(2, 0, 3, 1, 4)
|
||||
)
|
||||
q, k, v = (
|
||||
qkv[0],
|
||||
qkv[1],
|
||||
qkv[2],
|
||||
) # make torchscript happy (cannot use tensor as tuple)
|
||||
|
||||
attn = (q @ k.transpose(-2, -1)) * self.scale
|
||||
attn = attn.softmax(dim=-1)
|
||||
attn = self.attn_drop(attn)
|
||||
|
||||
x = (attn @ v).transpose(1, 2).reshape(B, N, C)
|
||||
x = self.proj(x)
|
||||
x = self.proj_drop(x)
|
||||
return x
|
||||
|
||||
|
||||
class AxialAttention(nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
in_planes,
|
||||
out_planes,
|
||||
groups=8,
|
||||
kernel_size=56,
|
||||
stride=1,
|
||||
bias=False,
|
||||
width=False,
|
||||
):
|
||||
assert (in_planes % groups == 0) and (out_planes % groups == 0)
|
||||
super(AxialAttention, self).__init__()
|
||||
self.in_planes = in_planes
|
||||
self.out_planes = out_planes
|
||||
self.groups = groups
|
||||
self.group_planes = out_planes // groups
|
||||
self.kernel_size = kernel_size
|
||||
self.stride = stride
|
||||
self.bias = bias
|
||||
self.width = width
|
||||
|
||||
# Multi-head self attention
|
||||
self.qkv_transform = nn.Conv1d(
|
||||
in_planes,
|
||||
out_planes * 2,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
bias=False,
|
||||
)
|
||||
self.bn_qkv = nn.BatchNorm1d(out_planes * 2)
|
||||
self.bn_similarity = nn.BatchNorm2d(groups * 3)
|
||||
self.bn_output = nn.BatchNorm1d(out_planes * 2)
|
||||
|
||||
# Position embedding
|
||||
self.relative = nn.Parameter(
|
||||
torch.randn(self.group_planes * 2, kernel_size * 2 - 1),
|
||||
requires_grad=True,
|
||||
)
|
||||
query_index = torch.arange(kernel_size).unsqueeze(0)
|
||||
key_index = torch.arange(kernel_size).unsqueeze(1)
|
||||
relative_index = key_index - query_index + kernel_size - 1
|
||||
self.register_buffer('flatten_index', relative_index.view(-1))
|
||||
if stride > 1:
|
||||
self.pooling = nn.AvgPool2d(stride, stride=stride)
|
||||
|
||||
self.reset_parameters()
|
||||
|
||||
def forward(self, x):
|
||||
if self.width:
|
||||
x = x.permute(0, 2, 1, 3)
|
||||
else:
|
||||
x = x.permute(0, 3, 1, 2) # N, W, C, H
|
||||
N, W, C, H = x.shape
|
||||
x = x.contiguous().view(N * W, C, H)
|
||||
|
||||
# Transformations
|
||||
qkv = self.bn_qkv(self.qkv_transform(x))
|
||||
q, k, v = torch.split(
|
||||
qkv.reshape(N * W, self.groups, self.group_planes * 2, H),
|
||||
[self.group_planes // 2, self.group_planes // 2, self.group_planes],
|
||||
dim=2,
|
||||
)
|
||||
|
||||
# Calculate position embedding
|
||||
all_embeddings = torch.index_select(
|
||||
self.relative, 1, self.flatten_index
|
||||
).view(self.group_planes * 2, self.kernel_size, self.kernel_size)
|
||||
q_embedding, k_embedding, v_embedding = torch.split(
|
||||
all_embeddings,
|
||||
[self.group_planes // 2, self.group_planes // 2, self.group_planes],
|
||||
dim=0,
|
||||
)
|
||||
qr = torch.einsum('bgci,cij->bgij', q, q_embedding)
|
||||
kr = torch.einsum('bgci,cij->bgij', k, k_embedding).transpose(2, 3)
|
||||
qk = torch.einsum('bgci, bgcj->bgij', q, k)
|
||||
stacked_similarity = torch.cat([qk, qr, kr], dim=1)
|
||||
stacked_similarity = (
|
||||
self.bn_similarity(stacked_similarity)
|
||||
.view(N * W, 3, self.groups, H, H)
|
||||
.sum(dim=1)
|
||||
)
|
||||
|
||||
# (N, groups, H, H, W)
|
||||
similarity = F.softmax(stacked_similarity, dim=3)
|
||||
sv = torch.einsum('bgij,bgcj->bgci', similarity, v)
|
||||
sve = torch.einsum('bgij,cij->bgci', similarity, v_embedding)
|
||||
stacked_output = torch.cat([sv, sve], dim=-1).view(
|
||||
N * W, self.out_planes * 2, H
|
||||
)
|
||||
output = (
|
||||
self.bn_output(stacked_output)
|
||||
.view(N, W, self.out_planes, 2, H)
|
||||
.sum(dim=-2)
|
||||
)
|
||||
|
||||
if self.width:
|
||||
output = output.permute(0, 2, 1, 3)
|
||||
else:
|
||||
output = output.permute(0, 2, 3, 1)
|
||||
|
||||
if self.stride > 1:
|
||||
output = self.pooling(output)
|
||||
|
||||
return output
|
||||
|
||||
def reset_parameters(self):
|
||||
self.qkv_transform.weight.data.normal_(
|
||||
0, math.sqrt(1.0 / self.in_planes)
|
||||
)
|
||||
nn.init.normal_(self.relative, 0.0, math.sqrt(1.0 / self.group_planes))
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from .Attention import AxialAttention
|
||||
|
||||
|
||||
def conv1x1(in_planes, out_planes, stride=1):
|
||||
"""1x1 convolution"""
|
||||
return nn.Conv2d(
|
||||
in_planes, out_planes, kernel_size=1, stride=stride, bias=False
|
||||
)
|
||||
|
||||
|
||||
class AxialBlock(nn.Module):
|
||||
expansion = 2
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
inplanes,
|
||||
planes,
|
||||
stride=1,
|
||||
downsample=None,
|
||||
groups=1,
|
||||
base_width=64,
|
||||
dilation=1,
|
||||
norm_layer=None,
|
||||
kernel_size=56,
|
||||
):
|
||||
super(AxialBlock, self).__init__()
|
||||
if norm_layer is None:
|
||||
norm_layer = nn.BatchNorm2d
|
||||
width = int(planes * (base_width / 64.0))
|
||||
# Both self.conv2 and self.downsample layers downsample the input when stride != 1
|
||||
self.conv_down = conv1x1(inplanes, width)
|
||||
self.bn1 = norm_layer(width)
|
||||
self.hight_block = AxialAttention(
|
||||
width, width, groups=groups, kernel_size=kernel_size
|
||||
)
|
||||
self.width_block = AxialAttention(
|
||||
width,
|
||||
width,
|
||||
groups=groups,
|
||||
kernel_size=kernel_size,
|
||||
stride=stride,
|
||||
width=True,
|
||||
)
|
||||
self.conv_up = conv1x1(width, planes * self.expansion)
|
||||
self.bn2 = norm_layer(planes * self.expansion)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.downsample = downsample
|
||||
self.stride = stride
|
||||
|
||||
def forward(self, x):
|
||||
identity = x
|
||||
|
||||
out = self.conv_down(x)
|
||||
out = self.bn1(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.hight_block(out)
|
||||
out = self.width_block(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv_up(out)
|
||||
out = self.bn2(out)
|
||||
|
||||
if self.downsample is not None:
|
||||
identity = self.downsample(x)
|
||||
|
||||
out += identity
|
||||
out = self.relu(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class AxialAttentionNet(nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
layers,
|
||||
num_classes=1000,
|
||||
zero_init_residual=True,
|
||||
groups=8,
|
||||
width_per_group=64,
|
||||
replace_stride_with_dilation=None,
|
||||
norm_layer=None,
|
||||
s=0.5,
|
||||
):
|
||||
super(AxialAttentionNet, self).__init__()
|
||||
block = AxialBlock
|
||||
if norm_layer is None:
|
||||
norm_layer = nn.BatchNorm2d
|
||||
self._norm_layer = norm_layer
|
||||
|
||||
self.inplanes = int(64 * s)
|
||||
self.dilation = 1
|
||||
if replace_stride_with_dilation is None:
|
||||
# each element in the tuple indicates if we should replace
|
||||
# the 2x2 stride with a dilated convolution instead
|
||||
replace_stride_with_dilation = [False, False, False]
|
||||
if len(replace_stride_with_dilation) != 3:
|
||||
raise ValueError(
|
||||
"replace_stride_with_dilation should be None "
|
||||
"or a 3-element tuple, got {}".format(
|
||||
replace_stride_with_dilation
|
||||
)
|
||||
)
|
||||
self.groups = groups
|
||||
self.base_width = width_per_group
|
||||
self.conv1 = nn.Conv2d(
|
||||
3, self.inplanes, kernel_size=7, stride=2, padding=3, bias=False
|
||||
)
|
||||
self.bn1 = norm_layer(self.inplanes)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
|
||||
self.layer1 = self._make_layer(
|
||||
block, int(128 * s), layers[0], kernel_size=56
|
||||
)
|
||||
self.layer2 = self._make_layer(
|
||||
block,
|
||||
int(256 * s),
|
||||
layers[1],
|
||||
stride=2,
|
||||
kernel_size=56,
|
||||
dilate=replace_stride_with_dilation[0],
|
||||
)
|
||||
self.layer3 = self._make_layer(
|
||||
block,
|
||||
int(512 * s),
|
||||
layers[2],
|
||||
stride=2,
|
||||
kernel_size=28,
|
||||
dilate=replace_stride_with_dilation[1],
|
||||
)
|
||||
self.layer4 = self._make_layer(
|
||||
block,
|
||||
int(1024 * s),
|
||||
layers[3],
|
||||
stride=2,
|
||||
kernel_size=14,
|
||||
dilate=replace_stride_with_dilation[2],
|
||||
)
|
||||
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
|
||||
self.fc = nn.Linear(int(1024 * block.expansion * s), num_classes)
|
||||
|
||||
for m in self.modules():
|
||||
if isinstance(m, (nn.Conv2d, nn.Conv1d)):
|
||||
if isinstance(m, nn.Conv1d):
|
||||
pass
|
||||
else:
|
||||
nn.init.kaiming_normal_(
|
||||
m.weight, mode='fan_out', nonlinearity='relu'
|
||||
)
|
||||
elif isinstance(m, (nn.BatchNorm2d, nn.BatchNorm1d, nn.GroupNorm)):
|
||||
nn.init.constant_(m.weight, 1)
|
||||
nn.init.constant_(m.bias, 0)
|
||||
|
||||
# Zero-initialize the last BN in each residual branch,
|
||||
# so that the residual branch starts with zeros, and each residual block behaves like an identity.
|
||||
# This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677
|
||||
if zero_init_residual:
|
||||
for m in self.modules():
|
||||
if isinstance(m, AxialBlock):
|
||||
nn.init.constant_(m.bn2.weight, 0)
|
||||
|
||||
def _make_layer(
|
||||
self, block, planes, blocks, kernel_size=56, stride=1, dilate=False
|
||||
):
|
||||
norm_layer = self._norm_layer
|
||||
downsample = None
|
||||
previous_dilation = self.dilation
|
||||
if dilate:
|
||||
self.dilation *= stride
|
||||
stride = 1
|
||||
if stride != 1 or self.inplanes != planes * block.expansion:
|
||||
downsample = nn.Sequential(
|
||||
conv1x1(self.inplanes, planes * block.expansion, stride),
|
||||
norm_layer(planes * block.expansion),
|
||||
)
|
||||
|
||||
layers = []
|
||||
layers.append(
|
||||
block(
|
||||
self.inplanes,
|
||||
planes,
|
||||
stride,
|
||||
downsample,
|
||||
groups=self.groups,
|
||||
base_width=self.base_width,
|
||||
dilation=previous_dilation,
|
||||
norm_layer=norm_layer,
|
||||
kernel_size=kernel_size,
|
||||
)
|
||||
)
|
||||
self.inplanes = planes * block.expansion
|
||||
if stride != 1:
|
||||
kernel_size = kernel_size // 2
|
||||
|
||||
for _ in range(1, blocks):
|
||||
layers.append(
|
||||
block(
|
||||
self.inplanes,
|
||||
planes,
|
||||
groups=self.groups,
|
||||
base_width=self.base_width,
|
||||
dilation=self.dilation,
|
||||
norm_layer=norm_layer,
|
||||
kernel_size=kernel_size,
|
||||
)
|
||||
)
|
||||
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
def _forward_impl(self, x, include_conv5=False, include_top=False):
|
||||
# See note [TorchScript super()]
|
||||
x = self.conv1(x)
|
||||
x = self.bn1(x)
|
||||
x = self.relu(x)
|
||||
x = self.maxpool(x)
|
||||
|
||||
x = self.layer1(x)
|
||||
x = self.layer2(x)
|
||||
x = self.layer3(x)
|
||||
if include_conv5:
|
||||
x = self.layer4(x)
|
||||
|
||||
if include_top:
|
||||
x = self.avgpool(x)
|
||||
x = torch.flatten(x, 1)
|
||||
x = self.fc(x)
|
||||
|
||||
return x
|
||||
|
||||
def forward(self, x, include_conv5=False, include_top=False):
|
||||
return self._forward_impl(x, include_conv5, include_top)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
model = AxialAttentionNet([1, 2, 4, 1], s=0.5)
|
||||
# model = AxialAttentionNet([3, 4, 6, 3], s=0.25)
|
||||
# print(model)
|
||||
|
||||
x = torch.randn([8, 3, 224, 224])
|
||||
print(model(x).shape)
|
||||
print(model(x, include_conv5=True).shape)
|
||||
print(model(x, include_conv5=True, include_top=True).shape)
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
class StdConv2d(nn.Conv2d):
|
||||
def forward(self, x):
|
||||
w = self.weight
|
||||
v, m = torch.var_mean(w, dim=[1, 2, 3], keepdim=True, unbiased=False)
|
||||
w = (w - m) / torch.sqrt(v + 1e-10)
|
||||
return F.conv2d(
|
||||
x,
|
||||
w,
|
||||
self.bias,
|
||||
self.stride,
|
||||
self.padding,
|
||||
self.dilation,
|
||||
self.groups,
|
||||
)
|
||||
|
||||
|
||||
def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):
|
||||
"""3x3 convolution with padding"""
|
||||
return StdConv2d(
|
||||
in_planes,
|
||||
out_planes,
|
||||
kernel_size=3,
|
||||
stride=stride,
|
||||
padding=dilation,
|
||||
groups=groups,
|
||||
bias=False,
|
||||
dilation=dilation,
|
||||
)
|
||||
|
||||
|
||||
def conv1x1(in_planes, out_planes, stride=1):
|
||||
"""1x1 convolution"""
|
||||
return StdConv2d(
|
||||
in_planes, out_planes, kernel_size=1, stride=stride, bias=False
|
||||
)
|
||||
|
||||
|
||||
class PreActBottleneck(nn.Module):
|
||||
"""Pre-activation (v2) bottleneck block.
|
||||
Follows the implementation of "Identity Mappings in Deep Residual Networks":
|
||||
https://github.com/KaimingHe/resnet-1k-layers/blob/master/resnet-pre-act.lua
|
||||
Except it puts the stride on 3x3 conv when available.
|
||||
"""
|
||||
|
||||
def __init__(self, in_planes, out_planes=None, mid_planes=None, stride=1):
|
||||
super(PreActBottleneck, self).__init__()
|
||||
out_planes = out_planes or in_planes
|
||||
mid_planes = mid_planes or out_planes // 4
|
||||
|
||||
self.gn1 = nn.GroupNorm(32, in_planes)
|
||||
self.conv1 = conv1x1(in_planes, mid_planes)
|
||||
self.gn2 = nn.GroupNorm(32, mid_planes)
|
||||
self.conv2 = conv3x3(mid_planes, mid_planes, stride)
|
||||
self.gn3 = nn.GroupNorm(32, mid_planes)
|
||||
self.conv3 = conv1x1(mid_planes, out_planes)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
|
||||
if stride != 1 or in_planes != out_planes:
|
||||
# Projection also with pre-activation according to paper.
|
||||
self.downsample = conv1x1(in_planes, out_planes, stride)
|
||||
|
||||
def forward(self, x):
|
||||
out = self.relu(self.gn1(x))
|
||||
|
||||
# Residual branch
|
||||
residual = x
|
||||
if hasattr(self, 'downsample'):
|
||||
residual = self.downsample(out)
|
||||
|
||||
# Unit's branch
|
||||
out = self.conv1(out)
|
||||
out = self.conv2(self.relu(self.gn2(out)))
|
||||
out = self.conv3(self.relu(self.gn3(out)))
|
||||
|
||||
return out + residual
|
||||
|
||||
|
||||
class ResNetV2Model(nn.Module):
|
||||
"""Implementation of Pre-activation (v2) ResNet mode."""
|
||||
|
||||
def __init__(self, block_units, width_factor, head_size=21843):
|
||||
super(ResNetV2Model, self).__init__()
|
||||
wf = width_factor # shortcut 'cause we'll use it a lot.
|
||||
|
||||
# The following will be unreadable if we split lines.
|
||||
# pylint: disable=line-too-long
|
||||
self.conv1 = nn.Sequential(OrderedDict([
|
||||
('conv', StdConv2d(3, 64*wf, kernel_size=7, stride=2, padding=3, bias=False)),
|
||||
('pad', nn.ConstantPad2d(1, 0)),
|
||||
('pool', nn.MaxPool2d(kernel_size=3, stride=2, padding=0)),
|
||||
]))
|
||||
|
||||
self.conv2 = nn.Sequential(OrderedDict(
|
||||
[('unit01', PreActBottleneck(in_planes=64*wf, out_planes=256*wf, mid_planes=64*wf))] +
|
||||
[(f'unit{i:02d}', PreActBottleneck(in_planes=256*wf, out_planes=256*wf, mid_planes=64*wf)) for i in range(2, block_units[0] + 1)],
|
||||
))
|
||||
self.conv3 = nn.Sequential(OrderedDict(
|
||||
[('unit01', PreActBottleneck(in_planes=256*wf, out_planes=512*wf, mid_planes=128*wf, stride=2))] +
|
||||
[(f'unit{i:02d}', PreActBottleneck(in_planes=512*wf, out_planes=512*wf, mid_planes=128*wf)) for i in range(2, block_units[1] + 1)],
|
||||
))
|
||||
self.conv4 = nn.Sequential(OrderedDict(
|
||||
[('unit01', PreActBottleneck(in_planes=512*wf, out_planes=1024*wf, mid_planes=256*wf, stride=2))] +
|
||||
[(f'unit{i:02d}', PreActBottleneck(in_planes=1024*wf, out_planes=1024*wf, mid_planes=256*wf)) for i in range(2, block_units[2] + 1)],
|
||||
))
|
||||
self.conv5 = nn.Sequential(OrderedDict(
|
||||
[('unit01', PreActBottleneck(in_planes=1024*wf, out_planes=2048*wf, mid_planes=512*wf, stride=2))] +
|
||||
[(f'unit{i:02d}', PreActBottleneck(in_planes=2048*wf, out_planes=2048*wf, mid_planes=512*wf)) for i in range(2, block_units[3] + 1)],
|
||||
))
|
||||
# pylint: enable=line-too-long
|
||||
|
||||
self.head = nn.Sequential(OrderedDict([
|
||||
('gn', nn.GroupNorm(32, 2048*wf)),
|
||||
('relu', nn.ReLU(inplace=True)),
|
||||
('avg', nn.AdaptiveAvgPool2d(output_size=1)),
|
||||
('conv', nn.Conv2d(2048*wf, head_size, kernel_size=1, bias=True)),
|
||||
]))
|
||||
|
||||
def forward(self, x, include_conv5=False, include_top=False):
|
||||
x = self.conv1(x)
|
||||
x = self.conv2(x)
|
||||
x = self.conv3(x)
|
||||
x = self.conv4(x)
|
||||
if include_conv5:
|
||||
x = self.conv5(x)
|
||||
if include_top:
|
||||
x = self.head(x)
|
||||
|
||||
if include_top and include_conv5:
|
||||
assert x.shape[-2:] == (1,1,)
|
||||
return x[..., 0, 0]
|
||||
|
||||
return x
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from .BiT import ResNetV2Model
|
||||
from .AxialNet import AxialAttentionNet
|
||||
from .Transformer import TransformerModel
|
||||
from .PositionalEncoding import (
|
||||
FixedPositionalEncoding,
|
||||
LearnedPositionalEncoding,
|
||||
)
|
||||
|
||||
|
||||
class HybridVisionTransformer(nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
img_dim,
|
||||
out_dim,
|
||||
num_channels,
|
||||
embedding_dim,
|
||||
num_heads,
|
||||
num_layers,
|
||||
hidden_dim,
|
||||
include_conv5,
|
||||
dropout_rate,
|
||||
positional_encoding_type,
|
||||
backbone=None,
|
||||
):
|
||||
super(HybridVisionTransformer, self).__init__()
|
||||
|
||||
assert embedding_dim % num_heads == 0
|
||||
|
||||
self.embedding_dim = embedding_dim
|
||||
self.num_heads = num_heads
|
||||
self.out_dim = out_dim
|
||||
self.num_channels = num_channels
|
||||
self.include_conv5 = include_conv5
|
||||
self.backbone = backbone
|
||||
|
||||
self.backbone_model, self.flatten_dim = self.configure_backbone()
|
||||
|
||||
self.projection_encoding = nn.Linear(self.flatten_dim, embedding_dim)
|
||||
self.cls_token = nn.Parameter(torch.randn(1, 1, embedding_dim))
|
||||
|
||||
self.decoder_dim = int(img_dim / 16.0) ** 2
|
||||
if self.include_conv5:
|
||||
self.decoder_dim = int(img_dim / 32.0) ** 2
|
||||
|
||||
self.decoder_dim += 1 # for the cls token
|
||||
|
||||
if positional_encoding_type == "learned":
|
||||
self.position_encoding = LearnedPositionalEncoding(
|
||||
self.decoder_dim, self.embedding_dim, self.decoder_dim
|
||||
)
|
||||
elif positional_encoding_type == "fixed":
|
||||
self.position_encoding = FixedPositionalEncoding(
|
||||
self.embedding_dim,
|
||||
)
|
||||
|
||||
self.transformer = TransformerModel(
|
||||
embedding_dim, num_layers, num_heads, hidden_dim
|
||||
)
|
||||
self.mlp_head = nn.Sequential(
|
||||
nn.Linear(embedding_dim, hidden_dim),
|
||||
nn.GELU(),
|
||||
nn.Linear(hidden_dim, out_dim),
|
||||
)
|
||||
self.to_cls_token = nn.Identity()
|
||||
|
||||
def forward(self, x):
|
||||
# apply bit backbone
|
||||
x = self.backbone_model(x, include_conv5=self.include_conv5)
|
||||
x = x.view(x.size(0), -1, self.flatten_dim)
|
||||
|
||||
x = self.projection_encoding(x)
|
||||
cls_tokens = self.cls_token.expand(x.shape[0], -1, -1)
|
||||
x = torch.cat((cls_tokens, x), dim=1)
|
||||
x = self.position_encoding(x)
|
||||
|
||||
# apply transformer
|
||||
x = self.transformer(x)
|
||||
x = self.to_cls_token(x[:, 0])
|
||||
x = self.mlp_head(x)
|
||||
x = F.log_softmax(x, dim=-1)
|
||||
|
||||
return x
|
||||
|
||||
def configure_backbone(self):
|
||||
raise NotImplementedError("Method to be called in child class!!")
|
||||
|
||||
|
||||
class ResNetHybridViT(HybridVisionTransformer):
|
||||
def __init__(
|
||||
self,
|
||||
img_dim,
|
||||
out_dim,
|
||||
num_channels,
|
||||
embedding_dim,
|
||||
num_heads,
|
||||
num_layers,
|
||||
hidden_dim,
|
||||
include_conv5=False,
|
||||
dropout_rate=0.1,
|
||||
positional_encoding_type="learned",
|
||||
backbone='r50x1',
|
||||
):
|
||||
super(ResNetHybridViT, self).__init__(
|
||||
img_dim=img_dim,
|
||||
out_dim=out_dim,
|
||||
num_channels=num_channels,
|
||||
embedding_dim=embedding_dim,
|
||||
num_heads=num_heads,
|
||||
num_layers=num_layers,
|
||||
hidden_dim=hidden_dim,
|
||||
include_conv5=include_conv5,
|
||||
dropout_rate=dropout_rate,
|
||||
positional_encoding_type=positional_encoding_type,
|
||||
backbone=backbone,
|
||||
)
|
||||
|
||||
def configure_backbone(self):
|
||||
"""
|
||||
Current support offered for all BiT models
|
||||
KNOWN_MODELS in https://github.com/google-research/big_transfer/blob/master/bit_pytorch/models.py
|
||||
|
||||
expects model name of style 'r{depth}x{width}'
|
||||
where depth in [50, 101, 152]
|
||||
where width in [1,2,3,4]
|
||||
"""
|
||||
backbone = self.backbone
|
||||
out_dim = self.out_dim
|
||||
|
||||
splits = backbone.split('x')
|
||||
model_name = splits[0]
|
||||
width_factor = int(splits[1])
|
||||
|
||||
if model_name in ['r50', 'r101'] and width_factor in [2, 4]:
|
||||
return ValueError(
|
||||
"Invalid Configuration of models -- expect 50x1, 50x3, 101x1, 101x3"
|
||||
)
|
||||
elif model_name == 'r152' and width_factor in [1, 3]:
|
||||
return ValueError(
|
||||
"Invalid Configuration of models -- expect 152x2, 152x4"
|
||||
)
|
||||
|
||||
block_units_dict = {
|
||||
'r50': [3, 4, 6, 3],
|
||||
'r101': [3, 4, 23, 3],
|
||||
'r152': [3, 8, 36, 3],
|
||||
}
|
||||
block_units = block_units_dict.get(model_name, [3, 4, 6, 3])
|
||||
model = ResNetV2Model(block_units, width_factor, head_size=out_dim)
|
||||
|
||||
if self.num_channels == 3:
|
||||
flatten_dim = 1024 * width_factor
|
||||
if self.include_conv5:
|
||||
flatten_dim *= 2
|
||||
|
||||
return model, flatten_dim
|
||||
|
||||
|
||||
class AxialNetHybridViT(HybridVisionTransformer):
|
||||
def __init__(
|
||||
self,
|
||||
img_dim,
|
||||
out_dim,
|
||||
num_channels,
|
||||
embedding_dim,
|
||||
num_heads,
|
||||
num_layers,
|
||||
hidden_dim,
|
||||
include_conv5=False,
|
||||
dropout_rate=0.1,
|
||||
positional_encoding_type="learned",
|
||||
backbone='a50m',
|
||||
):
|
||||
super(AxialNetHybridViT, self).__init__(
|
||||
img_dim=img_dim,
|
||||
out_dim=out_dim,
|
||||
num_channels=num_channels,
|
||||
embedding_dim=embedding_dim,
|
||||
num_heads=num_heads,
|
||||
num_layers=num_layers,
|
||||
hidden_dim=hidden_dim,
|
||||
include_conv5=include_conv5,
|
||||
dropout_rate=dropout_rate,
|
||||
positional_encoding_type=positional_encoding_type,
|
||||
backbone=backbone,
|
||||
)
|
||||
|
||||
def configure_backbone(self):
|
||||
"""
|
||||
Current support offered for all BiT models
|
||||
models from https://github.com/csrhddlam/axial-deeplab/blob/master/lib/models/axialnet.py
|
||||
|
||||
expects model name of style 'a{depth}{width}'
|
||||
where depth in [26, 50, 101]
|
||||
where width in [s, m, l]
|
||||
"""
|
||||
backbone = self.backbone
|
||||
out_dim = self.out_dim
|
||||
|
||||
model_name = backbone[:3]
|
||||
width = backbone[-1]
|
||||
|
||||
block_units_dict = {
|
||||
'a26': [1, 2, 4, 1],
|
||||
'a50': [3, 4, 6, 3],
|
||||
'a101': [3, 4, 23, 3],
|
||||
}
|
||||
block_units = block_units_dict.get(model_name, [3, 4, 6, 3])
|
||||
|
||||
scale_factor_dict = {'s': 0.5, 'm': 0.75, 'l': 1.0}
|
||||
scale_factor = scale_factor_dict.get(width, 0.75)
|
||||
model = AxialAttentionNet(
|
||||
block_units, s=scale_factor, num_classes=out_dim
|
||||
)
|
||||
|
||||
if self.num_channels == 3:
|
||||
flatten_dim = int(512 * float(scale_factor / 0.5))
|
||||
if self.include_conv5:
|
||||
flatten_dim *= 2
|
||||
|
||||
return model, flatten_dim
|
||||
@@ -0,0 +1,331 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from .decoder import Decoder, DecoderLayer
|
||||
from .attn import FullAttention, ProbAttention, AttentionLayer
|
||||
from .Transformer import TransformerModel
|
||||
from ipdb import set_trace
|
||||
from .PositionalEncoding import (
|
||||
FixedPositionalEncoding,
|
||||
LearnedPositionalEncoding,
|
||||
)
|
||||
|
||||
__all__ = ['ViT_B16', 'ViT_B32', 'ViT_L16', 'ViT_L32', 'ViT_H14']
|
||||
|
||||
|
||||
class VisionTransformer_v3(nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
args,
|
||||
img_dim,
|
||||
patch_dim,
|
||||
out_dim,
|
||||
embedding_dim,
|
||||
num_heads,
|
||||
num_layers,
|
||||
hidden_dim,
|
||||
dropout_rate=0.0,
|
||||
attn_dropout_rate=0.0,
|
||||
use_representation=True,
|
||||
conv_patch_representation=False,
|
||||
positional_encoding_type="learned", with_camera=True, with_motion=True, num_channels=3072,
|
||||
):
|
||||
super(VisionTransformer_v3, self).__init__()
|
||||
|
||||
assert embedding_dim % num_heads == 0
|
||||
assert img_dim % patch_dim == 0
|
||||
self.with_camera = with_camera
|
||||
self.with_motion = with_motion
|
||||
self.embedding_dim = embedding_dim
|
||||
self.num_heads = num_heads
|
||||
self.patch_dim = patch_dim
|
||||
# num_channels = img_dim
|
||||
self.num_channels = num_channels
|
||||
self.dropout_rate = dropout_rate
|
||||
self.attn_dropout_rate = attn_dropout_rate
|
||||
self.conv_patch_representation = conv_patch_representation
|
||||
|
||||
# self.num_patches = int((img_dim // patch_dim) ** 2)
|
||||
self.num_patches = int(img_dim // patch_dim)
|
||||
self.seq_length = self.num_patches + 1
|
||||
self.flatten_dim = patch_dim * patch_dim * num_channels
|
||||
self.cls_token = nn.Parameter(torch.zeros(1, 1, embedding_dim))
|
||||
|
||||
self.linear_encoding = nn.Linear(self.flatten_dim, embedding_dim)
|
||||
if positional_encoding_type == "learned":
|
||||
self.position_encoding = LearnedPositionalEncoding(
|
||||
self.seq_length, self.embedding_dim, self.seq_length
|
||||
)
|
||||
elif positional_encoding_type == "fixed":
|
||||
self.position_encoding = FixedPositionalEncoding(
|
||||
self.embedding_dim,
|
||||
)
|
||||
print('position encoding :', positional_encoding_type)
|
||||
|
||||
self.pe_dropout = nn.Dropout(p=self.dropout_rate)
|
||||
|
||||
self.encoder = TransformerModel(
|
||||
embedding_dim,
|
||||
num_layers,
|
||||
num_heads,
|
||||
hidden_dim,
|
||||
self.dropout_rate,
|
||||
self.attn_dropout_rate,
|
||||
)
|
||||
self.pre_head_ln = nn.LayerNorm(embedding_dim)
|
||||
|
||||
d_model = args.decoder_embedding_dim
|
||||
use_representation = False # False
|
||||
if use_representation:
|
||||
self.mlp_head = nn.Sequential(
|
||||
nn.Linear(embedding_dim + d_model, hidden_dim//2),
|
||||
# nn.Tanh(),
|
||||
nn.ReLU(),
|
||||
nn.Linear(hidden_dim//2, out_dim),
|
||||
)
|
||||
else:
|
||||
self.mlp_head = nn.Linear(embedding_dim + d_model, out_dim)
|
||||
|
||||
if self.conv_patch_representation:
|
||||
# self.conv_x = nn.Conv2d(
|
||||
# self.num_channels,
|
||||
# self.embedding_dim,
|
||||
# kernel_size=(self.patch_dim, self.patch_dim),
|
||||
# stride=(self.patch_dim, self.patch_dim),
|
||||
# padding=self._get_padding(
|
||||
# 'VALID', (self.patch_dim, self.patch_dim),
|
||||
# ),
|
||||
# )
|
||||
self.conv_x = nn.Conv1d(
|
||||
self.num_channels,
|
||||
self.embedding_dim,
|
||||
kernel_size=self.patch_dim,
|
||||
stride=self.patch_dim,
|
||||
padding=self._get_padding(
|
||||
'VALID', (self.patch_dim),
|
||||
),
|
||||
)
|
||||
else:
|
||||
self.conv_x = None
|
||||
|
||||
self.to_cls_token = nn.Identity()
|
||||
|
||||
# Decoder
|
||||
factor = 1 # 5
|
||||
dropout = args.decoder_attn_dropout_rate
|
||||
# d_model = args.decoder_embedding_dim
|
||||
n_heads = args.decoder_num_heads
|
||||
d_layers = args.decoder_layers
|
||||
d_ff = args.decoder_embedding_dim_out # args.decoder_embedding_dim_out or 4*args.decoder_embedding_dim None
|
||||
activation = 'gelu' # 'gelu'
|
||||
self.decoder = Decoder(
|
||||
[
|
||||
DecoderLayer(
|
||||
AttentionLayer(FullAttention(True, factor, attention_dropout=dropout), # True
|
||||
d_model, n_heads), # ProbAttention FullAttention
|
||||
AttentionLayer(FullAttention(False, factor, attention_dropout=dropout), # False
|
||||
d_model, n_heads),
|
||||
d_model,
|
||||
d_ff,
|
||||
dropout=dropout,
|
||||
activation=activation,
|
||||
)
|
||||
for l in range(d_layers)
|
||||
],
|
||||
norm_layer=torch.nn.LayerNorm(d_model)
|
||||
)
|
||||
self.decoder_cls_token = nn.Parameter(torch.zeros(1, args.query_num, d_model))
|
||||
if positional_encoding_type == "learned":
|
||||
self.decoder_position_encoding = LearnedPositionalEncoding(
|
||||
args.query_num, self.embedding_dim, args.query_num
|
||||
)
|
||||
elif positional_encoding_type == "fixed":
|
||||
self.decoder_position_encoding = FixedPositionalEncoding(
|
||||
self.embedding_dim,
|
||||
)
|
||||
print('position decoding :', positional_encoding_type)
|
||||
self.classifier = nn.Linear(d_model, out_dim)
|
||||
self.after_dropout = nn.Dropout(p=self.dropout_rate)
|
||||
# self.merge_fc = nn.Linear(d_model, 1)
|
||||
# self.merge_sigmoid = nn.Sigmoid()
|
||||
|
||||
def forward(self, sequence_input_rgb, sequence_input_flow):
|
||||
if self.with_camera and self.with_motion:
|
||||
x = torch.cat((sequence_input_rgb, sequence_input_flow), 2)
|
||||
elif self.with_camera:
|
||||
x = sequence_input_rgb
|
||||
elif self.with_motion:
|
||||
x = sequence_input_flow
|
||||
|
||||
x = self.linear_encoding(x)
|
||||
cls_tokens = self.cls_token.expand(x.shape[0], -1, -1)
|
||||
# x = torch.cat((cls_tokens, x), dim=1)
|
||||
x = torch.cat((x, cls_tokens), dim=1)
|
||||
x = self.position_encoding(x)
|
||||
x = self.pe_dropout(x) # not delete
|
||||
|
||||
# apply transformer
|
||||
x = self.encoder(x)
|
||||
x = self.pre_head_ln(x) # [128, 33, 1024]
|
||||
# x = self.after_dropout(x) # add
|
||||
# decoder
|
||||
decoder_cls_token = self.decoder_cls_token.expand(x.shape[0], -1, -1)
|
||||
# decoder_cls_token = self.after_dropout(decoder_cls_token) # add
|
||||
# decoder_cls_token = self.decoder_position_encoding(decoder_cls_token) # [128, 8, 1024]
|
||||
dec = self.decoder(decoder_cls_token, x) # [128, 8, 1024]
|
||||
dec = self.after_dropout(dec) # add
|
||||
# merge_atte = self.merge_sigmoid(self.merge_fc(dec)) # [128, 8, 1]
|
||||
# dec_for_token = (merge_atte*dec).sum(dim=1) # [128, 1024]
|
||||
# dec_for_token = (merge_atte*dec).sum(dim=1)/(merge_atte.sum(dim=-2) + 0.0001)
|
||||
dec_for_token = dec.mean(dim=1)
|
||||
# dec_for_token = dec.max(dim=1)[0]
|
||||
dec_cls_out = self.classifier(dec)
|
||||
# set_trace()
|
||||
# x = self.to_cls_token(x[:, 0])
|
||||
x = torch.cat((self.to_cls_token(x[:, -1]), dec_for_token), dim=1)
|
||||
x = self.mlp_head(x)
|
||||
# x = F.log_softmax(x, dim=-1)
|
||||
|
||||
return x, dec_cls_out
|
||||
|
||||
def _get_padding(self, padding_type, kernel_size):
|
||||
assert padding_type in ['SAME', 'VALID']
|
||||
if padding_type == 'SAME':
|
||||
_list = [(k - 1) // 2 for k in kernel_size]
|
||||
return tuple(_list)
|
||||
return tuple(0 for _ in kernel_size)
|
||||
|
||||
|
||||
def ViT_B16(dataset='imagenet'):
|
||||
if dataset == 'imagenet':
|
||||
img_dim = 224
|
||||
out_dim = 1000
|
||||
patch_dim = 16
|
||||
elif 'cifar' in dataset:
|
||||
img_dim = 32
|
||||
out_dim = 10
|
||||
patch_dim = 4
|
||||
|
||||
return VisionTransformer(
|
||||
img_dim=img_dim,
|
||||
patch_dim=patch_dim,
|
||||
out_dim=out_dim,
|
||||
num_channels=3,
|
||||
embedding_dim=768,
|
||||
num_heads=12,
|
||||
num_layers=12,
|
||||
hidden_dim=3072,
|
||||
dropout_rate=0.1,
|
||||
attn_dropout_rate=0.0,
|
||||
use_representation=False,
|
||||
conv_patch_representation=False,
|
||||
positional_encoding_type="learned",
|
||||
)
|
||||
|
||||
|
||||
def ViT_B32(dataset='imagenet'):
|
||||
if dataset == 'imagenet':
|
||||
img_dim = 224
|
||||
out_dim = 1000
|
||||
patch_dim = 32
|
||||
elif 'cifar' in dataset:
|
||||
img_dim = 32
|
||||
out_dim = 10
|
||||
patch_dim = 4
|
||||
|
||||
return VisionTransformer(
|
||||
img_dim=img_dim,
|
||||
patch_dim=patch_dim,
|
||||
out_dim=out_dim,
|
||||
num_channels=3,
|
||||
embedding_dim=768,
|
||||
num_heads=12,
|
||||
num_layers=12,
|
||||
hidden_dim=3072,
|
||||
dropout_rate=0.1,
|
||||
attn_dropout_rate=0.0,
|
||||
use_representation=False,
|
||||
conv_patch_representation=False,
|
||||
positional_encoding_type="learned",
|
||||
)
|
||||
|
||||
|
||||
def ViT_L16(dataset='imagenet'):
|
||||
if dataset == 'imagenet':
|
||||
img_dim = 224
|
||||
out_dim = 1000
|
||||
patch_dim = 16
|
||||
elif 'cifar' in dataset:
|
||||
img_dim = 32
|
||||
out_dim = 10
|
||||
patch_dim = 4
|
||||
|
||||
return VisionTransformer(
|
||||
img_dim=img_dim,
|
||||
patch_dim=patch_dim,
|
||||
out_dim=out_dim,
|
||||
num_channels=3,
|
||||
embedding_dim=1024,
|
||||
num_heads=16,
|
||||
num_layers=24,
|
||||
hidden_dim=4096,
|
||||
dropout_rate=0.1,
|
||||
attn_dropout_rate=0.0,
|
||||
use_representation=False,
|
||||
conv_patch_representation=False,
|
||||
positional_encoding_type="learned",
|
||||
)
|
||||
|
||||
|
||||
def ViT_L32(dataset='imagenet'):
|
||||
if dataset == 'imagenet':
|
||||
img_dim = 224
|
||||
out_dim = 1000
|
||||
patch_dim = 32
|
||||
elif 'cifar' in dataset:
|
||||
img_dim = 32
|
||||
out_dim = 10
|
||||
patch_dim = 4
|
||||
|
||||
return VisionTransformer(
|
||||
img_dim=img_dim,
|
||||
patch_dim=patch_dim,
|
||||
out_dim=out_dim,
|
||||
num_channels=3,
|
||||
embedding_dim=1024,
|
||||
num_heads=16,
|
||||
num_layers=24,
|
||||
hidden_dim=4096,
|
||||
dropout_rate=0.1,
|
||||
attn_dropout_rate=0.0,
|
||||
use_representation=False,
|
||||
conv_patch_representation=False,
|
||||
positional_encoding_type="learned",
|
||||
)
|
||||
|
||||
|
||||
def ViT_H14(dataset='imagenet'):
|
||||
if dataset == 'imagenet':
|
||||
img_dim = 224
|
||||
out_dim = 1000
|
||||
patch_dim = 14
|
||||
elif 'cifar' in dataset:
|
||||
img_dim = 32
|
||||
out_dim = 10
|
||||
patch_dim = 4
|
||||
|
||||
return VisionTransformer(
|
||||
img_dim=img_dim,
|
||||
patch_dim=patch_dim,
|
||||
out_dim=out_dim,
|
||||
num_channels=3,
|
||||
embedding_dim=1280,
|
||||
num_heads=16,
|
||||
num_layers=32,
|
||||
hidden_dim=5120,
|
||||
dropout_rate=0.1,
|
||||
attn_dropout_rate=0.0,
|
||||
use_representation=False,
|
||||
conv_patch_representation=False,
|
||||
positional_encoding_type="learned",
|
||||
)
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
|
||||
class FixedPositionalEncoding(nn.Module):
|
||||
def __init__(self, embedding_dim, max_length=5000):
|
||||
super(FixedPositionalEncoding, self).__init__()
|
||||
|
||||
pe = torch.zeros(max_length, embedding_dim)
|
||||
position = torch.arange(0, max_length, dtype=torch.float).unsqueeze(1)
|
||||
div_term = torch.exp(
|
||||
torch.arange(0, embedding_dim, 2).float()
|
||||
* (-torch.log(torch.tensor(10000.0)) / embedding_dim)
|
||||
)
|
||||
pe[:, 0::2] = torch.sin(position * div_term)
|
||||
pe[:, 1::2] = torch.cos(position * div_term)
|
||||
pe = pe.unsqueeze(0).transpose(0, 1)
|
||||
self.register_buffer('pe', pe)
|
||||
|
||||
def forward(self, x):
|
||||
x = x + self.pe[: x.size(0), :]
|
||||
return x
|
||||
|
||||
|
||||
class LearnedPositionalEncoding(nn.Module):
|
||||
def __init__(self, max_position_embeddings, embedding_dim, seq_length):
|
||||
super(LearnedPositionalEncoding, self).__init__()
|
||||
self.pe = nn.Embedding(max_position_embeddings, embedding_dim)
|
||||
self.seq_length = seq_length
|
||||
|
||||
self.register_buffer(
|
||||
"position_ids",
|
||||
torch.arange(max_position_embeddings).expand((1, -1)),
|
||||
)
|
||||
|
||||
def forward(self, x, position_ids=None):
|
||||
if position_ids is None:
|
||||
position_ids = self.position_ids[:, : self.seq_length]
|
||||
|
||||
try:
|
||||
position_embeddings = self.pe(position_ids)
|
||||
return x + position_embeddings
|
||||
except:
|
||||
import pdb; pdb.set_trace()
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
from torch import nn
|
||||
from .Attention import SelfAttention
|
||||
|
||||
|
||||
class Residual(nn.Module):
|
||||
def __init__(self, fn):
|
||||
super().__init__()
|
||||
self.fn = fn
|
||||
|
||||
def forward(self, x):
|
||||
return self.fn(x) + x
|
||||
|
||||
|
||||
class PreNorm(nn.Module):
|
||||
def __init__(self, dim, fn):
|
||||
super().__init__()
|
||||
self.norm = nn.LayerNorm(dim)
|
||||
self.fn = fn
|
||||
|
||||
def forward(self, x):
|
||||
return self.fn(self.norm(x))
|
||||
|
||||
|
||||
class PreNormDrop(nn.Module):
|
||||
def __init__(self, dim, dropout_rate, fn):
|
||||
super().__init__()
|
||||
self.norm = nn.LayerNorm(dim)
|
||||
self.dropout = nn.Dropout(p=dropout_rate)
|
||||
self.fn = fn
|
||||
|
||||
def forward(self, x):
|
||||
return self.dropout(self.fn(self.norm(x)))
|
||||
|
||||
|
||||
class FeedForward(nn.Module):
|
||||
def __init__(self, dim, hidden_dim, dropout_rate):
|
||||
super().__init__()
|
||||
self.net = nn.Sequential(
|
||||
nn.Linear(dim, hidden_dim),
|
||||
nn.GELU(),
|
||||
nn.Dropout(p=dropout_rate),
|
||||
nn.Linear(hidden_dim, dim),
|
||||
nn.Dropout(p=dropout_rate),
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
return self.net(x)
|
||||
|
||||
|
||||
class TransformerModel(nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
dim,
|
||||
depth,
|
||||
heads,
|
||||
mlp_dim,
|
||||
dropout_rate=0.1,
|
||||
attn_dropout_rate=0.1,
|
||||
):
|
||||
super().__init__()
|
||||
layers = []
|
||||
for _ in range(depth):
|
||||
layers.extend(
|
||||
[
|
||||
Residual(
|
||||
PreNormDrop(
|
||||
dim,
|
||||
dropout_rate,
|
||||
SelfAttention(
|
||||
dim, heads=heads, dropout_rate=attn_dropout_rate
|
||||
),
|
||||
)
|
||||
),
|
||||
Residual(
|
||||
PreNorm(dim, FeedForward(dim, mlp_dim, dropout_rate))
|
||||
),
|
||||
]
|
||||
)
|
||||
self.net = nn.Sequential(*layers)
|
||||
|
||||
def forward(self, x):
|
||||
return self.net(x)
|
||||
+350
@@ -0,0 +1,350 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from configs.feature_sizes import FEATURE_SIZES
|
||||
from model.model_builder import META_ARCHITECTURES as registry
|
||||
|
||||
from .attn import AttentionLayer, FullAttention, ProbAttention
|
||||
from .decoder import Decoder, DecoderLayer
|
||||
from .PositionalEncoding import (FixedPositionalEncoding,
|
||||
LearnedPositionalEncoding)
|
||||
from .Transformer import TransformerModel
|
||||
|
||||
|
||||
# @registry.register('TRANSFORMER')
|
||||
@registry.register('OADTR')
|
||||
class OADTR(nn.Module):
|
||||
def __init__(self, cfg, use_representation=True,
|
||||
conv_patch_representation=False,
|
||||
positional_encoding_type="learned"
|
||||
):
|
||||
super(OADTR, self).__init__()
|
||||
|
||||
self.img_dim = cfg["window_size"] # img_dim
|
||||
self.out_dim = cfg["num_classes"]
|
||||
|
||||
self.patch_dim = cfg["patch_dim"]
|
||||
self.query_num = cfg["query_num"]
|
||||
|
||||
self.embedding_dim = cfg["embedding_dim"]
|
||||
self.num_heads = cfg["num_heads"]
|
||||
self.num_layers = cfg["num_layers"]
|
||||
self.hidden_dim = cfg["hidden_dim"]
|
||||
|
||||
self.dropout_rate = cfg["dropout"]
|
||||
self.attn_dropout_rate = cfg["attn_dropout_rate"]
|
||||
|
||||
self.conv_patch_representation = conv_patch_representation
|
||||
|
||||
self.decoder_embedding_dim = cfg["decoder_embedding_dim"]
|
||||
self.decoder_num_heads = cfg["decoder_num_heads"]
|
||||
self.decoder_layers = cfg["decoder_layers"]
|
||||
self.decoder_embedding_dim_out = cfg["decoder_embedding_dim_out"]
|
||||
self.decoder_attn_dropout_rate = cfg["decoder_attn_dropout_rate"]
|
||||
|
||||
self.use_flow = not cfg['no_flow']
|
||||
self.use_rgb = not cfg['no_rgb']
|
||||
|
||||
self.num_channels = 0
|
||||
if self.use_rgb:
|
||||
self.num_channels += FEATURE_SIZES[cfg['rgb_type']]
|
||||
if self.use_flow:
|
||||
self.num_channels += FEATURE_SIZES[cfg['flow_type']]
|
||||
|
||||
assert self.embedding_dim % self.num_heads == 0
|
||||
assert self.img_dim % self.patch_dim == 0
|
||||
|
||||
self.num_patches = int(self.img_dim // self.patch_dim)
|
||||
self.seq_length = self.num_patches + 1
|
||||
# self.seq_length = self.num_patches + self.img_dim
|
||||
self.flatten_dim = self.patch_dim * self.patch_dim * self.num_channels
|
||||
self.cls_token = nn.Parameter(torch.zeros(1, 1, self.embedding_dim))
|
||||
# self.cls_token = nn.Parameter(torch.zeros(1, self.img_dim, self.embedding_dim))
|
||||
|
||||
self.linear_encoding = nn.Linear(self.flatten_dim, self.embedding_dim)
|
||||
if positional_encoding_type == "learned":
|
||||
self.position_encoding = LearnedPositionalEncoding(
|
||||
self.seq_length, self.embedding_dim, self.seq_length
|
||||
)
|
||||
elif positional_encoding_type == "fixed":
|
||||
self.position_encoding = FixedPositionalEncoding(
|
||||
self.embedding_dim,
|
||||
)
|
||||
print('position encoding :', positional_encoding_type)
|
||||
|
||||
self.pe_dropout = nn.Dropout(p=self.dropout_rate)
|
||||
|
||||
self.encoder = TransformerModel(
|
||||
self.embedding_dim,
|
||||
self.num_layers,
|
||||
self.num_heads,
|
||||
self.hidden_dim,
|
||||
self.dropout_rate,
|
||||
self.attn_dropout_rate,
|
||||
)
|
||||
self.pre_head_ln = nn.LayerNorm(self.embedding_dim)
|
||||
|
||||
d_model = self.decoder_embedding_dim
|
||||
use_representation = False # False
|
||||
if use_representation:
|
||||
self.mlp_head = nn.Sequential(
|
||||
nn.Linear(self.embedding_dim , self.hidden_dim//2),
|
||||
# nn.Tanh(),
|
||||
nn.ReLU(),
|
||||
nn.Linear(self.hidden_dim//2, self.out_dim),
|
||||
)
|
||||
else:
|
||||
self.mlp_head = nn.Linear(self.embedding_dim + d_model, self.out_dim)
|
||||
|
||||
if self.conv_patch_representation:
|
||||
# self.conv_x = nn.Conv2d(
|
||||
# self.num_channels,
|
||||
# self.embedding_dim,
|
||||
# kernel_size=(self.patch_dim, self.patch_dim),
|
||||
# stride=(self.patch_dim, self.patch_dim),
|
||||
# padding=self._get_padding(
|
||||
# 'VALID', (self.patch_dim, self.patch_dim),
|
||||
# ),
|
||||
# )
|
||||
self.conv_x = nn.Conv1d(
|
||||
self.num_channels,
|
||||
self.embedding_dim,
|
||||
kernel_size=self.patch_dim,
|
||||
stride=self.patch_dim,
|
||||
padding=self._get_padding(
|
||||
'VALID', (self.patch_dim),
|
||||
),
|
||||
)
|
||||
else:
|
||||
self.conv_x = None
|
||||
|
||||
self.to_cls_token = nn.Identity()
|
||||
|
||||
# Decoder
|
||||
factor = 1 # 5
|
||||
dropout = self.decoder_attn_dropout_rate
|
||||
# d_model = self.decoder_embedding_dim
|
||||
n_heads = self.decoder_num_heads
|
||||
d_layers = self.decoder_layers
|
||||
d_ff = self.decoder_embedding_dim_out # args.decoder_embedding_dim_out or 4*args.decoder_embedding_dim None
|
||||
activation = 'gelu' # 'gelu'
|
||||
self.decoder = Decoder(
|
||||
[
|
||||
DecoderLayer(
|
||||
AttentionLayer(FullAttention(True, factor, attention_dropout=dropout), # True
|
||||
d_model, n_heads), # ProbAttention FullAttention
|
||||
AttentionLayer(FullAttention(False, factor, attention_dropout=dropout), # False
|
||||
d_model, n_heads),
|
||||
d_model,
|
||||
d_ff,
|
||||
dropout=dropout,
|
||||
activation=activation,
|
||||
)
|
||||
for l in range(d_layers)
|
||||
],
|
||||
norm_layer=torch.nn.LayerNorm(d_model)
|
||||
)
|
||||
self.decoder_cls_token = nn.Parameter(torch.zeros(1, self.query_num, d_model))
|
||||
if positional_encoding_type == "learned":
|
||||
self.decoder_position_encoding = LearnedPositionalEncoding(
|
||||
self.query_num, self.embedding_dim, self.query_num
|
||||
)
|
||||
elif positional_encoding_type == "fixed":
|
||||
self.decoder_position_encoding = FixedPositionalEncoding(
|
||||
self.embedding_dim,
|
||||
)
|
||||
print('position decoding :', positional_encoding_type)
|
||||
self.classifier = nn.Linear(d_model, self.out_dim)
|
||||
self.after_dropout = nn.Dropout(p=self.dropout_rate)
|
||||
# self.merge_fc = nn.Linear(d_model, 1)
|
||||
# self.merge_sigmoid = nn.Sigmoid()
|
||||
|
||||
|
||||
def forward(self, sequence_input_rgb, sequence_input_flow):
|
||||
if self.use_rgb and self.use_flow:
|
||||
x = torch.cat((sequence_input_rgb, sequence_input_flow), 2)
|
||||
elif self.use_rgb:
|
||||
x = sequence_input_rgb
|
||||
elif self.use_flow:
|
||||
x = sequence_input_flow
|
||||
|
||||
x = self.linear_encoding(x) # B, 128, 1204
|
||||
cls_tokens = self.cls_token.expand(x.shape[0], -1, -1) # B, 1, 1024
|
||||
# x = torch.cat((cls_tokens, x), dim=1)
|
||||
x = torch.cat((x, cls_tokens), dim=1) # B, seq+1, 1024
|
||||
try:
|
||||
x = self.position_encoding(x) # B, seq+1, 1024
|
||||
except:
|
||||
import pdb; pdb.set_trace()
|
||||
x = self.pe_dropout(x) # not delete
|
||||
|
||||
# apply transformer
|
||||
x = self.encoder(x)
|
||||
x = self.pre_head_ln(x) # B, seq+1, 1024
|
||||
|
||||
# decoder
|
||||
decoder_cls_token = self.decoder_cls_token.expand(x.shape[0], -1, -1) # [B, 8, 1024]
|
||||
# decoder_cls_token = self.after_dropout(decoder_cls_token) # add
|
||||
# decoder_cls_token = self.decoder_position_encoding(decoder_cls_token) # [128, 8, 1024]
|
||||
dec = self.decoder(decoder_cls_token, x) # [128, 8, 1024]
|
||||
dec = self.after_dropout(dec) # add
|
||||
|
||||
# merge_atte = self.merge_sigmoid(self.merge_fc(dec)) # [128, 8, 1]
|
||||
# dec_for_token = (merge_atte*dec).sum(dim=1) # [128, 1024]
|
||||
# dec_for_token = (merge_atte*dec).sum(dim=1)/(merge_atte.sum(dim=-2) + 0.0001)
|
||||
# *** RuntimeError: mat1 and mat2 shapes cannot be multiplied (64x2048 and 1024x2)
|
||||
dec_for_token = dec.mean(dim=1) # [64, 1024]
|
||||
|
||||
# dec_for_token = dec.max(dim=1)[0]
|
||||
dec_cls_out = self.classifier(dec) # [B, 8, NUM_CLASS?]
|
||||
# set_trace()
|
||||
# x = self.to_cls_token(x[:, 0])
|
||||
|
||||
x = torch.cat((self.to_cls_token(x[:, -1]), dec_for_token), dim=1) # [B, 2048]
|
||||
x = self.mlp_head(x) # [B, NUM_CLASSES]
|
||||
|
||||
# original OADTR
|
||||
# return x, dec_cls_out
|
||||
|
||||
# x = F.log_softmax(x, dim=-1)
|
||||
out_dict = {}
|
||||
out_dict['logits'] = x.unsqueeze(1) # [B, 1, NUM_CLASSES] ex) [64, 1, 2]
|
||||
out_dict['dec_logits'] = dec_cls_out # [B, 8, NUM_CLASSES] ex) [64, 8, 2]
|
||||
|
||||
# out_dict['logits'] = x #x.unsqueeze(1)
|
||||
return out_dict
|
||||
|
||||
def _get_padding(self, padding_type, kernel_size):
|
||||
assert padding_type in ['SAME', 'VALID']
|
||||
if padding_type == 'SAME':
|
||||
_list = [(k - 1) // 2 for k in kernel_size]
|
||||
return tuple(_list)
|
||||
return tuple(0 for _ in kernel_size)
|
||||
|
||||
@registry.register('Transformer')
|
||||
class ViTEnc(nn.Module):
|
||||
def __init__(self, cfg, use_representation=True,
|
||||
conv_patch_representation=False,
|
||||
positional_encoding_type="learned"
|
||||
):
|
||||
super(ViTEnc, self).__init__()
|
||||
self.img_dim = cfg["window_size"]
|
||||
self.out_dim = cfg["num_classes"]
|
||||
self.embedding_dim = cfg["embedding_dim"]
|
||||
self.patch_dim = cfg["patch_dim"]
|
||||
self.num_heads = cfg["num_heads"]
|
||||
self.num_layers = cfg["num_layers"]
|
||||
self.hidden_dim = cfg["hidden_dim"]
|
||||
self.dropout_rate = cfg["dropout"]
|
||||
self.use_flow = not cfg['no_flow']
|
||||
self.use_rgb = not cfg['no_rgb']
|
||||
self.num_channels= 0
|
||||
if self.use_rgb:
|
||||
self.num_channels += FEATURE_SIZES[cfg['rgb_type']]
|
||||
if self.use_flow:
|
||||
self.num_channels += FEATURE_SIZES[cfg['flow_type']]
|
||||
self.attn_dropout_rate = cfg["attn_dropout_rate"]
|
||||
assert self.embedding_dim % self.num_heads == 0
|
||||
assert self.img_dim % self.patch_dim == 0
|
||||
self.conv_patch_representation = conv_patch_representation
|
||||
self.num_patches = int(self.img_dim // self.patch_dim)
|
||||
self.seq_length = self.num_patches + 1
|
||||
# self.seq_length = self.num_patches + self.img_dim
|
||||
self.flatten_dim = self.patch_dim * self.patch_dim * self.num_channels
|
||||
self.cls_token = nn.Parameter(torch.zeros(1, 1, self.embedding_dim))
|
||||
# self.cls_token = nn.Parameter(torch.zeros(1, self.img_dim, self.embedding_dim))
|
||||
|
||||
self.linear_encoding = nn.Linear(self.flatten_dim, self.embedding_dim)
|
||||
if positional_encoding_type == "learned":
|
||||
self.position_encoding = LearnedPositionalEncoding(
|
||||
self.seq_length, self.embedding_dim, self.seq_length
|
||||
)
|
||||
elif positional_encoding_type == "fixed":
|
||||
self.position_encoding = FixedPositionalEncoding(
|
||||
self.embedding_dim,
|
||||
)
|
||||
print('position encoding :', positional_encoding_type)
|
||||
|
||||
self.pe_dropout = nn.Dropout(p=self.dropout_rate)
|
||||
|
||||
self.encoder = TransformerModel(
|
||||
self.embedding_dim,
|
||||
self.num_layers,
|
||||
self.num_heads,
|
||||
self.hidden_dim,
|
||||
self.dropout_rate,
|
||||
self.attn_dropout_rate,
|
||||
)
|
||||
self.pre_head_ln = nn.LayerNorm(self.embedding_dim)
|
||||
|
||||
use_representation = False # False
|
||||
if use_representation:
|
||||
self.mlp_head = nn.Sequential(
|
||||
nn.Linear(self.embedding_dim , self.hidden_dim//2),
|
||||
# nn.Tanh(),
|
||||
nn.ReLU(),
|
||||
nn.Linear(self.hidden_dim//2, self.out_dim),
|
||||
)
|
||||
else:
|
||||
self.mlp_head = nn.Linear(self.embedding_dim, self.out_dim)
|
||||
|
||||
if self.conv_patch_representation:
|
||||
# self.conv_x = nn.Conv2d(
|
||||
# self.num_channels,
|
||||
# self.embedding_dim,
|
||||
# kernel_size=(self.patch_dim, self.patch_dim),
|
||||
# stride=(self.patch_dim, self.patch_dim),
|
||||
# padding=self._get_padding(
|
||||
# 'VALID', (self.patch_dim, self.patch_dim),
|
||||
# ),
|
||||
# )
|
||||
self.conv_x = nn.Conv1d(
|
||||
self.num_channels,
|
||||
self.embedding_dim,
|
||||
kernel_size=self.patch_dim,
|
||||
stride=self.patch_dim,
|
||||
padding=self._get_padding(
|
||||
'VALID', (self.patch_dim),
|
||||
),
|
||||
)
|
||||
else:
|
||||
self.conv_x = None
|
||||
|
||||
self.to_cls_token = nn.Identity()
|
||||
|
||||
|
||||
def forward(self, sequence_input_rgb, sequence_input_flow):
|
||||
if self.use_rgb and self.use_flow:
|
||||
x = torch.cat((sequence_input_rgb, sequence_input_flow), 2)
|
||||
elif self.use_rgb:
|
||||
x = sequence_input_rgb
|
||||
elif self.use_flow:
|
||||
x = sequence_input_flow
|
||||
|
||||
x = self.linear_encoding(x)
|
||||
cls_tokens = self.cls_token.expand(x.shape[0], -1, -1) # B, 1, 1024
|
||||
# x = torch.cat((cls_tokens, x), dim=1)
|
||||
x = torch.cat((x, cls_tokens), dim=1) # B, seq+1, 1024
|
||||
|
||||
x = self.position_encoding(x) # B, seq+1, 1024
|
||||
x = self.pe_dropout(x) # not delete
|
||||
|
||||
# apply transformer
|
||||
x = self.encoder(x)
|
||||
x = self.pre_head_ln(x) # B, seq+1, 1024
|
||||
|
||||
x = self.to_cls_token(x[:, 0]) # B, 1024
|
||||
# x = self.to_cls_token(x[:,0:self.img_dim]) # B, 1024
|
||||
x = self.mlp_head(x)
|
||||
# x = F.log_softmax(x, dim=-1)
|
||||
out_dict = {}
|
||||
out_dict['logits'] = x.unsqueeze(1)
|
||||
# out_dict['logits'] = x #x.unsqueeze(1)
|
||||
return out_dict
|
||||
|
||||
def _get_padding(self, padding_type, kernel_size):
|
||||
assert padding_type in ['SAME', 'VALID']
|
||||
if padding_type == 'SAME':
|
||||
_list = [(k - 1) // 2 for k in kernel_size]
|
||||
return tuple(_list)
|
||||
return tuple(0 for _ in kernel_size)
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
from .ViT import ViTEnc
|
||||
from .HybridViT import ResNetHybridViT, AxialNetHybridViT
|
||||
|
||||
__all__ = ['ResNetHybridViT', 'AxialNetHybridViT', 'VisionTransformer_v3']
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
import numpy as np
|
||||
|
||||
from math import sqrt
|
||||
|
||||
|
||||
class TriangularCausalMask():
|
||||
def __init__(self, B, L, device="cpu"):
|
||||
mask_shape = [B, 1, L, L]
|
||||
with torch.no_grad():
|
||||
self._mask = torch.triu(torch.ones(mask_shape, dtype=torch.bool), diagonal=1).to(device)
|
||||
|
||||
@property
|
||||
def mask(self):
|
||||
return self._mask
|
||||
|
||||
|
||||
class ProbMask():
|
||||
def __init__(self, B, H, L, index, scores, device="cpu"):
|
||||
_mask = torch.ones(L, scores.shape[-1], dytpe=torch.bool).to(device).triu(1)
|
||||
_mask_ex = _mask[None, None, :].expand(B, H, L, scores.shape[-1])
|
||||
indicator = _mask_ex[torch.arange(B)[:, None, None],
|
||||
torch.arange(H)[None, :, None],
|
||||
index, :].to(device)
|
||||
self._mask = indicator.view(scores.shape).to(device)
|
||||
|
||||
@property
|
||||
def mask(self):
|
||||
return self._mask
|
||||
|
||||
|
||||
class FullAttention(nn.Module):
|
||||
def __init__(self, mask_flag=True, factor=5, scale=None, attention_dropout=0.1):
|
||||
super(FullAttention, self).__init__()
|
||||
self.scale = scale
|
||||
self.mask_flag = mask_flag
|
||||
self.dropout = nn.Dropout(attention_dropout)
|
||||
|
||||
def forward(self, queries, keys, values, attn_mask):
|
||||
B, L, H, E = queries.shape
|
||||
_, S, _, D = values.shape
|
||||
scale = self.scale or 1. / sqrt(E)
|
||||
|
||||
scores = torch.einsum("blhe,bshe->bhls", queries, keys)
|
||||
if self.mask_flag:
|
||||
if attn_mask is None:
|
||||
attn_mask = TriangularCausalMask(B, L, device=queries.device)
|
||||
|
||||
scores.masked_fill_(attn_mask.mask, -np.inf)
|
||||
|
||||
A = self.dropout(torch.softmax(scale * scores, dim=-1))
|
||||
V = torch.einsum("bhls,bshd->blhd", A, values)
|
||||
|
||||
return V.contiguous()
|
||||
|
||||
|
||||
class ProbAttention(nn.Module):
|
||||
def __init__(self, mask_flag=True, factor=5, scale=None, attention_dropout=0.1):
|
||||
super(ProbAttention, self).__init__()
|
||||
self.factor = factor
|
||||
self.scale = scale
|
||||
self.mask_flag = mask_flag
|
||||
self.dropout = nn.Dropout(attention_dropout)
|
||||
|
||||
def _prob_QK(self, Q, K, sample_k, n_top):
|
||||
# Q [B, H, L, D]
|
||||
B, H, L, E = K.shape
|
||||
_, _, S, _ = Q.shape
|
||||
|
||||
# calculate the sampled Q_K
|
||||
K_expand = K.unsqueeze(-3).expand(B, H, S, L, E)
|
||||
indx_sample = torch.randint(L, (S, sample_k))
|
||||
K_sample = K_expand[:, :, torch.arange(S).unsqueeze(1), indx_sample, :]
|
||||
Q_K_sample = torch.matmul(Q.unsqueeze(-2), K_sample.transpose(-2, -1)).squeeze()
|
||||
|
||||
# find the Top_k query with sparisty measurement
|
||||
M = Q_K_sample.max(-1)[0] - torch.div(Q_K_sample.sum(-1), L)
|
||||
M_top = M.topk(n_top, sorted=False)[1]
|
||||
|
||||
# use the reduced Q to calculate Q_K
|
||||
Q_reduce = Q[torch.arange(B)[:, None, None],
|
||||
torch.arange(H)[None, :, None],
|
||||
M_top, :]
|
||||
Q_K = torch.matmul(Q_reduce, K.transpose(-2, -1))
|
||||
|
||||
return Q_K, M_top
|
||||
|
||||
def _get_initial_context(self, V, L_Q):
|
||||
B, H, L_V, D = V.shape
|
||||
if not self.mask_flag:
|
||||
V_sum = V.sum(dim=-2)
|
||||
contex = V_sum.unsqueeze(-2).expand(B, H, L_Q, V_sum.shape[-1]).clone()
|
||||
else: # use mask
|
||||
assert (L_Q == L_V) # requires that L_Q == L_V, i.e. for self-attention only
|
||||
contex = V.cumsum(dim=-1)
|
||||
return contex
|
||||
|
||||
def _update_context(self, context_in, V, scores, index, L_Q, attn_mask):
|
||||
B, H, L_V, D = V.shape
|
||||
|
||||
if self.mask_flag:
|
||||
attn_mask = ProbMask(B, H, L_Q, index, scores, device=V.device)
|
||||
scores.masked_fill_(attn_mask.mask, -np.inf)
|
||||
|
||||
attn = torch.softmax(scores, dim=-1) # nn.Softmax(dim=-1)(scores)
|
||||
|
||||
context_in[torch.arange(B)[:, None, None],
|
||||
torch.arange(H)[None, :, None],
|
||||
index, :] = torch.matmul(attn, V)
|
||||
return context_in
|
||||
|
||||
def forward(self, queries, keys, values, attn_mask):
|
||||
B, L, H, D = queries.shape
|
||||
_, S, _, _ = keys.shape
|
||||
|
||||
queries = queries.view(B, H, L, -1)
|
||||
keys = keys.view(B, H, S, -1)
|
||||
values = values.view(B, H, S, -1)
|
||||
|
||||
U = self.factor * np.ceil(np.log(S)).astype('int').item()
|
||||
u = self.factor * np.ceil(np.log(L)).astype('int').item()
|
||||
|
||||
scores_top, index = self._prob_QK(queries, keys, u, U)
|
||||
# add scale factor
|
||||
scale = self.scale or 1. / sqrt(D)
|
||||
if scale is not None:
|
||||
scores_top = scores_top * scale
|
||||
# get the context
|
||||
context = self._get_initial_context(values, L)
|
||||
# update the context with selected top_k queries
|
||||
context = self._update_context(context, values, scores_top, index, L, attn_mask)
|
||||
|
||||
return context.contiguous()
|
||||
|
||||
|
||||
class AttentionLayer(nn.Module):
|
||||
def __init__(self, attention, d_model, n_heads, d_keys=None,
|
||||
d_values=None):
|
||||
super(AttentionLayer, self).__init__()
|
||||
|
||||
d_keys = d_keys or (d_model // n_heads)
|
||||
d_values = d_values or (d_model // n_heads)
|
||||
|
||||
self.inner_attention = attention
|
||||
self.query_projection = nn.Linear(d_model, d_keys * n_heads)
|
||||
self.key_projection = nn.Linear(d_model, d_keys * n_heads)
|
||||
self.value_projection = nn.Linear(d_model, d_values * n_heads)
|
||||
self.out_projection = nn.Linear(d_values * n_heads, d_model)
|
||||
self.n_heads = n_heads
|
||||
|
||||
def forward(self, queries, keys, values, attn_mask):
|
||||
B, L, _ = queries.shape
|
||||
_, S, _ = keys.shape
|
||||
H = self.n_heads
|
||||
|
||||
queries = self.query_projection(queries).view(B, L, H, -1)
|
||||
keys = self.key_projection(keys).view(B, S, H, -1)
|
||||
values = self.value_projection(values).view(B, S, H, -1)
|
||||
|
||||
out = self.inner_attention(
|
||||
queries,
|
||||
keys,
|
||||
values,
|
||||
attn_mask
|
||||
).view(B, L, -1)
|
||||
|
||||
return self.out_projection(out)
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
|
||||
class DecoderLayer(nn.Module):
|
||||
def __init__(self, self_attention, cross_attention, d_model, d_ff=None,
|
||||
dropout=0.1, activation="relu"):
|
||||
super(DecoderLayer, self).__init__()
|
||||
d_ff = d_ff or 4*d_model
|
||||
self.self_attention = self_attention
|
||||
self.cross_attention = cross_attention
|
||||
self.conv1 = nn.Conv1d(in_channels=d_model, out_channels=d_ff, kernel_size=1)
|
||||
self.conv2 = nn.Conv1d(in_channels=d_ff, out_channels=d_model, kernel_size=1)
|
||||
self.norm1 = nn.LayerNorm(d_model)
|
||||
self.norm2 = nn.LayerNorm(d_model)
|
||||
self.norm3 = nn.LayerNorm(d_model)
|
||||
self.dropout = nn.Dropout(dropout)
|
||||
self.activation = F.relu if activation == "relu" else F.gelu
|
||||
|
||||
def forward(self, x, cross, x_mask=None, cross_mask=None):
|
||||
x = x + self.dropout(self.self_attention(
|
||||
x, x, x,
|
||||
attn_mask=x_mask
|
||||
))
|
||||
x = self.norm1(x)
|
||||
|
||||
x = x + self.dropout(self.cross_attention(
|
||||
x, cross, cross,
|
||||
attn_mask=cross_mask
|
||||
))
|
||||
|
||||
y = x = self.norm2(x)
|
||||
y = self.dropout(self.activation(self.conv1(y.transpose(-1,1))))
|
||||
y = self.dropout(self.conv2(y).transpose(-1,1))
|
||||
|
||||
return self.norm3(x+y)
|
||||
|
||||
|
||||
class Decoder(nn.Module):
|
||||
def __init__(self, layers, norm_layer=None):
|
||||
super(Decoder, self).__init__()
|
||||
self.layers = nn.ModuleList(layers)
|
||||
self.norm = norm_layer
|
||||
|
||||
def forward(self, x, cross, x_mask=None, cross_mask=None):
|
||||
for layer in self.layers:
|
||||
x = layer(x, cross, x_mask=x_mask, cross_mask=cross_mask)
|
||||
|
||||
if self.norm is not None:
|
||||
x = self.norm(x)
|
||||
|
||||
return x
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
# FROM LSTR
|
||||
import math
|
||||
import torch.nn as nn
|
||||
|
||||
def weights_init(m):
|
||||
if isinstance(m, nn.Linear):
|
||||
nn.init.kaiming_uniform_(m.weight.data, a=math.sqrt(5))
|
||||
elif isinstance(m, nn.Conv1d):
|
||||
nn.init.normal_(m.weight.data)
|
||||
if m.bias is not None:
|
||||
nn.init.normal_(m.bias.data)
|
||||
elif isinstance(m, nn.Conv2d):
|
||||
nn.init.xavier_normal_(m.weight.data)
|
||||
if m.bias is not None:
|
||||
nn.init.normal_(m.bias.data)
|
||||
elif isinstance(m, nn.ConvTranspose1d):
|
||||
nn.init.normal_(m.weight.data)
|
||||
if m.bias is not None:
|
||||
nn.init.normal_(m.bias.data)
|
||||
elif isinstance(m, nn.ConvTranspose2d):
|
||||
nn.init.xavier_normal_(m.weight.data)
|
||||
if m.bias is not None:
|
||||
nn.init.normal_(m.bias.data)
|
||||
elif isinstance(m, nn.BatchNorm1d):
|
||||
nn.init.normal_(m.weight.data, mean=1, std=0.02)
|
||||
nn.init.constant_(m.bias.data, 0)
|
||||
elif isinstance(m, nn.BatchNorm2d):
|
||||
nn.init.normal_(m.weight.data, mean=1, std=0.02)
|
||||
nn.init.constant_(m.bias.data, 0)
|
||||
elif isinstance(m, nn.GRUCell):
|
||||
for param in m.parameters():
|
||||
if len(param.shape) >= 2:
|
||||
nn.init.orthogonal_(param.data)
|
||||
else:
|
||||
nn.init.normal_(param.data)
|
||||
@@ -0,0 +1,26 @@
|
||||
PATH_TO_CONFIG_FILE=configs/EasyCom/mamba_aa_easycom.yaml
|
||||
PATH_TO_CHECKPOINT_FILE=checkpoints/mamba_easycom_AV.pth
|
||||
shift # Remove the first argument so $@ contains all other arguments
|
||||
|
||||
echo "Running MiniROAD inference"
|
||||
echo "Config file: $PATH_TO_CONFIG_FILE"
|
||||
echo "Additional options: $@"
|
||||
|
||||
# get the basename of the config file, without the extension
|
||||
CONFIG_FILENAME=$(basename -- "$PATH_TO_CONFIG_FILE")
|
||||
CONFIG_FILENAME="${CONFIG_FILENAME%.*}"
|
||||
|
||||
# --no_flow (for no audio) --no_rgb --wandb
|
||||
# Pass all remaining arguments directly to the Python script
|
||||
python -u main.py --config "$PATH_TO_CONFIG_FILE" \
|
||||
--save-as-cfg \
|
||||
--eval \
|
||||
--ckpt "$PATH_TO_CHECKPOINT_FILE" \
|
||||
$@
|
||||
|
||||
echo "Finished MiniROAD inference"
|
||||
echo "python -u main.py --config '$PATH_TO_CONFIG_FILE' \
|
||||
--save-as-cfg \
|
||||
--ckpt '$PATH_TO_CHECKPOINT_FILE' \
|
||||
--eval \
|
||||
$@"
|
||||
@@ -0,0 +1,26 @@
|
||||
PATH_TO_CONFIG_FILE=configs/EasyCom/miniroad_aa_easycom.yaml
|
||||
PATH_TO_CHECKPOINT_FILE=checkpoints/miniroad_easycom_AV.pth
|
||||
shift # Remove the first argument so $@ contains all other arguments
|
||||
|
||||
echo "Running MiniROAD inference"
|
||||
echo "Config file: $PATH_TO_CONFIG_FILE"
|
||||
echo "Additional options: $@"
|
||||
|
||||
# get the basename of the config file, without the extension
|
||||
CONFIG_FILENAME=$(basename -- "$PATH_TO_CONFIG_FILE")
|
||||
CONFIG_FILENAME="${CONFIG_FILENAME%.*}"
|
||||
|
||||
# --no_flow (for no audio) --no_rgb --wandb
|
||||
# Pass all remaining arguments directly to the Python script
|
||||
python -u main.py --config "$PATH_TO_CONFIG_FILE" \
|
||||
--save-as-cfg \
|
||||
--eval \
|
||||
--ckpt "$PATH_TO_CHECKPOINT_FILE" \
|
||||
$@
|
||||
|
||||
echo "Finished MiniROAD inference"
|
||||
echo "python -u main.py --config '$PATH_TO_CONFIG_FILE' \
|
||||
--save-as-cfg \
|
||||
--ckpt '$PATH_TO_CHECKPOINT_FILE' \
|
||||
--eval \
|
||||
$@"
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
PATH_TO_CONFIG_FILE=configs/EasyCom/miniroad_aa_easycom.yaml
|
||||
|
||||
echo "Running MiniROAD training"
|
||||
echo "Config file: $PATH_TO_CONFIG_FILE"
|
||||
echo "Additional options: $@"
|
||||
|
||||
# Additional options: --no_flow (for no audio) --no_rgb --wandb
|
||||
# Pass all remaining arguments directly to the Python script
|
||||
python -u main.py --config "$PATH_TO_CONFIG_FILE" --save-as-cfg $@
|
||||
|
||||
echo "Finished MiniROAD training"
|
||||
echo "Run python -u main.py --config $PATH_TO_CONFIG_FILE --save-as-cfg $@"
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
from .train_builder import build_trainer
|
||||
from .train import train_one_epoch
|
||||
from .eval_builder import build_eval
|
||||
from .eval import Evaluate
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
import json
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import wandb
|
||||
from tqdm import tqdm
|
||||
from trainer.eval_builder import EVAL
|
||||
from utils import *
|
||||
from utils import (event_accuracy, perframe_average_precision,
|
||||
thumos_postprocessing)
|
||||
|
||||
|
||||
@EVAL.register("OAD")
|
||||
class Evaluate(nn.Module):
|
||||
|
||||
def __init__(self, cfg):
|
||||
super(Evaluate, self).__init__()
|
||||
self.data_processing = thumos_postprocessing if 'THUMOS' in cfg['data_name'] else None
|
||||
self.metric = cfg['metric']
|
||||
self.eval_method = perframe_average_precision
|
||||
self.event_eval_method = event_accuracy
|
||||
self.all_class_names = json.load(open(cfg['video_list_path']))[cfg["data_name"].split('_')[0]]['class_index']
|
||||
self.cfg = cfg
|
||||
self.ignore_index = cfg['ignore_index'] if 'ignore_index' in cfg else -100
|
||||
|
||||
def eval(self, model, dataloader, logger, criterion, use_wandb, epoch, writer, result_path):
|
||||
device = "cuda:0"
|
||||
model.eval()
|
||||
eval_preds = {}
|
||||
gt_targets_dict = {}
|
||||
|
||||
with torch.no_grad():
|
||||
pred_scores, gt_targets = [], []
|
||||
start = time.time()
|
||||
eval_loss = 0.0
|
||||
for it, (vid_name, rgb_input, flow_input, target) in enumerate(tqdm(dataloader, desc='Evaluation:', leave=False)):
|
||||
rgb_input, flow_input, target = rgb_input.to(device), flow_input.to(device), target.to(device)
|
||||
out_dict = model(rgb_input, flow_input)
|
||||
|
||||
if criterion != None:
|
||||
loss = criterion(out_dict, target)
|
||||
eval_loss += loss.item()
|
||||
|
||||
pred_logit = out_dict['logits']
|
||||
prob_val = pred_logit.squeeze().cpu().numpy()
|
||||
target_batch = target.squeeze().cpu().numpy()
|
||||
pred_scores += list(prob_val)
|
||||
gt_targets += list(target_batch)
|
||||
|
||||
eval_preds[vid_name] = prob_val
|
||||
|
||||
end = time.time()
|
||||
num_frames = len(gt_targets)
|
||||
|
||||
# save prediction scores
|
||||
# if result_path != None:
|
||||
time_taken = end - start
|
||||
logger.info(f'Processed {num_frames} frames in {time_taken:.1f} seconds ({num_frames / time_taken :.1f} FPS)')
|
||||
|
||||
result_AP = self.eval_method(pred_scores, gt_targets, self.all_class_names, self.ignore_index, self.data_processing, 'AP')
|
||||
result_cAP = self.eval_method(pred_scores, gt_targets, self.all_class_names, self.ignore_index, self.data_processing, 'cAP')
|
||||
logger.info(f'per_class_ap: {result_AP["per_class_AP"]}')
|
||||
logger.info(f'all_class_names: {self.all_class_names}')
|
||||
logger.info(f'len(result_AP["per_class_AP"]: {len(result_AP["per_class_AP"])}')
|
||||
|
||||
per_class_AP = result_AP["per_class_AP"]
|
||||
if use_wandb == True:
|
||||
wandb.log({"Validation Loss": eval_loss / len(dataloader)})
|
||||
wandb.log({"Validation per_class_AP": per_class_AP})
|
||||
|
||||
if writer != None:
|
||||
writer.add_scalar("Validation Loss", eval_loss / len(dataloader), epoch)
|
||||
|
||||
sum_class_AP = 0.0
|
||||
|
||||
for class_name in self.all_class_names:
|
||||
# TVSeries
|
||||
if class_name == 'background':
|
||||
continue
|
||||
# THUMOS
|
||||
if class_name == 'Background' or class_name == 'Ambiguous':
|
||||
continue
|
||||
|
||||
# logger.info(f'{class_name}')
|
||||
class_AP = result_AP["per_class_AP"][class_name]
|
||||
sum_class_AP += class_AP
|
||||
|
||||
class_cAP = result_cAP["per_class_AP"][class_name]
|
||||
logger.info(f'AP @ {class_name}: {class_AP:.4f}')
|
||||
logger.info(f'cAP @ {class_name}: {class_cAP:.4f}')
|
||||
|
||||
logger.info(f'sum_AP: {sum_class_AP}, mean_AP: {sum_class_AP / (len(self.all_class_names) - 1):.4f}')
|
||||
|
||||
logger.info(f'pred_scores: {pred_scores[:2]}')
|
||||
logger.info(f'len(pred_scores): {len(pred_scores)}')
|
||||
logger.info(f'len(pred_scores[0]): {len(pred_scores[0])}')
|
||||
|
||||
return result_AP['mean_AP'], result_cAP['mean_AP'], eval_preds
|
||||
|
||||
def forward(self, model, dataloader, logger, criterion=None, use_wandb=False, epoch=None, writer=None, result_path = None):
|
||||
return self.eval(model, dataloader, logger, criterion, use_wandb, epoch, writer, result_path)
|
||||
|
||||
@EVAL.register("OADTR")
|
||||
class EvaluateOADTR(nn.Module):
|
||||
|
||||
def __init__(self, cfg):
|
||||
super(EvaluateOADTR, self).__init__()
|
||||
self.data_processing = thumos_postprocessing if 'THUMOS' in cfg['data_name'] else None
|
||||
self.metric = cfg['metric']
|
||||
self.eval_method = perframe_average_precision
|
||||
self.all_class_names = json.load(open(cfg['video_list_path']))[cfg["data_name"].split('_')[0]]['class_index']
|
||||
self.cfg = cfg
|
||||
|
||||
def eval(self, model, dataloader, logger, criterion, use_wandb, epoch, writer):
|
||||
device = "cuda:0"
|
||||
model.eval()
|
||||
eval_preds = {}
|
||||
gt_targets_dict = {}
|
||||
|
||||
with torch.no_grad():
|
||||
pred_scores, gt_targets = [], []
|
||||
start = time.time()
|
||||
eval_loss = 0.0
|
||||
for it, (vid_name, rgb_input, flow_input, enc_target, distance_target, class_h_target, dec_target) in enumerate(tqdm(dataloader, desc='Evaluation:', leave=False)):
|
||||
rgb_input, flow_input, target = rgb_input.to(device), flow_input.to(device), target.to(device)
|
||||
out_dict = model(rgb_input, flow_input)
|
||||
|
||||
if criterion != None:
|
||||
loss = criterion(out_dict, target)
|
||||
eval_loss += loss.item()
|
||||
|
||||
pred_logit = out_dict['logits']
|
||||
prob_val = pred_logit.squeeze().cpu().numpy()
|
||||
target_batch = target.squeeze().cpu().numpy()
|
||||
pred_scores += list(prob_val)
|
||||
gt_targets += list(target_batch)
|
||||
|
||||
# eval_preds[vid_name] = pred_scores
|
||||
# gt_targets_dict[vid_name] = pred_scores
|
||||
|
||||
if use_wandb == True:
|
||||
wandb.log({"Validation Loss": eval_loss / len(dataloader)})
|
||||
|
||||
if writer != None:
|
||||
writer.add_scalar("Validation Loss", eval_loss / len(dataloader), epoch)
|
||||
|
||||
end = time.time()
|
||||
num_frames = len(gt_targets)
|
||||
result_AP = self.eval_method(pred_scores, gt_targets, self.all_class_names, self.data_processing, 'AP')
|
||||
result_cAP = self.eval_method(pred_scores, gt_targets, self.all_class_names, self.data_processing, 'cAP')
|
||||
time_taken = end - start
|
||||
logger.info(f'Processed {num_frames} frames in {time_taken:.1f} seconds ({num_frames / time_taken :.1f} FPS)')
|
||||
# logger.info(f'per_class_ap: {result["per_class_AP"]}')
|
||||
logger.info(f'all_class_names: {self.all_class_names}')
|
||||
logger.info(f'len(result_AP["per_class_AP"]: {len(result_AP["per_class_AP"])}')
|
||||
|
||||
sum_class_AP = 0.0
|
||||
|
||||
for class_name in self.all_class_names:
|
||||
# TVSeries
|
||||
if class_name == 'background':
|
||||
continue
|
||||
# THUMOS
|
||||
if class_name == 'Background' or class_name == 'Ambiguous':
|
||||
continue
|
||||
|
||||
# logger.info(f'{class_name}')
|
||||
class_AP = result_AP["per_class_AP"][class_name]
|
||||
sum_class_AP += class_AP
|
||||
|
||||
class_cAP = result_cAP["per_class_AP"][class_name]
|
||||
logger.info(f'AP @ {class_name}: {class_AP:.4f}')
|
||||
logger.info(f'cAP @ {class_name}: {class_cAP:.4f}')
|
||||
|
||||
logger.info(f'sum_AP: {sum_class_AP}, mean_AP: {sum_class_AP / (len(self.all_class_names) - 1):.4f}')
|
||||
|
||||
logger.info(f'pred_scores: {pred_scores[:2]}')
|
||||
logger.info(f'len(pred_scores): {len(pred_scores)}')
|
||||
logger.info(f'len(pred_scores[0]): {len(pred_scores[0])}')
|
||||
|
||||
return result_AP['mean_AP'], result_cAP['mean_AP'], eval_preds
|
||||
|
||||
def forward(self, model, dataloader, logger, criterion=None, use_wandb=False, epoch=None, writer=None):
|
||||
return self.eval(model, dataloader, logger, criterion, use_wandb, epoch, writer)
|
||||
|
||||
|
||||
@EVAL.register("ANTICIPATION")
|
||||
class ANT_Evaluate(nn.Module):
|
||||
|
||||
def __init__(self, cfg):
|
||||
super(ANT_Evaluate, self).__init__()
|
||||
data_name = cfg["data_name"].split('_')[0]
|
||||
self.data_processing = thumos_postprocessing if data_name == 'THUMOS' else None
|
||||
self.metric = cfg['metric']
|
||||
self.eval_method = perframe_average_precision
|
||||
self.event_eval_method = event_accuracy
|
||||
self.all_class_names = json.load(open(cfg['video_list_path']))[data_name]['class_index']
|
||||
self.cfg = cfg
|
||||
|
||||
if 'ignore_index' in cfg:
|
||||
self.ignore_index = cfg['ignore_index']
|
||||
else:
|
||||
raise NotImplementedError("Please set ignore_index in config")
|
||||
|
||||
def eval(self, model, dataloader, logger, criterion, use_wandb, epoch, writer, result_path):
|
||||
device = "cuda:0"
|
||||
model.eval()
|
||||
ant_eval_pred_scores_dict = {}
|
||||
ant_gt_targets_dict = {}
|
||||
|
||||
with torch.no_grad():
|
||||
pred_scores, gt_targets, ant_pred_scores, ant_gt_targets = [], [], [], []
|
||||
start = time.time()
|
||||
anticipation_mAPs = []
|
||||
for vid_name, rgb_input, flow_input, target, ant_target in tqdm(dataloader, desc='Evaluation:', leave=False):
|
||||
rgb_input, flow_input, target, ant_target = rgb_input.to(device), flow_input.to(device), target.to(device), ant_target.to(device)
|
||||
|
||||
out_dict = model(rgb_input, flow_input)
|
||||
pred_logit = out_dict['logits']
|
||||
ant_pred_logit = out_dict['anticipation_logits']
|
||||
|
||||
prob_val = pred_logit.squeeze().cpu().numpy() # [8390, 3]
|
||||
target_batch = target.squeeze().cpu().numpy()
|
||||
ant_prob_val = ant_pred_logit.squeeze().cpu().numpy() # [8390, ANTICIPATION_LENGTH, 3]
|
||||
ant_target_batch = ant_target.squeeze().cpu().numpy()
|
||||
|
||||
pred_scores += list(prob_val)
|
||||
gt_targets += list(target_batch)
|
||||
ant_pred_scores += list(ant_prob_val)
|
||||
ant_gt_targets += list(ant_target_batch)
|
||||
if isinstance(vid_name, tuple):
|
||||
vid_name = vid_name[0]
|
||||
|
||||
ant_eval_pred_scores_dict[vid_name] = ant_prob_val
|
||||
ant_gt_targets_dict[vid_name] = ant_target_batch
|
||||
|
||||
end = time.time()
|
||||
num_frames = len(gt_targets)
|
||||
|
||||
result = self.eval_method(pred_scores, gt_targets, self.all_class_names, self.ignore_index, self.data_processing, self.metric)
|
||||
per_class_AP = result['per_class_AP']
|
||||
|
||||
ant_pred_scores = np.array(ant_pred_scores)
|
||||
ant_gt_targets = np.array(ant_gt_targets)
|
||||
logger.info(f'OAD mAP: {result["mean_AP"]*100:.2f}')
|
||||
# Prepare the formatted string
|
||||
formatted_output = "per_class_AP:\n" + "\n".join(f" {class_name:<25} : {ap_value:.4f}" for class_name, ap_value in per_class_AP.items())
|
||||
|
||||
# Log the formatted string
|
||||
logger.info(formatted_output)
|
||||
|
||||
if use_wandb == True:
|
||||
wandb.log({"OAD mAP": result["mean_AP"]})
|
||||
|
||||
anticipation_mAPs_per_class = {key: [] for key in per_class_AP.keys()}
|
||||
|
||||
for step in range(ant_gt_targets.shape[1]):
|
||||
result[f'anticipation_{step+1}'] = self.eval_method(ant_pred_scores[:,step,:], ant_gt_targets[:,step,:], self.all_class_names, self.ignore_index, self.data_processing, self.metric)
|
||||
anticipation_mAPs.append(result[f'anticipation_{step+1}']['mean_AP'])
|
||||
formatted_output = "per_class_AP:\n" + "\n".join(f" {class_name:<25} : {ap_value:.4f}" for class_name, ap_value in result[f'anticipation_{step+1}']['per_class_AP'].items())
|
||||
logger.info(f"Anticipation at step {step+1}: {result[f'anticipation_{step+1}']['mean_AP']*100:.2f}")
|
||||
logger.info(formatted_output)
|
||||
|
||||
for class_name in self.all_class_names:
|
||||
anticipation_mAPs_per_class[class_name].append(result[f'anticipation_{step+1}']['per_class_AP'][class_name])
|
||||
|
||||
if use_wandb == True:
|
||||
wandb.log({f"Anticipation at step {step+1}": result[f'anticipation_{step+1}']['mean_AP']})
|
||||
|
||||
logger.info(f'Mean Anticipation mAP: {np.mean(anticipation_mAPs)*100:.2f}')
|
||||
formatted_output = "Mean Anticipation per_class_AP:\n" + "\n".join(f" {class_name:<25} : {np.mean(anticipation_mAPs_per_class[class_name])*100:.4f}" for class_name in self.all_class_names)
|
||||
# logger.info(f'Mean Anticipation mAP per class: {np.mean(list(anticipation_mAPs_per_class.values()), axis=1)}')
|
||||
logger.info(formatted_output)
|
||||
|
||||
if use_wandb == True:
|
||||
wandb.log({"Mean Anticipation mAP": np.mean(anticipation_mAPs)})
|
||||
|
||||
time_taken = end - start
|
||||
logger.info(f'Processed {num_frames} frames in {time_taken:.1f} seconds ({num_frames / time_taken :.1f} FPS)')
|
||||
|
||||
return np.mean(anticipation_mAPs), np.mean(anticipation_mAPs), ant_eval_pred_scores_dict
|
||||
|
||||
# def forward(self, model, dataloader, logger):
|
||||
# return self.eval(model, dataloader, logger)
|
||||
def forward(self, model, dataloader, logger, criterion=None, use_wandb=False, epoch=None, writer=None, result_path=None):
|
||||
return self.eval(model, dataloader, logger, criterion, use_wandb, epoch, writer, result_path)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
__all__ = [
|
||||
'build_eval'
|
||||
]
|
||||
|
||||
from utils import Registry
|
||||
|
||||
EVAL = Registry()
|
||||
|
||||
def build_eval(cfg):
|
||||
eval = EVAL[cfg["task"]](cfg)
|
||||
return eval
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
import torch
|
||||
import wandb
|
||||
from tqdm import tqdm
|
||||
from trainer.train_builder import TRAINER
|
||||
|
||||
|
||||
@TRAINER.register("OAD")
|
||||
def train_one_epoch(trainloader, model, criterion, optimizer, scaler, epoch, use_wandb = False, writer=None, scheduler=None):
|
||||
epoch_loss = 0
|
||||
for it, (vid_name, rgb_input, flow_input, target) in enumerate(tqdm(trainloader, desc=f'Epoch:{epoch} Training', postfix=f'lr: {optimizer.param_groups[0]["lr"]:.7f}')):
|
||||
rgb_input, flow_input, target = rgb_input.cuda(), flow_input.cuda(), target.cuda()
|
||||
model.train()
|
||||
if scaler != None:
|
||||
with torch.cuda.amp.autocast():
|
||||
out_dict = model(rgb_input, flow_input)
|
||||
loss = criterion(out_dict, target)
|
||||
optimizer.zero_grad(set_to_none=True)
|
||||
scaler.scale(loss).backward()
|
||||
scaler.step(optimizer)
|
||||
scaler.update()
|
||||
# scaler is None as argparse by default
|
||||
else:
|
||||
out_dict = model(rgb_input, flow_input)
|
||||
loss = criterion(out_dict, target)
|
||||
optimizer.zero_grad(set_to_none=True)
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
|
||||
epoch_loss += loss.item()
|
||||
if use_wandb == True:
|
||||
wandb.log({"Train Loss": loss.item()})
|
||||
|
||||
if writer != None:
|
||||
writer.add_scalar("Train Loss", loss.item(), it+epoch*len(trainloader))
|
||||
return epoch_loss
|
||||
|
||||
@TRAINER.register("OADTR")
|
||||
def train_one_epoch_oadtr(trainloader, model, criterion, optimizer, scaler, epoch, use_wandb = False, writer=None, scheduler=None):
|
||||
epoch_loss = 0
|
||||
for it, (vid_name, rgb_input, flow_input, enc_target, distance_target, class_h_target, dec_target) in enumerate(tqdm(trainloader, desc=f'Epoch:{epoch} Training', postfix=f'lr: {optimizer.param_groups[0]["lr"]:.7f}')):
|
||||
rgb_input, flow_input, enc_target, distance_target, class_h_target = rgb_input.cuda(), flow_input.cuda(), enc_target.cuda(), distance_target.cuda(), class_h_target.cuda()
|
||||
model.train()
|
||||
if scaler != None:
|
||||
with torch.cuda.amp.autocast():
|
||||
out_dict = model(rgb_input, flow_input)
|
||||
loss = criterion(out_dict, enc_target, dec_target)
|
||||
optimizer.zero_grad(set_to_none=True)
|
||||
scaler.scale(loss).backward()
|
||||
scaler.step(optimizer)
|
||||
scaler.update()
|
||||
else:
|
||||
out_dict = model(rgb_input, flow_input)
|
||||
loss = criterion(out_dict, enc_target, dec_target)
|
||||
optimizer.zero_grad(set_to_none=True)
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
|
||||
epoch_loss += loss.item()
|
||||
if use_wandb == True:
|
||||
wandb.log({"Train Loss": loss.item()})
|
||||
|
||||
if writer != None:
|
||||
writer.add_scalar("Train Loss", loss.item(), it+epoch*len(trainloader))
|
||||
|
||||
return epoch_loss
|
||||
|
||||
|
||||
@TRAINER.register("ANTICIPATION")
|
||||
def ant_train_one_epoch(trainloader, model, criterion, optimizer, scaler, epoch, use_wandb = False, writer=None, scheduler=None):
|
||||
epoch_loss = 0
|
||||
for it, (vid_name, rgb_input, flow_input, target, ant_target) in enumerate(tqdm(trainloader, desc=f'Epoch:{epoch} Training', postfix=f'lr: {optimizer.param_groups[0]["lr"]:.7f}')):
|
||||
rgb_input, flow_input, target, ant_target = rgb_input.cuda(), flow_input.cuda(), target.cuda(), ant_target.cuda()
|
||||
model.train()
|
||||
if scaler != None:
|
||||
with torch.cuda.amp.autocast():
|
||||
out_dict = model(rgb_input, flow_input)
|
||||
loss = criterion(out_dict, target, ant_target)
|
||||
optimizer.zero_grad(set_to_none=True)
|
||||
scaler.scale(loss).backward()
|
||||
scaler.step(optimizer)
|
||||
scaler.update()
|
||||
else:
|
||||
out_dict = model(rgb_input, flow_input)
|
||||
loss = criterion(out_dict, target, ant_target)
|
||||
optimizer.zero_grad(set_to_none=True)
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
epoch_loss += loss.item()
|
||||
|
||||
if use_wandb == True:
|
||||
wandb.log({"Train Loss": loss.item()})
|
||||
|
||||
if writer != None:
|
||||
writer.add_scalar("Train Loss", loss.item(), it+epoch*len(trainloader))
|
||||
|
||||
return epoch_loss
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
__all__ = [
|
||||
'build_trainer'
|
||||
]
|
||||
|
||||
from utils import Registry
|
||||
|
||||
TRAINER = Registry()
|
||||
|
||||
def build_trainer(cfg):
|
||||
trainer = TRAINER[cfg["task"]]
|
||||
return trainer
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
from .metrics import perframe_average_precision, perstage_average_precision, event_accuracy
|
||||
from .postprocessing import thumos_postprocessing
|
||||
from .util import *
|
||||
from .group_transforms import *
|
||||
from .lr_scheduler import build_lr_scheduler
|
||||
from .logger import get_logger
|
||||
from .registry import Registry
|
||||
+497
@@ -0,0 +1,497 @@
|
||||
"""
|
||||
This file is inherited from tsn-pytorch
|
||||
"""
|
||||
|
||||
import torchvision
|
||||
import random
|
||||
from PIL import Image, ImageOps
|
||||
import numpy as np
|
||||
import numbers
|
||||
import math
|
||||
import torch
|
||||
from torchvision.transforms import functional as F
|
||||
|
||||
# lstr
|
||||
class Compose(object):
|
||||
|
||||
def __init__(self, transforms):
|
||||
self.transforms = transforms
|
||||
|
||||
def __call__(self, data):
|
||||
for t in self.transforms:
|
||||
data = t(data)
|
||||
return data
|
||||
|
||||
#LSTR
|
||||
class GroupCrop(object):
|
||||
"""perfrom group crop'"""
|
||||
|
||||
def __init__(self, crop_size, crop_pos='random'):
|
||||
super(GroupCrop, self).__init__()
|
||||
self.crop_size = crop_size
|
||||
self.crop_pos = crop_pos
|
||||
assert crop_pos in ['random', 'top_left', 'center', 'bottom_right'
|
||||
], f'crop_pos: {self.crop_pos} not implemented'
|
||||
|
||||
def __call__(self, img_group):
|
||||
"""
|
||||
Args:
|
||||
img_group (List(PIL.Image) or List(torch.Tensor)): a list of image to crop.
|
||||
Returns: The same type as input. cropped img_group.
|
||||
|
||||
"""
|
||||
crop_w, crop_h = self.crop_size
|
||||
if isinstance(img_group, torch.Tensor):
|
||||
assert img_group.dim(
|
||||
) == 4, f'input img_group should be dim-4(TCHW). Got shape: {img_group.shape}'
|
||||
img_h, img_w = img_group.shape[-2:]
|
||||
offset_h, offset_w = self.get_crop_offset(img_w, img_h, crop_w,
|
||||
crop_h)
|
||||
return F.crop(img_group, offset_h, offset_w, crop_h, crop_w)
|
||||
elif isinstance(img_group[0], Image):
|
||||
img_w, img_h = img_group[0].size
|
||||
offset_h, offset_w = self.get_crop_offset(img_w, img_h, crop_w,
|
||||
crop_h)
|
||||
return [
|
||||
F.crop(img, offset_h, offset_w, crop_h, crop_w)
|
||||
for img in img_group
|
||||
]
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f'img_group of type: {type(img_group)} is not supported')
|
||||
|
||||
def get_crop_offset(self, img_w, img_h, crop_w, crop_h):
|
||||
"""get the offset to crop.
|
||||
Returns: (offset_h, offset_w).
|
||||
"""
|
||||
if self.crop_pos == 'random':
|
||||
offset_h = int(torch.randint(img_h - crop_h, []))
|
||||
offset_w = int(torch.randint(img_w - crop_w, []))
|
||||
elif self.crop_pos == 'top_left':
|
||||
offset_h = offset_w = 0
|
||||
elif self.crop_pos == 'center':
|
||||
offset_h = int((img_h - crop_h) // 2)
|
||||
offset_w = int((img_w - crop_w) // 2)
|
||||
elif self.crop_pos == 'bottom_right':
|
||||
offset_h = img_h - crop_h
|
||||
offset_w = img_w - crop_w
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f'crop_pos: {self.crop_pos} not implemented')
|
||||
return (offset_h, offset_w)
|
||||
|
||||
class GroupRandomCrop(object):
|
||||
def __init__(self, size):
|
||||
if isinstance(size, numbers.Number):
|
||||
self.size = (int(size), int(size))
|
||||
else:
|
||||
self.size = size
|
||||
|
||||
def __call__(self, img_group):
|
||||
|
||||
w, h = img_group[0].size
|
||||
th, tw = self.size
|
||||
|
||||
out_images = list()
|
||||
|
||||
x1 = random.randint(0, w - tw)
|
||||
y1 = random.randint(0, h - th)
|
||||
|
||||
for img in img_group:
|
||||
assert(img.size[0] == w and img.size[1] == h)
|
||||
if w == tw and h == th:
|
||||
out_images.append(img)
|
||||
else:
|
||||
out_images.append(img.crop((x1, y1, x1 + tw, y1 + th)))
|
||||
|
||||
return out_images
|
||||
|
||||
|
||||
class GroupCenterCrop(object):
|
||||
def __init__(self, size):
|
||||
self.worker = torchvision.transforms.CenterCrop(size)
|
||||
|
||||
def __call__(self, img_group):
|
||||
return [self.worker(img) for img in img_group]
|
||||
|
||||
|
||||
class GroupRandomHorizontalFlip(object):
|
||||
"""Randomly horizontally flips the given PIL.Image with a probability of 0.5
|
||||
"""
|
||||
def __init__(self, is_flow=False):
|
||||
self.is_flow = is_flow
|
||||
|
||||
def __call__(self, img_group, is_flow=False):
|
||||
v = random.random()
|
||||
if v < 0.5:
|
||||
ret = [img.transpose(Image.FLIP_LEFT_RIGHT) for img in img_group]
|
||||
if self.is_flow:
|
||||
for i in range(0, len(ret), 2):
|
||||
ret[i] = ImageOps.invert(ret[i]) # invert flow pixel values when flipping
|
||||
return ret
|
||||
else:
|
||||
return img_group
|
||||
|
||||
|
||||
# class GroupNormalize(object):
|
||||
# def __init__(self, mean, std):
|
||||
# self.mean = mean
|
||||
# self.std = std
|
||||
|
||||
# def __call__(self, tensor):
|
||||
# rep_mean = self.mean * (tensor.size()[0]//len(self.mean))
|
||||
# rep_std = self.std * (tensor.size()[0]//len(self.std))
|
||||
|
||||
# # TODO: make efficient
|
||||
# for t, m, s in zip(tensor, rep_mean, rep_std):
|
||||
# t.sub_(m).div_(s)
|
||||
|
||||
# return tensor
|
||||
|
||||
# lstr
|
||||
class GroupNormalize(object):
|
||||
|
||||
def __init__(self, mean, std):
|
||||
|
||||
if len(mean) == 1: # flow
|
||||
assert len(
|
||||
std
|
||||
) == 1, f'flow input must have std of length 1. got {len(std)}'
|
||||
self.mean = mean[0]
|
||||
self.std = std[0]
|
||||
else:
|
||||
mean = np.array(mean, dtype=np.float32)
|
||||
self.mean = mean[np.newaxis, :, np.newaxis,
|
||||
np.newaxis] # shape: (C,) ->(T,C,H,W)
|
||||
std = np.array(std, dtype=np.float32)
|
||||
self.std = std[np.newaxis, :, np.newaxis,
|
||||
np.newaxis] # shape: (C,) ->(T,C,H,W)
|
||||
|
||||
def __call__(self, tensor):
|
||||
"""
|
||||
Args:
|
||||
tensor (torch.Tensor) : The stacked images. shape: (T, C, H, W).
|
||||
Returns: (torch.Tensor). sub mean and div std along the channel dim.
|
||||
|
||||
"""
|
||||
return (tensor - self.mean) / self.std
|
||||
|
||||
class GroupScale(object):
|
||||
""" Rescales the input PIL.Image to the given 'size'.
|
||||
'size' will be the size of the smaller edge.
|
||||
For example, if height > width, then image will be
|
||||
rescaled to (size * height / width, size)
|
||||
size: size of the smaller edge
|
||||
interpolation: Default: PIL.Image.BILINEAR
|
||||
"""
|
||||
|
||||
def __init__(self, size, interpolation=Image.BILINEAR):
|
||||
self.worker = torchvision.transforms.Resize(size, interpolation)
|
||||
|
||||
def __call__(self, img_group):
|
||||
return [self.worker(img) for img in img_group]
|
||||
|
||||
# lstr
|
||||
class GroupResize(object):
|
||||
"""perfrom group random resize"""
|
||||
|
||||
def __init__(self, scale_range, keep_aspect_ratio=True):
|
||||
"""
|
||||
Args:
|
||||
scale_range (tuple/list): The shorter side of the image will be resized to
|
||||
1) the range if `scale_range` is a tuple
|
||||
2) the specific size if `scale_range` contains only 1 element.
|
||||
"""
|
||||
super(GroupResize, self).__init__()
|
||||
self.scale_range = scale_range
|
||||
self.keep_aspect_ratio = keep_aspect_ratio
|
||||
|
||||
def __call__(self, img_group):
|
||||
"""
|
||||
Args:
|
||||
img_group (List(PIL.Image) or Tensor([T,C,H,W])): a list of image to resize.
|
||||
Returns: The same type as input. resized img_group.
|
||||
|
||||
"""
|
||||
# resize
|
||||
size = self.scale_range[0] if len(
|
||||
self.scale_range) == 1 else torch.randint(self.scale_range[0],
|
||||
self.scale_range[1], [])
|
||||
size = int(size)
|
||||
size = size if self.keep_aspect_ratio else (size, size)
|
||||
|
||||
if isinstance(img_group, torch.Tensor):
|
||||
assert img_group.dim(
|
||||
) == 4, f'input img_group should be dim-4(TCHW). Got shape: {img_group.shape}'
|
||||
return F.resize(img_group, size)
|
||||
elif isinstance(img_group[0], Image):
|
||||
return [F.resize(img, size) for img in img_group]
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f'img_group of type: {type(img_group)} is not supported')
|
||||
|
||||
class GroupOverSample(object):
|
||||
def __init__(self, crop_size, scale_size=None):
|
||||
self.crop_size = crop_size if not isinstance(crop_size, int) else (crop_size, crop_size)
|
||||
|
||||
if scale_size is not None:
|
||||
self.scale_worker = GroupScale(scale_size)
|
||||
else:
|
||||
self.scale_worker = None
|
||||
|
||||
def __call__(self, img_group):
|
||||
|
||||
if self.scale_worker is not None:
|
||||
img_group = self.scale_worker(img_group)
|
||||
image_w, image_h = img_group[0].size
|
||||
crop_w, crop_h = self.crop_size
|
||||
|
||||
offsets = GroupMultiScaleCrop.fill_fix_offset(False, image_w, image_h, crop_w, crop_h)
|
||||
oversample_group = list()
|
||||
for o_w, o_h in offsets:
|
||||
normal_group = list()
|
||||
flip_group = list()
|
||||
for i, img in enumerate(img_group):
|
||||
crop = img.crop((o_w, o_h, o_w + crop_w, o_h + crop_h))
|
||||
normal_group.append(crop)
|
||||
flip_crop = crop.copy().transpose(Image.FLIP_LEFT_RIGHT)
|
||||
|
||||
if img.mode == 'L' and i % 2 == 0:
|
||||
flip_group.append(ImageOps.invert(flip_crop))
|
||||
else:
|
||||
flip_group.append(flip_crop)
|
||||
|
||||
oversample_group.extend(normal_group)
|
||||
oversample_group.extend(flip_group)
|
||||
return oversample_group
|
||||
|
||||
|
||||
class GroupMultiScaleCrop(object):
|
||||
|
||||
def __init__(self, input_size, scales=None, max_distort=1, fix_crop=True, more_fix_crop=True):
|
||||
self.scales = scales if scales is not None else [1, 875, .75, .66]
|
||||
self.max_distort = max_distort
|
||||
self.fix_crop = fix_crop
|
||||
self.more_fix_crop = more_fix_crop
|
||||
self.input_size = input_size if not isinstance(input_size, int) else [input_size, input_size]
|
||||
self.interpolation = Image.BILINEAR
|
||||
|
||||
def __call__(self, img_group):
|
||||
|
||||
im_size = img_group[0].size
|
||||
|
||||
crop_w, crop_h, offset_w, offset_h = self._sample_crop_size(im_size)
|
||||
crop_img_group = [img.crop((offset_w, offset_h, offset_w + crop_w, offset_h + crop_h)) for img in img_group]
|
||||
ret_img_group = [img.resize((self.input_size[0], self.input_size[1]), self.interpolation)
|
||||
for img in crop_img_group]
|
||||
return ret_img_group
|
||||
|
||||
def _sample_crop_size(self, im_size):
|
||||
image_w, image_h = im_size[0], im_size[1]
|
||||
|
||||
# find a crop size
|
||||
base_size = min(image_w, image_h)
|
||||
crop_sizes = [int(base_size * x) for x in self.scales]
|
||||
crop_h = [self.input_size[1] if abs(x - self.input_size[1]) < 3 else x for x in crop_sizes]
|
||||
crop_w = [self.input_size[0] if abs(x - self.input_size[0]) < 3 else x for x in crop_sizes]
|
||||
|
||||
pairs = []
|
||||
for i, h in enumerate(crop_h):
|
||||
for j, w in enumerate(crop_w):
|
||||
if abs(i - j) <= self.max_distort:
|
||||
pairs.append((w, h))
|
||||
|
||||
crop_pair = random.choice(pairs)
|
||||
if not self.fix_crop:
|
||||
w_offset = random.randint(0, image_w - crop_pair[0])
|
||||
h_offset = random.randint(0, image_h - crop_pair[1])
|
||||
else:
|
||||
w_offset, h_offset = self._sample_fix_offset(image_w, image_h, crop_pair[0], crop_pair[1])
|
||||
|
||||
return crop_pair[0], crop_pair[1], w_offset, h_offset
|
||||
|
||||
def _sample_fix_offset(self, image_w, image_h, crop_w, crop_h):
|
||||
offsets = self.fill_fix_offset(self.more_fix_crop, image_w, image_h, crop_w, crop_h)
|
||||
return random.choice(offsets)
|
||||
|
||||
@staticmethod
|
||||
def fill_fix_offset(more_fix_crop, image_w, image_h, crop_w, crop_h):
|
||||
w_step = (image_w - crop_w) // 4
|
||||
h_step = (image_h - crop_h) // 4
|
||||
|
||||
ret = list()
|
||||
ret.append((0, 0)) # upper left
|
||||
ret.append((4 * w_step, 0)) # upper right
|
||||
ret.append((0, 4 * h_step)) # lower left
|
||||
ret.append((4 * w_step, 4 * h_step)) # lower right
|
||||
ret.append((2 * w_step, 2 * h_step)) # center
|
||||
|
||||
if more_fix_crop:
|
||||
ret.append((0, 2 * h_step)) # center left
|
||||
ret.append((4 * w_step, 2 * h_step)) # center right
|
||||
ret.append((2 * w_step, 4 * h_step)) # lower center
|
||||
ret.append((2 * w_step, 0 * h_step)) # upper center
|
||||
|
||||
ret.append((1 * w_step, 1 * h_step)) # upper left quarter
|
||||
ret.append((3 * w_step, 1 * h_step)) # upper right quarter
|
||||
ret.append((1 * w_step, 3 * h_step)) # lower left quarter
|
||||
ret.append((3 * w_step, 3 * h_step)) # lower righ quarter
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
class GroupRandomSizedCrop(object):
|
||||
"""Random crop the given PIL.Image to a random size of (0.08 to 1.0) of the original size
|
||||
and and a random aspect ratio of 3/4 to 4/3 of the original aspect ratio
|
||||
This is popularly used to train the Inception networks
|
||||
size: size of the smaller edge
|
||||
interpolation: Default: PIL.Image.BILINEAR
|
||||
"""
|
||||
def __init__(self, size, interpolation=Image.BILINEAR):
|
||||
self.size = size
|
||||
self.interpolation = interpolation
|
||||
|
||||
def __call__(self, img_group):
|
||||
for attempt in range(10):
|
||||
area = img_group[0].size(0) * img_group[0].size(1)
|
||||
target_area = random.uniform(0.08, 1.0) * area
|
||||
aspect_ratio = random.uniform(3. / 4, 4. / 3)
|
||||
|
||||
w = int(round(math.sqrt(target_area * aspect_ratio)))
|
||||
h = int(round(math.sqrt(target_area / aspect_ratio)))
|
||||
|
||||
if random.random() < 0.5:
|
||||
w, h = h, w
|
||||
|
||||
if w <= img_group[0].size(0) and h <= img_group[0].size(1):
|
||||
x1 = random.randint(0, img_group[0].size(0) - w)
|
||||
y1 = random.randint(0, img_group[0].size(1) - h)
|
||||
found = True
|
||||
break
|
||||
else:
|
||||
found = False
|
||||
x1 = 0
|
||||
y1 = 0
|
||||
|
||||
if found:
|
||||
out_group = list()
|
||||
for img in img_group:
|
||||
img = img.crop((x1, y1, x1 + w, y1 + h))
|
||||
assert(img.size == (w, h))
|
||||
out_group.append(img.resize((self.size, self.size), self.interpolation))
|
||||
return out_group
|
||||
else:
|
||||
# Fallback
|
||||
scale = GroupScale(self.size, interpolation=self.interpolation)
|
||||
crop = GroupRandomCrop(self.size)
|
||||
return crop(scale(img_group))
|
||||
|
||||
|
||||
class Stack(object):
|
||||
|
||||
def __init__(self, roll=False):
|
||||
self.roll = roll
|
||||
|
||||
def __call__(self, img_group):
|
||||
if img_group[0].mode == 'L':
|
||||
return np.concatenate([np.expand_dims(x, 2) for x in img_group], axis=2)
|
||||
elif img_group[0].mode == 'RGB':
|
||||
if self.roll:
|
||||
return np.concatenate([np.array(x)[:, :, ::-1] for x in img_group], axis=2)
|
||||
else:
|
||||
return np.concatenate(img_group, axis=2)
|
||||
|
||||
# LSTR
|
||||
class GroupStack(object):
|
||||
"""Stack to numpy array and convert THWC to TCHW"""
|
||||
|
||||
def __call__(self, img_group):
|
||||
"""
|
||||
Args:
|
||||
img_group (List(PIL.Image)): a list of image. image shape: (H,W,C) for rgb, (H,W) for grayscale
|
||||
Returns: np.ndarray. stack the images along the first axis.
|
||||
shape: (T, C, H, W).
|
||||
"""
|
||||
if isinstance(img_group, (list, tuple)):
|
||||
imgs = np.stack(img_group, axis=0)
|
||||
else:
|
||||
imgs = img_group
|
||||
if imgs.ndim == 3: # THW
|
||||
imgs = imgs[:, np.newaxis, :, :] # TCHW
|
||||
else: # THWC
|
||||
imgs = np.transpose(imgs, [0, 3, 1, 2]) # THWC -> TCHW
|
||||
return imgs
|
||||
|
||||
class ToTorchFormatTensor(object):
|
||||
""" Converts a PIL.Image (RGB) or numpy.ndarray (H x W x C) in the range [0, 255]
|
||||
to a torch.FloatTensor of shape (C x H x W) in the range [0.0, 1.0] """
|
||||
def __init__(self, div=True):
|
||||
self.div = div
|
||||
|
||||
def __call__(self, pic):
|
||||
if isinstance(pic, np.ndarray):
|
||||
# handle numpy array
|
||||
img = torch.from_numpy(pic).permute(2, 0, 1).contiguous()
|
||||
else:
|
||||
# handle PIL Image
|
||||
img = torch.ByteTensor(torch.ByteStorage.from_buffer(pic.tobytes()))
|
||||
img = img.view(pic.size[1], pic.size[0], len(pic.mode))
|
||||
# put it from HWC to CHW format
|
||||
# yikes, this transpose takes 80% of the loading time/CPU
|
||||
img = img.transpose(0, 1).transpose(0, 2).contiguous()
|
||||
return img.float().div(255) if self.div else img.float()
|
||||
|
||||
# LSTR
|
||||
class GroupToTensor(object):
|
||||
|
||||
def __init__(self, div=True):
|
||||
self.div = div
|
||||
|
||||
def __call__(self, imgs):
|
||||
"""
|
||||
Args:
|
||||
imgs (np.ndarray) : The stacked images. shape: (T, C, H, W). dtype: np.uint8
|
||||
Returns: (torch.Tensor). convert dtype to torch.float32.
|
||||
If `div` is True, the images are divided by 255 to be normalized to [0,1]
|
||||
|
||||
"""
|
||||
imgs = torch.from_numpy(imgs).float()
|
||||
if self.div:
|
||||
imgs = imgs.div(255.)
|
||||
return imgs
|
||||
|
||||
class IdentityTransform(object):
|
||||
|
||||
def __call__(self, data):
|
||||
return data
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
trans = torchvision.transforms.Compose([
|
||||
GroupScale(256),
|
||||
GroupRandomCrop(224),
|
||||
Stack(),
|
||||
ToTorchFormatTensor(),
|
||||
GroupNormalize(
|
||||
mean=[.485, .456, .406],
|
||||
std=[.229, .224, .225]
|
||||
)]
|
||||
)
|
||||
|
||||
im = Image.open('../tensorflow-model-zoo.torch/lena_299.png')
|
||||
|
||||
color_group = [im] * 3
|
||||
rst = trans(color_group)
|
||||
|
||||
gray_group = [im.convert('L')] * 9
|
||||
gray_rst = trans(gray_group)
|
||||
|
||||
trans2 = torchvision.transforms.Compose([
|
||||
GroupRandomSizedCrop(256),
|
||||
Stack(),
|
||||
ToTorchFormatTensor(),
|
||||
GroupNormalize(
|
||||
mean=[.485, .456, .406],
|
||||
std=[.229, .224, .225])
|
||||
])
|
||||
print(trans2(color_group))
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
def get_logger(output_path):
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
# formatter = logging.Formatter('%(asctime)s:%(message)s ', '%Y-%m-%d %H:%M:%S')
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(logging.INFO)
|
||||
# console_handler.setFormatter(formatter)
|
||||
logger.addHandler(console_handler)
|
||||
log_file = os.path.join(output_path, 'log.txt')
|
||||
fh = logging.FileHandler(log_file)
|
||||
fh.setLevel(logging.INFO)
|
||||
logger.addHandler(fh)
|
||||
|
||||
return logger
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
# code from detectron2
|
||||
|
||||
import math
|
||||
from typing import List
|
||||
import torch
|
||||
|
||||
try:
|
||||
from torch.optim.lr_scheduler import LRScheduler
|
||||
except ImportError:
|
||||
from torch.optim.lr_scheduler import _LRScheduler as LRScheduler
|
||||
|
||||
class WarmupCosineLR(LRScheduler):
|
||||
def __init__(
|
||||
self,
|
||||
optimizer: torch.optim.Optimizer,
|
||||
max_iters: int,
|
||||
warmup_factor: float = 0.001,
|
||||
warmup_iters: int = 1000,
|
||||
warmup_method: str = "linear",
|
||||
last_epoch: int = -1,
|
||||
):
|
||||
self.max_iters = max_iters
|
||||
self.warmup_factor = warmup_factor
|
||||
self.warmup_iters = warmup_iters
|
||||
self.warmup_method = warmup_method
|
||||
super().__init__(optimizer, last_epoch)
|
||||
|
||||
def get_lr(self) -> List[float]:
|
||||
warmup_factor = _get_warmup_factor_at_iter(
|
||||
self.warmup_method, self.last_epoch, self.warmup_iters, self.warmup_factor
|
||||
)
|
||||
# Different definitions of half-cosine with warmup are possible. For
|
||||
# simplicity we multiply the standard half-cosine schedule by the warmup
|
||||
# factor. An alternative is to start the period of the cosine at warmup_iters
|
||||
# instead of at 0. In the case that warmup_iters << max_iters the two are
|
||||
# very close to each other.
|
||||
return [
|
||||
base_lr
|
||||
* warmup_factor
|
||||
* 0.5
|
||||
* (1.0 + math.cos(math.pi * self.last_epoch / self.max_iters))
|
||||
for base_lr in self.base_lrs
|
||||
]
|
||||
|
||||
def _compute_values(self) -> List[float]:
|
||||
# The new interface
|
||||
return self.get_lr()
|
||||
|
||||
|
||||
def _get_warmup_factor_at_iter(
|
||||
method: str, iter: int, warmup_iters: int, warmup_factor: float
|
||||
) -> float:
|
||||
"""
|
||||
Return the learning rate warmup factor at a specific iteration.
|
||||
See :paper:`ImageNet in 1h` for more details.
|
||||
|
||||
Args:
|
||||
method (str): warmup method; either "constant" or "linear".
|
||||
iter (int): iteration at which to calculate the warmup factor.
|
||||
warmup_iters (int): the number of warmup iterations.
|
||||
warmup_factor (float): the base warmup factor (the meaning changes according
|
||||
to the method used).
|
||||
|
||||
Returns:
|
||||
float: the effective warmup factor at the given iteration.
|
||||
"""
|
||||
if iter >= warmup_iters:
|
||||
return 1.0
|
||||
|
||||
if method == "constant":
|
||||
return warmup_factor
|
||||
elif method == "linear":
|
||||
alpha = iter / warmup_iters
|
||||
return warmup_factor * (1 - alpha) + alpha
|
||||
else:
|
||||
raise ValueError("Unknown warmup method: {}".format(method))
|
||||
|
||||
def build_lr_scheduler(cfg, optimizer, iters_per_epoch):
|
||||
max_iters = cfg['num_epoch'] * iters_per_epoch
|
||||
warmup_iters = int(cfg['warmup_epoch'] * iters_per_epoch)
|
||||
# warmup_iters = int(max_iters * 1/6)
|
||||
scheduler = WarmupCosineLR(
|
||||
optimizer,
|
||||
max_iters=max_iters,
|
||||
warmup_factor=cfg['warmup_factor'],
|
||||
warmup_iters=warmup_iters,
|
||||
warmup_method=cfg['warmup_method'],
|
||||
last_epoch=-1)
|
||||
return scheduler
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
# Evaluation code from LSTR
|
||||
|
||||
from collections import OrderedDict
|
||||
from multiprocessing import Pool
|
||||
|
||||
import numpy as np
|
||||
from sklearn.metrics import average_precision_score
|
||||
|
||||
|
||||
def calibrated_average_precision_score(y_true, y_score):
|
||||
"""Compute calibrated average precision (cAP), which is particularly
|
||||
proposed for the TVSeries dataset.
|
||||
"""
|
||||
y_true_sorted = y_true[np.argsort(-y_score)]
|
||||
tp = y_true_sorted.astype(float)
|
||||
fp = np.abs(y_true_sorted.astype(float) - 1)
|
||||
tps = np.cumsum(tp)
|
||||
fps = np.cumsum(fp)
|
||||
ratio = np.sum(tp == 0) / np.sum(tp)
|
||||
cprec = tps / (tps + fps / (ratio + np.finfo(float).eps) + np.finfo(float).eps)
|
||||
cap = np.sum(cprec[tp == 1]) / np.sum(tp)
|
||||
return cap
|
||||
|
||||
def perframe_average_precision(prediction, ground_truth, class_names, ignore_index,
|
||||
postprocessing=None, metrics='AP'):
|
||||
"""Compute (frame-level) average precision between ground truth and
|
||||
predictions data frames.
|
||||
"""
|
||||
result = OrderedDict()
|
||||
ground_truth = np.array(ground_truth)
|
||||
prediction = np.array(prediction)
|
||||
|
||||
# Postprocessing
|
||||
if postprocessing is not None:
|
||||
ground_truth, prediction = postprocessing(ground_truth, prediction)
|
||||
|
||||
# Build metrics
|
||||
if metrics == 'AP':
|
||||
compute_score = average_precision_score
|
||||
elif metrics == 'cAP':
|
||||
# print('cAP')
|
||||
compute_score = calibrated_average_precision_score
|
||||
else:
|
||||
raise RuntimeError('Unknown metrics: {}'.format(metrics))
|
||||
|
||||
# Ignore backgroud class
|
||||
ignore_set = set([ignore_index])
|
||||
|
||||
# print('ignore_set = set([{ignore_index}])')
|
||||
|
||||
# Enable background according to ignore_index
|
||||
# ignore_set = set([ignore_index])
|
||||
|
||||
# Compute average precision
|
||||
result['per_class_AP'] = OrderedDict()
|
||||
result['num'] = OrderedDict()
|
||||
print(f"NUM FRAMES: {np.sum(ground_truth[:, 1:])}")
|
||||
for idx, class_name in enumerate(class_names):
|
||||
if idx not in ignore_set:
|
||||
# ground_truth.shape [2901376, 2]
|
||||
# prediction.shape [45334 ?]
|
||||
if np.any(ground_truth[:, idx]):
|
||||
ap_score = compute_score(ground_truth[:, idx], prediction[:, idx])
|
||||
result['per_class_AP'][class_name] = ap_score
|
||||
result['num'][class_name] = f'[true: {int(np.sum(ground_truth[:, idx]))}, pred:{int(np.sum(prediction[:,idx]))}, AP:{ap_score*100:.1f}]'
|
||||
result['mean_AP'] = np.mean(list(result['per_class_AP'].values()))
|
||||
print(f"Per_class_AP: {result['per_class_AP']}")
|
||||
return result
|
||||
|
||||
def event_accuracy(pred_scores_dict, gt_targets_dict):
|
||||
th_session_accuracies = {}
|
||||
|
||||
threshold = 0.5
|
||||
# ERROR: something is wrong
|
||||
# PLEASE EDIT:
|
||||
# pred_scores_dict here has wrong dimension
|
||||
# [64, 128, 10, 3] <- this is because I am using trainloader (do not evaluate on trainloader)
|
||||
for threshold in np.arange(0.1, 1.0, 0.2): # 0.1, 0.3, 0.5, 0.7, 0.9
|
||||
num_events = 0
|
||||
th_total_speaking_start_correct = 0
|
||||
th_total_speaking_end_correct = 0
|
||||
|
||||
for session in pred_scores_dict.keys():
|
||||
th_session_speaking_start_correct = 0
|
||||
th_session_speaking_start_predictions = 0
|
||||
|
||||
th_session_speaking_end_correct = 0
|
||||
th_session_speaking_end_predictions = 0
|
||||
|
||||
argmax_session_speaking_start_correct = 0
|
||||
argmax_session_speaking_end_correct = 0
|
||||
|
||||
pred_scores = pred_scores_dict[session]
|
||||
# pred_scores: [num_feature_frames, anticipation_steps, num_classes]
|
||||
# classes: background, target speaker speaking, other speaker speaking
|
||||
|
||||
# 12/06: To-do
|
||||
# I forgot whether miniroad AA step 0 means anticipate 1 step future or current
|
||||
# I did change something it would likely to be
|
||||
ant_length = pred_scores.shape[1]
|
||||
|
||||
gt_targets = gt_targets_dict[session]
|
||||
event_labels = np.load(f'/home/junhyeok/projects/turn-taking/datasets/EasyCom/target_perframe/events/{session}.npy')
|
||||
|
||||
num_speaking_start = 0
|
||||
num_speaking_end = 0
|
||||
print(f'{session} loaded, {len(event_labels)} events')
|
||||
# 12/06:
|
||||
# IndexError: index 8504 is out of bounds for axis 0 with size 8390
|
||||
# why the event_idx has much higher value than the length of pred_scores?
|
||||
# check how the event label is made
|
||||
|
||||
# event_label: [event_idx, event_class]
|
||||
# ['18', 'target speaker speaking start']
|
||||
for event_label in event_labels:
|
||||
event_idx, event_class = event_label
|
||||
event_idx = int(event_idx)
|
||||
|
||||
if event_class == 'target speaker speaking start':
|
||||
num_speaking_start += 1
|
||||
num_speaking_end += 1
|
||||
# right before the event start, want to see how the model predict?
|
||||
ant_prob = pred_scores[event_idx-1, 0, :]
|
||||
if np.argmax(ant_prob) == 1:
|
||||
argmax_session_speaking_start_correct += 1
|
||||
|
||||
ant_target_prob = pred_scores[event_idx-1, 0, 1]
|
||||
if ant_target_prob >= threshold:
|
||||
th_session_speaking_start_correct += 1
|
||||
|
||||
elif event_class == 'target speaker speaking end':
|
||||
# right after the event end, want to see how the model predict?
|
||||
ant_prob = pred_scores[event_idx, 0, :]
|
||||
if np.argmax(ant_prob) == 0:
|
||||
argmax_session_speaking_end_correct += 1
|
||||
|
||||
# target speaker will not speaker anymore
|
||||
ant_target_prob = pred_scores[event_idx, 0, 1]
|
||||
if ant_target_prob < threshold:
|
||||
th_session_speaking_end_correct += 1
|
||||
|
||||
print(f'Threshold {threshold:.2f}) {session} speaking start accuracy: {(th_session_speaking_start_correct / (len(event_labels) / 2)):.4f}')
|
||||
print(f'Threshold {threshold:.2f}) {session} speaking end accuracy: {(th_session_speaking_end_correct / (len(event_labels)/ 2 )):.4f}')
|
||||
print(f'argmax {session} speaking start accuracy: {argmax_session_speaking_start_correct / (len(event_labels) / 2)}')
|
||||
print(f'argmax {session} speaking end accuracy: {argmax_session_speaking_end_correct / (len(event_labels)/ 2 )}')
|
||||
th_session_accuracies[session] = [th_session_speaking_start_correct / len(event_labels), th_session_speaking_end_correct / len(event_labels)]
|
||||
th_total_speaking_start_correct += th_session_speaking_start_correct
|
||||
th_total_speaking_end_correct += th_session_speaking_end_correct
|
||||
num_events += len(event_labels)
|
||||
|
||||
th_total_speaking_start_accuracy = th_total_speaking_start_correct / len(event_labels)
|
||||
th_total_speaking_end_accuracy = th_total_speaking_end_correct / len(event_labels)
|
||||
|
||||
th_total_accuracy = (th_total_speaking_start_correct + th_total_speaking_end_correct) / (num_events)
|
||||
print(f'total speaking start accuracy: {th_total_accuracy}')
|
||||
|
||||
return th_total_accuracy
|
||||
|
||||
def get_stage_pred_scores(gt_targets, pred_scores, perc_s, perc_e):
|
||||
starts = []
|
||||
ends = []
|
||||
stage_gt_targets = []
|
||||
stage_pred_scores = []
|
||||
for i in range(len(gt_targets)):
|
||||
if gt_targets[i] == 0:
|
||||
stage_gt_targets.append(gt_targets[i])
|
||||
stage_pred_scores.append(pred_scores[i])
|
||||
else:
|
||||
if i == 0 or gt_targets[i - 1] == 0:
|
||||
starts.append(i)
|
||||
if i == len(gt_targets) - 1 or gt_targets[i + 1] == 0:
|
||||
ends.append(i)
|
||||
if len(starts) != len(ends):
|
||||
raise ValueError('starts and ends cannot pair!')
|
||||
|
||||
action_lens = [ends[i] - starts[i] for i in range(len(starts))]
|
||||
stage_starts = [starts[i] + int(action_lens[i] * perc_s) for i in range(len(starts))]
|
||||
stage_ends = [max(stage_starts[i] + 1, starts[i] + int(action_lens[i] * perc_e)) for i in range(len(starts))]
|
||||
for i in range(len(starts)):
|
||||
stage_gt_targets.extend(gt_targets[stage_starts[i]: stage_ends[i]])
|
||||
stage_pred_scores.extend(pred_scores[stage_starts[i]: stage_ends[i]])
|
||||
return np.array(stage_gt_targets), np.array(stage_pred_scores)
|
||||
|
||||
|
||||
def perstage_average_precision(prediction, ground_truth,
|
||||
class_names, postprocessing,
|
||||
metrics='cAP'):
|
||||
result = OrderedDict()
|
||||
ground_truth = np.array(ground_truth)
|
||||
prediction = np.array(prediction)
|
||||
|
||||
# Postprocessing
|
||||
if postprocessing is not None:
|
||||
ground_truth, prediction = postprocessing(ground_truth, prediction)
|
||||
|
||||
# Build metrics
|
||||
if metrics == 'AP':
|
||||
compute_score = average_precision_score
|
||||
elif metrics == 'cAP':
|
||||
compute_score = calibrated_average_precision_score
|
||||
else:
|
||||
raise RuntimeError('Unknown metrics: {}'.format(metrics))
|
||||
|
||||
raise NotImplementedError('This function is not implemented yet.')
|
||||
# Ignore backgroud class
|
||||
# ignore_index = set([0])
|
||||
|
||||
# Compute average precision
|
||||
for perc_s in range(10):
|
||||
perc_e = perc_s + 1
|
||||
stage_name = '{:2}%_{:3}%'.format(perc_s * 10, perc_e * 10)
|
||||
result[stage_name] = OrderedDict({'per_class_AP': OrderedDict()})
|
||||
for idx, class_name in enumerate(class_names):
|
||||
if idx not in ignore_index:
|
||||
stage_gt_targets, stage_pred_scores = get_stage_pred_scores(
|
||||
(ground_truth[:, idx] == 1).astype(int),
|
||||
prediction[:, idx],
|
||||
perc_s / 10,
|
||||
perc_e / 10,
|
||||
)
|
||||
result[stage_name]['per_class_AP'][class_name] = \
|
||||
compute_score(stage_gt_targets, stage_pred_scores)
|
||||
result[stage_name]['mean_AP'] = \
|
||||
np.mean(list(result[stage_name]['per_class_AP'].values()))
|
||||
|
||||
return result
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
|
||||
import numpy as np
|
||||
|
||||
def thumos_postprocessing(ground_truth, prediction, smooth=False, switch=False):
|
||||
"""
|
||||
We follow (Shou et al., 2017) and adopt their perframe postprocessing method on THUMOS'14 datset.
|
||||
Source: https://bitbucket.org/columbiadvmm/cdc/src/master/THUMOS14/eval/PreFrameLabeling/compute_framelevel_mAP.m
|
||||
"""
|
||||
|
||||
# Simple temporal smoothing via NMS of 5-frames window
|
||||
if smooth:
|
||||
prob = np.copy(prediction)
|
||||
prob1 = prob.reshape(1, prob.shape[0], prob.shape[1])
|
||||
prob2 = np.append(prob[0, :].reshape(1, -1), prob[0: -1, :], axis=0).reshape(1, prob.shape[0], prob.shape[1])
|
||||
prob3 = np.append(prob[1:, :], prob[-1, :].reshape(1, -1), axis=0).reshape(1, prob.shape[0], prob.shape[1])
|
||||
prob4 = np.append(prob[0: 2, :], prob[0: -2, :], axis=0).reshape(1, prob.shape[0], prob.shape[1])
|
||||
prob5 = np.append(prob[2:, :], prob[-2:, :], axis=0).reshape(1, prob.shape[0], prob.shape[1])
|
||||
probsmooth = np.squeeze(np.max(np.concatenate((prob1, prob2, prob3, prob4, prob5), axis=0), axis=0))
|
||||
prediction = np.copy(probsmooth)
|
||||
|
||||
# Assign cliff diving (5) as diving (8)
|
||||
if switch:
|
||||
switch_index = np.where(prediction[:, 5] > prediction[:, 8])[0]
|
||||
prediction[switch_index, 8] = prediction[switch_index, 5]
|
||||
|
||||
# Remove ambiguous (21)
|
||||
valid_index = np.where(ground_truth[:, 21] != 1)[0]
|
||||
|
||||
return ground_truth[valid_index], prediction[valid_index]
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
def _register_generic(module_dict, module_name, module):
|
||||
assert module_name not in module_dict
|
||||
module_dict[module_name] = module
|
||||
|
||||
|
||||
class Registry(dict):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Registry, self).__init__(*args, **kwargs)
|
||||
|
||||
def register(self, module_name, module=None):
|
||||
if module is not None:
|
||||
_register_generic(self, module_name, module)
|
||||
return
|
||||
|
||||
def register_fn(fn):
|
||||
_register_generic(self, module_name, fn)
|
||||
return fn
|
||||
|
||||
return register_fn
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
import pickle
|
||||
import os
|
||||
import os.path as osp
|
||||
import random
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
def dump_pickle(lst, file_path, file_name):
|
||||
with open(osp.join(file_path, file_name + '.pkl'), 'wb') as f:
|
||||
pickle.dump(lst, f)
|
||||
|
||||
def create_dir(dir_path):
|
||||
if not osp.exists(dir_path):
|
||||
os.makedirs(dir_path)
|
||||
|
||||
def create_outdir(result_path, overwrite=False):
|
||||
i = 1
|
||||
new_result_path = result_path
|
||||
|
||||
if overwrite == False:
|
||||
while osp.exists(new_result_path):
|
||||
new_result_path = f'{result_path}_{i}'
|
||||
i += 1
|
||||
|
||||
create_dir(osp.join(new_result_path, 'ckpts'))
|
||||
create_dir(osp.join(new_result_path, 'runs'))
|
||||
return new_result_path
|
||||
|
||||
def set_seed(seed):
|
||||
# os.environ['PYTHONHASHSEED'] = str(seed)
|
||||
random.seed(seed)
|
||||
np.random.seed(seed)
|
||||
torch.manual_seed(seed)
|
||||
if torch.cuda.is_available():
|
||||
torch.cuda.manual_seed(seed)
|
||||
torch.cuda.manual_seed_all(seed)
|
||||
torch.backends.cudnn.benchmark = False
|
||||
torch.backends.cudnn.deterministic = True
|
||||
|
||||
def parse_rgb_type(rgb_type):
|
||||
if "rgb_kinetics_resnet50" in rgb_type:
|
||||
rgb_type = "rgb_kinetics_resnet50"
|
||||
elif "rgb_farl" in rgb_type:
|
||||
rgb_type = "rgb_farl"
|
||||
|
||||
return rgb_type
|
||||
|
||||
def parse_flow_type(flow_type):
|
||||
if "audio_wav2vec2" in flow_type:
|
||||
flow_type = "audio_wav2vec2"
|
||||
|
||||
return flow_type
|
||||
|
||||
def get_identifier(cfg):
|
||||
mode = "eval" if cfg["eval"] == True else "train"
|
||||
|
||||
modality = ''
|
||||
if not cfg["no_flow"]:
|
||||
modality += 'A'
|
||||
if not cfg["no_rgb"]:
|
||||
modality += 'V'
|
||||
|
||||
identifier = f'{cfg["model"]}_{cfg["data_name"]}'
|
||||
if 'A' in modality:
|
||||
identifier += f"_{cfg['flow_type']}"
|
||||
if 'V' in modality:
|
||||
identifier += f"_{cfg['rgb_type']}"
|
||||
|
||||
identifier += f'_{modality}_epoch{cfg["num_epoch"]}_{mode}'
|
||||
if cfg["ckpt"] != None:
|
||||
identifier += f'_ckpt_{".".join(osp.basename(cfg["ckpt"]).split(".")[:-1])}'
|
||||
|
||||
return identifier
|
||||
|
||||
Reference in New Issue
Block a user