Spaces:
Running on Zero
Running on Zero
Download src/dataset/tensor.py from hugging-apps/looking-ahead-ti2v: direct link, hf CLI and curl.
- Browser
- Download file 8.08 kB
-
https://huggingface.co/spaces/hugging-apps/looking-ahead-ti2v/resolve/main/src/dataset/tensor.py
- Command line
-
hf download hf://spaces/hugging-apps/looking-ahead-ti2v/src/dataset/tensor.py
-
curl -L -o tensor.py https://huggingface.co/spaces/hugging-apps/looking-ahead-ti2v/resolve/main/src/dataset/tensor.py
8.08 kB
| import io | |
| import json | |
| import logging | |
| import os | |
| from collections.abc import Callable | |
| from typing import Any, Union | |
| import numpy as np | |
| import torch | |
| from .utils import BaseWebDataset | |
| logger = logging.getLogger(__name__) | |
| def _load_pth_bytes(raw: bytes) -> Any: | |
| """Load a torch.save-serialized object from raw bytes.""" | |
| return torch.load(io.BytesIO(raw), map_location="cpu", weights_only=False) | |
| def _coerce_latent(obj: Any) -> torch.Tensor: | |
| """Extract a latent tensor from the deserialized .pth payload. | |
| Strict mode: raises ``TypeError`` / ``KeyError`` if no tensor can be | |
| extracted. Accepts: | |
| - a bare ``torch.Tensor`` (returned as-is) | |
| - a numpy array -> converted to tensor | |
| - a dict containing one of the common keys: ``vae_latent``, | |
| ``latent``, ``latents``, ``data`` -> returns that value | |
| - a dict with exactly one tensor value -> returns it | |
| Returns: | |
| A tensor that downstream decoders expect to be 4-D ``[C, T, H, W]``; | |
| dimensionality is validated by :meth:`PytorchTensorWebDataset._decode_payload`. | |
| """ | |
| if isinstance(obj, torch.Tensor): | |
| return obj | |
| if isinstance(obj, np.ndarray): | |
| return torch.from_numpy(obj) | |
| if isinstance(obj, dict): | |
| for k in ("vae_latent", "latent", "latents", "data"): | |
| if k in obj: | |
| v = obj[k] | |
| if isinstance(v, torch.Tensor): | |
| return v | |
| if isinstance(v, np.ndarray): | |
| return torch.from_numpy(v) | |
| raise TypeError( | |
| f"_coerce_latent: payload[{k!r}] has unsupported type {type(v).__name__}" | |
| ) | |
| # Fallback: if there's exactly one tensor value, take it. | |
| tensor_vals = [v for v in obj.values() if isinstance(v, torch.Tensor)] | |
| if len(tensor_vals) == 1: | |
| return tensor_vals[0] | |
| raise KeyError( | |
| f"_coerce_latent: dict payload has no recognized latent key " | |
| f"(expected one of vae_latent/latent/latents/data); " | |
| f"keys={list(obj.keys())!r}" | |
| ) | |
| raise TypeError(f"_coerce_latent: unsupported payload type {type(obj).__name__}") | |
| class PytorchTensorWebDataset(BaseWebDataset): | |
| """Pre-encoded latent tar input, with JSONL captions as the authority. | |
| Store one <key>.latent.pth in [C,T,H,W]; yield float32 [T,C,H,W]. | |
| Optional meta.json is parsed; action payloads are ignored. The runtime has | |
| no payload-layout selector, custom transform, or disk annotation cache. | |
| target_frame_num crops a prefix and raises on short samples. | |
| """ | |
| SHARD_KEY_STRIP_PREFIX = "latent_" | |
| def __init__( | |
| self, | |
| data_path: Union[str, "os.PathLike[str]"], | |
| annotation_path: str, | |
| target_frame_num: int | None = None, | |
| shuffle: int = 1000, | |
| shuffle_initial: int = 100, | |
| shardshuffle: bool | int = True, | |
| resampled: bool = False, | |
| nodesplitter: Callable | None = None, | |
| handler: Callable | None = None, | |
| length: int | None = None, | |
| seed: int = 0, | |
| global_rank: int = 0, | |
| seed_namespace: int = 0, | |
| ): | |
| super().__init__( | |
| data_path=data_path, | |
| annotation_path=annotation_path, | |
| shuffle=shuffle, | |
| shuffle_initial=shuffle_initial, | |
| shardshuffle=shardshuffle, | |
| resampled=resampled, | |
| nodesplitter=nodesplitter, | |
| handler=handler, | |
| length=length, | |
| seed=seed, | |
| global_rank=global_rank, | |
| seed_namespace=seed_namespace, | |
| ) | |
| if isinstance(target_frame_num, bool) or ( | |
| target_frame_num is not None and not isinstance(target_frame_num, int) | |
| ): | |
| raise TypeError("target_frame_num must be a positive integer or None.") | |
| if target_frame_num is not None and target_frame_num <= 0: | |
| raise ValueError("target_frame_num must be greater than zero.") | |
| self.target_frame_num = target_frame_num | |
| logger.info( | |
| "Initialized %s with %d shard(s) " | |
| "(annotation_path=%r, caption_index_size=%d, cast_dtype=%s, " | |
| "target_frame_num=%s)", | |
| self.__class__.__name__, | |
| len(self.urls), | |
| self.annotation_path, | |
| len(self.caption_index), | |
| torch.float32, | |
| self.target_frame_num, | |
| ) | |
| # -- helpers ---------------------------------------------------------------- | |
| def _decode_payload(self, sample: dict[str, Any]) -> dict[str, Any]: | |
| """Decode one WebDataset sample into the trainer sample contract. | |
| Args: | |
| sample: Raw WebDataset sample containing a latent ``.pth`` payload, | |
| optional ``actions.json`` and optional ``meta.json``. | |
| Returns: | |
| Dict with ``prompts`` string, ``clean_latent`` float tensor | |
| ``[T, C, H, W]`` and metadata keys. On-disk latent layout must be | |
| ``[C, T, H, W]`` and is permuted before returning. | |
| Raises: | |
| ``KeyError`` / ``TypeError`` / ``ValueError`` on malformed samples. | |
| The dataset is intentionally strict so shard/index drift is visible. | |
| """ | |
| key = sample.get("__key__", "") | |
| url = sample.get("__url__", "") | |
| shard_key = self._extract_shard_key(url) | |
| latent_raw: bytes | None = None | |
| meta_obj: dict[str, Any] = {} | |
| for k, v in sample.items(): | |
| if k.startswith("__"): | |
| continue | |
| kl = k.lower() | |
| if latent_raw is None and ( | |
| kl == "latent.pth" | |
| or kl.endswith(".latent.pth") | |
| or kl == "pth" | |
| or kl.endswith(".pth") | |
| ): | |
| latent_raw = v | |
| elif kl == "meta.json" or kl.endswith(".meta.json"): | |
| meta_raw = v | |
| if isinstance(meta_raw, (bytes, bytearray)): | |
| meta_raw = meta_raw.decode("utf-8", errors="replace") | |
| meta_obj = json.loads(meta_raw) if isinstance(meta_raw, str) else (meta_raw or {}) | |
| if latent_raw is None: | |
| raise KeyError( | |
| f"{type(self).__name__}: no latent .pth payload found in " | |
| f"sample (url={url!r}, key={key!r}, fields={list(sample.keys())!r})" | |
| ) | |
| if not isinstance(latent_raw, (bytes, bytearray)): | |
| raise TypeError( | |
| f"{type(self).__name__}: latent payload has unexpected " | |
| f"type {type(latent_raw).__name__} " | |
| f"(url={url!r}, key={key!r}); expected raw bytes. " | |
| f"Did webdataset auto-decode the .pth file?" | |
| ) | |
| prompt = self._resolve_caption(shard_key, key, meta_obj) | |
| payload = _load_pth_bytes(bytes(latent_raw)) | |
| latent = _coerce_latent(payload) | |
| latent = latent.to(dtype=torch.float32) | |
| # On-disk layout is (C, T, H, W); align with the trainer layout (T, C, H, W). | |
| if latent.dim() != 4: | |
| raise ValueError( | |
| f"{type(self).__name__}: latent must be 4-D (C, T, H, W), " | |
| f"got shape {tuple(latent.shape)} (url={url!r}, key={key!r})" | |
| ) | |
| latent = latent.permute(1, 0, 2, 3).contiguous() | |
| if self.target_frame_num is not None: | |
| actual_frame_num = latent.shape[0] | |
| if actual_frame_num < self.target_frame_num: | |
| raise ValueError( | |
| f"{type(self).__name__}: latent requires at least " | |
| f"{self.target_frame_num} frame(s), got {actual_frame_num} " | |
| f"(url={url!r}, key={key!r}); short samples are not padded." | |
| ) | |
| latent = latent[: self.target_frame_num].contiguous() | |
| result: dict[str, Any] = { | |
| "prompts": prompt, | |
| "clean_latent": latent, | |
| "metadata": { | |
| "key": key, | |
| "shard_key": shard_key, | |
| }, | |
| } | |
| return result | |