Add DeMemWM model acceptance checks
Browse files
algorithms/dememwm/models/dit.py
CHANGED
|
@@ -400,6 +400,7 @@ class SpatioTemporalDiTBlock(nn.Module):
|
|
| 400 |
frame_memory_geometry,
|
| 401 |
)
|
| 402 |
elif self.use_memory_attention:
|
|
|
|
| 403 |
r_shift_msa, r_scale_msa, r_gate_msa, r_shift_mlp, r_scale_mlp, r_gate_mlp = self.r_adaLN_modulation(c).chunk(6, dim=-1)
|
| 404 |
|
| 405 |
if pose_cond is not None:
|
|
@@ -443,12 +444,12 @@ class SpatioTemporalDiTBlock(nn.Module):
|
|
| 443 |
else:
|
| 444 |
# pose_cond *= 0
|
| 445 |
x = x + gate(self.r_attn(modulate(self.r_norm1(x+pose_cond[:,:,None, None]), r_shift_msa, r_scale_msa),
|
| 446 |
-
current_frame=current_frame, timestep=timestep,
|
| 447 |
-
is_last_block=is_last_block,
|
| 448 |
reference_length=reference_length), r_gate_msa)
|
| 449 |
else:
|
| 450 |
-
x = x + gate(self.r_attn(
|
| 451 |
-
|
|
|
|
|
|
|
| 452 |
|
| 453 |
x = x + gate(self.r_mlp(modulate(self.r_norm2(x), r_shift_mlp, r_scale_mlp)), r_gate_mlp)
|
| 454 |
|
|
|
|
| 400 |
frame_memory_geometry,
|
| 401 |
)
|
| 402 |
elif self.use_memory_attention:
|
| 403 |
+
reference_length = self.reference_length if reference_length is None else reference_length
|
| 404 |
r_shift_msa, r_scale_msa, r_gate_msa, r_shift_mlp, r_scale_mlp, r_gate_mlp = self.r_adaLN_modulation(c).chunk(6, dim=-1)
|
| 405 |
|
| 406 |
if pose_cond is not None:
|
|
|
|
| 444 |
else:
|
| 445 |
# pose_cond *= 0
|
| 446 |
x = x + gate(self.r_attn(modulate(self.r_norm1(x+pose_cond[:,:,None, None]), r_shift_msa, r_scale_msa),
|
|
|
|
|
|
|
| 447 |
reference_length=reference_length), r_gate_msa)
|
| 448 |
else:
|
| 449 |
+
x = x + gate(self.r_attn(
|
| 450 |
+
modulate(self.r_norm1(x), r_shift_msa, r_scale_msa),
|
| 451 |
+
reference_length=reference_length,
|
| 452 |
+
), r_gate_msa)
|
| 453 |
|
| 454 |
x = x + gate(self.r_mlp(modulate(self.r_norm2(x), r_shift_mlp, r_scale_mlp)), r_gate_mlp)
|
| 455 |
|
tests/test_dememwm_diffusion.py
CHANGED
|
@@ -106,6 +106,31 @@ class DeMemWMDiffusionTargetOnlyTests(unittest.TestCase):
|
|
| 106 |
self.assertEqual(tuple(x_pred.shape), tuple(x.shape))
|
| 107 |
self.assertEqual(tuple(loss.shape), tuple(x.shape))
|
| 108 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
def test_frame_memory_pose_is_batch_first_and_separate_from_pose_cond(self):
|
| 110 |
torch.manual_seed(0)
|
| 111 |
diffusion = _make_diffusion(output_frames=2)
|
|
@@ -198,6 +223,19 @@ class DeMemWMDiffusionTargetOnlyTests(unittest.TestCase):
|
|
| 198 |
self.assertEqual(diffusion.model.calls[0]["x_shape"], (1, 5, 1, 1, 1))
|
| 199 |
self.assertEqual(tuple(ddim.shape), (2, 1, 1, 1, 1))
|
| 200 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
|
| 202 |
if __name__ == "__main__":
|
| 203 |
unittest.main()
|
|
|
|
| 106 |
self.assertEqual(tuple(x_pred.shape), tuple(x.shape))
|
| 107 |
self.assertEqual(tuple(loss.shape), tuple(x.shape))
|
| 108 |
|
| 109 |
+
def test_padded_frame_memory_masks_keep_target_prediction_and_loss_shapes(self):
|
| 110 |
+
torch.manual_seed(0)
|
| 111 |
+
diffusion = _make_diffusion(output_frames=2)
|
| 112 |
+
x, action_cond, noise_levels, segments = _packed_inputs()
|
| 113 |
+
masks = {
|
| 114 |
+
"target": torch.ones((1, 2), dtype=torch.bool),
|
| 115 |
+
"anchor": torch.ones((1, 1), dtype=torch.bool),
|
| 116 |
+
"dynamic": torch.zeros((1, 1), dtype=torch.bool),
|
| 117 |
+
"revisit": torch.ones((1, 1), dtype=torch.bool),
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
x_pred, loss = diffusion(
|
| 121 |
+
x,
|
| 122 |
+
action_cond,
|
| 123 |
+
None,
|
| 124 |
+
noise_levels=noise_levels,
|
| 125 |
+
reference_length=0,
|
| 126 |
+
frame_memory_segments=segments,
|
| 127 |
+
frame_memory_masks=masks,
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
self.assertIs(diffusion.model.calls[0]["kwargs"]["frame_memory_masks"], masks)
|
| 131 |
+
self.assertEqual(tuple(x_pred.shape), (2, 1, 1, 1, 1))
|
| 132 |
+
self.assertEqual(tuple(loss.shape), (2, 1, 1, 1, 1))
|
| 133 |
+
|
| 134 |
def test_frame_memory_pose_is_batch_first_and_separate_from_pose_cond(self):
|
| 135 |
torch.manual_seed(0)
|
| 136 |
diffusion = _make_diffusion(output_frames=2)
|
|
|
|
| 223 |
self.assertEqual(diffusion.model.calls[0]["x_shape"], (1, 5, 1, 1, 1))
|
| 224 |
self.assertEqual(tuple(ddim.shape), (2, 1, 1, 1, 1))
|
| 225 |
|
| 226 |
+
diffusion = _make_diffusion(output_frames=2)
|
| 227 |
+
sample = diffusion.sample_step(
|
| 228 |
+
x,
|
| 229 |
+
action_cond,
|
| 230 |
+
None,
|
| 231 |
+
curr_noise_level=torch.tensor([[3], [2], [0], [0], [0]], dtype=torch.long),
|
| 232 |
+
next_noise_level=torch.tensor([[2], [1], [0], [0], [0]], dtype=torch.long),
|
| 233 |
+
reference_length=0,
|
| 234 |
+
frame_memory_segments=segments,
|
| 235 |
+
)
|
| 236 |
+
self.assertEqual(diffusion.model.calls[0]["x_shape"], (1, 5, 1, 1, 1))
|
| 237 |
+
self.assertEqual(tuple(sample.shape), (2, 1, 1, 1, 1))
|
| 238 |
+
|
| 239 |
|
| 240 |
if __name__ == "__main__":
|
| 241 |
unittest.main()
|
tests/test_dememwm_temporal_attention.py
CHANGED
|
@@ -61,6 +61,19 @@ class DeMemWMTemporalAttentionTests(unittest.TestCase):
|
|
| 61 |
expected = torch.tensor([10.0, 15.0, 20.0, 100.0, 1.0, 3.0, 200.0])
|
| 62 |
self.assertTrue(torch.allclose(out.flatten(), expected))
|
| 63 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
def test_block_threads_frame_memory_metadata_to_temporal_attention(self):
|
| 65 |
class ZeroAttention(nn.Module):
|
| 66 |
def forward(self, x):
|
|
@@ -135,6 +148,52 @@ class DeMemWMTemporalAttentionTests(unittest.TestCase):
|
|
| 135 |
self.assertIs(spy.calls[0]["frame_memory_segments"], segments)
|
| 136 |
self.assertIs(spy.calls[0]["frame_memory_masks"], masks)
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
def test_dit_builds_geometry_cache_once_and_threads_to_reference_attention(self):
|
| 139 |
class ZeroAttention(nn.Module):
|
| 140 |
def forward(self, x):
|
|
|
|
| 61 |
expected = torch.tensor([10.0, 15.0, 20.0, 100.0, 1.0, 3.0, 200.0])
|
| 62 |
self.assertTrue(torch.allclose(out.flatten(), expected))
|
| 63 |
|
| 64 |
+
def test_frame_memory_temporal_targets_do_not_depend_on_appended_memory(self):
|
| 65 |
+
attn = _averaging_temporal_attention()
|
| 66 |
+
segments = {"target": 3, "anchor": 1, "dynamic": 2, "revisit": 1}
|
| 67 |
+
x = torch.tensor([10.0, 20.0, 30.0, 100.0, 1.0, 3.0, 200.0]).view(1, 7, 1, 1, 1)
|
| 68 |
+
changed_memory = torch.tensor([10.0, 20.0, 30.0, -500.0, 700.0, 900.0, -300.0]).view(1, 7, 1, 1, 1)
|
| 69 |
+
|
| 70 |
+
out = attn(x, frame_memory_segments=segments)
|
| 71 |
+
changed_out = attn(changed_memory, frame_memory_segments=segments)
|
| 72 |
+
|
| 73 |
+
expected_target = torch.tensor([10.0, 15.0, 20.0]).view(1, 3, 1, 1, 1)
|
| 74 |
+
self.assertTrue(torch.allclose(out[:, :3], expected_target))
|
| 75 |
+
self.assertTrue(torch.allclose(out[:, :3], changed_out[:, :3]))
|
| 76 |
+
|
| 77 |
def test_block_threads_frame_memory_metadata_to_temporal_attention(self):
|
| 78 |
class ZeroAttention(nn.Module):
|
| 79 |
def forward(self, x):
|
|
|
|
| 148 |
self.assertIs(spy.calls[0]["frame_memory_segments"], segments)
|
| 149 |
self.assertIs(spy.calls[0]["frame_memory_masks"], masks)
|
| 150 |
|
| 151 |
+
def test_dit_baseline_and_packed_frame_memory_output_lengths_with_real_blocks(self):
|
| 152 |
+
torch.manual_seed(0)
|
| 153 |
+
model = DiT(
|
| 154 |
+
input_h=2,
|
| 155 |
+
input_w=2,
|
| 156 |
+
patch_size=1,
|
| 157 |
+
in_channels=1,
|
| 158 |
+
hidden_size=8,
|
| 159 |
+
depth=1,
|
| 160 |
+
num_heads=1,
|
| 161 |
+
mlp_ratio=1.0,
|
| 162 |
+
action_cond_dim=3,
|
| 163 |
+
pose_cond_dim=0,
|
| 164 |
+
reference_length=1,
|
| 165 |
+
use_memory_attention=True,
|
| 166 |
+
)
|
| 167 |
+
model.eval()
|
| 168 |
+
|
| 169 |
+
x = torch.randn((1, 3, 1, 2, 2))
|
| 170 |
+
t = torch.zeros((1, 3), dtype=torch.long)
|
| 171 |
+
action_cond = torch.zeros((1, 3, 3))
|
| 172 |
+
with torch.no_grad():
|
| 173 |
+
baseline = model(x, t, action_cond, reference_length=1)
|
| 174 |
+
self.assertEqual(tuple(baseline.shape), (1, 3, 1, 2, 2))
|
| 175 |
+
|
| 176 |
+
packed = torch.randn((1, 5, 1, 2, 2))
|
| 177 |
+
packed_t = torch.zeros((1, 5), dtype=torch.long)
|
| 178 |
+
packed_action = torch.zeros((1, 5, 3))
|
| 179 |
+
segments = {"target": 2, "anchor": 1, "dynamic": 1, "revisit": 1}
|
| 180 |
+
masks = {
|
| 181 |
+
"target": torch.ones((1, 2), dtype=torch.bool),
|
| 182 |
+
"anchor": torch.ones((1, 1), dtype=torch.bool),
|
| 183 |
+
"dynamic": torch.zeros((1, 1), dtype=torch.bool),
|
| 184 |
+
"revisit": torch.ones((1, 1), dtype=torch.bool),
|
| 185 |
+
}
|
| 186 |
+
with torch.no_grad():
|
| 187 |
+
packed_out = model(
|
| 188 |
+
packed,
|
| 189 |
+
packed_t,
|
| 190 |
+
packed_action,
|
| 191 |
+
reference_length=1,
|
| 192 |
+
frame_memory_segments=segments,
|
| 193 |
+
frame_memory_masks=masks,
|
| 194 |
+
)
|
| 195 |
+
self.assertEqual(tuple(packed_out.shape), (1, 2, 1, 2, 2))
|
| 196 |
+
|
| 197 |
def test_dit_builds_geometry_cache_once_and_threads_to_reference_attention(self):
|
| 198 |
class ZeroAttention(nn.Module):
|
| 199 |
def forward(self, x):
|