BonanDing commited on
Commit
63bd9b0
·
1 Parent(s): b093f25

Fix DeMemWM memory dropout and inference stabilization

Browse files
algorithms/dememwm/df_video.py CHANGED
@@ -645,11 +645,22 @@ def _select_online_event_dynamic_from_cache(
645
  return _select_dynamic_from_stream(eligible_stream, reference_frames, count)
646
 
647
 
 
 
 
 
 
 
648
  def _memory_noise_levels_for_streams(cfg, diffusion_model, query_noise_levels, stream_lengths, mode: str):
649
  is_training = str(mode) == "training"
650
  noise_cfg = _cfg_get(cfg, "memory_noise")
651
  if noise_cfg is None:
652
- value = int(getattr(diffusion_model, "stabilization_level", 0)) if is_training else 0
 
 
 
 
 
653
  return {
654
  key: torch.full((int(stream_lengths[key]), query_noise_levels.shape[-1]), value, device=query_noise_levels.device, dtype=torch.long)
655
  for key in _DEMEMWM_STREAM_KEYS
@@ -663,6 +674,11 @@ def _memory_noise_levels_for_streams(cfg, diffusion_model, query_noise_levels, s
663
  )
664
  query = query_noise_levels[:, None] if query_noise_levels.ndim == 1 else query_noise_levels
665
  query_bounds = query.to(dtype=torch.long).clamp_min(0).min(dim=0).values
 
 
 
 
 
666
  if noise_mode == "independent":
667
  if is_training:
668
  max_level = int(getattr(diffusion_model, "timesteps", 0) or 0) - 1
@@ -2077,7 +2093,7 @@ class DeMemWMMinecraft(DiffusionForcingBase):
2077
  for m in range(scheduling_matrix.shape[0] - 1):
2078
  from_query = torch.as_tensor(scheduling_matrix[m], device=xs.device, dtype=torch.long)[:, None].repeat(1, batch_size)
2079
  to_query = torch.as_tensor(scheduling_matrix[m + 1], device=xs.device, dtype=torch.long)[:, None].repeat(1, batch_size)
2080
- context_levels = from_query.new_zeros((query_offset, batch_size))
2081
  from_target = torch.cat([context_levels, from_query], dim=0)
2082
  to_target = torch.cat([context_levels, to_query], dim=0)
2083
  memory_noise_levels_by_key = _memory_noise_levels_for_streams(
 
645
  return _select_dynamic_from_stream(eligible_stream, reference_frames, count)
646
 
647
 
648
+ def _stabilized_sampling_levels(reference_levels, count: int):
649
+ """Schedule level 0 enters sample_step's fixed stabilization path."""
650
+ levels = reference_levels[:, None] if reference_levels.ndim == 1 else reference_levels
651
+ return levels.new_zeros((int(count), levels.shape[-1]), dtype=torch.long)
652
+
653
+
654
  def _memory_noise_levels_for_streams(cfg, diffusion_model, query_noise_levels, stream_lengths, mode: str):
655
  is_training = str(mode) == "training"
656
  noise_cfg = _cfg_get(cfg, "memory_noise")
657
  if noise_cfg is None:
658
+ if not is_training:
659
+ return {
660
+ key: _stabilized_sampling_levels(query_noise_levels, int(stream_lengths[key]))
661
+ for key in _DEMEMWM_STREAM_KEYS
662
+ }
663
+ value = int(getattr(diffusion_model, "stabilization_level", 0))
664
  return {
665
  key: torch.full((int(stream_lengths[key]), query_noise_levels.shape[-1]), value, device=query_noise_levels.device, dtype=torch.long)
666
  for key in _DEMEMWM_STREAM_KEYS
 
674
  )
675
  query = query_noise_levels[:, None] if query_noise_levels.ndim == 1 else query_noise_levels
676
  query_bounds = query.to(dtype=torch.long).clamp_min(0).min(dim=0).values
677
+ if not noisy_memory:
678
+ return {
679
+ key: _stabilized_sampling_levels(query, max(0, int(stream_lengths[key])))
680
+ for key in _DEMEMWM_STREAM_KEYS
681
+ }
682
  if noise_mode == "independent":
683
  if is_training:
684
  max_level = int(getattr(diffusion_model, "timesteps", 0) or 0) - 1
 
2093
  for m in range(scheduling_matrix.shape[0] - 1):
2094
  from_query = torch.as_tensor(scheduling_matrix[m], device=xs.device, dtype=torch.long)[:, None].repeat(1, batch_size)
2095
  to_query = torch.as_tensor(scheduling_matrix[m + 1], device=xs.device, dtype=torch.long)[:, None].repeat(1, batch_size)
2096
+ context_levels = _stabilized_sampling_levels(from_query, query_offset)
2097
  from_target = torch.cat([context_levels, from_query], dim=0)
2098
  to_target = torch.cat([context_levels, to_query], dim=0)
2099
  memory_noise_levels_by_key = _memory_noise_levels_for_streams(
configurations/algorithm/dememwm_base.yaml CHANGED
@@ -13,6 +13,8 @@ memory_noise:
13
  anchor_max_fraction: 0.25
14
  dynamic_max_fraction: 0.50
15
  revisit_max_fraction: 0.25
 
 
16
  # Per stream: all = always active, high = active for high query noise,
17
  # low = active for low query noise.
18
  noise_route: {anchor: all, dynamic: all, revisit: all}
 
13
  anchor_max_fraction: 0.25
14
  dynamic_max_fraction: 0.50
15
  revisit_max_fraction: 0.25
16
+ # Inference memory follows the WorldMem stabilization path by default.
17
+ validation_noisy_memory: false
18
  # Per stream: all = always active, high = active for high query noise,
19
  # low = active for low query noise.
20
  noise_route: {anchor: all, dynamic: all, revisit: all}
configurations/dataset/video_minecraft_dememwm_latent.yaml CHANGED
@@ -26,6 +26,7 @@ memory_selection:
26
  pose_similarity_threshold: 0.6
27
  training_use_plucker: true
28
  training_plucker_weight: 1.0
 
29
  fov_overlap_threshold: 0.6
30
  min_total_selected_coverage: 0.1
31
  local_context_exclusion_frames: 8
 
26
  pose_similarity_threshold: 0.6
27
  training_use_plucker: true
28
  training_plucker_weight: 1.0
29
+ training_dropout: 0.1
30
  fov_overlap_threshold: 0.6
31
  min_total_selected_coverage: 0.1
32
  local_context_exclusion_frames: 8
datasets/video/memory_selection.py CHANGED
@@ -421,6 +421,27 @@ def _write_segment(indices: MutableMapping[str, np.ndarray], masks: MutableMappi
421
  masks[key][: len(selected)] = True
422
 
423
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
  def _best_row(rows: torch.Tensor, gains: torch.Tensor, fov_values: torch.Tensor, plucker: torch.Tensor, gaps: torch.Tensor, candidates_t: torch.Tensor) -> int:
425
  active = rows
426
  for values in (gains, fov_values, plucker, -gaps.to(dtype=torch.float32), -candidates_t.to(dtype=torch.float32)):
@@ -1319,6 +1340,37 @@ def _select_dynamic_by_policy(
1319
  raise AssertionError(f"unhandled dynamic selection policy: {policy!r}")
1320
 
1321
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1322
  def _select_revisit(
1323
  poses: np.ndarray,
1324
  target_positions: np.ndarray,
@@ -1433,7 +1485,6 @@ def select_memory_indices(
1433
  min_candidate_frame=min_candidate_frame,
1434
  fov_pool=fov_pool,
1435
  )
1436
-
1437
  if policy == "recent":
1438
  dynamic = _select_dynamic_by_policy(
1439
  target_start,
@@ -1486,6 +1537,30 @@ def select_memory_indices(
1486
  else:
1487
  raise AssertionError(f"unhandled dynamic selection policy: {policy!r}")
1488
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1489
  _write_segment(indices, masks, "anchor", anchor)
1490
  _write_segment(indices, masks, "dynamic", dynamic)
1491
  _write_segment(indices, masks, "revisit", revisit)
 
421
  masks[key][: len(selected)] = True
422
 
423
 
424
+ def _rng_choice(rng, values: np.ndarray, size: int, *, replace: bool) -> np.ndarray:
425
+ chooser = np.random if rng is None else rng
426
+ return np.asarray(chooser.choice(values, size=int(size), replace=replace), dtype=np.int64)
427
+
428
+
429
+ def _rng_random(rng) -> float:
430
+ return float(np.random.random() if rng is None else rng.random())
431
+
432
+
433
+ def _sample_random_selection(candidates: np.ndarray, count: int, rng=None) -> np.ndarray:
434
+ count = int(count)
435
+ if count <= 0:
436
+ return np.empty((0,), dtype=np.int64)
437
+
438
+ candidates = np.asarray(candidates, dtype=np.int64)
439
+ candidates = candidates[candidates >= 0]
440
+ if len(candidates) == 0:
441
+ return np.empty((0,), dtype=np.int64)
442
+ return _rng_choice(rng, candidates, count, replace=True)
443
+
444
+
445
  def _best_row(rows: torch.Tensor, gains: torch.Tensor, fov_values: torch.Tensor, plucker: torch.Tensor, gaps: torch.Tensor, candidates_t: torch.Tensor) -> int:
446
  active = rows
447
  for values in (gains, fov_values, plucker, -gaps.to(dtype=torch.float32), -candidates_t.to(dtype=torch.float32)):
 
1340
  raise AssertionError(f"unhandled dynamic selection policy: {policy!r}")
1341
 
1342
 
1343
+ def _dynamic_random_candidates(
1344
+ num_frames: int,
1345
+ target_start: int,
1346
+ target_positions: np.ndarray,
1347
+ cfg,
1348
+ split: str,
1349
+ min_candidate_frame: int,
1350
+ policy: str,
1351
+ *,
1352
+ excluded: np.ndarray | None = None,
1353
+ ) -> np.ndarray:
1354
+ if policy == "recent":
1355
+ stop = int(target_start)
1356
+ exclusion = max(0, int(cfg_get(cfg, "local_context_exclusion_frames", 8)))
1357
+ if exclusion > 0:
1358
+ stop = max(int(min_candidate_frame), stop - exclusion)
1359
+ return np.arange(int(min_candidate_frame), stop, dtype=np.int64)
1360
+
1361
+ candidates = _memory_candidate_frames(
1362
+ int(num_frames),
1363
+ target_positions,
1364
+ cfg,
1365
+ split,
1366
+ min_candidate_frame=int(min_candidate_frame),
1367
+ )
1368
+ candidates = _exclude_local_context(candidates, target_positions, cfg)
1369
+ if policy == "multiview" and excluded is not None and len(excluded) > 0 and len(candidates) > 0:
1370
+ candidates = candidates[~np.isin(candidates, np.asarray(excluded, dtype=np.int64))]
1371
+ return candidates.astype(np.int64, copy=False)
1372
+
1373
+
1374
  def _select_revisit(
1375
  poses: np.ndarray,
1376
  target_positions: np.ndarray,
 
1485
  min_candidate_frame=min_candidate_frame,
1486
  fov_pool=fov_pool,
1487
  )
 
1488
  if policy == "recent":
1489
  dynamic = _select_dynamic_by_policy(
1490
  target_start,
 
1537
  else:
1538
  raise AssertionError(f"unhandled dynamic selection policy: {policy!r}")
1539
 
1540
+ training_dropout = float(cfg_get(cfg, "training_dropout", 0.0)) if split == "training" else 0.0
1541
+ if training_dropout > 0.0 and _rng_random(rng) < training_dropout:
1542
+ revisit_candidates = _memory_candidate_frames(
1543
+ len(poses),
1544
+ target_positions,
1545
+ cfg,
1546
+ split,
1547
+ min_candidate_frame=min_candidate_frame,
1548
+ )
1549
+ revisit_candidates = _exclude_revisit_local_context(revisit_candidates, target_positions, cfg)
1550
+ dynamic_candidates = _dynamic_random_candidates(
1551
+ len(poses),
1552
+ target_start,
1553
+ target_positions,
1554
+ cfg,
1555
+ split,
1556
+ min_candidate_frame,
1557
+ policy,
1558
+ excluded=revisit if policy == "multiview" else None,
1559
+ )
1560
+ anchor = _sample_random_selection(anchor_candidates, counts["anchor"], rng=rng)
1561
+ dynamic = _sample_random_selection(dynamic_candidates, counts["dynamic"], rng=rng)
1562
+ revisit = _sample_random_selection(revisit_candidates, counts["revisit"], rng=rng)
1563
+
1564
  _write_segment(indices, masks, "anchor", anchor)
1565
  _write_segment(indices, masks, "dynamic", dynamic)
1566
  _write_segment(indices, masks, "revisit", revisit)
tests/test_dememwm_latent_dataset.py CHANGED
@@ -48,6 +48,7 @@ def _selection_cfg(**overrides):
48
  "pose_similarity_threshold": 0.6,
49
  "training_use_plucker": False,
50
  "training_plucker_weight": 0.1,
 
51
  "fov_overlap_threshold": 0.0,
52
  "min_total_selected_coverage": 0.0,
53
  "local_context_exclusion_frames": 2,
@@ -219,6 +220,7 @@ class MemorySelectionTests(unittest.TestCase):
219
  self.assertEqual(cfg.memory_selection.dynamic.multiview_selector, "fov_greedy")
220
  self.assertEqual(cfg.memory_selection.pose_preselect_topk, 32)
221
  self.assertEqual(cfg.memory_selection.candidate_chunk_size, 0)
 
222
 
223
  def test_revisit_uses_sampled_fov_selection(self):
224
  poses = np.array(
@@ -833,6 +835,32 @@ class MemorySelectionTests(unittest.TestCase):
833
  self.assertEqual(indices["anchor"].tolist(), [2, 5])
834
  self.assertEqual(masks["anchor"].tolist(), [True, True])
835
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
836
  def test_event_triggered_dynamic_causal_stream_stays_before_target(self):
837
  poses = _poses(12)
838
  cfg = _selection_cfg(
@@ -1771,7 +1799,10 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
1771
  self.memory_condition_length, self.clip_noise = 3, 0.0
1772
  self.context_frames, self.frame_stack, self.chunk_size, self.n_tokens = 2, 1, 2, 2
1773
  self.cfg = OmegaConf.create(
1774
- {"memory_selection": {"enabled": True, "max_anchor_frames": 1, "max_dynamic_frames": 1, "max_revisit_frames": 1, "fov_overlap_threshold": 0.75}}
 
 
 
1775
  )
1776
  self.diffusion_model = FakeDiffusion()
1777
  self.logged = []
@@ -1858,6 +1889,8 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
1858
  self.assertEqual(calls[0]["action_cond"][:, 0, 0].tolist(), [102.0, 103.0, 0.0, 0.0])
1859
  self.assertEqual(calls[0]["kwargs"]["frame_idx"][:, 0].tolist(), [12, 13, 10, 11])
1860
  self.assertEqual(calls[2]["kwargs"]["frame_idx"][:, 0].tolist(), [13, 14, 10, 12, 12])
 
 
1861
  self.assertEqual(calls[0]["kwargs"]["frame_memory_masks"]["revisit"].tolist(), [[]])
1862
  self.assertEqual(calls[2]["kwargs"]["frame_memory_masks"]["revisit"].tolist(), [[True]])
1863
  self.assertAlmostEqual(float(loss), 1.0 / 3.0)
 
48
  "pose_similarity_threshold": 0.6,
49
  "training_use_plucker": False,
50
  "training_plucker_weight": 0.1,
51
+ "training_dropout": 0.0,
52
  "fov_overlap_threshold": 0.0,
53
  "min_total_selected_coverage": 0.0,
54
  "local_context_exclusion_frames": 2,
 
220
  self.assertEqual(cfg.memory_selection.dynamic.multiview_selector, "fov_greedy")
221
  self.assertEqual(cfg.memory_selection.pose_preselect_topk, 32)
222
  self.assertEqual(cfg.memory_selection.candidate_chunk_size, 0)
223
+ self.assertEqual(cfg.memory_selection.training_dropout, 0.1)
224
 
225
  def test_revisit_uses_sampled_fov_selection(self):
226
  poses = np.array(
 
835
  self.assertEqual(indices["anchor"].tolist(), [2, 5])
836
  self.assertEqual(masks["anchor"].tolist(), [True, True])
837
 
838
+ def test_training_dropout_randomizes_memory_streams_with_replacement(self):
839
+ poses = _poses(12)
840
+ cfg = _selection_cfg(
841
+ max_anchor_frames=2,
842
+ max_dynamic_frames=3,
843
+ max_revisit_frames=2,
844
+ training_dropout=1.0,
845
+ dynamic=_dynamic_cfg("recent"),
846
+ )
847
+
848
+ indices, masks = select_memory_indices(
849
+ poses,
850
+ np.array([8]),
851
+ cfg,
852
+ split="training",
853
+ rng=np.random.default_rng(0),
854
+ anchor_candidate_start=2,
855
+ anchor_candidate_stop=6,
856
+ )
857
+
858
+ self.assertEqual(indices["anchor"].tolist(), [4, 3])
859
+ self.assertEqual(indices["dynamic"].tolist(), [1, 0, 0])
860
+ self.assertEqual(indices["revisit"].tolist(), [0, 1])
861
+ for key in ("anchor", "dynamic", "revisit"):
862
+ self.assertTrue(masks[key].all())
863
+
864
  def test_event_triggered_dynamic_causal_stream_stays_before_target(self):
865
  poses = _poses(12)
866
  cfg = _selection_cfg(
 
1799
  self.memory_condition_length, self.clip_noise = 3, 0.0
1800
  self.context_frames, self.frame_stack, self.chunk_size, self.n_tokens = 2, 1, 2, 2
1801
  self.cfg = OmegaConf.create(
1802
+ {
1803
+ "memory_selection": {"enabled": True, "max_anchor_frames": 1, "max_dynamic_frames": 1, "max_revisit_frames": 1, "fov_overlap_threshold": 0.75},
1804
+ "memory_noise": {"enabled": True, "validation_noisy_memory": False},
1805
+ }
1806
  )
1807
  self.diffusion_model = FakeDiffusion()
1808
  self.logged = []
 
1889
  self.assertEqual(calls[0]["action_cond"][:, 0, 0].tolist(), [102.0, 103.0, 0.0, 0.0])
1890
  self.assertEqual(calls[0]["kwargs"]["frame_idx"][:, 0].tolist(), [12, 13, 10, 11])
1891
  self.assertEqual(calls[2]["kwargs"]["frame_idx"][:, 0].tolist(), [13, 14, 10, 12, 12])
1892
+ self.assertEqual(calls[0]["curr_noise_level"][:, 0].tolist(), [2, 2, 0, 0])
1893
+ self.assertEqual(calls[2]["curr_noise_level"][:, 0].tolist(), [0, 2, 0, 0, 0])
1894
  self.assertEqual(calls[0]["kwargs"]["frame_memory_masks"]["revisit"].tolist(), [[]])
1895
  self.assertEqual(calls[2]["kwargs"]["frame_memory_masks"]["revisit"].tolist(), [[True]])
1896
  self.assertAlmostEqual(float(loss), 1.0 / 3.0)