import math import torch from torch import nn from typing import Optional, Any from torch import Tensor import torch.nn.functional as F import torchaudio import torchaudio.functional as audio_F import random random.seed(0) def _get_activation_fn(activ): if activ == "relu": return nn.ReLU() elif activ == "lrelu": return nn.LeakyReLU(0.2) elif activ == "swish": return lambda x: x * torch.sigmoid(x) else: raise RuntimeError( "Unexpected activ type %s, expected [relu, lrelu, swish]" % activ ) class LinearNorm(torch.nn.Module): def __init__(self, in_dim, out_dim, bias=True, w_init_gain="linear"): super(LinearNorm, self).__init__() self.linear_layer = torch.nn.Linear(in_dim, out_dim, bias=bias) torch.nn.init.xavier_uniform_( self.linear_layer.weight, gain=torch.nn.init.calculate_gain(w_init_gain) ) def forward(self, x): return self.linear_layer(x) class ConvNorm(torch.nn.Module): def __init__( self, in_channels, out_channels, kernel_size=1, stride=1, padding=None, dilation=1, bias=True, w_init_gain="linear", param=None, ): super(ConvNorm, self).__init__() if padding is None: assert kernel_size % 2 == 1 padding = int(dilation * (kernel_size - 1) / 2) self.conv = torch.nn.Conv1d( in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding, dilation=dilation, bias=bias, ) torch.nn.init.xavier_uniform_( self.conv.weight, gain=torch.nn.init.calculate_gain(w_init_gain, param=param), ) def forward(self, signal): conv_signal = self.conv(signal) return conv_signal class CausualConv(nn.Module): def __init__( self, in_channels, out_channels, kernel_size=1, stride=1, padding=1, dilation=1, bias=True, w_init_gain="linear", param=None, ): super(CausualConv, self).__init__() if padding is None: assert kernel_size % 2 == 1 padding = int(dilation * (kernel_size - 1) / 2) * 2 else: self.padding = padding * 2 self.conv = nn.Conv1d( in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=self.padding, dilation=dilation, bias=bias, ) torch.nn.init.xavier_uniform_( self.conv.weight, gain=torch.nn.init.calculate_gain(w_init_gain, param=param), ) def forward(self, x): x = self.conv(x) x = x[:, :, : -self.padding] return x class CausualBlock(nn.Module): def __init__(self, hidden_dim, n_conv=3, dropout_p=0.2, activ="lrelu"): super(CausualBlock, self).__init__() self.blocks = nn.ModuleList( [ self._get_conv( hidden_dim, dilation=3**i, activ=activ, dropout_p=dropout_p ) for i in range(n_conv) ] ) def forward(self, x): for block in self.blocks: res = x x = block(x) x += res return x def _get_conv(self, hidden_dim, dilation, activ="lrelu", dropout_p=0.2): layers = [ CausualConv( hidden_dim, hidden_dim, kernel_size=3, padding=dilation, dilation=dilation, ), _get_activation_fn(activ), nn.BatchNorm1d(hidden_dim), nn.Dropout(p=dropout_p), CausualConv(hidden_dim, hidden_dim, kernel_size=3, padding=1, dilation=1), _get_activation_fn(activ), nn.Dropout(p=dropout_p), ] return nn.Sequential(*layers) class ConvBlock(nn.Module): def __init__(self, hidden_dim, n_conv=3, dropout_p=0.2, activ="relu"): super().__init__() self._n_groups = 8 self.blocks = nn.ModuleList( [ self._get_conv( hidden_dim, dilation=3**i, activ=activ, dropout_p=dropout_p ) for i in range(n_conv) ] ) def forward(self, x): for block in self.blocks: res = x x = block(x) x += res return x def _get_conv(self, hidden_dim, dilation, activ="relu", dropout_p=0.2): layers = [ ConvNorm( hidden_dim, hidden_dim, kernel_size=3, padding=dilation, dilation=dilation, ), _get_activation_fn(activ), nn.GroupNorm(num_groups=self._n_groups, num_channels=hidden_dim), nn.Dropout(p=dropout_p), ConvNorm(hidden_dim, hidden_dim, kernel_size=3, padding=1, dilation=1), _get_activation_fn(activ), nn.Dropout(p=dropout_p), ] return nn.Sequential(*layers) class LocationLayer(nn.Module): def __init__(self, attention_n_filters, attention_kernel_size, attention_dim): super(LocationLayer, self).__init__() padding = int((attention_kernel_size - 1) / 2) self.location_conv = ConvNorm( 2, attention_n_filters, kernel_size=attention_kernel_size, padding=padding, bias=False, stride=1, dilation=1, ) self.location_dense = LinearNorm( attention_n_filters, attention_dim, bias=False, w_init_gain="tanh" ) def forward(self, attention_weights_cat): processed_attention = self.location_conv(attention_weights_cat) processed_attention = processed_attention.transpose(1, 2) processed_attention = self.location_dense(processed_attention) return processed_attention class Attention(nn.Module): def __init__( self, attention_rnn_dim, embedding_dim, attention_dim, attention_location_n_filters, attention_location_kernel_size, ): super(Attention, self).__init__() self.query_layer = LinearNorm( attention_rnn_dim, attention_dim, bias=False, w_init_gain="tanh" ) self.memory_layer = LinearNorm( embedding_dim, attention_dim, bias=False, w_init_gain="tanh" ) self.v = LinearNorm(attention_dim, 1, bias=False) self.location_layer = LocationLayer( attention_location_n_filters, attention_location_kernel_size, attention_dim ) self.score_mask_value = -float("inf") def get_alignment_energies(self, query, processed_memory, attention_weights_cat): """ PARAMS ------ query: decoder output (batch, n_mel_channels * n_frames_per_step) processed_memory: processed encoder outputs (B, T_in, attention_dim) attention_weights_cat: cumulative and prev. att weights (B, 2, max_time) RETURNS ------- alignment (batch, max_time) """ processed_query = self.query_layer(query.unsqueeze(1)) processed_attention_weights = self.location_layer(attention_weights_cat) energies = self.v( torch.tanh(processed_query + processed_attention_weights + processed_memory) ) energies = energies.squeeze(-1) return energies def forward( self, attention_hidden_state, memory, processed_memory, attention_weights_cat, mask, ): """ PARAMS ------ attention_hidden_state: attention rnn last output memory: encoder outputs processed_memory: processed encoder outputs attention_weights_cat: previous and cummulative attention weights mask: binary mask for padded data """ alignment = self.get_alignment_energies( attention_hidden_state, processed_memory, attention_weights_cat ) if mask is not None: alignment.data.masked_fill_(mask, self.score_mask_value) attention_weights = F.softmax(alignment, dim=1) attention_context = torch.bmm(attention_weights.unsqueeze(1), memory) attention_context = attention_context.squeeze(1) return attention_context, attention_weights class ForwardAttentionV2(nn.Module): def __init__( self, attention_rnn_dim, embedding_dim, attention_dim, attention_location_n_filters, attention_location_kernel_size, ): super(ForwardAttentionV2, self).__init__() self.query_layer = LinearNorm( attention_rnn_dim, attention_dim, bias=False, w_init_gain="tanh" ) self.memory_layer = LinearNorm( embedding_dim, attention_dim, bias=False, w_init_gain="tanh" ) self.v = LinearNorm(attention_dim, 1, bias=False) self.location_layer = LocationLayer( attention_location_n_filters, attention_location_kernel_size, attention_dim ) self.score_mask_value = -float(1e20) def get_alignment_energies(self, query, processed_memory, attention_weights_cat): """ PARAMS ------ query: decoder output (batch, n_mel_channels * n_frames_per_step) processed_memory: processed encoder outputs (B, T_in, attention_dim) attention_weights_cat: prev. and cumulative att weights (B, 2, max_time) RETURNS ------- alignment (batch, max_time) """ processed_query = self.query_layer(query.unsqueeze(1)) processed_attention_weights = self.location_layer(attention_weights_cat) energies = self.v( torch.tanh(processed_query + processed_attention_weights + processed_memory) ) energies = energies.squeeze(-1) return energies def forward( self, attention_hidden_state, memory, processed_memory, attention_weights_cat, mask, log_alpha, ): """ PARAMS ------ attention_hidden_state: attention rnn last output memory: encoder outputs processed_memory: processed encoder outputs attention_weights_cat: previous and cummulative attention weights mask: binary mask for padded data """ log_energy = self.get_alignment_energies( attention_hidden_state, processed_memory, attention_weights_cat ) # log_energy = if mask is not None: log_energy.data.masked_fill_(mask, self.score_mask_value) # attention_weights = F.softmax(alignment, dim=1) # content_score = log_energy.unsqueeze(1) #[B, MAX_TIME] -> [B, 1, MAX_TIME] # log_alpha = log_alpha.unsqueeze(2) #[B, MAX_TIME] -> [B, MAX_TIME, 1] # log_total_score = log_alpha + content_score # previous_attention_weights = attention_weights_cat[:,0,:] log_alpha_shift_padded = [] max_time = log_energy.size(1) for sft in range(2): shifted = log_alpha[:, : max_time - sft] shift_padded = F.pad(shifted, (sft, 0), "constant", self.score_mask_value) log_alpha_shift_padded.append(shift_padded.unsqueeze(2)) biased = torch.logsumexp(torch.cat(log_alpha_shift_padded, 2), 2) log_alpha_new = biased + log_energy attention_weights = F.softmax(log_alpha_new, dim=1) attention_context = torch.bmm(attention_weights.unsqueeze(1), memory) attention_context = attention_context.squeeze(1) return attention_context, attention_weights, log_alpha_new class PhaseShuffle2d(nn.Module): def __init__(self, n=2): super(PhaseShuffle2d, self).__init__() self.n = n self.random = random.Random(1) def forward(self, x, move=None): # x.size = (B, C, M, L) if move is None: move = self.random.randint(-self.n, self.n) if move == 0: return x else: left = x[:, :, :, :move] right = x[:, :, :, move:] shuffled = torch.cat([right, left], dim=3) return shuffled class PhaseShuffle1d(nn.Module): def __init__(self, n=2): super(PhaseShuffle1d, self).__init__() self.n = n self.random = random.Random(1) def forward(self, x, move=None): # x.size = (B, C, M, L) if move is None: move = self.random.randint(-self.n, self.n) if move == 0: return x else: left = x[:, :, :move] right = x[:, :, move:] shuffled = torch.cat([right, left], dim=2) return shuffled class MFCC(nn.Module): def __init__(self, n_mfcc=40, n_mels=80): super(MFCC, self).__init__() self.n_mfcc = n_mfcc self.n_mels = n_mels self.norm = "ortho" dct_mat = audio_F.create_dct(self.n_mfcc, self.n_mels, self.norm) self.register_buffer("dct_mat", dct_mat) def forward(self, mel_specgram): if len(mel_specgram.shape) == 2: mel_specgram = mel_specgram.unsqueeze(0) unsqueezed = True else: unsqueezed = False # (channel, n_mels, time).tranpose(...) dot (n_mels, n_mfcc) # -> (channel, time, n_mfcc).tranpose(...) mfcc = torch.matmul(mel_specgram.transpose(1, 2), self.dct_mat).transpose(1, 2) # unpack batch if unsqueezed: mfcc = mfcc.squeeze(0) return mfcc