echo-memory / diffsynth /trainers /latent_dataset.py
multimodalart's picture
multimodalart HF Staff
Upload folder using huggingface_hub
6e4b62e verified
Raw
History Blame Contribute Delete
5.12 kB
"""
LatentDataset: Load precomputed ctx and target latents for Context-as-Memory dataset.
Use after running precompute_ctx_target_latents.py. Returns samples with:
- context_latents: (1, C, K, H//8, W//8) - 1 latent per context frame
- target_latents: (1, C, T, H//8, W//8) - 1 latent per 4 target frames
- prompt, video_name, start_frame, end_frame, actions
Compatible with training that uses precomputed latents instead of encoding on the fly.
"""
import json
import os
import warnings
import torch
class LatentDataset(torch.utils.data.Dataset):
"""
Dataset that loads precomputed ctx and target latents.
"""
def __init__(
self,
latent_dir,
metadata_path=None,
action_base_path=None,
repeat=1,
num_frames=81,
context_frames=5,
target_frames_per_latent=4,
):
"""
Args:
latent_dir: Directory containing ctx_latents/ and target_latents/ subdirs.
metadata_path: Optional. If provided, used to get total_samples and validate.
action_base_path: Base path for action JSON files (for loading actions if not in .pt).
repeat: Dataset repeat factor.
num_frames: Expected num_frames per segment.
context_frames: Number of context frames (K).
target_frames_per_latent: Target: 1 latent per N frames.
"""
self.latent_dir = latent_dir
self.ctx_dir = os.path.join(latent_dir, "ctx_latents")
self.target_dir = os.path.join(latent_dir, "target_latents")
self.action_base_path = action_base_path or latent_dir
self.repeat = repeat
self.num_frames = num_frames
self.context_frames = context_frames
self.target_frames_per_latent = target_frames_per_latent
# Infer valid indices from existing files (both ctx and target must exist)
self._indices = []
if os.path.isdir(self.ctx_dir) and os.path.isdir(self.target_dir):
ctx_files = {f.replace(".pt", "") for f in os.listdir(self.ctx_dir) if f.endswith(".pt")}
target_files = {f.replace(".pt", "") for f in os.listdir(self.target_dir) if f.endswith(".pt")}
common = sorted([int(x) for x in ctx_files & target_files])
self._indices = common
if not self._indices:
meta_path = os.path.join(latent_dir, "metadata_precompute.json")
if os.path.isfile(meta_path):
with open(meta_path) as f:
meta = json.load(f)
self._total = meta.get("total_samples", 0)
self._indices = list(range(self._total))
else:
self._total = 0
else:
self._total = len(self._indices)
def __len__(self):
return self._total * self.repeat
def __getitem__(self, idx):
real_idx = idx % self._total
if self._indices is not None:
real_idx = self._indices[real_idx]
ctx_path = os.path.join(self.ctx_dir, f"{real_idx:08d}.pt")
target_path = os.path.join(self.target_dir, f"{real_idx:08d}.pt")
if not os.path.isfile(ctx_path) or not os.path.isfile(target_path):
warnings.warn(f"Latent files not found for idx {real_idx}. Returning None.")
return None
ctx_data = torch.load(ctx_path, map_location="cpu", weights_only=True)
target_data = torch.load(target_path, map_location="cpu", weights_only=True)
ctx_latent = ctx_data["latent"]
target_latent = target_data["latent"]
# Ensure batch dimension: (C, K, H, W) -> (1, C, K, H, W)
if ctx_latent.dim() == 4:
ctx_latent = ctx_latent.unsqueeze(0)
if target_latent.dim() == 4:
target_latent = target_latent.unsqueeze(0)
out = {
"context_latents": ctx_latent,
"target_latents": target_latent,
"prompt": ctx_data.get("prompt", ""),
"video_name": ctx_data.get("video_name"),
"start_frame": ctx_data.get("start_frame"),
"end_frame": ctx_data.get("end_frame"),
}
if "actions" in ctx_data and ctx_data["actions"] is not None:
out["actions"] = ctx_data["actions"]
elif "actions" in target_data and target_data["actions"] is not None:
out["actions"] = target_data["actions"]
return out
def get_latent_dataset_args(latent_dir, action_base_path=None, **kwargs):
"""Build argparse.Namespace for LatentDataset from precompute metadata."""
meta_path = os.path.join(latent_dir, "metadata_precompute.json")
if not os.path.isfile(meta_path):
return None
with open(meta_path) as f:
meta = json.load(f)
from argparse import Namespace
return Namespace(
latent_dir=latent_dir,
action_base_path=action_base_path or meta.get("dataset_base_path", latent_dir),
num_frames=meta.get("num_frames", 81),
context_frames=meta.get("context_frames", 5),
target_frames_per_latent=meta.get("target_frames_per_latent", 4),
**kwargs,
)