BonanDing commited on
Commit
aa4688c
·
verified ·
1 Parent(s): 181f440

Add constrained DecMem STM6/topk27 Minecraft evaluation

Browse files

Add a read-budget profile preserving native patch retrieval, weights, sampler and four-latent blocks. Native settings remain the default. The new eight-H200 seed42/43 launcher runs a36-frame preflight, then300clips with600observations and500predictions each. Runtime metadata records actual settings and logical/backing KVstorage; this is not an exact whole-frame or persistent-storage cap. CPU selection/cache/interface/CLI and shell checks passed; GPU checks run in the submitted preflight.

DecMem/evaluation/worldmem_adapter.py CHANGED
@@ -45,7 +45,7 @@ def prepare_controls(case, target_frames):
45
 
46
 
47
  class DecMemAdapter:
48
- def __init__(self, checkpoint, device="cuda"):
49
  self.checkpoint = str(Path(checkpoint).resolve())
50
  # Upstream model and VAE loading use paths relative to the DecMem checkout.
51
  os.chdir(REPO)
@@ -62,10 +62,29 @@ class DecMemAdapter:
62
  self.config.num_frames = self.config.image_or_video_shape[1]
63
  self.config.prope_max_time = 60
64
  self.config.rope_scale_pattern = "rope"
 
 
 
65
  self.pipeline = WorldMemCausalDiffusionInferencePipeline(self.config, device=self.device)
66
  checkpoint_state = torch.load(self.checkpoint, map_location="cpu", weights_only=True, mmap=True)
67
  self.pipeline.generator.load_state_dict(checkpoint_state["generator"], strict=True)
68
  self.pipeline.to(device=self.device, dtype=self.dtype).eval().requires_grad_(False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  self.parameter_counts = {
70
  "generator_total": sum(p.numel() for p in self.pipeline.generator.parameters()),
71
  "generator_trainable": sum(p.numel() for p in self.pipeline.generator.parameters() if p.requires_grad),
@@ -109,6 +128,12 @@ class DecMemAdapter:
109
  return_latents=True,
110
  skip_vae_decode=True,
111
  )
 
 
 
 
 
 
112
  # Denoising is finished. Release global KV before decoding the complete causal timeline.
113
  self.pipeline.kv_cache = None
114
  self.pipeline.crossattn_cache = None
@@ -125,7 +150,7 @@ class DecMemAdapter:
125
  "batch_size": 1,
126
  "cfg_batch_size": 2,
127
  "sampling": {"solver": "UniPC", "steps": 20, "guidance_scale": 5.0, "timestep_shift": 5.0, "source": "scripts/infer_example.sh"},
128
- "memory": {"use_ltm_attn": True, "ltm_topk": 80, "ltm_tile_size": [1, 8, 8], "stm_sliding_window": 8, "num_frame_per_block": 4},
129
  "history_frames": history,
130
  "history_source_indices": case["source_indices"][:history].tolist(),
131
  "history_left_padding": padding,
 
45
 
46
 
47
  class DecMemAdapter:
48
+ def __init__(self, checkpoint, device="cuda", budget=False):
49
  self.checkpoint = str(Path(checkpoint).resolve())
50
  # Upstream model and VAE loading use paths relative to the DecMem checkout.
51
  os.chdir(REPO)
 
62
  self.config.num_frames = self.config.image_or_video_shape[1]
63
  self.config.prope_max_time = 60
64
  self.config.rope_scale_pattern = "rope"
65
+ if budget:
66
+ self.config.model_kwargs.stm_sliding_window = 6
67
+ self.config.model_kwargs.ltm_topk = 27
68
  self.pipeline = WorldMemCausalDiffusionInferencePipeline(self.config, device=self.device)
69
  checkpoint_state = torch.load(self.checkpoint, map_location="cpu", weights_only=True, mmap=True)
70
  self.pipeline.generator.load_state_dict(checkpoint_state["generator"], strict=True)
71
  self.pipeline.to(device=self.device, dtype=self.dtype).eval().requires_grad_(False)
72
+ model = self.pipeline.generator.model
73
+ window, topk = model.stm_sliding_window, model.ltm_topk
74
+ if budget and any((a.stm_sliding_window, a.ltm_topk) != (6, 27) for a in [model, *(b.self_attn for b in model.blocks)]):
75
+ raise ValueError("DecMem read budget was not applied to every attention layer")
76
+ self.memory = {
77
+ "profile": "stm6_topk27" if budget else "native", "use_ltm_attn": model.use_ltm_attn,
78
+ "stm_sliding_window": window, "ltm_topk": topk, "ltm_tile_size": list(model.ltm_tile_size),
79
+ "num_frame_per_block": self.pipeline.num_frame_per_block,
80
+ "cached_past_latents": window-self.pipeline.num_frame_per_block,
81
+ "tokens_per_latent": self.pipeline.frame_seq_length,
82
+ "dense_key_tokens_per_query": window*self.pipeline.frame_seq_length,
83
+ "fine_key_tokens_per_query_upper_bound": topk*math.prod(model.ltm_tile_size),
84
+ "scope": "dense local context and sparse fine-attention reads; not a whole-frame or storage cap",
85
+ "candidate_history": "all committed history retained; coarse ranking includes all history and current block",
86
+ "temporal_caveat": "4x temporal VAE and 4-latent joint generation; first noisy block has 3 observed latents",
87
+ }
88
  self.parameter_counts = {
89
  "generator_total": sum(p.numel() for p in self.pipeline.generator.parameters()),
90
  "generator_trainable": sum(p.numel() for p in self.pipeline.generator.parameters() if p.requires_grad),
 
128
  return_latents=True,
129
  skip_vae_decode=True,
130
  )
131
+ # STM slices retain their backing allocations; report both sizes before releasing KV.
132
+ caches = [value for layer in self.pipeline.kv_cache for value in layer.values() if torch.is_tensor(value)]
133
+ storages = {(value.device, value.untyped_storage().data_ptr()): value.untyped_storage().nbytes() for value in caches}
134
+ cache_usage = {"final_logical_bytes": sum(value.numel()*value.element_size() for value in caches),
135
+ "final_storage_bytes": sum(storages.values())}
136
+ del caches, storages
137
  # Denoising is finished. Release global KV before decoding the complete causal timeline.
138
  self.pipeline.kv_cache = None
139
  self.pipeline.crossattn_cache = None
 
150
  "batch_size": 1,
151
  "cfg_batch_size": 2,
152
  "sampling": {"solver": "UniPC", "steps": 20, "guidance_scale": 5.0, "timestep_shift": 5.0, "source": "scripts/infer_example.sh"},
153
+ "memory": {**self.memory, **cache_usage},
154
  "history_frames": history,
155
  "history_source_indices": case["source_indices"][:history].tolist(),
156
  "history_left_padding": padding,
deployment/evaluate_decmem_budget_8h200.sh ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ #SBATCH --job-name=decmem-minecraft-stm6-topk27-full
3
+ #SBATCH --nodes=1
4
+ #SBATCH --ntasks-per-node=8
5
+ #SBATCH --cpus-per-task=14
6
+ #SBATCH --gres=gpu:8
7
+ #SBATCH --time=48:00:00
8
+ #SBATCH --account=berzelius-2026-196
9
+ #SBATCH --array=42-43%1
10
+ #SBATCH --chdir=/proj/cvl/users/x_fahkh2/worldmem-baseline-evals/DecMem
11
+ #SBATCH --output=slurm_logs/%x-%A_%a.out
12
+ #SBATCH --error=slurm_logs/%x-%A_%a.out
13
+
14
+ set -euo pipefail
15
+ module load buildenv-gcccuda/12.9.1-gcc11
16
+
17
+ ROOT=/proj/cvl/users/x_fahkh2/worldmem-baseline-evals
18
+ ENV_ROOT=/proj/cvl/users/x_fahkh2/envs
19
+ REPO=$ROOT/DecMem
20
+ PYTHON=$ENV_ROOT/decmem-eval/bin/python
21
+ METRIC_PYTHON=$ENV_ROOT/geometry-forcing-eval/bin/python
22
+ WORLD_MEM_ROOT=$ROOT/references/worldmem
23
+ DATA_ROOT=/proj/cvl/users/x_fahkh2/WorldMem_Repro/datasets/minecraft
24
+ MANIFEST=$ROOT/deployment/manifests/minecraft_test_300.csv
25
+ FID_MANIFEST=$ROOT/deployment/manifests/minecraft_test_fid50.csv
26
+ SEED=$SLURM_ARRAY_TASK_ID
27
+ JOB=$SLURM_ARRAY_JOB_ID
28
+ RUN=decmem_stm6_topk27_minecraft_300cases_seed${SEED}_job${JOB}
29
+ OUTPUT=$REPO/outputs/$RUN
30
+ PREFLIGHT_OUTPUT=$REPO/outputs/${RUN}_preflight
31
+
32
+ export TMPDIR=$ROOT/cache TRITON_CACHE_DIR=$ROOT/cache
33
+ export HF_HOME=$ROOT/cache/huggingface TORCH_HOME=$ROOT/cache/torch
34
+ export PIP_CACHE_DIR=$ROOT/cache/pip CUDA_HOME=$CUDA_ROOT
35
+ export XDG_CACHE_HOME=$ROOT/cache MPLCONFIGDIR=$ROOT/cache/matplotlib
36
+ export PYTHONNOUSERSITE=1 PYTHONUNBUFFERED=1
37
+ export PYTHONPATH=$REPO
38
+ export OMP_NUM_THREADS=4 MKL_NUM_THREADS=4
39
+ export WANDB_MODE=disabled HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1
40
+ mkdir -p "$TMPDIR" "$OUTPUT"
41
+ cd "$REPO"
42
+
43
+ "$PYTHON" "$ROOT/deployment/verify_node.py" --output "$OUTPUT"
44
+ cp "$0" "$OUTPUT/launcher.sh"
45
+ cp "$REPO/evaluation/worldmem_adapter.py" "$OUTPUT/worldmem_adapter.py"
46
+
47
+ # Read-budget ablation: STM6/top-k27 keeps all candidate history and block size4.
48
+ # Exercise three denoised blocks and the complete scoring path before the full run.
49
+ srun --exclusive --exact --nodes=1 --ntasks=1 --gpus-per-task=1 --cpus-per-task=14 --cpu-bind=cores \
50
+ "$PYTHON" "$ROOT/shared/run_and_score.py" \
51
+ --method decmem --decmem-budget \
52
+ --workspace-root "$ROOT" \
53
+ --metric-python "$METRIC_PYTHON" \
54
+ --worldmem-root "$WORLD_MEM_ROOT" \
55
+ --manifest "$MANIFEST" \
56
+ --data-root "$DATA_ROOT" \
57
+ --output "$PREFLIGHT_OUTPUT" \
58
+ --seed "$SEED" \
59
+ --limit 1 --max-generated 36 --rank 0 --world-size 1
60
+
61
+ srun --exclusive --exact --nodes=1 --ntasks=1 --gpus-per-task=1 --cpus-per-task=14 \
62
+ "$METRIC_PYTHON" "$ROOT/shared/aggregate.py" \
63
+ --output "$PREFLIGHT_OUTPUT" \
64
+ --manifest "$MANIFEST" \
65
+ --dataset minecraft \
66
+ --seed "$SEED" \
67
+ --expected-frames 36 --limit 1
68
+
69
+ srun --ntasks=8 --gpus-per-task=1 --cpus-per-task=14 --cpu-bind=cores --kill-on-bad-exit=1 \
70
+ "$PYTHON" "$ROOT/shared/run_and_score.py" \
71
+ --method decmem --decmem-budget \
72
+ --workspace-root "$ROOT" \
73
+ --metric-python "$METRIC_PYTHON" \
74
+ --worldmem-root "$WORLD_MEM_ROOT" \
75
+ --manifest "$MANIFEST" \
76
+ --data-root "$DATA_ROOT" \
77
+ --output "$OUTPUT" \
78
+ --seed "$SEED" \
79
+ --limit 300 --max-generated 500 --world-size 8
80
+
81
+ srun --exclusive --exact --nodes=1 --ntasks=1 --gpus-per-task=1 --cpus-per-task=14 \
82
+ "$METRIC_PYTHON" "$ROOT/shared/aggregate.py" \
83
+ --output "$OUTPUT" \
84
+ --manifest "$MANIFEST" \
85
+ --dataset minecraft \
86
+ --fid-manifest "$FID_MANIFEST" \
87
+ --seed "$SEED"
shared/run_and_score.py CHANGED
@@ -15,7 +15,7 @@ import torch
15
  from protocol import case_key, case_seed, load_minecraft_case, load_re10k_case, save_case, select_cases
16
 
17
 
18
- def load_adapter(method, root):
19
  entries = {
20
  "live": ("LIVE", "scripts/evaluate_worldmem.py", "LivePredictor"),
21
  "geometry-forcing": ("GeometryForcing", "evaluation/worldmem_adapter.py", "GeometryForcingAdapter"),
@@ -35,7 +35,7 @@ def load_adapter(method, root):
35
  if method == "geometry-forcing":
36
  return constructor(root / "checkpoints/geometry-forcing/geometry_forcing_state_dict.ckpt")
37
  if method == "decmem":
38
- return constructor(root / "checkpoints/decmem/decmem.pt")
39
  return constructor(root / "checkpoints/matrix-game2", mouse_scale=1.0)
40
 
41
 
@@ -56,6 +56,7 @@ def main():
56
  parser.add_argument("--metric-python", type=Path, required=True)
57
  parser.add_argument("--vae-checkpoint", type=Path)
58
  parser.add_argument("--keep-tensor", action="store_true")
 
59
  parser.add_argument("--dry-run", action="store_true")
60
  args = parser.parse_args()
61
  root = args.workspace_root.resolve()
@@ -71,10 +72,11 @@ def main():
71
  rows = select_cases(args.manifest, args.scope, args.rank, args.world_size, args.limit)
72
  if args.dry_run:
73
  print(json.dumps({"method": args.method, "rank": args.rank, "cases": [case_key(row) for row in rows],
74
- "seed": args.seed, "generated_frames": args.max_generated}, indent=2))
 
75
  return
76
  torch.cuda.set_device(0)
77
- adapter = load_adapter(args.method, root)
78
  args.output.mkdir(parents=True, exist_ok=True)
79
  metadata = getattr(adapter, "metadata", getattr(adapter, "provenance", {}))
80
  if args.method in {"decmem", "matrix-game2"}:
@@ -83,6 +85,14 @@ def main():
83
  metadata = {**json.loads(manifest_path.read_text()),
84
  "instantiated_parameter_counts": adapter.parameter_counts,
85
  "runtime_strict_checkpoint_load": "passed"}
 
 
 
 
 
 
 
 
86
  (args.output / f"method_manifest_rank{args.rank}.json").write_text(json.dumps(metadata, indent=2, default=str) + "\n")
87
  for row in rows:
88
  case = (load_minecraft_case if dataset == "minecraft" else load_re10k_case)(row, args.data_root)
 
15
  from protocol import case_key, case_seed, load_minecraft_case, load_re10k_case, save_case, select_cases
16
 
17
 
18
+ def load_adapter(method, root, decmem_budget=False):
19
  entries = {
20
  "live": ("LIVE", "scripts/evaluate_worldmem.py", "LivePredictor"),
21
  "geometry-forcing": ("GeometryForcing", "evaluation/worldmem_adapter.py", "GeometryForcingAdapter"),
 
35
  if method == "geometry-forcing":
36
  return constructor(root / "checkpoints/geometry-forcing/geometry_forcing_state_dict.ckpt")
37
  if method == "decmem":
38
+ return constructor(root / "checkpoints/decmem/decmem.pt", budget=decmem_budget)
39
  return constructor(root / "checkpoints/matrix-game2", mouse_scale=1.0)
40
 
41
 
 
56
  parser.add_argument("--metric-python", type=Path, required=True)
57
  parser.add_argument("--vae-checkpoint", type=Path)
58
  parser.add_argument("--keep-tensor", action="store_true")
59
+ parser.add_argument("--decmem-budget", action="store_true", help="DecMem STM6/topk27 read-budget ablation")
60
  parser.add_argument("--dry-run", action="store_true")
61
  args = parser.parse_args()
62
  root = args.workspace_root.resolve()
 
72
  rows = select_cases(args.manifest, args.scope, args.rank, args.world_size, args.limit)
73
  if args.dry_run:
74
  print(json.dumps({"method": args.method, "rank": args.rank, "cases": [case_key(row) for row in rows],
75
+ "seed": args.seed, "generated_frames": args.max_generated,
76
+ "decmem_budget": args.decmem_budget}, indent=2))
77
  return
78
  torch.cuda.set_device(0)
79
+ adapter = load_adapter(args.method, root, args.decmem_budget)
80
  args.output.mkdir(parents=True, exist_ok=True)
81
  metadata = getattr(adapter, "metadata", getattr(adapter, "provenance", {}))
82
  if args.method in {"decmem", "matrix-game2"}:
 
85
  metadata = {**json.loads(manifest_path.read_text()),
86
  "instantiated_parameter_counts": adapter.parameter_counts,
87
  "runtime_strict_checkpoint_load": "passed"}
88
+ if args.method == "decmem":
89
+ for key in ("verification", "full_ready", "blocker"):
90
+ metadata.pop(key, None)
91
+ metadata["benchmark_protocol"] = metadata.pop("protocol")
92
+ metadata["generated_frames_per_case"] = args.max_generated
93
+ metadata["model"].update({key: adapter.memory[key] for key in ("stm_sliding_window", "ltm_topk")})
94
+ metadata["memory"] = adapter.memory
95
+ metadata["profile"] = adapter.memory["profile"]
96
  (args.output / f"method_manifest_rank{args.rank}.json").write_text(json.dumps(metadata, indent=2, default=str) + "\n")
97
  for row in rows:
98
  case = (load_minecraft_case if dataset == "minecraft" else load_re10k_case)(row, args.data_root)