BonanDing commited on
Commit
8ea8089
·
1 Parent(s): 837b695

Build DeMemWM frame memory geometry cache

Browse files
algorithms/dememwm/models/dit.py CHANGED
@@ -155,7 +155,7 @@ class FrameMemoryReferenceAttention(nn.Module):
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)
@@ -321,7 +321,7 @@ class SpatioTemporalDiTBlock(nn.Module):
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
@@ -340,7 +340,8 @@ class SpatioTemporalDiTBlock(nn.Module):
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:
@@ -362,7 +363,7 @@ class SpatioTemporalDiTBlock(nn.Module):
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
@@ -396,6 +397,7 @@ class SpatioTemporalDiTBlock(nn.Module):
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)
@@ -611,6 +613,84 @@ class DiT(nn.Module):
611
  imgs = x.reshape(shape=(x.shape[0], c, h * p, w * p))
612
  return imgs
613
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
614
  def forward(self, x, t, action_cond=None, pose_cond=None, current_frame=None, mode=None,
615
  reference_length=None, frame_idx=None, frame_memory_segments=None,
616
  frame_memory_masks=None, frame_memory_pose=None, image_hw=None):
@@ -670,13 +750,25 @@ class DiT(nn.Module):
670
  else:
671
  pc = None
672
 
 
 
 
 
 
 
 
 
 
 
 
673
  for i, block in enumerate(self.blocks):
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")
 
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, geometry_cache=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)
 
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, frame_memory_geometry=None):
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
 
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
+ stream_geometry = None if frame_memory_geometry is None else frame_memory_geometry.get(stream_name)
344
+ deltas.append(r_attn(attn_target, stream_attn, stream_mask, geometry_cache=stream_geometry))
345
  if stream_mask is None:
346
  active_stream_count = active_stream_count + 1
347
  else:
 
363
  def forward(self, x, c, current_frame=None, timestep=None, is_last_block=False,
364
  pose_cond=None, mode="training", c_action_cond=None, reference_length=None,
365
  frame_memory_segments=None, frame_memory_masks=None, frame_memory_pose=None,
366
+ image_hw=None, frame_memory_geometry=None):
367
  B, T, H, W, D = x.shape
368
 
369
  # spatial block
 
397
  c,
398
  frame_memory_segments,
399
  frame_memory_masks,
400
+ frame_memory_geometry,
401
  )
402
  elif self.use_memory_attention:
403
  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)
 
613
  imgs = x.reshape(shape=(x.shape[0], c, h * p, w * p))
614
  return imgs
615
 
616
+ def _frame_memory_pose_to_c2w(self, pose):
617
+ B, T = pose.shape[:2]
618
+ x, y, z, pitch, yaw = pose.unbind(dim=-1)
619
+ pitch, yaw = torch.deg2rad(pitch), torch.deg2rad(yaw)
620
+ cp, sp = torch.cos(pitch), torch.sin(pitch)
621
+ cy, sy = torch.cos(yaw), torch.sin(yaw)
622
+ one, zero = torch.ones_like(pitch), torch.zeros_like(pitch)
623
+ r_pitch = torch.stack((one, zero, zero, zero, cp, -sp, zero, sp, cp), dim=-1).reshape(B, T, 3, 3)
624
+ r_yaw = torch.stack((cy, zero, sy, zero, one, zero, -sy, zero, cy), dim=-1).reshape(B, T, 3, 3)
625
+ c2w = torch.eye(4, device=pose.device, dtype=pose.dtype).view(1, 1, 4, 4).repeat(B, T, 1, 1)
626
+ c2w[:, :, :3, :3] = torch.matmul(r_yaw, r_pitch)
627
+ c2w[:, :, :3, 3] = torch.stack((x, y, z), dim=-1)
628
+ return c2w
629
+
630
+ def _build_frame_memory_geometry(self, frame_memory_segments, frame_memory_masks, frame_memory_pose, image_hw, grid_h, grid_w, device, dtype):
631
+ if frame_memory_segments is None or frame_memory_pose is None or image_hw is None:
632
+ return None
633
+
634
+ pose = frame_memory_pose.to(device=device, dtype=dtype)
635
+ B = pose.shape[0]
636
+ target_frames = int(frame_memory_segments.get("target", 0))
637
+ image_hw = image_hw if torch.is_tensor(image_hw) else torch.as_tensor(image_hw)
638
+ image_hw = image_hw.to(device=device, dtype=dtype)
639
+ if image_hw.ndim == 1:
640
+ image_hw = image_hw.unsqueeze(0)
641
+ if image_hw.shape[0] == 1 and B != 1:
642
+ image_hw = image_hw.expand(B, -1)
643
+ image_hw = image_hw.contiguous()
644
+ image_h, image_w = image_hw[:, 0].view(B, 1, 1), image_hw[:, 1].view(B, 1, 1)
645
+
646
+ # Token grid sets position count; image_hw keeps camera scaling in original image coordinates.
647
+ y = (torch.arange(grid_h, device=device, dtype=dtype).view(1, grid_h, 1) + 0.5) * (image_h / grid_h)
648
+ x = (torch.arange(grid_w, device=device, dtype=dtype).view(1, 1, grid_w) + 0.5) * (image_w / grid_w)
649
+ fx, fy = 0.35 * image_w, 0.35 * image_h
650
+ cx, cy = 0.5 * image_w, 0.5 * image_h
651
+ directions = torch.stack(
652
+ (
653
+ (-(x - cx) / fx).expand(-1, grid_h, -1),
654
+ (-(y - cy) / fy).expand(-1, -1, grid_w),
655
+ torch.ones((B, grid_h, grid_w), device=device, dtype=dtype),
656
+ ),
657
+ dim=-1,
658
+ )
659
+ directions = F.normalize(directions, dim=-1)
660
+ query_rays = torch.cat([torch.zeros_like(directions), directions], dim=-1)[:, None].expand(-1, target_frames, -1, -1, -1)
661
+ intrinsics = torch.stack((fx[:, 0, 0], fy[:, 0, 0], cx[:, 0, 0], cy[:, 0, 0]), dim=-1)
662
+
663
+ c2w = self._frame_memory_pose_to_c2w(pose)
664
+ target_pose = pose[:, :target_frames]
665
+ target_c2w = c2w[:, :target_frames]
666
+ target_rot = target_c2w[:, :, :3, :3].transpose(-1, -2)
667
+ target_t = target_c2w[:, :, :3, 3]
668
+ target_w2c = torch.eye(4, device=device, dtype=dtype).view(1, 1, 4, 4).repeat(B, target_frames, 1, 1)
669
+ target_w2c[:, :, :3, :3] = target_rot
670
+ target_w2c[:, :, :3, 3] = -torch.matmul(target_rot, target_t.unsqueeze(-1)).squeeze(-1)
671
+
672
+ cache = {"query_rays": query_rays, "target_pose": target_pose, "image_hw": image_hw, "intrinsics": intrinsics}
673
+ cursor = target_frames
674
+ for stream_name in ("anchor", "dynamic", "revisit"):
675
+ stream_len = int(frame_memory_segments.get(stream_name, 0))
676
+ stream_pose = pose[:, cursor:cursor + stream_len]
677
+ relative_c2w = torch.matmul(target_w2c[:, :, None], c2w[:, None, cursor:cursor + stream_len])
678
+ rays_d = torch.einsum("bhwj,btmij->btmhwi", directions, relative_c2w[..., :3, :3])
679
+ rays_d = F.normalize(rays_d, dim=-1)
680
+ rays_o = relative_c2w[..., :3, 3][:, :, :, None, None, :].expand(-1, -1, -1, grid_h, grid_w, -1)
681
+ stream_mask = None if frame_memory_masks is None or frame_memory_masks.get(stream_name) is None else frame_memory_masks[stream_name].to(device=device, dtype=torch.bool)
682
+ cache[stream_name] = {
683
+ "query_rays": query_rays,
684
+ "relative_pose": stream_pose[:, None] - target_pose[:, :, None],
685
+ "relative_c2w": relative_c2w,
686
+ "relative_rays": torch.cat([torch.linalg.cross(rays_o, rays_d, dim=-1), rays_d], dim=-1),
687
+ "image_hw": image_hw,
688
+ "intrinsics": intrinsics,
689
+ "mask": stream_mask,
690
+ }
691
+ cursor += stream_len
692
+ return cache
693
+
694
  def forward(self, x, t, action_cond=None, pose_cond=None, current_frame=None, mode=None,
695
  reference_length=None, frame_idx=None, frame_memory_segments=None,
696
  frame_memory_masks=None, frame_memory_pose=None, image_hw=None):
 
750
  else:
751
  pc = None
752
 
753
+ frame_memory_geometry = self._build_frame_memory_geometry(
754
+ frame_memory_segments,
755
+ frame_memory_masks,
756
+ frame_memory_pose,
757
+ image_hw,
758
+ x.shape[2],
759
+ x.shape[3],
760
+ x.device,
761
+ x.dtype,
762
+ )
763
+
764
  for i, block in enumerate(self.blocks):
765
  x = block(x, c, current_frame=current_frame, timestep=t, is_last_block= (i+1 == len(self.blocks)),
766
  pose_cond=pc, mode=mode, c_action_cond=c_action_cond, reference_length=reference_length,
767
  frame_memory_segments=frame_memory_segments,
768
  frame_memory_masks=frame_memory_masks,
769
  frame_memory_pose=frame_memory_pose,
770
+ image_hw=image_hw,
771
+ frame_memory_geometry=frame_memory_geometry) # (N, T, H, W, D)
772
  x = self.final_layer(x, c) # (N, T, H, W, patch_size ** 2 * out_channels)
773
  # unpatchify
774
  x = rearrange(x, "b t h w d -> (b t) h w d")
tests/test_dememwm_temporal_attention.py CHANGED
@@ -134,6 +134,80 @@ 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
  def test_frame_memory_reference_attention_masks_padded_keys(self):
138
  attn = FrameMemoryReferenceAttention(hidden_size=1, num_heads=1)
139
  with torch.no_grad():
 
134
  self.assertIs(spy.calls[0]["frame_memory_segments"], segments)
135
  self.assertIs(spy.calls[0]["frame_memory_masks"], masks)
136
 
137
+ def test_dit_builds_geometry_cache_once_and_threads_to_reference_attention(self):
138
+ class ZeroAttention(nn.Module):
139
+ def forward(self, x):
140
+ return torch.zeros_like(x)
141
+
142
+ class ZeroTemporalAttention(nn.Module):
143
+ def forward(self, x, frame_memory_segments=None, frame_memory_masks=None):
144
+ return torch.zeros_like(x)
145
+
146
+ class SpyReferenceAttention(nn.Module):
147
+ def __init__(self):
148
+ super().__init__()
149
+ self.geometry = []
150
+
151
+ def forward(self, target_hidden, memory_hidden, memory_mask=None, geometry_cache=None):
152
+ self.geometry.append(geometry_cache)
153
+ return torch.zeros_like(target_hidden)
154
+
155
+ model = DiT(
156
+ input_h=2,
157
+ input_w=2,
158
+ patch_size=1,
159
+ in_channels=1,
160
+ hidden_size=8,
161
+ depth=2,
162
+ num_heads=1,
163
+ mlp_ratio=1.0,
164
+ action_cond_dim=3,
165
+ pose_cond_dim=0,
166
+ reference_length=0,
167
+ use_memory_attention=True,
168
+ )
169
+ spies = []
170
+ for block in model.blocks:
171
+ block.s_attn = ZeroAttention()
172
+ block.t_attn = ZeroTemporalAttention()
173
+ anchor = SpyReferenceAttention()
174
+ dynamic = SpyReferenceAttention()
175
+ revisit = SpyReferenceAttention()
176
+ block.r_attn_anchor = anchor
177
+ block.r_attn_dynamic = dynamic
178
+ block.r_attn_revisit = revisit
179
+ spies.append((anchor, dynamic, revisit))
180
+ model.eval()
181
+
182
+ x = torch.zeros((1, 5, 1, 2, 2))
183
+ t = torch.zeros((1, 5), dtype=torch.long)
184
+ action_cond = torch.zeros((1, 5, 3))
185
+ frame_memory_pose = torch.zeros((1, 5, 5))
186
+ frame_memory_pose[0, :, 0] = torch.arange(5, dtype=torch.float32)
187
+ image_hw = torch.tensor([[4, 8]], dtype=torch.long)
188
+ segments = {"target": 2, "anchor": 1, "dynamic": 1, "revisit": 1}
189
+ masks = {"dynamic": torch.tensor([[False]])}
190
+
191
+ with torch.no_grad():
192
+ model(
193
+ x,
194
+ t,
195
+ action_cond,
196
+ frame_memory_segments=segments,
197
+ frame_memory_masks=masks,
198
+ frame_memory_pose=frame_memory_pose,
199
+ image_hw=image_hw,
200
+ )
201
+
202
+ anchor_cache = spies[0][0].geometry[0]
203
+ self.assertIs(anchor_cache, spies[1][0].geometry[0])
204
+ self.assertEqual(tuple(anchor_cache["query_rays"].shape), (1, 2, 2, 2, 6))
205
+ self.assertEqual(tuple(anchor_cache["relative_rays"].shape), (1, 2, 1, 2, 2, 6))
206
+ self.assertTrue(torch.equal(anchor_cache["image_hw"], torch.tensor([[4.0, 8.0]])))
207
+ self.assertTrue(torch.allclose(anchor_cache["intrinsics"], torch.tensor([[2.8, 1.4, 4.0, 2.0]])))
208
+ self.assertTrue(torch.equal(anchor_cache["relative_pose"][0, :, 0, 0], torch.tensor([2.0, 1.0])))
209
+ self.assertTrue(torch.equal(spies[0][1].geometry[0]["mask"], masks["dynamic"]))
210
+
211
  def test_frame_memory_reference_attention_masks_padded_keys(self):
212
  attn = FrameMemoryReferenceAttention(hidden_size=1, num_heads=1)
213
  with torch.no_grad():