Introduce code from Rust monorepo. This is a mess

This commit is contained in:
Brandon Thomas
2022-02-28 05:43:54 -05:00
parent 921fb49f9a
commit ba326bfcfd
1058 changed files with 192192 additions and 0 deletions
@@ -0,0 +1,21 @@
# Speaker Recognition
Documentation section for speaker related tasks can be found at:
- [Speaker Recognition and Verification](https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/main/asr/speaker_recognition/intro.html)
- [Speaker Diarization](https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/main/asr/speaker_diarization/intro.html)
This folder contains scripts for performing various speaker related tasks as breiefly explained below:
- **speaker_reco.py**
: Training script for speaker verification and recognition
- **speaker_reco_infer.py**
: Inference for speaker recognition. Can be used to map labels using trained model on test set with known speaker labels from train set. (speaker id)
- **extract_speaker_embeddings.py**
: Extract speaker embeddings using trained model for speaker verification purposes.
- **speaker_reco_finetune.py**
: Sample script to demonstrate on how to finetune a already trained model for specific sub tasks.
- **voxceleb_eval.py**
: Helper script to evaluate speaker embeddings on voxceleb trail files.
- **speaker_diarize.py**
: Inference script for speaker diarization - knowing who spoke when.
- **conf/**
: Folder containing configuration files for training speaker tasks. We currently only support inference for speaker diarization.
@@ -0,0 +1,157 @@
name: &name "SpeakerNet"
sample_rate: &sample_rate 16000
repeat: &rep 2
dropout: &drop 0.5
separable: &separable True
n_filters: &n_filters 512
model:
train_ds:
manifest_filepath: ???
sample_rate: 16000
labels: null
batch_size: 64
shuffle: True
time_length: 8
is_tarred: False
tarred_audio_filepaths: null
tarred_shard_strategy: "scatter"
validation_ds:
manifest_filepath: ???
sample_rate: 16000
labels: null
batch_size: 128
shuffle: False
time_length: 8
test_ds:
manifest_filepath: ???
sample_rate: 16000
labels: null
batch_size: 1
shuffle: False
time_length: 8
embedding_dir: '.'
preprocessor:
_target_: nemo.collections.asr.modules.AudioToMelSpectrogramPreprocessor
normalize: "per_feature"
window_size: 0.02
sample_rate: *sample_rate
window_stride: 0.01
window: "hann"
features: &n_mels 64
n_fft: 512
frame_splicing: 1
dither: 0.00001
encoder:
_target_: nemo.collections.asr.modules.ConvASREncoder
feat_in: *n_mels
activation: relu
conv_mask: true
jasper:
- filters: *n_filters
repeat: 1
kernel: [3]
stride: [1]
dilation: [1]
dropout: *drop
residual: true
separable: *separable
- filters: *n_filters
repeat: *rep
kernel: [7]
stride: [1]
dilation: [1]
dropout: *drop
residual: true
separable: *separable
- filters: *n_filters
repeat: *rep
kernel: [11]
stride: [1]
dilation: [1]
dropout: *drop
residual: true
separable: *separable
- filters: *n_filters
repeat: *rep
kernel: [15]
stride: [1]
dilation: [1]
dropout: *drop
residual: true
separable: *separable
- filters: &enc_feat_out 1500
repeat: 1
kernel: [1]
stride: [1]
dilation: [1]
dropout: 0.0
residual: false
separable: *separable
decoder:
_target_: nemo.collections.asr.modules.SpeakerDecoder
feat_in: *enc_feat_out
num_classes: 7205
pool_mode: 'xvector'
emb_sizes: 512,512
angular: False
loss:
scale: 30
margin: 0.2
optim:
name: novograd
# _target_: nemo.core.optim.optimizers.Novograd
lr: .008
# optimizer arguments
args:
name: auto
# _target_: nemo.core.config.optimizers.NovogradParams
betas: [0.95, 0.5]
weight_decay: 0.001
# scheduler setup
sched:
name: CosineAnnealing
iters_per_batch: 1 # computed at runtime
max_steps: null # computed at runtime or explicitly set here
# scheduler config override
args:
name: auto
# _target_: nemo.core.config.schedulers.CosineAnnealingParams
warmup_steps: null
warmup_ratio: 0.1
min_lr: 0.0
last_epoch: -1
trainer:
gpus: 1 # number of gpus
max_epochs: 200
max_steps: null # computed at runtime if not set
num_nodes: 1
accelerator: ddp
accumulate_grad_batches: 1
amp_level: O0
deterministic: True
checkpoint_callback: False
logger: False
log_every_n_steps: 1 # Interval of logging.
val_check_interval: 1.0 # Set to 0.25 to check 4 times per epoch, or an int for number of iterations
exp_manager:
exp_dir: null
name: *name
create_tensorboard_logger: True
create_checkpoint_callback: True
@@ -0,0 +1,145 @@
name: &name "SpeakerNet"
sample_rate: &sample_rate 16000
repeat: &rep 2
dropout: &drop 0.5
separable: &separable True
n_filters: &n_filters 512
model:
train_ds:
manifest_filepath: ???
sample_rate: 16000
labels: null
batch_size: 64
shuffle: True
time_length: 8
is_tarred: False
tarred_audio_filepaths: null
tarred_shard_strategy: "scatter"
validation_ds:
manifest_filepath: ???
sample_rate: 16000
labels: null
batch_size: 128
shuffle: False
time_length: 8
preprocessor:
_target_: nemo.collections.asr.modules.AudioToMelSpectrogramPreprocessor
normalize: "per_feature"
window_size: 0.02
sample_rate: *sample_rate
window_stride: 0.01
window: "hann"
features: &n_mels 64
n_fft: 512
frame_splicing: 1
dither: 0.00001
encoder:
_target_: nemo.collections.asr.modules.ConvASREncoder
feat_in: *n_mels
activation: relu
conv_mask: true
jasper:
- filters: *n_filters
repeat: 1
kernel: [3]
stride: [1]
dilation: [1]
dropout: *drop
residual: true
separable: *separable
- filters: *n_filters
repeat: *rep
kernel: [7]
stride: [1]
dilation: [1]
dropout: *drop
residual: true
separable: *separable
- filters: *n_filters
repeat: *rep
kernel: [11]
stride: [1]
dilation: [1]
dropout: *drop
residual: true
separable: *separable
- filters: *n_filters
repeat: *rep
kernel: [15]
stride: [1]
dilation: [1]
dropout: *drop
residual: true
separable: *separable
- filters: &enc_feat_out 1500
repeat: 1
kernel: [1]
stride: [1]
dilation: [1]
dropout: 0.0
residual: false
separable: *separable
decoder:
_target_: nemo.collections.asr.modules.SpeakerDecoder
feat_in: *enc_feat_out
num_classes: 7205
pool_mode: 'xvector'
emb_sizes: 256
angular: True
loss:
scale: 30
margin: 0.2
optim:
name: sgd
lr: .006
# optimizer arguments
args:
name: auto
weight_decay: 0.001
momentum: 0.9
# scheduler setup
sched:
name: CosineAnnealing
iters_per_batch: 1 # computed at runtime
max_steps: null # computed at runtime or explicitly set here
# scheduler config override
args:
name: auto
warmup_steps: null
warmup_ratio: 0.1
min_lr: 0.0
last_epoch: -1
trainer:
gpus: 1 # number of gpus
max_epochs: 200
max_steps: null # computed at runtime if not set
num_nodes: 1
accelerator: ddp
accumulate_grad_batches: 1
amp_level: O0
deterministic: True
checkpoint_callback: False
logger: False
log_every_n_steps: 1 # Interval of logging.
val_check_interval: 1.0 # Set to 0.25 to check 4 times per epoch, or an int for number of iterations
exp_manager:
exp_dir: null
name: *name
create_tensorboard_logger: True
create_checkpoint_callback: True
@@ -0,0 +1,27 @@
name: &name "ClusterDiarizer"
num_workers: 4
sample_rate: &sample_rate 16000
diarizer:
oracle_num_speakers: ??? # oracle number of speakers or pass null if not known. [int or path to file containing uniq-id with num of speakers of that session]
max_num_speakers: 20 # max number of speakers for each recording. If oracle num speakers is passed this value is ignored.
out_dir: ???
paths2audio_files: ??? #file containing paths to audio files for inference
path2groundtruth_rttm_files: null #file containing paths to ground truth rttm files for score computation or null if no reference rttms are present
vad:
model_path: null #.nemo local model path or pretrained model name or none
window_length_in_sec: 0.15
shift_length_in_sec: 0.01
threshold: 0.7 # tune threshold on dev set. Check <NeMo_git_root>/scripts/voice_activity_detection/vad_tune_threshold.py
vad_decision_smoothing: True
smoothing_params:
method: "median"
overlap: 0.875
speaker_embeddings:
oracle_vad_manifest: null
model_path: ??? #.nemo local model path or pretrained model name (speakerverification_speakernet or speakerdiarization_speakernet)
window_length_in_sec: 1.5 # window length in sec for speaker embedding extraction
shift_length_in_sec: 0.75 # shift length in sec for speaker embedding extraction
@@ -0,0 +1,101 @@
# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This is a helper script to extract speaker embeddings based on manifest file
Usage:
python extract_speaker_embeddings.py --manifest=/path/to/manifest/file'
--model_path='/path/to/.nemo/file'(optional)
--embedding_dir='/path/to/embedding/directory'
Args:
--manifest: path to manifest file containing audio_file paths for which embeddings need to be extracted
--model_path(optional): path to .nemo speaker verification model file to extract embeddings, if not passed SpeakerNet-M model would
be downloaded from NGC and used to extract embeddings
--embeddings_dir(optional): path to directory where embeddings need to stored default:'./'
"""
from argparse import ArgumentParser
import pytorch_lightning as pl
import torch
from omegaconf import OmegaConf
from nemo.collections.asr.models.label_models import ExtractSpeakerEmbeddingsModel
from nemo.utils import logging
try:
from torch.cuda.amp import autocast
except ImportError:
from contextlib import contextmanager
@contextmanager
def autocast(enabled=None):
yield
def main():
parser = ArgumentParser()
parser.add_argument(
"--manifest", type=str, required=True, help="Path to manifest file",
)
parser.add_argument(
"--model_path",
type=str,
default='speakerverification_speakernet',
required=False,
help="path to .nemo speaker verification model file to extract embeddings, if not passed SpeakerNet-M model would be downloaded from NGC and used to extract embeddings",
)
parser.add_argument(
"--embedding_dir",
type=str,
default='./',
required=False,
help="path to directory where embeddings need to stored default:'./'",
)
args = parser.parse_args()
torch.set_grad_enabled(False)
if args.model_path.endswith('.nemo'):
logging.info(f"Using local speaker model from {args.model_path}")
speaker_model = ExtractSpeakerEmbeddingsModel.restore_from(restore_path=args.model_path)
else:
speaker_model = ExtractSpeakerEmbeddingsModel.from_pretrained(model_name="speakerverification_speakernet")
logging.info(f"using pretrained speaker verification model from NGC")
num_gpus = 1 if torch.cuda.is_available() else 0
if not num_gpus:
logging.warning("Running model on CPU, for faster performance it is adviced to use atleast one NVIDIA GPUs")
trainer = pl.Trainer(gpus=num_gpus, accelerator=None)
test_config = OmegaConf.create(
dict(
manifest_filepath=args.manifest,
sample_rate=16000,
labels=None,
batch_size=1,
shuffle=False,
time_length=8,
embedding_dir=args.embedding_dir,
)
)
speaker_model.setup_test_data(test_config)
trainer.test(speaker_model)
if __name__ == '__main__':
main()
@@ -0,0 +1,49 @@
# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from omegaconf import OmegaConf
from pytorch_lightning import seed_everything
from nemo.collections.asr.models import ClusteringDiarizer
from nemo.core.config import hydra_runner
from nemo.utils import logging
"""
This script demonstrates how to use run speaker diarization.
Usage:
python speaker_diarize.py \
diarizer.paths2audio_files=<either list of audio file paths or file containing paths to audio files> \
diarizer.path2groundtruth_rttm_files=<(Optional) either list of rttm file paths or file containing paths to rttm files> \
diarizer.vad.model_path="vad_telephony_marblenet" \
diarizer.vad.threshold=0.7 \
diarizer.speaker_embeddings.model_path="speakerverification_speakernet" \
diarizer.out_dir="demo"
Check out whole parameters in ./conf/speaker_diarization.yaml and their meanings.
For details, have a look at <NeMo_git_root>/tutorials/speaker_recognition/Speaker_Diarization_Inference.ipynb
"""
seed_everything(42)
@hydra_runner(config_path="conf", config_name="speaker_diarization.yaml")
def main(cfg):
logging.info(f'Hydra config: {OmegaConf.to_yaml(cfg)}')
sd_model = ClusteringDiarizer(cfg=cfg)
sd_model.diarize()
if __name__ == '__main__':
main()
@@ -0,0 +1,73 @@
# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import pytorch_lightning as pl
from omegaconf import ListConfig, OmegaConf
from pytorch_lightning import seed_everything
from nemo.collections.asr.models import EncDecSpeakerLabelModel
from nemo.core.config import hydra_runner
from nemo.utils import logging
from nemo.utils.exp_manager import exp_manager
"""
Basic run (on GPU for 10 epochs for 2 class training):
EXP_NAME=sample_run
python ./speaker_reco.py --config-path='conf' --config-name='SpeakerNet_recognition_3x2x512.yaml' \
trainer.max_epochs=10 \
model.train_ds.batch_size=64 model.validation_ds.batch_size=64 \
model.train_ds.manifest_filepath="<train_manifest>" model.validation_ds.manifest_filepath="<dev_manifest>" \
model.test_ds.manifest_filepath="<test_manifest>" \
trainer.gpus=1 \
model.decoder.params.num_classes=2 \
exp_manager.name=$EXP_NAME +exp_manager.use_datetime_version=False \
exp_manager.exp_dir='./speaker_exps'
See https://github.com/NVIDIA/NeMo/blob/main/tutorials/speaker_recognition/Speaker_Recognition_Verification.ipynb for notebook tutorial
Optional: Use tarred dataset to speech up data loading.
Prepare ONE manifest that contains all training data you would like to include. Validation should use non-tarred dataset.
Note that it's possible that tarred datasets impacts validation scores because it drop values in order to have same amount of files per tarfile;
Scores might be off since some data is missing.
Use the `convert_to_tarred_audio_dataset.py` script under <NEMO_ROOT>/speech_recognition/scripts in order to prepare tarred audio dataset.
For details, please see TarredAudioToClassificationLabelDataset in <NEMO_ROOT>/nemo/collections/asr/data/audio_to_label.py
"""
seed_everything(42)
@hydra_runner(config_path="conf", config_name="SpeakerNet_recognition_3x2x512.yaml")
def main(cfg):
logging.info(f'Hydra config: {OmegaConf.to_yaml(cfg)}')
trainer = pl.Trainer(**cfg.trainer)
log_dir = exp_manager(trainer, cfg.get("exp_manager", None))
speaker_model = EncDecSpeakerLabelModel(cfg=cfg.model, trainer=trainer)
trainer.fit(speaker_model)
if not trainer.fast_dev_run:
model_path = os.path.join(log_dir, '..', 'spkr.nemo')
speaker_model.save_to(model_path)
if hasattr(cfg.model, 'test_ds') and cfg.model.test_ds.manifest_filepath is not None:
gpu = 1 if cfg.trainer.gpus != 0 else 0
trainer = pl.Trainer(gpus=gpu)
if speaker_model.prepare_test(trainer):
trainer.test(speaker_model)
if __name__ == '__main__':
main()
@@ -0,0 +1,98 @@
# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This script demonstrates how to use pretrained .nemo speaker recognition model to finetune on your domain set
Usage:
python speaker_reco_finetune.py --pretrained_model='/path/to/.nemo_or_.ckpt/file' --finetune_config_file=/path/to/finetune/config/yaml/file'
in finetune config file make sure to change
- train manifest
- validation manifest
- decoder num classes
- learning rate
- num of epochs
for finetuning tips see: https://github.com/NVIDIA/NeMo/blob/main/tutorials/speaker_recognition/Speaker_Recognition_Verification.ipynb
"""
from argparse import ArgumentParser
import pytorch_lightning as pl
from omegaconf import OmegaConf
from nemo.collections.asr.models import EncDecSpeakerLabelModel
from nemo.utils import logging
from nemo.utils.exp_manager import exp_manager
def main():
parser = ArgumentParser()
parser.add_argument(
"--pretrained_model",
type=str,
default="speakerverification_speakernet",
required=False,
help="Pass your trained .nemo model",
)
parser.add_argument(
"--finetune_config_file",
type=str,
required=True,
help="path to speakernet config yaml file to load train, validation dataset and also for trainer parameters",
)
parser.add_argument(
"--freeze_encoder",
type=bool,
required=False,
default=True,
help="True if speakernet encoder paramteres needs to be frozen while finetuning",
)
args = parser.parse_args()
if args.pretrained_model.endswith('.nemo'):
logging.info(f"Using local speaker model from {args.pretrained_model}")
speaker_model = EncDecSpeakerLabelModel.restore_from(restore_path=args.pretrained_model)
elif args.pretrained_model.endswith('.ckpt'):
logging.info(f"Using local speaker model from checkpoint {args.pretrained_model}")
speaker_model = EncDecSpeakerLabelModel.load_from_checkpoint(checkpoint_path=args.pretrained_model)
else:
logging.info("Using pretrained speaker recognition model from NGC")
speaker_model = EncDecSpeakerLabelModel.from_pretrained(model_name=args.pretrained_model)
finetune_config = OmegaConf.load(args.finetune_config_file)
if 'test_ds' in finetune_config.model and finetune_config.model.test_ds is not None:
finetune_config.model.test_ds = None
logging.warning("Removing test ds")
speaker_model.setup_finetune_model(finetune_config.model)
finetune_trainer = pl.Trainer(**finetune_config.trainer)
speaker_model.set_trainer(finetune_trainer)
_ = exp_manager(finetune_trainer, finetune_config.get('exp_manager', None))
speaker_model.setup_optimization(finetune_config.optim)
if args.freeze_encoder:
for param in speaker_model.encoder.parameters():
param.requires_grad = False
finetune_trainer.fit(speaker_model)
if __name__ == '__main__':
main()
@@ -0,0 +1,139 @@
# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This script serves two goals:
(1) Demonstrate how to use NeMo Models outside of PytorchLightning
(2) Shows example of batch speaker recognition inference
Usage:
python speaker_reco_infer.py --spkr_model='/path/to/.nemo/file' --train_manifest=/path/to/train/manifest/file'
--test_manifest=/path/to/train/manifest/file' --batch_size=32
train_manifest file is used to map the labels from which model was trained so it is mandatory to
pass the train manifest file
for finetuning tips see: https://colab.research.google.com/github/NVIDIA/NeMo/blob/main/tutorials/speaker_recognition/Speaker_Recognition_Verification.ipynb
"""
import json
import os
import sys
from argparse import ArgumentParser
import numpy as np
import torch
from tqdm import tqdm
from nemo.collections.asr.models import EncDecSpeakerLabelModel
from nemo.utils import logging
try:
from torch.cuda.amp import autocast
except ImportError:
from contextlib import contextmanager
@contextmanager
def autocast(enabled=None):
yield
can_gpu = torch.cuda.is_available()
def main():
parser = ArgumentParser()
parser.add_argument(
"--spkr_model",
type=str,
default="speakerrecognition_speakernet",
required=True,
help="Pass your trained .nemo model",
)
parser.add_argument(
"--train_manifest", type=str, required=True, help="path to train manifest file to match labels"
)
parser.add_argument(
"--test_manifest", type=str, required=True, help="path to test manifest file to perform inference"
)
parser.add_argument("--batch_size", type=int, default=32)
args = parser.parse_args()
torch.set_grad_enabled(False)
if args.spkr_model.endswith('.nemo'):
logging.info(f"Using local speaker model from {args.spkr_model}")
speaker_model = EncDecSpeakerLabelModel.restore_from(restore_path=args.spkr_model)
else:
logging.error(f"Please pass a trained .nemo file")
sys.exit()
labels = []
with open(args.train_manifest, 'rb') as f:
lines = f.readlines()
for line in lines:
line = line.strip()
item = json.loads(line)
labels.append(item['label'])
labels_map = sorted(set(labels))
label2id, id2label = {}, {}
for label_id, label in enumerate(labels_map):
label2id[label] = label_id
id2label[label_id] = label
speaker_model.setup_test_data(
test_data_layer_params={
'sample_rate': 16000,
'manifest_filepath': args.test_manifest,
'labels': labels_map,
'batch_size': args.batch_size,
'trim_silence': False,
'shuffle': False,
}
)
if can_gpu:
speaker_model = speaker_model.cuda()
speaker_model.eval()
speaker_model.test_dataloader()
all_labels = []
all_logits = []
for test_batch in tqdm(speaker_model.test_dataloader()):
if can_gpu:
test_batch = [x.cuda() for x in test_batch]
with autocast():
audio_signal, audio_signal_len, labels, _ = test_batch
logits, _ = speaker_model.forward(input_signal=audio_signal, input_signal_length=audio_signal_len)
all_logits.extend(logits.cpu().numpy())
all_labels.extend(labels.cpu().numpy())
all_logits, true_labels = np.asarray(all_logits), np.asarray(all_labels)
infer_labels = all_logits.argmax(axis=1)
out_manifest = os.path.basename(args.test_manifest).split('.')[0] + '_infer.json'
out_manifest = os.path.join(os.path.dirname(args.test_manifest), out_manifest)
with open(args.test_manifest, 'rb') as f1, open(out_manifest, 'w') as f2:
lines = f1.readlines()
for idx, line in enumerate(lines):
line = line.strip()
item = json.loads(line)
item['infer'] = id2label[infer_labels[idx]]
json.dump(item, f2)
f2.write('\n')
logging.info("Inference labels have been written to {} manifest file".format(out_manifest))
if __name__ == '__main__':
main()
@@ -0,0 +1,110 @@
# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import os
import pickle as pkl
import sys
import numpy as np
from scipy.interpolate import interp1d
from scipy.optimize import brentq
from sklearn.metrics import roc_curve
from tqdm import tqdm
"""
This script faciliates to get EER % based on cosine-smilarity
for Voxceleb dataset.
Args:
trial_file str: path to voxceleb trial file
emb : path to pickle file of embeddings dictionary (generated from spkr_get_emb.py)
save_kaldi_emb: if required pass this argument to save kaldi embeddings for KALDI PLDA training later
Note: order of audio files in manifest file should match the embeddings
"""
def get_acc(trial_file='', emb='', save_kaldi_emb=False):
trial_score = open('trial_score.txt', 'w')
dirname = os.path.dirname(trial_file)
emb = pkl.load(open(emb, 'rb'))
trial_embs = []
keys = []
all_scores = []
all_keys = []
# for each trials in trial file
with open(trial_file, 'r') as f:
tmp_file = f.readlines()
for line in tqdm(tmp_file):
line = line.strip()
truth, x_speaker, y_speaker = line.split()
x_speaker = x_speaker.split('/')
x_speaker = '@'.join(x_speaker)
y_speaker = y_speaker.split('/')
y_speaker = '@'.join(y_speaker)
X = emb[x_speaker]
Y = emb[y_speaker]
if save_kaldi_emb and x_speaker not in keys:
keys.append(x_speaker)
trial_embs.extend([X])
if save_kaldi_emb and y_speaker not in keys:
keys.append(y_speaker)
trial_embs.extend([Y])
score = (X @ Y.T) / (((X @ X.T) * (Y @ Y.T)) ** 0.5)
score = (score + 1) / 2
all_scores.append(score)
trial_score.write(str(score) + "\t" + truth)
truth = int(truth)
all_keys.append(truth)
trial_score.write('\n')
trial_score.close()
if save_kaldi_emb:
np.save(dirname + '/all_embs_voxceleb.npy', np.asarray(trial_embs))
np.save(dirname + '/all_ids_voxceleb.npy', np.asarray(keys))
print("Saved KALDI PLDA related embeddings to {}".format(dirname))
return np.asarray(all_scores), np.asarray(all_keys)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--trial_file", help="path to voxceleb trial file", type=str, required=True)
parser.add_argument("--emb", help="path to numpy file of embeddings", type=str, required=True)
parser.add_argument(
"--save_kaldi_emb",
help=":save kaldi embeddings for KALDI PLDA training later",
required=False,
action='store_true',
)
args = parser.parse_args()
trial_file, emb, save_kaldi_emb = args.trial_file, args.emb, args.save_kaldi_emb
y_score, y = get_acc(trial_file=trial_file, emb=emb, save_kaldi_emb=save_kaldi_emb)
fpr, tpr, thresholds = roc_curve(y, y_score, pos_label=1)
eer = brentq(lambda x: 1.0 - x - interp1d(fpr, tpr)(x), 0.0, 1.0)
sys.stdout.write("{0:.2f}\n".format(eer * 100))