SlopTTS / Modules /diffusion /module_dit.py
FashionFlora's picture
Upload full repo excluding dump_40, dump_100, precomputed_tokens, precomputed_data
fb0011a verified
Raw
History Blame Contribute Delete
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
@torch.no_grad()
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}")