| 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
|
|
|
|
|
|
|
| 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)
|
|
|
|
|
|
|
| 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):
|
|
|
|
|
| pos = rearrange(pos, "n d -> 1 1 n d")
|
| return t * pos.cos() + rotate_half(t) * pos.sin()
|
|
|
|
|
|
|
| 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 = x.transpose(-1, -2).transpose(1, -1)
|
|
|
| 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)
|
|
|
| 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),
|
| )
|
|
|
|
|
|
|
| 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
|
|
|
|
|
| if use_rope:
|
| self.rotary_emb = RotaryEmbedding(head_features)
|
|
|
| self.to_q = nn.Conv1d(features, mid_features, 1, bias=False)
|
|
|
|
|
| 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
|
|
|
| kv_input = default(context, x)
|
|
|
|
|
| q = self.to_q(x)
|
| k, v = self.to_kv(kv_input).chunk(2, dim=1)
|
|
|
|
|
| q, k, v = map(
|
| lambda t: rearrange(t, "b (h d) n -> b h n d", h=self.num_heads),
|
| (q, k, v)
|
| )
|
|
|
|
|
| if self.use_rope:
|
|
|
| q_freqs = self.rotary_emb(n, q.device)
|
| q = apply_rotary_pos_emb(q_freqs, q)
|
|
|
|
|
|
|
|
|
| if context is None:
|
| k_freqs = q_freqs
|
| k = apply_rotary_pos_emb(k_freqs, k)
|
|
|
|
|
| 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)
|
|
|
|
|
| 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)
|
|
|
|
|
| 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)
|
|
|
|
|
|
|
| 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
|
| )
|
|
|
| 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
|
| )
|
|
|
| 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
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
| 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_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,
|
| 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
|
|
|
|
|
|
|
| if use_context_time or use_context_features:
|
| context_mapping_features = input_dim
|
|
|
| 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:
|
|
|
| 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):
|
|
|
|
|
|
|
| mapping = self.get_mapping(time, features)
|
|
|
|
|
| if exists(embedding):
|
|
|
| emb_t = embedding.transpose(1, 2)
|
| x = torch.cat([x, emb_t], dim=1)
|
|
|
|
|
| for block in self.blocks:
|
| x = block(x, mapping, context=features)
|
|
|
| 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
|
|
|
|
|
| final_embedding = None
|
| if self.context_embedding_features > 0 and exists(embedding):
|
| fixed_emb = self.fixed_embedding(embedding)
|
|
|
| if embedding_mask_proba > 0.0:
|
| batch_mask = rand_bool(shape=(b, 1, 1), proba=embedding_mask_proba, device=device)
|
|
|
|
|
|
|
|
|
| final_embedding = torch.where(batch_mask, fixed_emb, fixed_emb)
|
|
|
|
|
| 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
|
|
|
|
|
| if embedding_scale != 1.0:
|
|
|
| out = self.run_network(x, time, embedding=final_embedding, features=features)
|
|
|
|
|
|
|
|
|
| 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)
|
|
|
|
|
| return out_uncond + (out - out_uncond) * embedding_scale
|
|
|
| else:
|
| return self.run_network(x, time, embedding=final_embedding, features=features)
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
| x0 = torch.randn_like(x1)
|
|
|
|
|
| t = torch.rand((b,), device=device)
|
|
|
|
|
|
|
|
|
| t_expand = t.view(b, 1, 1)
|
| x_t = (1 - (1 - self.sigma_min) * t_expand) * x0 + t_expand * x1
|
|
|
|
|
|
|
| target_v = x1 - (1 - self.sigma_min) * x0
|
|
|
|
|
|
|
|
|
| v_pred = self.model(x_t, t, features=condition, embedding_mask_proba=0.1)
|
|
|
|
|
| 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
|
|
|
|
|
| x = torch.randn(shape, device=device)
|
|
|
| dt = 1.0 / steps
|
| traj = []
|
|
|
| for i in range(steps):
|
| t_val = i / steps
|
| t = torch.full((b,), t_val, device=device)
|
|
|
|
|
| v = self.model(x, t, features=condition, embedding_scale=cfg_scale)
|
|
|
|
|
| x = x + v * dt
|
|
|
| return x
|
|
|
|
|
|
|
| if __name__ == "__main__":
|
|
|
| B, Channels, Length = 2, 32, 128
|
| Cond_Dim = 64
|
|
|
|
|
| backbone = StyleTransformer1d(
|
| num_layers=4,
|
| channels=Channels,
|
| num_heads=4,
|
| head_features=32,
|
| multiplier=2,
|
| use_context_time=True,
|
| context_features=Cond_Dim,
|
| context_embedding_features=0,
|
| embedding_max_length=Length
|
| )
|
|
|
|
|
| flow_model = FlowMatching(backbone)
|
|
|
|
|
| data = torch.randn(B, Channels, Length)
|
| cond = torch.randn(B, Cond_Dim)
|
|
|
| loss = flow_model(data, condition=cond)
|
| print(f"Training Loss: {loss.item()}")
|
|
|
|
|
|
|
| generated = flow_model.sample(
|
| shape=(B, Channels, Length),
|
| steps=20,
|
| condition=cond,
|
| cfg_scale=3.0
|
| )
|
| print(f"Generated Shape: {generated.shape}") |