Download Modules/diffusion/sampler.py from FashionFlora/SFlowTTS: direct link, hf CLI and curl.
- Browser
- Download file 12.7 kB
-
https://huggingface.co/FashionFlora/SFlowTTS/resolve/main/Modules/diffusion/sampler.py
- Command line
-
hf download hf://FashionFlora/SFlowTTS/Modules/diffusion/sampler.py
-
curl -L -o sampler.py https://huggingface.co/FashionFlora/SFlowTTS/resolve/main/Modules/diffusion/sampler.py
12.7 kB
| from math import atan, cos, pi, sin, sqrt, log | |
| from typing import Any, Callable, List, Optional, Tuple, Type, Literal | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from einops import rearrange, reduce as einops_reduce | |
| from torch import Tensor | |
| import math | |
| # ----------------------------------------------------------------------------- | |
| # Helpers | |
| # ----------------------------------------------------------------------------- | |
| def exists(x): | |
| return x is not None | |
| def default(val, d): | |
| if exists(val): | |
| return val | |
| return d() if callable(d) else d | |
| def pad_dims(x: Tensor, ndim: int) -> Tensor: | |
| # Pads additional ndims to the right of the tensor | |
| return x.view(*x.shape, *((1,) * ndim)) | |
| def clip(x: Tensor, dynamic_threshold: float = 0.0): | |
| if dynamic_threshold == 0.0: | |
| return x.clamp(-1.0, 1.0) | |
| else: | |
| # Dynamic thresholding | |
| x_flat = rearrange(x, "b ... -> b (...)") | |
| scale = torch.quantile(x_flat.abs(), dynamic_threshold, dim=-1) | |
| scale.clamp_(min=1.0) | |
| scale = pad_dims(scale, ndim=x.ndim - scale.ndim) | |
| x = x.clamp(-scale, scale) / scale | |
| return x | |
| def _to_batch_sigma( | |
| batch_size: int, | |
| device: torch.device, | |
| x: Optional[float] = None, | |
| xs: Optional[Tensor] = None, | |
| ) -> Tensor: | |
| if (x is None) and (xs is None): | |
| raise ValueError("Either x (scalar) or xs (tensor) must be provided") | |
| if (x is not None) and (xs is not None): | |
| raise ValueError("Only one of x or xs may be provided") | |
| if xs is None: | |
| return torch.full((batch_size,), float(x), device=device) | |
| if not isinstance(xs, torch.Tensor): | |
| xs = torch.tensor(xs, device=device, dtype=torch.get_default_dtype()) | |
| else: | |
| xs = xs.to(device) | |
| xs = xs.reshape(-1) | |
| if xs.numel() == 1: | |
| xs = xs.expand(batch_size).contiguous() | |
| elif xs.numel() != batch_size: | |
| raise ValueError(f"xs has {xs.numel()} values but batch_size is {batch_size}") | |
| return xs | |
| # ----------------------------------------------------------------------------- | |
| # Distributions | |
| # ----------------------------------------------------------------------------- | |
| class Distribution: | |
| def __call__(self, num_samples: int, device: torch.device): | |
| raise NotImplementedError() | |
| class LogNormalDistribution(Distribution): | |
| def __init__(self, mean: float, std: float): | |
| self.mean = mean | |
| self.std = std | |
| def __call__( | |
| self, num_samples: int, device: torch.device = torch.device("cpu") | |
| ) -> Tensor: | |
| normal = self.mean + self.std * torch.randn((num_samples,), device=device) | |
| return normal.exp() | |
| # ----------------------------------------------------------------------------- | |
| # Base Diffusion | |
| # ----------------------------------------------------------------------------- | |
| class Diffusion(nn.Module): | |
| alias: str = "" | |
| def denoise_fn(self, x_noisy: Tensor, sigmas: Optional[Tensor] = None, sigma: Optional[float] = None, **kwargs) -> Tensor: | |
| raise NotImplementedError("Diffusion class missing denoise_fn") | |
| def forward(self, x: Tensor, noise: Tensor = None, **kwargs) -> Tensor: | |
| raise NotImplementedError("Diffusion class missing forward function") | |
| # ----------------------------------------------------------------------------- | |
| # VKDiffusion (Corrected) | |
| # ----------------------------------------------------------------------------- | |
| class UniformDistribution(Distribution): | |
| def __call__(self, num_samples: int, device: torch.device = torch.device("cpu")): | |
| return torch.rand(num_samples, device=device) | |
| class VKDiffusion(Diffusion): | |
| """ | |
| Velocity-prediction diffusion (v-prediction) specifically adapted for | |
| Karras/Elucidated framework. | |
| Target: v = alpha * eps - beta * x0 | |
| Reconstruction: x0 = alpha * x_noised - beta * v_pred | |
| """ | |
| alias = "v" | |
| def __init__( | |
| self, | |
| net: nn.Module, | |
| *, | |
| sigma_distribution: Distribution, | |
| sigma_data: float = 1.0, | |
| dynamic_threshold: float = 0.0, | |
| loss_type: str = "mse", | |
| huber_delta: float = 0.1, | |
| ): | |
| super().__init__() | |
| self.net = net | |
| self.register_buffer("sigma_data", torch.tensor(float(sigma_data))) | |
| self.sigma_distribution = sigma_distribution | |
| self.dynamic_threshold = float(dynamic_threshold) | |
| self.loss_type = loss_type | |
| self.huber_delta = float(huber_delta) | |
| def get_scalings(self, sigmas: Tensor) -> Tuple[Tensor, Tensor, Tensor, Tensor]: | |
| """ | |
| Returns scaling factors for Karras inputs and V-target calculation. | |
| """ | |
| sigma_data = self.sigma_data | |
| # 1. Network Input Scalings (Karras et al.) | |
| # c_in: scales input to unit variance | |
| c_in = (sigmas ** 2 + sigma_data ** 2).rsqrt() | |
| # c_noise: conditioning on noise level | |
| c_noise = sigmas.log() * 0.25 | |
| # 2. Geometric factors for V-prediction | |
| # These represent the 'angle' of the noise vs data | |
| # z = sqrt(sigma^2 + sigma_data^2) | |
| # alpha = sigma_data / z | |
| # beta = sigma / z | |
| # We compute them via c_in to save ops: c_in = 1/z | |
| alpha = c_in * sigma_data | |
| beta = c_in * sigmas | |
| return c_in, c_noise, alpha, beta | |
| def denoise_fn( | |
| self, | |
| x_noisy: Tensor, | |
| sigmas: Optional[Tensor] = None, | |
| sigma: Optional[float] = None, | |
| **kwargs, | |
| ) -> Tensor: | |
| """ | |
| Inference time: Reconstruct x0 from x_noisy and predicted v. | |
| """ | |
| batch_size, device = x_noisy.shape[0], x_noisy.device | |
| sigmas = _to_batch_sigma(batch_size, device, sigma, sigmas) | |
| # Pad sigmas for broadcasting [B, 1, 1] | |
| sigmas_view = pad_dims(sigmas, x_noisy.ndim - 1) | |
| c_in, c_noise, alpha, beta = self.get_scalings(sigmas_view) | |
| # 1. Run Network | |
| # IMPORTANT: kwargs must contain 'embedding' and optionally 'mask' for the transformer | |
| v_pred = self.net(x_noisy * c_in, c_noise.flatten(), **kwargs) | |
| # 2. Reconstruct x0 | |
| # x0 = alpha * x_noisy - beta * v_pred (scaled by sigma_data implicitly in definition) | |
| x_denoised = (alpha * x_noisy) - (beta * v_pred) * self.sigma_data | |
| if self.dynamic_threshold > 0.0: | |
| x_denoised = clip(x_denoised, dynamic_threshold=self.dynamic_threshold) | |
| return x_denoised | |
| def forward( | |
| self, | |
| x: Tensor, | |
| noise: Optional[Tensor] = None, | |
| mask: Optional[Tensor] = None, | |
| **kwargs, | |
| ) -> Tensor: | |
| """ | |
| Training forward pass. | |
| x: [B, 2, T] (Pitch, Energy) | |
| mask: [B, T] (Valid frames) - Passed to both loss and attention | |
| """ | |
| batch_size, device = x.shape[0], x.device | |
| # 1. Sample Sigmas | |
| sigmas = self.sigma_distribution(num_samples=batch_size, device=device) | |
| sigmas_view = pad_dims(sigmas, x.ndim - 1) # [B, 1, 1] | |
| # 2. Perturb Data | |
| noise = default(noise, lambda: torch.randn_like(x)) | |
| x_noisy = x + sigmas_view * noise | |
| # 3. Get Scalings | |
| c_in, c_noise, alpha, beta = self.get_scalings(sigmas_view) | |
| # 4. Calculate Target V | |
| # v_target = alpha * noise - beta * (x / sigma_data) | |
| # Note: Inputs are normalized by dataloader, so x is effectively x/1.0 | |
| v_target = (alpha * noise) - (beta * x) | |
| # 5. Network Prediction | |
| # Flatten c_noise for the linear layer in model [B] | |
| # CRITICAL: We pass 'mask' to the net here for ATTENTION MASKING | |
| v_pred = self.net(x_noisy * c_in, c_noise.flatten(), mask=mask, **kwargs) | |
| # 6. Loss Calculation | |
| if self.loss_type == "mse": | |
| loss = F.mse_loss(v_pred, v_target, reduction="none") | |
| elif self.loss_type == "huber": | |
| loss = F.smooth_l1_loss(v_pred, v_target, reduction="none", beta=self.huber_delta) | |
| # 7. Masking for Loss (CRITICAL for Seq2Seq) | |
| if mask is not None: | |
| # mask comes in as [B, T], we need [B, 1, T] for broadcasting to [B, 2, T] | |
| if mask.ndim == 2: | |
| mask_bc = mask.unsqueeze(1) | |
| else: | |
| mask_bc = mask | |
| loss = loss * mask_bc | |
| # Sum over time and channels, divide by sum of mask | |
| # Add epsilon to avoid divide by zero | |
| return loss.sum() / (mask_bc.sum() * x.shape[1] + 1e-8) | |
| return loss.mean() | |
| # ----------------------------------------------------------------------------- | |
| # Samplers / Schedules | |
| # ----------------------------------------------------------------------------- | |
| class Schedule(nn.Module): | |
| def forward(self, num_steps: int, device: torch.device) -> Tensor: | |
| raise NotImplementedError() | |
| class KarrasSchedule(Schedule): | |
| def __init__(self, sigma_min: float, sigma_max: float, rho: float = 7.0): | |
| super().__init__() | |
| self.sigma_min = sigma_min | |
| self.sigma_max = sigma_max | |
| self.rho = rho | |
| def forward(self, num_steps: int, device: Any) -> Tensor: | |
| rho_inv = 1.0 / self.rho | |
| steps = torch.arange(num_steps, device=device, dtype=torch.float32) | |
| sigmas = ( | |
| self.sigma_max ** rho_inv | |
| + (steps / (num_steps - 1)) | |
| * (self.sigma_min ** rho_inv - self.sigma_max ** rho_inv) | |
| ) ** self.rho | |
| sigmas = F.pad(sigmas, pad=(0, 1), value=0.0) | |
| return sigmas | |
| class Sampler(nn.Module): | |
| def forward(self, noise: Tensor, fn: Callable, sigmas: Tensor, num_steps: int) -> Tensor: | |
| raise NotImplementedError() | |
| class DPMpp2MSampler(Sampler): | |
| """ DPM-Solver++(2M) """ | |
| def __init__(self, s_churn: float = 0.0, s_tmin: float = 0.0, | |
| s_tmax: float = float("inf"), s_noise: float = 1.0): | |
| super().__init__() | |
| self.s_churn = s_churn | |
| self.s_tmin = s_tmin | |
| self.s_tmax = s_tmax | |
| self.s_noise = s_noise | |
| def step( | |
| self, | |
| x: Tensor, | |
| fn: Callable, | |
| sigma: float, | |
| sigma_next: float, | |
| i: int, | |
| n: int, | |
| d_prev: Optional[Tensor], | |
| sigma_prev_hat: Optional[float], | |
| ) -> Tuple[Tensor, Tensor, float]: | |
| gamma = 0.0 | |
| if self.s_tmin <= sigma <= self.s_tmax: | |
| gamma = min(self.s_churn / max(n - 1, 1), sqrt(2.0) - 1.0) | |
| sigma_hat = sigma * (1.0 + gamma) | |
| if gamma > 0.0: | |
| eps = torch.randn_like(x) * self.s_noise | |
| x = x + (sigma_hat**2 - sigma**2).sqrt() * eps | |
| denoised = fn(x, sigma=sigma_hat) | |
| d = (x - denoised) / sigma_hat | |
| h = sigma_next - sigma_hat | |
| if d_prev is None: | |
| # First step -> Euler | |
| x_next = x + h * d | |
| else: | |
| # 2M multistep | |
| h_prev = sigma_hat - sigma_prev_hat | |
| r = h / (h_prev + 1e-12) | |
| x_next = x + h * ((1 + r) * d - r * d_prev) | |
| return x_next, d, sigma_hat | |
| def forward( | |
| self, noise: Tensor, fn: Callable, sigmas: Tensor, num_steps: int | |
| ) -> Tensor: | |
| x = sigmas[0] * noise | |
| d_prev: Optional[Tensor] = None | |
| sigma_prev_hat: Optional[float] = None | |
| for i in range(num_steps - 1): | |
| x, d_prev, sigma_prev_hat = self.step( | |
| x, | |
| fn=fn, | |
| sigma=sigmas[i], | |
| sigma_next=sigmas[i + 1], | |
| i=i, | |
| n=num_steps, | |
| d_prev=d_prev, | |
| sigma_prev_hat=sigma_prev_hat, | |
| ) | |
| return x | |
| class DiffusionSampler(nn.Module): | |
| def __init__( | |
| self, | |
| diffusion: Diffusion, | |
| *, | |
| sampler: Sampler, | |
| sigma_schedule: Schedule, | |
| num_steps: Optional[int] = None, | |
| clamp: bool = True, | |
| ): | |
| super().__init__() | |
| self.denoise_fn = diffusion.denoise_fn | |
| self.sampler = sampler | |
| self.sigma_schedule = sigma_schedule | |
| self.num_steps = num_steps | |
| self.clamp = clamp | |
| def forward( | |
| self, noise: Tensor, num_steps: Optional[int] = None, **kwargs | |
| ) -> Tensor: | |
| device = noise.device | |
| num_steps = default(num_steps, self.num_steps) | |
| assert exists(num_steps), "Parameter `num_steps` must be provided" | |
| sigmas = self.sigma_schedule(num_steps, device) | |
| # We wrap denoise_fn to pass **kwargs (like embedding, mask) | |
| fn = lambda *a, **ka: self.denoise_fn(*a, **{**ka, **kwargs}) | |
| x = self.sampler(noise, fn=fn, sigmas=sigmas, num_steps=sigmas.numel()) | |
| if self.clamp: | |
| x = x.clamp(-1.0, 1.0) | |
| return x |