Elastic-forcing-code / 14B /code /tools /precompute_vjepa2_dense_nystrom_reference.py
Elastic-forcing's picture
Add training and inference source code
2ea2835 verified
Raw History Blame Contribute Delete
43 kB
#!/usr/bin/env python3
"""Precompute a video-level V-JEPA2 dense Nyström teacher reference.
The extraction path deliberately matches ``WanKVMMD``:
* read the final teacher latent from the ODE LMDB;
* decode with the frozen Wan VAE in bfloat16;
* map decoded pixels from ``[-1, 1]`` to ``[0, 1]``;
* select a deterministic contiguous temporal window, then apply the V-JEPA2
adapter's 292 -> 256 spatial preprocessing;
* execute complete V-JEPA2 blocks 0..the configured dense layer;
* retain the canonical 128 aligned token positions as one ``[128, 1024]``
video sample.
Each torchrun rank writes one indexed FP16 feature shard to a temporary
directory. Rank 0 validates and merges the shards, builds a Nyström reference,
publishes it atomically, and removes the temporary directory only after the
published pack has passed validation.
"""
from __future__ import annotations
import argparse
from datetime import timedelta
import gc
import hashlib
import json
import math
import os
from pathlib import Path
import shutil
import sys
import tempfile
import time
from typing import Any, Dict, Iterable, Optional, Sequence, Tuple
import torch
import torch.distributed as dist
REPO_ROOT = Path(__file__).resolve().parents[1]
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
TOOL_FORMAT_VERSION = 2
MIB = 1024 ** 2
FRAME_SAMPLING_CHOICES = (
"center_contiguous",
"random_contiguous_per_video",
)
TEACHER_TEMPORAL_WINDOW_DOMAIN = "teacher"
WINDOW_STARTS_HASH_ENCODING = "lmdb_index_ordered_uint64_big_endian"
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description=(
"Build a video-level Nyström RBF-MMD teacher reference from an "
"ODE teacher LMDB using the exact Wan-VAE/V-JEPA2 dense pipeline."
)
)
parser.add_argument(
"--teacher-lmdb",
type=Path,
required=True,
help="ODE LMDB containing latents and prompts arrays.",
)
parser.add_argument(
"--model-id",
type=str,
required=True,
help="Readable local Hugging Face V-JEPA2 model directory.",
)
parser.add_argument(
"--output",
type=Path,
required=True,
help="Final atomic torch reference-pack path.",
)
parser.add_argument(
"--max-samples",
type=int,
default=0,
help="Use the first N LMDB rows; 0 traverses the complete actual LMDB.",
)
parser.add_argument(
"--batch-size",
type=int,
default=1,
help="Per-rank extraction batch size; 1 is the memory-safe default.",
)
parser.add_argument("--layer", type=int, default=15)
parser.add_argument("--num-frames", type=int, default=64)
parser.add_argument(
"--frame-sampling",
choices=FRAME_SAMPLING_CHOICES,
default="center_contiguous",
help=(
"center_contiguous preserves the legacy adapter-selected center "
"window; random_contiguous_per_video uses one stable LMDB-row-keyed "
"contiguous window."
),
)
parser.add_argument(
"--source-num-frames",
type=int,
default=81,
help=(
"Expected decoded source length for random contiguous sampling "
"(default: 81). It is ignored by the legacy center path."
),
)
parser.add_argument(
"--window-seed",
type=str,
default="0",
help=(
"Non-empty string seed for stateless per-video temporal-window "
"selection."
),
)
parser.add_argument("--num-tokens", type=int, default=128)
parser.add_argument("--resize-short-side", type=int, default=292)
parser.add_argument("--crop-size", type=int, default=256)
parser.add_argument("--num-landmarks", type=int, default=256)
parser.add_argument("--seed", type=int, default=0)
parser.add_argument(
"--median-max-samples",
type=int,
default=2048,
help="Deterministic subset cap for automatic median bandwidth.",
)
parser.add_argument(
"--bandwidth",
type=float,
default=None,
help="Fixed Gaussian bandwidth; omitted lets the builder estimate it.",
)
parser.add_argument("--kmeans-iterations", type=int, default=20)
parser.add_argument("--ridge", type=float, default=1.0e-6)
parser.add_argument(
"--chunk-size",
type=int,
default=256,
help="Sample chunk size used by rank-0 Nyström construction.",
)
parser.add_argument(
"--tmp-dir",
type=Path,
default=None,
help=(
"Parent for FP16 rank shards. By default /dev/shm is preferred "
"when writable and large enough, then the platform temp directory."
),
)
parser.add_argument(
"--overwrite",
action="store_true",
help="Atomically replace an existing output pack.",
)
parser.add_argument(
"--log-every",
type=int,
default=25,
help="Rank-local extraction progress interval.",
)
parser.add_argument(
"--distributed-timeout-minutes",
type=int,
default=360,
help="Long timeout while rank 0 builds the reference after extraction.",
)
parser.add_argument(
"--dry-run",
action="store_true",
help=(
"Inspect LMDB/model geometry, distribution and disk budgets without "
"loading either model or writing files."
),
)
return parser
def _validate_args(args: argparse.Namespace) -> None:
positive = {
"batch_size": args.batch_size,
"num_frames": args.num_frames,
"source_num_frames": args.source_num_frames,
"num_tokens": args.num_tokens,
"resize_short_side": args.resize_short_side,
"crop_size": args.crop_size,
"num_landmarks": args.num_landmarks,
"median_max_samples": args.median_max_samples,
"kmeans_iterations": args.kmeans_iterations,
"chunk_size": args.chunk_size,
"distributed_timeout_minutes": args.distributed_timeout_minutes,
}
invalid = {name: value for name, value in positive.items() if int(value) <= 0}
if invalid:
raise ValueError(f"arguments must be positive: {invalid}")
if args.layer < 0:
raise ValueError("--layer must be non-negative")
if args.max_samples < 0:
raise ValueError("--max-samples must be non-negative")
if not isinstance(args.window_seed, str) or not args.window_seed:
raise ValueError("--window-seed must be a non-empty string")
if "\0" in args.window_seed:
raise ValueError("--window-seed must not contain NUL characters")
if (
args.frame_sampling == "random_contiguous_per_video"
and args.source_num_frames < args.num_frames
):
raise ValueError(
"--source-num-frames must be >= --num-frames for random "
"contiguous sampling"
)
if args.seed != 0:
raise ValueError(
"--seed currently must be 0; landmark selection is deterministic")
if args.resize_short_side < args.crop_size:
raise ValueError("--resize-short-side must be >= --crop-size")
if args.ridge <= 0.0 or not math.isfinite(float(args.ridge)):
raise ValueError("--ridge must be finite and positive")
if args.bandwidth is not None and (
args.bandwidth <= 0.0 or not math.isfinite(float(args.bandwidth))
):
raise ValueError("--bandwidth must be finite and positive")
if args.log_every < 0:
raise ValueError("--log-every must be non-negative")
def _read_model_geometry(model_id: str) -> Dict[str, int]:
model_path = Path(os.path.expanduser(model_id))
if not model_path.is_dir():
raise FileNotFoundError(
f"--model-id must be a readable local directory, got {model_id!r}"
)
config_path = model_path / "config.json"
if not config_path.is_file():
raise FileNotFoundError(f"V-JEPA2 config not found: {config_path}")
with config_path.open(encoding="utf-8") as handle:
config = json.load(handle)
required = (
"hidden_size",
"num_hidden_layers",
"patch_size",
"tubelet_size",
)
missing = [key for key in required if key not in config]
if missing:
raise ValueError(
f"V-JEPA2 config {config_path} is missing fields {missing}")
return {key: int(config[key]) for key in required}
def _inspect_lmdb(path: Path) -> Dict[str, Any]:
try:
import lmdb
except ImportError as exc:
raise ImportError(
"python-lmdb is required; use the same Python/PYTHONPATH as training"
) from exc
from utils.lmdb import get_array_shape_from_lmdb
resolved = path.expanduser().resolve()
if not resolved.is_dir():
raise FileNotFoundError(f"teacher LMDB directory not found: {resolved}")
env = lmdb.open(
str(resolved),
readonly=True,
lock=False,
readahead=False,
meminit=False,
)
try:
latents_shape = tuple(
int(value) for value in get_array_shape_from_lmdb(env, "latents"))
prompts_shape = tuple(
int(value) for value in get_array_shape_from_lmdb(env, "prompts"))
finally:
env.close()
if len(latents_shape) != 6:
raise ValueError(
"teacher LMDB latents must be "
"[samples,trajectory_steps,frames,channels,height,width], got "
f"{latents_shape}"
)
if prompts_shape != (latents_shape[0],):
raise ValueError(
"teacher LMDB prompts/latents count mismatch: "
f"{prompts_shape} vs {latents_shape}")
data_path = resolved / "data.mdb"
stat = data_path.stat()
return {
"path": str(resolved),
"latents_shape": latents_shape,
"prompts_shape": prompts_shape,
"data_mdb_size_bytes": int(stat.st_size),
"data_mdb_mtime_ns": int(stat.st_mtime_ns),
}
def _selected_count(total: int, max_samples: int) -> int:
count = int(total) if int(max_samples) == 0 else min(int(total), int(max_samples))
if count <= 0:
raise ValueError("the selected teacher reference set is empty")
return count
def _rank_indices(count: int, rank: int, world_size: int) -> range:
return range(int(rank), int(count), int(world_size))
def _window_starts_sha256(starts: Sequence[int]) -> str:
"""Hash starts in selected-LMDB-index order using fixed-width integers."""
digest = hashlib.sha256()
for start in starts:
value = int(start)
if value < 0:
raise ValueError("temporal window starts must be non-negative")
digest.update(value.to_bytes(8, byteorder="big", signed=False))
return digest.hexdigest()
def _random_teacher_window_starts(
args: argparse.Namespace,
indices: Sequence[int],
) -> list[int]:
from video_fd.encoders.vjepa2_temporal_sampling import (
stateless_temporal_window_start,
)
return [
stateless_temporal_window_start(
str(args.window_seed),
TEACHER_TEMPORAL_WINDOW_DOMAIN,
int(index),
source_num_frames=int(args.source_num_frames),
window_num_frames=int(args.num_frames),
)
for index in indices
]
def _temporal_sampling_plan(
args: argparse.Namespace,
selected_count: int,
) -> Dict[str, Any]:
"""Return JSON-safe temporal provenance visible in plan/dry-run output."""
sampling = str(args.frame_sampling)
common = {
"frame_sampling": sampling,
"window_num_frames": int(args.num_frames),
"window_starts_sha256_encoding": WINDOW_STARTS_HASH_ENCODING,
}
if sampling == "center_contiguous":
# Preserve the legacy path: the adapter derives the center from the
# actual decoded length. source_num_frames/window_seed are random-mode
# inputs and intentionally do not change the old center contract.
return {
**common,
"source_num_frames": None,
"window_seed": None,
"window_domain": None,
"window_start_min": None,
"window_start_max": None,
"teacher_window_starts_sha256": None,
"selection": "legacy_adapter_center",
}
starts = _random_teacher_window_starts(
args, list(range(int(selected_count))))
return {
**common,
"source_num_frames": int(args.source_num_frames),
"window_seed": str(args.window_seed),
"window_domain": TEACHER_TEMPORAL_WINDOW_DOMAIN,
"window_start_min": 0,
"window_start_max": (
int(args.source_num_frames) - int(args.num_frames)),
"teacher_window_starts_sha256": _window_starts_sha256(starts),
"selection": (
"sha256(seed\\0domain\\0decimal_lmdb_index)"
"_first_u64_big_endian_mod_num_windows"
),
}
def _select_teacher_temporal_windows(
video_clip_01: torch.Tensor,
sample_indices: Sequence[int],
args: argparse.Namespace,
) -> Tuple[torch.Tensor, list[int]]:
"""Select teacher windows while retaining the legacy center behavior."""
if video_clip_01.ndim != 5:
raise ValueError(
"decoded teacher video must be [B,T,3,H,W], got "
f"{tuple(video_clip_01.shape)}")
if video_clip_01.shape[0] != len(sample_indices):
raise ValueError(
"decoded teacher batch/sample index count mismatch: "
f"{video_clip_01.shape[0]} != {len(sample_indices)}")
total_frames = int(video_clip_01.shape[1])
window_frames = int(args.num_frames)
if total_frames < window_frames:
raise ValueError(
f"decoded teacher video has {total_frames} frames, fewer than "
f"--num-frames={window_frames}")
if str(args.frame_sampling) == "center_contiguous":
start = (total_frames - window_frames) // 2
# Do not crop here: forwarding the original decoded video preserves the
# historical adapter-centered preprocessing byte for byte.
return video_clip_01, [start] * len(sample_indices)
expected_source_frames = int(args.source_num_frames)
if total_frames != expected_source_frames:
raise ValueError(
"random contiguous teacher sampling requires the decoded source "
f"length to equal --source-num-frames: {total_frames} != "
f"{expected_source_frames}")
starts = _random_teacher_window_starts(args, sample_indices)
clips = torch.stack([
video_clip_01[batch_index].narrow(0, start, window_frames)
for batch_index, start in enumerate(starts)
], dim=0)
return clips, starts
def _required_temp_bytes(
count: int,
num_tokens: int,
feature_dim: int,
) -> int:
feature_bytes = int(count) * int(num_tokens) * int(feature_dim) * 2
return int(math.ceil(feature_bytes * 1.15)) + 64 * MIB
def _required_output_bytes(
num_landmarks: int,
num_tokens: int,
feature_dim: int,
) -> int:
landmark_bytes = (
int(num_landmarks) * int(num_tokens) * int(feature_dim) * 4)
return int(math.ceil(landmark_bytes * 1.25)) + 32 * MIB
def _choose_temp_parent(
requested: Optional[Path],
required_bytes: int,
) -> Tuple[Path, int]:
candidates: Sequence[Path]
if requested is not None:
candidates = (requested.expanduser(),)
else:
candidates = (Path("/dev/shm"), Path(tempfile.gettempdir()))
errors = []
for candidate in candidates:
try:
candidate = candidate.resolve()
if not candidate.is_dir():
errors.append(f"{candidate}: not a directory")
continue
if not os.access(candidate, os.W_OK | os.X_OK):
errors.append(f"{candidate}: not writable")
continue
free = int(shutil.disk_usage(candidate).free)
if free < required_bytes:
errors.append(
f"{candidate}: {free / MIB:.1f} MiB free, "
f"{required_bytes / MIB:.1f} MiB required")
continue
return candidate, free
except OSError as exc:
errors.append(f"{candidate}: {exc}")
raise RuntimeError(
"no temporary shard directory has sufficient capacity: "
+ "; ".join(errors)
)
def _check_output(
output: Path,
*,
required_bytes: int,
overwrite: bool,
) -> Dict[str, Any]:
output = output.expanduser().resolve()
if output.exists() and not overwrite:
raise FileExistsError(
f"output already exists: {output}; pass --overwrite to replace it")
parent = output.parent
if not parent.exists():
existing = parent
while not existing.exists() and existing != existing.parent:
existing = existing.parent
if not existing.is_dir():
raise FileNotFoundError(
f"no existing output parent for capacity check: {parent}")
capacity_parent = existing
else:
capacity_parent = parent
free = int(shutil.disk_usage(capacity_parent).free)
if free < required_bytes:
raise RuntimeError(
f"output filesystem has only {free / MIB:.1f} MiB free; "
f"at least {required_bytes / MIB:.1f} MiB is required")
return {
"path": str(output),
"capacity_parent": str(capacity_parent),
"free_bytes": free,
}
def _plan(args: argparse.Namespace, world_size: int) -> Dict[str, Any]:
lmdb_info = _inspect_lmdb(args.teacher_lmdb)
geometry = _read_model_geometry(args.model_id)
if args.layer >= geometry["num_hidden_layers"]:
raise ValueError(
f"--layer={args.layer} exceeds model's "
f"{geometry['num_hidden_layers']} blocks")
if args.num_frames % geometry["tubelet_size"] != 0:
raise ValueError("--num-frames must be divisible by model tubelet_size")
if args.crop_size % geometry["patch_size"] != 0:
raise ValueError("--crop-size must be divisible by model patch_size")
raw_tokens = (
args.num_frames // geometry["tubelet_size"]
* (args.crop_size // geometry["patch_size"]) ** 2
)
if args.num_tokens > raw_tokens:
raise ValueError(
f"--num-tokens={args.num_tokens} exceeds raw grid {raw_tokens}")
count = _selected_count(lmdb_info["latents_shape"][0], args.max_samples)
if args.num_landmarks > count:
raise ValueError(
f"--num-landmarks={args.num_landmarks} exceeds {count} samples")
temp_required = _required_temp_bytes(
count, args.num_tokens, geometry["hidden_size"])
temp_parent, temp_free = _choose_temp_parent(args.tmp_dir, temp_required)
output_required = _required_output_bytes(
args.num_landmarks, args.num_tokens, geometry["hidden_size"])
output_info = _check_output(
args.output,
required_bytes=output_required,
overwrite=args.overwrite,
)
per_rank = [
len(_rank_indices(count, rank, world_size))
for rank in range(world_size)
]
return {
"tool_format_version": TOOL_FORMAT_VERSION,
"teacher_lmdb": lmdb_info,
"model_id": str(Path(args.model_id).expanduser().resolve()),
"model_geometry": geometry,
"feature_geometry": {
"raw_tokens": raw_tokens,
"sample_shape": [args.num_tokens, geometry["hidden_size"]],
},
"selected_samples": count,
"temporal_sampling": _temporal_sampling_plan(args, count),
"world_size": int(world_size),
"samples_per_rank": per_rank,
"num_landmarks": int(args.num_landmarks),
"temporary": {
"parent": str(temp_parent),
"required_bytes": temp_required,
"free_bytes": temp_free,
},
"output": {
**output_info,
"required_bytes": output_required,
},
}
def _setup_distributed(timeout_minutes: int) -> Tuple[torch.device, int, int]:
world_size = int(os.environ.get("WORLD_SIZE", "1"))
rank = int(os.environ.get("RANK", "0"))
local_rank = int(os.environ.get("LOCAL_RANK", "0"))
if not torch.cuda.is_available():
raise RuntimeError(
"reference feature extraction requires CUDA; use --dry-run for "
"CPU-only validation")
torch.cuda.set_device(local_rank)
if world_size > 1:
dist.init_process_group(
backend="nccl",
timeout=timedelta(minutes=int(timeout_minutes)),
)
rank = dist.get_rank()
world_size = dist.get_world_size()
return torch.device("cuda", local_rank), rank, world_size
def _barrier() -> None:
if dist.is_available() and dist.is_initialized():
dist.barrier()
def _broadcast_rank0_object(value: Any, rank: int) -> Any:
if not (dist.is_available() and dist.is_initialized()):
return value
payload = [value if rank == 0 else None]
dist.broadcast_object_list(payload, src=0)
return payload[0]
def _atomic_torch_save(value: Any, path: Path) -> None:
temporary = path.with_name(f".{path.name}.tmp-{os.getpid()}")
try:
torch.save(value, temporary)
os.replace(temporary, path)
finally:
if temporary.exists():
temporary.unlink()
def _prompt_entry_digest(index: int, prompt: str) -> bytes:
digest = hashlib.sha256()
digest.update(int(index).to_bytes(8, byteorder="little", signed=False))
digest.update(b"\0")
digest.update(str(prompt).encode("utf-8"))
return digest.digest()
@torch.no_grad()
def _extract_rank_shard(
args: argparse.Namespace,
plan: Dict[str, Any],
*,
device: torch.device,
rank: int,
world_size: int,
shard_path: Path,
) -> None:
from utils.dataset import ODERegressionLMDBDataset
from utils.wan_wrapper import WanVAEWrapper
from video_fd.encoders.vjepa2_adapter import VJEPA2Adapter
from video_fd.encoders.vjepa2_dense_metadata import (
uniform_dense_token_indices,
)
selected_count = int(plan["selected_samples"])
assigned = list(_rank_indices(selected_count, rank, world_size))
feature_dim = int(plan["model_geometry"]["hidden_size"])
raw_tokens = int(plan["feature_geometry"]["raw_tokens"])
dataset = ODERegressionLMDBDataset(
str(args.teacher_lmdb.expanduser().resolve()),
max_pair=selected_count,
)
if len(dataset) != selected_count:
raise RuntimeError(
f"LMDB length changed after planning: {len(dataset)} != "
f"{selected_count}")
vae = (
WanVAEWrapper()
.to(device=device, dtype=torch.bfloat16)
.eval()
.requires_grad_(False)
)
encoder = (
VJEPA2Adapter({
"name": "vjepa2_dense",
"model_id": str(Path(args.model_id).expanduser().resolve()),
"input_frames": int(args.num_frames),
"dense_num_frames": int(args.num_frames),
"input_size": int(args.crop_size),
"resize_short_side": int(args.resize_short_side),
"crop_mode": "center",
"dense_only": True,
"dense_layer_index": int(args.layer),
})
.to(device=device, dtype=torch.float32)
.eval()
.requires_grad_(False)
)
actual = {
"raw_tokens": int(encoder.dense_raw_tokens),
"feature_dim": int(encoder.dense_feature_dim),
"retained_blocks": int(encoder.retained_dense_blocks),
}
expected = {
"raw_tokens": raw_tokens,
"feature_dim": feature_dim,
"retained_blocks": int(args.layer) + 1,
}
if actual != expected:
raise RuntimeError(
f"loaded V-JEPA2 geometry mismatch: actual={actual}, "
f"expected={expected}")
token_indices = uniform_dense_token_indices(
raw_tokens,
args.num_tokens,
device=device,
)
feature_chunks = []
index_chunks = []
prompt_digests = []
temporal_window_starts = []
started = time.monotonic()
for offset in range(0, len(assigned), args.batch_size):
batch_indices = assigned[offset:offset + args.batch_size]
items = [dataset[index] for index in batch_indices]
trajectories = [item["ode_latent"] for item in items]
expected_tail = tuple(plan["teacher_lmdb"]["latents_shape"][1:])
for index, trajectory in zip(batch_indices, trajectories):
if tuple(trajectory.shape) != expected_tail:
raise ValueError(
f"LMDB row {index} shape {tuple(trajectory.shape)} != "
f"{expected_tail}")
if not torch.isfinite(trajectory).all():
raise FloatingPointError(
f"LMDB row {index} contains non-finite latents")
latent = torch.stack(
[trajectory[-1] for trajectory in trajectories],
dim=0,
).to(device=device, dtype=torch.bfloat16)
pixels = vae.decode_to_pixel(latent.detach())
video01 = ((pixels + 1.0) * 0.5).clamp(0.0, 1.0)
encoder_video01, batch_window_starts = (
_select_teacher_temporal_windows(
video01,
batch_indices,
args,
)
)
features = encoder.forward_dense(
encoder_video01,
layer_index=int(args.layer),
token_indices=token_indices,
gradient_checkpointing=False,
).float()
expected_shape = (
len(batch_indices),
int(args.num_tokens),
feature_dim,
)
if tuple(features.shape) != expected_shape:
raise RuntimeError(
f"V-JEPA2 features {tuple(features.shape)} != "
f"{expected_shape}")
if not torch.isfinite(features).all():
raise FloatingPointError(
f"non-finite V-JEPA2 features for indices {batch_indices}")
features_fp16 = features.to(dtype=torch.float16, device="cpu")
if not torch.isfinite(features_fp16).all():
raise FloatingPointError(
"FP16 shard conversion overflowed for indices "
f"{batch_indices}")
feature_chunks.append(features_fp16)
index_chunks.append(torch.tensor(batch_indices, dtype=torch.long))
prompt_digests.extend(
_prompt_entry_digest(index, item["prompts"])
for index, item in zip(batch_indices, items)
)
temporal_window_starts.extend(batch_window_starts)
del (
latent,
pixels,
video01,
encoder_video01,
features,
features_fp16,
items,
trajectories,
)
completed = min(offset + len(batch_indices), len(assigned))
if args.log_every > 0 and (
completed == len(assigned) or completed % args.log_every == 0
):
print(
f"[extract rank {rank}] {completed}/{len(assigned)} "
f"({time.monotonic() - started:.1f}s)",
flush=True,
)
if feature_chunks:
shard_features = torch.cat(feature_chunks, dim=0).contiguous()
shard_indices = torch.cat(index_chunks, dim=0).contiguous()
digest_tensor = torch.tensor(
[list(value) for value in prompt_digests],
dtype=torch.uint8,
)
window_starts_tensor = torch.tensor(
temporal_window_starts, dtype=torch.long)
else:
shard_features = torch.empty(
0, args.num_tokens, feature_dim, dtype=torch.float16)
shard_indices = torch.empty(0, dtype=torch.long)
digest_tensor = torch.empty(0, 32, dtype=torch.uint8)
window_starts_tensor = torch.empty(0, dtype=torch.long)
payload = {
"shard_format_version": TOOL_FORMAT_VERSION,
"rank": int(rank),
"world_size": int(world_size),
"indices": shard_indices,
"features": shard_features,
"prompt_entry_sha256": digest_tensor,
"temporal_window_starts": window_starts_tensor,
"sample_shape": [int(args.num_tokens), feature_dim],
"feature_dtype": "float16",
}
_atomic_torch_save(payload, shard_path)
print(
f"[extract rank {rank}] wrote {shard_path} "
f"{tuple(shard_features.shape)}",
flush=True,
)
del encoder, vae, dataset, payload, feature_chunks, shard_features
gc.collect()
torch.cuda.empty_cache()
def _load_and_merge_shards(
shard_dir: Path,
*,
world_size: int,
selected_count: int,
sample_shape: Sequence[int],
) -> Tuple[torch.Tensor, str, str, str]:
features_parts = []
indices_parts = []
digest_parts = []
window_start_parts = []
for rank in range(world_size):
path = shard_dir / f"rank_{rank:05d}.pt"
if not path.is_file():
raise FileNotFoundError(f"missing rank shard: {path}")
payload = torch.load(path, map_location="cpu", weights_only=False)
if int(payload.get("shard_format_version", -1)) != TOOL_FORMAT_VERSION:
raise ValueError(f"incompatible shard format in {path}")
if int(payload.get("rank", -1)) != rank:
raise ValueError(f"rank identity mismatch in {path}")
if int(payload.get("world_size", -1)) != world_size:
raise ValueError(f"world-size mismatch in {path}")
features = payload.get("features")
indices = payload.get("indices")
digests = payload.get("prompt_entry_sha256")
window_starts = payload.get("temporal_window_starts")
expected_feature_tail = tuple(int(value) for value in sample_shape)
if (
not isinstance(features, torch.Tensor)
or features.dtype != torch.float16
or features.ndim != 3
or tuple(features.shape[1:]) != expected_feature_tail
):
raise ValueError(
f"invalid FP16 feature shard {path}: "
f"{getattr(features, 'shape', None)}")
if (
not isinstance(indices, torch.Tensor)
or indices.dtype != torch.long
or tuple(indices.shape) != (features.shape[0],)
):
raise ValueError(f"invalid indices in {path}")
if (
not isinstance(digests, torch.Tensor)
or digests.dtype != torch.uint8
or tuple(digests.shape) != (features.shape[0], 32)
):
raise ValueError(f"invalid prompt hashes in {path}")
if (
not isinstance(window_starts, torch.Tensor)
or window_starts.dtype != torch.long
or tuple(window_starts.shape) != (features.shape[0],)
or bool((window_starts < 0).any().item())
):
raise ValueError(f"invalid temporal window starts in {path}")
if not torch.isfinite(features).all():
raise FloatingPointError(f"non-finite FP16 features in {path}")
features_parts.append(features)
indices_parts.append(indices)
digest_parts.append(digests)
window_start_parts.append(window_starts)
features = torch.cat(features_parts, dim=0)
indices = torch.cat(indices_parts, dim=0)
digests = torch.cat(digest_parts, dim=0)
window_starts = torch.cat(window_start_parts, dim=0)
if features.shape[0] != selected_count:
raise ValueError(
f"rank shards contain {features.shape[0]} samples, expected "
f"{selected_count}")
order = torch.argsort(indices, stable=True)
indices = indices.index_select(0, order)
expected_indices = torch.arange(selected_count, dtype=torch.long)
if not torch.equal(indices, expected_indices):
raise ValueError(
"rank shards do not cover each selected LMDB index exactly once")
features = features.index_select(0, order).contiguous()
digests = digests.index_select(0, order).contiguous()
window_starts = window_starts.index_select(0, order).contiguous()
prompt_index_hash = hashlib.sha256(
digests.numpy().tobytes()).hexdigest()
feature_bank_hash = hashlib.sha256(
features.view(torch.uint8).numpy().tobytes()).hexdigest()
window_starts_hash = _window_starts_sha256(window_starts.tolist())
return (
features,
prompt_index_hash,
feature_bank_hash,
window_starts_hash,
)
def _reference_metadata(
args: argparse.Namespace,
plan: Dict[str, Any],
*,
prompt_index_hash: str,
feature_bank_hash: str,
teacher_window_starts_hash: Optional[str] = None,
) -> Dict[str, Any]:
from video_fd.encoders.vjepa2_dense_metadata import (
canonical_vjepa2_dense_feature_metadata,
)
geometry = plan["model_geometry"]
feature_kwargs = {
"model_id": str(Path(args.model_id).expanduser().resolve()),
"layer_index": args.layer,
"num_frames": args.num_frames,
"resize_short_side": args.resize_short_side,
"crop_size": args.crop_size,
"raw_tokens": plan["feature_geometry"]["raw_tokens"],
"num_tokens": args.num_tokens,
"feature_dim": geometry["hidden_size"],
}
if args.frame_sampling == "random_contiguous_per_video":
feature_kwargs.update({
"frame_sampling": args.frame_sampling,
"source_num_frames": args.source_num_frames,
"window_seed": args.window_seed,
})
feature_metadata = canonical_vjepa2_dense_feature_metadata(
**feature_kwargs)
source_metadata = {
"kind": "ode_teacher_lmdb_final_latent",
"lmdb_path": plan["teacher_lmdb"]["path"],
"latents_shape": list(plan["teacher_lmdb"]["latents_shape"]),
"prompts_shape": list(plan["teacher_lmdb"]["prompts_shape"]),
"selected_samples": int(plan["selected_samples"]),
"selected_index_start": 0,
"selected_index_stop_exclusive": int(
plan["selected_samples"]),
"trajectory_index": -1,
"lmdb_storage_dtype": "float16",
"data_mdb_size_bytes": int(
plan["teacher_lmdb"]["data_mdb_size_bytes"]),
"data_mdb_mtime_ns": int(
plan["teacher_lmdb"]["data_mdb_mtime_ns"]),
"prompt_index_sha256": prompt_index_hash,
"feature_bank_fp16_sha256": feature_bank_hash,
}
temporal_sampling = plan["temporal_sampling"]
if temporal_sampling["frame_sampling"] == (
"random_contiguous_per_video"
):
expected_starts_hash = temporal_sampling[
"teacher_window_starts_sha256"]
if teacher_window_starts_hash != expected_starts_hash:
raise ValueError(
"merged teacher temporal-window starts do not match the "
"dry-run/plan identity: "
f"{teacher_window_starts_hash!r} != {expected_starts_hash!r}"
)
source_metadata.update({
"frame_sampling": temporal_sampling["frame_sampling"],
"source_num_frames": temporal_sampling["source_num_frames"],
"window_num_frames": temporal_sampling["window_num_frames"],
"window_seed": temporal_sampling["window_seed"],
"window_domain": temporal_sampling["window_domain"],
"window_start_min": temporal_sampling["window_start_min"],
"window_start_max": temporal_sampling["window_start_max"],
"teacher_window_starts_sha256": teacher_window_starts_hash,
"window_starts_sha256_encoding": (
temporal_sampling["window_starts_sha256_encoding"]),
})
return {
"feature": feature_metadata,
"reference": {
"source": source_metadata,
"extraction": {
"tool": (
"tools/precompute_vjepa2_dense_nystrom_reference.py"),
"tool_format_version": TOOL_FORMAT_VERSION,
"world_size": int(plan["world_size"]),
"vae_compute_dtype": "bfloat16",
"vae_decode_use_cache": False,
"decoded_pixel_transform": "((pixels+1)*0.5).clamp(0,1)",
"vjepa2_compute_dtype": "float32",
"rank_shard_dtype": "float16",
},
"build_request": {
"num_landmarks": int(args.num_landmarks),
"seed": int(args.seed),
"bandwidth": (
None if args.bandwidth is None else float(args.bandwidth)),
"median_max_samples": int(args.median_max_samples),
"kmeans_iterations": int(args.kmeans_iterations),
"ridge": float(args.ridge),
"chunk_size": int(args.chunk_size),
},
},
}
def _build_and_save_reference(
args: argparse.Namespace,
plan: Dict[str, Any],
shard_dir: Path,
*,
device: torch.device,
) -> Dict[str, Any]:
from losses.video_nystrom_mmd import (
build_video_nystrom_reference,
load_reference_pack,
save_reference_pack,
validate_reference_pack,
)
(
features,
prompt_index_hash,
feature_bank_hash,
teacher_window_starts_hash,
) = _load_and_merge_shards(
shard_dir,
world_size=int(plan["world_size"]),
selected_count=int(plan["selected_samples"]),
sample_shape=plan["feature_geometry"]["sample_shape"],
)
metadata = _reference_metadata(
args,
plan,
prompt_index_hash=prompt_index_hash,
feature_bank_hash=feature_bank_hash,
teacher_window_starts_hash=teacher_window_starts_hash,
)
print(
f"[merge] building m={args.num_landmarks} Nyström reference from "
f"{tuple(features.shape)} FP16 video matrices on {device}",
flush=True,
)
reference = features.to(device=device, non_blocking=False)
pack = build_video_nystrom_reference(
reference,
num_landmarks=int(args.num_landmarks),
metadata=metadata,
bandwidth=args.bandwidth,
median_max_samples=int(args.median_max_samples),
kmeans_iterations=int(args.kmeans_iterations),
ridge=float(args.ridge),
chunk_size=int(args.chunk_size),
seed=int(args.seed),
)
validate_reference_pack(pack)
del reference
torch.cuda.empty_cache()
output = args.output.expanduser().resolve()
output.parent.mkdir(parents=True, exist_ok=True)
temporary = output.with_name(f".{output.name}.tmp-{os.getpid()}")
try:
save_reference_pack(pack, temporary)
published = load_reference_pack(temporary, map_location="cpu")
validate_reference_pack(published)
if output.exists() and not args.overwrite:
raise FileExistsError(
f"output appeared during construction: {output}")
os.replace(temporary, output)
finally:
if temporary.exists():
temporary.unlink()
return {
"output": str(output),
"samples": int(features.shape[0]),
"sample_shape": list(features.shape[1:]),
"num_landmarks": int(pack["landmarks"].shape[0]),
"sigma": float(torch.as_tensor(pack["sigma"]).item()),
"content_hash": str(pack["content_hash"]),
"prompt_index_sha256": prompt_index_hash,
"feature_bank_fp16_sha256": feature_bank_hash,
"teacher_window_starts_sha256": teacher_window_starts_hash,
}
def _run(args: argparse.Namespace) -> None:
_validate_args(args)
env_world_size = int(os.environ.get("WORLD_SIZE", "1"))
env_rank = int(os.environ.get("RANK", "0"))
plan = _plan(args, env_world_size)
if args.dry_run:
if env_rank == 0:
print(json.dumps(plan, indent=2, sort_keys=True), flush=True)
return
device, rank, world_size = _setup_distributed(
args.distributed_timeout_minutes)
if world_size != int(plan["world_size"]):
raise RuntimeError(
f"distributed world size changed after planning: {world_size} != "
f"{plan['world_size']}")
temp_result = None
if rank == 0:
try:
parent = Path(plan["temporary"]["parent"])
shard_dir = Path(tempfile.mkdtemp(
prefix="vjepa2_dense_nystrom_",
dir=parent,
))
temp_result = {"path": str(shard_dir), "error": None}
except Exception as exc:
temp_result = {
"path": None,
"error": f"{type(exc).__name__}: {exc}",
}
temp_result = _broadcast_rank0_object(temp_result, rank)
if temp_result["error"] is not None:
raise RuntimeError(
f"rank 0 could not create shard directory: "
f"{temp_result['error']}")
shard_dir = Path(temp_result["path"])
success = False
try:
_extract_rank_shard(
args,
plan,
device=device,
rank=rank,
world_size=world_size,
shard_path=shard_dir / f"rank_{rank:05d}.pt",
)
_barrier()
summary = None
build_error = None
if rank == 0:
try:
summary = _build_and_save_reference(
args,
plan,
shard_dir,
device=device,
)
except Exception as exc:
import traceback
traceback.print_exc()
build_error = f"{type(exc).__name__}: {exc}"
outcome = _broadcast_rank0_object(
{"summary": summary, "error": build_error},
rank,
)
if outcome["error"] is not None:
raise RuntimeError(
f"rank 0 reference construction failed: {outcome['error']}")
_barrier()
success = True
if rank == 0:
print(json.dumps(
{"status": "ok", **outcome["summary"]},
indent=2,
sort_keys=True,
), flush=True)
finally:
if rank == 0:
if success:
shutil.rmtree(shard_dir)
print(f"[cleanup] removed temporary shards {shard_dir}", flush=True)
else:
print(
f"[cleanup] preserving failed-run shards at {shard_dir}",
flush=True,
)
if dist.is_available() and dist.is_initialized():
dist.destroy_process_group()
def main(argv: Optional[Iterable[str]] = None) -> None:
args = build_parser().parse_args(argv)
_run(args)
if __name__ == "__main__":
main()