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

Add DeMemWM frame memory reference attention

Browse files
algorithms/dememwm/models/dit.py CHANGED
@@ -8,6 +8,7 @@ References:
8
  from typing import Optional, Literal
9
  import torch
10
  from torch import nn
 
11
  from .rotary_embedding_torch import RotaryEmbedding
12
  from einops import rearrange
13
  from .attention import SpatialAxialAttention, TemporalAxialAttention, MemTemporalAxialAttention, MemFullAttention
@@ -140,6 +141,64 @@ class FinalLayer(nn.Module):
140
  return x
141
 
142
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  class SpatioTemporalDiTBlock(nn.Module):
144
  def __init__(
145
  self,
@@ -240,9 +299,70 @@ class SpatioTemporalDiTBlock(nn.Module):
240
  if self.ref_mode == 'parallel':
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
@@ -270,7 +390,14 @@ class SpatioTemporalDiTBlock(nn.Module):
270
  # memory block
271
  relative_embedding = self.relative_embedding # and mode == "training"
272
 
273
- if self.use_memory_attention:
 
 
 
 
 
 
 
274
  r_shift_msa, r_scale_msa, r_gate_msa, r_shift_mlp, r_scale_mlp, r_gate_mlp = self.r_adaLN_modulation(c).chunk(6, dim=-1)
275
 
276
  if pose_cond is not None:
@@ -547,7 +674,9 @@ class DiT(nn.Module):
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")
 
8
  from typing import Optional, Literal
9
  import torch
10
  from torch import nn
11
+ from torch.nn import functional as F
12
  from .rotary_embedding_torch import RotaryEmbedding
13
  from einops import rearrange
14
  from .attention import SpatialAxialAttention, TemporalAxialAttention, MemTemporalAxialAttention, MemFullAttention
 
141
  return x
142
 
143
 
144
+ class FrameMemoryReferenceAttention(nn.Module):
145
+ def __init__(self, hidden_size, num_heads):
146
+ super().__init__()
147
+ self.hidden_size = hidden_size
148
+ self.num_heads = num_heads
149
+ self.head_dim = hidden_size // num_heads
150
+ self.to_q = nn.Linear(hidden_size, hidden_size, bias=False)
151
+ self.to_k = nn.Linear(hidden_size, hidden_size, bias=False)
152
+ self.to_v = nn.Linear(hidden_size, hidden_size, bias=False)
153
+ self.out_proj = nn.Linear(hidden_size, hidden_size, bias=True)
154
+
155
+ def _split_heads(self, x):
156
+ return rearrange(x, "b n (h d) -> b h n d", h=self.num_heads)
157
+
158
+ def forward(self, target_hidden, memory_hidden, memory_mask=None):
159
+ B, T_target, H, W, D = target_hidden.shape
160
+ if memory_hidden is None or int(memory_hidden.shape[1]) == 0:
161
+ return target_hidden.new_zeros(target_hidden.shape)
162
+
163
+ T_memory = memory_hidden.shape[1]
164
+ P = H * W
165
+ q = rearrange(target_hidden, "b t h w d -> (b t) (h w) d")
166
+ q = self._split_heads(self.to_q(q))
167
+
168
+ memory_tokens = rearrange(memory_hidden, "b m h w d -> b (m h w) d")
169
+ k = self._split_heads(self.to_k(memory_tokens))
170
+ v = self._split_heads(self.to_v(memory_tokens))
171
+
172
+ # A zero dummy key keeps all-padded samples finite; the projected delta
173
+ # is masked back to zero below so padded memory cannot contribute.
174
+ dummy = k.new_zeros((B, self.num_heads, 1, self.head_dim))
175
+ k = torch.cat([k, dummy], dim=2)
176
+ v = torch.cat([v, dummy], dim=2)
177
+
178
+ if memory_mask is None:
179
+ frame_valid = torch.ones((B, T_memory), device=target_hidden.device, dtype=torch.bool)
180
+ else:
181
+ frame_valid = memory_mask.to(device=target_hidden.device, dtype=torch.bool)
182
+ key_valid = frame_valid[:, :, None].expand(B, T_memory, P).reshape(B, T_memory * P)
183
+ active = key_valid.any(dim=1)
184
+ key_valid = torch.cat([key_valid, ~active[:, None]], dim=1)
185
+
186
+ row_batch = torch.arange(B, device=target_hidden.device).repeat_interleave(T_target)
187
+ attn_bias = torch.zeros((B * T_target, 1, 1, key_valid.shape[1]), device=q.device, dtype=q.dtype)
188
+ attn_bias = attn_bias.masked_fill(~key_valid[row_batch][:, None, None], float("-inf"))
189
+
190
+ x = F.scaled_dot_product_attention(
191
+ query=q.contiguous(),
192
+ key=k.index_select(0, row_batch).contiguous(),
193
+ value=v.index_select(0, row_batch).contiguous(),
194
+ attn_mask=attn_bias,
195
+ )
196
+ x = rearrange(x, "n h p d -> n p (h d)")
197
+ x = self.out_proj(x).to(dtype=target_hidden.dtype)
198
+ x = rearrange(x, "(b t) (h w) d -> b t h w d", b=B, t=T_target, h=H, w=W)
199
+ return x * active[:, None, None, None, None].to(dtype=x.dtype)
200
+
201
+
202
  class SpatioTemporalDiTBlock(nn.Module):
203
  def __init__(
204
  self,
 
299
  if self.ref_mode == 'parallel':
300
  self.parallel_map = nn.Linear(hidden_size, hidden_size)
301
 
302
+ if self.use_memory_attention:
303
+ self.r_attn_anchor = FrameMemoryReferenceAttention(hidden_size, num_heads)
304
+ self.r_attn_dynamic = FrameMemoryReferenceAttention(hidden_size, num_heads)
305
+ self.r_attn_revisit = FrameMemoryReferenceAttention(hidden_size, num_heads)
306
+
307
+ def _split_frame_memory(self, x, frame_memory_segments):
308
+ target = int(frame_memory_segments.get("target", 0))
309
+ anchor = int(frame_memory_segments.get("anchor", 0))
310
+ dynamic = int(frame_memory_segments.get("dynamic", 0))
311
+ revisit = int(frame_memory_segments.get("revisit", 0))
312
+ a0, a1 = target, target + anchor
313
+ d0, d1 = a1, a1 + dynamic
314
+ r0, r1 = d1, d1 + revisit
315
+ return x[:, :target], x[:, a0:a1], x[:, d0:d1], x[:, r0:r1]
316
+
317
+ def _frame_memory_stream_mask(self, frame_memory_masks, stream_name, stream_hidden):
318
+ if stream_hidden is None or int(stream_hidden.shape[1]) == 0:
319
+ return None
320
+ if frame_memory_masks is None or frame_memory_masks.get(stream_name) is None:
321
+ return None
322
+ return frame_memory_masks[stream_name].to(device=stream_hidden.device, dtype=torch.bool)
323
+
324
+ def _apply_frame_memory_reference_attention(self, x, c, frame_memory_segments, frame_memory_masks):
325
+ x_target, x_anchor, x_dynamic, x_revisit = self._split_frame_memory(x, frame_memory_segments)
326
+ if int(x_target.shape[1]) == 0:
327
+ return x
328
+
329
+ r_shift_msa, r_scale_msa, r_gate_msa, r_shift_mlp, r_scale_mlp, r_gate_mlp = self.r_adaLN_modulation(c).chunk(6, dim=-1)
330
+ attn_input = modulate(self.r_norm1(x), r_shift_msa, r_scale_msa)
331
+ attn_target, attn_anchor, attn_dynamic, attn_revisit = self._split_frame_memory(attn_input, frame_memory_segments)
332
+
333
+ deltas = []
334
+ active_stream_count = x_target.new_zeros((x_target.shape[0],))
335
+ for stream_name, r_attn, stream_hidden, stream_attn in (
336
+ ("anchor", self.r_attn_anchor, x_anchor, attn_anchor),
337
+ ("dynamic", self.r_attn_dynamic, x_dynamic, attn_dynamic),
338
+ ("revisit", self.r_attn_revisit, x_revisit, attn_revisit),
339
+ ):
340
+ if int(stream_hidden.shape[1]) == 0:
341
+ continue
342
+ stream_mask = self._frame_memory_stream_mask(frame_memory_masks, stream_name, stream_hidden)
343
+ deltas.append(r_attn(attn_target, stream_attn, stream_mask))
344
+ if stream_mask is None:
345
+ active_stream_count = active_stream_count + 1
346
+ else:
347
+ active_stream_count = active_stream_count + stream_mask.any(dim=1).to(dtype=active_stream_count.dtype)
348
+
349
+ if deltas:
350
+ # Average over streams that have at least one valid memory frame per
351
+ # sample; samples with no active streams receive a zero reference delta.
352
+ r_delta = sum(deltas)
353
+ active = active_stream_count > 0
354
+ r_delta = r_delta / active_stream_count.clamp_min(1)[:, None, None, None, None]
355
+ r_delta = r_delta * active[:, None, None, None, None].to(dtype=r_delta.dtype)
356
+ x_target = x_target + gate(r_delta, r_gate_msa[:, :x_target.shape[1]])
357
+
358
+ x = torch.cat([x_target, x_anchor, x_dynamic, x_revisit], dim=1)
359
+ x = x + gate(self.r_mlp(modulate(self.r_norm2(x), r_shift_mlp, r_scale_mlp)), r_gate_mlp)
360
+ return x
361
+
362
  def forward(self, x, c, current_frame=None, timestep=None, is_last_block=False,
363
  pose_cond=None, mode="training", c_action_cond=None, reference_length=None,
364
+ frame_memory_segments=None, frame_memory_masks=None, frame_memory_pose=None,
365
+ image_hw=None):
366
  B, T, H, W, D = x.shape
367
 
368
  # spatial block
 
390
  # memory block
391
  relative_embedding = self.relative_embedding # and mode == "training"
392
 
393
+ if self.use_memory_attention and frame_memory_segments is not None:
394
+ x = self._apply_frame_memory_reference_attention(
395
+ x,
396
+ c,
397
+ frame_memory_segments,
398
+ frame_memory_masks,
399
+ )
400
+ elif self.use_memory_attention:
401
  r_shift_msa, r_scale_msa, r_gate_msa, r_shift_mlp, r_scale_mlp, r_gate_mlp = self.r_adaLN_modulation(c).chunk(6, dim=-1)
402
 
403
  if pose_cond is not None:
 
674
  x = block(x, c, current_frame=current_frame, timestep=t, is_last_block= (i+1 == len(self.blocks)),
675
  pose_cond=pc, mode=mode, c_action_cond=c_action_cond, reference_length=reference_length,
676
  frame_memory_segments=frame_memory_segments,
677
+ frame_memory_masks=frame_memory_masks,
678
+ frame_memory_pose=frame_memory_pose,
679
+ image_hw=image_hw) # (N, T, H, W, D)
680
  x = self.final_layer(x, c) # (N, T, H, W, patch_size ** 2 * out_channels)
681
  # unpatchify
682
  x = rearrange(x, "b t h w d -> (b t) h w d")
tests/test_dememwm_temporal_attention.py CHANGED
@@ -4,7 +4,7 @@ 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:
@@ -134,6 +134,25 @@ class DeMemWMTemporalAttentionTests(unittest.TestCase):
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()
 
4
  from torch import nn
5
 
6
  from algorithms.dememwm.models.attention import TemporalAxialAttention
7
+ from algorithms.dememwm.models.dit import DiT, FrameMemoryReferenceAttention, SpatioTemporalDiTBlock
8
 
9
 
10
  class IdentityRotary:
 
134
  self.assertIs(spy.calls[0]["frame_memory_segments"], segments)
135
  self.assertIs(spy.calls[0]["frame_memory_masks"], masks)
136
 
137
+ def test_frame_memory_reference_attention_masks_padded_keys(self):
138
+ attn = FrameMemoryReferenceAttention(hidden_size=1, num_heads=1)
139
+ with torch.no_grad():
140
+ attn.to_q.weight.zero_()
141
+ attn.to_k.weight.zero_()
142
+ attn.to_v.weight.fill_(1.0)
143
+ attn.out_proj.weight.fill_(1.0)
144
+ attn.out_proj.bias.zero_()
145
+
146
+ target = torch.zeros((1, 1, 1, 1, 1))
147
+ memory = torch.tensor([1.0, 10.0]).view(1, 2, 1, 1, 1)
148
+
149
+ first_only = attn(target, memory, torch.tensor([[True, False]]))
150
+ second_only = attn(target, memory, torch.tensor([[False, True]]))
151
+ empty = attn(target, memory, torch.zeros((1, 2), dtype=torch.bool))
152
+
153
+ self.assertTrue(torch.allclose(first_only, torch.ones_like(first_only)))
154
+ self.assertTrue(torch.allclose(second_only, torch.full_like(second_only, 10.0)))
155
+ self.assertTrue(torch.equal(empty, torch.zeros_like(empty)))
156
 
157
  if __name__ == "__main__":
158
  unittest.main()