-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathagl.py
More file actions
153 lines (122 loc) · 5.66 KB
/
Copy pathagl.py
File metadata and controls
153 lines (122 loc) · 5.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
from decoder import MultiheadAttention, SimpleTransformer, Cross_MultiheadAttention
import torch
import torch.nn as nn
from functools import partial
import numpy as np
class AGL(nn.Module):
def __init__(self, embed_dim: int = 1024, num_blocks: int = 8, num_heads: int = 8, max_temporal_length: int = 265, threshold: int = 0.6):
super().__init__()
self.threshold = nn.Parameter(torch.tensor(0.5), requires_grad=True)
# [b,l,128] -> [b,l,1024]
self.audio_projector = nn.Sequential(
nn.Linear(128, int(embed_dim/2)),
nn.ReLU(),
nn.Linear(int(embed_dim/2), embed_dim)
)
self.video_projector = nn.Sequential(
nn.Linear(1024, int(embed_dim/2)),
nn.ReLU(),
nn.Linear(int(embed_dim/2), embed_dim)
)
self.ca = SimpleTransformer(
embed_dim=embed_dim,
num_blocks=num_blocks,
ffn_dropout_rate=0.0,
drop_path_rate=0.1,
attn_target=partial(
Cross_MultiheadAttention,
embed_dim=embed_dim,
num_heads=num_heads,
bias=True,
add_bias_kv=True,
),
pre_transformer_layer=nn.Sequential(
nn.LayerNorm(embed_dim, eps=1e-6),
nn.Identity(),
#EinOpsRearrange("b l d -> l b d"),
),
#post_transformer_layer=EinOpsRearrange("l b d -> b d l"),
)
#
self.locate_head = nn.Sequential(
nn.Linear(embed_dim,int(embed_dim/2)),
nn.ReLU(),
nn.Linear(int(embed_dim/2), 1),
nn.Sigmoid()
)
self.adaptive_pool = nn.AdaptiveAvgPool1d(1)
def forward(self, video_features: torch.Tensor = None, audio_features: torch.Tensor = None):
#assert video_features.shape[1] == audio_features.shape[1]
#cut longer features to the same length
gap = video_features.shape[1] - audio_features.shape[1]
if np.abs(gap) > 10:
pass
#print(f"warning: VA features has {gap} frame gaps")
if video_features.shape[1] > audio_features.shape[1]:
video_features = video_features[:, :audio_features.shape[1], :]
elif video_features.shape[1] < audio_features.shape[1]:
audio_features = audio_features[:, :video_features.shape[1], :]
audio_features = self.audio_projector(audio_features)
video_features = self.video_projector(video_features)
hidden_states = self.ca([audio_features,video_features])
located = self.locate_head(hidden_states).squeeze(-1)
#segments = self.get_segments(located.reshape(-1).detach().cpu().numpy())
clips = (located > self.threshold).type(torch.int)
# from the [0,0,0,1,1,1,0,0,0] to get three segments token [3,1024] after adaptive_pooling
segments = []
current_segment = []
current_type = clips[0, 0].item()
for i in range(clips.shape[1]):
if clips[0, i] == current_type:
current_segment.append(i)
else:
if current_segment:
segments.append((current_type, current_segment))
current_segment = [i]
current_type = clips[0, i].item()
if current_segment:
segments.append((current_type, current_segment))
pooled_segments = []
segment_types = []
for segment_type, segment in segments:
video_segment_features = video_features[:, segment, :]
audio_segment_features = audio_features[:, segment, :]
video_pooled_segment = self.adaptive_pool(video_segment_features.permute(0, 2, 1)).reshape(-1)
audio_pooled_segment = self.adaptive_pool(audio_segment_features.permute(0, 2, 1)).reshape(-1)
pooled_segments.append((video_pooled_segment, audio_pooled_segment))
segment_types.append(segment_type)
stacked_video_segments = torch.stack([seg[0] for seg in pooled_segments]).unsqueeze(0)
stacked_audio_segments = torch.stack([seg[1] for seg in pooled_segments]).unsqueeze(0)
return stacked_video_segments, stacked_audio_segments, located, segments, segment_types
if __name__ == "__main__":
import torch.optim as optim
# Initialize the model
model = AGL(embed_dim=1024, num_blocks=8, num_heads=8, max_temporal_length=265)
# Create dummy input data
batch_size = 1
seq_length = 16
video_dim = 1024
audio_dim = 128
video_features = torch.randn(batch_size, seq_length, video_dim)
audio_features = torch.randn(batch_size, seq_length, audio_dim)
# Define the target label (binary for BCELoss)
target = torch.ones(batch_size, seq_length) # Example target, use appropriate labels
# Define loss criterion
criterion = nn.BCELoss()
# Forward pass
vs,aps,l,s,st = model(video_featuers=video_features, audio_features=audio_features)
# threshold the output to get binary predictions
res = (l > 0.5).float()
# Compute loss
loss = criterion(l, target)
# Print the loss for verification
print(f"Loss before backward: {loss.item()}")
# Backward pass
loss.backward()
# Check gradients (optionally you can also perform an optimizer step to check updates)
optimizer = optim.SGD(model.parameters(), lr=0.01)
optimizer.step()
# Print some gradients or updated parameters for verification
for name, param in model.named_parameters():
if param.grad is not None:
print(f"Grad for {name}: {param.grad.norm()}") # Just an example to show gradient