mirror of
https://github.com/storytold/storyteller-ml.git
synced 2026-10-17 00:23:14 +00:00
Introduce code from Rust monorepo. This is a mess
This commit is contained in:
@@ -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))
|
||||
Reference in New Issue
Block a user