Spaces:
Running
Running
import torch | |
import torch.nn as nn | |
from tortoise.models.arch_util import ( | |
AttentionBlock, | |
Downsample, | |
Upsample, | |
normalization, | |
zero_module, | |
) | |
class ResBlock(nn.Module): | |
def __init__( | |
self, | |
channels, | |
dropout, | |
out_channels=None, | |
use_conv=False, | |
use_scale_shift_norm=False, | |
dims=2, | |
up=False, | |
down=False, | |
kernel_size=3, | |
do_checkpoint=True, | |
): | |
super().__init__() | |
self.channels = channels | |
self.dropout = dropout | |
self.out_channels = out_channels or channels | |
self.use_conv = use_conv | |
self.use_scale_shift_norm = use_scale_shift_norm | |
self.do_checkpoint = do_checkpoint | |
padding = 1 if kernel_size == 3 else 2 | |
self.in_layers = nn.Sequential( | |
normalization(channels), | |
nn.SiLU(), | |
nn.Conv1d(channels, self.out_channels, kernel_size, padding=padding), | |
) | |
self.updown = up or down | |
if up: | |
self.h_upd = Upsample(channels, False, dims) | |
self.x_upd = Upsample(channels, False, dims) | |
elif down: | |
self.h_upd = Downsample(channels, False, dims) | |
self.x_upd = Downsample(channels, False, dims) | |
else: | |
self.h_upd = self.x_upd = nn.Identity() | |
self.out_layers = nn.Sequential( | |
normalization(self.out_channels), | |
nn.SiLU(), | |
nn.Dropout(p=dropout), | |
zero_module( | |
nn.Conv1d( | |
self.out_channels, self.out_channels, kernel_size, padding=padding | |
) | |
), | |
) | |
if self.out_channels == channels: | |
self.skip_connection = nn.Identity() | |
elif use_conv: | |
self.skip_connection = nn.Conv1d( | |
dims, channels, self.out_channels, kernel_size, padding=padding | |
) | |
else: | |
self.skip_connection = nn.Conv1d(dims, channels, self.out_channels, 1) | |
def forward(self, x): | |
if self.updown: | |
in_rest, in_conv = self.in_layers[:-1], self.in_layers[-1] | |
h = in_rest(x) | |
h = self.h_upd(h) | |
x = self.x_upd(x) | |
h = in_conv(h) | |
else: | |
h = self.in_layers(x) | |
h = self.out_layers(h) | |
return self.skip_connection(x) + h | |
class AudioMiniEncoder(nn.Module): | |
def __init__( | |
self, | |
spec_dim, | |
embedding_dim, | |
base_channels=128, | |
depth=2, | |
resnet_blocks=2, | |
attn_blocks=4, | |
num_attn_heads=4, | |
dropout=0, | |
downsample_factor=2, | |
kernel_size=3, | |
): | |
super().__init__() | |
self.init = nn.Sequential(nn.Conv1d(spec_dim, base_channels, 3, padding=1)) | |
ch = base_channels | |
res = [] | |
self.layers = depth | |
for l in range(depth): | |
for r in range(resnet_blocks): | |
res.append( | |
ResBlock(ch, dropout, do_checkpoint=False, kernel_size=kernel_size) | |
) | |
res.append( | |
Downsample( | |
ch, use_conv=True, out_channels=ch * 2, factor=downsample_factor | |
) | |
) | |
ch *= 2 | |
self.res = nn.Sequential(*res) | |
self.final = nn.Sequential( | |
normalization(ch), nn.SiLU(), nn.Conv1d(ch, embedding_dim, 1) | |
) | |
attn = [] | |
for a in range(attn_blocks): | |
attn.append( | |
AttentionBlock(embedding_dim, num_attn_heads, do_checkpoint=False) | |
) | |
self.attn = nn.Sequential(*attn) | |
self.dim = embedding_dim | |
def forward(self, x): | |
h = self.init(x) | |
h = self.res(h) | |
h = self.final(h) | |
for blk in self.attn: | |
h = blk(h) | |
return h[:, :, 0] | |
class AudioMiniEncoderWithClassifierHead(nn.Module): | |
def __init__(self, classes, distribute_zero_label=True, **kwargs): | |
super().__init__() | |
self.enc = AudioMiniEncoder(**kwargs) | |
self.head = nn.Linear(self.enc.dim, classes) | |
self.num_classes = classes | |
self.distribute_zero_label = distribute_zero_label | |
def forward(self, x, labels=None): | |
h = self.enc(x) | |
logits = self.head(h) | |
if labels is None: | |
return logits | |
else: | |
if self.distribute_zero_label: | |
oh_labels = nn.functional.one_hot(labels, num_classes=self.num_classes) | |
zeros_indices = (labels == 0).unsqueeze(-1) | |
# Distribute 20% of the probability mass on all classes when zero is specified, to compensate for dataset noise. | |
zero_extra_mass = torch.full_like( | |
oh_labels, | |
dtype=torch.float, | |
fill_value=0.2 / (self.num_classes - 1), | |
) | |
zero_extra_mass[:, 0] = -0.2 | |
zero_extra_mass = zero_extra_mass * zeros_indices | |
oh_labels = oh_labels + zero_extra_mass | |
else: | |
oh_labels = labels | |
loss = nn.functional.cross_entropy(logits, oh_labels) | |
return loss | |