BonanDing commited on
Commit
2c06b7e
·
1 Parent(s): 9fd9a97

Allow noncausal DeMemWM revisit selection

Browse files
.exp_artifact/dememwm_future_memory_selection_plan.md CHANGED
@@ -201,7 +201,7 @@ Add DeMemWM memory causality config
201
 
202
  ## Substep 2: Enable Future And Local-Window Revisit Selection
203
 
204
- Status: `[ ]`
205
 
206
  Goal:
207
 
 
201
 
202
  ## Substep 2: Enable Future And Local-Window Revisit Selection
203
 
204
+ Status: `[x]`
205
 
206
  Goal:
207
 
algorithms/dememwm/df_video.py CHANGED
@@ -1544,20 +1544,14 @@ class DeMemWMMinecraft(DiffusionForcingBase):
1544
  dynamic_count,
1545
  memory_selection_cfg,
1546
  )
1547
- revisit_excluded = (
1548
- np.concatenate([selected["anchor"], selected["dynamic"]])
1549
- if len(selected["anchor"]) or len(selected["dynamic"])
1550
- else np.empty((0,), dtype=np.int64)
1551
- )
1552
  else:
1553
  selected["dynamic"] = np.empty((0,), dtype=np.int64)
1554
- revisit_excluded = selected["anchor"]
1555
  selected["revisit"] = _select_revisit(
1556
  poses_np[:, batch_i, :5],
1557
  target_positions,
1558
  memory_selection_cfg,
1559
  revisit_count,
1560
- revisit_excluded,
1561
  namespace,
1562
  )
1563
  if dynamic_policy == "event_triggered":
 
1544
  dynamic_count,
1545
  memory_selection_cfg,
1546
  )
 
 
 
 
 
1547
  else:
1548
  selected["dynamic"] = np.empty((0,), dtype=np.int64)
 
1549
  selected["revisit"] = _select_revisit(
1550
  poses_np[:, batch_i, :5],
1551
  target_positions,
1552
  memory_selection_cfg,
1553
  revisit_count,
1554
+ np.empty((0,), dtype=np.int64),
1555
  namespace,
1556
  )
1557
  if dynamic_policy == "event_triggered":
datasets/video/memory_selection.py CHANGED
@@ -785,10 +785,13 @@ def _select_revisit(
785
  if count <= 0:
786
  return np.empty((0,), dtype=np.int64)
787
 
788
- target_start = int(target_positions[0])
789
- exclusion = max(0, int(cfg_get(cfg, "local_context_exclusion_frames", 8)))
790
- causal_stop = max(0, target_start - exclusion)
791
- candidates = np.arange(min_candidate_frame, causal_stop, dtype=np.int64)
 
 
 
792
  if len(candidates) == 0:
793
  return np.empty((0,), dtype=np.int64)
794
 
@@ -868,15 +871,12 @@ def select_memory_indices(
868
  anchor_candidates = anchor_candidates[~np.isin(anchor_candidates, dynamic)]
869
  anchor = _select_anchor(anchor_candidates, counts["anchor"], cfg, poses=poses)
870
 
871
- revisit_excluded = anchor if policy == "event_triggered" else (
872
- np.concatenate([anchor, dynamic]) if len(anchor) or len(dynamic) else np.empty((0,), dtype=np.int64)
873
- )
874
  revisit = _select_revisit(
875
  poses,
876
  target_positions,
877
  cfg,
878
  counts["revisit"],
879
- revisit_excluded,
880
  split,
881
  rng=rng,
882
  min_candidate_frame=min_candidate_frame,
 
785
  if count <= 0:
786
  return np.empty((0,), dtype=np.int64)
787
 
788
+ candidates = _memory_candidate_frames(
789
+ len(poses),
790
+ target_positions,
791
+ cfg,
792
+ split,
793
+ min_candidate_frame=min_candidate_frame,
794
+ )
795
  if len(candidates) == 0:
796
  return np.empty((0,), dtype=np.int64)
797
 
 
871
  anchor_candidates = anchor_candidates[~np.isin(anchor_candidates, dynamic)]
872
  anchor = _select_anchor(anchor_candidates, counts["anchor"], cfg, poses=poses)
873
 
 
 
 
874
  revisit = _select_revisit(
875
  poses,
876
  target_positions,
877
  cfg,
878
  counts["revisit"],
879
+ np.empty((0,), dtype=np.int64),
880
  split,
881
  rng=rng,
882
  min_candidate_frame=min_candidate_frame,
tests/test_dememwm_latent_dataset.py CHANGED
@@ -232,17 +232,137 @@ class MemorySelectionTests(unittest.TestCase):
232
  self.assertEqual(indices["revisit"].tolist(), [0, -1])
233
  self.assertEqual(masks["revisit"].tolist(), [True, False])
234
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  def test_dememwm_selection_is_typed_and_causal(self):
236
  poses = _poses(12)
237
  indices, masks = select_memory_indices(poses, np.array([6, 7, 8]), _selection_cfg())
238
 
239
  self.assertEqual(indices["anchor"].tolist(), [0, 1])
240
  self.assertEqual(indices["dynamic"].tolist(), [4, 5])
241
- self.assertTrue(np.all(indices["revisit"][masks["revisit"]] < 4))
242
  for key in ("anchor", "dynamic", "revisit"):
243
  self.assertTrue(np.all(indices[key][masks[key]] < 6))
244
- selected = np.concatenate([indices[key][masks[key]] for key in ("anchor", "dynamic", "revisit")])
245
- self.assertEqual(len(selected), len(np.unique(selected)))
246
 
247
  def test_anchor_selection_is_context_bounded_farthest_point(self):
248
  poses = _poses(10)
@@ -950,7 +1070,7 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
950
 
951
  self.assertEqual(harness.horizons, [2, 1])
952
  self.assertEqual(anchor_calls, [(2, 1, (2, 5))])
953
- self.assertEqual(revisit_calls, [([2, 3], "test", 0.75, [0, 1]), ([4], "test", 0.75, [0, 2])])
954
  self.assertEqual(len(revisit_calls), len(harness.horizons))
955
  self.assertEqual(len(calls), 4)
956
  self.assertTrue(all(required_kwargs <= call["kwargs"].keys() for call in calls))
@@ -963,10 +1083,10 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
963
  self.assertTrue(torch.equal(calls[0]["kwargs"]["frame_idx"], calls[1]["kwargs"]["frame_idx"]))
964
  self.assertTrue(torch.equal(calls[2]["kwargs"]["frame_idx"], calls[3]["kwargs"]["frame_idx"]))
965
  self.assertEqual(calls[0]["x"][:, 0, 0, 0, 0].tolist(), [0.0, 0.0, 0.0, 1.0, 0.0])
966
- self.assertEqual(calls[2]["x"][:, 0, 0, 0, 0].tolist(), [2.0, 0.0, 0.0, 2.0, 1.0])
967
  self.assertEqual(calls[0]["action_cond"][:, 0, 0].tolist(), [102.0, 103.0, 0.0, 0.0, 0.0])
968
  self.assertEqual(calls[0]["kwargs"]["frame_idx"][:, 0].tolist(), [12, 13, 10, 11, 0])
969
- self.assertEqual(calls[2]["kwargs"]["frame_idx"][:, 0].tolist(), [13, 14, 10, 12, 11])
970
  self.assertEqual(calls[0]["kwargs"]["frame_memory_masks"]["revisit"].tolist(), [[False]])
971
  self.assertEqual(calls[2]["kwargs"]["frame_memory_masks"]["revisit"].tolist(), [[True]])
972
  self.assertAlmostEqual(float(loss), 1.0 / 3.0)
@@ -1209,7 +1329,7 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
1209
  actions=np.ones((num_frames, 25), dtype=np.float32),
1210
  )
1211
  dataset = MinecraftVideoDeMemWMLatentDataset(
1212
- _dataset_cfg(root, context_length=6, memory_selection=_selection_cfg()),
1213
  split="training",
1214
  )
1215
  sample = dataset[0]
@@ -1220,7 +1340,7 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
1220
  self.assertEqual(sample["frame_indices"][3:5].tolist(), [100, 101])
1221
  self.assertEqual(sample["frame_indices"][5:7].tolist(), [104, 105])
1222
  revisit_frames = sample["frame_indices"][7:9].numpy()
1223
- self.assertEqual(revisit_frames.tolist(), [102, 103])
1224
  self.assertEqual(len(np.unique(revisit_frames)), 2)
1225
  self.assertTrue(sample["memory_masks"]["target"].all().item())
1226
  self.assertTrue(sample["memory_masks"]["anchor"].all().item())
 
232
  self.assertEqual(indices["revisit"].tolist(), [0, -1])
233
  self.assertEqual(masks["revisit"].tolist(), [True, False])
234
 
235
+ def test_training_default_causal_revisit_does_not_select_future(self):
236
+ poses = _poses(8)
237
+ poses[6] = poses[4]
238
+ cfg = _selection_cfg(
239
+ max_anchor_frames=0,
240
+ max_dynamic_frames=0,
241
+ max_revisit_frames=1,
242
+ pose_similarity_threshold=0.0,
243
+ )
244
+
245
+ indices, masks = select_memory_indices(
246
+ poses,
247
+ np.array([4]),
248
+ cfg,
249
+ split="training",
250
+ rng=np.random.default_rng(0),
251
+ )
252
+
253
+ selected = indices["revisit"][masks["revisit"]]
254
+ self.assertTrue(len(selected) > 0)
255
+ self.assertTrue(np.all(selected < 4))
256
+
257
+ def test_training_noncausal_revisit_can_select_future_pose_match(self):
258
+ poses = np.zeros((8, 5), dtype=np.float32)
259
+ poses[:, 0] = 100.0
260
+ poses[:, 4] = 180.0
261
+ poses[4] = np.asarray([0, 0, 0, 0, 0], dtype=np.float32)
262
+ poses[6] = np.asarray([0, 0, 0, 0, 0], dtype=np.float32)
263
+ cfg = _selection_cfg(
264
+ causal=False,
265
+ max_anchor_frames=0,
266
+ max_dynamic_frames=0,
267
+ max_revisit_frames=2,
268
+ pose_similarity_threshold=0.95,
269
+ )
270
+
271
+ indices, masks = select_memory_indices(
272
+ poses,
273
+ np.array([4]),
274
+ cfg,
275
+ split="training",
276
+ rng=np.random.default_rng(0),
277
+ )
278
+
279
+ selected = indices["revisit"][masks["revisit"]]
280
+ self.assertIn(6, selected.tolist())
281
+
282
+ def test_training_noncausal_revisit_can_select_target_window_frame(self):
283
+ poses = np.zeros((8, 5), dtype=np.float32)
284
+ poses[:, 0] = 100.0
285
+ poses[:, 4] = 180.0
286
+ poses[4] = np.asarray([0, 0, 0, 0, 0], dtype=np.float32)
287
+ cfg = _selection_cfg(
288
+ causal=False,
289
+ max_anchor_frames=0,
290
+ max_dynamic_frames=0,
291
+ max_revisit_frames=1,
292
+ pose_similarity_threshold=0.95,
293
+ )
294
+
295
+ indices, masks = select_memory_indices(
296
+ poses,
297
+ np.array([4]),
298
+ cfg,
299
+ split="training",
300
+ rng=np.random.default_rng(0),
301
+ )
302
+
303
+ self.assertEqual(indices["revisit"].tolist(), [4])
304
+ self.assertEqual(masks["revisit"].tolist(), [True])
305
+
306
+ def test_validation_and_test_noncausal_revisit_stays_past_only(self):
307
+ import datasets.video.memory_selection as memory_selection
308
+
309
+ poses = _poses(8)
310
+ cfg = _selection_cfg(
311
+ causal=False,
312
+ max_anchor_frames=0,
313
+ max_dynamic_frames=0,
314
+ max_revisit_frames=1,
315
+ )
316
+
317
+ for split in ("validation", "test"):
318
+ seen_candidates = []
319
+
320
+ def fake_point_union(poses_arg, candidates, target_positions, cfg_arg, count, **kwargs):
321
+ seen_candidates.append(candidates.copy())
322
+ return candidates[-count:].astype(np.int64, copy=False)
323
+
324
+ with self.subTest(split=split), mock.patch.object(memory_selection, "_select_by_point_union", side_effect=fake_point_union):
325
+ indices, masks = select_memory_indices(poses, np.array([4]), cfg, split=split)
326
+
327
+ self.assertEqual([candidates.tolist() for candidates in seen_candidates], [[0, 1, 2, 3]])
328
+ self.assertEqual(indices["revisit"].tolist(), [3])
329
+ self.assertEqual(masks["revisit"].tolist(), [True])
330
+
331
+ def test_revisit_can_duplicate_anchor_and_dynamic_when_scored_best(self):
332
+ poses = np.zeros((8, 5), dtype=np.float32)
333
+ poses[:, 0] = 100.0
334
+ poses[:, 4] = 180.0
335
+ poses[0] = np.asarray([0, 0, 0, 0, 0], dtype=np.float32)
336
+ poses[5] = np.asarray([0, 0, 0, 0, 0], dtype=np.float32)
337
+ poses[6] = np.asarray([0, 0, 0, 0, 0], dtype=np.float32)
338
+ cfg = _selection_cfg(
339
+ max_anchor_frames=1,
340
+ max_dynamic_frames=1,
341
+ max_revisit_frames=2,
342
+ pose_similarity_threshold=0.95,
343
+ )
344
+
345
+ indices, masks = select_memory_indices(
346
+ poses,
347
+ np.array([6]),
348
+ cfg,
349
+ split="training",
350
+ rng=np.random.default_rng(0),
351
+ )
352
+
353
+ self.assertEqual(indices["anchor"].tolist(), [0])
354
+ self.assertEqual(indices["dynamic"].tolist(), [5])
355
+ self.assertEqual(indices["revisit"].tolist(), [0, 5])
356
+ self.assertTrue(masks["revisit"].all())
357
+
358
  def test_dememwm_selection_is_typed_and_causal(self):
359
  poses = _poses(12)
360
  indices, masks = select_memory_indices(poses, np.array([6, 7, 8]), _selection_cfg())
361
 
362
  self.assertEqual(indices["anchor"].tolist(), [0, 1])
363
  self.assertEqual(indices["dynamic"].tolist(), [4, 5])
 
364
  for key in ("anchor", "dynamic", "revisit"):
365
  self.assertTrue(np.all(indices[key][masks[key]] < 6))
 
 
366
 
367
  def test_anchor_selection_is_context_bounded_farthest_point(self):
368
  poses = _poses(10)
 
1070
 
1071
  self.assertEqual(harness.horizons, [2, 1])
1072
  self.assertEqual(anchor_calls, [(2, 1, (2, 5))])
1073
+ self.assertEqual(revisit_calls, [([2, 3], "test", 0.75, []), ([4], "test", 0.75, [])])
1074
  self.assertEqual(len(revisit_calls), len(harness.horizons))
1075
  self.assertEqual(len(calls), 4)
1076
  self.assertTrue(all(required_kwargs <= call["kwargs"].keys() for call in calls))
 
1083
  self.assertTrue(torch.equal(calls[0]["kwargs"]["frame_idx"], calls[1]["kwargs"]["frame_idx"]))
1084
  self.assertTrue(torch.equal(calls[2]["kwargs"]["frame_idx"], calls[3]["kwargs"]["frame_idx"]))
1085
  self.assertEqual(calls[0]["x"][:, 0, 0, 0, 0].tolist(), [0.0, 0.0, 0.0, 1.0, 0.0])
1086
+ self.assertEqual(calls[2]["x"][:, 0, 0, 0, 0].tolist(), [2.0, 0.0, 0.0, 2.0, 2.0])
1087
  self.assertEqual(calls[0]["action_cond"][:, 0, 0].tolist(), [102.0, 103.0, 0.0, 0.0, 0.0])
1088
  self.assertEqual(calls[0]["kwargs"]["frame_idx"][:, 0].tolist(), [12, 13, 10, 11, 0])
1089
+ self.assertEqual(calls[2]["kwargs"]["frame_idx"][:, 0].tolist(), [13, 14, 10, 12, 12])
1090
  self.assertEqual(calls[0]["kwargs"]["frame_memory_masks"]["revisit"].tolist(), [[False]])
1091
  self.assertEqual(calls[2]["kwargs"]["frame_memory_masks"]["revisit"].tolist(), [[True]])
1092
  self.assertAlmostEqual(float(loss), 1.0 / 3.0)
 
1329
  actions=np.ones((num_frames, 25), dtype=np.float32),
1330
  )
1331
  dataset = MinecraftVideoDeMemWMLatentDataset(
1332
+ _dataset_cfg(root, context_length=6, memory_selection=_selection_cfg(training_use_plucker=True)),
1333
  split="training",
1334
  )
1335
  sample = dataset[0]
 
1340
  self.assertEqual(sample["frame_indices"][3:5].tolist(), [100, 101])
1341
  self.assertEqual(sample["frame_indices"][5:7].tolist(), [104, 105])
1342
  revisit_frames = sample["frame_indices"][7:9].numpy()
1343
+ self.assertEqual(revisit_frames.tolist(), [104, 105])
1344
  self.assertEqual(len(np.unique(revisit_frames)), 2)
1345
  self.assertTrue(sample["memory_masks"]["target"].all().item())
1346
  self.assertTrue(sample["memory_masks"]["anchor"].all().item())