looking-ahead-ti2v / src /utils /caption_dropout.py
multimodalart's picture
multimodalart HF Staff
Upload folder using huggingface_hub
05182f3 verified
Raw History Blame Contribute Delete
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