SFlowTTS / Modules /predictor_duration_speaker_context.py
FashionFlora's picture
Upload full repo excluding dump_40, dump_100, precomputed_tokens, precomputed_data
fb0011a verified
Raw History Blame Contribute Delete
9.95 kB
# predictor_duration_speaker_context.py
# ==============================================================================
# Duration Predictor with Speaker ID + Context Window Conditioning
#
# Architecture:
# - Text encoder (shared ConvNeXtV2TextEncoder from predictor_fsq_flow_convnext)
# encodes the full context window (prev + curr + next concatenated) in one pass
# - _extract_current_context_embeddings slices out only the current
# sentence embeddings by token_start/end indices
# - Stochastic Duration Predictor (Flow-based) with speaker conditioning
# ==============================================================================
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
from typing import Optional
from Modules.transforms import piecewise_rational_quadratic_transform
# Import from meldataset
from meldataset import SPOKEN_IDS
# ==============================================================================
# 1. UTILS & BASIC LAYERS
# ==============================================================================
def length_to_mask(lengths: torch.Tensor, max_len: Optional[int] = None) -> torch.Tensor:
max_len = int(max_len or lengths.max().item())
rng = torch.arange(max_len, device=lengths.device).unsqueeze(0)
mask = rng < lengths.unsqueeze(1)
return mask
# ==============================================================================
# 2. SDP COMPONENTS (Flow Layers)
# ==============================================================================
class Flip(nn.Module):
def forward(self, x, *args, reverse=False, **kwargs):
x = torch.flip(x, [1])
if not reverse:
logdet = torch.zeros(x.size(0)).to(dtype=x.dtype, device=x.device)
return x, logdet
else:
return x
class ElementwiseAffine(nn.Module):
def __init__(self, channels):
super().__init__()
self.channels = channels
self.m = nn.Parameter(torch.zeros(channels, 1))
self.logs = nn.Parameter(torch.zeros(channels, 1))
def forward(self, x, x_mask, reverse=False, **kwargs):
if not reverse:
y = self.m + torch.exp(self.logs) * x
y = y * x_mask
logdet = torch.sum(self.logs * x_mask, [1, 2])
return y, logdet
else:
x = (x - self.m) * torch.exp(-self.logs) * x_mask
return x
class Log(nn.Module):
def forward(self, x, x_mask, reverse=False, **kwargs):
if not reverse:
y = torch.log(torch.clamp_min(x, 1e-5)) * x_mask
logdet = torch.sum(-y, [1, 2])
return y, logdet
else:
x = torch.exp(x) * x_mask
return x
class LayerNorm(nn.Module):
def __init__(self, channels, eps=1e-5):
super().__init__()
self.channels = channels
self.eps = eps
self.gamma = nn.Parameter(torch.ones(channels))
self.beta = nn.Parameter(torch.zeros(channels))
def forward(self, x):
x = x.transpose(1, -1)
x = F.layer_norm(x, (self.channels,), self.gamma, self.beta, self.eps)
return x.transpose(1, -1)
class DDSConv(nn.Module):
def __init__(self, channels, kernel_size, n_layers, p_dropout=0.0):
super().__init__()
self.n_layers = n_layers
self.drop = nn.Dropout(p_dropout)
self.convs_sep = nn.ModuleList()
self.linears = nn.ModuleList()
self.norms_1 = nn.ModuleList()
self.norms_2 = nn.ModuleList()
for i in range(n_layers):
dilation = kernel_size**i
padding = (kernel_size * dilation - dilation) // 2
self.convs_sep.append(nn.Conv1d(channels, channels, kernel_size, groups=channels, dilation=dilation, padding=padding))
self.linears.append(nn.Linear(channels, channels))
self.norms_1.append(LayerNorm(channels))
self.norms_2.append(LayerNorm(channels))
def forward(self, x, x_mask, g=None):
if g is not None:
x = x + g
for i in range(self.n_layers):
y = self.convs_sep[i](x * x_mask)
y = self.norms_1[i](y)
y = F.gelu(y)
y = self.linears[i](y.mT).mT
y = self.norms_2[i](y)
y = F.gelu(y)
y = self.drop(y)
x = x + y
return x * x_mask
class ConvFlow(nn.Module):
def __init__(self, in_channels, filter_channels, kernel_size, n_layers, num_bins=16, tail_bound=5.0):
super().__init__()
self.filter_channels = filter_channels
self.num_bins = num_bins
self.tail_bound = tail_bound
self.half_channels = in_channels // 2
self.pre = nn.Linear(self.half_channels, filter_channels)
self.convs = DDSConv(filter_channels, kernel_size, n_layers, p_dropout=0.0)
self.proj = nn.Linear(filter_channels, self.half_channels * (num_bins * 3 - 1))
def forward(self, x, x_mask, g=None, reverse=False):
x0, x1 = torch.split(x, [self.half_channels] * 2, 1)
h = self.pre(x0.mT).mT
h = self.convs(h, x_mask, g=g)
h = self.proj(h.mT).mT * x_mask
b, c, t = x0.shape
h = h.reshape(b, c, -1, t).permute(0, 1, 3, 2)
unnormalized_widths = h[..., : self.num_bins] / math.sqrt(self.filter_channels)
unnormalized_heights = h[..., self.num_bins : 2 * self.num_bins] / math.sqrt(self.filter_channels)
unnormalized_derivatives = h[..., 2 * self.num_bins :]
x1, logabsdet = piecewise_rational_quadratic_transform(
x1, unnormalized_widths, unnormalized_heights, unnormalized_derivatives,
inverse=reverse, tails="linear", tail_bound=self.tail_bound
)
x = torch.cat([x0, x1], 1) * x_mask
logdet = torch.sum(logabsdet * x_mask, [1, 2])
if not reverse:
return x, logdet
else:
return x
# ==============================================================================
# 3. STOCHASTIC DURATION PREDICTOR WITH CONTEXT
# ==============================================================================
class StochasticDurationPredictorContext(nn.Module):
"""
Stochastic Duration Predictor with speaker + context conditioning.
"""
def __init__(self, in_channels, filter_channels, kernel_size, p_dropout, n_flows=4,
speaker_emb_dim=256):
super().__init__()
self.log_flow = Log()
self.flows = nn.ModuleList()
self.flows.append(ElementwiseAffine(2))
for i in range(n_flows):
self.flows.append(ConvFlow(2, filter_channels, kernel_size, n_layers=3))
self.flows.append(Flip())
self.pre = nn.Linear(in_channels, filter_channels)
self.convs = DDSConv(filter_channels, kernel_size, n_layers=3, p_dropout=p_dropout)
self.proj = nn.Linear(filter_channels, filter_channels)
self.post_pre = nn.Linear(1, filter_channels)
self.post_convs = DDSConv(filter_channels, kernel_size, n_layers=3, p_dropout=p_dropout)
self.post_proj = nn.Linear(filter_channels, filter_channels)
self.post_flows = nn.ModuleList()
self.post_flows.append(ElementwiseAffine(2))
for i in range(4):
self.post_flows.append(ConvFlow(2, filter_channels, kernel_size, n_layers=3))
self.post_flows.append(Flip())
# Speaker conditioning
if speaker_emb_dim > 0:
self.cond_speaker = nn.Linear(speaker_emb_dim, filter_channels)
def forward(self, x, x_mask, w=None, g=None, reverse=False, noise_scale=1.0):
"""
Args:
x: [B, C, T] text features
x_mask: [B, 1, T] mask
w: [B, 1, T] ground truth durations (training only)
g: [B, speaker_emb_dim] speaker embedding
reverse: inference mode
noise_scale: sampling temperature
"""
x = self.pre(x.mT).mT
# Add speaker conditioning
if g is not None and hasattr(self, 'cond_speaker'):
g_expanded = g.unsqueeze(-1).expand(-1, -1, x.size(2))
x = x + self.cond_speaker(g_expanded.mT).mT
x = self.convs(x, x_mask)
x = self.proj(x.mT).mT * x_mask
if not reverse:
# Training
h_w = self.post_pre(w.mT).mT
h_w = self.post_convs(h_w, x_mask)
h_w = self.post_proj(h_w.mT).mT * x_mask
e_q = torch.randn(w.size(0), 2, w.size(2)).to(device=x.device, dtype=x.dtype) * x_mask
z_q = e_q
logdet_tot_q = 0
for flow in self.post_flows:
z_q, logdet_q = flow(z_q, x_mask, g=(x + h_w))
logdet_tot_q += logdet_q
z_u, z1 = torch.split(z_q, [1, 1], 1)
u = torch.sigmoid(z_u) * x_mask
z0 = (w - u) * x_mask
logdet_tot_q += torch.sum((F.logsigmoid(z_u) + F.logsigmoid(-z_u)) * x_mask, [1, 2])
logq = torch.sum(-0.5 * (math.log(2 * math.pi) + (e_q**2)) * x_mask, [1, 2]) - logdet_tot_q
logdet_tot = 0
z0, logdet = self.log_flow(z0, x_mask)
logdet_tot += logdet
z = torch.cat([z0, z1], 1)
for flow in self.flows:
z, logdet = flow(z, x_mask, g=x, reverse=reverse)
logdet_tot += logdet
nll = torch.sum(0.5 * (math.log(2 * math.pi) + (z**2)) * x_mask, [1, 2]) - logdet_tot
return nll + logq
else:
# Inference
flows = list(reversed(self.flows))
flows = flows[:-2] + [flows[-1]]
z = torch.randn(x.size(0), 2, x.size(2)).to(device=x.device, dtype=x.dtype) * noise_scale
for flow in flows:
z = flow(z, x_mask, g=x, reverse=reverse)
z0, z1 = torch.split(z, [1, 1], 1)
return z0