BonanDing commited on
Commit
3df329c
·
1 Parent(s): d15d8f2

Add DeMemWM segment-aware temporal mask

Browse files
algorithms/dememwm/models/attention.py CHANGED
@@ -44,7 +44,56 @@ class TemporalAxialAttention(nn.Module):
44
 
45
  self.reference_length = reference_length
46
 
47
- def forward(self, x: torch.Tensor):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  B, T, H, W, D = x.shape
49
 
50
  # if T>=9:
@@ -75,7 +124,18 @@ class TemporalAxialAttention(nn.Module):
75
 
76
  q, k, v = map(lambda t: t.contiguous(), (q, k, v))
77
 
78
- if self.is_temporal_independent:
 
 
 
 
 
 
 
 
 
 
 
79
  attn_bias = torch.ones((T, T), dtype=q.dtype, device=q.device)
80
  attn_bias = attn_bias.masked_fill(attn_bias == 1, float('-inf'))
81
  attn_bias[range(T), range(T)] = 0
 
44
 
45
  self.reference_length = reference_length
46
 
47
+ def _frame_memory_attn_bias(
48
+ self,
49
+ B,
50
+ T,
51
+ H,
52
+ W,
53
+ dtype,
54
+ device,
55
+ frame_memory_segments,
56
+ frame_memory_masks,
57
+ ):
58
+ allow = torch.zeros((B, T, T), dtype=torch.bool, device=device)
59
+
60
+ target_frames = frame_memory_segments["target"] if frame_memory_segments else T
61
+ cursor = 0
62
+ target_slice = slice(cursor, cursor + target_frames)
63
+ target_idx = torch.arange(target_frames, device=device)
64
+ allow[:, target_slice, target_slice] = target_idx[:, None] >= target_idx[None, :]
65
+ cursor += target_frames
66
+
67
+ segment_slices = {"target": target_slice}
68
+ for segment in ("anchor", "dynamic", "revisit"):
69
+ length = frame_memory_segments.get(segment, 0)
70
+ segment_slice = slice(cursor, cursor + length)
71
+ segment_slices[segment] = segment_slice
72
+ if length > 0:
73
+ if segment == "dynamic":
74
+ allow[:, segment_slice, segment_slice] = True
75
+ else:
76
+ idx = torch.arange(cursor, cursor + length, device=device)
77
+ allow[:, idx, idx] = True
78
+ cursor += length
79
+
80
+ if frame_memory_masks is not None:
81
+ valid = torch.ones((B, T), dtype=torch.bool, device=device)
82
+ for segment, segment_slice in segment_slices.items():
83
+ mask = frame_memory_masks.get(segment)
84
+ if mask is not None:
85
+ valid[:, segment_slice] = mask.to(device=device, dtype=torch.bool)
86
+ allow = allow & valid[:, :, None] & valid[:, None, :]
87
+
88
+ # Invalid padded rows keep only self-attention finite so SDPA never sees
89
+ # an all -inf query row.
90
+ diag_idx = torch.arange(T, device=device)
91
+ allow[:, diag_idx, diag_idx] = True
92
+ attn_bias = torch.zeros((B, T, T), dtype=dtype, device=device)
93
+ attn_bias = attn_bias.masked_fill(~allow, float("-inf"))
94
+ return attn_bias.repeat_interleave(H * W, dim=0)[:, None]
95
+
96
+ def forward(self, x: torch.Tensor, frame_memory_segments=None, frame_memory_masks=None):
97
  B, T, H, W, D = x.shape
98
 
99
  # if T>=9:
 
124
 
125
  q, k, v = map(lambda t: t.contiguous(), (q, k, v))
126
 
127
+ if frame_memory_segments is not None:
128
+ attn_bias = self._frame_memory_attn_bias(
129
+ B,
130
+ T,
131
+ H,
132
+ W,
133
+ q.dtype,
134
+ q.device,
135
+ frame_memory_segments,
136
+ frame_memory_masks,
137
+ )
138
+ elif self.is_temporal_independent:
139
  attn_bias = torch.ones((T, T), dtype=q.dtype, device=q.device)
140
  attn_bias = attn_bias.masked_fill(attn_bias == 1, float('-inf'))
141
  attn_bias[range(T), range(T)] = 0
algorithms/dememwm/models/dit.py CHANGED
@@ -241,7 +241,8 @@ class SpatioTemporalDiTBlock(nn.Module):
241
  self.parallel_map = nn.Linear(hidden_size, hidden_size)
242
 
243
  def forward(self, x, c, current_frame=None, timestep=None, is_last_block=False,
244
- pose_cond=None, mode="training", c_action_cond=None, reference_length=None):
 
245
  B, T, H, W, D = x.shape
246
 
247
  # spatial block
@@ -256,7 +257,11 @@ class SpatioTemporalDiTBlock(nn.Module):
256
  else:
257
  t_shift_msa, t_scale_msa, t_gate_msa, t_shift_mlp, t_scale_mlp, t_gate_mlp = self.t_adaLN_modulation(c).chunk(6, dim=-1)
258
 
259
- x_t = x + gate(self.t_attn(modulate(self.t_norm1(x), t_shift_msa, t_scale_msa)), t_gate_msa)
 
 
 
 
260
  x_t = x_t + gate(self.t_mlp(modulate(self.t_norm2(x_t), t_shift_mlp, t_scale_mlp)), t_gate_mlp)
261
 
262
  if self.ref_mode == 'sequential':
@@ -540,7 +545,9 @@ class DiT(nn.Module):
540
 
541
  for i, block in enumerate(self.blocks):
542
  x = block(x, c, current_frame=current_frame, timestep=t, is_last_block= (i+1 == len(self.blocks)),
543
- pose_cond=pc, mode=mode, c_action_cond=c_action_cond, reference_length=reference_length) # (N, T, H, W, D)
 
 
544
  x = self.final_layer(x, c) # (N, T, H, W, patch_size ** 2 * out_channels)
545
  # unpatchify
546
  x = rearrange(x, "b t h w d -> (b t) h w d")
 
241
  self.parallel_map = nn.Linear(hidden_size, hidden_size)
242
 
243
  def forward(self, x, c, current_frame=None, timestep=None, is_last_block=False,
244
+ pose_cond=None, mode="training", c_action_cond=None, reference_length=None,
245
+ frame_memory_segments=None, frame_memory_masks=None):
246
  B, T, H, W, D = x.shape
247
 
248
  # spatial block
 
257
  else:
258
  t_shift_msa, t_scale_msa, t_gate_msa, t_shift_mlp, t_scale_mlp, t_gate_mlp = self.t_adaLN_modulation(c).chunk(6, dim=-1)
259
 
260
+ x_t = x + gate(self.t_attn(
261
+ modulate(self.t_norm1(x), t_shift_msa, t_scale_msa),
262
+ frame_memory_segments=frame_memory_segments,
263
+ frame_memory_masks=frame_memory_masks,
264
+ ), t_gate_msa)
265
  x_t = x_t + gate(self.t_mlp(modulate(self.t_norm2(x_t), t_shift_mlp, t_scale_mlp)), t_gate_mlp)
266
 
267
  if self.ref_mode == 'sequential':
 
545
 
546
  for i, block in enumerate(self.blocks):
547
  x = block(x, c, current_frame=current_frame, timestep=t, is_last_block= (i+1 == len(self.blocks)),
548
+ pose_cond=pc, mode=mode, c_action_cond=c_action_cond, reference_length=reference_length,
549
+ frame_memory_segments=frame_memory_segments,
550
+ frame_memory_masks=frame_memory_masks) # (N, T, H, W, D)
551
  x = self.final_layer(x, c) # (N, T, H, W, patch_size ** 2 * out_channels)
552
  # unpatchify
553
  x = rearrange(x, "b t h w d -> (b t) h w d")
tests/test_dememwm_temporal_attention.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+
3
+ import torch
4
+ from torch import nn
5
+
6
+ from algorithms.dememwm.models.attention import TemporalAxialAttention
7
+ from algorithms.dememwm.models.dit import DiT, SpatioTemporalDiTBlock
8
+
9
+
10
+ class IdentityRotary:
11
+ freqs = None
12
+
13
+ def rotate_queries_or_keys(self, x, freqs):
14
+ return x
15
+
16
+
17
+ def _averaging_temporal_attention(reference_length=0):
18
+ attn = TemporalAxialAttention(
19
+ dim=1,
20
+ heads=1,
21
+ dim_head=1,
22
+ reference_length=reference_length,
23
+ rotary_emb=IdentityRotary(),
24
+ )
25
+ with torch.no_grad():
26
+ attn.to_qkv.weight.zero_()
27
+ attn.to_qkv.weight[2, 0] = 1.0
28
+ attn.to_out.weight.fill_(1.0)
29
+ attn.to_out.bias.zero_()
30
+ return attn
31
+
32
+
33
+ class DeMemWMTemporalAttentionTests(unittest.TestCase):
34
+ def test_without_frame_memory_keeps_causal_reference_mask(self):
35
+ attn = _averaging_temporal_attention(reference_length=2)
36
+ x = torch.tensor([10.0, 20.0, 30.0, 100.0, 200.0]).view(1, 5, 1, 1, 1)
37
+
38
+ out = attn(x)
39
+
40
+ expected = torch.tensor([10.0, 15.0, 20.0, 100.0, 200.0])
41
+ self.assertTrue(torch.allclose(out.flatten(), expected))
42
+
43
+ def test_frame_memory_segments_mask_temporal_streams(self):
44
+ attn = _averaging_temporal_attention()
45
+ x = torch.tensor([10.0, 20.0, 30.0, 100.0, 1.0, 3.0, 200.0]).view(1, 7, 1, 1, 1)
46
+ segments = {"target": 3, "anchor": 1, "dynamic": 2, "revisit": 1}
47
+
48
+ out = attn(x, frame_memory_segments=segments)
49
+
50
+ expected = torch.tensor([10.0, 15.0, 20.0, 100.0, 2.0, 2.0, 200.0])
51
+ self.assertTrue(torch.allclose(out.flatten(), expected))
52
+
53
+ masks = {
54
+ "target": torch.ones((1, 3), dtype=torch.bool),
55
+ "anchor": torch.tensor([[False]]),
56
+ "dynamic": torch.tensor([[True, False]]),
57
+ "revisit": torch.ones((1, 1), dtype=torch.bool),
58
+ }
59
+ out = attn(x, frame_memory_segments=segments, frame_memory_masks=masks)
60
+
61
+ expected = torch.tensor([10.0, 15.0, 20.0, 100.0, 1.0, 3.0, 200.0])
62
+ self.assertTrue(torch.allclose(out.flatten(), expected))
63
+
64
+ def test_block_threads_frame_memory_metadata_to_temporal_attention(self):
65
+ class ZeroAttention(nn.Module):
66
+ def forward(self, x):
67
+ return torch.zeros_like(x)
68
+
69
+ class SpyTemporalAttention(nn.Module):
70
+ def __init__(self):
71
+ super().__init__()
72
+ self.calls = []
73
+
74
+ def forward(self, x, frame_memory_segments=None, frame_memory_masks=None):
75
+ self.calls.append((frame_memory_segments, frame_memory_masks))
76
+ return torch.zeros_like(x)
77
+
78
+ block = SpatioTemporalDiTBlock(
79
+ hidden_size=4,
80
+ num_heads=1,
81
+ reference_length=0,
82
+ spatial_rotary_emb=None,
83
+ temporal_rotary_emb=None,
84
+ )
85
+ spy = SpyTemporalAttention()
86
+ block.s_attn = ZeroAttention()
87
+ block.t_attn = spy
88
+ x = torch.zeros((1, 5, 1, 1, 4))
89
+ c = torch.zeros((1, 5, 4))
90
+ segments = {"target": 2, "anchor": 1, "dynamic": 1, "revisit": 1}
91
+ masks = {"dynamic": torch.ones((1, 1), dtype=torch.bool)}
92
+
93
+ block(x, c, frame_memory_segments=segments, frame_memory_masks=masks)
94
+
95
+ self.assertIs(spy.calls[0][0], segments)
96
+ self.assertIs(spy.calls[0][1], masks)
97
+
98
+ def test_dit_threads_frame_memory_metadata_to_blocks(self):
99
+ class SpyBlock(nn.Module):
100
+ def __init__(self):
101
+ super().__init__()
102
+ self.calls = []
103
+
104
+ def forward(self, x, c, **kwargs):
105
+ self.calls.append(kwargs)
106
+ return x
107
+
108
+ model = DiT(
109
+ input_h=2,
110
+ input_w=2,
111
+ patch_size=1,
112
+ in_channels=1,
113
+ hidden_size=8,
114
+ depth=1,
115
+ num_heads=1,
116
+ mlp_ratio=1.0,
117
+ action_cond_dim=3,
118
+ pose_cond_dim=0,
119
+ reference_length=0,
120
+ use_memory_attention=False,
121
+ )
122
+ spy = SpyBlock()
123
+ model.blocks[0] = spy
124
+ model.eval()
125
+ x = torch.zeros((1, 5, 1, 2, 2))
126
+ t = torch.zeros((1, 5), dtype=torch.long)
127
+ action_cond = torch.zeros((1, 5, 3))
128
+ segments = {"target": 2, "anchor": 1, "dynamic": 1, "revisit": 1}
129
+ masks = {"dynamic": torch.ones((1, 1), dtype=torch.bool)}
130
+
131
+ with torch.no_grad():
132
+ model(x, t, action_cond, frame_memory_segments=segments, frame_memory_masks=masks)
133
+
134
+ self.assertIs(spy.calls[0]["frame_memory_segments"], segments)
135
+ self.assertIs(spy.calls[0]["frame_memory_masks"], masks)
136
+
137
+
138
+ if __name__ == "__main__":
139
+ unittest.main()