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 |
-
|
| 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 |
-
|
| 789 |
-
|
| 790 |
-
|
| 791 |
-
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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, [
|
| 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,
|
| 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,
|
| 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(), [
|
| 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())
|