MEGAMI / networks /dit_multitrack.py
Vansh Chugh
initial deploy
1a3ce68
Raw
History Blame Contribute Delete
21.2 kB
# adapted from https://github.com/Stability-AI/stable-audio-tools/blob/main/stable_audio_tools/models/dit.py
import typing as tp
import math
import torch
from einops import rearrange
from torch import nn
from torch.nn import functional as F
class FourierFeatures(nn.Module):
def __init__(self, in_features, out_features, std=1.):
super().__init__()
assert out_features % 2 == 0
self.weight = nn.Parameter(torch.randn(
[out_features // 2, in_features]) * std)
def forward(self, input):
f = 2 * math.pi * input @ self.weight.T
return torch.cat([f.cos(), f.sin()], dim=-1)
class OneHotPositionalEmbedding(nn.Module):
def __init__(self, max_seq_len):
super().__init__()
self.max_seq_len = max_seq_len
def forward(self, x, pos=None, seq_start_pos=None):
seq_len, device = x.shape[1], x.device
assert seq_len <= self.max_seq_len, f'you are passing in a sequence length of {seq_len} but your one-hot positional embedding has a max sequence length of {self.max_seq_len}'
if pos is None:
pos = torch.arange(seq_len, device=device)
if seq_start_pos is not None:
pos = (pos - seq_start_pos[..., None]).clamp(min=0)
pos_emb = F.one_hot(pos, num_classes=self.max_seq_len).to(x.dtype)
return pos_emb
class DiffusionTransformer(nn.Module):
def __init__(self,
io_channels=32,
patch_size=1,
embed_dim=768,
cond_token_dim=0,
cond_token_proj_dim=64,
project_cond_tokens=False,
global_cond_dim=0,
project_global_cond=True,
input_concat_dim=0,
prepend_cond_dim=0,
depth=12,
num_heads=8,
transformer_type: tp.Literal["x-transformers", "continuous_transformer"] = "x-transformers",
global_cond_type: tp.Literal["prepend", "adaLN"] = "prepend",
timestep_cond_type: tp.Literal["global", "input_concat"] = "global",
timestep_embed_dim=None,
pos_emb_strategy="concatenation",
pos_emb_dim=None,
pos_emb_type="one-hot",
pos_emb_crossattn_strategy="concatenation",
pos_emb_crossattn_dim=None,
pos_emb_crossattn_type="one-hot",
use_taxonomy_in_pos_emb=True,
max_num_tracks=14,#used for one-hot positional embeddings
**kwargs):
super().__init__()
self.cond_token_dim = cond_token_dim
# Timestep embeddings
self.timestep_cond_type = timestep_cond_type
timestep_features_dim = 256
self.timestep_features = FourierFeatures(1, timestep_features_dim)
if timestep_cond_type == "global":
timestep_embed_dim = embed_dim
elif timestep_cond_type == "input_concat":
assert timestep_embed_dim is not None, "timestep_embed_dim must be specified if timestep_cond_type is input_concat"
input_concat_dim += timestep_embed_dim
self.to_timestep_embed = nn.Sequential(
nn.Linear(timestep_features_dim, timestep_embed_dim, bias=True),
nn.SiLU(),
nn.Linear(timestep_embed_dim, timestep_embed_dim, bias=True),
)
self.project_cond_tokens = project_cond_tokens
if cond_token_dim > 0:
# Conditioning tokens
if self.project_cond_tokens:
self.to_cond_embed = nn.Sequential(
nn.Linear(cond_token_dim, cond_token_proj_dim, bias=False),
nn.SiLU(),
nn.Linear(cond_token_proj_dim, cond_token_proj_dim, bias=False)
)
cond_embed_dim = cond_token_proj_dim
else:
cond_embed_dim = cond_token_dim
else:
cond_embed_dim = 0
if global_cond_dim > 0:
# Global conditioning
global_embed_dim = global_cond_dim if not project_global_cond else embed_dim
self.to_global_embed = nn.Sequential(
nn.Linear(global_cond_dim, global_embed_dim, bias=False),
nn.SiLU(),
nn.Linear(global_embed_dim, global_embed_dim, bias=False)
)
if prepend_cond_dim > 0:
# Prepend conditioning
self.to_prepend_embed = nn.Sequential(
nn.Linear(prepend_cond_dim, embed_dim, bias=False),
nn.SiLU(),
nn.Linear(embed_dim, embed_dim, bias=False)
)
self.input_concat_dim = input_concat_dim
dim_in = io_channels + self.input_concat_dim
self.patch_size = patch_size
# Transformer
self.transformer_type = transformer_type
self.global_cond_type = global_cond_type
if pos_emb_strategy == "concatenation":
assert pos_emb_dim is not None, "pos_emb_dim must be specified if pos_emb_strategy is concatenation"
if pos_emb_type == "one-hot":
# One-hot positional embeddings
self.pos_emb = OneHotPositionalEmbedding(pos_emb_dim-max_num_tracks)
self.extra_dim = pos_emb_dim
def concat_pos_emb(x, pos=None, seq_start_pos=None, taxonomy=None):
B, N, T, C = x.shape
pos_emb = self.pos_emb(x.view(-1,x.shape[-2], x.shape[-1]), pos=pos, seq_start_pos=seq_start_pos)
assert pos_emb.shape[-1] == pos_emb_dim-max_num_tracks, f"pos_emb shape mismatch: {pos_emb.shape[-1]} != {pos_emb_dim}"
assert pos_emb.shape[-2] == T, f"pos_emb sequence length mismatch: {pos_emb.shape[-2]} != {x.shape[-2]}"
pos_emb=pos_emb.unsqueeze(0).unsqueeze(0).expand(B,N,T,-1)
assert pos_emb.ndim == 4, f"pos_emb must be 2D or 3D, got {pos_emb.ndim}"
assert pos_emb.shape[0] == x.shape[0], f"pos_emb batch size mismatch: {pos_emb.shape[0]} != {x.shape[0]}"
pos_emb_track=torch.zeros((B, N, T, max_num_tracks), device=x.device, dtype=x.dtype)
for i in range(B):
for j in range(N):
if use_taxonomy_in_pos_emb:
raise NotImplementedError("use_taxonomy_in_pos_emb is not implemented for pos_emb_type 'one-hot'")
assert taxonomy is not None, "taxonomy must be provided if use_taxonomy_in_pos_emb is True"
if taxonomy[i][j]=="92":
pos_emb_track[i, j, :, :]= F.one_hot(torch.tensor([0], device=x.device), num_classes=3).to(x.dtype).expand(T, -1)
elif taxonomy[i][j]=="2":
pos_emb_track[i, j, :, :]= F.one_hot(torch.tensor([1], device=x.device), num_classes=3).to(x.dtype).expand(T, -1)
elif taxonomy[i][j]=="11":
pos_emb_track[i, j, :, :]= F.one_hot(torch.tensor([2], device=x.device), num_classes=3).to(x.dtype).expand(T, -1)
else:
if j >= max_num_tracks:
j= j% max_num_tracks
pos_emb_track[i, j, :, :]= F.one_hot(torch.tensor([j], device=x.device), num_classes=max_num_tracks).to(x.dtype).expand(T, -1)
return torch.cat((x, pos_emb, pos_emb_track), dim=-1)
self.pos_emb_fn = concat_pos_emb
self.remove_pos_emb = lambda x: x[..., :-pos_emb_dim] if pos_emb_dim > 0 else x
else:
raise ValueError(f"Unknown pos_emb_type: {pos_emb_type}")
else:
raise ValueError(f"Unknown pos_emb_strategy: {pos_emb_strategy}")
if pos_emb_crossattn_strategy == "concatenation":
assert pos_emb_crossattn_dim is not None, "pos_emb_crossattn_dim must be specified if pos_emb_crossattn_strategy is concatenation"
if pos_emb_crossattn_type == "one-hot":
# One-hot positional embeddings for cross-attention
self.pos_emb_crossattn = OneHotPositionalEmbedding(pos_emb_crossattn_dim-max_num_tracks)
self.crossattn_extra_dim = pos_emb_crossattn_dim
if not self.project_cond_tokens:
def concat_pos_emb_crossattn(x, pos=None, seq_start_pos=None, taxonomy=None):
B, N, T, C = x.shape
pos_emb = self.pos_emb_crossattn(x.view(-1, T,C), pos=pos, seq_start_pos=seq_start_pos)
assert pos_emb.shape[-1] == pos_emb_crossattn_dim-max_num_tracks, f"pos_emb shape mismatch: {pos_emb.shape[-1]} != {pos_emb_crossattn_dim}"
assert pos_emb.shape[-2] == T, f"pos_emb sequence length mismatch: {pos_emb.shape[-2]} != {x.shape[-2]}"
pos_emb=pos_emb.unsqueeze(0).unsqueeze(0).expand(B,N,T,-1)
#if pos_emb.ndim == 3:
assert pos_emb.ndim == 4, f"pos_emb must be 2D or 3D, got {pos_emb.ndim}"
assert pos_emb.shape[0] == x.shape[0], f"pos_emb batch size mismatch: {pos_emb.shape[0]} != {x.shape[0]}"
pos_emb_track=torch.zeros((B, N, T, max_num_tracks), device=x.device, dtype=x.dtype)
for i in range(B):
for j in range(N):
if use_taxonomy_in_pos_emb:
raise NotImplementedError("use_taxonomy_in_pos_emb is not implemented for pos_emb_crossattn_type 'one-hot'")
assert taxonomy is not None, "taxonomy must be provided if use_taxonomy_in_pos_emb is True"
if taxonomy[i][j]=="92":
pos_emb_track[i, j, :, :]= F.one_hot(torch.tensor([0], device=x.device), num_classes=3).to(x.dtype).expand(T, -1)
elif taxonomy[i][j]=="2":
pos_emb_track[i, j, :, :]= F.one_hot(torch.tensor([1], device=x.device), num_classes=3).to(x.dtype).expand(T, -1)
elif taxonomy[i][j]=="11":
pos_emb_track[i, j, :, :]= F.one_hot(torch.tensor([2], device=x.device), num_classes=3).to(x.dtype).expand(T, -1)
else:
if j >= max_num_tracks:
j= j% max_num_tracks
pos_emb_track[i, j, :, :]= F.one_hot(torch.tensor([j], device=x.device), num_classes=max_num_tracks).to(x.dtype).expand(T, -1)
return torch.cat((x, pos_emb, pos_emb_track), dim=-1)
else:
def concat_pos_emb_crossattn(x, pos=None, seq_start_pos=None, taxonomy=None):
#print("x shape",x.shape)
B, N, T, C = x.shape
assert T*C== cond_token_dim, f"cond_token_proj_dim must match T*C, got {cond_token_dim} != {T*C}"
#rehape to B, N, 1, T*C
x= rearrange(x, "b n t c -> b n 1 (t c)")
#pos_emb = self.pos_emb_crossattn(x.view(-1, T,C), pos=pos, seq_start_pos=seq_start_pos)
#assert pos_emb.shape[-1] == pos_emb_crossattn_dim-3, f"pos_emb shape mismatch: {pos_emb.shape[-1]} != {pos_emb_crossattn_dim}"
#assert pos_emb.shape[-2] == T, f"pos_emb sequence length mismatch: {pos_emb.shape[-2]} != {x.shape[-2]}"
#pos_emb=pos_emb.unsqueeze(0).unsqueeze(0).expand(B,N,T,-1)
#if pos_emb.ndim == 3:
#assert pos_emb.ndim == 4, f"pos_emb must be 2D or 3D, got {pos_emb.ndim}"
#assert pos_emb.shape[0] == x.shape[0], f"pos_emb batch size mismatch: {pos_emb.shape[0]} != {x.shape[0]}"
x=rearrange(x, "b n 1 c -> (b n) 1 c")
x=self.to_cond_embed(x)
x=rearrange(x, "(b n) 1 c -> b n 1 c", b=B, n=N)
pos_emb_track=torch.zeros((B, N, 1, pos_emb_crossattn_dim), device=x.device, dtype=x.dtype)
for i in range(B):
for j in range(N):
if use_taxonomy_in_pos_emb:
assert taxonomy is not None, "taxonomy must be provided if use_taxonomy_in_pos_emb is True"
if taxonomy[i][j]=="92":
pos_emb_track[i, j, :, :]= F.one_hot(torch.tensor([0], device=x.device), num_classes=pos_emb_crossattn_dim).to(x.dtype)
elif taxonomy[i][j]=="2":
pos_emb_track[i, j, :, :]= F.one_hot(torch.tensor([1], device=x.device), num_classes=pos_emb_crossattn_dim).to(x.dtype)
elif taxonomy[i][j]=="11":
pos_emb_track[i, j, :, :]= F.one_hot(torch.tensor([2], device=x.device), num_classes=pos_emb_crossattn_dim).to(x.dtype)
else:
pos_emb_track[i, j, :, :]= F.one_hot(torch.tensor([j], device=x.device), num_classes=pos_emb_crossattn_dim).to(x.dtype)
return torch.cat((x, pos_emb_track), dim=-1)
self.pos_emb_crossattn_fn = concat_pos_emb_crossattn
else:
raise ValueError(f"Unknown pos_emb_type: {pos_emb_crossattn_type}")
global_dim = None
if self.global_cond_type == "adaLN":
# The global conditioning is projected to the embed_dim already at this point
global_dim = embed_dim
from networks.transformer import ContinuousTransformer
self.transformer = ContinuousTransformer(
dim=embed_dim,
depth=depth,
num_heads= num_heads,
dim_in=dim_in + pos_emb_dim,
dim_out=io_channels ,
cross_attend = cond_token_dim > 0,
cond_token_dim = cond_embed_dim + pos_emb_crossattn_dim,
global_cond_dim=global_dim,
**kwargs
)
self.preprocess_conv = nn.Conv1d(dim_in, dim_in, 1, bias=False)
nn.init.zeros_(self.preprocess_conv.weight)
self.postprocess_conv = nn.Conv1d(io_channels, io_channels, 1, bias=False)
nn.init.zeros_(self.postprocess_conv.weight)
def _forward(
self,
x,
t,
mask=None,
cross_attn_cond=None,
cross_attn_cond_mask=None,
input_concat_cond=None,
global_embed=None,
prepend_cond=None,
prepend_cond_mask=None,
return_info=False,
**kwargs):
t=t.squeeze(1)
#if cross_attn_cond is not None:
# cross_attn_cond = self.to_cond_embed(cross_attn_cond)
if global_embed is not None:
# Project the global conditioning to the embedding dimension
global_embed = self.to_global_embed(global_embed)
prepend_inputs = None
prepend_mask = None
prepend_length = 0
if prepend_cond is not None:
# Project the prepend conditioning to the embedding dimension
prepend_cond = self.to_prepend_embed(prepend_cond)
prepend_inputs = prepend_cond
if prepend_cond_mask is not None:
prepend_mask = prepend_cond_mask
# Get the batch of timestep embeddings
timestep_embed = self.to_timestep_embed(self.timestep_features(t[:, None])) # (b, embed_dim)
# Timestep embedding is considered a global embedding. Add to the global conditioning if it exists
if self.timestep_cond_type == "global":
if global_embed is not None:
global_embed = global_embed + timestep_embed
else:
global_embed = timestep_embed
elif self.timestep_cond_type == "input_concat":
x = torch.cat([x, timestep_embed.unsqueeze(1).expand(-1, -1, x.shape[2])], dim=1)
# Add the global_embed to the prepend inputs if there is no global conditioning support in the transformer
if self.global_cond_type == "prepend" and global_embed is not None:
if prepend_inputs is None:
# Prepend inputs are just the global embed, and the mask is all ones
prepend_inputs = global_embed.unsqueeze(1)
prepend_mask = torch.ones((x.shape[0], 1), device=x.device, dtype=torch.bool)
else:
# Prepend inputs are the prepend conditioning + the global embed
prepend_inputs = torch.cat([prepend_inputs, global_embed.unsqueeze(1)], dim=1)
prepend_mask = torch.cat([prepend_mask, torch.ones((x.shape[0], 1), device=x.device, dtype=torch.bool)], dim=1)
prepend_length = prepend_inputs.shape[1]
extra_args = {}
if self.global_cond_type == "adaLN":
extra_args["global_cond"] = global_embed
output = self.transformer(x, prepend_embeds=prepend_inputs, context=cross_attn_cond, context_mask=cross_attn_cond_mask, mask=mask, prepend_mask=prepend_mask, return_info=return_info, **extra_args, **kwargs)
if return_info:
output, info = output
output=output[:,prepend_length:,:]
if return_info:
return output, info
return output
def forward(
self,
x,
t,
cross_attn_cond=None,
cross_attn_cond_mask=None,
input_concat_cond=None,
global_embed=None,
taxonomy=None,
mask=None,
return_info=False,
**kwargs):
model_dtype = next(self.parameters()).dtype
x = x.to(model_dtype)
t = t.to(model_dtype)
if cross_attn_cond is not None:
cross_attn_cond = cross_attn_cond.to(model_dtype)
if input_concat_cond is not None:
input_concat_cond = input_concat_cond.to(model_dtype)
# Interpolate input_concat_cond to the same length as x
assert input_concat_cond.ndim == 4, f"input_concat_cond must be 4D, got {input_concat_cond.ndim}"
assert input_concat_cond.shape[0] == x.shape[0]
assert input_concat_cond.shape[1] == x.shape[1]
assert input_concat_cond.shape[-1] == x.shape[-1]
assert input_concat_cond.shape[-2] == self.input_concat_dim, f"input_concat_cond shape mismatch: {input_concat_cond.shape[-2]} != {self.input_concat_dim}"
x = torch.cat([x, input_concat_cond], dim=-2)
if global_embed is not None:
global_embed = global_embed.to(model_dtype)
if cross_attn_cond_mask is not None:
cross_attn_cond_mask = cross_attn_cond_mask.bool()
#cross_attn_cond_mask = None # Temporarily disabling conditioning masks due to kernel issue for flash attention
orig_shape = x.shape
x= rearrange(x, "b n c t -> (b n) c t")
x = self.preprocess_conv(x) + x
x=x.view(orig_shape)
x= rearrange(x, "b n c t -> b n t c")
#shape of contecxt is already [B, N, T, C] so no need to rearrange
orig_shape = x.shape
x= self.pos_emb_fn(x, taxonomy=taxonomy)
cross_attn_cond= self.pos_emb_crossattn_fn(cross_attn_cond, taxonomy=taxonomy)
x=rearrange(x, "b n t c -> b (n t) c")
cross_attn_cond_orig_shape = cross_attn_cond.shape
cross_attn_cond = rearrange(cross_attn_cond, "b n t c -> b (n t) c")
# rehape to [B, N \times T, C] for the transformer
# mask has shape [B, N ], I need to expand it to [B, N, T] for the convolution
mask= mask.unsqueeze(-1).expand(orig_shape[0], orig_shape[1], orig_shape[2])
mask= rearrange(mask, "b n t -> b (n t)")
cross_attn_cond_mask = cross_attn_cond_mask.unsqueeze(-1).expand(cross_attn_cond_orig_shape[0], cross_attn_cond_orig_shape[1], cross_attn_cond_orig_shape[2])
cross_attn_cond_mask = rearrange(cross_attn_cond_mask, "b n t -> b (n t)")
out= self._forward(
x,
t,
cross_attn_cond=cross_attn_cond,
cross_attn_cond_mask=cross_attn_cond_mask,
input_concat_cond=input_concat_cond,
global_embed=global_embed,
mask=mask,
return_info=return_info,
**kwargs
)
#print("out shape", out.shape)
out = rearrange(out, "b t c -> b c t")
out= self.postprocess_conv(out) + out
out = rearrange(out, "b c t -> b t c")
#print("out shape after postprocess", out.shape)
#now we reshape...
out = rearrange(out, "b (n t) c -> b n t c", n=orig_shape[1], t=orig_shape[2])
out=rearrange(out, "b n t c -> b n c t")
return out