Spaces:
Running on Zero
Running on Zero
Download src/utils/caption_dropout.py from hugging-apps/looking-ahead-ti2v: direct link, hf CLI and curl.
- Browser
- Download file 5.05 kB
-
https://huggingface.co/spaces/hugging-apps/looking-ahead-ti2v/resolve/main/src/utils/caption_dropout.py
- Command line
-
hf download hf://spaces/hugging-apps/looking-ahead-ti2v/src/utils/caption_dropout.py
-
curl -L -o caption_dropout.py https://huggingface.co/spaces/hugging-apps/looking-ahead-ti2v/resolve/main/src/utils/caption_dropout.py
5.05 kB
| """Deterministic, RNG-isolated caption dropout for diffusion training.""" | |
| from __future__ import annotations | |
| import math | |
| from collections.abc import Mapping | |
| from numbers import Real | |
| import torch | |
| _CAPTION_DROPOUT_STREAM_ID = 0x4341_5054_494F_4E01 | |
| def validate_caption_dropout_probability(value: object) -> float: | |
| """Return a validated caption-dropout probability in ``[0, 1]``.""" | |
| if isinstance(value, bool) or not isinstance(value, Real): | |
| raise TypeError("caption_dropout_p must be a non-boolean real number.") | |
| probability = float(value) | |
| if not math.isfinite(probability) or not 0.0 <= probability <= 1.0: | |
| raise ValueError("caption_dropout_p must be finite and within [0, 1].") | |
| return probability | |
| def derive_caption_dropout_seed( | |
| base_seed: int, | |
| global_rank: int, | |
| global_step: int, | |
| ) -> int: | |
| """Derive a stable, position-addressed seed without consuming global RNG. | |
| The address deliberately includes rank and global training step. Batch-slot | |
| identity is supplied by the stable position within the mask vector sampled | |
| from this seed. | |
| """ | |
| components = (base_seed, global_rank, global_step, _CAPTION_DROPOUT_STREAM_ID) | |
| if any(isinstance(value, bool) or not isinstance(value, int) for value in components): | |
| raise TypeError("Caption-dropout seed components must be non-boolean integers.") | |
| if global_rank < 0 or global_step < 0: | |
| raise ValueError("Caption-dropout seed address requires global_rank and global_step >= 0.") | |
| mask = (1 << 64) - 1 | |
| value = 0x510E_527F_ADE6_82D1 | |
| for component in components: | |
| value ^= (component & mask) + 0x9E37_79B9_7F4A_7C15 + ((value << 6) & mask) | |
| value ^= value >> 30 | |
| value = (value * 0xBF58_476D_1CE4_E5B9) & mask | |
| value ^= value >> 27 | |
| value = (value * 0x94D0_49BB_1331_11EB) & mask | |
| value ^= value >> 31 | |
| return value & ((1 << 63) - 1) | |
| def sample_caption_dropout_mask( | |
| probability: object, | |
| batch_size: int, | |
| *, | |
| base_seed: int, | |
| global_rank: int, | |
| global_step: int, | |
| ) -> torch.Tensor: | |
| """Sample a per-sample CPU mask from an isolated generator. | |
| ``p=0`` and ``p=1`` use exact deterministic fast paths. For intermediate | |
| probabilities, a fresh CPU generator is addressed by seed/rank/step, so | |
| this function never reads or advances the process-global CPU or CUDA RNG. | |
| """ | |
| probability = validate_caption_dropout_probability(probability) | |
| if isinstance(batch_size, bool) or not isinstance(batch_size, int): | |
| raise TypeError("caption-dropout batch_size must be a non-boolean integer.") | |
| if batch_size <= 0: | |
| raise ValueError("caption-dropout batch_size must be positive.") | |
| seed = derive_caption_dropout_seed( | |
| base_seed=base_seed, | |
| global_rank=global_rank, | |
| global_step=global_step, | |
| ) | |
| if probability == 0.0: | |
| return torch.zeros(batch_size, dtype=torch.bool) | |
| if probability == 1.0: | |
| return torch.ones(batch_size, dtype=torch.bool) | |
| generator = torch.Generator(device="cpu") | |
| generator.manual_seed(seed) | |
| return torch.rand(batch_size, generator=generator) < probability | |
| def replace_prompt_embeds_with_negative( | |
| conditional_dict: Mapping[str, object], | |
| unconditional_dict: Mapping[str, object], | |
| dropout_mask: torch.Tensor, | |
| ) -> dict[str, object]: | |
| """Return conditioning with selected prompt-embedding rows replaced. | |
| Only ``prompt_embeds`` is changed. Other conditioning fields and both input | |
| mappings remain untouched. | |
| """ | |
| if not torch.is_tensor(dropout_mask): | |
| raise TypeError("caption-dropout mask must be a torch.Tensor.") | |
| if dropout_mask.dtype != torch.bool or dropout_mask.ndim != 1: | |
| raise ValueError("caption-dropout mask must be a one-dimensional bool tensor.") | |
| conditional = conditional_dict.get("prompt_embeds") | |
| negative = unconditional_dict.get("prompt_embeds") | |
| if not torch.is_tensor(conditional) or not torch.is_tensor(negative): | |
| raise KeyError("Both conditioning dictionaries must contain tensor prompt_embeds.") | |
| if conditional.shape != negative.shape: | |
| raise ValueError( | |
| "Conditional and negative prompt_embeds must have identical shapes, " | |
| f"got {tuple(conditional.shape)} and {tuple(negative.shape)}." | |
| ) | |
| if conditional.ndim < 1 or conditional.shape[0] != dropout_mask.numel(): | |
| raise ValueError( | |
| "caption-dropout mask length must equal the prompt_embeds batch dimension." | |
| ) | |
| if conditional.device != negative.device or conditional.dtype != negative.dtype: | |
| raise ValueError("Conditional and negative prompt_embeds must share device and dtype.") | |
| broadcast_shape = (dropout_mask.numel(),) + (1,) * (conditional.ndim - 1) | |
| selected = torch.where( | |
| dropout_mask.to(device=conditional.device).reshape(broadcast_shape), | |
| negative, | |
| conditional, | |
| ) | |
| result = dict(conditional_dict) | |
| result["prompt_embeds"] = selected | |
| return result | |