BonanDing commited on
Commit
d762809
·
1 Parent(s): 4bb1560

Add DeMemWM noise-route pruning diagnostic

Browse files
.exp_artifact/dememwm_noise_route_inference_pruning/diagnostic.py ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ from types import SimpleNamespace
3
+ import sys
4
+
5
+ import torch
6
+
7
+
8
+ REPO_ROOT = Path(__file__).resolve().parents[2]
9
+ if str(REPO_ROOT) not in sys.path:
10
+ sys.path.insert(0, str(REPO_ROOT))
11
+
12
+ from algorithms.dememwm.df_video import ( # noqa: E402
13
+ _DEMEMWM_STREAM_KEYS,
14
+ _apply_memory_route_masks,
15
+ _pack_active_inference_memory_streams,
16
+ )
17
+
18
+
19
+ ROUTE_CFG = {"noise_route": {"anchor": "high", "dynamic": "low", "revisit": "all"}}
20
+ DIFFUSION = SimpleNamespace(timesteps=100, sampling_timesteps=4)
21
+ STREAM_LENGTHS = {"anchor": 2, "dynamic": 3, "revisit": 2}
22
+ STREAM_VALUES = {"anchor": 20.0, "dynamic": 30.0, "revisit": 40.0}
23
+ STREAM_NOISE_BASE = {"anchor": 200, "dynamic": 300, "revisit": 400}
24
+ BATCH_SIZE = 2
25
+ TARGET_LENGTH = 3
26
+
27
+
28
+ def _make_inputs(stream_masks):
29
+ target_latents = torch.arange(TARGET_LENGTH * BATCH_SIZE * 2, dtype=torch.float32).reshape(TARGET_LENGTH, BATCH_SIZE, 2)
30
+ target_conditions = torch.arange(TARGET_LENGTH * BATCH_SIZE * 4, dtype=torch.float32).reshape(TARGET_LENGTH, BATCH_SIZE, 4)
31
+ target_poses = torch.arange(TARGET_LENGTH * BATCH_SIZE * 3, dtype=torch.float32).reshape(TARGET_LENGTH, BATCH_SIZE, 3)
32
+ target_frame_indices = torch.arange(TARGET_LENGTH, dtype=torch.long)[:, None].expand(TARGET_LENGTH, BATCH_SIZE).clone()
33
+ target_mask = torch.tensor([[True, False, True], [False, True, True]], dtype=torch.bool)
34
+
35
+ stream_latents_by_key = {}
36
+ stream_poses_by_key = {}
37
+ stream_frame_indices_by_key = {}
38
+ memory_noise_levels_by_key = {}
39
+ for key in _DEMEMWM_STREAM_KEYS:
40
+ length = STREAM_LENGTHS[key]
41
+ stream_latents_by_key[key] = torch.full((length, BATCH_SIZE, 2), STREAM_VALUES[key])
42
+ stream_poses_by_key[key] = torch.full((length, BATCH_SIZE, 3), STREAM_VALUES[key] + 0.5)
43
+ stream_frame_indices_by_key[key] = (
44
+ torch.arange(length, dtype=torch.long)[:, None].expand(length, BATCH_SIZE).clone()
45
+ + int(STREAM_VALUES[key])
46
+ )
47
+ memory_noise_levels_by_key[key] = (
48
+ torch.arange(length * BATCH_SIZE, dtype=torch.long).reshape(length, BATCH_SIZE)
49
+ + STREAM_NOISE_BASE[key]
50
+ )
51
+
52
+ frame_memory_masks = {"target": target_mask, **stream_masks}
53
+ return {
54
+ "target_latents": target_latents,
55
+ "target_conditions": target_conditions,
56
+ "target_poses": target_poses,
57
+ "target_frame_indices": target_frame_indices,
58
+ "target_mask": target_mask,
59
+ "stream_latents_by_key": stream_latents_by_key,
60
+ "stream_poses_by_key": stream_poses_by_key,
61
+ "stream_frame_indices_by_key": stream_frame_indices_by_key,
62
+ "frame_memory_masks": frame_memory_masks,
63
+ "memory_noise_levels_by_key": memory_noise_levels_by_key,
64
+ }
65
+
66
+
67
+ def _sampler_index_noise(index):
68
+ real_steps = torch.linspace(-1, DIFFUSION.timesteps - 1, steps=DIFFUSION.sampling_timesteps + 1).long()
69
+ return int(real_steps[index].item())
70
+
71
+
72
+ def _target_proxy(packed_latents, frame_memory_segments, frame_memory_masks):
73
+ target_len = frame_memory_segments["target"]
74
+ target = packed_latents[:target_len].clone()
75
+ cursor = target_len
76
+ contribution = target.new_zeros((BATCH_SIZE, packed_latents.shape[-1]))
77
+ for key_index, key in enumerate(_DEMEMWM_STREAM_KEYS, start=1):
78
+ length = frame_memory_segments[key]
79
+ segment = packed_latents[cursor : cursor + length]
80
+ mask = frame_memory_masks[key]
81
+ cursor += length
82
+ if length == 0 or mask.numel() == 0:
83
+ continue
84
+ visible = mask.T.to(dtype=segment.dtype).unsqueeze(-1)
85
+ contribution = contribution + float(key_index) * (segment * visible).sum(dim=0)
86
+ return target + contribution.unsqueeze(0)
87
+
88
+
89
+ def _full_proxy_inputs(inputs, routed_masks):
90
+ packed_latents = torch.cat(
91
+ [inputs["target_latents"], *[inputs["stream_latents_by_key"][key] for key in _DEMEMWM_STREAM_KEYS]],
92
+ dim=0,
93
+ )
94
+ segments = {"target": TARGET_LENGTH, **{key: STREAM_LENGTHS[key] for key in _DEMEMWM_STREAM_KEYS}}
95
+ return packed_latents, segments, {"target": inputs["target_mask"], **routed_masks}
96
+
97
+
98
+ def _expected_noise(memory_noise_levels_by_key, active_streams):
99
+ if not active_streams:
100
+ return torch.zeros((0, BATCH_SIZE), dtype=torch.long)
101
+ return torch.cat([memory_noise_levels_by_key[key] for key in active_streams], dim=0)
102
+
103
+
104
+ def _check(condition, message):
105
+ if not condition:
106
+ raise AssertionError(message)
107
+
108
+
109
+ def _run_case(name, sampler_index, stream_masks, expected_active, expected_pruned):
110
+ inputs = _make_inputs(stream_masks)
111
+ query_noise_levels = torch.full((TARGET_LENGTH, BATCH_SIZE), sampler_index, dtype=torch.long)
112
+ routed_masks = _apply_memory_route_masks(
113
+ inputs["frame_memory_masks"],
114
+ query_noise_levels,
115
+ ROUTE_CFG,
116
+ DIFFUSION,
117
+ mode="validation",
118
+ )
119
+ _check(torch.equal(routed_masks["target"], inputs["target_mask"]), f"{name}: target mask was routed")
120
+
121
+ (
122
+ active_packed_latents,
123
+ active_packed_conditions,
124
+ active_frame_memory_pose,
125
+ active_frame_indices,
126
+ active_segments,
127
+ active_masks,
128
+ active_memory_noise_levels,
129
+ active_streams,
130
+ pruned_streams,
131
+ ) = _pack_active_inference_memory_streams(
132
+ inputs["target_latents"],
133
+ inputs["target_conditions"],
134
+ inputs["target_poses"],
135
+ inputs["target_frame_indices"],
136
+ inputs["target_mask"],
137
+ inputs["stream_latents_by_key"],
138
+ inputs["stream_poses_by_key"],
139
+ inputs["stream_frame_indices_by_key"],
140
+ routed_masks,
141
+ inputs["memory_noise_levels_by_key"],
142
+ )
143
+
144
+ full_segments = {"target": TARGET_LENGTH, **{key: STREAM_LENGTHS[key] for key in _DEMEMWM_STREAM_KEYS}}
145
+ full_memory_tokens = sum(full_segments[key] for key in _DEMEMWM_STREAM_KEYS)
146
+ pruned_memory_tokens = sum(active_segments[key] for key in _DEMEMWM_STREAM_KEYS)
147
+
148
+ _check(active_streams == expected_active, f"{name}: active streams {active_streams} != {expected_active}")
149
+ _check(pruned_streams == expected_pruned, f"{name}: pruned streams {pruned_streams} != {expected_pruned}")
150
+ _check(set(active_segments) == {"target", *_DEMEMWM_STREAM_KEYS}, f"{name}: missing segment keys")
151
+ _check(active_segments["target"] == TARGET_LENGTH, f"{name}: target segment length changed")
152
+ for key in expected_pruned:
153
+ _check(active_segments[key] == 0, f"{name}: pruned {key} has nonzero segment")
154
+ _check(active_masks[key].shape == (BATCH_SIZE, 0), f"{name}: pruned {key} mask is not zero-length")
155
+ for key in expected_active:
156
+ _check(active_segments[key] == STREAM_LENGTHS[key], f"{name}: active {key} segment length changed")
157
+ _check(torch.equal(active_masks[key], routed_masks[key]), f"{name}: active {key} mask changed")
158
+
159
+ expected_total_len = TARGET_LENGTH + pruned_memory_tokens
160
+ _check(active_packed_latents.shape == (expected_total_len, BATCH_SIZE, 2), f"{name}: packed latent shape mismatch")
161
+ _check(active_packed_conditions.shape == (expected_total_len, BATCH_SIZE, 4), f"{name}: packed condition shape mismatch")
162
+ _check(active_frame_memory_pose.shape == (expected_total_len, BATCH_SIZE, 3), f"{name}: packed pose shape mismatch")
163
+ _check(active_frame_indices.shape == (expected_total_len, BATCH_SIZE), f"{name}: packed frame index shape mismatch")
164
+ _check(torch.equal(active_packed_conditions[:TARGET_LENGTH], inputs["target_conditions"]), f"{name}: target conditions changed")
165
+ _check(torch.equal(active_packed_conditions[TARGET_LENGTH:], torch.zeros_like(active_packed_conditions[TARGET_LENGTH:])), f"{name}: memory conditions are not zero")
166
+
167
+ cursor = TARGET_LENGTH
168
+ for key in expected_active:
169
+ length = STREAM_LENGTHS[key]
170
+ expected_chunk = torch.full((length, BATCH_SIZE, 2), STREAM_VALUES[key])
171
+ _check(torch.equal(active_packed_latents[cursor : cursor + length], expected_chunk), f"{name}: canonical packed order broke at {key}")
172
+ cursor += length
173
+
174
+ expected_noise = _expected_noise(inputs["memory_noise_levels_by_key"], expected_active)
175
+ _check(torch.equal(active_memory_noise_levels, expected_noise), f"{name}: memory-noise concatenation order mismatch")
176
+ from_target = torch.full((TARGET_LENGTH, BATCH_SIZE), sampler_index, dtype=torch.long)
177
+ _check(
178
+ torch.cat([from_target, active_memory_noise_levels], dim=0).shape[0] == active_packed_latents.shape[0],
179
+ f"{name}: denoising noise-level shape contract mismatch",
180
+ )
181
+
182
+ full_latents, full_proxy_segments, full_proxy_masks = _full_proxy_inputs(inputs, routed_masks)
183
+ full_output = _target_proxy(full_latents, full_proxy_segments, full_proxy_masks)
184
+ pruned_output = _target_proxy(active_packed_latents, active_segments, active_masks)
185
+ target_max_abs_diff = (full_output - pruned_output).abs().max().item()
186
+ _check(target_max_abs_diff == 0.0, f"{name}: proxy target output changed by {target_max_abs_diff}")
187
+
188
+ print(f"case={name}")
189
+ print(f" route=anchor:high,dynamic:low,revisit:all")
190
+ print(f" denoising_step={sampler_index} noise_level={_sampler_index_noise(sampler_index)}")
191
+ print(f" active_streams={active_streams}")
192
+ print(f" pruned_streams={pruned_streams}")
193
+ print(f" full_memory_token_count={full_memory_tokens}")
194
+ print(f" pruned_memory_token_count={pruned_memory_tokens}")
195
+ print(f" segments_before={full_segments}")
196
+ print(f" segments_after={active_segments}")
197
+ print(f" target_proxy_max_abs_diff={target_max_abs_diff:.6f}")
198
+
199
+
200
+ def main():
201
+ all_valid_masks = {
202
+ "anchor": torch.tensor([[True, True], [True, False]], dtype=torch.bool),
203
+ "dynamic": torch.tensor([[True, True, False], [False, True, True]], dtype=torch.bool),
204
+ "revisit": torch.tensor([[True, False], [True, True]], dtype=torch.bool),
205
+ }
206
+ empty_anchor_masks = {
207
+ **all_valid_masks,
208
+ "anchor": torch.zeros((BATCH_SIZE, STREAM_LENGTHS["anchor"]), dtype=torch.bool),
209
+ }
210
+
211
+ _run_case(
212
+ "high_noise_all_valid",
213
+ sampler_index=4,
214
+ stream_masks=all_valid_masks,
215
+ expected_active=["anchor", "revisit"],
216
+ expected_pruned=["dynamic"],
217
+ )
218
+ _run_case(
219
+ "low_noise_all_valid",
220
+ sampler_index=1,
221
+ stream_masks=all_valid_masks,
222
+ expected_active=["dynamic", "revisit"],
223
+ expected_pruned=["anchor"],
224
+ )
225
+ _run_case(
226
+ "high_noise_empty_anchor",
227
+ sampler_index=4,
228
+ stream_masks=empty_anchor_masks,
229
+ expected_active=["revisit"],
230
+ expected_pruned=["anchor", "dynamic"],
231
+ )
232
+ print("diagnostic=ok")
233
+
234
+
235
+ if __name__ == "__main__":
236
+ main()