SlopTTS / Modules /diffusion /dit1d.py
FashionFlora's picture
Upload full repo excluding dump_40, dump_100, precomputed_tokens, precomputed_data
fb0011a verified
Raw
History Blame Contribute Delete
11.6 kB
# Modules/diffusion/dit1d.py
# Refactored 1D DiT with Multi-Query Attention and Adaptive Layer Normalization
# Drop-in replacements for Transformer1d and StyleTransformer1d
# AdaLNZero (two separate linears for gamma and beta) used for adaptive LN.
from typing import Optional
import torch
import torch.nn as nn
import torch.nn.functional as F
from einops import rearrange
from einops.layers.torch import Rearrange
# --- Helpers (from your provided code) ---
def exists(x):
return x is not None
def default(val, d):
return val if exists(val) else (d() if callable(d) else d)
def rand_bool(shape, proba, device=None):
if proba == 1:
return torch.ones(shape, device=device, dtype=torch.bool)
if proba == 0:
return torch.zeros(shape, device=device, dtype=torch.bool)
return torch.bernoulli(torch.full(shape, proba, device=device)).to(torch.bool)
class LearnedPositionalEmbedding(nn.Module):
def __init__(self, dim: int):
super().__init__()
assert dim % 2 == 0, "dim must be even"
self.half_dim = dim // 2
self.weights = nn.Parameter(torch.randn(self.half_dim))
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = x.view(-1, 1)
freqs = x * self.weights.view(1, -1) * 2 * torch.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: torch.Tensor) -> torch.Tensor:
B, L = x.shape[0], x.shape[1]
assert L <= self.max_length, "Input sequence length must be <= max_length"
pos = torch.arange(L, device=x.device)
pe = self.embedding(pos)
pe = pe.unsqueeze(0).expand(B, L, -1)
return pe
# --- Adaptive LayerNorm: AdaLNZero (two linears for gamma and beta) ---
class AdaLNZero(nn.Module):
def __init__(self, dim: int, cond_dim: int):
super().__init__()
self.norm = nn.LayerNorm(dim)
self.gamma = nn.Linear(cond_dim, dim, bias=True)
self.beta = nn.Linear(cond_dim, dim, bias=True)
nn.init.zeros_(self.gamma.weight)
nn.init.zeros_(self.gamma.bias)
nn.init.zeros_(self.beta.weight)
nn.init.zeros_(self.beta.bias)
def forward(self, x: torch.Tensor, c: torch.Tensor) -> torch.Tensor:
# x: [B, T, D], c: [B, Dc]
x = self.norm(x)
g = self.gamma(c)[:, None, :] # [B, 1, D]
b = self.beta(c)[:, None, :]
return x * (1 + g) + b
# --- (Keep original single-linear AdaLayerNorm as an option) ---
class AdaLayerNorm(nn.Module):
"""
Alternative fused single-linear implementation (kept for compatibility).
"""
def __init__(self, cond_dim: int, channels: int, eps: float = 1e-5):
super().__init__()
self.channels = channels
self.eps = eps
self.fc = nn.Linear(cond_dim, channels * 2)
def forward(self, x: torch.Tensor, cond: torch.Tensor) -> torch.Tensor:
# x: [B, T, C], cond: [B, cond_dim]
cond = cond.unsqueeze(1) # [B, 1, cond_dim] for broadcasting
gamma_beta = self.fc(cond) # [B, 1, 2C]
gamma, beta = gamma_beta.chunk(2, dim=-1) # [B, 1, C], [B, 1, C]
x = F.layer_norm(x, (self.channels,), eps=self.eps)
return (1 + gamma) * x + beta
# --- Attention, MLP, Blocks ---
class MQAttention(nn.Module):
def __init__(
self,
features: int,
head_features: int,
num_heads: int,
):
super().__init__()
self.num_heads = num_heads
self.head_features = head_features
self.norm = nn.LayerNorm(features)
self.to_q = nn.Linear(features, num_heads * head_features, bias=False)
# Multi-query: shared k/v per head set (no separate heads for kv)
self.to_kv = nn.Linear(features, 2 * head_features, bias=False)
self.scale = head_features ** -0.5
self.to_out = nn.Linear(num_heads * head_features, features)
def forward(self, x: torch.Tensor):
x_norm = self.norm(x)
q = self.to_q(x_norm)
k, v = self.to_kv(x_norm).chunk(2, dim=-1)
q = rearrange(q, "b t (h d) -> b h t d", h=self.num_heads)
attn = torch.einsum("b h t d, b s d -> b h t s", q, k) * self.scale
attn = attn.softmax(dim=-1)
out = torch.einsum("b h t s, b s d -> b h t d", attn, v)
out = rearrange(out, "b h t d -> b t (h d)")
return self.to_out(out) + x
class MLP(nn.Module):
def __init__(self, features: int, multiplier: int):
super().__init__()
self.net = nn.Sequential(
nn.Linear(features, features * multiplier),
nn.GELU(),
nn.Linear(features * multiplier, features),
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.net(x)
class DiTBlock(nn.Module):
def __init__(
self,
features: int,
num_heads: int,
head_features: int,
multiplier: int,
cond_dim: int,
use_adalnzero: bool = True,
):
super().__init__()
# choose AdaLNZero by default, fallback to AdaLayerNorm if disabled
ln_class = AdaLNZero if use_adalnzero else AdaLayerNorm
# adapter expects (dim, cond_dim) signature for AdaLNZero
if ln_class is AdaLNZero:
self.attn_mod = ln_class(dim=features, cond_dim=cond_dim)
self.ffn_mod = ln_class(dim=features, cond_dim=cond_dim)
else:
self.attn_mod = ln_class(cond_dim=cond_dim, channels=features)
self.ffn_mod = ln_class(cond_dim=cond_dim, channels=features)
self.attn = MQAttention(
features=features, head_features=head_features, num_heads=num_heads
)
self.ffn = MLP(features=features, multiplier=multiplier)
def forward(self, x: torch.Tensor, cond: torch.Tensor) -> torch.Tensor:
x = self.attn(self.attn_mod(x, cond))
x = self.ffn(self.ffn_mod(x, cond)) + x
return x
# --- Main Model Class ---
class _BaseDiT1d(nn.Module):
def __init__(
self,
num_layers: int,
channels: int, # Dimension of the style vector
num_heads: int,
head_features: int,
multiplier: int,
context_embedding_features: int, # Dimension of BERT embedding
embedding_max_length: int = 512,
use_style_conditioning: bool = False,
style_features: Optional[int] = None, # Dimension of reference style features
use_adalnzero: bool = True, # toggle to use AdaLNZero or fused AdaLayerNorm
):
super().__init__()
self.use_style_conditioning = use_style_conditioning
self.use_adalnzero = use_adalnzero
token_dim = channels + context_embedding_features
cond_dim = token_dim
self.to_time = nn.Sequential(
TimePositionalEmbedding(dim=channels, out_features=cond_dim),
nn.GELU(),
)
if use_style_conditioning:
assert exists(style_features), "style_features dimension must be provided"
self.to_style = nn.Sequential(
nn.Linear(in_features=style_features, out_features=cond_dim),
nn.GELU(),
)
self.mapping = nn.Sequential(
nn.Linear(cond_dim, cond_dim),
nn.GELU(),
nn.Linear(cond_dim, cond_dim),
nn.GELU(),
)
self.blocks = nn.ModuleList([
DiTBlock(
features=token_dim,
num_heads=num_heads,
head_features=head_features,
multiplier=multiplier,
cond_dim=cond_dim,
use_adalnzero=use_adalnzero,
) for _ in range(num_layers)
])
self.fixed_embedding = FixedEmbedding(
max_length=embedding_max_length, features=context_embedding_features
)
self.out_norm = nn.LayerNorm(token_dim)
self.to_out = nn.Linear(in_features=token_dim, out_features=channels)
def get_conditioning(self, time, features):
cond = self.to_time(time)
if self.use_style_conditioning:
assert exists(features), "features must be provided for style conditioning"
cond = cond + self.to_style(features)
return self.mapping(cond)
def run(self, x, time, embedding, features):
# x: [B, 1, C], embedding: [B, L, E]
L = embedding.shape[1]
x_tokens = x.expand(-1, L, -1)
tokens = torch.cat([x_tokens, embedding], dim=-1)
cond = self.get_conditioning(time, features)
for block in self.blocks:
tokens = block(tokens, cond)
tokens = self.out_norm(tokens)
tokens = tokens.mean(axis=1) # Average pool over sequence length
out = self.to_out(tokens)
return out.unsqueeze(1) # [B, 1, C]
def forward(
self,
x: torch.Tensor,
time: torch.Tensor,
embedding_mask_proba: float = 0.0,
embedding: Optional[torch.Tensor] = None,
features: Optional[torch.Tensor] = None,
embedding_scale: float = 1.0,
) -> torch.Tensor:
b, device = x.shape[0], x.device
assert exists(embedding), "BERT embedding must be provided"
fixed_embedding = self.fixed_embedding(embedding)
if embedding_mask_proba > 0.0:
batch_mask = rand_bool((b, 1, 1), proba=embedding_mask_proba, device=device)
embedding = torch.where(batch_mask, fixed_embedding, embedding)
if embedding_scale != 1.0:
out = self.run(x, time, embedding=embedding, features=features)
out_masked = self.run(x, time, embedding=fixed_embedding, features=features)
return out_masked + (out - out_masked) * embedding_scale
else:
return self.run(x, time, embedding=embedding, features=features)
# --- Drop-in Replacement Classes ---
class Transformer1d(_BaseDiT1d):
""" API-compatible DiT1d for unconditional/time-conditional generation """
def __init__(
self,
num_layers: int,
channels: int,
num_heads: int,
head_features: int,
multiplier: int,
context_embedding_features: Optional[int] = None,
embedding_max_length: int = 512,
use_adalnzero: bool = True,
**kwargs # Ignore unused legacy arguments
):
assert exists(context_embedding_features)
super().__init__(
num_layers=num_layers,
channels=channels,
num_heads=num_heads,
head_features=head_features,
multiplier=multiplier,
context_embedding_features=context_embedding_features,
embedding_max_length=embedding_max_length,
use_style_conditioning=False,
use_adalnzero=use_adalnzero,
)