Simplify DeMemWM memory noise modes
Browse files
algorithms/dememwm/df_video.py
CHANGED
|
@@ -237,17 +237,6 @@ def _select_online_dynamic_indices(start_frame: int, count: int) -> np.ndarray:
|
|
| 237 |
return np.arange(start, int(start_frame), dtype=np.int64)[-count:]
|
| 238 |
|
| 239 |
|
| 240 |
-
def _shift_memory_noise_levels(levels, shift: float, diffusion_model, bounds):
|
| 241 |
-
if levels.numel() == 0 or float(shift) == 0.0 or not hasattr(diffusion_model, "snr"):
|
| 242 |
-
return levels
|
| 243 |
-
|
| 244 |
-
log_snr = diffusion_model.snr.to(device=levels.device, dtype=torch.float32).clamp_min(1e-20).log()
|
| 245 |
-
shifted_log_snr = log_snr[levels.clamp(0, log_snr.numel() - 1)] + float(shift)
|
| 246 |
-
table = log_snr.view(*((1,) * shifted_log_snr.ndim), -1)
|
| 247 |
-
shifted = (table - shifted_log_snr.unsqueeze(-1)).abs().argmin(dim=-1).to(dtype=levels.dtype)
|
| 248 |
-
return torch.minimum(shifted, bounds.reshape(1, -1).expand_as(shifted))
|
| 249 |
-
|
| 250 |
-
|
| 251 |
def _memory_noise_levels_for_streams(cfg, diffusion_model, query_noise_levels, stream_lengths, mode: str):
|
| 252 |
is_training = str(mode) == "training"
|
| 253 |
noise_cfg = _cfg_get(cfg, "memory_noise")
|
|
@@ -258,15 +247,26 @@ def _memory_noise_levels_for_streams(cfg, diffusion_model, query_noise_levels, s
|
|
| 258 |
for key in _DEMEMWM_STREAM_KEYS
|
| 259 |
}
|
| 260 |
|
| 261 |
-
|
| 262 |
-
|
|
|
|
| 263 |
noisy_memory = bool(_cfg_get(noise_cfg, "enabled", True)) and (
|
| 264 |
is_training or bool(_cfg_get(noise_cfg, "validation_noisy_memory", False))
|
| 265 |
)
|
| 266 |
-
snr_cfg = _cfg_get(noise_cfg, "snr_shift")
|
| 267 |
-
snr_enabled = is_training and noisy_memory and bool(_cfg_get(snr_cfg, "enabled", False))
|
| 268 |
query = query_noise_levels[:, None] if query_noise_levels.ndim == 1 else query_noise_levels
|
| 269 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 270 |
defaults = {"anchor": 0.25, "dynamic": 0.50, "revisit": 0.25}
|
| 271 |
levels_by_stream = {}
|
| 272 |
for key in _DEMEMWM_STREAM_KEYS:
|
|
@@ -274,14 +274,25 @@ def _memory_noise_levels_for_streams(cfg, diffusion_model, query_noise_levels, s
|
|
| 274 |
if count == 0:
|
| 275 |
levels_by_stream[key] = bounds.new_zeros((0, bounds.numel()))
|
| 276 |
continue
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
levels = torch.floor(torch.rand((count, bounds.numel()), device=bounds.device) * (max_levels[None].float() + 1)).to(dtype=torch.long)
|
| 281 |
else:
|
| 282 |
levels = max_levels.reshape(1, -1).expand(count, -1).contiguous()
|
| 283 |
-
|
| 284 |
-
levels_by_stream[key] = _shift_memory_noise_levels(levels, shift, diffusion_model, bounds)
|
| 285 |
return levels_by_stream
|
| 286 |
|
| 287 |
|
|
|
|
| 237 |
return np.arange(start, int(start_frame), dtype=np.int64)[-count:]
|
| 238 |
|
| 239 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
def _memory_noise_levels_for_streams(cfg, diffusion_model, query_noise_levels, stream_lengths, mode: str):
|
| 241 |
is_training = str(mode) == "training"
|
| 242 |
noise_cfg = _cfg_get(cfg, "memory_noise")
|
|
|
|
| 247 |
for key in _DEMEMWM_STREAM_KEYS
|
| 248 |
}
|
| 249 |
|
| 250 |
+
noise_mode = str(_cfg_get(noise_cfg, "mode", "random_cleaner_fraction"))
|
| 251 |
+
if noise_mode not in {"random_cleaner_fraction", "independent"}:
|
| 252 |
+
raise ValueError("memory_noise.mode must be random_cleaner_fraction or independent")
|
| 253 |
noisy_memory = bool(_cfg_get(noise_cfg, "enabled", True)) and (
|
| 254 |
is_training or bool(_cfg_get(noise_cfg, "validation_noisy_memory", False))
|
| 255 |
)
|
|
|
|
|
|
|
| 256 |
query = query_noise_levels[:, None] if query_noise_levels.ndim == 1 else query_noise_levels
|
| 257 |
+
query_bounds = query.to(dtype=torch.long).clamp_min(0).min(dim=0).values
|
| 258 |
+
if noise_mode == "independent":
|
| 259 |
+
if is_training:
|
| 260 |
+
max_level = int(getattr(diffusion_model, "timesteps", 0) or 0) - 1
|
| 261 |
+
else:
|
| 262 |
+
max_level = int(
|
| 263 |
+
getattr(diffusion_model, "sampling_timesteps", 0)
|
| 264 |
+
or getattr(diffusion_model, "timesteps", 0)
|
| 265 |
+
or 1
|
| 266 |
+
)
|
| 267 |
+
bounds = query_bounds.new_full(query_bounds.shape, max(0, max_level))
|
| 268 |
+
else:
|
| 269 |
+
bounds = query_bounds
|
| 270 |
defaults = {"anchor": 0.25, "dynamic": 0.50, "revisit": 0.25}
|
| 271 |
levels_by_stream = {}
|
| 272 |
for key in _DEMEMWM_STREAM_KEYS:
|
|
|
|
| 274 |
if count == 0:
|
| 275 |
levels_by_stream[key] = bounds.new_zeros((0, bounds.numel()))
|
| 276 |
continue
|
| 277 |
+
if noise_mode == "independent":
|
| 278 |
+
max_levels = bounds if noisy_memory else bounds.new_zeros(bounds.shape)
|
| 279 |
+
else:
|
| 280 |
+
fraction = float(_cfg_get(noise_cfg, f"{key}_max_fraction", defaults[key])) if noisy_memory else 0.0
|
| 281 |
+
max_levels = torch.floor(bounds.to(dtype=torch.float32) * fraction).to(dtype=torch.long).clamp_min(0)
|
| 282 |
+
if is_training and noisy_memory and noise_mode == "independent":
|
| 283 |
+
levels = torch.randint(
|
| 284 |
+
0,
|
| 285 |
+
int(bounds.max().item()) + 1,
|
| 286 |
+
(count, bounds.numel()),
|
| 287 |
+
device=bounds.device,
|
| 288 |
+
dtype=torch.long,
|
| 289 |
+
)
|
| 290 |
+
levels = torch.minimum(levels, max_levels.reshape(1, -1).expand_as(levels))
|
| 291 |
+
elif is_training and noisy_memory:
|
| 292 |
levels = torch.floor(torch.rand((count, bounds.numel()), device=bounds.device) * (max_levels[None].float() + 1)).to(dtype=torch.long)
|
| 293 |
else:
|
| 294 |
levels = max_levels.reshape(1, -1).expand(count, -1).contiguous()
|
| 295 |
+
levels_by_stream[key] = levels
|
|
|
|
| 296 |
return levels_by_stream
|
| 297 |
|
| 298 |
|
algorithms/dememwm/models/dit.py
CHANGED
|
@@ -171,7 +171,8 @@ class FrameMemoryReferenceAttention(nn.Module):
|
|
| 171 |
relative_rays = geometry_cache["relative_rays"].to(device=target_hidden.device, dtype=target_hidden.dtype)
|
| 172 |
else:
|
| 173 |
query_rays = None
|
| 174 |
-
|
|
|
|
| 175 |
q_input = target_hidden
|
| 176 |
if query_rays is not None:
|
| 177 |
# Values stay visual-only; Plucker features only steer Q/K matching.
|
|
|
|
| 171 |
relative_rays = geometry_cache["relative_rays"].to(device=target_hidden.device, dtype=target_hidden.dtype)
|
| 172 |
else:
|
| 173 |
query_rays = None
|
| 174 |
+
relative_rays = None
|
| 175 |
+
|
| 176 |
q_input = target_hidden
|
| 177 |
if query_rays is not None:
|
| 178 |
# Values stay visual-only; Plucker features only steer Q/K matching.
|
configurations/algorithm/dememwm_base.yaml
CHANGED
|
@@ -8,11 +8,11 @@ memory_selection: ${dataset.memory_selection}
|
|
| 8 |
|
| 9 |
memory_noise:
|
| 10 |
enabled: true
|
|
|
|
| 11 |
mode: random_cleaner_fraction
|
| 12 |
anchor_max_fraction: 0.25
|
| 13 |
dynamic_max_fraction: 0.50
|
| 14 |
revisit_max_fraction: 0.25
|
| 15 |
-
snr_shift: {enabled: true, anchor: 0.0, dynamic: 0.0, revisit: 0.0}
|
| 16 |
noise_route: {anchor: all, dynamic: all, revisit: all}
|
| 17 |
trainability:
|
| 18 |
freeze_vae: true
|
|
|
|
| 8 |
|
| 9 |
memory_noise:
|
| 10 |
enabled: true
|
| 11 |
+
# Options: random_cleaner_fraction (query-bounded), independent (full schedule).
|
| 12 |
mode: random_cleaner_fraction
|
| 13 |
anchor_max_fraction: 0.25
|
| 14 |
dynamic_max_fraction: 0.50
|
| 15 |
revisit_max_fraction: 0.25
|
|
|
|
| 16 |
noise_route: {anchor: all, dynamic: all, revisit: all}
|
| 17 |
trainability:
|
| 18 |
freeze_vae: true
|
tests/test_dememwm_latent_dataset.py
CHANGED
|
@@ -436,15 +436,14 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
|
|
| 436 |
with self.assertRaisesRegex(TypeError, "latent dict batch contract"):
|
| 437 |
DeMemWMMinecraft.training_step(Harness(), (None, None, None, None), 0)
|
| 438 |
|
| 439 |
-
def
|
| 440 |
from algorithms.dememwm.df_video import _apply_memory_route_masks, _memory_noise_levels_for_streams
|
| 441 |
|
| 442 |
class Diffusion:
|
| 443 |
stabilization_level, timesteps, sampling_timesteps = 7, 10, 4
|
| 444 |
-
snr = torch.exp(torch.linspace(5.0, -4.0, 10))
|
| 445 |
|
| 446 |
cfg = OmegaConf.create({
|
| 447 |
-
"memory_noise": {"enabled": True, "anchor_max_fraction": 1.0, "dynamic_max_fraction": 0.5, "revisit_max_fraction": 0.25
|
| 448 |
"noise_route": {"anchor": "high", "dynamic": "low", "revisit": "all"},
|
| 449 |
})
|
| 450 |
query_noise = torch.tensor([[9, 1], [7, 1]], dtype=torch.long)
|
|
@@ -452,7 +451,7 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
|
|
| 452 |
torch.manual_seed(0)
|
| 453 |
|
| 454 |
levels = _memory_noise_levels_for_streams(cfg, Diffusion, query_noise, stream_lengths, mode="training")
|
| 455 |
-
self.
|
| 456 |
self.assertTrue(bool((levels["dynamic"] <= torch.tensor([[3, 0]])).all()))
|
| 457 |
self.assertTrue(bool((levels["revisit"] <= torch.tensor([[1, 0]])).all()))
|
| 458 |
validation_levels = _memory_noise_levels_for_streams(cfg, Diffusion, query_noise, stream_lengths, mode="validation")
|
|
@@ -462,6 +461,34 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
|
|
| 462 |
routed = _apply_memory_route_masks(masks, query_noise, cfg, Diffusion, mode="training")
|
| 463 |
self.assertEqual({key: routed[key].tolist() for key in stream_lengths}, {"anchor": [[True], [False]], "dynamic": [[False], [True]], "revisit": [[True], [True]]})
|
| 464 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 465 |
def test_trainability_controls_memory_groups_full_dit_ramp_and_vae_freeze(self):
|
| 466 |
from algorithms.dememwm.df_video import (
|
| 467 |
_apply_dememwm_trainability,
|
|
|
|
| 436 |
with self.assertRaisesRegex(TypeError, "latent dict batch contract"):
|
| 437 |
DeMemWMMinecraft.training_step(Harness(), (None, None, None, None), 0)
|
| 438 |
|
| 439 |
+
def test_memory_noise_helpers_route_and_keep_validation_clean(self):
|
| 440 |
from algorithms.dememwm.df_video import _apply_memory_route_masks, _memory_noise_levels_for_streams
|
| 441 |
|
| 442 |
class Diffusion:
|
| 443 |
stabilization_level, timesteps, sampling_timesteps = 7, 10, 4
|
|
|
|
| 444 |
|
| 445 |
cfg = OmegaConf.create({
|
| 446 |
+
"memory_noise": {"enabled": True, "anchor_max_fraction": 1.0, "dynamic_max_fraction": 0.5, "revisit_max_fraction": 0.25},
|
| 447 |
"noise_route": {"anchor": "high", "dynamic": "low", "revisit": "all"},
|
| 448 |
})
|
| 449 |
query_noise = torch.tensor([[9, 1], [7, 1]], dtype=torch.long)
|
|
|
|
| 451 |
torch.manual_seed(0)
|
| 452 |
|
| 453 |
levels = _memory_noise_levels_for_streams(cfg, Diffusion, query_noise, stream_lengths, mode="training")
|
| 454 |
+
self.assertTrue(bool((levels["anchor"] <= torch.tensor([[7, 1]])).all()))
|
| 455 |
self.assertTrue(bool((levels["dynamic"] <= torch.tensor([[3, 0]])).all()))
|
| 456 |
self.assertTrue(bool((levels["revisit"] <= torch.tensor([[1, 0]])).all()))
|
| 457 |
validation_levels = _memory_noise_levels_for_streams(cfg, Diffusion, query_noise, stream_lengths, mode="validation")
|
|
|
|
| 461 |
routed = _apply_memory_route_masks(masks, query_noise, cfg, Diffusion, mode="training")
|
| 462 |
self.assertEqual({key: routed[key].tolist() for key in stream_lengths}, {"anchor": [[True], [False]], "dynamic": [[False], [True]], "revisit": [[True], [True]]})
|
| 463 |
|
| 464 |
+
def test_memory_noise_independent_mode_ignores_query_bounds(self):
|
| 465 |
+
from algorithms.dememwm.df_video import _memory_noise_levels_for_streams
|
| 466 |
+
|
| 467 |
+
class Diffusion:
|
| 468 |
+
stabilization_level, timesteps, sampling_timesteps = 7, 10, 4
|
| 469 |
+
|
| 470 |
+
cfg = OmegaConf.create({
|
| 471 |
+
"memory_noise": {
|
| 472 |
+
"enabled": True,
|
| 473 |
+
"mode": "independent",
|
| 474 |
+
},
|
| 475 |
+
})
|
| 476 |
+
query_noise = torch.ones((2, 2), dtype=torch.long)
|
| 477 |
+
stream_lengths = {"anchor": 3, "dynamic": 0, "revisit": 0}
|
| 478 |
+
|
| 479 |
+
def fake_randint(low, high, size, device=None, dtype=None):
|
| 480 |
+
self.assertEqual((low, high, size), (0, 10, (3, 2)))
|
| 481 |
+
return torch.full(size, high - 1, device=device, dtype=dtype)
|
| 482 |
+
|
| 483 |
+
with mock.patch("algorithms.dememwm.df_video.torch.randint", side_effect=fake_randint):
|
| 484 |
+
levels = _memory_noise_levels_for_streams(cfg, Diffusion, query_noise, stream_lengths, mode="training")
|
| 485 |
+
|
| 486 |
+
self.assertEqual(levels["anchor"].tolist(), [[9, 9], [9, 9], [9, 9]])
|
| 487 |
+
self.assertTrue(bool((levels["anchor"] > query_noise.max()).all()))
|
| 488 |
+
|
| 489 |
+
validation_levels = _memory_noise_levels_for_streams(cfg, Diffusion, query_noise, stream_lengths, mode="validation")
|
| 490 |
+
self.assertEqual(sum(int(v.sum().item()) for v in validation_levels.values()), 0)
|
| 491 |
+
|
| 492 |
def test_trainability_controls_memory_groups_full_dit_ramp_and_vae_freeze(self):
|
| 493 |
from algorithms.dememwm.df_video import (
|
| 494 |
_apply_dememwm_trainability,
|