Download Modules/diffusion/module_dit.py from FashionFlora/SFlowTTS: direct link, hf CLI and curl.
- Browser
- Download file 19.3 kB
-
https://huggingface.co/FashionFlora/SFlowTTS/resolve/main/Modules/diffusion/module_dit.py
- Command line
-
hf download hf://FashionFlora/SFlowTTS/Modules/diffusion/module_dit.py
-
curl -L -o module_dit.py https://huggingface.co/FashionFlora/SFlowTTS/resolve/main/Modules/diffusion/module_dit.py
19.3 kB
| import math | |
| from math import floor, log, pi | |
| from typing import Any, List, Optional, Sequence, Tuple, Union | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from einops import rearrange, reduce, repeat | |
| from einops.layers.torch import Rearrange | |
| from torch import Tensor, einsum | |
| # --- Utils --- | |
| def exists(val): | |
| return val is not None | |
| def default(val, d): | |
| return val if exists(val) else d | |
| def rand_bool(shape, proba, device=None): | |
| if proba == 1: | |
| return torch.ones(shape, device=device, dtype=torch.bool) | |
| elif proba == 0: | |
| return torch.zeros(shape, device=device, dtype=torch.bool) | |
| return torch.bernoulli(torch.full(shape, proba, device=device)).to(torch.bool) | |
| def rearrange_many(tensors, pattern, **kwargs): | |
| return tuple(rearrange(tensor, pattern, **kwargs) for tensor in tensors) | |
| # --- RoPE (Rotary Positional Embeddings) --- | |
| class RotaryEmbedding(nn.Module): | |
| def __init__(self, dim, theta=10000): | |
| super().__init__() | |
| inv_freq = 1.0 / (theta ** (torch.arange(0, dim, 2).float() / dim)) | |
| self.register_buffer("inv_freq", inv_freq) | |
| def forward(self, seq_len, device): | |
| t = torch.arange(seq_len, device=device).type_as(self.inv_freq) | |
| freqs = torch.einsum("i , j -> i j", t, self.inv_freq) | |
| return torch.cat((freqs, freqs), dim=-1) | |
| def rotate_half(x): | |
| x1, x2 = x.chunk(2, dim=-1) | |
| return torch.cat((-x2, x1), dim=-1) | |
| def apply_rotary_pos_emb(pos, t): | |
| # t: [b, h, n, d] | |
| # pos: [n, d] | |
| pos = rearrange(pos, "n d -> 1 1 n d") | |
| return t * pos.cos() + rotate_half(t) * pos.sin() | |
| # --- Core Layers --- | |
| class AdaLayerNorm(nn.Module): | |
| def __init__(self, style_dim, channels, eps=1e-5): | |
| super().__init__() | |
| self.channels = channels | |
| self.eps = eps | |
| self.fc = nn.Linear(style_dim, channels * 2) | |
| def forward(self, x, s): | |
| # x: [b, c, t] -> needs transpose for LayerNorm | |
| x = x.transpose(-1, -2).transpose(1, -1) # [b, t, c] | |
| h = self.fc(s) | |
| h = h.view(h.size(0), 1, h.size(1)) | |
| gamma, beta = torch.chunk(h, chunks=2, dim=2) | |
| x = F.layer_norm(x, (self.channels,), eps=self.eps) | |
| x = (1 + gamma) * x + beta | |
| return x.transpose(1, -1).transpose(-1, -2) # Back to [b, c, t] | |
| def FeedForward(features: int, multiplier: int) -> nn.Module: | |
| mid_features = features * multiplier | |
| return nn.Sequential( | |
| nn.Conv1d(features, mid_features, 1), | |
| nn.GELU(), | |
| nn.Conv1d(mid_features, features, 1), | |
| ) | |
| # --- Attention with RoPE --- | |
| class Attention(nn.Module): | |
| def __init__( | |
| self, | |
| features: int, | |
| *, | |
| head_features: int, | |
| num_heads: int, | |
| context_features: Optional[int] = None, | |
| use_rope: bool = True | |
| ): | |
| super().__init__() | |
| self.scale = head_features ** -0.5 | |
| self.num_heads = num_heads | |
| mid_features = head_features * num_heads | |
| self.use_rope = use_rope | |
| # RoPE Generator | |
| if use_rope: | |
| self.rotary_emb = RotaryEmbedding(head_features) | |
| self.to_q = nn.Conv1d(features, mid_features, 1, bias=False) | |
| # Determine Key/Value input dimension (self vs cross attention) | |
| kv_dim = default(context_features, features) | |
| self.to_kv = nn.Conv1d(kv_dim, mid_features * 2, 1, bias=False) | |
| self.to_out = nn.Conv1d(mid_features, features, 1) | |
| def forward(self, x: Tensor, context: Optional[Tensor] = None) -> Tensor: | |
| b, c, n = x.shape | |
| # If context is None, it's self-attention | |
| kv_input = default(context, x) | |
| # Projections [b, (h d), n] | |
| q = self.to_q(x) | |
| k, v = self.to_kv(kv_input).chunk(2, dim=1) | |
| # Split heads: [b, h, n, d] | |
| q, k, v = map( | |
| lambda t: rearrange(t, "b (h d) n -> b h n d", h=self.num_heads), | |
| (q, k, v) | |
| ) | |
| # Apply RoPE (Rotary Positional Embeddings) | |
| if self.use_rope: | |
| # Generate frequencies for query length | |
| q_freqs = self.rotary_emb(n, q.device) | |
| q = apply_rotary_pos_emb(q_freqs, q) | |
| # If self-attention, k length is same as q. | |
| # If cross-attention, we usually don't apply rotary to K unless time-aligned. | |
| # Assuming self-attention for RoPE here strictly or aligned cross. | |
| if context is None: | |
| k_freqs = q_freqs | |
| k = apply_rotary_pos_emb(k_freqs, k) | |
| # Attention | |
| sim = einsum("b h i d, b h j d -> b h i j", q, k) * self.scale | |
| attn = sim.softmax(dim=-1) | |
| out = einsum("b h i j, b h j d -> b h i d", attn, v) | |
| # Merge heads | |
| out = rearrange(out, "b h n d -> b (h d) n") | |
| return self.to_out(out) | |
| class StyleAttention(nn.Module): | |
| """ | |
| Wrapper that handles AdaLayerNorm before passing to the main Attention mechanism. | |
| """ | |
| def __init__( | |
| self, | |
| features: int, | |
| *, | |
| style_dim: int, | |
| head_features: int, | |
| num_heads: int, | |
| context_features: Optional[int] = None, | |
| use_rope: bool = True, | |
| ): | |
| super().__init__() | |
| self.context_features = context_features | |
| context_dim = default(context_features, features) | |
| self.norm = AdaLayerNorm(style_dim, features) | |
| self.norm_context = AdaLayerNorm(style_dim, context_dim) | |
| self.attention = Attention( | |
| features=features, | |
| head_features=head_features, | |
| num_heads=num_heads, | |
| context_features=context_features, | |
| use_rope=use_rope | |
| ) | |
| def forward(self, x: Tensor, s: Tensor, *, context: Optional[Tensor] = None) -> Tensor: | |
| if self.context_features is not None: | |
| assert exists(context), "Context required for cross attention" | |
| context_input = default(context, x) | |
| # Adaptive Norm | |
| x = self.norm(x, s) | |
| context_input = self.norm_context(context_input, s) | |
| return self.attention(x, context=context_input if self.context_features else None) | |
| # --- Transformer Blocks --- | |
| class StyleTransformerBlock(nn.Module): | |
| def __init__( | |
| self, | |
| features: int, | |
| num_heads: int, | |
| head_features: int, | |
| style_dim: int, | |
| multiplier: int, | |
| context_features: Optional[int] = None, | |
| ): | |
| super().__init__() | |
| self.use_cross_attention = exists(context_features) and context_features > 0 | |
| self.attention = StyleAttention( | |
| features=features, | |
| style_dim=style_dim, | |
| num_heads=num_heads, | |
| head_features=head_features, | |
| use_rope=True # Force RoPE | |
| ) | |
| if self.use_cross_attention: | |
| self.cross_attention = StyleAttention( | |
| features=features, | |
| style_dim=style_dim, | |
| num_heads=num_heads, | |
| head_features=head_features, | |
| context_features=context_features, | |
| use_rope=False # Usually no positional embedding on cross-attn to generic context | |
| ) | |
| self.feed_forward = FeedForward(features=features, multiplier=multiplier) | |
| def forward(self, x: Tensor, s: Tensor, *, context: Optional[Tensor] = None) -> Tensor: | |
| x = self.attention(x, s) + x | |
| if self.use_cross_attention: | |
| x = self.cross_attention(x, s, context=context) + x | |
| x = self.feed_forward(x) + x | |
| return x | |
| # --- Embeddings & Time --- | |
| class LearnedPositionalEmbedding(nn.Module): | |
| def __init__(self, dim: int): | |
| super().__init__() | |
| assert (dim % 2) == 0 | |
| half_dim = dim // 2 | |
| self.weights = nn.Parameter(torch.randn(half_dim)) | |
| def forward(self, x: Tensor) -> Tensor: | |
| x = rearrange(x, "b -> b 1") | |
| freqs = x * rearrange(self.weights, "d -> 1 d") * 2 * pi | |
| fouriered = torch.cat((freqs.sin(), freqs.cos()), dim=-1) | |
| fouriered = torch.cat((x, fouriered), dim=-1) | |
| return fouriered | |
| def TimePositionalEmbedding(dim: int, out_features: int) -> nn.Module: | |
| return nn.Sequential( | |
| LearnedPositionalEmbedding(dim), | |
| nn.Linear(in_features=dim + 1, out_features=out_features), | |
| ) | |
| class FixedEmbedding(nn.Module): | |
| def __init__(self, max_length: int, features: int): | |
| super().__init__() | |
| self.max_length = max_length | |
| self.embedding = nn.Embedding(max_length, features) | |
| def forward(self, x: Tensor) -> Tensor: | |
| batch_size, length, device = *x.shape[0:2], x.device | |
| assert length <= self.max_length | |
| position = torch.arange(length, device=device) | |
| fixed_embedding = self.embedding(position) | |
| fixed_embedding = repeat(fixed_embedding, "n d -> b n d", b=batch_size) | |
| return fixed_embedding | |
| # --- Main Backbone: StyleTransformer1d --- | |
| class StyleTransformer1d(nn.Module): | |
| def __init__( | |
| self, | |
| num_layers: int, | |
| channels: int, | |
| num_heads: int, | |
| head_features: int, | |
| multiplier: int, | |
| use_context_time: bool = True, | |
| context_features: Optional[int] = None, | |
| context_embedding_features: Optional[int] = None, | |
| embedding_max_length: int = 512, | |
| ): | |
| super().__init__() | |
| self.channels = channels | |
| self.context_embedding_features = default(context_embedding_features, 0) | |
| # Input projection | |
| input_dim = channels + self.context_embedding_features | |
| self.blocks = nn.ModuleList([ | |
| StyleTransformerBlock( | |
| features=input_dim, | |
| head_features=head_features, | |
| num_heads=num_heads, | |
| multiplier=multiplier, | |
| style_dim=channels + self.context_embedding_features, # Mapping dim | |
| context_features=context_features, | |
| ) | |
| for _ in range(num_layers) | |
| ]) | |
| self.to_out = nn.Sequential( | |
| nn.Conv1d(input_dim, channels, 1) | |
| ) | |
| use_context_features = exists(context_features) | |
| self.use_context_features = use_context_features | |
| self.use_context_time = use_context_time | |
| # Mapping Network (Conditioning) | |
| # We project time/features into a shared 'style' vector 's' | |
| if use_context_time or use_context_features: | |
| context_mapping_features = input_dim # Must match block width for AdaLN | |
| self.to_mapping = nn.Sequential( | |
| nn.Linear(context_mapping_features, context_mapping_features), | |
| nn.GELU(), | |
| nn.Linear(context_mapping_features, context_mapping_features), | |
| nn.GELU(), | |
| ) | |
| if use_context_time: | |
| self.to_time = nn.Sequential( | |
| TimePositionalEmbedding(dim=channels, out_features=context_mapping_features), | |
| nn.GELU(), | |
| ) | |
| if use_context_features: | |
| self.to_features = nn.Sequential( | |
| nn.Linear(in_features=context_features, out_features=context_mapping_features), | |
| nn.GELU(), | |
| ) | |
| if self.context_embedding_features > 0: | |
| self.fixed_embedding = FixedEmbedding( | |
| max_length=embedding_max_length, features=self.context_embedding_features | |
| ) | |
| def get_mapping( | |
| self, time: Optional[Tensor] = None, features: Optional[Tensor] = None | |
| ) -> Optional[Tensor]: | |
| """Combines time and global features into a style vector s""" | |
| items = [] | |
| if self.use_context_time: | |
| assert exists(time) | |
| items += [self.to_time(time)] | |
| if self.use_context_features: | |
| assert exists(features) | |
| items += [self.to_features(features)] | |
| mapping = None | |
| if items: | |
| # sum aggregation of conditions | |
| mapping = reduce(torch.stack(items), "n b m -> b m", "sum") | |
| mapping = self.to_mapping(mapping) | |
| return mapping | |
| def run_network(self, x, time, embedding=None, features=None): | |
| # x: [b, c, t] (expecting channels first for convs, but transposing internally if needed) | |
| # Note: Previous blocks expect [b, c, t] | |
| mapping = self.get_mapping(time, features) # [b, style_dim] | |
| # Concatenate x with learned embeddings if they exist | |
| if exists(embedding): | |
| # embedding: [b, t, c_emb] -> [b, c_emb, t] | |
| emb_t = embedding.transpose(1, 2) | |
| x = torch.cat([x, emb_t], dim=1) | |
| # Forward through blocks | |
| for block in self.blocks: | |
| x = block(x, mapping, context=features) # context used for cross-attn if enabled | |
| x = self.to_out(x) | |
| return x | |
| def forward(self, | |
| x: Tensor, | |
| time: Tensor, | |
| embedding_mask_proba: float = 0.0, | |
| embedding: Optional[Tensor] = None, | |
| features: Optional[Tensor] = None, | |
| embedding_scale: float = 1.0) -> Tensor: | |
| b, device = x.shape[0], x.device | |
| # Handle Discrete Embedding (e.g. text/class tokens) | |
| final_embedding = None | |
| if self.context_embedding_features > 0 and exists(embedding): | |
| fixed_emb = self.fixed_embedding(embedding) # [b, t, d] | |
| if embedding_mask_proba > 0.0: | |
| batch_mask = rand_bool(shape=(b, 1, 1), proba=embedding_mask_proba, device=device) | |
| # Null embedding is assumed to be the fixed positional one here, | |
| # or typically a separate learnable null token. | |
| # Here we just mask with the fixed pos embedding as 'unconditional' proxy | |
| # or keep it simple. | |
| final_embedding = torch.where(batch_mask, fixed_emb, fixed_emb) # Placeholder logic | |
| # Ideally: if masked, replace with a learnable null parameter. | |
| # For simplicity in this structure: we assume unmasked is passed explicitly or we use 0. | |
| if embedding_mask_proba > 0: | |
| mask = rand_bool((b, 1, 1), 1 - embedding_mask_proba, device) | |
| final_embedding = fixed_emb * mask | |
| else: | |
| final_embedding = fixed_emb | |
| # Classifier-Free Guidance (CFG) Logic | |
| if embedding_scale != 1.0: | |
| # 1. Conditional Forward Pass | |
| out = self.run_network(x, time, embedding=final_embedding, features=features) | |
| # 2. Unconditional Forward Pass | |
| # To make it unconditional, we zero out the condition or use a null token. | |
| # Here we simulate unconditional by zeroing the explicit embedding/features if they exist. | |
| null_embedding = torch.zeros_like(final_embedding) if exists(final_embedding) else None | |
| null_features = torch.zeros_like(features) if exists(features) else None | |
| out_uncond = self.run_network(x, time, embedding=null_embedding, features=null_features) | |
| # CFG Formula: uncond + scale * (cond - uncond) | |
| return out_uncond + (out - out_uncond) * embedding_scale | |
| else: | |
| return self.run_network(x, time, embedding=final_embedding, features=features) | |
| # --- Flow Matching Wrapper --- | |
| class FlowMatching(nn.Module): | |
| def __init__(self, model: nn.Module, sigma_min=0.0): | |
| super().__init__() | |
| self.model = model | |
| self.sigma_min = sigma_min | |
| def forward(self, x1, condition=None): | |
| """ | |
| Training Step: | |
| x1: Real data sample [b, c, n] | |
| condition: Optional condition tensor | |
| """ | |
| b, c, n = x1.shape | |
| device = x1.device | |
| # 1. Sample Noise x0 | |
| x0 = torch.randn_like(x1) | |
| # 2. Sample Time t | |
| t = torch.rand((b,), device=device) | |
| # 3. Compute Conditional Flow (Optimal Transport path) | |
| # x_t = (1 - (1 - sigma_min) * t) * x0 + t * x1 | |
| # Simplified (assuming sigma_min approx 0 for standard OT): x_t = (1 - t) * x0 + t * x1 | |
| t_expand = t.view(b, 1, 1) | |
| x_t = (1 - (1 - self.sigma_min) * t_expand) * x0 + t_expand * x1 | |
| # 4. Compute Target Vector Field | |
| # u_t(x|x1) = x1 - (1 - sigma_min) * x0 | |
| target_v = x1 - (1 - self.sigma_min) * x0 | |
| # 5. Predict Vector Field | |
| # We pass t, x_t, and conditions to the backbone | |
| # We reshape features if necessary depending on how they are passed | |
| v_pred = self.model(x_t, t, features=condition, embedding_mask_proba=0.1) # 10% dropout for CFG training | |
| # 6. MSE Loss | |
| loss = F.mse_loss(v_pred, target_v) | |
| return loss | |
| def sample(self, shape, steps=50, condition=None, cfg_scale=1.0): | |
| """ | |
| Euler ODE Solver for generation | |
| """ | |
| b, c, n = shape | |
| device = next(self.model.parameters()).device | |
| # Start from Normal distribution (x0) | |
| x = torch.randn(shape, device=device) | |
| dt = 1.0 / steps | |
| traj = [] # keep track of trajectory if needed | |
| for i in range(steps): | |
| t_val = i / steps | |
| t = torch.full((b,), t_val, device=device) | |
| # Predict velocity | |
| v = self.model(x, t, features=condition, embedding_scale=cfg_scale) | |
| # Euler step | |
| x = x + v * dt | |
| return x | |
| # --- Example Usage --- | |
| if __name__ == "__main__": | |
| # Dimensions | |
| B, Channels, Length = 2, 32, 128 | |
| Cond_Dim = 64 | |
| # Instantiate Backbone | |
| backbone = StyleTransformer1d( | |
| num_layers=4, | |
| channels=Channels, | |
| num_heads=4, | |
| head_features=32, | |
| multiplier=2, | |
| use_context_time=True, | |
| context_features=Cond_Dim, # Global condition (e.g. class embedding mapped) | |
| context_embedding_features=0, # Sequence condition | |
| embedding_max_length=Length | |
| ) | |
| # Wrap in Flow Matching | |
| flow_model = FlowMatching(backbone) | |
| # --- Training Step --- | |
| data = torch.randn(B, Channels, Length) | |
| cond = torch.randn(B, Cond_Dim) | |
| loss = flow_model(data, condition=cond) | |
| print(f"Training Loss: {loss.item()}") | |
| # --- Sampling Step --- | |
| # Generate new samples with CFG | |
| generated = flow_model.sample( | |
| shape=(B, Channels, Length), | |
| steps=20, | |
| condition=cond, | |
| cfg_scale=3.0 | |
| ) | |
| print(f"Generated Shape: {generated.shape}") |