Implement DeMemWM online validation memory
Browse files- algorithms/dememwm/df_video.py +110 -58
- tests/test_dememwm_latent_dataset.py +39 -49
algorithms/dememwm/df_video.py
CHANGED
|
@@ -17,6 +17,7 @@ from lightning.pytorch.utilities.types import STEP_OUTPUT
|
|
| 17 |
from algorithms.common.metrics import (
|
| 18 |
LearnedPerceptualImagePatchSimilarity,
|
| 19 |
)
|
|
|
|
| 20 |
from utils.logging_utils import log_video, get_validation_metrics_for_videos
|
| 21 |
from .df_base import DiffusionForcingBase
|
| 22 |
from .models.vae import VAE_models
|
|
@@ -151,6 +152,19 @@ def _preprocess_dememwm_latent_batch(batch):
|
|
| 151 |
}
|
| 152 |
|
| 153 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
def euler_to_rotation_matrix(pitch, yaw):
|
| 155 |
"""
|
| 156 |
Convert pitch and yaw angles (in radians) to a 3x3 rotation matrix.
|
|
@@ -904,76 +918,114 @@ class DeMemWMMinecraft(DiffusionForcingBase):
|
|
| 904 |
memory_condition_length = self.memory_condition_length
|
| 905 |
preprocessed = self._preprocess_batch(batch)
|
| 906 |
if isinstance(preprocessed, Mapping):
|
| 907 |
-
|
|
|
|
| 908 |
target_length = preprocessed["target_length"]
|
| 909 |
batch_size = xs.shape[1]
|
| 910 |
-
conditions = preprocessed["action_conditions"].to(device=xs.device)
|
| 911 |
-
frame_indices = preprocessed["frame_indices"].to(device=xs.device)
|
| 912 |
-
frame_memory_pose = preprocessed["frame_memory_pose"].to(device=xs.device, dtype=xs.dtype)
|
| 913 |
image_hw = preprocessed["image_hw"].to(device=xs.device)
|
| 914 |
-
|
| 915 |
-
|
| 916 |
-
|
| 917 |
-
|
|
|
|
|
|
|
|
|
|
| 918 |
|
| 919 |
xs_pred = xs.clone()
|
| 920 |
-
|
| 921 |
-
|
| 922 |
-
xs_pred[:target_length] = torch.randn_like(xs_pred[:target_length]).clamp(
|
| 923 |
-
-self.clip_noise,
|
| 924 |
-
self.clip_noise,
|
| 925 |
-
)
|
| 926 |
-
scheduling_matrix = self._generate_scheduling_matrix(target_length)
|
| 927 |
-
memory_length = xs.shape[0] - target_length
|
| 928 |
-
memory_noise_levels = torch.zeros(
|
| 929 |
-
(memory_length, batch_size),
|
| 930 |
-
device=xs.device,
|
| 931 |
-
dtype=torch.long,
|
| 932 |
-
)
|
| 933 |
|
| 934 |
-
|
| 935 |
-
|
| 936 |
-
|
| 937 |
-
|
| 938 |
-
|
| 939 |
-
|
| 940 |
-
|
| 941 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 942 |
device=xs.device,
|
| 943 |
dtype=torch.long,
|
| 944 |
-
)[:, None].repeat(1, batch_size)
|
| 945 |
-
from_noise_levels = torch.cat([from_target, memory_noise_levels], dim=0)
|
| 946 |
-
to_noise_levels = torch.cat([to_target, memory_noise_levels], dim=0)
|
| 947 |
-
|
| 948 |
-
sampled_target = self.diffusion_model.sample_step(
|
| 949 |
-
xs_pred,
|
| 950 |
-
conditions,
|
| 951 |
-
None,
|
| 952 |
-
from_noise_levels,
|
| 953 |
-
to_noise_levels,
|
| 954 |
-
current_frame=0,
|
| 955 |
-
mode=namespace,
|
| 956 |
-
reference_length=0,
|
| 957 |
-
frame_idx=frame_indices,
|
| 958 |
-
frame_memory_segments=preprocessed["memory_segments"],
|
| 959 |
-
frame_memory_masks=frame_memory_masks,
|
| 960 |
-
frame_memory_pose=frame_memory_pose,
|
| 961 |
-
image_hw=image_hw,
|
| 962 |
)
|
| 963 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 964 |
|
| 965 |
-
|
| 966 |
-
|
| 967 |
-
|
| 968 |
-
|
| 969 |
-
)
|
| 970 |
-
target_mask = frame_memory_masks.get("target")
|
| 971 |
if target_mask is not None:
|
| 972 |
-
|
| 973 |
-
|
| 974 |
-
latent_loss = (latent_loss *
|
| 975 |
-
|
| 976 |
latent_loss = latent_loss.mean()
|
|
|
|
|
|
|
| 977 |
|
| 978 |
self.log(f"{namespace}/latent_mse", latent_loss.detach())
|
| 979 |
return latent_loss
|
|
|
|
| 17 |
from algorithms.common.metrics import (
|
| 18 |
LearnedPerceptualImagePatchSimilarity,
|
| 19 |
)
|
| 20 |
+
from datasets.video.memory_selection import select_memory_indices
|
| 21 |
from utils.logging_utils import log_video, get_validation_metrics_for_videos
|
| 22 |
from .df_base import DiffusionForcingBase
|
| 23 |
from .models.vae import VAE_models
|
|
|
|
| 152 |
}
|
| 153 |
|
| 154 |
|
| 155 |
+
def _gather_online_memory_tensor(source, indices, masks):
|
| 156 |
+
output_shape = (indices.shape[1], indices.shape[0], *source.shape[2:])
|
| 157 |
+
if output_shape[0] == 0 or source.shape[0] == 0:
|
| 158 |
+
return source.new_zeros(output_shape)
|
| 159 |
+
|
| 160 |
+
gather_idx = indices.clamp(0, source.shape[0] - 1).T.to(source.device)
|
| 161 |
+
batch_idx = torch.arange(indices.shape[0], device=source.device).expand_as(gather_idx)
|
| 162 |
+
gathered = source[gather_idx, batch_idx]
|
| 163 |
+
mask = masks.T.to(device=source.device, dtype=torch.bool)
|
| 164 |
+
mask = mask.view(*mask.shape, *((1,) * (gathered.ndim - 2)))
|
| 165 |
+
return gathered * mask.to(dtype=gathered.dtype)
|
| 166 |
+
|
| 167 |
+
|
| 168 |
def euler_to_rotation_matrix(pitch, yaw):
|
| 169 |
"""
|
| 170 |
Convert pitch and yaw angles (in radians) to a 3x3 rotation matrix.
|
|
|
|
| 918 |
memory_condition_length = self.memory_condition_length
|
| 919 |
preprocessed = self._preprocess_batch(batch)
|
| 920 |
if isinstance(preprocessed, Mapping):
|
| 921 |
+
target_tensors = preprocessed["target_tensors"]
|
| 922 |
+
xs = target_tensors["latents"]
|
| 923 |
target_length = preprocessed["target_length"]
|
| 924 |
batch_size = xs.shape[1]
|
|
|
|
|
|
|
|
|
|
| 925 |
image_hw = preprocessed["image_hw"].to(device=xs.device)
|
| 926 |
+
target_mask = preprocessed["memory_masks"].get("target")
|
| 927 |
+
target_mask = None if target_mask is None else target_mask.to(device=xs.device)
|
| 928 |
+
target_poses = target_tensors["poses"].to(device=xs.device, dtype=xs.dtype)
|
| 929 |
+
target_frame_indices = target_tensors["frame_indices"].to(device=xs.device)
|
| 930 |
+
stream_lengths = preprocessed["stream_lengths"]
|
| 931 |
+
memory_selection_cfg = _cfg_get(self.cfg, "memory_selection", {})
|
| 932 |
+
poses_np = target_poses.detach().cpu().numpy()
|
| 933 |
|
| 934 |
xs_pred = xs.clone()
|
| 935 |
+
n_context_frames = min(self.context_frames // self.frame_stack, target_length)
|
| 936 |
+
curr_frame = n_context_frames
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 937 |
|
| 938 |
+
while curr_frame < target_length:
|
| 939 |
+
horizon = min(target_length - curr_frame, self.chunk_size) if self.chunk_size > 0 else target_length - curr_frame
|
| 940 |
+
target_slice = slice(curr_frame, curr_frame + horizon)
|
| 941 |
+
xs_pred[target_slice] = torch.randn_like(xs_pred[target_slice]).clamp(-self.clip_noise, self.clip_noise)
|
| 942 |
+
|
| 943 |
+
stream_indices = {
|
| 944 |
+
key: torch.full((batch_size, int(stream_lengths[key])), -1, device=xs.device, dtype=torch.long)
|
| 945 |
+
for key in _DEMEMWM_STREAM_KEYS
|
| 946 |
+
}
|
| 947 |
+
stream_masks = {key: torch.zeros_like(indices, dtype=torch.bool) for key, indices in stream_indices.items()}
|
| 948 |
+
target_positions = np.arange(target_slice.start, target_slice.stop, dtype=np.int64)
|
| 949 |
+
for batch_i in range(batch_size):
|
| 950 |
+
selected, selected_masks = select_memory_indices(
|
| 951 |
+
poses_np[:, batch_i, :5],
|
| 952 |
+
target_positions,
|
| 953 |
+
memory_selection_cfg,
|
| 954 |
+
split=namespace,
|
| 955 |
+
)
|
| 956 |
+
for key, indices in stream_indices.items():
|
| 957 |
+
count = indices.shape[1]
|
| 958 |
+
selected_key = np.asarray(selected.get(key, []), dtype=np.int64)[:count]
|
| 959 |
+
mask_key = np.asarray(selected_masks.get(key, []), dtype=bool)[:count]
|
| 960 |
+
indices[batch_i, : len(selected_key)] = torch.as_tensor(selected_key, device=xs.device, dtype=torch.long)
|
| 961 |
+
stream_masks[key][batch_i, : len(mask_key)] = torch.as_tensor(mask_key, device=xs.device, dtype=torch.bool)
|
| 962 |
+
|
| 963 |
+
source_latents = xs_pred[:curr_frame]
|
| 964 |
+
source_poses = target_poses[:curr_frame]
|
| 965 |
+
source_frame_indices = target_frame_indices[:curr_frame]
|
| 966 |
+
frame_memory_masks = {
|
| 967 |
+
"target": torch.ones((batch_size, horizon), device=xs.device, dtype=torch.bool)
|
| 968 |
+
if target_mask is None
|
| 969 |
+
else target_mask[:, target_slice],
|
| 970 |
+
**stream_masks,
|
| 971 |
+
}
|
| 972 |
+
stream_latents = [_gather_online_memory_tensor(source_latents, stream_indices[key], stream_masks[key]) for key in _DEMEMWM_STREAM_KEYS]
|
| 973 |
+
stream_poses = [_gather_online_memory_tensor(source_poses, stream_indices[key], stream_masks[key]) for key in _DEMEMWM_STREAM_KEYS]
|
| 974 |
+
stream_frame_indices = [_gather_online_memory_tensor(source_frame_indices, stream_indices[key], stream_masks[key]) for key in _DEMEMWM_STREAM_KEYS]
|
| 975 |
+
target_conditions = target_tensors["action_conditions"][target_slice].to(device=xs.device)
|
| 976 |
+
memory_length = sum(mask.shape[1] for mask in stream_masks.values())
|
| 977 |
+
|
| 978 |
+
packed_latents = torch.cat([xs_pred[target_slice], *stream_latents], dim=0)
|
| 979 |
+
packed_conditions = torch.cat(
|
| 980 |
+
[target_conditions, target_conditions.new_zeros((memory_length, batch_size, target_conditions.shape[-1]))],
|
| 981 |
+
dim=0,
|
| 982 |
+
)
|
| 983 |
+
frame_memory_pose = torch.cat([target_poses[target_slice], *stream_poses], dim=0)
|
| 984 |
+
frame_indices = torch.cat([target_frame_indices[target_slice], *stream_frame_indices], dim=0)
|
| 985 |
+
frame_memory_segments = {
|
| 986 |
+
"target": int(horizon),
|
| 987 |
+
**{key: int(stream_masks[key].shape[1]) for key in _DEMEMWM_STREAM_KEYS},
|
| 988 |
+
}
|
| 989 |
+
memory_noise_levels = torch.zeros(
|
| 990 |
+
(packed_latents.shape[0] - horizon, batch_size),
|
| 991 |
device=xs.device,
|
| 992 |
dtype=torch.long,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 993 |
)
|
| 994 |
+
scheduling_matrix = self._generate_scheduling_matrix(horizon)
|
| 995 |
+
|
| 996 |
+
for m in range(scheduling_matrix.shape[0] - 1):
|
| 997 |
+
from_target = torch.as_tensor(scheduling_matrix[m], device=xs.device, dtype=torch.long)[:, None].repeat(1, batch_size)
|
| 998 |
+
to_target = torch.as_tensor(scheduling_matrix[m + 1], device=xs.device, dtype=torch.long)[:, None].repeat(1, batch_size)
|
| 999 |
+
sampled_target = self.diffusion_model.sample_step(
|
| 1000 |
+
packed_latents,
|
| 1001 |
+
packed_conditions,
|
| 1002 |
+
None,
|
| 1003 |
+
torch.cat([from_target, memory_noise_levels], dim=0),
|
| 1004 |
+
torch.cat([to_target, memory_noise_levels], dim=0),
|
| 1005 |
+
current_frame=curr_frame,
|
| 1006 |
+
mode=namespace,
|
| 1007 |
+
reference_length=0,
|
| 1008 |
+
frame_idx=frame_indices,
|
| 1009 |
+
frame_memory_segments=frame_memory_segments,
|
| 1010 |
+
frame_memory_masks=frame_memory_masks,
|
| 1011 |
+
frame_memory_pose=frame_memory_pose,
|
| 1012 |
+
image_hw=image_hw,
|
| 1013 |
+
)
|
| 1014 |
+
packed_latents[:horizon] = sampled_target[:horizon]
|
| 1015 |
|
| 1016 |
+
xs_pred[target_slice] = packed_latents[:horizon]
|
| 1017 |
+
curr_frame += horizon
|
| 1018 |
+
|
| 1019 |
+
eval_start = n_context_frames
|
| 1020 |
+
latent_loss = F.mse_loss(xs_pred[eval_start:target_length], xs[eval_start:target_length], reduction="none")
|
|
|
|
| 1021 |
if target_mask is not None:
|
| 1022 |
+
loss_mask = rearrange(target_mask[:, eval_start:target_length].to(dtype=latent_loss.dtype), "b t -> t b")
|
| 1023 |
+
loss_mask = loss_mask.view(*loss_mask.shape, *((1,) * (latent_loss.ndim - 2)))
|
| 1024 |
+
latent_loss = (latent_loss * loss_mask).sum() / loss_mask.expand_as(latent_loss).sum().clamp_min(1.0)
|
| 1025 |
+
elif latent_loss.numel():
|
| 1026 |
latent_loss = latent_loss.mean()
|
| 1027 |
+
else:
|
| 1028 |
+
latent_loss = xs_pred.new_tensor(0.0)
|
| 1029 |
|
| 1030 |
self.log(f"{namespace}/latent_mse", latent_loss.detach())
|
| 1031 |
return latent_loss
|
tests/test_dememwm_latent_dataset.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
import tempfile
|
| 2 |
import unittest
|
|
|
|
| 3 |
from pathlib import Path
|
| 4 |
|
| 5 |
import numpy as np
|
|
@@ -300,9 +301,17 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
|
|
| 300 |
self.assertEqual(harness.logged[0][0], "training/loss")
|
| 301 |
self.assertEqual(float(harness.logged[0][1]), 2.0)
|
| 302 |
|
| 303 |
-
def
|
|
|
|
| 304 |
from algorithms.dememwm.df_video import DeMemWMMinecraft, _preprocess_dememwm_latent_batch
|
| 305 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
class FakeDiffusion:
|
| 307 |
def __init__(self):
|
| 308 |
self.calls = []
|
|
@@ -318,16 +327,15 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
|
|
| 318 |
"kwargs": kwargs,
|
| 319 |
}
|
| 320 |
)
|
| 321 |
-
|
| 322 |
-
sampled = x.clone()
|
| 323 |
-
sampled[:target_length] = float(len(self.calls))
|
| 324 |
-
sampled[target_length:] = 999.0
|
| 325 |
-
return sampled
|
| 326 |
|
| 327 |
class Harness:
|
| 328 |
def __init__(self):
|
| 329 |
-
self.memory_condition_length = 3
|
| 330 |
-
self.
|
|
|
|
|
|
|
|
|
|
| 331 |
self.diffusion_model = FakeDiffusion()
|
| 332 |
self.logged = []
|
| 333 |
self.horizons = []
|
|
@@ -337,10 +345,10 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
|
|
| 337 |
|
| 338 |
def _generate_scheduling_matrix(self, horizon):
|
| 339 |
self.horizons.append(horizon)
|
| 340 |
-
return np.
|
| 341 |
|
| 342 |
def _generate_condition_indices(self, *args, **kwargs):
|
| 343 |
-
raise AssertionError("latent dict validation must not
|
| 344 |
|
| 345 |
def encode(self, xs):
|
| 346 |
raise AssertionError("latent dict validation must not VAE-encode inputs")
|
|
@@ -352,51 +360,33 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
|
|
| 352 |
self.logged.append((name, value))
|
| 353 |
|
| 354 |
batch = {
|
| 355 |
-
"latents": torch.
|
| 356 |
-
"actions": (100 + torch.arange(
|
| 357 |
-
"poses": (200 + torch.arange(
|
| 358 |
-
"frame_indices": (10
|
| 359 |
-
"memory_segments": {
|
| 360 |
-
|
| 361 |
-
"anchor": torch.tensor([1]),
|
| 362 |
-
"dynamic": torch.tensor([1]),
|
| 363 |
-
"revisit": torch.tensor([1]),
|
| 364 |
-
},
|
| 365 |
-
"memory_masks": {
|
| 366 |
-
"target": torch.tensor([[True, False]]),
|
| 367 |
-
"anchor": torch.tensor([[True]]),
|
| 368 |
-
"dynamic": torch.tensor([[False]]),
|
| 369 |
-
"revisit": torch.tensor([[True]]),
|
| 370 |
-
},
|
| 371 |
"image_hw": torch.tensor([[360, 640]], dtype=torch.long),
|
| 372 |
}
|
| 373 |
harness = Harness()
|
| 374 |
|
| 375 |
-
|
|
|
|
| 376 |
calls = harness.diffusion_model.calls
|
|
|
|
| 377 |
|
| 378 |
-
self.assertEqual(harness.horizons, [2])
|
| 379 |
-
self.
|
| 380 |
-
for call in calls
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
self.assertEqual(call["kwargs"]["frame_memory_pose"][:, 0, 0].tolist(), [200.0, 201.0, 202.0, 203.0, 204.0])
|
| 391 |
-
self.assertEqual(call["kwargs"]["frame_memory_masks"]["dynamic"].tolist(), [[False]])
|
| 392 |
-
self.assertEqual(call["kwargs"]["image_hw"].tolist(), [[360, 640]])
|
| 393 |
-
self.assertEqual(calls[0]["curr_noise_level"][:, 0].tolist(), [2, 2, 0, 0, 0])
|
| 394 |
-
self.assertEqual(calls[0]["next_noise_level"][:, 0].tolist(), [1, 1, 0, 0, 0])
|
| 395 |
-
self.assertEqual(calls[1]["curr_noise_level"][:, 0].tolist(), [1, 1, 0, 0, 0])
|
| 396 |
-
self.assertEqual(calls[1]["next_noise_level"][:, 0].tolist(), [0, 0, 0, 0, 0])
|
| 397 |
-
self.assertEqual(float(loss), 4.0)
|
| 398 |
-
self.assertEqual(harness.logged[0][0], "test/latent_mse")
|
| 399 |
-
self.assertEqual(float(harness.logged[0][1]), 4.0)
|
| 400 |
|
| 401 |
def test_dataset_returns_target_anchor_dynamic_revisit_contract(self):
|
| 402 |
with tempfile.TemporaryDirectory() as tmp:
|
|
|
|
| 1 |
import tempfile
|
| 2 |
import unittest
|
| 3 |
+
from unittest import mock
|
| 4 |
from pathlib import Path
|
| 5 |
|
| 6 |
import numpy as np
|
|
|
|
| 301 |
self.assertEqual(harness.logged[0][0], "training/loss")
|
| 302 |
self.assertEqual(float(harness.logged[0][1]), 2.0)
|
| 303 |
|
| 304 |
+
def test_validation_step_builds_online_memory_from_committed_latents(self):
|
| 305 |
+
import algorithms.dememwm.df_video as df_video
|
| 306 |
from algorithms.dememwm.df_video import DeMemWMMinecraft, _preprocess_dememwm_latent_batch
|
| 307 |
|
| 308 |
+
selection_calls = []
|
| 309 |
+
|
| 310 |
+
def fake_select_memory_indices(poses, target_positions, cfg=None, split="training", rng=None):
|
| 311 |
+
start = int(target_positions[0])
|
| 312 |
+
selection_calls.append((target_positions.tolist(), split, float(cfg.fov_overlap_threshold)))
|
| 313 |
+
return {"anchor": [0], "dynamic": [max(0, start - 1)], "revisit": [max(0, start - 2)]}, {"anchor": [start > 0], "dynamic": [start > 0], "revisit": [start > 2]}
|
| 314 |
+
|
| 315 |
class FakeDiffusion:
|
| 316 |
def __init__(self):
|
| 317 |
self.calls = []
|
|
|
|
| 327 |
"kwargs": kwargs,
|
| 328 |
}
|
| 329 |
)
|
| 330 |
+
return torch.full_like(x[: kwargs["frame_memory_segments"]["target"]], float(len(self.calls)))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
|
| 332 |
class Harness:
|
| 333 |
def __init__(self):
|
| 334 |
+
self.memory_condition_length, self.clip_noise = 3, 0.0
|
| 335 |
+
self.context_frames, self.frame_stack, self.chunk_size = 2, 1, 2
|
| 336 |
+
self.cfg = OmegaConf.create(
|
| 337 |
+
{"memory_selection": {"enabled": True, "max_anchor_frames": 1, "max_dynamic_frames": 1, "max_revisit_frames": 1, "fov_overlap_threshold": 0.75}}
|
| 338 |
+
)
|
| 339 |
self.diffusion_model = FakeDiffusion()
|
| 340 |
self.logged = []
|
| 341 |
self.horizons = []
|
|
|
|
| 345 |
|
| 346 |
def _generate_scheduling_matrix(self, horizon):
|
| 347 |
self.horizons.append(horizon)
|
| 348 |
+
return np.stack([np.full(horizon, level, dtype=np.int64) for level in (2, 1, 0)])
|
| 349 |
|
| 350 |
def _generate_condition_indices(self, *args, **kwargs):
|
| 351 |
+
raise AssertionError("latent dict validation must not use the legacy memory_condition_length path")
|
| 352 |
|
| 353 |
def encode(self, xs):
|
| 354 |
raise AssertionError("latent dict validation must not VAE-encode inputs")
|
|
|
|
| 360 |
self.logged.append((name, value))
|
| 361 |
|
| 362 |
batch = {
|
| 363 |
+
"latents": torch.tensor([0, 1, 2, 3, 4, 100, 101, 102], dtype=torch.float32).view(1, 8, 1, 1, 1),
|
| 364 |
+
"actions": (100 + torch.arange(8, dtype=torch.float32)).view(1, 8, 1),
|
| 365 |
+
"poses": (200 + torch.arange(8, dtype=torch.float32)).view(1, 8, 1).repeat(1, 1, 5),
|
| 366 |
+
"frame_indices": torch.tensor([[10, 11, 12, 13, 14, 900, 901, 902]], dtype=torch.long),
|
| 367 |
+
"memory_segments": {"target": torch.tensor([5]), "anchor": torch.tensor([1]), "dynamic": torch.tensor([1]), "revisit": torch.tensor([1])},
|
| 368 |
+
"memory_masks": {"target": torch.ones((1, 5), dtype=torch.bool), "anchor": torch.ones((1, 1), dtype=torch.bool), "dynamic": torch.ones((1, 1), dtype=torch.bool), "revisit": torch.ones((1, 1), dtype=torch.bool)},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 369 |
"image_hw": torch.tensor([[360, 640]], dtype=torch.long),
|
| 370 |
}
|
| 371 |
harness = Harness()
|
| 372 |
|
| 373 |
+
with mock.patch.object(df_video, "select_memory_indices", side_effect=fake_select_memory_indices):
|
| 374 |
+
loss = DeMemWMMinecraft.validation_step(harness, batch, 0, namespace="test")
|
| 375 |
calls = harness.diffusion_model.calls
|
| 376 |
+
required_kwargs = {"reference_length", "frame_memory_segments", "frame_memory_masks", "frame_memory_pose", "frame_idx", "image_hw"}
|
| 377 |
|
| 378 |
+
self.assertEqual((harness.horizons, selection_calls, len(calls)), ([2, 1], [([2, 3], "test", 0.75), ([4], "test", 0.75)], 4))
|
| 379 |
+
self.assertTrue(all(required_kwargs <= call["kwargs"].keys() for call in calls))
|
| 380 |
+
self.assertTrue(all(call["kwargs"]["reference_length"] == 0 for call in calls))
|
| 381 |
+
self.assertEqual(
|
| 382 |
+
([call["kwargs"]["current_frame"] for call in calls[::2]], [call["kwargs"]["frame_memory_segments"]["target"] for call in calls[::2]]),
|
| 383 |
+
([2, 4], [2, 1]),
|
| 384 |
+
)
|
| 385 |
+
self.assertEqual(calls[0]["x"][:, 0, 0, 0, 0].tolist(), [0.0, 0.0, 0.0, 1.0, 0.0])
|
| 386 |
+
self.assertEqual(calls[0]["action_cond"][:, 0, 0].tolist(), [102.0, 103.0, 0.0, 0.0, 0.0])
|
| 387 |
+
self.assertEqual(calls[0]["kwargs"]["frame_idx"][:, 0].tolist(), [12, 13, 10, 11, 0])
|
| 388 |
+
self.assertEqual(calls[0]["kwargs"]["frame_memory_masks"]["revisit"].tolist(), [[False]])
|
| 389 |
+
self.assertAlmostEqual(float(loss), 1.0 / 3.0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
|
| 391 |
def test_dataset_returns_target_anchor_dynamic_revisit_contract(self):
|
| 392 |
with tempfile.TemporaryDirectory() as tmp:
|