Download src/nida/model/sampling.py from Bilal7Dev/nida-tts: direct link, hf CLI and curl.
- Browser
- Download file 3.94 kB
-
https://huggingface.co/Bilal7Dev/nida-tts/resolve/main/src/nida/model/sampling.py
- Command line
-
hf download hf://Bilal7Dev/nida-tts/src/nida/model/sampling.py
-
curl -L -o sampling.py https://huggingface.co/Bilal7Dev/nida-tts/resolve/main/src/nida/model/sampling.py
3.94 kB
| """Inference: multi-condition classifier-free guidance + ODE (docs/04 "Çok koşullu CFG"). | |
| Three passes per step, batched together: | |
| * ``v∅`` — unconditional: text dropped, emotion/intensity null (the prompt policy is a | |
| parameter: ``drop_prompt_uncond``); | |
| * ``vT`` — text + tags, emotion null; | |
| * ``vTE`` — text + tags + emotion. | |
| ``v = v∅ + w_text · (vT − v∅) + w_emo · (vTE − vT)``. With ``w_text = w_emo = 1`` this is | |
| exactly ``vTE``. ``w_emo`` is the user-facing "emotion intensity" knob. | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import replace | |
| from typing import Literal | |
| import torch | |
| from torch import Tensor | |
| from nida.model.dit import Conditions, DiT | |
| from nida.model.flow import sample_ode | |
| __all__ = ["guided_velocity", "synthesize_latents"] | |
| def _null_emotion(model: DiT, c: Conditions) -> Conditions: | |
| batch = c.emotion.shape[0] | |
| return replace( | |
| c, | |
| emotion=torch.full( | |
| (batch,), model.cfg.n_emotions, dtype=c.emotion.dtype, device=c.emotion.device | |
| ), | |
| intensity_known=torch.zeros(batch, dtype=torch.bool, device=c.emotion.device), | |
| ) | |
| def _concat(parts: list[Conditions]) -> Conditions: | |
| """Concatenate along the batch; an optional field must be set in all parts or none.""" | |
| values: dict[str, Tensor | None] = {} | |
| for name in Conditions.__dataclass_fields__: | |
| tensors = [getattr(p, name) for p in parts] | |
| if all(x is None for x in tensors): | |
| values[name] = None | |
| elif any(x is None for x in tensors): | |
| raise ValueError(f"Conditions field {name!r} is set in some parts only") | |
| else: | |
| values[name] = torch.cat(tensors) | |
| return Conditions(**values) # type: ignore[arg-type] # required fields are never None | |
| def guided_velocity( | |
| model: DiT, | |
| x_t: Tensor, | |
| t: Tensor, | |
| c: Conditions, | |
| *, | |
| w_text: float, | |
| w_emo: float, | |
| drop_prompt_uncond: bool = False, | |
| ) -> Tensor: | |
| """Guided velocity ``(B, T, C)`` from one batched forward of ``3·B`` items.""" | |
| batch = x_t.shape[0] | |
| c_te = c | |
| c_t = _null_emotion(model, c) | |
| c_u = replace(c_t, drop_text=torch.ones(batch, dtype=torch.bool, device=x_t.device)) | |
| if drop_prompt_uncond: | |
| c_u = replace(c_u, cond_latent=torch.zeros_like(c.cond_latent)) | |
| v = model(torch.cat([x_t] * 3), torch.cat([t] * 3), _concat([c_u, c_t, c_te])) | |
| v_u, v_t, v_te = v.split(batch) | |
| guided: Tensor = v_u + w_text * (v_t - v_u) + w_emo * (v_te - v_t) | |
| return guided | |
| def synthesize_latents( | |
| model: DiT, | |
| c: Conditions, | |
| prompt_mask: Tensor, | |
| *, | |
| steps: int, | |
| w_text: float, | |
| w_emo: float, | |
| seed: int, | |
| method: Literal["euler", "midpoint"] = "euler", | |
| sway: float = -1.0, | |
| drop_prompt_uncond: bool = False, | |
| ) -> Tensor: | |
| """Generate latents ``(B, T, C)``. | |
| ``prompt_mask`` ``(B, T)`` bool, True = prompt frame: copied from ``cond_latent`` in the | |
| output (they are context, not generated). The noise comes from a CPU generator seeded | |
| with ``seed`` (callers always record it). | |
| """ | |
| batch, length = c.frame_mask.shape | |
| if prompt_mask.shape != (batch, length) or prompt_mask.dtype != torch.bool: | |
| raise ValueError(f"prompt_mask must be bool ({batch}, {length})") | |
| device = c.cond_latent.device | |
| generator = torch.Generator(device="cpu").manual_seed(seed) | |
| x0 = torch.randn(batch, length, model.cfg.latent_dim, generator=generator) | |
| x0 = x0.to(device=device, dtype=c.cond_latent.dtype) | |
| model.eval() | |
| out = sample_ode( | |
| lambda x, t: guided_velocity( | |
| model, x, t, c, w_text=w_text, w_emo=w_emo, drop_prompt_uncond=drop_prompt_uncond | |
| ), | |
| x0, | |
| steps=steps, | |
| method=method, | |
| sway=sway, | |
| ) | |
| result: Tensor = torch.where(prompt_mask[..., None], c.cond_latent, out) | |
| result = result * c.frame_mask[..., None] | |
| return result | |