speaker-verification
audio
nemotron
tyger
experimental
ChristophSchuhmann's picture
Publish audited Robby source and completed Nemotron speaker-prediction experiments
b21cfae verified
Raw History Blame Contribute Delete
2.76 kB
"""Small causal Nemotron-L15 -> ECAPA-192 predictors; no ASR/teacher weights."""
from __future__ import annotations
import torch
from torch import nn
from torch.nn import functional as F
class SpeakerPredictor(nn.Module):
def __init__(self, architecture="gru", width=128, input_dim=1024, output_dim=192, centered=False):
super().__init__()
self.architecture = architecture
self.width = width
self.centered = centered
self.norm = nn.LayerNorm(input_dim)
if architecture == "mlp":
self.encoder = nn.Sequential(nn.Linear(input_dim * 2, width), nn.GELU(),
nn.Linear(width, width), nn.GELU())
elif architecture == "gru":
self.project = nn.Sequential(nn.Linear(input_dim, width), nn.GELU())
self.encoder = nn.GRU(width, width, batch_first=True)
elif architecture == "tcn":
self.project = nn.Sequential(nn.Linear(input_dim, width), nn.GELU())
self.encoder = nn.ModuleList([
nn.Sequential(nn.Conv1d(width, width, 3, groups=width, dilation=d),
nn.GELU(), nn.Conv1d(width, width, 1), nn.GELU())
for d in (1, 2, 4)])
else:
raise ValueError(architecture)
self.embedding = nn.Linear(width, output_dim)
self.activity = nn.Linear(width, 3) # speech / silence / ambiguous overlap
self.register_buffer("teacher_mean",torch.zeros(output_dim))
self.register_buffer("teacher_radius",torch.ones(()))
def forward(self, features, lengths=None):
x = self.norm(features)
if self.architecture == "mlp":
h = self.encoder(torch.cat([x.mean(1), x.std(1, unbiased=False)], -1))
elif self.architecture == "gru":
h, _ = self.encoder(self.project(x))
h = h[:, -1]
else:
h = self.project(x).transpose(1, 2)
for dilation, layer in zip((1, 2, 4), self.encoder):
h = h + layer(F.pad(h, (2 * dilation, 0)))
h = h[:, :, -1]
direction = F.normalize(self.embedding(h),dim=-1)
embedding = F.normalize(self.teacher_mean+self.teacher_radius*direction,dim=-1) if self.centered else direction
return embedding, self.activity(h)
def load_state(model, state):
"""Compatibility with the first uncentered pilot checkpoints."""
missing, unexpected = model.load_state_dict(state,strict=False)
if unexpected or set(missing)-{"teacher_mean","teacher_radius"}:
raise ValueError(f"Unexpected checkpoint mismatch: missing={missing}, unexpected={unexpected}")
def parameter_count(model):
return sum(p.numel() for p in model.parameters() if p.requires_grad)