BonanDing commited on
Commit
0ab9561
·
1 Parent(s): 824571d

Use fp32 for DeMemWM Plucker geometry

Browse files
.exp_artifact/dememwm_remaining_fix_plan.md CHANGED
@@ -285,7 +285,7 @@ Validate DeMemWM DiT stream boundaries
285
 
286
  ## Substep 5: Compute Plucker Geometry In fp32 Under Mixed Precision
287
 
288
- Status: `[ ]`
289
 
290
  Bug:
291
 
 
285
 
286
  ## Substep 5: Compute Plucker Geometry In fp32 Under Mixed Precision
287
 
288
+ Status: `[x]`
289
 
290
  Bug:
291
 
algorithms/dememwm/models/dit.py CHANGED
@@ -645,7 +645,7 @@ class DiT(nn.Module):
645
  )
646
  return None
647
 
648
- pose = frame_memory_pose.to(device=device, dtype=dtype)
649
  if pose.ndim != 3 or pose.shape[-1] != 5:
650
  _warn_frame_memory_geometry_once(
651
  "malformed_pose_metadata",
@@ -665,7 +665,7 @@ class DiT(nn.Module):
665
  )
666
  return None
667
  image_hw = image_hw if torch.is_tensor(image_hw) else torch.as_tensor(image_hw)
668
- image_hw = image_hw.to(device=device, dtype=dtype)
669
  if image_hw.ndim == 1:
670
  image_hw = image_hw.unsqueeze(0)
671
  if image_hw.ndim != 2 or image_hw.shape[-1] < 2:
@@ -683,54 +683,64 @@ class DiT(nn.Module):
683
  )
684
  return None
685
  image_hw = image_hw.contiguous()
686
- image_h, image_w = image_hw[:, 0].view(B, 1, 1), image_hw[:, 1].view(B, 1, 1)
687
-
688
- # Token grid sets position count; image_hw keeps camera scaling in original image coordinates.
689
- y = (torch.arange(grid_h, device=device, dtype=dtype).view(1, grid_h, 1) + 0.5) * (image_h / grid_h)
690
- x = (torch.arange(grid_w, device=device, dtype=dtype).view(1, 1, grid_w) + 0.5) * (image_w / grid_w)
691
- fx, fy = 0.35 * image_w, 0.35 * image_h
692
- cx, cy = 0.5 * image_w, 0.5 * image_h
693
- directions = torch.stack(
694
- (
695
- (-(x - cx) / fx).expand(-1, grid_h, -1),
696
- (-(y - cy) / fy).expand(-1, -1, grid_w),
697
- torch.ones((B, grid_h, grid_w), device=device, dtype=dtype),
698
- ),
699
- dim=-1,
700
- )
701
- directions = F.normalize(directions, dim=-1)
702
- query_rays = torch.cat([torch.zeros_like(directions), directions], dim=-1)[:, None].expand(-1, target_frames, -1, -1, -1)
703
- intrinsics = torch.stack((fx[:, 0, 0], fy[:, 0, 0], cx[:, 0, 0], cy[:, 0, 0]), dim=-1)
704
-
705
- c2w = self._frame_memory_pose_to_c2w(pose)
706
- target_pose = pose[:, :target_frames]
707
- target_c2w = c2w[:, :target_frames]
708
- target_rot = target_c2w[:, :, :3, :3].transpose(-1, -2)
709
- target_t = target_c2w[:, :, :3, 3]
710
- target_w2c = torch.eye(4, device=device, dtype=dtype).view(1, 1, 4, 4).repeat(B, target_frames, 1, 1)
711
- target_w2c[:, :, :3, :3] = target_rot
712
- target_w2c[:, :, :3, 3] = -torch.matmul(target_rot, target_t.unsqueeze(-1)).squeeze(-1)
713
-
714
- cache = {"query_rays": query_rays, "target_pose": target_pose, "image_hw": image_hw, "intrinsics": intrinsics}
715
- cursor = target_frames
716
- for stream_name in ("anchor", "dynamic", "revisit"):
717
- stream_len = int(frame_memory_segments.get(stream_name, 0))
718
- stream_pose = pose[:, cursor:cursor + stream_len]
719
- relative_c2w = torch.matmul(target_w2c[:, :, None], c2w[:, None, cursor:cursor + stream_len])
720
- rays_d = torch.einsum("bhwj,btmij->btmhwi", directions, relative_c2w[..., :3, :3])
721
- rays_d = F.normalize(rays_d, dim=-1)
722
- rays_o = relative_c2w[..., :3, 3][:, :, :, None, None, :].expand(-1, -1, -1, grid_h, grid_w, -1)
723
- 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)
724
- cache[stream_name] = {
725
- "query_rays": query_rays,
726
- "relative_pose": stream_pose[:, None] - target_pose[:, :, None],
727
- "relative_c2w": relative_c2w,
728
- "relative_rays": torch.cat([torch.linalg.cross(rays_o, rays_d, dim=-1), rays_d], dim=-1),
729
- "image_hw": image_hw,
730
- "intrinsics": intrinsics,
731
- "mask": stream_mask,
732
  }
733
- cursor += stream_len
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
734
  return cache
735
 
736
  def forward(self, x, t, action_cond=None, pose_cond=None, current_frame=None, mode=None,
 
645
  )
646
  return None
647
 
648
+ pose = frame_memory_pose.to(device=device)
649
  if pose.ndim != 3 or pose.shape[-1] != 5:
650
  _warn_frame_memory_geometry_once(
651
  "malformed_pose_metadata",
 
665
  )
666
  return None
667
  image_hw = image_hw if torch.is_tensor(image_hw) else torch.as_tensor(image_hw)
668
+ image_hw = image_hw.to(device=device)
669
  if image_hw.ndim == 1:
670
  image_hw = image_hw.unsqueeze(0)
671
  if image_hw.ndim != 2 or image_hw.shape[-1] < 2:
 
683
  )
684
  return None
685
  image_hw = image_hw.contiguous()
686
+ # Mixed precision can round camera offsets before relative poses/rays are
687
+ # formed, so keep this local geometry block in fp32 and only cast caches.
688
+ with torch.autocast(device_type=device.type, enabled=False):
689
+ pose = pose.to(dtype=torch.float32)
690
+ image_hw = image_hw.to(dtype=torch.float32)
691
+ image_h, image_w = image_hw[:, 0].view(B, 1, 1), image_hw[:, 1].view(B, 1, 1)
692
+
693
+ # Token grid sets position count; image_hw keeps camera scaling in original image coordinates.
694
+ y = (torch.arange(grid_h, device=device, dtype=torch.float32).view(1, grid_h, 1) + 0.5) * (image_h / grid_h)
695
+ x = (torch.arange(grid_w, device=device, dtype=torch.float32).view(1, 1, grid_w) + 0.5) * (image_w / grid_w)
696
+ fx, fy = 0.35 * image_w, 0.35 * image_h
697
+ cx, cy = 0.5 * image_w, 0.5 * image_h
698
+ directions = torch.stack(
699
+ (
700
+ (-(x - cx) / fx).expand(-1, grid_h, -1),
701
+ (-(y - cy) / fy).expand(-1, -1, grid_w),
702
+ torch.ones((B, grid_h, grid_w), device=device, dtype=torch.float32),
703
+ ),
704
+ dim=-1,
705
+ )
706
+ directions = F.normalize(directions, dim=-1)
707
+ query_rays = torch.cat([torch.zeros_like(directions), directions], dim=-1)[:, None].expand(-1, target_frames, -1, -1, -1)
708
+ intrinsics = torch.stack((fx[:, 0, 0], fy[:, 0, 0], cx[:, 0, 0], cy[:, 0, 0]), dim=-1)
709
+
710
+ c2w = self._frame_memory_pose_to_c2w(pose)
711
+ target_pose = pose[:, :target_frames]
712
+ target_c2w = c2w[:, :target_frames]
713
+ target_rot = target_c2w[:, :, :3, :3].transpose(-1, -2)
714
+ target_t = target_c2w[:, :, :3, 3]
715
+ target_w2c = torch.eye(4, device=device, dtype=torch.float32).view(1, 1, 4, 4).repeat(B, target_frames, 1, 1)
716
+ target_w2c[:, :, :3, :3] = target_rot
717
+ target_w2c[:, :, :3, 3] = -torch.matmul(target_rot, target_t.unsqueeze(-1)).squeeze(-1)
718
+
719
+ cache = {
720
+ "query_rays": query_rays.to(dtype=dtype),
721
+ "target_pose": target_pose.to(dtype=dtype),
722
+ "image_hw": image_hw.to(dtype=dtype),
723
+ "intrinsics": intrinsics.to(dtype=dtype),
 
 
 
 
 
 
 
 
724
  }
725
+ cursor = target_frames
726
+ for stream_name in ("anchor", "dynamic", "revisit"):
727
+ stream_len = int(frame_memory_segments.get(stream_name, 0))
728
+ stream_pose = pose[:, cursor:cursor + stream_len]
729
+ relative_c2w = torch.matmul(target_w2c[:, :, None], c2w[:, None, cursor:cursor + stream_len])
730
+ rays_d = torch.einsum("bhwj,btmij->btmhwi", directions, relative_c2w[..., :3, :3])
731
+ rays_d = F.normalize(rays_d, dim=-1)
732
+ rays_o = relative_c2w[..., :3, 3][:, :, :, None, None, :].expand(-1, -1, -1, grid_h, grid_w, -1)
733
+ 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)
734
+ cache[stream_name] = {
735
+ "query_rays": query_rays.to(dtype=dtype),
736
+ "relative_pose": (stream_pose[:, None] - target_pose[:, :, None]).to(dtype=dtype),
737
+ "relative_c2w": relative_c2w.to(dtype=dtype),
738
+ "relative_rays": torch.cat([torch.linalg.cross(rays_o, rays_d, dim=-1), rays_d], dim=-1).to(dtype=dtype),
739
+ "image_hw": image_hw.to(dtype=dtype),
740
+ "intrinsics": intrinsics.to(dtype=dtype),
741
+ "mask": stream_mask,
742
+ }
743
+ cursor += stream_len
744
  return cache
745
 
746
  def forward(self, x, t, action_cond=None, pose_cond=None, current_frame=None, mode=None,
tests/test_dememwm_temporal_attention.py CHANGED
@@ -373,6 +373,56 @@ class DeMemWMTemporalAttentionTests(unittest.TestCase):
373
  self.assertTrue(torch.equal(anchor_cache["relative_pose"][0, :, 0, 0], torch.tensor([2.0, 1.0])))
374
  self.assertTrue(torch.equal(spies[0][1].geometry[0]["mask"], masks["dynamic"]))
375
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  def test_frame_memory_reference_attention_masks_padded_keys(self):
377
  attn = FrameMemoryReferenceAttention(hidden_size=1, num_heads=1)
378
  with torch.no_grad():
 
373
  self.assertTrue(torch.equal(anchor_cache["relative_pose"][0, :, 0, 0], torch.tensor([2.0, 1.0])))
374
  self.assertTrue(torch.equal(spies[0][1].geometry[0]["mask"], masks["dynamic"]))
375
 
376
+ def test_frame_memory_geometry_uses_fp32_math_then_returns_activation_dtype(self):
377
+ model = DiT(
378
+ input_h=2,
379
+ input_w=2,
380
+ patch_size=1,
381
+ in_channels=1,
382
+ hidden_size=8,
383
+ depth=1,
384
+ num_heads=1,
385
+ mlp_ratio=1.0,
386
+ action_cond_dim=3,
387
+ pose_cond_dim=0,
388
+ reference_length=0,
389
+ use_plucker=True,
390
+ use_memory_attention=True,
391
+ )
392
+ device = torch.device("cpu")
393
+ segments = {"target": 1, "anchor": 1, "dynamic": 0, "revisit": 0}
394
+ frame_memory_pose = torch.zeros((1, 2, 5), device=device, dtype=torch.float32)
395
+ frame_memory_pose[0, 0, 0] = 10000.0
396
+ frame_memory_pose[0, 1, 0] = 10001.0
397
+ image_hw = torch.tensor([[4, 8]], device=device, dtype=torch.long)
398
+
399
+ with torch.autocast(device_type=device.type, dtype=torch.bfloat16):
400
+ cache = model._build_frame_memory_geometry(
401
+ segments,
402
+ None,
403
+ frame_memory_pose,
404
+ image_hw,
405
+ grid_h=2,
406
+ grid_w=2,
407
+ device=device,
408
+ dtype=torch.float16,
409
+ )
410
+
411
+ anchor_cache = cache["anchor"]
412
+ for tensor in (
413
+ cache["query_rays"],
414
+ cache["target_pose"],
415
+ cache["image_hw"],
416
+ cache["intrinsics"],
417
+ anchor_cache["relative_pose"],
418
+ anchor_cache["relative_c2w"],
419
+ anchor_cache["relative_rays"],
420
+ ):
421
+ self.assertEqual(tensor.dtype, torch.float16)
422
+ self.assertTrue(torch.isfinite(tensor).all().item())
423
+ self.assertEqual(anchor_cache["relative_pose"][0, 0, 0, 0].item(), 1.0)
424
+ self.assertEqual(anchor_cache["relative_c2w"][0, 0, 0, 0, 3].item(), 1.0)
425
+
426
  def test_frame_memory_reference_attention_masks_padded_keys(self):
427
  attn = FrameMemoryReferenceAttention(hidden_size=1, num_heads=1)
428
  with torch.no_grad():