mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-07 02:21:23 +00:00
46 KiB
46 KiB
In [ ]:
"""
You can run either this notebook locally (if you have all the dependencies and a GPU) or on Google Colab.
Instructions for setting up Colab are as follows:
1. Open a new Python 3 notebook.
2. Import this notebook from GitHub (File -> Upload Notebook -> "GITHUB" tab -> copy/paste GitHub URL)
3. Connect to an instance with a GPU (Runtime -> Change runtime type -> select "GPU" for hardware accelerator)
4. Run this cell to set up dependencies.
"""
# If you're using Google Colab and not running locally, run this cell.
## Install dependencies
!pip install wget
!apt-get install sox libsndfile1 ffmpeg
!pip install unidecode
# ## Install NeMo
BRANCH = 'v1.0.2'
!python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[asr]
## Install TorchAudio
!pip install torchaudio>=0.6.0 -f https://download.pytorch.org/whl/torch_stable.html
## Grab the config we'll use in this example
!mkdir configsIn [ ]:
# Some utility imports
import os
from omegaconf import OmegaConfIn [ ]:
# This is where the Google Speech Commands directory will be placed.
# Change this if you don't want the data to be extracted in the current directory.
# Select the version of the dataset required as well (can be 1 or 2)
DATASET_VER = 1
data_dir = './google_dataset_v{0}/'.format(DATASET_VER)
if DATASET_VER == 1:
MODEL_CONFIG = "matchboxnet_3x1x64_v1.yaml"
else:
MODEL_CONFIG = "matchboxnet_3x1x64_v2.yaml"
if not os.path.exists(f"configs/{MODEL_CONFIG}"):
!wget -P configs/ "https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/examples/asr/conf/matchboxnet/{MODEL_CONFIG}"In [ ]:
if not os.path.exists("process_speech_commands_data.py"):
!wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/scripts/dataset_processing/process_speech_commands_data.pyIn [ ]:
!mkdir {data_dir}
!python process_speech_commands_data.py --data_root={data_dir} --data_version={DATASET_VER} --skip_duration --log
print("Dataset ready !")In [ ]:
dataset_path = 'google_speech_recognition_v{0}'.format(DATASET_VER)
dataset_basedir = os.path.join(data_dir, dataset_path)
train_dataset = os.path.join(dataset_basedir, 'train_manifest.json')
val_dataset = os.path.join(dataset_basedir, 'validation_manifest.json')
test_dataset = os.path.join(dataset_basedir, 'validation_manifest.json')In [ ]:
!head -n 5 {train_dataset}In [ ]:
# NeMo's "core" package
import nemo
# NeMo's ASR collection - this collections contains complete ASR models and
# building blocks (modules) for ASR
import nemo.collections.asr as nemo_asrIn [ ]:
# This line will print the entire config of the MatchboxNet model
config_path = f"configs/{MODEL_CONFIG}"
config = OmegaConf.load(config_path)
config = OmegaConf.to_container(config, resolve=True)
config = OmegaConf.create(config)
print(OmegaConf.to_yaml(config))In [ ]:
# Preserve some useful parameters
labels = config.model.labels
sample_rate = config.sample_rateIn [ ]:
print(OmegaConf.to_yaml(config.model.train_ds))In [ ]:
config.model.train_ds.manifest_filepath = train_dataset
config.model.validation_ds.manifest_filepath = val_dataset
config.model.test_ds.manifest_filepath = test_datasetIn [ ]:
import torch
import pytorch_lightning as plIn [ ]:
print("Trainer config - \n")
print(OmegaConf.to_yaml(config.trainer))In [ ]:
# Lets modify some trainer configs for this demo
# Checks if we have GPU available and uses it
cuda = 1 if torch.cuda.is_available() else 0
config.trainer.gpus = cuda
# Reduces maximum number of epochs to 5 for quick demonstration
config.trainer.max_epochs = 5
# Remove distributed training flags
config.trainer.accelerator = NoneIn [ ]:
trainer = pl.Trainer(**config.trainer)In [ ]:
from nemo.utils.exp_manager import exp_managerIn [ ]:
exp_dir = exp_manager(trainer, config.get("exp_manager", None))In [ ]:
# The exp_dir provides a path to the current experiment for easy access
exp_dir = str(exp_dir)
exp_dirIn [ ]:
asr_model = nemo_asr.models.EncDecClassificationModel(cfg=config.model, trainer=trainer)In [ ]:
try:
from google import colab
COLAB_ENV = True
except (ImportError, ModuleNotFoundError):
COLAB_ENV = FalseIn [ ]:
# Load the TensorBoard notebook extension
if COLAB_ENV:
%load_ext tensorboard
else:
print("To use tensorboard, please use this notebook in a Google Colab environment.")In [ ]:
if COLAB_ENV:
%tensorboard --logdir {exp_dir}
else:
print("To use tensorboard, please use this notebook in a Google Colab environment.")In [ ]:
trainer.fit(asr_model)In [ ]:
trainer.test(asr_model, ckpt_path=None)In [ ]:
!pip install librosaIn [ ]:
asr_model.setup_test_data(config.model.test_ds)
test_dl = asr_model._test_dlIn [ ]:
@torch.no_grad()
def extract_logits(model, dataloader):
logits_buffer = []
label_buffer = []
# Follow the above definition of the test_step
for batch in dataloader:
audio_signal, audio_signal_len, labels, labels_len = batch
logits = model(input_signal=audio_signal, input_signal_length=audio_signal_len)
logits_buffer.append(logits)
label_buffer.append(labels)
print(".", end='')
print()
print("Finished extracting logits !")
logits = torch.cat(logits_buffer, 0)
labels = torch.cat(label_buffer, 0)
return logits, labels
In [ ]:
cpu_model = asr_model.cpu()
cpu_model.eval()
logits, labels = extract_logits(cpu_model, test_dl)
print("Logits:", logits.shape, "Labels :", labels.shape)In [ ]:
# Compute accuracy - `_accuracy` is a PyTorch Lightning Metric !
acc = cpu_model._accuracy(logits=logits, labels=labels)
print("Accuracy : ", float(acc[0]*100))In [ ]:
import librosa
import json
import IPython.display as ipdIn [ ]:
# First let's create a utility class to remap the integer class labels to actual string label
class ReverseMapLabel:
def __init__(self, data_loader):
self.label2id = dict(data_loader.dataset.label2id)
self.id2label = dict(data_loader.dataset.id2label)
def __call__(self, pred_idx, label_idx):
return self.id2label[pred_idx], self.id2label[label_idx]In [ ]:
# Next, let's get the indices of all the incorrectly labeled samples
sample_idx = 0
incorrect_preds = []
rev_map = ReverseMapLabel(test_dl)
# Remember, evaluated_tensor = (loss, logits, labels)
probs = torch.softmax(logits, dim=-1)
probas, preds = torch.max(probs, dim=-1)
total_count = cpu_model._accuracy.total_counts_k[0]
incorrect_ids = (preds != labels).nonzero()
for idx in incorrect_ids:
proba = float(probas[idx][0])
pred = int(preds[idx][0])
label = int(labels[idx][0])
idx = int(idx[0]) + sample_idx
incorrect_preds.append((idx, *rev_map(pred, label), proba))
print(f"Num test samples : {total_count.item()}")
print(f"Num errors : {len(incorrect_preds)}")
# First lets sort by confidence of prediction
incorrect_preds = sorted(incorrect_preds, key=lambda x: x[-1], reverse=False)In [ ]:
for incorrect_sample in incorrect_preds[:20]:
print(str(incorrect_sample))In [ ]:
# Filter out how many such samples exist
low_confidence_threshold = 0.25
count_low_confidence = len(list(filter(lambda x: x[-1] <= low_confidence_threshold, incorrect_preds)))
print(f"Number of low confidence predictions : {count_low_confidence}")In [ ]:
# First let's create a helper function to parse the manifest files
def parse_manifest(manifest):
data = []
for line in manifest:
line = json.loads(line)
data.append(line)
return dataIn [ ]:
# Next, let's create a helper function to actually listen to certain samples
def listen_to_file(sample_id, pred=None, label=None, proba=None):
# Load the audio waveform using librosa
filepath = test_samples[sample_id]['audio_filepath']
audio, sample_rate = librosa.load(filepath)
if pred is not None and label is not None and proba is not None:
print(f"Sample : {sample_id} Prediction : {pred} Label : {label} Confidence = {proba: 0.4f}")
else:
print(f"Sample : {sample_id}")
return ipd.Audio(audio, rate=sample_rate)In [ ]:
# Now let's load the test manifest into memory
test_samples = []
with open(test_dataset, 'r') as test_f:
test_samples = test_f.readlines()
test_samples = parse_manifest(test_samples)In [ ]:
# Finally, let's listen to all the audio samples where the model made a mistake
# Note: This list of incorrect samples may be quite large, so you may choose to subsample `incorrect_preds`
count = min(count_low_confidence, 20) # replace this line with just `count_low_confidence` to listen to all samples with low confidence
for sample_id, pred, label, proba in incorrect_preds[:count]:
ipd.display(listen_to_file(sample_id, pred=pred, label=label, proba=proba))In [ ]:
import jsonIn [ ]:
def extract_subset_from_manifest(name: str, manifest_path: str, labels: list):
manifest_dir = os.path.split(manifest_path)[0]
labels = set(labels)
manifest_values = []
print(f"Parsing manifest: {manifest_path}")
with open(manifest_path, 'r') as f:
for line in f:
val = json.loads(line)
if val['command'] in labels:
manifest_values.append(val)
print(f"Number of files extracted from dataset: {len(manifest_values)}")
outpath = os.path.join(manifest_dir, name)
with open(outpath, 'w') as f:
for val in manifest_values:
json.dump(val, f)
f.write("\n")
f.flush()
print("Manifest subset written to path :", outpath)
print()
return outpathIn [ ]:
labels = ["yes", "no"]
train_subdataset = extract_subset_from_manifest("train_subset.json", train_dataset, labels)
val_subdataset = extract_subset_from_manifest("val_subset.json", val_dataset, labels)
test_subdataset = extract_subset_from_manifest("test_subset.json", test_dataset, labels)In [ ]:
import globIn [ ]:
print(exp_dir)In [ ]:
# Let's list all the checkpoints we have
checkpoint_dir = os.path.join(exp_dir, 'checkpoints')
checkpoint_paths = list(glob.glob(os.path.join(checkpoint_dir, "*.ckpt")))
checkpoint_pathsIn [ ]:
# We want the checkpoint saved after the final step of training
final_checkpoint = list(filter(lambda x: "-last.ckpt" in x, checkpoint_paths))[0]
print(final_checkpoint)In [ ]:
restored_model = nemo_asr.models.EncDecClassificationModel.load_from_checkpoint(final_checkpoint)In [ ]:
restored_model.change_labels(labels)In [ ]:
import copyIn [ ]:
train_subdataset_cfg = copy.deepcopy(restored_model.cfg.train_ds)
val_subdataset_cfg = copy.deepcopy(restored_model.cfg.validation_ds)
test_subdataset_cfg = copy.deepcopy(restored_model.cfg.test_ds)In [ ]:
# Set the paths to the subset of the dataset
train_subdataset_cfg.manifest_filepath = train_subdataset
val_subdataset_cfg.manifest_filepath = val_subdataset
test_subdataset_cfg.manifest_filepath = test_subdatasetIn [ ]:
# Setup the data loader for the restored model
restored_model.setup_training_data(train_subdataset_cfg)
restored_model.setup_multiple_validation_data(val_subdataset_cfg)
restored_model.setup_multiple_test_data(test_subdataset_cfg)In [ ]:
# Check data loaders are correct
print("Train dataset labels :", restored_model._train_dl.dataset.labels)
print("Val dataset labels :", restored_model._validation_dl.dataset.labels)
print("Test dataset labels :", restored_model._test_dl.dataset.labels)In [ ]:
# Setup the new trainer object
# Let's modify some trainer configs for this demo
# Checks if we have GPU available and uses it
cuda = 1 if torch.cuda.is_available() else 0
trainer_config = OmegaConf.create(dict(
gpus=cuda,
max_epochs=5,
max_steps=None, # computed at runtime if not set
num_nodes=1,
accumulate_grad_batches=1,
checkpoint_callback=False, # Provided by exp_manager
logger=False, # Provided by exp_manager
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
))
print(OmegaConf.to_yaml(trainer_config))In [ ]:
trainer_finetune = pl.Trainer(**trainer_config)In [ ]:
restored_model.set_trainer(trainer_finetune)In [ ]:
exp_dir_finetune = exp_manager(trainer_finetune, config.get("exp_manager", None))In [ ]:
exp_dir_finetune = str(exp_dir_finetune)
exp_dir_finetuneIn [ ]:
optim_sched_cfg = copy.deepcopy(restored_model.cfg.optim)
# Struct mode prevents us from popping off elements from the config, so let's disable it
OmegaConf.set_struct(optim_sched_cfg, False)In [ ]:
# Lets change the maximum learning rate to previous minimum learning rate
optim_sched_cfg.lr = 0.001
# Lets change the scheduler
optim_sched_cfg.sched.name = "CosineAnnealing"
# "power" isnt applicable to CosineAnnealing so let's remove it
optim_sched_cfg.sched.pop('power')
# "hold_ratio" isnt applicable to CosineAnnealing, so let's remove it
optim_sched_cfg.sched.pop('hold_ratio')
# Set "min_lr" to lower value
optim_sched_cfg.sched.min_lr = 1e-4
print(OmegaConf.to_yaml(optim_sched_cfg))In [ ]:
# Now lets update the optimizer settings
restored_model.setup_optimization(optim_sched_cfg)In [ ]:
# We can also just directly replace the config inplace if we choose to
restored_model.cfg.optim = optim_sched_cfgWarning:
Output truncated. This notebook contains too many cells to display efficiently.
