SlopTTS / Modules /diffusion /modules.py
FashionFlora's picture
Upload full repo excluding dump_40, dump_100, precomputed_tokens, precomputed_data
fb0011a verified
Raw
History Blame Contribute Delete
17.4 kB
from math import floor, log, pi
from typing import Any, List, Optional, Sequence, Tuple, Union
from .utils import *
import torch
import torch.nn as nn
from einops import rearrange, reduce, repeat
from einops.layers.torch import Rearrange
from einops_exts import rearrange_many
from torch import Tensor, einsum
"""
Utils + Transformer with RoPE (Rotary Positional Embeddings)
Extended to support RMSNorm or LayerNorm via `norm_type` parameter.
"""
class RMSNorm(nn.Module):
"""RMSNorm: normalizacja po RMS (nie odejmuje średniej)."""
def __init__(self, dim: int, eps: float = 1e-8, elementwise_affine: bool = True):
super().__init__()
self.dim = dim
self.eps = eps
self.elementwise_affine = elementwise_affine
if elementwise_affine:
self.weight = nn.Parameter(torch.ones(dim))
else:
self.register_buffer("weight", torch.ones(dim))
def forward(self, x: Tensor) -> Tensor:
# x: (..., dim)
rms = x.pow(2).mean(dim=-1, keepdim=True).add(self.eps).sqrt()
x_normed = x / rms
return x_normed * self.weight
def _make_norm(norm_type: str, dim: int) -> nn.Module:
"""Helper: create norm module by name."""
if norm_type is None or norm_type == "none":
return nn.Identity()
if norm_type == "layer":
return nn.LayerNorm(dim)
if norm_type == "rms":
return RMSNorm(dim)
raise ValueError(f"Unknown norm_type: {norm_type}")
class Transformer1d(nn.Module):
def __init__(
self,
num_layers: int,
channels: int,
num_heads: int,
head_features: int,
multiplier: int,
use_context_time: bool = True,
use_rope: bool = False,
rope_max_seq_len: int = 512,
context_embedding_features: Optional[int] = None,
embedding_max_length: int = 512,
# Dropout params
dropout: float = 0.0, # general dropout (used as input/out dropout)
attn_dropout: float = 0.0, # dropout on attention weights
ff_dropout: float = 0.0, # dropout after feed-forward
# norm type: "layer" (default) or "rms" or "none"
norm_type: str = "layer",
):
"""
Transformer1d simplified for the case where cross-attention/context
features are never used.
- context_features and context_features_multiplier removed.
- All attention is self-attention.
"""
super().__init__()
# if context_embedding_features is None, treat as 0 (no extra embedding dim)
context_embedding_features = context_embedding_features or 0
self.context_embedding_features = context_embedding_features
total_features = channels + context_embedding_features
# Save dropout modules and params
self.input_dropout = nn.Dropout(dropout) if dropout > 0.0 else nn.Identity()
self._dropout = dropout
self._attn_dropout = attn_dropout
self._ff_dropout = ff_dropout
self.blocks = nn.ModuleList(
[
TransformerBlock(
features=total_features,
head_features=head_features,
num_heads=num_heads,
multiplier=multiplier,
use_rope=use_rope,
rope_max_seq_len=rope_max_seq_len,
# pass dropout settings into blocks
dropout=dropout,
attn_dropout=attn_dropout,
ff_dropout=ff_dropout,
norm_type=norm_type,
)
for _ in range(num_layers)
]
)
self.to_out = nn.Sequential(
Rearrange("b t c -> b c t"),
nn.Conv1d(in_channels=total_features, out_channels=channels, kernel_size=1),
)
# We assume `features: Optional[Tensor]` will never be passed.
# So we only keep time-based context (if enabled).
self.use_context_time = use_context_time
if use_context_time:
context_mapping_features = total_features
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(),
)
self.to_time = nn.Sequential(
TimePositionalEmbedding(dim=channels, out_features=context_mapping_features),
nn.GELU(),
)
self.mapping_features = context_mapping_features
else:
self.to_mapping = None
self.to_time = None
self.mapping_features = None
self.fixed_embedding = FixedEmbedding(
max_length=embedding_max_length, features=context_embedding_features
)
def get_mapping(self, time: Optional[Tensor] = None) -> Optional[Tensor]:
"""Compute mapping solely from time. `features` is intentionally removed."""
if not self.use_context_time:
return None
assert exists(time), "use_context_time=True but no time features provided"
mapping = self.to_time(time)
mapping = self.to_mapping(mapping)
return mapping
def run(self, x: Tensor, time: Tensor, embedding: Tensor) -> Tensor:
# x: (b, seq_len_x, channels)
# embedding: (b, seq_len_e, context_embedding_features)
mapping = self.get_mapping(time)
# Concatenate fixed embedding channels (if embedding features == 0, this is a no-op)
x = torch.cat([x.expand(-1, embedding.size(1), -1), embedding], dim=-1)
if mapping is not None:
mapping = mapping.unsqueeze(1).expand(-1, embedding.size(1), -1)
# Apply input dropout once to the inputs to the transformer blocks.
x = self.input_dropout(x)
for block in self.blocks:
if mapping is not None:
x = x + mapping
x = block(x)
x = x.mean(dim=1).unsqueeze(1)
x = self.to_out(x)
x = x.transpose(-1, -2)
return x
def forward(
self,
x: Tensor,
time: Tensor,
embedding_mask_proba: float = 0.1,
embedding: Optional[Tensor] = None,
embedding_scale: float = 1.0,
) -> Tensor:
"""
Note: `features` tensor argument has been removed intentionally because it will
never be provided.
"""
assert exists(embedding), "embedding must be provided"
b, device = embedding.shape[0], embedding.device
fixed_embedding = self.fixed_embedding(embedding)
if embedding_mask_proba > 0.0:
# Randomly mask embedding per-batch
batch_mask = rand_bool(
shape=(b, 1, 1), proba=embedding_mask_proba, device=device
)
embedding = torch.where(batch_mask, fixed_embedding, embedding)
if embedding_scale != 1.0:
# Compute both normal and fixed embedding outputs (classifier-free guidance)
out = self.run(x, time, embedding=embedding)
out_masked = self.run(x, time, embedding=fixed_embedding)
return out_masked + (out - out_masked) * embedding_scale
else:
return self.run(x, time, embedding=embedding)
"""
Rotary Positional Embedding implementation
"""
class RotaryEmbedding(nn.Module):
"""
RoPE implementation that caches sin/cos for up to max_seq_len.
Works on head dimension d (must be even).
"""
def __init__(self, dim: int, max_seq_len: int = 512, base: int = 10000):
super().__init__()
assert dim % 2 == 0, "Rotary embedding dim must be even"
self.dim = dim
self.max_seq_len = max_seq_len
self.base = base
# inv_freq = 1.0 / (base ** (i/dim)) for even positions
inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
self.register_buffer("inv_freq", inv_freq, persistent=False)
# No precomputed cos/sin buffer here — generate on demand (optionally could cache)
def _build_sin_cos(self, seq_len: int, device: torch.device, dtype: torch.dtype):
# seq_len x (dim/2)
positions = torch.arange(seq_len, device=device, dtype=dtype).unsqueeze(1)
angles = positions * rearrange(self.inv_freq.to(device=device, dtype=dtype), "d -> 1 d")
sin = torch.sin(angles) # seq_len x (dim/2)
cos = torch.cos(angles) # seq_len x (dim/2)
# interleave to match original (d) layout: [cos0, cos0, cos1, cos1, ...] when applied
sin = torch.stack([sin, sin], dim=-1).reshape(seq_len, self.dim)
cos = torch.stack([cos, cos], dim=-1).reshape(seq_len, self.dim)
return sin, cos
@staticmethod
def rotate_half(x: Tensor) -> Tensor:
# x: (..., d) where d is even
x1 = x[..., ::2]
x2 = x[..., 1::2]
# rotate: (-x2, x1) interleaved
x_rotated = torch.stack((-x2, x1), dim=-1).reshape_as(x)
return x_rotated
def apply_rotary(self, q: Tensor, k: Tensor) -> Tuple[Tensor, Tensor]:
"""
Apply RoPE to q and k.
q,k shapes: (b, h, n, d) with d == self.dim
"""
assert q.shape[-1] == self.dim and k.shape[-1] == self.dim
seq_len = q.shape[-2]
device = q.device
dtype = q.dtype
sin, cos = self._build_sin_cos(seq_len, device=device, dtype=dtype)
# make broadcastable: (1, 1, n, d)
sin = sin.unsqueeze(0).unsqueeze(0)
cos = cos.unsqueeze(0).unsqueeze(0)
q_out = (q * cos) + (self.rotate_half(q) * sin)
k_out = (k * cos) + (self.rotate_half(k) * sin)
return q_out, k_out
"""
Attention Components (self-attention only) with RoPE
"""
def FeedForward(features: int, multiplier: int) -> nn.Module:
mid_features = features * multiplier
return nn.Sequential(
nn.Linear(in_features=features, out_features=mid_features),
nn.GELU(),
nn.Linear(in_features=mid_features, out_features=features),
)
class AttentionBase(nn.Module):
def __init__(
self,
features: int,
*,
head_features: int,
num_heads: int,
use_rope: bool,
rope_max_seq_len: int = 512,
out_features: Optional[int] = None,
# new dropout params
attn_dropout: float = 0.0,
out_dropout: float = 0.0,
):
super().__init__()
self.scale = head_features ** -0.5
self.num_heads = num_heads
self.use_rope = use_rope
mid_features = head_features * num_heads
if out_features is None:
out_features = features
self.to_out = nn.Linear(in_features=mid_features, out_features=out_features)
# dropout modules
self.attn_dropout = nn.Dropout(attn_dropout) if attn_dropout > 0.0 else nn.Identity()
self.out_dropout = nn.Dropout(out_dropout) if out_dropout > 0.0 else nn.Identity()
# Rotary embedding per-head-dim
if use_rope:
# head_features is the d per head that RoPE should be applied to
self.rotary = RotaryEmbedding(dim=head_features, max_seq_len=rope_max_seq_len)
else:
self.rotary = None
def forward(self, q: Tensor, k: Tensor, v: Tensor) -> Tensor:
# Split heads
# q,k,v shape before: (b, n, h * d) -> after: (b, h, n, d)
q, k, v = rearrange_many((q, k, v), "b n (h d) -> b h n d", h=self.num_heads)
# Apply RoPE to q and k if enabled
if self.rotary is not None:
q, k = self.rotary.apply_rotary(q, k)
# Compute similarity matrix
sim = einsum("... n d, ... m d -> ... n m", q, k)
sim = sim * self.scale
# Get attention matrix with softmax
attn = sim.softmax(dim=-1)
# apply dropout to attention weights if configured
attn = self.attn_dropout(attn)
# Compute values
out = einsum("... n m, ... m d -> ... n d", attn, v)
out = rearrange(out, "b h n d -> b n (h d)")
out = self.to_out(out)
out = self.out_dropout(out)
return out
class Attention(nn.Module):
def __init__(
self,
features: int,
*,
head_features: int,
num_heads: int,
out_features: Optional[int] = None,
use_rope: bool,
rope_max_seq_len: int = 512,
# propagate dropout params
attn_dropout: float = 0.0,
out_dropout: float = 0.0,
# norm type: "layer", "rms", or "none"
norm_type: str = "layer",
):
"""
Self-attention only (context / cross-attention removed).
"""
super().__init__()
mid_features = head_features * num_heads
self.norm = _make_norm(norm_type, features)
# For self-attention we compute q from x and k,v from x as well.
self.to_q = nn.Linear(in_features=features, out_features=mid_features, bias=False)
self.to_kv = nn.Linear(in_features=features, out_features=mid_features * 2, bias=False)
self.attention = AttentionBase(
features,
out_features=out_features,
num_heads=num_heads,
head_features=head_features,
use_rope=use_rope,
rope_max_seq_len=rope_max_seq_len,
attn_dropout=attn_dropout,
out_dropout=out_dropout,
)
def forward(self, x: Tensor) -> Tensor:
# Pre-norm before computing q/k/v (recommended for stability)
if not isinstance(self.norm, nn.Identity):
x_norm = self.norm(x)
else:
x_norm = x
q = self.to_q(x_norm)
k, v = torch.chunk(self.to_kv(x_norm), 2, dim=-1)
return self.attention(q, k, v)
"""
Transformer Blocks
"""
class TransformerBlock(nn.Module):
def __init__(
self,
features: int,
num_heads: int,
head_features: int,
multiplier: int,
use_rope: bool,
rope_max_seq_len: int = 512,
# new: dropout params per-block
dropout: float = 0.0,
attn_dropout: float = 0.0,
ff_dropout: float = 0.0,
# norm type
norm_type: str = "layer",
):
super().__init__()
# Only self-attention (no cross-attention)
self.attention = Attention(
features=features,
num_heads=num_heads,
head_features=head_features,
use_rope=use_rope,
rope_max_seq_len=rope_max_seq_len,
attn_dropout=attn_dropout,
out_dropout=dropout,
norm_type=norm_type,
)
self.feed_forward = FeedForward(features=features, multiplier=multiplier)
# LayerNorm or RMSNorm before feed-forward and dropout after FF
self.norm_ff = _make_norm(norm_type, features)
self.ff_dropout = nn.Dropout(ff_dropout) if ff_dropout > 0.0 else nn.Identity()
def forward(self, x: Tensor) -> Tensor:
x = self.attention(x) + x
# Apply norm before feed-forward (pre-norm style) and apply FF + dropout then residual
if not isinstance(self.norm_ff, nn.Identity):
ff_in = self.norm_ff(x)
else:
ff_in = x
ff_out = self.feed_forward(ff_in)
ff_out = self.ff_dropout(ff_out)
x = ff_out + x
return x
"""
Time Embeddings (unchanged)
"""
class SinusoidalEmbedding(nn.Module):
def __init__(self, dim: int):
super().__init__()
self.dim = dim
def forward(self, x: Tensor) -> Tensor:
device, half_dim = x.device, self.dim // 2
emb = torch.tensor(log(10000) / (half_dim - 1), device=device)
emb = torch.exp(torch.arange(half_dim, device=device) * -emb)
emb = rearrange(x, "i -> i 1") * rearrange(emb, "j -> 1 j")
return torch.cat((emb.sin(), emb.cos()), dim=-1)
class LearnedPositionalEmbedding(nn.Module):
"""Used for continuous time"""
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_message = "Input sequence length must be <= max_length"
assert length <= self.max_length, assert_message
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