|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import torch |
|
import torch.nn as nn |
|
|
|
from typing import List, Tuple |
|
from sparktts.modules.fsq.residual_fsq import ResidualFSQ |
|
from sparktts.modules.speaker.ecapa_tdnn import ECAPA_TDNN_GLOB_c512 |
|
from sparktts.modules.speaker.perceiver_encoder import PerceiverResampler |
|
|
|
""" |
|
x-vector + d-vector |
|
""" |
|
|
|
|
|
class SpeakerEncoder(nn.Module): |
|
""" |
|
|
|
Args: |
|
input_dim (int): acoustic feature dimension |
|
out_dim (int): output dimension of x-vector and d-vector |
|
latent_dim (int): latent dimension before quantization |
|
token_num (int): sequence length of speaker tokens |
|
fsq_levels (List[int]): number of levels for each quantizer |
|
fsq_num_quantizers (int): number of quantizers |
|
|
|
Return: |
|
speaker_embs: (B, T2, out_dim) |
|
""" |
|
|
|
def __init__( |
|
self, |
|
input_dim: int = 100, |
|
out_dim: int = 512, |
|
latent_dim: int = 128, |
|
token_num: int = 32, |
|
fsq_levels: List[int] = [4, 4, 4, 4, 4, 4], |
|
fsq_num_quantizers: int = 1, |
|
): |
|
super(SpeakerEncoder, self).__init__() |
|
|
|
self.speaker_encoder = ECAPA_TDNN_GLOB_c512( |
|
feat_dim=input_dim, embed_dim=out_dim |
|
) |
|
self.perceiver_sampler = PerceiverResampler( |
|
dim=latent_dim, dim_context=512 * 3, num_latents=token_num |
|
) |
|
self.quantizer = ResidualFSQ( |
|
levels=fsq_levels, |
|
num_quantizers=fsq_num_quantizers, |
|
dim=latent_dim, |
|
is_channel_first=True, |
|
quantize_dropout=False, |
|
) |
|
|
|
self.project = nn.Linear(latent_dim * token_num, out_dim) |
|
|
|
def get_codes_from_indices(self, indices: torch.Tensor) -> torch.Tensor: |
|
zq = self.quantizer.get_codes_from_indices(indices.transpose(1, 2)) |
|
return zq.transpose(1, 2) |
|
|
|
def get_indices(self, mels: torch.Tensor) -> torch.Tensor: |
|
mels = mels.transpose(1, 2) |
|
x = self.perceiver_sampler(mels).transpose(1, 2) |
|
zq, indices = self.quantizer(x) |
|
return indices |
|
|
|
def forward(self, mels: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: |
|
""" |
|
Args: |
|
mels: (B, D_mel, T1) |
|
|
|
Return: |
|
x_vector: (B, out_dim) |
|
d_vector: (B, out_dim) |
|
""" |
|
|
|
|
|
x_vector, features = self.speaker_encoder(mels, True) |
|
x = self.perceiver_sampler(features.transpose(1, 2)).transpose(1, 2) |
|
zq, indices = self.quantizer(x) |
|
x = zq.reshape(zq.shape[0], -1) |
|
d_vector = self.project(x) |
|
|
|
return x_vector, d_vector |
|
|
|
def tokenize(self, mels: torch.Tensor) -> torch.Tensor: |
|
"""tokenize the input mel spectrogram""" |
|
_, features = self.speaker_encoder(mels, True) |
|
x = self.perceiver_sampler(features.transpose(1, 2)).transpose(1, 2) |
|
zq, indices = self.quantizer(x) |
|
return indices |
|
|
|
def detokenize(self, indices: torch.Tensor) -> torch.Tensor: |
|
"""detokenize the input indices to d-vector""" |
|
zq = self.quantizer.get_output_from_indices(indices.transpose(1, 2)).transpose(1, 2) |
|
x = zq.reshape(zq.shape[0], -1) |
|
d_vector = self.project(x) |
|
return d_vector |
|
|
|
if __name__ == "__main__": |
|
model = SpeakerEncoder( |
|
input_dim=100, |
|
latent_dim=128, |
|
token_num=32, |
|
fsq_levels=[4, 4, 4, 4, 4, 4], |
|
fsq_num_quantizers=1, |
|
) |
|
mel = torch.randn(8, 200, 100) |
|
x_vector, d_vector = model(mel) |
|
print("x-vector shape", x_vector.shape) |
|
print("d-vector shape", d_vector.shape) |
|
|
|
indices = model.tokenize(mel) |
|
print("indices shape", indices.shape) |
|
d_vector_post = model.detokenize(indices) |
|
print("d-vector shape", d_vector_post.shape) |
|
if d_vector_post.all() == d_vector.all(): |
|
print("d-vector post and d-vector are the same") |
|
else: |
|
print("d-vector post and d-vector are different") |
|
num_params = sum(param.numel() for param in model.parameters()) |
|
print("{} M".format(num_params / 1e6)) |