Vansh Chugh commited on
Commit ·
bdb2428
1
Parent(s): c79d26b
cleanup (~25k lines) + torchlibrosa in requirements.txt
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- inference/validator_FxProcessor.py +0 -436
- requirements.txt +1 -0
- utils/MSS_loss.py +0 -260
- utils/collators.py +0 -262
- utils/common_audioeffects.py +0 -1729
- utils/dnnlib/__init__.py +0 -8
- utils/dnnlib/util.py +0 -491
- utils/evaluation/KAD_evaluate.py +0 -307
- utils/evaluation/dist_metrics.py +0 -971
- utils/evaluation/dist_metrics_multitrack.py +0 -619
- utils/evaluation/pairwise_metrics.py +0 -958
- utils/feature_extractors/audio_features.py +0 -76
- utils/feature_extractors/load_features.py +0 -163
- utils/feature_extractors/networks/pytorch_utils.py +0 -256
- utils/fx_normalization/fxnorm.py +0 -154
- utils/laion_clap/clap_module/__init__.py +0 -2
- utils/laion_clap/clap_module/bert.py +0 -32
- utils/laion_clap/clap_module/bpe_simple_vocab_16e6.txt.gz +0 -3
- utils/laion_clap/clap_module/linear_probe.py +0 -63
- utils/laion_clap/clap_module/loss.py +0 -307
- utils/laion_clap/clap_module/tokenizer.py +0 -180
- utils/laion_clap/clap_module/utils.py +0 -296
- utils/laion_clap/clap_module/version.py +0 -1
- utils/laion_clap/evaluate/__init__.py +0 -0
- utils/laion_clap/evaluate/eval_dcase.py +0 -150
- utils/laion_clap/evaluate/eval_linear_probe.py +0 -515
- utils/laion_clap/evaluate/eval_retrieval.py +0 -192
- utils/laion_clap/evaluate/eval_retrieval_main.py +0 -257
- utils/laion_clap/evaluate/eval_zeroshot_classification.py +0 -261
- utils/laion_clap/training/data.py +3 -755
- utils/laion_clap/training/distributed.py +0 -139
- utils/laion_clap/training/imagenet_zeroshot_data.py +0 -254
- utils/laion_clap/training/infer_demo.py +0 -92
- utils/laion_clap/training/logger.py +0 -26
- utils/laion_clap/training/lp_main.py +0 -643
- utils/laion_clap/training/lp_train.py +0 -292
- utils/laion_clap/training/main.py +0 -597
- utils/laion_clap/training/params.py +0 -567
- utils/laion_clap/training/scheduler.py +0 -23
- utils/laion_clap/training/train.py +0 -781
- utils/laion_clap/training/zero_shot.py +0 -90
- utils/laion_clap/unit_test.py +0 -75
- utils/log.py +0 -976
- utils/st_ito/__init__.py +0 -0
- utils/st_ito/callbacks.py +0 -164
- utils/st_ito/config.yaml +0 -151
- utils/st_ito/dataset/dataset_param.py +0 -595
- utils/st_ito/dataset/dataset_sim.py +0 -320
- utils/st_ito/dataset/dataset_style.py +0 -351
- utils/st_ito/dataset/utils.py +0 -25
inference/validator_FxProcessor.py
DELETED
|
@@ -1,436 +0,0 @@
|
|
| 1 |
-
# Copyright (c) 2025 Sony Research
|
| 2 |
-
# Licensed under CC BY-NC-SA 4.0
|
| 3 |
-
# See LICENSE file for details
|
| 4 |
-
|
| 5 |
-
from datetime import date
|
| 6 |
-
import math
|
| 7 |
-
import io
|
| 8 |
-
import matplotlib.pyplot as plt
|
| 9 |
-
from functools import partial
|
| 10 |
-
import re
|
| 11 |
-
import torch
|
| 12 |
-
import os
|
| 13 |
-
import wandb
|
| 14 |
-
import copy
|
| 15 |
-
from glob import glob
|
| 16 |
-
from tqdm import tqdm
|
| 17 |
-
import omegaconf
|
| 18 |
-
import hydra
|
| 19 |
-
import utils.log as utils_logging
|
| 20 |
-
import utils.training_utils as tr_utils
|
| 21 |
-
|
| 22 |
-
import soundfile as sf
|
| 23 |
-
import numpy as np
|
| 24 |
-
import torchaudio
|
| 25 |
-
|
| 26 |
-
from utils.data_utils import apply_RMS_normalization
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
class ValidatorFxProcessor:
|
| 30 |
-
def __init__(
|
| 31 |
-
self,
|
| 32 |
-
args,
|
| 33 |
-
network,
|
| 34 |
-
test_set_dict=None,
|
| 35 |
-
device=None,
|
| 36 |
-
in_training=False,
|
| 37 |
-
):
|
| 38 |
-
self.args = args
|
| 39 |
-
self.network = network
|
| 40 |
-
self.device = device
|
| 41 |
-
self.test_set_dict = test_set_dict
|
| 42 |
-
|
| 43 |
-
self.use_wandb = False # hardcoded for now
|
| 44 |
-
self.in_training = in_training
|
| 45 |
-
|
| 46 |
-
if in_training:
|
| 47 |
-
self.use_wandb = True
|
| 48 |
-
# Will inherit wandb_run from Trainer
|
| 49 |
-
else: # If we use the tester in training, we will log in WandB in the Trainer() class, no need to create all these paths
|
| 50 |
-
torch.backends.cudnn.benchmark = True
|
| 51 |
-
if self.device is None:
|
| 52 |
-
self.device = torch.device(
|
| 53 |
-
"cuda" if torch.cuda.is_available() else "cpu"
|
| 54 |
-
)
|
| 55 |
-
|
| 56 |
-
self.setup_wandb()
|
| 57 |
-
|
| 58 |
-
if self.args.tester.compute_metrics:
|
| 59 |
-
self.metrics_dict = self.prepare_metrics(self.args.tester.metrics)
|
| 60 |
-
else:
|
| 61 |
-
self.metrics_dict = {}
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
self.RMS_norm = (
|
| 65 |
-
self.args.exp.RMS_norm
|
| 66 |
-
) # Use fixed RMS for evaluation, hardcoded for now
|
| 67 |
-
|
| 68 |
-
if self.args.exp.style_encoder_type == "FxEncoder++_DynamicFeatures":
|
| 69 |
-
|
| 70 |
-
Fxencoder_kwargs = self.args.exp.fx_encoder_plusplus_args
|
| 71 |
-
|
| 72 |
-
from utils.feature_extractors.load_features import (
|
| 73 |
-
load_fx_encoder_plusplus_2048,
|
| 74 |
-
)
|
| 75 |
-
|
| 76 |
-
feat_extractor = load_fx_encoder_plusplus_2048(
|
| 77 |
-
Fxencoder_kwargs, self.device
|
| 78 |
-
)
|
| 79 |
-
|
| 80 |
-
from utils.feature_extractors.AF_features_embedding import (
|
| 81 |
-
AF_fourier_embedding,
|
| 82 |
-
)
|
| 83 |
-
|
| 84 |
-
AFembedding = AF_fourier_embedding(device=self.device)
|
| 85 |
-
|
| 86 |
-
def fxencode_fn(x):
|
| 87 |
-
"""
|
| 88 |
-
x: tensor of shape [B, C, L] where B is the batch size, C is the number of channels and L is the length of the audio
|
| 89 |
-
"""
|
| 90 |
-
z = feat_extractor(x)
|
| 91 |
-
z = torch.nn.functional.normalize(
|
| 92 |
-
z, dim=-1, p=2
|
| 93 |
-
) # normalize to unit variance
|
| 94 |
-
z = z * math.sqrt(z.shape[-1]) # rescale to keep the same scale
|
| 95 |
-
|
| 96 |
-
z_af, _ = AFembedding.encode(x)
|
| 97 |
-
# embedding is l2 normalized, normalize to unit variance
|
| 98 |
-
z_af = z_af * math.sqrt(
|
| 99 |
-
z_af.shape[-1]
|
| 100 |
-
) # rescale to keep the same scale
|
| 101 |
-
|
| 102 |
-
# concatenate z and z_af (rescaling with sqrt(dim) to keep the same scale)
|
| 103 |
-
z_all = torch.cat([z, z_af], dim=-1)
|
| 104 |
-
|
| 105 |
-
norm_z = z_all / math.sqrt(
|
| 106 |
-
z_all.shape[-1]
|
| 107 |
-
) # normalize by dividing by sqrt(dim) to keep the same scale
|
| 108 |
-
|
| 109 |
-
return norm_z
|
| 110 |
-
|
| 111 |
-
self.style_encode = fxencode_fn
|
| 112 |
-
else:
|
| 113 |
-
raise NotImplementedError(
|
| 114 |
-
"Only FxEncoder++_DynamicFeatures is implemented for now"
|
| 115 |
-
)
|
| 116 |
-
|
| 117 |
-
if self.args.exp.apply_fxnorm:
|
| 118 |
-
self.fx_normalizer = hydra.utils.instantiate(self.args.exp.fxnorm)
|
| 119 |
-
|
| 120 |
-
def setup_wandb(self):
|
| 121 |
-
"""
|
| 122 |
-
Configure wandb, open a new run and log the configuration.
|
| 123 |
-
"""
|
| 124 |
-
config = omegaconf.OmegaConf.to_container(
|
| 125 |
-
self.args, resolve=True, throw_on_missing=True
|
| 126 |
-
)
|
| 127 |
-
self.wandb_run = wandb.init(
|
| 128 |
-
project="testing" + self.args.tester.wandb.project,
|
| 129 |
-
entity=self.args.tester.wandb.entity,
|
| 130 |
-
config=config,
|
| 131 |
-
tags=self.args.tester.wandb.tags,
|
| 132 |
-
)
|
| 133 |
-
# wandb.watch(self.network,
|
| 134 |
-
# log_freq=self.args.logging.heavy_log_interval)
|
| 135 |
-
|
| 136 |
-
self.wandb_run.name = self.args.tester.wandb.run_name
|
| 137 |
-
self.use_wandb = True
|
| 138 |
-
|
| 139 |
-
def setup_wandb_run(self, run):
|
| 140 |
-
# get the wandb run object from outside (in trainer.py or somewhere else)
|
| 141 |
-
self.wandb_run = run
|
| 142 |
-
self.use_wandb = True
|
| 143 |
-
|
| 144 |
-
def load_latest_checkpoint(self):
|
| 145 |
-
# load the latest checkpoint from self.args.model_dir
|
| 146 |
-
try:
|
| 147 |
-
# find latest checkpoint_id
|
| 148 |
-
save_basename = f"{self.args.exp.exp_name}-*.pt"
|
| 149 |
-
save_name = f"{self.args.model_dir}/{save_basename}"
|
| 150 |
-
list_weights = glob(save_name)
|
| 151 |
-
id_regex = re.compile(f"{self.args.exp.exp_name}-(\d*)\.pt")
|
| 152 |
-
list_ids = [
|
| 153 |
-
int(id_regex.search(weight_path).groups()[0])
|
| 154 |
-
for weight_path in list_weights
|
| 155 |
-
]
|
| 156 |
-
checkpoint_id = max(list_ids)
|
| 157 |
-
|
| 158 |
-
state_dict = torch.load(
|
| 159 |
-
f"{self.args.model_dir}/{self.args.exp.exp_name}-{checkpoint_id}.pt",
|
| 160 |
-
map_location=self.device,
|
| 161 |
-
)
|
| 162 |
-
try:
|
| 163 |
-
self.network.load_state_dict(state_dict["network"])
|
| 164 |
-
except Exception as e:
|
| 165 |
-
print(e)
|
| 166 |
-
print("Failed to load in strict mode, trying again without strict mode")
|
| 167 |
-
self.network.load_state_dict(state_dict["model"], strict=False)
|
| 168 |
-
|
| 169 |
-
print(f"Loaded checkpoint {checkpoint_id}")
|
| 170 |
-
return True
|
| 171 |
-
except (FileNotFoundError, ValueError):
|
| 172 |
-
raise ValueError("No checkpoint found")
|
| 173 |
-
|
| 174 |
-
def load_checkpoint(self, path):
|
| 175 |
-
state_dict = torch.load(path, map_location=self.device, weights_only=False)
|
| 176 |
-
print("state_dict keys:", state_dict.keys())
|
| 177 |
-
try:
|
| 178 |
-
self.it = state_dict["it"]
|
| 179 |
-
except:
|
| 180 |
-
self.it = 0
|
| 181 |
-
|
| 182 |
-
print(f"loading checkpoint {self.it}")
|
| 183 |
-
return tr_utils.load_state_dict(state_dict, network=self.network)
|
| 184 |
-
|
| 185 |
-
def log_figure(self, fig, name: str, step=None):
|
| 186 |
-
# Save the figure to a buffer
|
| 187 |
-
|
| 188 |
-
self.wandb_run.log(
|
| 189 |
-
{name: wandb.Image(fig)}, step=step if step is not None else self.it
|
| 190 |
-
)
|
| 191 |
-
|
| 192 |
-
def log_metric(self, value, name: str, step=None):
|
| 193 |
-
# print("logging metric it:", self.it, "name:", name)
|
| 194 |
-
self.wandb_run.log({name: value}, step=step if step is not None else self.it)
|
| 195 |
-
|
| 196 |
-
def log_audio(self, pred, name: str, it=None):
|
| 197 |
-
if it is None:
|
| 198 |
-
it = self.it
|
| 199 |
-
if self.use_wandb:
|
| 200 |
-
pred = pred.permute(1, 0)
|
| 201 |
-
self.wandb_run.log(
|
| 202 |
-
{
|
| 203 |
-
name: wandb.Audio(
|
| 204 |
-
pred.detach().cpu().numpy(),
|
| 205 |
-
sample_rate=self.args.exp.sample_rate,
|
| 206 |
-
)
|
| 207 |
-
},
|
| 208 |
-
step=it,
|
| 209 |
-
)
|
| 210 |
-
|
| 211 |
-
# ------------- UNCONDITIONAL SAMPLING ---------------#
|
| 212 |
-
|
| 213 |
-
##############################
|
| 214 |
-
### UNCONDITIONAL SAMPLING ###
|
| 215 |
-
##############################
|
| 216 |
-
|
| 217 |
-
def prepare_metrics(self, metrics):
|
| 218 |
-
metrics_dict = {}
|
| 219 |
-
for metric in metrics:
|
| 220 |
-
print(f"Preparing metric {metric}")
|
| 221 |
-
if "pairwise" in metric:
|
| 222 |
-
from utils.evaluation.pairwise_metrics import metric_factory
|
| 223 |
-
|
| 224 |
-
metrics_dict[metric] = metric_factory(
|
| 225 |
-
metric, self.args.exp.sample_rate, **self.args.tester
|
| 226 |
-
)
|
| 227 |
-
|
| 228 |
-
return metrics_dict
|
| 229 |
-
|
| 230 |
-
def test_paired(self, mode, exp_description=""):
|
| 231 |
-
|
| 232 |
-
# self.it = 0
|
| 233 |
-
for k, test_set in self.test_set_dict.items():
|
| 234 |
-
|
| 235 |
-
print(f"Testing on {k} set", k)
|
| 236 |
-
|
| 237 |
-
assert len(test_set) != 0, "No samples found in test set"
|
| 238 |
-
|
| 239 |
-
dict_y = {}
|
| 240 |
-
dict_x = {}
|
| 241 |
-
dict_y_hat = {}
|
| 242 |
-
|
| 243 |
-
i = 0
|
| 244 |
-
|
| 245 |
-
for x, y in tqdm(test_set):
|
| 246 |
-
|
| 247 |
-
B, C, T = y.shape
|
| 248 |
-
|
| 249 |
-
x = x.to(self.device).float()
|
| 250 |
-
if x.shape[-1] > self.args.exp.audio_len:
|
| 251 |
-
x = x[:, :, : self.args.exp.audio_len]
|
| 252 |
-
elif x.shape[-1] < self.args.exp.audio_len:
|
| 253 |
-
raise ValueError(
|
| 254 |
-
f"Sample length {x.shape[-1]} is less than expected {self.args.exp.audio_len}"
|
| 255 |
-
)
|
| 256 |
-
|
| 257 |
-
if mode == "paired":
|
| 258 |
-
y = y.to(self.device).float()
|
| 259 |
-
|
| 260 |
-
if y.shape[-1] > self.args.exp.audio_len:
|
| 261 |
-
y = y[:, :, : self.args.exp.audio_len]
|
| 262 |
-
elif y.shape[-1] < self.args.exp.audio_len:
|
| 263 |
-
raise ValueError(
|
| 264 |
-
f"Sample length {y.shape[-1]} is less than expected {self.args.exp.audio_len}"
|
| 265 |
-
)
|
| 266 |
-
|
| 267 |
-
if x.shape[1] == 2:
|
| 268 |
-
x = x.mean(
|
| 269 |
-
dim=1, keepdim=True
|
| 270 |
-
) # expand to [B*N, 1, L] to keep the shape consistent
|
| 271 |
-
|
| 272 |
-
# RMS normalization of x and y
|
| 273 |
-
|
| 274 |
-
if self.args.exp.apply_fxnorm:
|
| 275 |
-
x = self.fx_normalizer(
|
| 276 |
-
x,
|
| 277 |
-
RMS=self.args.exp.RMS_norm,
|
| 278 |
-
use_gate=self.args.exp.use_gated_RMSnorm,
|
| 279 |
-
)
|
| 280 |
-
else:
|
| 281 |
-
x = apply_RMS_normalization(
|
| 282 |
-
x,
|
| 283 |
-
self.args.exp.RMS_norm,
|
| 284 |
-
use_gate=self.args.exp.use_gated_RMSnorm,
|
| 285 |
-
)
|
| 286 |
-
|
| 287 |
-
if "baseline" in mode:
|
| 288 |
-
if mode == "baseline_dry":
|
| 289 |
-
preds = x # Just return the dry vocals as baseline
|
| 290 |
-
elif mode == "baseline_autoencoder":
|
| 291 |
-
preds = self.autoencoder_reconstruction(
|
| 292 |
-
y
|
| 293 |
-
) # Just return the dry vocals as baseline
|
| 294 |
-
elif mode == "baseline_random":
|
| 295 |
-
raise NotImplementedError(
|
| 296 |
-
"Baseline random sampling not implemented yet"
|
| 297 |
-
)
|
| 298 |
-
pass
|
| 299 |
-
else:
|
| 300 |
-
with torch.no_grad():
|
| 301 |
-
z = self.style_encode(y)
|
| 302 |
-
try:
|
| 303 |
-
preds = self.network(
|
| 304 |
-
x, z
|
| 305 |
-
) # Get the predictions from the network
|
| 306 |
-
except Exception as e:
|
| 307 |
-
print(f"Error during inference: {e}")
|
| 308 |
-
continue
|
| 309 |
-
print(
|
| 310 |
-
"y_pred",
|
| 311 |
-
preds.shape,
|
| 312 |
-
preds.std(),
|
| 313 |
-
preds.mean(),
|
| 314 |
-
preds.min(),
|
| 315 |
-
preds.max(),
|
| 316 |
-
)
|
| 317 |
-
|
| 318 |
-
is_nan = torch.isnan(preds).any()
|
| 319 |
-
if is_nan:
|
| 320 |
-
num_nan = torch.sum(torch.isnan(x)).item()
|
| 321 |
-
print(
|
| 322 |
-
f"Number of NaN values in sample_x: {num_nan} of {x.numel()}"
|
| 323 |
-
)
|
| 324 |
-
|
| 325 |
-
y = apply_RMS_normalization(
|
| 326 |
-
y, self.args.exp.RMS_norm, use_gate=self.args.exp.use_gated_RMSnorm
|
| 327 |
-
)
|
| 328 |
-
|
| 329 |
-
for b in range(B):
|
| 330 |
-
if self.use_wandb:
|
| 331 |
-
if (
|
| 332 |
-
i < self.args.tester.wandb.num_examples_to_log
|
| 333 |
-
): # Log only first 10 samples
|
| 334 |
-
self.log_audio(
|
| 335 |
-
preds[b], f"pred_wet_{k}_{mode}_{i}", it=self.it
|
| 336 |
-
) # Just log first sample
|
| 337 |
-
self.log_audio(
|
| 338 |
-
y[b], f"original_wet_{k}_{mode}_{i}", it=self.it
|
| 339 |
-
) # Just log first sample
|
| 340 |
-
self.log_audio(
|
| 341 |
-
x[b], f"original_dry_{k}_{mode}_{i}", it=self.it
|
| 342 |
-
) # Just log first sample
|
| 343 |
-
|
| 344 |
-
dict_y[i] = y[b].detach().cpu().numpy()
|
| 345 |
-
dict_x[i] = x[b].detach().cpu().numpy()
|
| 346 |
-
dict_y_hat[i] = preds[b].detach().cpu().numpy()
|
| 347 |
-
|
| 348 |
-
i += 1
|
| 349 |
-
|
| 350 |
-
if self.args.tester.compute_metrics:
|
| 351 |
-
for metric in self.metrics_dict.keys():
|
| 352 |
-
try:
|
| 353 |
-
print(f"Computing metric {metric}")
|
| 354 |
-
result, result_dict = self.metrics_dict[metric].compute(
|
| 355 |
-
dict_y, dict_y_hat, dict_x
|
| 356 |
-
)
|
| 357 |
-
|
| 358 |
-
if self.use_wandb:
|
| 359 |
-
if result is not None:
|
| 360 |
-
self.log_metric(
|
| 361 |
-
result, metric + "_" + k + "_" + mode, step=self.it
|
| 362 |
-
)
|
| 363 |
-
|
| 364 |
-
for key, value in result_dict.items():
|
| 365 |
-
if "figure" in key:
|
| 366 |
-
# log figure as an image
|
| 367 |
-
self.log_figure(
|
| 368 |
-
value, key + "_" + k + "_" + mode, step=self.it
|
| 369 |
-
)
|
| 370 |
-
else:
|
| 371 |
-
self.log_metric(
|
| 372 |
-
value, key + "_" + k + "_" + mode, step=self.it
|
| 373 |
-
)
|
| 374 |
-
|
| 375 |
-
except Exception as e:
|
| 376 |
-
print(f"Error computing metric {metric}: {e}")
|
| 377 |
-
continue
|
| 378 |
-
|
| 379 |
-
def prepare_directories(self, mode, unconditional=False, string=None):
|
| 380 |
-
|
| 381 |
-
today = date.today()
|
| 382 |
-
self.paths = {}
|
| 383 |
-
if (
|
| 384 |
-
"overriden_name" in self.args.tester.keys()
|
| 385 |
-
and self.args.tester.overriden_name is not None
|
| 386 |
-
):
|
| 387 |
-
self.path_sampling = os.path.join(
|
| 388 |
-
self.args.model_dir, self.args.tester.overriden_name
|
| 389 |
-
)
|
| 390 |
-
else:
|
| 391 |
-
self.path_sampling = os.path.join(
|
| 392 |
-
self.args.model_dir, "test" + today.strftime("%d_%m_%Y")
|
| 393 |
-
)
|
| 394 |
-
if not os.path.exists(self.path_sampling):
|
| 395 |
-
os.makedirs(self.path_sampling)
|
| 396 |
-
|
| 397 |
-
self.paths[mode] = os.path.join(
|
| 398 |
-
self.path_sampling, mode, self.args.exp.exp_name
|
| 399 |
-
)
|
| 400 |
-
|
| 401 |
-
if not os.path.exists(self.paths[mode]):
|
| 402 |
-
os.makedirs(self.paths[mode])
|
| 403 |
-
if string is None:
|
| 404 |
-
string = ""
|
| 405 |
-
|
| 406 |
-
self.paths[mode + "wet_original"] = os.path.join(
|
| 407 |
-
self.paths[mode], string + "wet_original"
|
| 408 |
-
)
|
| 409 |
-
if not os.path.exists(self.paths[mode + "wet_original"]):
|
| 410 |
-
os.makedirs(self.paths[mode + "wet_original"])
|
| 411 |
-
self.paths[mode + "dry"] = os.path.join(self.paths[mode], string + "dry")
|
| 412 |
-
if not os.path.exists(self.paths[mode + "dry"]):
|
| 413 |
-
os.makedirs(self.paths[mode + "dry"])
|
| 414 |
-
self.paths[mode + "wet_estimate"] = os.path.join(
|
| 415 |
-
self.paths[mode], string + "wet_estimate"
|
| 416 |
-
)
|
| 417 |
-
if not os.path.exists(self.paths[mode + "wet_estimate"]):
|
| 418 |
-
os.makedirs(self.paths[mode + "wet_estimate"])
|
| 419 |
-
|
| 420 |
-
def save_experiment_args(self, mode):
|
| 421 |
-
with open(
|
| 422 |
-
os.path.join(self.paths[mode], ".argv"), "w"
|
| 423 |
-
) as f: # Keep track of the arguments we used for this experiment
|
| 424 |
-
omegaconf.OmegaConf.save(config=self.args, f=f.name)
|
| 425 |
-
|
| 426 |
-
def do_test(self, it=0):
|
| 427 |
-
|
| 428 |
-
self.it = it
|
| 429 |
-
for m in self.args.tester.modes:
|
| 430 |
-
if m == "paired":
|
| 431 |
-
if not self.in_training:
|
| 432 |
-
self.prepare_directories(m, unconditional=True)
|
| 433 |
-
self.save_experiment_args(m)
|
| 434 |
-
self.test_paired(m)
|
| 435 |
-
else:
|
| 436 |
-
print("Warning: unknown mode: ", m)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
CHANGED
|
@@ -11,6 +11,7 @@ pyloudnorm
|
|
| 11 |
einops
|
| 12 |
packaging
|
| 13 |
librosa
|
|
|
|
| 14 |
transformers
|
| 15 |
wget
|
| 16 |
huggingface-hub
|
|
|
|
| 11 |
einops
|
| 12 |
packaging
|
| 13 |
librosa
|
| 14 |
+
torchlibrosa
|
| 15 |
transformers
|
| 16 |
wget
|
| 17 |
huggingface-hub
|
utils/MSS_loss.py
DELETED
|
@@ -1,260 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Implementation of objective functions used in the task 'ITO-Master'
|
| 3 |
-
https://github.com/SonyResearch/ITO-Master/blob/master/ito_master/modules/loss.py
|
| 4 |
-
"""
|
| 5 |
-
|
| 6 |
-
import numpy as np
|
| 7 |
-
import math
|
| 8 |
-
import torch
|
| 9 |
-
import torch.nn.functional as F
|
| 10 |
-
import torch.nn as nn
|
| 11 |
-
import auraloss
|
| 12 |
-
import torchaudio
|
| 13 |
-
import warnings
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
import os
|
| 17 |
-
import sys
|
| 18 |
-
|
| 19 |
-
currentdir = os.path.dirname(os.path.realpath(__file__))
|
| 20 |
-
sys.path.append(os.path.dirname(currentdir))
|
| 21 |
-
|
| 22 |
-
class FrontEnd(nn.Module):
|
| 23 |
-
def __init__(self, channel='stereo', \
|
| 24 |
-
n_fft=2048, \
|
| 25 |
-
n_mels=128, \
|
| 26 |
-
sample_rate=44100, \
|
| 27 |
-
hop_length=None, \
|
| 28 |
-
win_length=None, \
|
| 29 |
-
window="hann", \
|
| 30 |
-
eps=1e-7, \
|
| 31 |
-
device=torch.device("cpu")):
|
| 32 |
-
super(FrontEnd, self).__init__()
|
| 33 |
-
self.channel = channel
|
| 34 |
-
self.n_fft = n_fft
|
| 35 |
-
self.n_mels = n_mels
|
| 36 |
-
self.sample_rate = sample_rate
|
| 37 |
-
self.hop_length = n_fft//4 if hop_length==None else hop_length
|
| 38 |
-
self.win_length = n_fft if win_length==None else win_length
|
| 39 |
-
self.eps = eps
|
| 40 |
-
if window=="hann":
|
| 41 |
-
self.window = torch.hann_window(window_length=self.win_length, periodic=True).to(device)
|
| 42 |
-
elif window=="hamming":
|
| 43 |
-
self.window = torch.hamming_window(window_length=self.win_length, periodic=True).to(device)
|
| 44 |
-
self.melscale_transform = torchaudio.transforms.MelScale(n_mels=self.n_mels, \
|
| 45 |
-
sample_rate=self.sample_rate, \
|
| 46 |
-
n_stft=self.n_fft//2+1).to(device)
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
def forward(self, input, mode):
|
| 50 |
-
# front-end function which channel-wise combines all demanded features
|
| 51 |
-
# input shape : batch x channel x raw waveform
|
| 52 |
-
# output shape : batch x channel x frequency x time
|
| 53 |
-
phase_output = None
|
| 54 |
-
|
| 55 |
-
front_output_list = []
|
| 56 |
-
for cur_mode in mode:
|
| 57 |
-
# Real & Imaginary
|
| 58 |
-
if cur_mode=="cplx":
|
| 59 |
-
if self.channel=="mono":
|
| 60 |
-
output = torch.stft(input, n_fft=self.n_fft, hop_length=self.hop_length, win_length=self.win_length, window=self.window)
|
| 61 |
-
elif self.channel=="stereo":
|
| 62 |
-
output_l = torch.stft(input[:,0], n_fft=self.n_fft, hop_length=self.hop_length, win_length=self.win_length, window=self.window)
|
| 63 |
-
output_r = torch.stft(input[:,1], n_fft=self.n_fft, hop_length=self.hop_length, win_length=self.win_length, window=self.window)
|
| 64 |
-
output = torch.cat((output_l, output_r), axis=-1)
|
| 65 |
-
if input.shape[-1] % round(self.n_fft/4) == 0:
|
| 66 |
-
output = output[:, :, :-1]
|
| 67 |
-
if self.n_fft % 2 == 0:
|
| 68 |
-
output = output[:, :-1]
|
| 69 |
-
front_output_list.append(output.permute(0, 3, 1, 2))
|
| 70 |
-
# Magnitude & Phase or Mel
|
| 71 |
-
elif "mag" in cur_mode or "mel" in cur_mode:
|
| 72 |
-
if self.channel=="mono":
|
| 73 |
-
cur_cplx = torch.stft(input, n_fft=self.n_fft, hop_length=self.hop_length, win_length=self.win_length, window=self.window, return_complex=True)
|
| 74 |
-
output = self.mag(cur_cplx).unsqueeze(-1)[..., 0:1]
|
| 75 |
-
if "mag_phase" in cur_mode:
|
| 76 |
-
phase = self.phase(cur_cplx)
|
| 77 |
-
if "mel" in cur_mode:
|
| 78 |
-
output = self.melscale_transform(output.squeeze(-1)).unsqueeze(-1)
|
| 79 |
-
elif self.channel=="stereo":
|
| 80 |
-
cplx_l = torch.stft(input[:,0], n_fft=self.n_fft, hop_length=self.hop_length, win_length=self.win_length, window=self.window, return_complex=True)
|
| 81 |
-
cplx_r = torch.stft(input[:,1], n_fft=self.n_fft, hop_length=self.hop_length, win_length=self.win_length, window=self.window, return_complex=True)
|
| 82 |
-
mag_l = self.mag(cplx_l).unsqueeze(-1)
|
| 83 |
-
mag_r = self.mag(cplx_r).unsqueeze(-1)
|
| 84 |
-
output = torch.cat((mag_l, mag_r), axis=-1)
|
| 85 |
-
if "mag_phase" in cur_mode:
|
| 86 |
-
phase_l = self.phase(cplx_l).unsqueeze(-1)
|
| 87 |
-
phase_r = self.phase(cplx_r).unsqueeze(-1)
|
| 88 |
-
output = torch.cat((mag_l, phase_l, mag_r, phase_r), axis=-1)
|
| 89 |
-
if "mel" in cur_mode:
|
| 90 |
-
output = torch.cat((self.melscale_transform(mag_l.squeeze(-1)).unsqueeze(-1), self.melscale_transform(mag_r.squeeze(-1)).unsqueeze(-1)), axis=-1)
|
| 91 |
-
|
| 92 |
-
if "log" in cur_mode:
|
| 93 |
-
output = torch.log(output+self.eps)
|
| 94 |
-
|
| 95 |
-
if input.shape[-1] % round(self.n_fft/4) == 0:
|
| 96 |
-
output = output[:, :, :-1]
|
| 97 |
-
if cur_mode!="mel" and self.n_fft % 2 == 0: # discard highest frequency
|
| 98 |
-
output = output[:, 1:]
|
| 99 |
-
front_output_list.append(output.permute(0, 3, 1, 2))
|
| 100 |
-
|
| 101 |
-
# combine all demanded features
|
| 102 |
-
if not front_output_list:
|
| 103 |
-
raise NameError("NameError at FrontEnd: check using features for front-end")
|
| 104 |
-
elif len(mode)!=1:
|
| 105 |
-
for i, cur_output in enumerate(front_output_list):
|
| 106 |
-
if i==0:
|
| 107 |
-
front_output = cur_output
|
| 108 |
-
else:
|
| 109 |
-
front_output = torch.cat((front_output, cur_output), axis=1)
|
| 110 |
-
else:
|
| 111 |
-
front_output = front_output_list[0]
|
| 112 |
-
|
| 113 |
-
return front_output
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
def mag(self, cplx_input, eps=1e-07):
|
| 117 |
-
# mag_summed = cplx_input.pow(2.).sum(-1) + eps
|
| 118 |
-
mag_summed = cplx_input.real.pow(2.) + cplx_input.imag.pow(2.) + eps
|
| 119 |
-
return mag_summed.pow(0.5)
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
def phase(self, cplx_input, ):
|
| 123 |
-
return torch.atan2(cplx_input.imag, cplx_input.real)
|
| 124 |
-
# return torch.angle(cplx_input)
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
# Multi-Scale Spectral Loss proposed at the paper "DDSP: DIFFERENTIABLE DIGITAL SIGNAL PROCESSING" (https://arxiv.org/abs/2001.04643)
|
| 129 |
-
# we extend this loss by applying it to mid/side channels
|
| 130 |
-
class MultiScale_Spectral_Loss_MidSide_DDSP(nn.Module):
|
| 131 |
-
def __init__(self, mode='midside', \
|
| 132 |
-
reduce=True, \
|
| 133 |
-
n_filters=None, \
|
| 134 |
-
windows_size=None, \
|
| 135 |
-
hops_size=None, \
|
| 136 |
-
window="hann", \
|
| 137 |
-
eps=1e-7, \
|
| 138 |
-
device=torch.device("cpu")):
|
| 139 |
-
super(MultiScale_Spectral_Loss_MidSide_DDSP, self).__init__()
|
| 140 |
-
self.mode = mode
|
| 141 |
-
self.eps = eps
|
| 142 |
-
self.mid_weight = 0.5 # value in the range of 0.0 ~ 1.0
|
| 143 |
-
self.logmag_weight = 0.1
|
| 144 |
-
|
| 145 |
-
if n_filters is None:
|
| 146 |
-
n_filters = [4096, 2048, 1024, 512]
|
| 147 |
-
if windows_size is None:
|
| 148 |
-
windows_size = [4096, 2048, 1024, 512]
|
| 149 |
-
if hops_size is None:
|
| 150 |
-
hops_size = [1024, 512, 256, 128]
|
| 151 |
-
|
| 152 |
-
self.multiscales = []
|
| 153 |
-
for i in range(len(windows_size)):
|
| 154 |
-
cur_scale = {'window_size' : float(windows_size[i])}
|
| 155 |
-
if self.mode=='midside':
|
| 156 |
-
cur_scale['front_end'] = FrontEnd(channel='mono', \
|
| 157 |
-
n_fft=n_filters[i], \
|
| 158 |
-
hop_length=hops_size[i], \
|
| 159 |
-
win_length=windows_size[i], \
|
| 160 |
-
window=window, \
|
| 161 |
-
device=device)
|
| 162 |
-
elif self.mode=='ori':
|
| 163 |
-
cur_scale['front_end'] = FrontEnd(channel='stereo', \
|
| 164 |
-
n_fft=n_filters[i], \
|
| 165 |
-
hop_length=hops_size[i], \
|
| 166 |
-
win_length=windows_size[i], \
|
| 167 |
-
window=window, \
|
| 168 |
-
device=device)
|
| 169 |
-
self.multiscales.append(cur_scale)
|
| 170 |
-
|
| 171 |
-
self.reduce=reduce
|
| 172 |
-
self.objective_l1 = nn.L1Loss(reduce=reduce)
|
| 173 |
-
self.objective_l2 = nn.MSELoss(reduce=reduce)
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
def forward(self, est_targets, targets):
|
| 177 |
-
if self.mode=='midside':
|
| 178 |
-
return self.forward_midside(est_targets, targets)
|
| 179 |
-
elif self.mode=='ori':
|
| 180 |
-
return self.forward_ori(est_targets, targets)
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
def forward_ori(self, est_targets, targets):
|
| 184 |
-
if self.reduce:
|
| 185 |
-
total_mag_loss = 0.0
|
| 186 |
-
total_logmag_loss = 0.0
|
| 187 |
-
else:
|
| 188 |
-
total_mag_loss=torch.zeros(est_targets.shape[0], 1).to(est_targets.device)
|
| 189 |
-
total_logmag_loss=torch.zeros(est_targets.shape[0], 1).to(est_targets.device)
|
| 190 |
-
for cur_scale in self.multiscales:
|
| 191 |
-
est_mag = cur_scale['front_end'](est_targets, mode=["mag"])
|
| 192 |
-
tgt_mag = cur_scale['front_end'](targets, mode=["mag"])
|
| 193 |
-
|
| 194 |
-
mag_loss = self.magnitude_loss(est_mag, tgt_mag)
|
| 195 |
-
logmag_loss = self.log_magnitude_loss(est_mag, tgt_mag)
|
| 196 |
-
if self.reduce:
|
| 197 |
-
total_mag_loss += mag_loss
|
| 198 |
-
total_logmag_loss += logmag_loss
|
| 199 |
-
else:
|
| 200 |
-
total_logmag_loss += logmag_loss.mean((1, 2, 3)).unsqueeze(-1)
|
| 201 |
-
total_mag_loss += mag_loss.mean((1, 2, 3)).unsqueeze(-1)
|
| 202 |
-
# return total_loss
|
| 203 |
-
return (1-self.logmag_weight)*total_mag_loss + \
|
| 204 |
-
(self.logmag_weight)*total_logmag_loss
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
def forward_midside(self, est_targets, targets):
|
| 208 |
-
est_mid, est_side = self.to_mid_side(est_targets)
|
| 209 |
-
tgt_mid, tgt_side = self.to_mid_side(targets)
|
| 210 |
-
if self.reduce:
|
| 211 |
-
total_mag_loss = 0.0
|
| 212 |
-
total_logmag_loss = 0.0
|
| 213 |
-
else:
|
| 214 |
-
total_logmag_loss=torch.zeros(est_targets.shape[0], 1).to(est_targets.device)
|
| 215 |
-
total_mag_loss=torch.zeros(est_targets.shape[0], 1).to(est_targets.device)
|
| 216 |
-
|
| 217 |
-
for cur_scale in self.multiscales:
|
| 218 |
-
est_mid_mag = cur_scale['front_end'](est_mid, mode=["mag"])
|
| 219 |
-
est_side_mag = cur_scale['front_end'](est_side, mode=["mag"])
|
| 220 |
-
tgt_mid_mag = cur_scale['front_end'](tgt_mid, mode=["mag"])
|
| 221 |
-
tgt_side_mag = cur_scale['front_end'](tgt_side, mode=["mag"])
|
| 222 |
-
|
| 223 |
-
mag_loss = self.mid_weight*self.magnitude_loss(est_mid_mag, tgt_mid_mag) + \
|
| 224 |
-
(1-self.mid_weight)*self.magnitude_loss(est_side_mag, tgt_side_mag)
|
| 225 |
-
logmag_loss = self.mid_weight*self.log_magnitude_loss(est_mid_mag, tgt_mid_mag) + \
|
| 226 |
-
(1-self.mid_weight)*self.log_magnitude_loss(est_side_mag, tgt_side_mag)
|
| 227 |
-
|
| 228 |
-
#take mean over all dimensions except batch
|
| 229 |
-
if self.reduce:
|
| 230 |
-
total_mag_loss += mag_loss
|
| 231 |
-
total_logmag_loss += logmag_loss
|
| 232 |
-
else:
|
| 233 |
-
total_mag_loss += mag_loss.mean((1, 2, 3)).unsqueeze(-1)
|
| 234 |
-
#mean over dims 1, 2, 3
|
| 235 |
-
total_logmag_loss += logmag_loss.mean((1, 2, 3)).unsqueeze(-1)
|
| 236 |
-
# return total_loss
|
| 237 |
-
return (1-self.logmag_weight)*total_mag_loss + \
|
| 238 |
-
(self.logmag_weight)*total_logmag_loss
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
def to_mid_side(self, stereo_in):
|
| 242 |
-
mid = stereo_in[:,0] + stereo_in[:,1]
|
| 243 |
-
side = stereo_in[:,0] - stereo_in[:,1]
|
| 244 |
-
return mid, side
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
def magnitude_loss(self, est_mag_spec, tgt_mag_spec):
|
| 248 |
-
if self.reduce:
|
| 249 |
-
return torch.norm(self.objective_l1(est_mag_spec, tgt_mag_spec))
|
| 250 |
-
else:
|
| 251 |
-
return self.objective_l1(est_mag_spec, tgt_mag_spec)
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
def log_magnitude_loss(self, est_mag_spec, tgt_mag_spec):
|
| 255 |
-
est_log_mag_spec = torch.log10(est_mag_spec+self.eps)
|
| 256 |
-
tgt_log_mag_spec = torch.log10(tgt_mag_spec+self.eps)
|
| 257 |
-
return self.objective_l2(est_log_mag_spec, tgt_log_mag_spec)
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/collators.py
DELETED
|
@@ -1,262 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
import torch
|
| 4 |
-
import torch.nn.functional as F
|
| 5 |
-
import torchaudio
|
| 6 |
-
|
| 7 |
-
def collate_multitrack_sim(batch, max_tracks=None):
|
| 8 |
-
|
| 9 |
-
x= [ data_i[0] for data_i in batch ] # x is a list of tensors, each tensor is a track
|
| 10 |
-
clusters=[ torch.tensor(data_i[1]) for data_i in batch ] # cluster is a list of tensors, each tensor is a cluster
|
| 11 |
-
taxonomies=[ data_i[2] for data_i in batch ] # taxonomy is a
|
| 12 |
-
|
| 13 |
-
if max_tracks is None:
|
| 14 |
-
max_tracks = max(track.shape[0] for track in x) # Find the maximum number of tracks in the batch
|
| 15 |
-
else:
|
| 16 |
-
if max_tracks > max(track.shape[0] for track in x):
|
| 17 |
-
print(f"Warning: max_tracks is set to {max_tracks}, but the maximum number of tracks in the batch is {max(track.shape[0] for track in x)}. I dont know what will happen, consider increasing max_tracks," )
|
| 18 |
-
|
| 19 |
-
# Pad each examples with zeros to the maximum length (dimension 0)
|
| 20 |
-
|
| 21 |
-
padded_x = []
|
| 22 |
-
padded_taxonomies = []
|
| 23 |
-
masks = torch.zeros((len(x), max_tracks), dtype=torch.bool) # Create a mask tensor
|
| 24 |
-
|
| 25 |
-
for i in range(len(x)):
|
| 26 |
-
current_x = x[i]
|
| 27 |
-
current_taxonomies = taxonomies[i]
|
| 28 |
-
|
| 29 |
-
# Get current number of tracks
|
| 30 |
-
current_tracks = current_x.shape[0]
|
| 31 |
-
|
| 32 |
-
masks[i, :current_tracks] = 1 # Set mask for current tracks
|
| 33 |
-
|
| 34 |
-
if current_tracks < max_tracks:
|
| 35 |
-
# Pad dimension N (first dimension)
|
| 36 |
-
# For a tensor of shape [N, C, L], we need to pad the first dimension
|
| 37 |
-
# F.pad expects padding as (pad_left, pad_right, pad_top, pad_bottom, pad_front, pad_back)
|
| 38 |
-
# To pad the first dimension, we need to specify padding for the third-from-last dimension
|
| 39 |
-
pad_size = (0, 0, # last dim (L): no padding
|
| 40 |
-
0, 0, # second-to-last dim (C): no padding
|
| 41 |
-
0, max_tracks - current_tracks) # third-to-last dim (N): pad at the end
|
| 42 |
-
|
| 43 |
-
padded_x.append(F.pad(current_x, pad_size))
|
| 44 |
-
|
| 45 |
-
# Pad taxonomies
|
| 46 |
-
padded_taxonomies.append(current_taxonomies + [None] * (max_tracks - len(current_taxonomies)))
|
| 47 |
-
elif current_tracks > max_tracks :
|
| 48 |
-
raise ValueError(f"Number of tracks {current_tracks} exceeds maximum allowed {max_tracks}. that is impossible")
|
| 49 |
-
else:
|
| 50 |
-
padded_x.append(current_x)
|
| 51 |
-
padded_taxonomies.append(current_taxonomies[:max_tracks])
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
x_stacked = torch.stack(padded_x, dim=0) # Shape: [B, max_tracks, C, L]
|
| 56 |
-
|
| 57 |
-
clusters_stacked = torch.stack(clusters, dim=0) # Shape: [B, max_tracks]
|
| 58 |
-
|
| 59 |
-
return {
|
| 60 |
-
'x': x_stacked,
|
| 61 |
-
'clusters': clusters_stacked,
|
| 62 |
-
'taxonomies': padded_taxonomies,
|
| 63 |
-
"masks": masks
|
| 64 |
-
}
|
| 65 |
-
|
| 66 |
-
def collate_multitrack(batch, max_tracks=None, sample_rate=None, segment_length=None, device=None):
|
| 67 |
-
|
| 68 |
-
x= [ data_i[0] for data_i in batch ] # x is a list of tensors, each tensor is a track
|
| 69 |
-
|
| 70 |
-
paths=[ data_i[1] for data_i in batch ] # paths is a list of paths to the audio files, each path is a string
|
| 71 |
-
fs= [ data_i[2] for data_i in batch ] # fs is a list of sample rates, each sample rate is an integer
|
| 72 |
-
|
| 73 |
-
if max_tracks is None:
|
| 74 |
-
max_tracks = max(track.shape[0] for track in x) # Find the maximum number of tracks in the batch
|
| 75 |
-
else:
|
| 76 |
-
if max_tracks < max(track.shape[0] for track in x):
|
| 77 |
-
print(f"Warning: max_tracks is set to {max_tracks}, but the maximum number of tracks in the batch is {max(track.shape[0] for track in x)}. I will crop the last tracks. I hope the order is random..." )
|
| 78 |
-
|
| 79 |
-
for i in range(len(x)):
|
| 80 |
-
if x[i].shape[0] > max_tracks:
|
| 81 |
-
print("cropping x[i] to max_tracks")
|
| 82 |
-
x[i] = x[i][:max_tracks]
|
| 83 |
-
|
| 84 |
-
is_x_none= any(x_i is None for x_i in x)
|
| 85 |
-
|
| 86 |
-
assert not (is_x_none ), "Either x or y should be None, but not both. This is a bug in the collator"
|
| 87 |
-
|
| 88 |
-
#now resample audio to 44100 Hz and cut to segment length
|
| 89 |
-
for i in range(len(x)):
|
| 90 |
-
x[i] = x[i].to(device) # Move to device
|
| 91 |
-
|
| 92 |
-
if fs[i] != sample_rate:
|
| 93 |
-
if fs[i] == 48000 and sample_rate == 44100:
|
| 94 |
-
#print(f"Resampling audio from {fs[i]} Hz to {sample_rate} Hz")
|
| 95 |
-
x[i]=torchaudio.functional.resample(x[i], orig_freq=160, new_freq=147)
|
| 96 |
-
else:
|
| 97 |
-
#print(f"Resampling audio from {fs[i]} Hz to {sample_rate} Hz")
|
| 98 |
-
x[i]=torchaudio.functional.resample(x[i], fs[i], sample_rate)
|
| 99 |
-
|
| 100 |
-
assert segment_length is not None, "segment_length should be set to the length of the audio segment in samples"
|
| 101 |
-
if x[i].shape[-1] > segment_length:
|
| 102 |
-
x[i] = x[i][..., :segment_length]
|
| 103 |
-
elif x[i].shape[-1] < segment_length:
|
| 104 |
-
raise ValueError(f"Audio length {x[i].shape[-1]} is less than segment length {segment_length}. Please check your data.")
|
| 105 |
-
|
| 106 |
-
# Pad each examples with zeros to the maximum length (dimension 0)
|
| 107 |
-
padded_x = []
|
| 108 |
-
masks = torch.zeros((len(x), max_tracks), dtype=torch.bool) # Create a mask tensor
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
for i in range(len(x)):
|
| 112 |
-
if not is_x_none:
|
| 113 |
-
current_x = x[i]
|
| 114 |
-
|
| 115 |
-
# Get current number of tracks
|
| 116 |
-
current_tracks = current_x.shape[0]
|
| 117 |
-
|
| 118 |
-
masks[i, :current_tracks] = 1 # Set mask for current tracks
|
| 119 |
-
|
| 120 |
-
if current_tracks < max_tracks:
|
| 121 |
-
# Pad dimension N (first dimension)
|
| 122 |
-
# For a tensor of shape [N, C, L], we need to pad the first dimension
|
| 123 |
-
# F.pad expects padding as (pad_left, pad_right, pad_top, pad_bottom, pad_front, pad_back)
|
| 124 |
-
# To pad the first dimension, we need to specify padding for the third-from-last dimension
|
| 125 |
-
pad_size = (0, 0, # last dim (L): no padding
|
| 126 |
-
0, 0, # second-to-last dim (C): no padding
|
| 127 |
-
0, max_tracks - current_tracks) # third-to-last dim (N): pad at the end
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
if not is_x_none:
|
| 131 |
-
padded_x.append(F.pad(current_x, pad_size))
|
| 132 |
-
|
| 133 |
-
elif current_tracks > max_tracks :
|
| 134 |
-
raise ValueError(f"Number of tracks {current_tracks} exceeds maximum allowed {max_tracks}. that is impossible")
|
| 135 |
-
else:
|
| 136 |
-
if not is_x_none:
|
| 137 |
-
padded_x.append(current_x)
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
if not is_x_none:
|
| 141 |
-
x_stacked = torch.stack(padded_x, dim=0) # Shape: [B, max_tracks, C, L]
|
| 142 |
-
|
| 143 |
-
return {
|
| 144 |
-
'y': x_stacked.to(device), # Shape: [B, max_tracks, C, L]
|
| 145 |
-
"masks": masks.to(device), # Shape: [B, max_tracks]
|
| 146 |
-
"paths": paths,
|
| 147 |
-
"fs": fs
|
| 148 |
-
}
|
| 149 |
-
|
| 150 |
-
def collate_multitrack_paired(batch, max_tracks=None, sample_rate=None, segment_length=None, device=None):
|
| 151 |
-
|
| 152 |
-
x= [ data_i[0] for data_i in batch ] # x is a list of tensors, each tensor is a track
|
| 153 |
-
y= [ data_i[1] for data_i in batch ] # x is a list of tensors, each tensor is a track
|
| 154 |
-
|
| 155 |
-
taxonomies=[ data_i[2] for data_i in batch ] # taxonomy is a
|
| 156 |
-
|
| 157 |
-
paths=[ data_i[3] for data_i in batch ] # paths is a list of paths to the audio files, each path is a string
|
| 158 |
-
|
| 159 |
-
if max_tracks is None:
|
| 160 |
-
max_tracks = max(track.shape[0] for track in x) # Find the maximum number of tracks in the batch
|
| 161 |
-
else:
|
| 162 |
-
if max_tracks < max(track.shape[0] for track in x):
|
| 163 |
-
print(f"Warning: max_tracks is set to {max_tracks}, but the maximum number of tracks in the batch is {max(track.shape[0] for track in x)}. I will crop the last tracks. I hope the order is random..." )
|
| 164 |
-
|
| 165 |
-
for i in range(len(x)):
|
| 166 |
-
if x[i].shape[0] > max_tracks:
|
| 167 |
-
print("cropping x[i] to max_tracks")
|
| 168 |
-
x[i] = x[i][:max_tracks]
|
| 169 |
-
if y[i].shape[0] > max_tracks:
|
| 170 |
-
print("cropping y[i] to max_tracks")
|
| 171 |
-
y[i] = y[i][:max_tracks]
|
| 172 |
-
if len(taxonomies[i]) > max_tracks:
|
| 173 |
-
print("cropping taxonomies[i] to max_tracks")
|
| 174 |
-
taxonomies[i] = taxonomies[i][:max_tracks]
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
is_x_none= any(x_i is None for x_i in x)
|
| 179 |
-
is_y_none= any(y_i is None for y_i in y)
|
| 180 |
-
|
| 181 |
-
assert not (is_x_none and is_y_none), "Either x or y should be None, but not both. This is a bug in the collator"
|
| 182 |
-
|
| 183 |
-
# Pad each examples with zeros to the maximum length (dimension 0)
|
| 184 |
-
padded_x = []
|
| 185 |
-
padded_y = []
|
| 186 |
-
padded_taxonomies = []
|
| 187 |
-
masks = torch.zeros((len(x), max_tracks), dtype=torch.bool) # Create a mask tensor
|
| 188 |
-
|
| 189 |
-
for i in range(len(x)):
|
| 190 |
-
|
| 191 |
-
if not is_x_none:
|
| 192 |
-
current_x = x[i]
|
| 193 |
-
if not is_y_none:
|
| 194 |
-
current_y = y[i]
|
| 195 |
-
current_taxonomies = taxonomies[i]
|
| 196 |
-
|
| 197 |
-
# Get current number of tracks
|
| 198 |
-
if is_x_none:
|
| 199 |
-
current_tracks = current_y.shape[0]
|
| 200 |
-
else:
|
| 201 |
-
current_tracks = current_x.shape[0]
|
| 202 |
-
if not is_y_none:
|
| 203 |
-
assert current_tracks == current_y.shape[0], f"Number of tracks in x ({current_tracks}) does not match number of tracks in y ({current_y.shape[0]})"
|
| 204 |
-
|
| 205 |
-
masks[i, :current_tracks] = 1 # Set mask for current tracks
|
| 206 |
-
|
| 207 |
-
if current_tracks < max_tracks:
|
| 208 |
-
# Pad dimension N (first dimension)
|
| 209 |
-
# For a tensor of shape [N, C, L], we need to pad the first dimension
|
| 210 |
-
# F.pad expects padding as (pad_left, pad_right, pad_top, pad_bottom, pad_front, pad_back)
|
| 211 |
-
# To pad the first dimension, we need to specify padding for the third-from-last dimension
|
| 212 |
-
pad_size = (0, 0, # last dim (L): no padding
|
| 213 |
-
0, 0, # second-to-last dim (C): no padding
|
| 214 |
-
0, max_tracks - current_tracks) # third-to-last dim (N): pad at the end
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
if not is_x_none:
|
| 218 |
-
padded_x.append(F.pad(current_x, pad_size))
|
| 219 |
-
if not is_y_none:
|
| 220 |
-
padded_y.append(F.pad(current_y, pad_size))
|
| 221 |
-
|
| 222 |
-
# Pad taxonomies
|
| 223 |
-
padded_taxonomies.append(current_taxonomies + [None] * (max_tracks - len(current_taxonomies)))
|
| 224 |
-
elif current_tracks > max_tracks :
|
| 225 |
-
raise ValueError(f"Number of tracks {current_tracks} exceeds maximum allowed {max_tracks}. that is impossible")
|
| 226 |
-
else:
|
| 227 |
-
if not is_x_none:
|
| 228 |
-
padded_x.append(current_x)
|
| 229 |
-
if not is_y_none:
|
| 230 |
-
padded_y.append(current_y)
|
| 231 |
-
|
| 232 |
-
padded_taxonomies.append(current_taxonomies[:max_tracks])
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
if not is_x_none:
|
| 236 |
-
x_stacked = torch.stack(padded_x, dim=0) # Shape: [B, max_tracks, C, L]
|
| 237 |
-
|
| 238 |
-
if not is_y_none:
|
| 239 |
-
y_stacked = torch.stack(padded_y, dim=0) # Shape: [B, max_tracks, C, L]
|
| 240 |
-
|
| 241 |
-
if is_x_none:
|
| 242 |
-
return {
|
| 243 |
-
'y': y_stacked,
|
| 244 |
-
'taxonomies': padded_taxonomies,
|
| 245 |
-
"masks": masks,
|
| 246 |
-
"paths": paths
|
| 247 |
-
}
|
| 248 |
-
if is_y_none:
|
| 249 |
-
return {
|
| 250 |
-
'x': x_stacked,
|
| 251 |
-
'taxonomies': padded_taxonomies,
|
| 252 |
-
"masks": masks,
|
| 253 |
-
"paths": paths
|
| 254 |
-
}
|
| 255 |
-
|
| 256 |
-
return {
|
| 257 |
-
'x': x_stacked,
|
| 258 |
-
'y': y_stacked,
|
| 259 |
-
'taxonomies': padded_taxonomies,
|
| 260 |
-
"masks": masks,
|
| 261 |
-
"paths": paths
|
| 262 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/common_audioeffects.py
DELETED
|
@@ -1,1729 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Audio effects for data augmentation.
|
| 3 |
-
|
| 4 |
-
Several audio effects can be combined into an augmentation chain.
|
| 5 |
-
|
| 6 |
-
Important note: We assume that the parallelization during training is done using
|
| 7 |
-
multi-processing and not multi-threading. Hence, we do not need the
|
| 8 |
-
`@sox.sox_context()` decorators as discussed in this
|
| 9 |
-
[thread](https://github.com/pseeth/soxbindings/issues/4).
|
| 10 |
-
|
| 11 |
-
Section 2, TL21
|
| 12 |
-
AI Speech and Sound Group, SL1
|
| 13 |
-
"""
|
| 14 |
-
|
| 15 |
-
from itertools import permutations
|
| 16 |
-
import os
|
| 17 |
-
import io
|
| 18 |
-
import functools
|
| 19 |
-
import lameenc
|
| 20 |
-
import logging
|
| 21 |
-
import numpy as np
|
| 22 |
-
import pymixconsole as pymc
|
| 23 |
-
from pymixconsole.parameter import Parameter
|
| 24 |
-
from pymixconsole.parameter_list import ParameterList
|
| 25 |
-
from pymixconsole.processor import Processor
|
| 26 |
-
from random import shuffle
|
| 27 |
-
from scipy.signal import oaconvolve
|
| 28 |
-
import soundfile as sf
|
| 29 |
-
#import soxbindings as sox
|
| 30 |
-
from typing import List, Optional, Tuple, Union, Dict
|
| 31 |
-
from numba import jit
|
| 32 |
-
|
| 33 |
-
#from common_dataprocessing import sample_data
|
| 34 |
-
|
| 35 |
-
# prevent pysox from logging warnings regarding non-opimal timestretch factors
|
| 36 |
-
logging.getLogger('sox').setLevel(logging.ERROR)
|
| 37 |
-
|
| 38 |
-
# set maximum peak value if we pass a signal through SOX
|
| 39 |
-
MAX_SOX_PROCESSING_PEAK = 0.707
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
def convert_audio2data(x):
|
| 43 |
-
"""
|
| 44 |
-
Convert audio data from the format it was stored in to float32.
|
| 45 |
-
|
| 46 |
-
Args:
|
| 47 |
-
x (Numpy array): input with `x.dtype` either `np.int16`, `np.int32`, `np.float32` or `np.float64`.
|
| 48 |
-
|
| 49 |
-
Returns:
|
| 50 |
-
Numpy array: output with values in [-1., 1.) where `dtype` is `np.float32`.
|
| 51 |
-
"""
|
| 52 |
-
if x.dtype in [np.float32, np.float64]:
|
| 53 |
-
return x.astype(dtype=np.float32)
|
| 54 |
-
else:
|
| 55 |
-
return (x.astype(dtype=np.float64) / (1. + np.iinfo(x.dtype).max)).astype(np.float32)
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
def sample_data(data: Tuple[int, Union[np.ndarray, functools.partial]],
|
| 60 |
-
start: int = 0, length: Optional[int] = None) -> np.ndarray:
|
| 61 |
-
"""
|
| 62 |
-
Load one stem specified by `data`.
|
| 63 |
-
|
| 64 |
-
Returns the audio beginning from `start` either up to the end (if `length` is None)
|
| 65 |
-
or until the provided `length`. For the case that `start + length > n_samples`, we do a wrap-around and
|
| 66 |
-
load the remaining samples from the beginning of `data`.
|
| 67 |
-
|
| 68 |
-
Args:
|
| 69 |
-
data: Data with shape (n_samples, data).
|
| 70 |
-
start: Start index.
|
| 71 |
-
length: Length of sample. If `length` is not None, `length` samples are returned (possibly with a wrap-around).
|
| 72 |
-
Otherwise, everything until the end of `data` is returned.
|
| 73 |
-
|
| 74 |
-
Returns:
|
| 75 |
-
samples: data with shape `n_samples x n_channels`
|
| 76 |
-
"""
|
| 77 |
-
n_samples, audio = data
|
| 78 |
-
|
| 79 |
-
# determine whether we have to load the audio or whether it was already loaded
|
| 80 |
-
is_loaded = True if type(audio) is np.ndarray else False
|
| 81 |
-
|
| 82 |
-
# if `length` is not None, then only select subset
|
| 83 |
-
do_wrap_around = False
|
| 84 |
-
if length is not None:
|
| 85 |
-
if start + length > n_samples:
|
| 86 |
-
# we need to wrap around and concatenate `start:` and `:stop`
|
| 87 |
-
do_wrap_around = True
|
| 88 |
-
stop = length - (n_samples - start)
|
| 89 |
-
else:
|
| 90 |
-
# no wrap around as it is inside the array/file boundaries
|
| 91 |
-
stop = start + length
|
| 92 |
-
else:
|
| 93 |
-
stop = None
|
| 94 |
-
|
| 95 |
-
if is_loaded:
|
| 96 |
-
if not do_wrap_around:
|
| 97 |
-
samples = convert_audio2data(audio[start:stop])
|
| 98 |
-
else:
|
| 99 |
-
samples = np.vstack((convert_audio2data(audio[start:]),
|
| 100 |
-
convert_audio2data(audio[:stop])))
|
| 101 |
-
else:
|
| 102 |
-
if not do_wrap_around:
|
| 103 |
-
samples = convert_audio2data(audio(start=start, stop=stop)[0])
|
| 104 |
-
else:
|
| 105 |
-
samples = np.vstack((convert_audio2data(audio(start=start)[0]),
|
| 106 |
-
convert_audio2data(audio(stop=stop)[0])))
|
| 107 |
-
|
| 108 |
-
return samples
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
# Monkey-Patch `Processor` for convenience
|
| 112 |
-
# (a) Allow `None` as blocksize if processor can work on variable-length audio
|
| 113 |
-
def new_init(self, name, parameters, block_size, sample_rate=None, normalize=None, dtype='float32'):
|
| 114 |
-
"""
|
| 115 |
-
Initialize processor.
|
| 116 |
-
|
| 117 |
-
Args:
|
| 118 |
-
self: Reference to object
|
| 119 |
-
name (str): Name of processor.
|
| 120 |
-
parameters (parameter_list): Parameters for this processor.
|
| 121 |
-
block_size (int): Size of blocks for blockwise processing.
|
| 122 |
-
Can also be `None` if full audio can be processed at once.
|
| 123 |
-
sample_rate (int): Sample rate of input audio. Use `None` if effect is independent of this value.
|
| 124 |
-
normalize (str): Defines, whether the processed signal is normalized.
|
| 125 |
-
Possible values are `'rms'`, `'max'` and `None`.
|
| 126 |
-
dtype (str): data type of samples
|
| 127 |
-
|
| 128 |
-
Raises:
|
| 129 |
-
ValueError: If `normalize` is not equal to `'rms'`, `'max'` or `False`.
|
| 130 |
-
"""
|
| 131 |
-
self.name = name
|
| 132 |
-
self.parameters = parameters
|
| 133 |
-
self.block_size = block_size
|
| 134 |
-
self.sample_rate = sample_rate
|
| 135 |
-
self.dtype = dtype
|
| 136 |
-
if normalize not in [None, 'rms', 'max']:
|
| 137 |
-
raise ValueError(f'Unknown value {normalize} for `normalize`. Must be either `rms`, `max` or `None`')
|
| 138 |
-
self.normalize = normalize
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
# (b) make code simpler
|
| 142 |
-
def new_update(self, parameter_name):
|
| 143 |
-
"""
|
| 144 |
-
Update processor after randomization of parameters.
|
| 145 |
-
|
| 146 |
-
Args:
|
| 147 |
-
self: Reference to object.
|
| 148 |
-
parameter_name (str): Parameter whose value has changed.
|
| 149 |
-
"""
|
| 150 |
-
pass
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
# (c) representation for nice print
|
| 154 |
-
def new_repr(self):
|
| 155 |
-
"""
|
| 156 |
-
Create human-readable representation.
|
| 157 |
-
|
| 158 |
-
Args:
|
| 159 |
-
self: Reference to object.
|
| 160 |
-
|
| 161 |
-
Returns:
|
| 162 |
-
string representation of object.
|
| 163 |
-
"""
|
| 164 |
-
return f'Processor(name={self.name!r}, parameters={self.parameters!r}'
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
Processor.__init__ = new_init
|
| 168 |
-
Processor.__repr__ = new_repr
|
| 169 |
-
Processor.update = new_update
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
class AugmentationChain:
|
| 173 |
-
"""Basic audio Fx chain which is used for data augmentation."""
|
| 174 |
-
|
| 175 |
-
def __init__(self,
|
| 176 |
-
fxs: Optional[List[Tuple[Union[Processor, 'AugmentationChain'], float]]] = [],
|
| 177 |
-
shuffle: Optional[bool] = False,
|
| 178 |
-
apply_to: Optional[str] = 'both'):
|
| 179 |
-
"""
|
| 180 |
-
Create augmentation chain from the dictionary `fxs`.
|
| 181 |
-
|
| 182 |
-
Args:
|
| 183 |
-
fxs (list of tuples): Each tuple has three elements:
|
| 184 |
-
First tuple element is an instance of `pymc.processor` or `AugmentationChain` that
|
| 185 |
-
we want to use for data augmentation.
|
| 186 |
-
Second element gives probability that effect should be applied.
|
| 187 |
-
shuffle (bool): If `True` then order of Fx are changed whenever chain is applied.
|
| 188 |
-
apply_to (str): Apply the chain to both input and target or one of them only.
|
| 189 |
-
Possible values are `'both'`, `'input'` and `target`.
|
| 190 |
-
|
| 191 |
-
Raises:
|
| 192 |
-
ValueError: If `apply_to` is not equal to `both`, `input` or `target`.
|
| 193 |
-
"""
|
| 194 |
-
self.fxs = fxs
|
| 195 |
-
self.shuffle = shuffle
|
| 196 |
-
if apply_to not in ['both', 'input', 'target']:
|
| 197 |
-
raise ValueError(f'Unknown value {apply_to} for `apply_to`. Must be `both`, `input` or `target`')
|
| 198 |
-
else:
|
| 199 |
-
self.apply_to = apply_to
|
| 200 |
-
|
| 201 |
-
def apply_processor(self, x, processor: Processor):
|
| 202 |
-
"""
|
| 203 |
-
Pass audio in `x` through `processor` and output the respective processed audio.
|
| 204 |
-
|
| 205 |
-
Args:
|
| 206 |
-
x (Numpy array): Input audio of shape `n_samples` x `n_channels`.
|
| 207 |
-
processor (Processor): Audio effect that we want to apply.
|
| 208 |
-
|
| 209 |
-
Returns:
|
| 210 |
-
Numpy array: Processed audio of shape `n_samples` x `n_channels` (same size as `x')
|
| 211 |
-
"""
|
| 212 |
-
n_samples_input = x.shape[0]
|
| 213 |
-
|
| 214 |
-
if processor.block_size is None:
|
| 215 |
-
y = processor.process(x)
|
| 216 |
-
else:
|
| 217 |
-
# make sure that n_samples is a multiple of `processor.block_size`
|
| 218 |
-
if x.shape[0] % processor.block_size != 0:
|
| 219 |
-
n_pad = processor.block_size - x.shape[0] % processor.block_size
|
| 220 |
-
x = np.pad(x, ((0, n_pad), (0, 0)), mode='reflective')
|
| 221 |
-
|
| 222 |
-
y = np.zeros_like(x)
|
| 223 |
-
for idx in range(0, x.shape[0], processor.block_size):
|
| 224 |
-
y[idx:idx+processor.block_size, :] = processor.process(x[idx:idx+processor.block_size, :])
|
| 225 |
-
|
| 226 |
-
if processor.normalize is not None:
|
| 227 |
-
if processor.normalize == 'rms': # normalize output energy such that it is the same as the input energy
|
| 228 |
-
scale = np.sqrt(np.mean(np.square(x)) / np.maximum(1e-7, np.mean(np.square(y))))
|
| 229 |
-
elif processor.normalize == 'max': # normalize output signal by its max. amplitude
|
| 230 |
-
scale = (1 + 1e-7)/(np.max(np.abs(y)) + 1e-7)
|
| 231 |
-
y *= scale
|
| 232 |
-
|
| 233 |
-
# return audio of same length as x
|
| 234 |
-
return y[:n_samples_input, :]
|
| 235 |
-
|
| 236 |
-
def __call__(self, input_x, target_x):
|
| 237 |
-
"""
|
| 238 |
-
Apply augmentation chain to audio in `input_x` and `target_x`.
|
| 239 |
-
|
| 240 |
-
Args:
|
| 241 |
-
input_x (Numpy array): Audio samples of shape `n_samples` x `n_channels`.
|
| 242 |
-
target_x (Numpy array): Audio samples of shape `n_samples` x `n_channels`.
|
| 243 |
-
|
| 244 |
-
Returns:
|
| 245 |
-
input_y (Numpy array): Processed audio of same shape as `input_x` where effects have been applied.
|
| 246 |
-
target_y (Numpy array): Processed audio of same shape as `target_x` where effects have been applied.
|
| 247 |
-
"""
|
| 248 |
-
# randomly shuffle effect order if `self.shuffle` is True
|
| 249 |
-
if self.shuffle:
|
| 250 |
-
shuffle(self.fxs)
|
| 251 |
-
|
| 252 |
-
input_y = input_x
|
| 253 |
-
target_y = target_x
|
| 254 |
-
|
| 255 |
-
# check whether we only need to process once later
|
| 256 |
-
if self.apply_to == 'both' and np.allclose(input_y, target_y):
|
| 257 |
-
is_input_equal_target = True
|
| 258 |
-
else:
|
| 259 |
-
is_input_equal_target = False
|
| 260 |
-
|
| 261 |
-
# apply effects with probabilities given in `self.fxs`
|
| 262 |
-
for fx, p in self.fxs:
|
| 263 |
-
if np.random.rand() < p:
|
| 264 |
-
if isinstance(fx, Processor):
|
| 265 |
-
# randomize all effect parameters (also calls `update()` for each processor)
|
| 266 |
-
fx.randomize()
|
| 267 |
-
# apply processor dependent on `apply_to`
|
| 268 |
-
if self.apply_to == 'both':
|
| 269 |
-
input_y = self.apply_processor(input_y, fx)
|
| 270 |
-
if not is_input_equal_target:
|
| 271 |
-
target_y = self.apply_processor(target_y, fx)
|
| 272 |
-
elif self.apply_to == 'input':
|
| 273 |
-
input_y = self.apply_processor(input_y, fx)
|
| 274 |
-
elif self.apply_to == 'target':
|
| 275 |
-
target_y = self.apply_processor(target_y, fx)
|
| 276 |
-
else:
|
| 277 |
-
# apply effect chain
|
| 278 |
-
if is_input_equal_target:
|
| 279 |
-
target_y = input_y
|
| 280 |
-
input_y, target_y = fx(input_y, target_y)
|
| 281 |
-
# check whether input and target are still the same
|
| 282 |
-
if not fx.apply_to == 'both':
|
| 283 |
-
is_input_equal_target = False
|
| 284 |
-
|
| 285 |
-
if is_input_equal_target:
|
| 286 |
-
target_y = input_y
|
| 287 |
-
|
| 288 |
-
return input_y, target_y
|
| 289 |
-
|
| 290 |
-
def __repr__(self):
|
| 291 |
-
"""
|
| 292 |
-
Human-readable representation.
|
| 293 |
-
|
| 294 |
-
Returns:
|
| 295 |
-
string representation of object.
|
| 296 |
-
"""
|
| 297 |
-
return f'AugmentationChain(fxs={self.fxs!r}, shuffle={self.shuffle!r})'
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% DISTORTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 301 |
-
def hard_clip(x, threshold_dB, drive):
|
| 302 |
-
"""
|
| 303 |
-
Hard clip distortion.
|
| 304 |
-
|
| 305 |
-
Args:
|
| 306 |
-
x: input audio
|
| 307 |
-
threshold_dB: threshold
|
| 308 |
-
drive: drive
|
| 309 |
-
|
| 310 |
-
Returns:
|
| 311 |
-
(Numpy array): distorted audio
|
| 312 |
-
"""
|
| 313 |
-
drive_linear = np.power(10., drive / 20.).astype(np.float32)
|
| 314 |
-
threshold_linear = 10. ** (threshold_dB / 20.)
|
| 315 |
-
return np.clip(x * drive_linear, -threshold_linear, threshold_linear)
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
def overdrive(x, drive, colour, sample_rate):
|
| 319 |
-
"""
|
| 320 |
-
Overdrive distortion.
|
| 321 |
-
|
| 322 |
-
Args:
|
| 323 |
-
x: input audio
|
| 324 |
-
drive: Controls the amount of distortion (dB).
|
| 325 |
-
colour: Controls the amount of even harmonic content in the output(dB)
|
| 326 |
-
sample_rate: sampling rate
|
| 327 |
-
|
| 328 |
-
Returns:
|
| 329 |
-
(Numpy array): distorted audio
|
| 330 |
-
"""
|
| 331 |
-
scale = np.max(np.abs(x))
|
| 332 |
-
if scale > MAX_SOX_PROCESSING_PEAK:
|
| 333 |
-
clips = True
|
| 334 |
-
x = x * (MAX_SOX_PROCESSING_PEAK / scale)
|
| 335 |
-
else:
|
| 336 |
-
clips = False
|
| 337 |
-
|
| 338 |
-
tfm = sox.Transformer()
|
| 339 |
-
tfm.overdrive(gain_db=drive, colour=colour)
|
| 340 |
-
y = tfm.build_array(input_array=x, sample_rate_in=sample_rate).astype(np.float32)
|
| 341 |
-
|
| 342 |
-
if clips:
|
| 343 |
-
y *= scale / MAX_SOX_PROCESSING_PEAK # rescale output to original scale
|
| 344 |
-
return y
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
def hyperbolic_tangent(x, drive):
|
| 348 |
-
"""
|
| 349 |
-
Hyperbolic Tanh distortion.
|
| 350 |
-
|
| 351 |
-
Args:
|
| 352 |
-
x: input audio
|
| 353 |
-
drive: drive
|
| 354 |
-
|
| 355 |
-
Returns:
|
| 356 |
-
(Numpy array): distorted audio
|
| 357 |
-
"""
|
| 358 |
-
drive_linear = np.power(10., drive / 20.).astype(np.float32)
|
| 359 |
-
return np.tanh(2. * x * drive_linear)
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
def soft_sine(x, drive):
|
| 363 |
-
"""
|
| 364 |
-
Soft sine distortion.
|
| 365 |
-
|
| 366 |
-
Args:
|
| 367 |
-
x: input audio
|
| 368 |
-
drive: drive
|
| 369 |
-
|
| 370 |
-
Returns:
|
| 371 |
-
(Numpy array): distorted audio
|
| 372 |
-
"""
|
| 373 |
-
drive_linear = np.power(10., drive / 20.).astype(np.float32)
|
| 374 |
-
y = np.clip(x * drive_linear, -np.pi/4.0, np.pi/4.0)
|
| 375 |
-
return np.sin(2. * y)
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
def bit_crusher(x, bits):
|
| 379 |
-
"""
|
| 380 |
-
Bit crusher distortion.
|
| 381 |
-
|
| 382 |
-
Args:
|
| 383 |
-
x: input audio
|
| 384 |
-
bits: bits
|
| 385 |
-
|
| 386 |
-
Returns:
|
| 387 |
-
(Numpy array): distorted audio
|
| 388 |
-
"""
|
| 389 |
-
return np.rint(x * (2 ** bits)) / (2 ** bits)
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
class Distortion(Processor):
|
| 393 |
-
"""
|
| 394 |
-
Distortion processor.
|
| 395 |
-
|
| 396 |
-
Processor parameters:
|
| 397 |
-
sample_rate (int): Current (assumed) sample rate of the audio.
|
| 398 |
-
mode (str): Currently supports the following five modes: hard_clip, waveshaper, soft_sine, tanh, bit_crusher.
|
| 399 |
-
Each mode has different parameters such as threshold, factor, or bits.
|
| 400 |
-
threshold (float): threshold
|
| 401 |
-
drive (float): drive
|
| 402 |
-
factor (float): factor
|
| 403 |
-
limit_range (float): limit range
|
| 404 |
-
bits (int): bits
|
| 405 |
-
"""
|
| 406 |
-
|
| 407 |
-
def __init__(self, sample_rates, name='Distortion', parameters=None, **kwargs):
|
| 408 |
-
"""
|
| 409 |
-
Initialize processor.
|
| 410 |
-
|
| 411 |
-
Args:
|
| 412 |
-
sample_rates (list of ints): sample rates of audio.
|
| 413 |
-
name (str): Name of processor.
|
| 414 |
-
parameters (parameter_list): Parameters for this processor.
|
| 415 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 416 |
-
"""
|
| 417 |
-
super().__init__(name, None, block_size=None, **kwargs)
|
| 418 |
-
if not parameters:
|
| 419 |
-
self.parameters = ParameterList()
|
| 420 |
-
self.parameters.add(Parameter('sample_rate', 44100, 'string', options=sample_rates))
|
| 421 |
-
self.parameters.add(Parameter('mode', 'hard_clip', 'string',
|
| 422 |
-
options=['hard_clip',
|
| 423 |
-
'overdrive',
|
| 424 |
-
'soft_sine',
|
| 425 |
-
'tanh',
|
| 426 |
-
'bit_crusher']))
|
| 427 |
-
self.parameters.add(Parameter('threshold', 0.0, 'float',
|
| 428 |
-
units='dB', maximum=0.0, minimum=-20.0))
|
| 429 |
-
self.parameters.add(Parameter('drive', 0.0, 'float',
|
| 430 |
-
units='dB', maximum=20.0, minimum=0.0))
|
| 431 |
-
self.parameters.add(Parameter('colour', 20.0, 'float',
|
| 432 |
-
maximum=100.0, minimum=0.0))
|
| 433 |
-
self.parameters.add(Parameter('bits', 12, 'int',
|
| 434 |
-
maximum=12, minimum=8))
|
| 435 |
-
else:
|
| 436 |
-
self.parameters = parameters
|
| 437 |
-
|
| 438 |
-
def process(self, x):
|
| 439 |
-
"""
|
| 440 |
-
Process audio.
|
| 441 |
-
|
| 442 |
-
Args:
|
| 443 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 444 |
-
|
| 445 |
-
Returns:
|
| 446 |
-
(Numpy array): distorted audio of size `n_samples x n_channels`.
|
| 447 |
-
"""
|
| 448 |
-
if self.parameters.mode.value == 'hard_clip':
|
| 449 |
-
y = hard_clip(x, self.parameters.threshold.value, self.parameters.drive.value)
|
| 450 |
-
elif self.parameters.mode.value == 'overdrive':
|
| 451 |
-
y = overdrive(x, self.parameters.drive.value,
|
| 452 |
-
self.parameters.colour.value, self.parameters.sample_rate.value)
|
| 453 |
-
elif self.parameters.mode.value == 'soft_sine':
|
| 454 |
-
y = soft_sine(x, self.parameters.drive.value)
|
| 455 |
-
elif self.parameters.mode.value == 'tanh':
|
| 456 |
-
y = hyperbolic_tangent(x, self.parameters.drive.value)
|
| 457 |
-
elif self.parameters.mode.value == 'bit_crusher':
|
| 458 |
-
y = bit_crusher(x, self.parameters.bits.value)
|
| 459 |
-
|
| 460 |
-
return y
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% EQUALISER %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 464 |
-
class Equaliser(Processor):
|
| 465 |
-
"""
|
| 466 |
-
Five band parametric equaliser (two shelves and three central bands).
|
| 467 |
-
|
| 468 |
-
All gains are set in dB values and range from `MIN_GAIN` dB to `MAX_GAIN` dB.
|
| 469 |
-
This processor is implemented as cascade of five biquad IIR filters
|
| 470 |
-
that are implemented using the infamous cookbook formulae from RBJ.
|
| 471 |
-
|
| 472 |
-
Processor parameters:
|
| 473 |
-
sample_rate (int): Current (assumed) sample rate of the audio.
|
| 474 |
-
low_shelf_gain (float), low_shelf_freq (float)
|
| 475 |
-
first_band_gain (float), first_band_freq (float), first_band_q (float)
|
| 476 |
-
second_band_gain (float), second_band_freq (float), second_band_q (float)
|
| 477 |
-
third_band_gain (float), third_band_freq (float), third_band_q (float)
|
| 478 |
-
|
| 479 |
-
original from https://github.com/csteinmetz1/pymixconsole/blob/master/pymixconsole/processors/equaliser.py
|
| 480 |
-
"""
|
| 481 |
-
|
| 482 |
-
def __init__(self, n_channels, sample_rates, gain_range=(-15.0, 15.0), q_range=(0.1, 2.0), hard_clip=False,
|
| 483 |
-
name='Equaliser', parameters=None, **kwargs):
|
| 484 |
-
"""
|
| 485 |
-
Initialize processor.
|
| 486 |
-
|
| 487 |
-
Args:
|
| 488 |
-
n_channels (int): Number of audio channels.
|
| 489 |
-
sample_rates (list of ints): Sample rates of audio.
|
| 490 |
-
gain_range (tuple of floats): minimum and maximum gain that can be used.
|
| 491 |
-
q_range (tuple of floats): minimum and maximum q value.
|
| 492 |
-
hard_clip (bool): Whether we clip to [-1, 1.] after processing.
|
| 493 |
-
name (str): Name of processor.
|
| 494 |
-
parameters (parameter_list): Parameters for this processor.
|
| 495 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 496 |
-
"""
|
| 497 |
-
super().__init__(name, parameters=parameters, block_size=None, **kwargs)
|
| 498 |
-
|
| 499 |
-
self.n_channels = n_channels
|
| 500 |
-
|
| 501 |
-
MIN_GAIN, MAX_GAIN = gain_range
|
| 502 |
-
MIN_Q, MAX_Q = q_range
|
| 503 |
-
|
| 504 |
-
if not parameters:
|
| 505 |
-
self.parameters = ParameterList()
|
| 506 |
-
self.parameters.add(Parameter('sample_rate', 44100, 'string', options=sample_rates))
|
| 507 |
-
# low shelf parameters -------
|
| 508 |
-
self.parameters.add(Parameter('low_shelf_gain', 0.0, 'float', minimum=MIN_GAIN, maximum=MAX_GAIN))
|
| 509 |
-
self.parameters.add(Parameter('low_shelf_freq', 80.0, 'float', minimum=30.0, maximum=200.0))
|
| 510 |
-
# first band parameters ------
|
| 511 |
-
self.parameters.add(Parameter('first_band_gain', 0.0, 'float', minimum=MIN_GAIN, maximum=MAX_GAIN))
|
| 512 |
-
self.parameters.add(Parameter('first_band_freq', 400.0, 'float', minimum=200.0, maximum=1000.0))
|
| 513 |
-
self.parameters.add(Parameter('first_band_q', 0.7, 'float', minimum=MIN_Q, maximum=MAX_Q))
|
| 514 |
-
# second band parameters -----
|
| 515 |
-
self.parameters.add(Parameter('second_band_gain', 0.0, 'float', minimum=MIN_GAIN, maximum=MAX_GAIN))
|
| 516 |
-
self.parameters.add(Parameter('second_band_freq', 2000.0, 'float', minimum=1000.0, maximum=3000.0))
|
| 517 |
-
self.parameters.add(Parameter('second_band_q', 0.7, 'float', minimum=MIN_Q, maximum=MAX_Q))
|
| 518 |
-
# third band parameters ------
|
| 519 |
-
self.parameters.add(Parameter('third_band_gain', 0.0, 'float', minimum=MIN_GAIN, maximum=MAX_GAIN))
|
| 520 |
-
self.parameters.add(Parameter('third_band_freq', 4000.0, 'float', minimum=3000.0, maximum=8000.0))
|
| 521 |
-
self.parameters.add(Parameter('third_band_q', 0.7, 'float', minimum=MIN_Q, maximum=MAX_Q))
|
| 522 |
-
# high shelf parameters ------
|
| 523 |
-
self.parameters.add(Parameter('high_shelf_gain', 0.0, 'float', minimum=MIN_GAIN, maximum=MAX_GAIN))
|
| 524 |
-
self.parameters.add(Parameter('high_shelf_freq', 8000.0, 'float', minimum=5000.0, maximum=10000.0))
|
| 525 |
-
else:
|
| 526 |
-
self.parameters = parameters
|
| 527 |
-
|
| 528 |
-
self.bands = ['low_shelf', 'first_band', 'second_band', 'third_band', 'high_shelf']
|
| 529 |
-
self.filters = self.setup_filters()
|
| 530 |
-
self.hard_clip = hard_clip
|
| 531 |
-
|
| 532 |
-
def setup_filters(self):
|
| 533 |
-
"""
|
| 534 |
-
Create IIR filters.
|
| 535 |
-
|
| 536 |
-
Returns:
|
| 537 |
-
IIR filters
|
| 538 |
-
"""
|
| 539 |
-
filters = {}
|
| 540 |
-
|
| 541 |
-
for band in self.bands:
|
| 542 |
-
|
| 543 |
-
G = getattr(self.parameters, band + '_gain').value
|
| 544 |
-
fc = getattr(self.parameters, band + '_freq').value
|
| 545 |
-
rate = self.parameters.sample_rate.value
|
| 546 |
-
|
| 547 |
-
if band in ['low_shelf', 'high_shelf']:
|
| 548 |
-
Q = 0.707
|
| 549 |
-
filter_type = band
|
| 550 |
-
else:
|
| 551 |
-
Q = getattr(self.parameters, band + '_q').value
|
| 552 |
-
filter_type = 'peaking'
|
| 553 |
-
|
| 554 |
-
filters[band] = pymc.components.iirfilter.IIRfilter(G, Q, fc, rate, filter_type, n_channels=self.n_channels)
|
| 555 |
-
|
| 556 |
-
return filters
|
| 557 |
-
|
| 558 |
-
def update_filter(self, band):
|
| 559 |
-
"""
|
| 560 |
-
Update filters.
|
| 561 |
-
|
| 562 |
-
Args:
|
| 563 |
-
band (str): Band that should be updated.
|
| 564 |
-
"""
|
| 565 |
-
self.filters[band].G = getattr(self.parameters, band + '_gain').value
|
| 566 |
-
self.filters[band].fc = getattr(self.parameters, band + '_freq').value
|
| 567 |
-
self.filters[band].rate = self.parameters.sample_rate.value
|
| 568 |
-
|
| 569 |
-
if band in ['first_band', 'second_band', 'third_band']:
|
| 570 |
-
self.filters[band].Q = getattr(self.parameters, band + '_q').value
|
| 571 |
-
|
| 572 |
-
def update(self, parameter_name=None):
|
| 573 |
-
"""
|
| 574 |
-
Update processor after randomization of parameters.
|
| 575 |
-
|
| 576 |
-
Args:
|
| 577 |
-
parameter_name (str): Parameter whose value has changed.
|
| 578 |
-
"""
|
| 579 |
-
if parameter_name is not None:
|
| 580 |
-
bands = ['_'.join(parameter_name.split('_')[:2])]
|
| 581 |
-
else:
|
| 582 |
-
bands = self.bands
|
| 583 |
-
|
| 584 |
-
for band in bands:
|
| 585 |
-
self.update_filter(band)
|
| 586 |
-
|
| 587 |
-
def process(self, x):
|
| 588 |
-
"""
|
| 589 |
-
Process audio.
|
| 590 |
-
|
| 591 |
-
Args:
|
| 592 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 593 |
-
|
| 594 |
-
Returns:
|
| 595 |
-
(Numpy array): equalized audio of size `n_samples x n_channels`.
|
| 596 |
-
"""
|
| 597 |
-
for _band, iirfilter in self.filters.items():
|
| 598 |
-
iirfilter.reset_state()
|
| 599 |
-
x = iirfilter.apply_filter(x)
|
| 600 |
-
|
| 601 |
-
if self.hard_clip:
|
| 602 |
-
x = np.clip(x, -1.0, 1.0)
|
| 603 |
-
|
| 604 |
-
# make sure that we have float32 as IIR filtering returns float64
|
| 605 |
-
x = x.astype(np.float32)
|
| 606 |
-
|
| 607 |
-
# make sure that we have two dimensions (if `n_channels == 1`)
|
| 608 |
-
if x.ndim == 1:
|
| 609 |
-
x = x[:, np.newaxis]
|
| 610 |
-
|
| 611 |
-
return x
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% COMPRESSOR %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 615 |
-
@jit(nopython=True)
|
| 616 |
-
def compressor_process(x, threshold, attack_time, release_time, ratio, makeup_gain, sample_rate):
|
| 617 |
-
"""
|
| 618 |
-
Apply compressor.
|
| 619 |
-
|
| 620 |
-
Args:
|
| 621 |
-
x (Numpy array): audio data.
|
| 622 |
-
threshold: threshold in dB.
|
| 623 |
-
attack_time: attack_time in ms.
|
| 624 |
-
release_time: release_time in ms.
|
| 625 |
-
ratio: ratio.
|
| 626 |
-
makeup_gain: makeup_gain.
|
| 627 |
-
sample_rate: sample rate.
|
| 628 |
-
|
| 629 |
-
Returns:
|
| 630 |
-
compressed audio.
|
| 631 |
-
"""
|
| 632 |
-
M = x.shape[0]
|
| 633 |
-
x_g = np.zeros(M)
|
| 634 |
-
x_l = np.zeros(M)
|
| 635 |
-
y_g = np.zeros(M)
|
| 636 |
-
y_l = np.zeros(M)
|
| 637 |
-
c = np.zeros(M)
|
| 638 |
-
yL_prev = 0.
|
| 639 |
-
|
| 640 |
-
alpha_attack = np.exp(-1/(0.001 * sample_rate * attack_time))
|
| 641 |
-
alpha_release = np.exp(-1/(0.001 * sample_rate * release_time))
|
| 642 |
-
|
| 643 |
-
for i in np.arange(M):
|
| 644 |
-
if np.abs(x[i]) < 0.000001:
|
| 645 |
-
x_g[i] = -120.0
|
| 646 |
-
else:
|
| 647 |
-
x_g[i] = 20 * np.log10(np.abs(x[i]))
|
| 648 |
-
|
| 649 |
-
if x_g[i] >= threshold:
|
| 650 |
-
y_g[i] = threshold + (x_g[i] - threshold) / ratio
|
| 651 |
-
else:
|
| 652 |
-
y_g[i] = x_g[i]
|
| 653 |
-
|
| 654 |
-
x_l[i] = x_g[i] - y_g[i]
|
| 655 |
-
|
| 656 |
-
if x_l[i] > yL_prev:
|
| 657 |
-
y_l[i] = alpha_attack * yL_prev + (1 - alpha_attack) * x_l[i]
|
| 658 |
-
else:
|
| 659 |
-
y_l[i] = alpha_release * yL_prev + (1 - alpha_release) * x_l[i]
|
| 660 |
-
|
| 661 |
-
c[i] = np.power(10.0, (makeup_gain - y_l[i]) / 20.0)
|
| 662 |
-
yL_prev = y_l[i]
|
| 663 |
-
|
| 664 |
-
y = x * c
|
| 665 |
-
|
| 666 |
-
return y
|
| 667 |
-
|
| 668 |
-
|
| 669 |
-
class Compressor(Processor):
|
| 670 |
-
"""
|
| 671 |
-
Single band stereo dynamic range compressor.
|
| 672 |
-
|
| 673 |
-
Processor parameters:
|
| 674 |
-
sample_rate (int): Current (assumed) sample rate of the audio.
|
| 675 |
-
threshold (float)
|
| 676 |
-
attack_time (float)
|
| 677 |
-
release_time (float)
|
| 678 |
-
ratio (float)
|
| 679 |
-
makeup_gain (float)
|
| 680 |
-
"""
|
| 681 |
-
|
| 682 |
-
def __init__(self, sample_rates, name='Compressor', parameters=None, **kwargs):
|
| 683 |
-
"""
|
| 684 |
-
Initialize processor.
|
| 685 |
-
|
| 686 |
-
Args:
|
| 687 |
-
sample_rates (list of ints): Sample rates of input audio.
|
| 688 |
-
name (str): Name of processor.
|
| 689 |
-
parameters (parameter_list): Parameters for this processor.
|
| 690 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 691 |
-
"""
|
| 692 |
-
super().__init__(name=name, parameters=parameters, block_size=None, **kwargs)
|
| 693 |
-
|
| 694 |
-
if not parameters:
|
| 695 |
-
self.parameters = ParameterList()
|
| 696 |
-
self.parameters.add(Parameter('sample_rate', 44100, 'string', options=sample_rates))
|
| 697 |
-
self.parameters.add(Parameter('threshold', 0.0, 'float', units='dB', minimum=-40.0, maximum=0.0))
|
| 698 |
-
self.parameters.add(Parameter('attack_time', 2.0, 'float', units='ms', minimum=0.03, maximum=30.0))
|
| 699 |
-
self.parameters.add(Parameter('release_time', 50.0, 'float', units='ms', minimum=50.0, maximum=100.0))
|
| 700 |
-
self.parameters.add(Parameter('ratio', 2.0, 'float', minimum=2.0, maximum=10.0))
|
| 701 |
-
self.parameters.add(Parameter('makeup_gain', 0.0, 'float', units='dB', minimum=-3.0, maximum=6.0))
|
| 702 |
-
else:
|
| 703 |
-
self.parameters = parameters
|
| 704 |
-
|
| 705 |
-
def process(self, x):
|
| 706 |
-
"""
|
| 707 |
-
Process audio.
|
| 708 |
-
|
| 709 |
-
Args:
|
| 710 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 711 |
-
|
| 712 |
-
Returns:
|
| 713 |
-
(Numpy array): compressed audio of size `n_samples x n_channels`.
|
| 714 |
-
"""
|
| 715 |
-
if not self.parameters.threshold.value == 0.0:
|
| 716 |
-
y = np.zeros_like(x)
|
| 717 |
-
|
| 718 |
-
for ch in range(x.shape[1]):
|
| 719 |
-
y[:, ch] = compressor_process(x[:, ch],
|
| 720 |
-
self.parameters.threshold.value,
|
| 721 |
-
self.parameters.attack_time.value,
|
| 722 |
-
self.parameters.release_time.value,
|
| 723 |
-
self.parameters.ratio.value,
|
| 724 |
-
self.parameters.makeup_gain.value,
|
| 725 |
-
self.parameters.sample_rate.value)
|
| 726 |
-
else:
|
| 727 |
-
y = x
|
| 728 |
-
|
| 729 |
-
return y
|
| 730 |
-
|
| 731 |
-
|
| 732 |
-
# %%%%%%%%%%%%%%%%%%%%%%%%%% CONVOLUTIONAL REVERB %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 733 |
-
class ConvolutionalReverb(Processor):
|
| 734 |
-
"""
|
| 735 |
-
Convolutional Reverb.
|
| 736 |
-
|
| 737 |
-
Important: Due to convolving the audio sequence with some impulse response, we should ignore the
|
| 738 |
-
first/last samples of the augmented audio sequence using `config['AUGMENTER_PADDING']`.
|
| 739 |
-
|
| 740 |
-
Processor parameters:
|
| 741 |
-
sample_rate (int): Current (assumed) sample rate of the audio.
|
| 742 |
-
wet_dry (float): Wet/dry ratio.
|
| 743 |
-
decay (float): Applies a fade out to the impulse response.
|
| 744 |
-
pre_delay (float): Value in ms. Shifts the IR in time and allows.
|
| 745 |
-
A positive value produces a traditional delay between the dry signal and the wet.
|
| 746 |
-
A negative delay is, in reality, zero delay, but effectively trims off the start of IR,
|
| 747 |
-
so the reverb response begins at a point further in.
|
| 748 |
-
"""
|
| 749 |
-
|
| 750 |
-
def __init__(self, impulse_responses, sample_rates, name='ConvolutionalReverb', parameters=None, **kwargs):
|
| 751 |
-
"""
|
| 752 |
-
Initialize processor.
|
| 753 |
-
|
| 754 |
-
Args:
|
| 755 |
-
impulse_responses (list): List with impulse responses created by `common_dataprocessing.create_dataset`
|
| 756 |
-
sample_rates (list of ints): Sample rates that we should assume (used for fade-out computation)
|
| 757 |
-
name (str): Name of processor.
|
| 758 |
-
parameters (parameter_list): Parameters for this processor.
|
| 759 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 760 |
-
|
| 761 |
-
Raises:
|
| 762 |
-
ValueError: if no impulse responses are provided.
|
| 763 |
-
"""
|
| 764 |
-
super().__init__(name=name, parameters=parameters, block_size=None, **kwargs)
|
| 765 |
-
|
| 766 |
-
if impulse_responses is None:
|
| 767 |
-
raise ValueError('List of impulse responses must be provided for ConvolutionalReverb processor.')
|
| 768 |
-
self.impulse_responses = impulse_responses
|
| 769 |
-
|
| 770 |
-
if not parameters:
|
| 771 |
-
self.parameters = ParameterList()
|
| 772 |
-
self.parameters.add(Parameter('sample_rate', 44100, 'string', options=sample_rates))
|
| 773 |
-
self.parameters.add(Parameter('index', 0, 'int', minimum=0, maximum=len(impulse_responses)))
|
| 774 |
-
self.parameters.add(Parameter('wet_dry', 1.0, 'float', minimum=0.1, maximum=1.0))
|
| 775 |
-
self.parameters.add(Parameter('decay', 1.0, 'float', minimum=0.1, maximum=1.0))
|
| 776 |
-
self.parameters.add(Parameter('pre_delay', 0, 'int', units='ms', minimum=-100, maximum=100))
|
| 777 |
-
else:
|
| 778 |
-
self.parameters = parameters
|
| 779 |
-
|
| 780 |
-
def update(self, parameter_name=None):
|
| 781 |
-
"""
|
| 782 |
-
Update processor after randomization of parameters.
|
| 783 |
-
|
| 784 |
-
Args:
|
| 785 |
-
parameter_name (str): Parameter whose value has changed.
|
| 786 |
-
"""
|
| 787 |
-
# copy IR from current index (to avoid modifying it in-place)
|
| 788 |
-
self.h = np.copy(sample_data(self.impulse_responses.loc[self.parameters.index.value]['impulse_response']))
|
| 789 |
-
|
| 790 |
-
# fade out the impulse based on the decay setting (starting from peak value) - constant 20ms fade-out
|
| 791 |
-
if self.parameters.decay.value < 1.:
|
| 792 |
-
idx_peak = np.argmax(np.max(np.abs(self.h), axis=1), axis=0)
|
| 793 |
-
fstart = np.minimum(self.h.shape[0],
|
| 794 |
-
idx_peak + int(self.parameters.decay.value * (self.h.shape[0] - idx_peak)))
|
| 795 |
-
fstop = np.minimum(self.h.shape[0], fstart + int(0.020*self.parameters.sample_rate.value))
|
| 796 |
-
flen = fstop - fstart
|
| 797 |
-
|
| 798 |
-
fade = np.arange(1, flen+1, dtype=self.dtype)/flen
|
| 799 |
-
fade = np.power(0.1, fade * 5)
|
| 800 |
-
self.h[fstart:fstop, :] *= fade[:, np.newaxis]
|
| 801 |
-
self.h = self.h[:fstop]
|
| 802 |
-
|
| 803 |
-
def process(self, x):
|
| 804 |
-
"""
|
| 805 |
-
Process audio.
|
| 806 |
-
|
| 807 |
-
Args:
|
| 808 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 809 |
-
|
| 810 |
-
Returns:
|
| 811 |
-
(Numpy array): reverbed audio of size `n_samples x n_channels`.
|
| 812 |
-
"""
|
| 813 |
-
# reshape IR to the correct size
|
| 814 |
-
n_channels = x.shape[1]
|
| 815 |
-
if self.h.shape[1] == 1 and n_channels > 1:
|
| 816 |
-
self.h = np.hstack([self.h] * n_channels) # repeat mono IR for multi-channel input
|
| 817 |
-
if self.h.shape[1] > 1 and n_channels == 1:
|
| 818 |
-
self.h = self.h[:, np.random.randint(self.h.shape[1]), np.newaxis] # randomly choose one IR channel
|
| 819 |
-
|
| 820 |
-
if self.parameters.wet_dry.value == 0.0:
|
| 821 |
-
return x
|
| 822 |
-
else:
|
| 823 |
-
# perform convolution to get wet signal
|
| 824 |
-
y = oaconvolve(x, self.h, mode='full', axes=0)
|
| 825 |
-
|
| 826 |
-
# cut out wet signal (compensating for the delay that the IR is introducing + predelay)
|
| 827 |
-
idx = np.argmax(np.max(np.abs(self.h), axis=1), axis=0)
|
| 828 |
-
idx += int(0.001 * self.parameters.pre_delay.value * self.parameters.sample_rate.value)
|
| 829 |
-
|
| 830 |
-
idx = np.clip(idx, 0, self.h.shape[0]-1)
|
| 831 |
-
|
| 832 |
-
y = y[idx:idx+x.shape[0], :]
|
| 833 |
-
|
| 834 |
-
# return weighted sum of dry and wet signal
|
| 835 |
-
return (1.0 - self.parameters.wet_dry.value) * x + self.parameters.wet_dry.value * y
|
| 836 |
-
|
| 837 |
-
|
| 838 |
-
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%% HAAS EFFECT %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 839 |
-
def haas_process(x, delay, feedback, wet_channel):
|
| 840 |
-
"""
|
| 841 |
-
Add Haas effect to audio.
|
| 842 |
-
|
| 843 |
-
Args:
|
| 844 |
-
x (Numpy array): input audio.
|
| 845 |
-
delay: Delay that we apply to one of the channels (in samples).
|
| 846 |
-
feedback: Feedback value.
|
| 847 |
-
wet_channel: Which channel we process (`left` or `right`).
|
| 848 |
-
|
| 849 |
-
Returns:
|
| 850 |
-
(Numpy array): Audio with Haas effect.
|
| 851 |
-
"""
|
| 852 |
-
y = np.copy(x)
|
| 853 |
-
if wet_channel == 'left':
|
| 854 |
-
y[:, 0] += feedback * np.roll(x[:, 0], delay)
|
| 855 |
-
elif wet_channel == 'right':
|
| 856 |
-
y[:, 1] += feedback * np.roll(x[:, 1], delay)
|
| 857 |
-
|
| 858 |
-
return y
|
| 859 |
-
|
| 860 |
-
|
| 861 |
-
class Haas(Processor):
|
| 862 |
-
"""
|
| 863 |
-
Haas Effect Processor.
|
| 864 |
-
|
| 865 |
-
Randomly selects one channel and applies a short delay to it.
|
| 866 |
-
|
| 867 |
-
Important: This audio effect uses `np.roll` to perform the shift of one channel. Hence, you should use
|
| 868 |
-
`config['AUGMENTER_PADDING']` to ignore the first samples of the augmented audio sequence.
|
| 869 |
-
|
| 870 |
-
Processor parameters:
|
| 871 |
-
sample_rate (int): Current (assumed) sample rate of the audio.
|
| 872 |
-
delay (float)
|
| 873 |
-
feedback (float)
|
| 874 |
-
wet_channel (string)
|
| 875 |
-
"""
|
| 876 |
-
|
| 877 |
-
def __init__(self, sample_rates, delay_range=(-0.040, 0.040), name='Haas', parameters=None, **kwargs):
|
| 878 |
-
"""
|
| 879 |
-
Initialize processor.
|
| 880 |
-
|
| 881 |
-
Args:
|
| 882 |
-
sample_rates (list of ints): Sample rates of input audio.
|
| 883 |
-
delay_range (tuple of floats): minimum/maximum delay in milliseconds for Haas effect.
|
| 884 |
-
name (str): Name of processor.
|
| 885 |
-
parameters (parameter_list): Parameters for this processor.
|
| 886 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 887 |
-
"""
|
| 888 |
-
super().__init__(name=name, parameters=parameters, block_size=None, **kwargs)
|
| 889 |
-
|
| 890 |
-
if not parameters:
|
| 891 |
-
self.parameters = ParameterList()
|
| 892 |
-
self.parameters.add(Parameter('sample_rate', 44100, 'string', options=sample_rates))
|
| 893 |
-
self.parameters.add(Parameter('delay', delay_range[1], 'float', units='ms',
|
| 894 |
-
minimum=delay_range[0], maximum=delay_range[1]))
|
| 895 |
-
self.parameters.add(Parameter('feedback', 0.35, 'float', minimum=0.33, maximum=0.66))
|
| 896 |
-
self.parameters.add(Parameter('wet_channel', 'left', 'string', options=['left', 'right']))
|
| 897 |
-
else:
|
| 898 |
-
self.parameters = parameters
|
| 899 |
-
|
| 900 |
-
def process(self, x):
|
| 901 |
-
"""
|
| 902 |
-
Process audio.
|
| 903 |
-
|
| 904 |
-
Args:
|
| 905 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 906 |
-
|
| 907 |
-
Returns:
|
| 908 |
-
(Numpy array): audio with Haas effect of size `n_samples x n_channels`.
|
| 909 |
-
"""
|
| 910 |
-
assert x.shape[1] == 1 or x.shape[1] == 2, 'Haas effect only works with monaural or stereo audio.'
|
| 911 |
-
|
| 912 |
-
if x.shape[1] < 2:
|
| 913 |
-
x = np.repeat(x, 2, axis=1)
|
| 914 |
-
|
| 915 |
-
y = haas_process(x, int(self.parameters.delay.value * self.parameters.sample_rate.value),
|
| 916 |
-
self.parameters.feedback.value, self.parameters.wet_channel.value)
|
| 917 |
-
|
| 918 |
-
return y
|
| 919 |
-
|
| 920 |
-
|
| 921 |
-
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% PANNER %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 922 |
-
class Panner(Processor):
|
| 923 |
-
"""
|
| 924 |
-
Simple stereo panner (adjusting amplitude and delay).
|
| 925 |
-
|
| 926 |
-
If input is mono, output is stereo.
|
| 927 |
-
Original edited from https://github.com/csteinmetz1/pymixconsole/blob/master/pymixconsole/processors/panner.py
|
| 928 |
-
|
| 929 |
-
Important: This audio effect uses `np.roll` to perform the shift of one channel. Hence, you should use
|
| 930 |
-
`config['AUGMENTER_PADDING']` to ignore the first samples of the augmented audio sequence.
|
| 931 |
-
|
| 932 |
-
Processor parameters:
|
| 933 |
-
sample_rate (int): Current (assumed) sample rate of the audio.
|
| 934 |
-
pan (float): Panning angle. Can take values in [0, 1] where `0` corresponds to fully panned to left
|
| 935 |
-
and `1` corresponds to fully panned to the right.
|
| 936 |
-
pan_law (str): Pan law to be used for amplitude panning. Can be '-4.5dB', 'linear' or 'constant_power'.
|
| 937 |
-
pan_mode (str): Scheme that is used for panning. Can be 'amplitude', 'delay' or 'both'.
|
| 938 |
-
pan_maxdelay (float): Maximum delay if we pan a source fully to the left/right.
|
| 939 |
-
For example `2. / 343.` is the maximum delay if the microphones are 2 meters apart.
|
| 940 |
-
"""
|
| 941 |
-
|
| 942 |
-
def __init__(self, sample_rates, maxdelay_range=(0, 2. / 343.0), name='Panner', parameters=None, **kwargs):
|
| 943 |
-
"""
|
| 944 |
-
Initialize processor.
|
| 945 |
-
|
| 946 |
-
Args:
|
| 947 |
-
sample_rates (list of ints): Sample rates of input audio.
|
| 948 |
-
maxdelay_range (tuple of floats): minimum/maximum delay for panning effect. `2. / 343.` corresponds to
|
| 949 |
-
the maximum delay if we have a microphone array where the microphones are 2 meters apart.
|
| 950 |
-
name (str): Name of processor.
|
| 951 |
-
parameters (parameter_list): Parameters for this processor.
|
| 952 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 953 |
-
"""
|
| 954 |
-
# default processor class constructor
|
| 955 |
-
super().__init__(name=name, parameters=parameters, block_size=None, **kwargs)
|
| 956 |
-
|
| 957 |
-
if not parameters:
|
| 958 |
-
self.parameters = ParameterList()
|
| 959 |
-
self.parameters.add(Parameter('sample_rate', 44100, 'string', options=sample_rates))
|
| 960 |
-
self.parameters.add(Parameter('pan', 0.5, 'float', minimum=0.1, maximum=0.9))
|
| 961 |
-
self.parameters.add(Parameter('pan_law', '-4.5dB', 'string',
|
| 962 |
-
options=['-4.5dB', 'linear', 'constant_power']))
|
| 963 |
-
self.parameters.add(Parameter('pan_mode', 'amplitude', 'string',
|
| 964 |
-
options=['amplitude', 'delay', 'both']))
|
| 965 |
-
self.parameters.add(Parameter('pan_maxdelay', (maxdelay_range[0] + maxdelay_range[1]) / 2., 'float',
|
| 966 |
-
units='ms', minimum=maxdelay_range[0], maximum=maxdelay_range[1]))
|
| 967 |
-
else:
|
| 968 |
-
self.parameters = parameters
|
| 969 |
-
|
| 970 |
-
# setup the coefficents based on default params
|
| 971 |
-
self.update()
|
| 972 |
-
|
| 973 |
-
def _calculate_pan_coefficents(self):
|
| 974 |
-
"""
|
| 975 |
-
Calculate panning coefficients from the chosen pan law.
|
| 976 |
-
|
| 977 |
-
Based on the set pan law determine the gain value
|
| 978 |
-
to apply for the left and right channel to achieve panning effect.
|
| 979 |
-
This operates on the assumption that the input channel is mono.
|
| 980 |
-
The output data will be stereo at the moment, but could be expanded
|
| 981 |
-
to a higher channel count format.
|
| 982 |
-
The panning value is in the range [0, 1], where
|
| 983 |
-
0 means the signal is panned completely to the left, and
|
| 984 |
-
1 means the signal is apanned copletely to the right.
|
| 985 |
-
|
| 986 |
-
Raises:
|
| 987 |
-
ValueError: `self.parameters.pan_law` is not supported.
|
| 988 |
-
"""
|
| 989 |
-
self.gains = np.zeros(2, dtype=self.dtype)
|
| 990 |
-
|
| 991 |
-
# first scale the linear [0, 1] to [0, pi/2]
|
| 992 |
-
theta = self.parameters.pan.value * (np.pi/2)
|
| 993 |
-
|
| 994 |
-
if self.parameters.pan_law.value == 'linear':
|
| 995 |
-
self.gains[0] = ((np.pi/2) - theta) * (2/np.pi)
|
| 996 |
-
self.gains[1] = theta * (2/np.pi)
|
| 997 |
-
elif self.parameters.pan_law.value == 'constant_power':
|
| 998 |
-
self.gains[0] = np.cos(theta)
|
| 999 |
-
self.gains[1] = np.sin(theta)
|
| 1000 |
-
elif self.parameters.pan_law.value == '-4.5dB':
|
| 1001 |
-
self.gains[0] = np.sqrt(((np.pi/2) - theta) * (2/np.pi) * np.cos(theta))
|
| 1002 |
-
self.gains[1] = np.sqrt(theta * (2/np.pi) * np.sin(theta))
|
| 1003 |
-
else:
|
| 1004 |
-
raise ValueError(f'Invalid pan_law {self.parameters.pan_law.value}.')
|
| 1005 |
-
|
| 1006 |
-
def _calculate_pan_delay(self):
|
| 1007 |
-
"""Calculate delay for the chosen pan angle."""
|
| 1008 |
-
self.shifts = np.zeros(2, dtype=np.int32)
|
| 1009 |
-
|
| 1010 |
-
# compute overall shift that we need between the two channels
|
| 1011 |
-
pan_maxdelay_samples = self.parameters.pan_maxdelay.value * self.parameters.sample_rate.value
|
| 1012 |
-
shift = 2 * int(pan_maxdelay_samples * np.abs(self.parameters.pan.value - 0.5))
|
| 1013 |
-
|
| 1014 |
-
if self.parameters.pan.value < 0.5:
|
| 1015 |
-
# panning to the left
|
| 1016 |
-
self.shifts[0] = -shift // 2
|
| 1017 |
-
self.shifts[1] = shift - shift // 2
|
| 1018 |
-
else:
|
| 1019 |
-
self.shifts[0] = shift - shift // 2
|
| 1020 |
-
self.shifts[1] = -shift // 2
|
| 1021 |
-
|
| 1022 |
-
def process(self, x):
|
| 1023 |
-
"""
|
| 1024 |
-
Process audio.
|
| 1025 |
-
|
| 1026 |
-
Args:
|
| 1027 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 1028 |
-
|
| 1029 |
-
Returns:
|
| 1030 |
-
(Numpy array): panned audio of size `n_samples x n_channels`.
|
| 1031 |
-
"""
|
| 1032 |
-
assert x.shape[1] == 1 or x.shape[1] == 2, 'Panner only works with monaural or stereo audio.'
|
| 1033 |
-
|
| 1034 |
-
# convert to stereo if signal is monaural
|
| 1035 |
-
if x.shape[1] < 2:
|
| 1036 |
-
x = np.repeat(x, 2, axis=1)
|
| 1037 |
-
|
| 1038 |
-
y = np.copy(x)
|
| 1039 |
-
if self.parameters.pan_mode.value in ['delay', 'both']:
|
| 1040 |
-
y[:, 0] = np.roll(y[:, 0], self.shifts[0])
|
| 1041 |
-
y[:, 1] = np.roll(y[:, 1], self.shifts[1])
|
| 1042 |
-
|
| 1043 |
-
if self.parameters.pan_mode.value in ['amplitude', 'both']:
|
| 1044 |
-
y *= self.gains
|
| 1045 |
-
|
| 1046 |
-
return y
|
| 1047 |
-
|
| 1048 |
-
def update(self, parameter_name=None):
|
| 1049 |
-
"""
|
| 1050 |
-
Update processor after randomization of parameters.
|
| 1051 |
-
|
| 1052 |
-
Args:
|
| 1053 |
-
parameter_name (str): Parameter whose value has changed.
|
| 1054 |
-
"""
|
| 1055 |
-
self._calculate_pan_coefficents()
|
| 1056 |
-
self._calculate_pan_delay()
|
| 1057 |
-
|
| 1058 |
-
|
| 1059 |
-
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% GAIN %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 1060 |
-
class Gain(Processor):
|
| 1061 |
-
"""
|
| 1062 |
-
Gain Processor.
|
| 1063 |
-
|
| 1064 |
-
Applies gain in dB and optionally inverts polarity.
|
| 1065 |
-
|
| 1066 |
-
Processor parameters:
|
| 1067 |
-
gain (float): Gain that should be applied (dB scale).
|
| 1068 |
-
invert (bool): If True, then we also invert the waveform (all channels jointly).
|
| 1069 |
-
"""
|
| 1070 |
-
|
| 1071 |
-
def __init__(self, name='Gain', parameters=None, **kwargs):
|
| 1072 |
-
"""
|
| 1073 |
-
Initialize processor.
|
| 1074 |
-
|
| 1075 |
-
Args:
|
| 1076 |
-
name (str): Name of processor.
|
| 1077 |
-
parameters (parameter_list): Parameters for this processor.
|
| 1078 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 1079 |
-
"""
|
| 1080 |
-
super().__init__(name, parameters=parameters, block_size=None, **kwargs)
|
| 1081 |
-
|
| 1082 |
-
if not parameters:
|
| 1083 |
-
self.parameters = ParameterList()
|
| 1084 |
-
self.parameters.add(Parameter('gain', 1.0, 'float', units='dB', minimum=-6.0, maximum=6.0))
|
| 1085 |
-
self.parameters.add(Parameter('invert', False, 'bool'))
|
| 1086 |
-
else:
|
| 1087 |
-
self.parameters = parameters
|
| 1088 |
-
|
| 1089 |
-
def process(self, x):
|
| 1090 |
-
"""
|
| 1091 |
-
Process audio.
|
| 1092 |
-
|
| 1093 |
-
Args:
|
| 1094 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 1095 |
-
|
| 1096 |
-
Returns:
|
| 1097 |
-
(Numpy array): gain-augmented audio of size `n_samples x n_channels`.
|
| 1098 |
-
"""
|
| 1099 |
-
gain = 10 ** (self.parameters.gain.value / 20.)
|
| 1100 |
-
if self.parameters.invert.value:
|
| 1101 |
-
gain = -gain
|
| 1102 |
-
return gain * x
|
| 1103 |
-
|
| 1104 |
-
|
| 1105 |
-
# %%%%%%%%%%%%%%%%%%%%%%% SIMPLE CHANNEL SWAP %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 1106 |
-
class SwapChannels(Processor):
|
| 1107 |
-
"""
|
| 1108 |
-
Swap channels in multi-channel audio.
|
| 1109 |
-
|
| 1110 |
-
Processor parameters:
|
| 1111 |
-
index (int) Selects the permutation that we are using.
|
| 1112 |
-
Please note that "no permutation" is one of the permutations in `self.permutations` at index `0`.
|
| 1113 |
-
Hence, this effect should be applied with probability "1.".
|
| 1114 |
-
"""
|
| 1115 |
-
|
| 1116 |
-
def __init__(self, n_channels, name='SwapChannels', parameters=None, **kwargs):
|
| 1117 |
-
"""
|
| 1118 |
-
Initialize processor.
|
| 1119 |
-
|
| 1120 |
-
Args:
|
| 1121 |
-
n_channels (int): Number of channels in audio that we want to process.
|
| 1122 |
-
name (str): Name of processor.
|
| 1123 |
-
parameters (parameter_list): Parameters for this processor.
|
| 1124 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 1125 |
-
"""
|
| 1126 |
-
super().__init__(name=name, parameters=parameters, block_size=None, **kwargs)
|
| 1127 |
-
|
| 1128 |
-
self.permutations = tuple(permutations(range(n_channels), n_channels))
|
| 1129 |
-
|
| 1130 |
-
if not parameters:
|
| 1131 |
-
self.parameters = ParameterList()
|
| 1132 |
-
self.parameters.add(Parameter('index', 0, 'int', minimum=0, maximum=len(self.permutations)))
|
| 1133 |
-
else:
|
| 1134 |
-
self.parameters = parameters
|
| 1135 |
-
|
| 1136 |
-
def process(self, x):
|
| 1137 |
-
"""
|
| 1138 |
-
Process audio.
|
| 1139 |
-
|
| 1140 |
-
Args:
|
| 1141 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 1142 |
-
|
| 1143 |
-
Returns:
|
| 1144 |
-
(Numpy array): channel-swapped audio of size `n_samples x n_channels`.
|
| 1145 |
-
"""
|
| 1146 |
-
return x[:, self.permutations[self.parameters.index.value]]
|
| 1147 |
-
|
| 1148 |
-
|
| 1149 |
-
# %%%%%%%%%%%%%%%%%%%%%%% Monauralize %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 1150 |
-
class Monauralize(Processor):
|
| 1151 |
-
"""
|
| 1152 |
-
Monauralizes audio (i.e., removes spatial information).
|
| 1153 |
-
|
| 1154 |
-
Process parameters:
|
| 1155 |
-
seed_channel (int): channel that we use for overwriting the others.
|
| 1156 |
-
`-1` refers to using the mean over all channels.
|
| 1157 |
-
"""
|
| 1158 |
-
|
| 1159 |
-
def __init__(self, n_channels, name='Monauralize', parameters=None, **kwargs):
|
| 1160 |
-
"""
|
| 1161 |
-
Initialize processor.
|
| 1162 |
-
|
| 1163 |
-
Args:
|
| 1164 |
-
n_channels (int): Number of channels in audio that we want to process.
|
| 1165 |
-
name (str): Name of processor.
|
| 1166 |
-
parameters (parameter_list): Parameters for this processor.
|
| 1167 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 1168 |
-
"""
|
| 1169 |
-
super().__init__(name=name, parameters=parameters, block_size=None, **kwargs)
|
| 1170 |
-
|
| 1171 |
-
if not parameters:
|
| 1172 |
-
self.parameters = ParameterList()
|
| 1173 |
-
self.parameters.add(Parameter('seed_channel', 0, 'int', minimum=-1, maximum=n_channels))
|
| 1174 |
-
else:
|
| 1175 |
-
self.parameters = parameters
|
| 1176 |
-
|
| 1177 |
-
def process(self, x):
|
| 1178 |
-
"""
|
| 1179 |
-
Process audio.
|
| 1180 |
-
|
| 1181 |
-
Args:
|
| 1182 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 1183 |
-
|
| 1184 |
-
Returns:
|
| 1185 |
-
(Numpy array): monauralized audio of size `n_samples x n_channels`.
|
| 1186 |
-
"""
|
| 1187 |
-
tile_reps = (1, x.shape[1])
|
| 1188 |
-
if self.parameters.seed_channel.value == -1:
|
| 1189 |
-
# use average as seed
|
| 1190 |
-
return np.tile(np.mean(x, axis=1, keepdims=True), tile_reps)
|
| 1191 |
-
else:
|
| 1192 |
-
# use one channel as seed
|
| 1193 |
-
return np.tile(x[:, [self.parameters.seed_channel.value]], tile_reps)
|
| 1194 |
-
|
| 1195 |
-
|
| 1196 |
-
# %%%%%%%%%%%%%%%%%%%%%%% ChunkShuffle %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 1197 |
-
class ChunkShuffle(Processor):
|
| 1198 |
-
"""
|
| 1199 |
-
Split audio into chunks and randomly re-arrange it.
|
| 1200 |
-
|
| 1201 |
-
Process parameters:
|
| 1202 |
-
n_chunks (int): number of chunks into which we split
|
| 1203 |
-
"""
|
| 1204 |
-
|
| 1205 |
-
def __init__(self, name='ChunkShuffle', parameters=None, **kwargs):
|
| 1206 |
-
"""
|
| 1207 |
-
Initialize processor.
|
| 1208 |
-
|
| 1209 |
-
Args:
|
| 1210 |
-
name (str): Name of processor.
|
| 1211 |
-
parameters (parameter_list): Parameters for this processor.
|
| 1212 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 1213 |
-
"""
|
| 1214 |
-
super().__init__(name=name, parameters=parameters, block_size=None, **kwargs)
|
| 1215 |
-
|
| 1216 |
-
if not parameters:
|
| 1217 |
-
self.parameters = ParameterList()
|
| 1218 |
-
self.parameters.add(Parameter('n_chunks', 2, 'int', minimum=2, maximum=6))
|
| 1219 |
-
else:
|
| 1220 |
-
self.parameters = parameters
|
| 1221 |
-
|
| 1222 |
-
def process(self, x):
|
| 1223 |
-
"""
|
| 1224 |
-
Process audio.
|
| 1225 |
-
|
| 1226 |
-
Args:
|
| 1227 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 1228 |
-
|
| 1229 |
-
Returns:
|
| 1230 |
-
(Numpy array): chunk-shuffled audio of size `n_samples x n_channels`.
|
| 1231 |
-
"""
|
| 1232 |
-
# determine random chunk boundaries
|
| 1233 |
-
boundaries = np.sort(np.random.randint(x.shape[0], size=self.parameters.n_chunks.value-1))
|
| 1234 |
-
|
| 1235 |
-
# create chunks
|
| 1236 |
-
chunks = np.split(x, boundaries)
|
| 1237 |
-
|
| 1238 |
-
# shuffle them
|
| 1239 |
-
np.random.shuffle(chunks)
|
| 1240 |
-
|
| 1241 |
-
# return chunks in new random order
|
| 1242 |
-
return np.concatenate(chunks)
|
| 1243 |
-
|
| 1244 |
-
|
| 1245 |
-
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% PITCH SHIFT %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 1246 |
-
class PitchShift(Processor):
|
| 1247 |
-
"""
|
| 1248 |
-
Simple pitch shifter using SoX and soxbindings (https://github.com/pseeth/soxbindings).
|
| 1249 |
-
|
| 1250 |
-
Processor parameters:
|
| 1251 |
-
sample_rate (int): Current (assumed) sample rate of the audio.
|
| 1252 |
-
steps (float): Pitch shift as positive/negative semitones
|
| 1253 |
-
quick (bool): If True, this effect will run faster but with lower sound quality.
|
| 1254 |
-
"""
|
| 1255 |
-
|
| 1256 |
-
def __init__(self, sample_rates, fix_length=True, name='PitchShift', parameters=None, **kwargs):
|
| 1257 |
-
"""
|
| 1258 |
-
Initialize processor.
|
| 1259 |
-
|
| 1260 |
-
Args:
|
| 1261 |
-
sample_rates (list of ints): Sample rates of input audio.
|
| 1262 |
-
fix_length (bool): If True, then output has same length as input.
|
| 1263 |
-
name (str): Name of processor.
|
| 1264 |
-
parameters (parameter_list): Parameters for this processor.
|
| 1265 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 1266 |
-
"""
|
| 1267 |
-
super().__init__(name=name, parameters=parameters, block_size=None, **kwargs)
|
| 1268 |
-
|
| 1269 |
-
if not parameters:
|
| 1270 |
-
self.parameters = ParameterList()
|
| 1271 |
-
self.parameters.add(Parameter('sample_rate', 44100, 'string', options=sample_rates))
|
| 1272 |
-
self.parameters.add(Parameter('steps', 0.0, 'float', minimum=-6., maximum=6.))
|
| 1273 |
-
self.parameters.add(Parameter('quick', False, 'bool'))
|
| 1274 |
-
else:
|
| 1275 |
-
self.parameters = parameters
|
| 1276 |
-
|
| 1277 |
-
self.fix_length = fix_length
|
| 1278 |
-
self.clips = False
|
| 1279 |
-
|
| 1280 |
-
def process(self, x):
|
| 1281 |
-
"""
|
| 1282 |
-
Process audio.
|
| 1283 |
-
|
| 1284 |
-
Args:
|
| 1285 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 1286 |
-
|
| 1287 |
-
Returns:
|
| 1288 |
-
(Numpy array): pitch-shifted audio of size `n_samples x n_channels`.
|
| 1289 |
-
"""
|
| 1290 |
-
if self.parameters.steps.value == 0.0:
|
| 1291 |
-
y = x
|
| 1292 |
-
else:
|
| 1293 |
-
scale = np.max(np.abs(x))
|
| 1294 |
-
if scale > MAX_SOX_PROCESSING_PEAK:
|
| 1295 |
-
clips = True
|
| 1296 |
-
x = x * (MAX_SOX_PROCESSING_PEAK / scale)
|
| 1297 |
-
else:
|
| 1298 |
-
clips = False
|
| 1299 |
-
|
| 1300 |
-
tfm = sox.Transformer()
|
| 1301 |
-
tfm.pitch(self.parameters.steps.value, quick=bool(self.parameters.quick.value))
|
| 1302 |
-
y = tfm.build_array(input_array=x, sample_rate_in=self.parameters.sample_rate.value).astype(np.float32)
|
| 1303 |
-
|
| 1304 |
-
if clips:
|
| 1305 |
-
y *= scale / MAX_SOX_PROCESSING_PEAK # rescale output to original scale
|
| 1306 |
-
|
| 1307 |
-
if self.fix_length:
|
| 1308 |
-
n_samples_input = x.shape[0]
|
| 1309 |
-
n_samples_output = y.shape[0]
|
| 1310 |
-
if n_samples_input < n_samples_output:
|
| 1311 |
-
idx1 = (n_samples_output - n_samples_input) // 2
|
| 1312 |
-
idx2 = idx1 + n_samples_input
|
| 1313 |
-
y = y[idx1:idx2]
|
| 1314 |
-
elif n_samples_input > n_samples_output:
|
| 1315 |
-
n_pad = n_samples_input - n_samples_output
|
| 1316 |
-
y = np.pad(y, ((n_pad//2, n_pad - n_pad//2), (0, 0)))
|
| 1317 |
-
|
| 1318 |
-
return y
|
| 1319 |
-
|
| 1320 |
-
|
| 1321 |
-
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% TIME STRETCH %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 1322 |
-
class TimeStretch(Processor):
|
| 1323 |
-
"""
|
| 1324 |
-
Simple time stretcher using SoX and soxbindings (https://github.com/pseeth/soxbindings).
|
| 1325 |
-
|
| 1326 |
-
Processor parameters:
|
| 1327 |
-
sample_rate (int): Current (assumed) sample rate of the audio.
|
| 1328 |
-
factor (float): Time stretch factor.
|
| 1329 |
-
quick (bool): If True, this effect will run faster but with lower sound quality.
|
| 1330 |
-
stretch_type (str): Algorithm used for stretching (`tempo` or `stretch`).
|
| 1331 |
-
audio_type (str): Sets which time segments are most optmial when finding
|
| 1332 |
-
the best overlapping points for time stretching.
|
| 1333 |
-
"""
|
| 1334 |
-
|
| 1335 |
-
def __init__(self, sample_rates, fix_length=True, name='TimeStretch', parameters=None, **kwargs):
|
| 1336 |
-
"""
|
| 1337 |
-
Initialize processor.
|
| 1338 |
-
|
| 1339 |
-
Args:
|
| 1340 |
-
sample_rates (list of ints): Sample rates of input audio.
|
| 1341 |
-
fix_length (bool): If True, then output has same length as input.
|
| 1342 |
-
name (str): Name of processor.
|
| 1343 |
-
parameters (parameter_list): Parameters for this processor.
|
| 1344 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 1345 |
-
"""
|
| 1346 |
-
super().__init__(name=name, parameters=parameters, block_size=None, **kwargs)
|
| 1347 |
-
|
| 1348 |
-
if not parameters:
|
| 1349 |
-
self.parameters = ParameterList()
|
| 1350 |
-
self.parameters.add(Parameter('sample_rate', 44100, 'string', options=sample_rates))
|
| 1351 |
-
self.parameters.add(Parameter('factor', 1.0, 'float', minimum=1/1.33, maximum=1.33))
|
| 1352 |
-
self.parameters.add(Parameter('quick', False, 'bool'))
|
| 1353 |
-
self.parameters.add(Parameter('stretch_type', 'tempo', 'string', options=['tempo', 'stretch']))
|
| 1354 |
-
self.parameters.add(Parameter('audio_type', 'l', 'string', options=['m', 's', 'l']))
|
| 1355 |
-
else:
|
| 1356 |
-
self.parameters = parameters
|
| 1357 |
-
|
| 1358 |
-
self.fix_length = fix_length
|
| 1359 |
-
|
| 1360 |
-
def process(self, x):
|
| 1361 |
-
"""
|
| 1362 |
-
Process audio.
|
| 1363 |
-
|
| 1364 |
-
Args:
|
| 1365 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 1366 |
-
|
| 1367 |
-
Returns:
|
| 1368 |
-
(Numpy array): time-stretched audio of size `n_samples x n_channels`.
|
| 1369 |
-
"""
|
| 1370 |
-
if self.parameters.factor.value == 1.0:
|
| 1371 |
-
y = x
|
| 1372 |
-
else:
|
| 1373 |
-
scale = np.max(np.abs(x))
|
| 1374 |
-
if scale > MAX_SOX_PROCESSING_PEAK:
|
| 1375 |
-
clips = True
|
| 1376 |
-
x = x * (MAX_SOX_PROCESSING_PEAK / scale)
|
| 1377 |
-
else:
|
| 1378 |
-
clips = False
|
| 1379 |
-
|
| 1380 |
-
tfm = sox.Transformer()
|
| 1381 |
-
if self.parameters.stretch_type.value == 'stretch':
|
| 1382 |
-
tfm.stretch(self.parameters.factor.value)
|
| 1383 |
-
elif self.parameters.stretch_type.value == 'tempo':
|
| 1384 |
-
tfm.tempo(1/self.parameters.factor.value,
|
| 1385 |
-
audio_type=self.parameters.audio_type.value,
|
| 1386 |
-
quick=bool(self.parameters.quick.value))
|
| 1387 |
-
y = tfm.build_array(input_array=x, sample_rate_in=self.parameters.sample_rate.value).astype(np.float32)
|
| 1388 |
-
|
| 1389 |
-
if clips:
|
| 1390 |
-
y *= scale / MAX_SOX_PROCESSING_PEAK # rescale output to original scale
|
| 1391 |
-
|
| 1392 |
-
if self.fix_length:
|
| 1393 |
-
n_samples_input = x.shape[0]
|
| 1394 |
-
n_samples_output = y.shape[0]
|
| 1395 |
-
if n_samples_input < n_samples_output:
|
| 1396 |
-
idx1 = (n_samples_output - n_samples_input) // 2
|
| 1397 |
-
idx2 = idx1 + n_samples_input
|
| 1398 |
-
y = y[idx1:idx2]
|
| 1399 |
-
elif n_samples_input > n_samples_output:
|
| 1400 |
-
n_pad = n_samples_input - n_samples_output
|
| 1401 |
-
y = np.pad(y, ((n_pad//2, n_pad - n_pad//2), (0, 0)))
|
| 1402 |
-
|
| 1403 |
-
return y
|
| 1404 |
-
|
| 1405 |
-
|
| 1406 |
-
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% PLAYBACK SPEED %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 1407 |
-
class PlaybackSpeed(Processor):
|
| 1408 |
-
"""
|
| 1409 |
-
Simple playback speed effect using SoX and soxbindings (https://github.com/pseeth/soxbindings).
|
| 1410 |
-
|
| 1411 |
-
Processor parameters:
|
| 1412 |
-
sample_rate (int): Current (assumed) sample rate of the audio.
|
| 1413 |
-
factor (float): Playback speed factor.
|
| 1414 |
-
"""
|
| 1415 |
-
|
| 1416 |
-
def __init__(self, sample_rates, fix_length=True, name='PlaybackSpeed', parameters=None, **kwargs):
|
| 1417 |
-
"""
|
| 1418 |
-
Initialize processor.
|
| 1419 |
-
|
| 1420 |
-
Args:
|
| 1421 |
-
sample_rates (list of ints): Sample rates of input audio.
|
| 1422 |
-
fix_length (bool): If True, then output has same length as input.
|
| 1423 |
-
name (str): Name of processor.
|
| 1424 |
-
parameters (parameter_list): Parameters for this processor.
|
| 1425 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 1426 |
-
"""
|
| 1427 |
-
super().__init__(name=name, parameters=parameters, block_size=None, **kwargs)
|
| 1428 |
-
|
| 1429 |
-
if not parameters:
|
| 1430 |
-
self.parameters = ParameterList()
|
| 1431 |
-
self.parameters.add(Parameter('sample_rate', 44100, 'string', options=sample_rates))
|
| 1432 |
-
self.parameters.add(Parameter('factor', 1.0, 'float', minimum=1./1.33, maximum=1.33))
|
| 1433 |
-
else:
|
| 1434 |
-
self.parameters = parameters
|
| 1435 |
-
|
| 1436 |
-
self.fix_length = fix_length
|
| 1437 |
-
|
| 1438 |
-
def process(self, x):
|
| 1439 |
-
"""
|
| 1440 |
-
Process audio.
|
| 1441 |
-
|
| 1442 |
-
Args:
|
| 1443 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 1444 |
-
|
| 1445 |
-
Returns:
|
| 1446 |
-
(Numpy array): resampled audio of size `n_samples x n_channels`.
|
| 1447 |
-
"""
|
| 1448 |
-
if self.parameters.factor.value == 1.0:
|
| 1449 |
-
y = x
|
| 1450 |
-
else:
|
| 1451 |
-
scale = np.max(np.abs(x))
|
| 1452 |
-
if scale > MAX_SOX_PROCESSING_PEAK:
|
| 1453 |
-
clips = True
|
| 1454 |
-
x = x * (MAX_SOX_PROCESSING_PEAK / scale)
|
| 1455 |
-
else:
|
| 1456 |
-
clips = False
|
| 1457 |
-
|
| 1458 |
-
tfm = sox.Transformer()
|
| 1459 |
-
tfm.speed(self.parameters.factor.value)
|
| 1460 |
-
y = tfm.build_array(input_array=x, sample_rate_in=self.parameters.sample_rate.value).astype(np.float32)
|
| 1461 |
-
|
| 1462 |
-
if clips:
|
| 1463 |
-
y *= scale / MAX_SOX_PROCESSING_PEAK # rescale output to original scale
|
| 1464 |
-
|
| 1465 |
-
if self.fix_length:
|
| 1466 |
-
n_samples_input = x.shape[0]
|
| 1467 |
-
n_samples_output = y.shape[0]
|
| 1468 |
-
if n_samples_input < n_samples_output:
|
| 1469 |
-
idx1 = (n_samples_output - n_samples_input) // 2
|
| 1470 |
-
idx2 = idx1 + n_samples_input
|
| 1471 |
-
y = y[idx1:idx2]
|
| 1472 |
-
elif n_samples_input > n_samples_output:
|
| 1473 |
-
n_pad = n_samples_input - n_samples_output
|
| 1474 |
-
y = np.pad(y, ((n_pad//2, n_pad - n_pad//2), (0, 0)))
|
| 1475 |
-
|
| 1476 |
-
return y
|
| 1477 |
-
|
| 1478 |
-
|
| 1479 |
-
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% BEND %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 1480 |
-
class Bend(Processor):
|
| 1481 |
-
"""
|
| 1482 |
-
Simple bend effect using SoX and soxbindings (https://github.com/pseeth/soxbindings).
|
| 1483 |
-
|
| 1484 |
-
Processor parameters:
|
| 1485 |
-
sample_rate (int): Current (assumed) sample rate of the audio.
|
| 1486 |
-
n_bends (int): Number of segments or intervals to pitch shift
|
| 1487 |
-
"""
|
| 1488 |
-
|
| 1489 |
-
def __init__(self, sample_rates, pitch_range=(-600, 600), fix_length=True, name='Bend', parameters=None, **kwargs):
|
| 1490 |
-
"""
|
| 1491 |
-
Initialize processor.
|
| 1492 |
-
|
| 1493 |
-
Args:
|
| 1494 |
-
sample_rates (list of ints): Sample rates of input audio.
|
| 1495 |
-
pitch_range (tuple of ints): min and max pitch bending ranges in cents
|
| 1496 |
-
fix_length (bool): If True, then output has same length as input.
|
| 1497 |
-
name (str): Name of processor.
|
| 1498 |
-
parameters (parameter_list): Parameters for this processor.
|
| 1499 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 1500 |
-
"""
|
| 1501 |
-
super().__init__(name=name, parameters=parameters, block_size=None, **kwargs)
|
| 1502 |
-
|
| 1503 |
-
if not parameters:
|
| 1504 |
-
self.parameters = ParameterList()
|
| 1505 |
-
self.parameters.add(Parameter('sample_rate', 44100, 'string', options=sample_rates))
|
| 1506 |
-
self.parameters.add(Parameter('n_bends', 2, 'int', minimum=2, maximum=10))
|
| 1507 |
-
else:
|
| 1508 |
-
self.parameters = parameters
|
| 1509 |
-
self.pitch_range_min, self.pitch_range_max = pitch_range
|
| 1510 |
-
|
| 1511 |
-
def process(self, x):
|
| 1512 |
-
"""
|
| 1513 |
-
Process audio.
|
| 1514 |
-
|
| 1515 |
-
Args:
|
| 1516 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 1517 |
-
|
| 1518 |
-
Returns:
|
| 1519 |
-
(Numpy array): pitch-bended audio of size `n_samples x n_channels`.
|
| 1520 |
-
"""
|
| 1521 |
-
n_bends = self.parameters.n_bends.value
|
| 1522 |
-
max_length = x.shape[0] / self.parameters.sample_rate.value
|
| 1523 |
-
|
| 1524 |
-
# Generates random non-overlapping segments
|
| 1525 |
-
delta = 1. / self.parameters.sample_rate.value
|
| 1526 |
-
boundaries = np.sort(delta + np.random.rand(n_bends-1) * (max_length - delta))
|
| 1527 |
-
|
| 1528 |
-
start, end = np.zeros(n_bends), np.zeros(n_bends)
|
| 1529 |
-
start[0] = delta
|
| 1530 |
-
for i, b in enumerate(boundaries):
|
| 1531 |
-
end[i] = b
|
| 1532 |
-
start[i+1] = b
|
| 1533 |
-
end[-1] = max_length
|
| 1534 |
-
|
| 1535 |
-
# randomly sample pitch-shifts in cents
|
| 1536 |
-
cents = np.random.randint(self.pitch_range_min, self.pitch_range_max+1, n_bends)
|
| 1537 |
-
|
| 1538 |
-
# remove segment if cent value is zero or start == end (as SoX does not allow such values)
|
| 1539 |
-
idx_keep = np.logical_and(cents != 0, start != end)
|
| 1540 |
-
n_bends, start, end, cents = sum(idx_keep), start[idx_keep], end[idx_keep], cents[idx_keep]
|
| 1541 |
-
|
| 1542 |
-
scale = np.max(np.abs(x))
|
| 1543 |
-
if scale > MAX_SOX_PROCESSING_PEAK:
|
| 1544 |
-
clips = True
|
| 1545 |
-
x = x * (MAX_SOX_PROCESSING_PEAK / scale)
|
| 1546 |
-
else:
|
| 1547 |
-
clips = False
|
| 1548 |
-
|
| 1549 |
-
tfm = sox.Transformer()
|
| 1550 |
-
tfm.bend(n_bends=int(n_bends), start_times=list(start), end_times=list(end), cents=list(cents))
|
| 1551 |
-
y = tfm.build_array(input_array=x, sample_rate_in=self.parameters.sample_rate.value).astype(np.float32)
|
| 1552 |
-
|
| 1553 |
-
if clips:
|
| 1554 |
-
y *= scale / MAX_SOX_PROCESSING_PEAK # rescale output to original scale
|
| 1555 |
-
|
| 1556 |
-
return y
|
| 1557 |
-
|
| 1558 |
-
|
| 1559 |
-
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% MASTERING %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 1560 |
-
class Mastering(Processor):
|
| 1561 |
-
"""
|
| 1562 |
-
Mastering Processor.
|
| 1563 |
-
|
| 1564 |
-
Models different mastering effects, i.e., ways how we deal with sample values outside [-1, 1].
|
| 1565 |
-
|
| 1566 |
-
Processor parameters:
|
| 1567 |
-
method (str): Method that should be applied. Can take the following values:
|
| 1568 |
-
`none`: Do nothing. E.g., useful if training a network for VST plugin where DAW can also handle peaks
|
| 1569 |
-
larger/smaller than `+1`/`-1`.
|
| 1570 |
-
`scale`: Scale to max-abs peak to avoid clipping.
|
| 1571 |
-
`hardclip`: Apply hardclipping to [-1, 1].
|
| 1572 |
-
`softclip`: Apply softclipping to [-1, 1] using tanh.
|
| 1573 |
-
"""
|
| 1574 |
-
|
| 1575 |
-
def __init__(self, name='Mastering', parameters=None, maximum_amplitude=1.0, **kwargs):
|
| 1576 |
-
"""
|
| 1577 |
-
Initialize processor.
|
| 1578 |
-
|
| 1579 |
-
Args:
|
| 1580 |
-
name (str): Name of processor.
|
| 1581 |
-
parameters (parameter_list): Parameters for this processor.
|
| 1582 |
-
maximum_amplitude (float): Maximum amplitude after applying the mastering processor.
|
| 1583 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 1584 |
-
"""
|
| 1585 |
-
super().__init__(name, parameters=parameters, block_size=None, **kwargs)
|
| 1586 |
-
|
| 1587 |
-
if not parameters:
|
| 1588 |
-
self.parameters = ParameterList()
|
| 1589 |
-
self.parameters.add(Parameter('method', 'none', 'string',
|
| 1590 |
-
options=['none', 'scale', 'hardclip', 'softclip']))
|
| 1591 |
-
else:
|
| 1592 |
-
self.parameters = parameters
|
| 1593 |
-
|
| 1594 |
-
self.maximum_amplitude = maximum_amplitude
|
| 1595 |
-
|
| 1596 |
-
def process(self, x):
|
| 1597 |
-
"""
|
| 1598 |
-
Process audio.
|
| 1599 |
-
|
| 1600 |
-
Args:
|
| 1601 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 1602 |
-
|
| 1603 |
-
Returns:
|
| 1604 |
-
(Numpy array): mastered audio of size `n_samples x n_channels`.
|
| 1605 |
-
"""
|
| 1606 |
-
y = np.copy(x)
|
| 1607 |
-
if self.parameters.method.value == 'none':
|
| 1608 |
-
return y
|
| 1609 |
-
elif self.parameters.method.value == 'scale':
|
| 1610 |
-
maxabs = np.maximum(self.maximum_amplitude, np.max(np.abs(y)))
|
| 1611 |
-
return y / (maxabs / self.maximum_amplitude)
|
| 1612 |
-
elif self.parameters.method.value == 'hardclip':
|
| 1613 |
-
return np.clip(y, -self.maximum_amplitude, self.maximum_amplitude)
|
| 1614 |
-
elif self.parameters.method.value == 'softclip':
|
| 1615 |
-
return self.maximum_amplitude * np.tanh(y)
|
| 1616 |
-
|
| 1617 |
-
|
| 1618 |
-
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% MP3 COMPRESSION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 1619 |
-
class MP3Compression(Processor):
|
| 1620 |
-
"""
|
| 1621 |
-
Models the effect of an mp3 compression using LAME.
|
| 1622 |
-
|
| 1623 |
-
LAME adds some delay at the beginning - we therefore truncate the first samples
|
| 1624 |
-
|
| 1625 |
-
Processor parameters:
|
| 1626 |
-
sample_rate (int): Current (assumed) sample rate of the audio.
|
| 1627 |
-
quality (int): in the range from 2 ... 7 (2 = highest, 7 = fastest)
|
| 1628 |
-
bitrate (int): supported bitrates are
|
| 1629 |
-
[8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 192, 224, 256, 320]
|
| 1630 |
-
"""
|
| 1631 |
-
|
| 1632 |
-
def __init__(self, sample_rates, fix_length=True, name='MP3Compression', parameters=None, **kwargs):
|
| 1633 |
-
"""
|
| 1634 |
-
Initialize processor.
|
| 1635 |
-
|
| 1636 |
-
Args:
|
| 1637 |
-
sample_rates (list of ints): Sample rates of input audio.
|
| 1638 |
-
fix_length (bool): If True, then output has same length as input.
|
| 1639 |
-
name (str): Name of processor.
|
| 1640 |
-
parameters (parameter_list): Parameters for this processor.
|
| 1641 |
-
**kwargs (dict): Keyword arguments that are passed to the Processor class.
|
| 1642 |
-
"""
|
| 1643 |
-
super().__init__(name=name, parameters=parameters, block_size=None, **kwargs)
|
| 1644 |
-
|
| 1645 |
-
if not parameters:
|
| 1646 |
-
supported_bitrates = [8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 192, 224, 256, 320]
|
| 1647 |
-
self.parameters = ParameterList()
|
| 1648 |
-
self.parameters.add(Parameter('sample_rate', 44100, 'string', options=sample_rates))
|
| 1649 |
-
self.parameters.add(Parameter('quality', 2, 'string', options=[2, 3, 4, 5, 6, 7]))
|
| 1650 |
-
self.parameters.add(Parameter('bitrate', 96, 'string', options=supported_bitrates))
|
| 1651 |
-
else:
|
| 1652 |
-
self.parameters = parameters
|
| 1653 |
-
|
| 1654 |
-
self.fix_length = fix_length
|
| 1655 |
-
|
| 1656 |
-
def process(self, x):
|
| 1657 |
-
"""
|
| 1658 |
-
Process audio.
|
| 1659 |
-
|
| 1660 |
-
Args:
|
| 1661 |
-
x (Numpy array): input audio of size `n_samples x n_channels`.
|
| 1662 |
-
|
| 1663 |
-
Returns:
|
| 1664 |
-
(Numpy array): MP3 compressed audio of size `n_samples x n_channels`.
|
| 1665 |
-
"""
|
| 1666 |
-
# scale if max-abs peak is larger than `MAX_SOX_PROCESSING_PEAK`
|
| 1667 |
-
scale = np.max(np.abs(x))
|
| 1668 |
-
if scale > MAX_SOX_PROCESSING_PEAK:
|
| 1669 |
-
clips = True
|
| 1670 |
-
x = x * (MAX_SOX_PROCESSING_PEAK / scale)
|
| 1671 |
-
else:
|
| 1672 |
-
clips = False
|
| 1673 |
-
|
| 1674 |
-
# convert to int16
|
| 1675 |
-
x_int = (x * np.iinfo(np.int16).max).astype(np.int16)
|
| 1676 |
-
|
| 1677 |
-
encoder = lameenc.Encoder()
|
| 1678 |
-
encoder.set_bit_rate(self.parameters.bitrate.value)
|
| 1679 |
-
encoder.set_in_sample_rate(self.parameters.sample_rate.value)
|
| 1680 |
-
encoder.set_channels(x.shape[1])
|
| 1681 |
-
encoder.set_quality(self.parameters.quality.value)
|
| 1682 |
-
encoder.silence()
|
| 1683 |
-
|
| 1684 |
-
mp3_data = encoder.encode(x_int.tobytes())
|
| 1685 |
-
mp3_data += encoder.flush()
|
| 1686 |
-
|
| 1687 |
-
memory_file = io.BytesIO(initial_bytes=mp3_data)
|
| 1688 |
-
y, fs = sf.read(memory_file, always_2d=True, dtype=np.float32)
|
| 1689 |
-
|
| 1690 |
-
# resample to original sampling rate if LAME changed it
|
| 1691 |
-
if fs != self.parameters.sample_rate.value:
|
| 1692 |
-
tfm = sox.Transformer()
|
| 1693 |
-
tfm.speed(fs / self.parameters.sample_rate.value)
|
| 1694 |
-
y = tfm.build_array(input_array=y, sample_rate_in=fs).astype(np.float32)
|
| 1695 |
-
|
| 1696 |
-
if clips:
|
| 1697 |
-
y *= scale / MAX_SOX_PROCESSING_PEAK # rescale output to original scale
|
| 1698 |
-
|
| 1699 |
-
if self.fix_length:
|
| 1700 |
-
# LAME adds some samples at the beginning - remove them here
|
| 1701 |
-
n_samples_input = x.shape[0]
|
| 1702 |
-
n_samples_output = y.shape[0]
|
| 1703 |
-
if n_samples_input < n_samples_output:
|
| 1704 |
-
y = y[-n_samples_input:, :]
|
| 1705 |
-
|
| 1706 |
-
return y
|
| 1707 |
-
|
| 1708 |
-
|
| 1709 |
-
def __main__():
|
| 1710 |
-
"""
|
| 1711 |
-
Main function for testing the audio effects.
|
| 1712 |
-
|
| 1713 |
-
examples:
|
| 1714 |
-
|
| 1715 |
-
config['AUGMENTER_CHAIN'] = AugmentationChain([(ConvolutionalReverb(impulse_responses=impulse_responses,
|
| 1716 |
-
sample_rates=config['ACCEPTED_SAMPLING_RATES']), 0.5),
|
| 1717 |
-
(Gain(), 0.5),
|
| 1718 |
-
(Haas(sample_rates=config['ACCEPTED_SAMPLING_RATES']), 0.5),
|
| 1719 |
-
(Panner(sample_rates=config['ACCEPTED_SAMPLING_RATES']), 0.5),
|
| 1720 |
-
(SwapChannels(n_channels=config['N_CHANNELS']), 1.),
|
| 1721 |
-
(Monauralize(n_channels=config['N_CHANNELS']), 0.5)],
|
| 1722 |
-
shuffle=True)
|
| 1723 |
-
|
| 1724 |
-
config['AUGMENTER_CHAIN'] = AugmentationChain([(Gain(), 1.),
|
| 1725 |
-
(SwapChannels(n_channels=config['N_CHANNELS']), 1.),
|
| 1726 |
-
(Monauralize(n_channels=config['N_CHANNELS']), 0.5)], shuffle=True)
|
| 1727 |
-
"""
|
| 1728 |
-
pass
|
| 1729 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/dnnlib/__init__.py
DELETED
|
@@ -1,8 +0,0 @@
|
|
| 1 |
-
# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
-
#
|
| 3 |
-
# This work is licensed under a Creative Commons
|
| 4 |
-
# Attribution-NonCommercial-ShareAlike 4.0 International License.
|
| 5 |
-
# You should have received a copy of the license along with this
|
| 6 |
-
# work. If not, see http://creativecommons.org/licenses/by-nc-sa/4.0/
|
| 7 |
-
|
| 8 |
-
from .util import EasyDict, make_cache_dir_path, call_func_by_name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/dnnlib/util.py
DELETED
|
@@ -1,491 +0,0 @@
|
|
| 1 |
-
# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
-
#
|
| 3 |
-
# This work is licensed under a Creative Commons
|
| 4 |
-
# Attribution-NonCommercial-ShareAlike 4.0 International License.
|
| 5 |
-
# You should have received a copy of the license along with this
|
| 6 |
-
# work. If not, see http://creativecommons.org/licenses/by-nc-sa/4.0/
|
| 7 |
-
|
| 8 |
-
"""Miscellaneous utility classes and functions."""
|
| 9 |
-
|
| 10 |
-
import ctypes
|
| 11 |
-
import fnmatch
|
| 12 |
-
import importlib
|
| 13 |
-
import inspect
|
| 14 |
-
import numpy as np
|
| 15 |
-
import os
|
| 16 |
-
import shutil
|
| 17 |
-
import sys
|
| 18 |
-
import types
|
| 19 |
-
import io
|
| 20 |
-
import pickle
|
| 21 |
-
import re
|
| 22 |
-
import requests
|
| 23 |
-
import html
|
| 24 |
-
import hashlib
|
| 25 |
-
import glob
|
| 26 |
-
import tempfile
|
| 27 |
-
import urllib
|
| 28 |
-
import urllib.request
|
| 29 |
-
import uuid
|
| 30 |
-
|
| 31 |
-
from distutils.util import strtobool
|
| 32 |
-
from typing import Any, List, Tuple, Union, Optional
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
# Util classes
|
| 36 |
-
# ------------------------------------------------------------------------------------------
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
class EasyDict(dict):
|
| 40 |
-
"""Convenience class that behaves like a dict but allows access with the attribute syntax."""
|
| 41 |
-
|
| 42 |
-
def __getattr__(self, name: str) -> Any:
|
| 43 |
-
try:
|
| 44 |
-
return self[name]
|
| 45 |
-
except KeyError:
|
| 46 |
-
raise AttributeError(name)
|
| 47 |
-
|
| 48 |
-
def __setattr__(self, name: str, value: Any) -> None:
|
| 49 |
-
self[name] = value
|
| 50 |
-
|
| 51 |
-
def __delattr__(self, name: str) -> None:
|
| 52 |
-
del self[name]
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
class Logger(object):
|
| 56 |
-
"""Redirect stderr to stdout, optionally print stdout to a file, and optionally force flushing on both stdout and the file."""
|
| 57 |
-
|
| 58 |
-
def __init__(self, file_name: Optional[str] = None, file_mode: str = "w", should_flush: bool = True):
|
| 59 |
-
self.file = None
|
| 60 |
-
|
| 61 |
-
if file_name is not None:
|
| 62 |
-
self.file = open(file_name, file_mode)
|
| 63 |
-
|
| 64 |
-
self.should_flush = should_flush
|
| 65 |
-
self.stdout = sys.stdout
|
| 66 |
-
self.stderr = sys.stderr
|
| 67 |
-
|
| 68 |
-
sys.stdout = self
|
| 69 |
-
sys.stderr = self
|
| 70 |
-
|
| 71 |
-
def __enter__(self) -> "Logger":
|
| 72 |
-
return self
|
| 73 |
-
|
| 74 |
-
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
|
| 75 |
-
self.close()
|
| 76 |
-
|
| 77 |
-
def write(self, text: Union[str, bytes]) -> None:
|
| 78 |
-
"""Write text to stdout (and a file) and optionally flush."""
|
| 79 |
-
if isinstance(text, bytes):
|
| 80 |
-
text = text.decode()
|
| 81 |
-
if len(text) == 0: # workaround for a bug in VSCode debugger: sys.stdout.write(''); sys.stdout.flush() => crash
|
| 82 |
-
return
|
| 83 |
-
|
| 84 |
-
if self.file is not None:
|
| 85 |
-
self.file.write(text)
|
| 86 |
-
|
| 87 |
-
self.stdout.write(text)
|
| 88 |
-
|
| 89 |
-
if self.should_flush:
|
| 90 |
-
self.flush()
|
| 91 |
-
|
| 92 |
-
def flush(self) -> None:
|
| 93 |
-
"""Flush written text to both stdout and a file, if open."""
|
| 94 |
-
if self.file is not None:
|
| 95 |
-
self.file.flush()
|
| 96 |
-
|
| 97 |
-
self.stdout.flush()
|
| 98 |
-
|
| 99 |
-
def close(self) -> None:
|
| 100 |
-
"""Flush, close possible files, and remove stdout/stderr mirroring."""
|
| 101 |
-
self.flush()
|
| 102 |
-
|
| 103 |
-
# if using multiple loggers, prevent closing in wrong order
|
| 104 |
-
if sys.stdout is self:
|
| 105 |
-
sys.stdout = self.stdout
|
| 106 |
-
if sys.stderr is self:
|
| 107 |
-
sys.stderr = self.stderr
|
| 108 |
-
|
| 109 |
-
if self.file is not None:
|
| 110 |
-
self.file.close()
|
| 111 |
-
self.file = None
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
# Cache directories
|
| 115 |
-
# ------------------------------------------------------------------------------------------
|
| 116 |
-
|
| 117 |
-
_dnnlib_cache_dir = None
|
| 118 |
-
|
| 119 |
-
def set_cache_dir(path: str) -> None:
|
| 120 |
-
global _dnnlib_cache_dir
|
| 121 |
-
_dnnlib_cache_dir = path
|
| 122 |
-
|
| 123 |
-
def make_cache_dir_path(*paths: str) -> str:
|
| 124 |
-
if _dnnlib_cache_dir is not None:
|
| 125 |
-
return os.path.join(_dnnlib_cache_dir, *paths)
|
| 126 |
-
if 'DNNLIB_CACHE_DIR' in os.environ:
|
| 127 |
-
return os.path.join(os.environ['DNNLIB_CACHE_DIR'], *paths)
|
| 128 |
-
if 'HOME' in os.environ:
|
| 129 |
-
return os.path.join(os.environ['HOME'], '.cache', 'dnnlib', *paths)
|
| 130 |
-
if 'USERPROFILE' in os.environ:
|
| 131 |
-
return os.path.join(os.environ['USERPROFILE'], '.cache', 'dnnlib', *paths)
|
| 132 |
-
return os.path.join(tempfile.gettempdir(), '.cache', 'dnnlib', *paths)
|
| 133 |
-
|
| 134 |
-
# Small util functions
|
| 135 |
-
# ------------------------------------------------------------------------------------------
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
def format_time(seconds: Union[int, float]) -> str:
|
| 139 |
-
"""Convert the seconds to human readable string with days, hours, minutes and seconds."""
|
| 140 |
-
s = int(np.rint(seconds))
|
| 141 |
-
|
| 142 |
-
if s < 60:
|
| 143 |
-
return "{0}s".format(s)
|
| 144 |
-
elif s < 60 * 60:
|
| 145 |
-
return "{0}m {1:02}s".format(s // 60, s % 60)
|
| 146 |
-
elif s < 24 * 60 * 60:
|
| 147 |
-
return "{0}h {1:02}m {2:02}s".format(s // (60 * 60), (s // 60) % 60, s % 60)
|
| 148 |
-
else:
|
| 149 |
-
return "{0}d {1:02}h {2:02}m".format(s // (24 * 60 * 60), (s // (60 * 60)) % 24, (s // 60) % 60)
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
def format_time_brief(seconds: Union[int, float]) -> str:
|
| 153 |
-
"""Convert the seconds to human readable string with days, hours, minutes and seconds."""
|
| 154 |
-
s = int(np.rint(seconds))
|
| 155 |
-
|
| 156 |
-
if s < 60:
|
| 157 |
-
return "{0}s".format(s)
|
| 158 |
-
elif s < 60 * 60:
|
| 159 |
-
return "{0}m {1:02}s".format(s // 60, s % 60)
|
| 160 |
-
elif s < 24 * 60 * 60:
|
| 161 |
-
return "{0}h {1:02}m".format(s // (60 * 60), (s // 60) % 60)
|
| 162 |
-
else:
|
| 163 |
-
return "{0}d {1:02}h".format(s // (24 * 60 * 60), (s // (60 * 60)) % 24)
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
def ask_yes_no(question: str) -> bool:
|
| 167 |
-
"""Ask the user the question until the user inputs a valid answer."""
|
| 168 |
-
while True:
|
| 169 |
-
try:
|
| 170 |
-
print("{0} [y/n]".format(question))
|
| 171 |
-
return strtobool(input().lower())
|
| 172 |
-
except ValueError:
|
| 173 |
-
pass
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
def tuple_product(t: Tuple) -> Any:
|
| 177 |
-
"""Calculate the product of the tuple elements."""
|
| 178 |
-
result = 1
|
| 179 |
-
|
| 180 |
-
for v in t:
|
| 181 |
-
result *= v
|
| 182 |
-
|
| 183 |
-
return result
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
_str_to_ctype = {
|
| 187 |
-
"uint8": ctypes.c_ubyte,
|
| 188 |
-
"uint16": ctypes.c_uint16,
|
| 189 |
-
"uint32": ctypes.c_uint32,
|
| 190 |
-
"uint64": ctypes.c_uint64,
|
| 191 |
-
"int8": ctypes.c_byte,
|
| 192 |
-
"int16": ctypes.c_int16,
|
| 193 |
-
"int32": ctypes.c_int32,
|
| 194 |
-
"int64": ctypes.c_int64,
|
| 195 |
-
"float32": ctypes.c_float,
|
| 196 |
-
"float64": ctypes.c_double
|
| 197 |
-
}
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
def get_dtype_and_ctype(type_obj: Any) -> Tuple[np.dtype, Any]:
|
| 201 |
-
"""Given a type name string (or an object having a __name__ attribute), return matching Numpy and ctypes types that have the same size in bytes."""
|
| 202 |
-
type_str = None
|
| 203 |
-
|
| 204 |
-
if isinstance(type_obj, str):
|
| 205 |
-
type_str = type_obj
|
| 206 |
-
elif hasattr(type_obj, "__name__"):
|
| 207 |
-
type_str = type_obj.__name__
|
| 208 |
-
elif hasattr(type_obj, "name"):
|
| 209 |
-
type_str = type_obj.name
|
| 210 |
-
else:
|
| 211 |
-
raise RuntimeError("Cannot infer type name from input")
|
| 212 |
-
|
| 213 |
-
assert type_str in _str_to_ctype.keys()
|
| 214 |
-
|
| 215 |
-
my_dtype = np.dtype(type_str)
|
| 216 |
-
my_ctype = _str_to_ctype[type_str]
|
| 217 |
-
|
| 218 |
-
assert my_dtype.itemsize == ctypes.sizeof(my_ctype)
|
| 219 |
-
|
| 220 |
-
return my_dtype, my_ctype
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
def is_pickleable(obj: Any) -> bool:
|
| 224 |
-
try:
|
| 225 |
-
with io.BytesIO() as stream:
|
| 226 |
-
pickle.dump(obj, stream)
|
| 227 |
-
return True
|
| 228 |
-
except:
|
| 229 |
-
return False
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
# Functionality to import modules/objects by name, and call functions by name
|
| 233 |
-
# ------------------------------------------------------------------------------------------
|
| 234 |
-
|
| 235 |
-
def get_module_from_obj_name(obj_name: str) -> Tuple[types.ModuleType, str]:
|
| 236 |
-
"""Searches for the underlying module behind the name to some python object.
|
| 237 |
-
Returns the module and the object name (original name with module part removed)."""
|
| 238 |
-
|
| 239 |
-
# allow convenience shorthands, substitute them by full names
|
| 240 |
-
obj_name = re.sub("^np.", "numpy.", obj_name)
|
| 241 |
-
obj_name = re.sub("^tf.", "tensorflow.", obj_name)
|
| 242 |
-
|
| 243 |
-
# list alternatives for (module_name, local_obj_name)
|
| 244 |
-
parts = obj_name.split(".")
|
| 245 |
-
name_pairs = [(".".join(parts[:i]), ".".join(parts[i:])) for i in range(len(parts), 0, -1)]
|
| 246 |
-
|
| 247 |
-
# try each alternative in turn
|
| 248 |
-
for module_name, local_obj_name in name_pairs:
|
| 249 |
-
try:
|
| 250 |
-
module = importlib.import_module(module_name) # may raise ImportError
|
| 251 |
-
get_obj_from_module(module, local_obj_name) # may raise AttributeError
|
| 252 |
-
return module, local_obj_name
|
| 253 |
-
except:
|
| 254 |
-
pass
|
| 255 |
-
|
| 256 |
-
# maybe some of the modules themselves contain errors?
|
| 257 |
-
for module_name, _local_obj_name in name_pairs:
|
| 258 |
-
try:
|
| 259 |
-
importlib.import_module(module_name) # may raise ImportError
|
| 260 |
-
except ImportError:
|
| 261 |
-
if not str(sys.exc_info()[1]).startswith("No module named '" + module_name + "'"):
|
| 262 |
-
raise
|
| 263 |
-
|
| 264 |
-
# maybe the requested attribute is missing?
|
| 265 |
-
for module_name, local_obj_name in name_pairs:
|
| 266 |
-
try:
|
| 267 |
-
module = importlib.import_module(module_name) # may raise ImportError
|
| 268 |
-
get_obj_from_module(module, local_obj_name) # may raise AttributeError
|
| 269 |
-
except ImportError:
|
| 270 |
-
pass
|
| 271 |
-
|
| 272 |
-
# we are out of luck, but we have no idea why
|
| 273 |
-
raise ImportError(obj_name)
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
def get_obj_from_module(module: types.ModuleType, obj_name: str) -> Any:
|
| 277 |
-
"""Traverses the object name and returns the last (rightmost) python object."""
|
| 278 |
-
if obj_name == '':
|
| 279 |
-
return module
|
| 280 |
-
obj = module
|
| 281 |
-
for part in obj_name.split("."):
|
| 282 |
-
obj = getattr(obj, part)
|
| 283 |
-
return obj
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
def get_obj_by_name(name: str) -> Any:
|
| 287 |
-
"""Finds the python object with the given name."""
|
| 288 |
-
module, obj_name = get_module_from_obj_name(name)
|
| 289 |
-
return get_obj_from_module(module, obj_name)
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
def call_func_by_name(*args, func_name: str = None, **kwargs) -> Any:
|
| 293 |
-
"""Finds the python object with the given name and calls it as a function."""
|
| 294 |
-
assert func_name is not None
|
| 295 |
-
func_obj = get_obj_by_name(func_name)
|
| 296 |
-
assert callable(func_obj)
|
| 297 |
-
return func_obj(*args, **kwargs)
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
def construct_class_by_name(*args, class_name: str = None, **kwargs) -> Any:
|
| 301 |
-
"""Finds the python class with the given name and constructs it with the given arguments."""
|
| 302 |
-
return call_func_by_name(*args, func_name=class_name, **kwargs)
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
def get_module_dir_by_obj_name(obj_name: str) -> str:
|
| 306 |
-
"""Get the directory path of the module containing the given object name."""
|
| 307 |
-
module, _ = get_module_from_obj_name(obj_name)
|
| 308 |
-
return os.path.dirname(inspect.getfile(module))
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
def is_top_level_function(obj: Any) -> bool:
|
| 312 |
-
"""Determine whether the given object is a top-level function, i.e., defined at module scope using 'def'."""
|
| 313 |
-
return callable(obj) and obj.__name__ in sys.modules[obj.__module__].__dict__
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
def get_top_level_function_name(obj: Any) -> str:
|
| 317 |
-
"""Return the fully-qualified name of a top-level function."""
|
| 318 |
-
assert is_top_level_function(obj)
|
| 319 |
-
module = obj.__module__
|
| 320 |
-
if module == '__main__':
|
| 321 |
-
module = os.path.splitext(os.path.basename(sys.modules[module].__file__))[0]
|
| 322 |
-
return module + "." + obj.__name__
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
# File system helpers
|
| 326 |
-
# ------------------------------------------------------------------------------------------
|
| 327 |
-
|
| 328 |
-
def list_dir_recursively_with_ignore(dir_path: str, ignores: List[str] = None, add_base_to_relative: bool = False) -> List[Tuple[str, str]]:
|
| 329 |
-
"""List all files recursively in a given directory while ignoring given file and directory names.
|
| 330 |
-
Returns list of tuples containing both absolute and relative paths."""
|
| 331 |
-
assert os.path.isdir(dir_path)
|
| 332 |
-
base_name = os.path.basename(os.path.normpath(dir_path))
|
| 333 |
-
|
| 334 |
-
if ignores is None:
|
| 335 |
-
ignores = []
|
| 336 |
-
|
| 337 |
-
result = []
|
| 338 |
-
|
| 339 |
-
for root, dirs, files in os.walk(dir_path, topdown=True):
|
| 340 |
-
for ignore_ in ignores:
|
| 341 |
-
dirs_to_remove = [d for d in dirs if fnmatch.fnmatch(d, ignore_)]
|
| 342 |
-
|
| 343 |
-
# dirs need to be edited in-place
|
| 344 |
-
for d in dirs_to_remove:
|
| 345 |
-
dirs.remove(d)
|
| 346 |
-
|
| 347 |
-
files = [f for f in files if not fnmatch.fnmatch(f, ignore_)]
|
| 348 |
-
|
| 349 |
-
absolute_paths = [os.path.join(root, f) for f in files]
|
| 350 |
-
relative_paths = [os.path.relpath(p, dir_path) for p in absolute_paths]
|
| 351 |
-
|
| 352 |
-
if add_base_to_relative:
|
| 353 |
-
relative_paths = [os.path.join(base_name, p) for p in relative_paths]
|
| 354 |
-
|
| 355 |
-
assert len(absolute_paths) == len(relative_paths)
|
| 356 |
-
result += zip(absolute_paths, relative_paths)
|
| 357 |
-
|
| 358 |
-
return result
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
def copy_files_and_create_dirs(files: List[Tuple[str, str]]) -> None:
|
| 362 |
-
"""Takes in a list of tuples of (src, dst) paths and copies files.
|
| 363 |
-
Will create all necessary directories."""
|
| 364 |
-
for file in files:
|
| 365 |
-
target_dir_name = os.path.dirname(file[1])
|
| 366 |
-
|
| 367 |
-
# will create all intermediate-level directories
|
| 368 |
-
if not os.path.exists(target_dir_name):
|
| 369 |
-
os.makedirs(target_dir_name)
|
| 370 |
-
|
| 371 |
-
shutil.copyfile(file[0], file[1])
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
# URL helpers
|
| 375 |
-
# ------------------------------------------------------------------------------------------
|
| 376 |
-
|
| 377 |
-
def is_url(obj: Any, allow_file_urls: bool = False) -> bool:
|
| 378 |
-
"""Determine whether the given object is a valid URL string."""
|
| 379 |
-
if not isinstance(obj, str) or not "://" in obj:
|
| 380 |
-
return False
|
| 381 |
-
if allow_file_urls and obj.startswith('file://'):
|
| 382 |
-
return True
|
| 383 |
-
try:
|
| 384 |
-
res = requests.compat.urlparse(obj)
|
| 385 |
-
if not res.scheme or not res.netloc or not "." in res.netloc:
|
| 386 |
-
return False
|
| 387 |
-
res = requests.compat.urlparse(requests.compat.urljoin(obj, "/"))
|
| 388 |
-
if not res.scheme or not res.netloc or not "." in res.netloc:
|
| 389 |
-
return False
|
| 390 |
-
except:
|
| 391 |
-
return False
|
| 392 |
-
return True
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
def open_url(url: str, cache_dir: str = None, num_attempts: int = 10, verbose: bool = True, return_filename: bool = False, cache: bool = True) -> Any:
|
| 396 |
-
"""Download the given URL and return a binary-mode file object to access the data."""
|
| 397 |
-
assert num_attempts >= 1
|
| 398 |
-
assert not (return_filename and (not cache))
|
| 399 |
-
|
| 400 |
-
# Doesn't look like an URL scheme so interpret it as a local filename.
|
| 401 |
-
if not re.match('^[a-z]+://', url):
|
| 402 |
-
return url if return_filename else open(url, "rb")
|
| 403 |
-
|
| 404 |
-
# Handle file URLs. This code handles unusual file:// patterns that
|
| 405 |
-
# arise on Windows:
|
| 406 |
-
#
|
| 407 |
-
# file:///c:/foo.txt
|
| 408 |
-
#
|
| 409 |
-
# which would translate to a local '/c:/foo.txt' filename that's
|
| 410 |
-
# invalid. Drop the forward slash for such pathnames.
|
| 411 |
-
#
|
| 412 |
-
# If you touch this code path, you should test it on both Linux and
|
| 413 |
-
# Windows.
|
| 414 |
-
#
|
| 415 |
-
# Some internet resources suggest using urllib.request.url2pathname() but
|
| 416 |
-
# but that converts forward slashes to backslashes and this causes
|
| 417 |
-
# its own set of problems.
|
| 418 |
-
if url.startswith('file://'):
|
| 419 |
-
filename = urllib.parse.urlparse(url).path
|
| 420 |
-
if re.match(r'^/[a-zA-Z]:', filename):
|
| 421 |
-
filename = filename[1:]
|
| 422 |
-
return filename if return_filename else open(filename, "rb")
|
| 423 |
-
|
| 424 |
-
assert is_url(url)
|
| 425 |
-
|
| 426 |
-
# Lookup from cache.
|
| 427 |
-
if cache_dir is None:
|
| 428 |
-
cache_dir = make_cache_dir_path('downloads')
|
| 429 |
-
|
| 430 |
-
url_md5 = hashlib.md5(url.encode("utf-8")).hexdigest()
|
| 431 |
-
if cache:
|
| 432 |
-
cache_files = glob.glob(os.path.join(cache_dir, url_md5 + "_*"))
|
| 433 |
-
if len(cache_files) == 1:
|
| 434 |
-
filename = cache_files[0]
|
| 435 |
-
return filename if return_filename else open(filename, "rb")
|
| 436 |
-
|
| 437 |
-
# Download.
|
| 438 |
-
url_name = None
|
| 439 |
-
url_data = None
|
| 440 |
-
with requests.Session() as session:
|
| 441 |
-
if verbose:
|
| 442 |
-
print("Downloading %s ..." % url, end="", flush=True)
|
| 443 |
-
for attempts_left in reversed(range(num_attempts)):
|
| 444 |
-
try:
|
| 445 |
-
with session.get(url) as res:
|
| 446 |
-
res.raise_for_status()
|
| 447 |
-
if len(res.content) == 0:
|
| 448 |
-
raise IOError("No data received")
|
| 449 |
-
|
| 450 |
-
if len(res.content) < 8192:
|
| 451 |
-
content_str = res.content.decode("utf-8")
|
| 452 |
-
if "download_warning" in res.headers.get("Set-Cookie", ""):
|
| 453 |
-
links = [html.unescape(link) for link in content_str.split('"') if "export=download" in link]
|
| 454 |
-
if len(links) == 1:
|
| 455 |
-
url = requests.compat.urljoin(url, links[0])
|
| 456 |
-
raise IOError("Google Drive virus checker nag")
|
| 457 |
-
if "Google Drive - Quota exceeded" in content_str:
|
| 458 |
-
raise IOError("Google Drive download quota exceeded -- please try again later")
|
| 459 |
-
|
| 460 |
-
match = re.search(r'filename="([^"]*)"', res.headers.get("Content-Disposition", ""))
|
| 461 |
-
url_name = match[1] if match else url
|
| 462 |
-
url_data = res.content
|
| 463 |
-
if verbose:
|
| 464 |
-
print(" done")
|
| 465 |
-
break
|
| 466 |
-
except KeyboardInterrupt:
|
| 467 |
-
raise
|
| 468 |
-
except:
|
| 469 |
-
if not attempts_left:
|
| 470 |
-
if verbose:
|
| 471 |
-
print(" failed")
|
| 472 |
-
raise
|
| 473 |
-
if verbose:
|
| 474 |
-
print(".", end="", flush=True)
|
| 475 |
-
|
| 476 |
-
# Save to cache.
|
| 477 |
-
if cache:
|
| 478 |
-
safe_name = re.sub(r"[^0-9a-zA-Z-._]", "_", url_name)
|
| 479 |
-
safe_name = safe_name[:min(len(safe_name), 128)]
|
| 480 |
-
cache_file = os.path.join(cache_dir, url_md5 + "_" + safe_name)
|
| 481 |
-
temp_file = os.path.join(cache_dir, "tmp_" + uuid.uuid4().hex + "_" + url_md5 + "_" + safe_name)
|
| 482 |
-
os.makedirs(cache_dir, exist_ok=True)
|
| 483 |
-
with open(temp_file, "wb") as f:
|
| 484 |
-
f.write(url_data)
|
| 485 |
-
os.replace(temp_file, cache_file) # atomic
|
| 486 |
-
if return_filename:
|
| 487 |
-
return cache_file
|
| 488 |
-
|
| 489 |
-
# Return data as file object.
|
| 490 |
-
assert not return_filename
|
| 491 |
-
return io.BytesIO(url_data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/evaluation/KAD_evaluate.py
DELETED
|
@@ -1,307 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import pandas as pd
|
| 3 |
-
|
| 4 |
-
import torch
|
| 5 |
-
import os
|
| 6 |
-
import omegaconf
|
| 7 |
-
import numpy as np
|
| 8 |
-
import glob
|
| 9 |
-
import pyloudnorm as pyln
|
| 10 |
-
import sys
|
| 11 |
-
#move to the parent directory to import datasets
|
| 12 |
-
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
| 13 |
-
|
| 14 |
-
from datasets.eval_benchmark import load_audio
|
| 15 |
-
from utils.evaluation.dist_metrics import KADFeatures
|
| 16 |
-
|
| 17 |
-
# see device 1
|
| 18 |
-
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
|
| 19 |
-
|
| 20 |
-
path_test_set="/scratch/elec/t412-asp/automix/MDX_TM_benchmark"
|
| 21 |
-
|
| 22 |
-
set="MDX_TM"
|
| 23 |
-
|
| 24 |
-
features=[
|
| 25 |
-
"AFxRep",
|
| 26 |
-
"FxEncoder",
|
| 27 |
-
"FxEncoder++",
|
| 28 |
-
"CLAP",
|
| 29 |
-
]
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
KAD_metrics={}
|
| 33 |
-
|
| 34 |
-
KAD_args = {
|
| 35 |
-
"do_PCA_figure": False,
|
| 36 |
-
"do_TSNE_figure": False,
|
| 37 |
-
"kernel": "gaussian", #kernel to use for the KAD metric
|
| 38 |
-
}
|
| 39 |
-
|
| 40 |
-
KAD_args = omegaconf.OmegaConf.create(KAD_args)
|
| 41 |
-
|
| 42 |
-
for feature in features:
|
| 43 |
-
if feature=="AFxRep":
|
| 44 |
-
AFxRep_args = {
|
| 45 |
-
"distance_type": "cosine", # not used
|
| 46 |
-
"ckpt_path": "/scratch/work/molinee2/projects/project_mfm_eloi/src/tmp/afx-rep.ckpt"
|
| 47 |
-
}
|
| 48 |
-
AFxRep_args = omegaconf.OmegaConf.create(AFxRep_args)
|
| 49 |
-
KAD_metrics[feature] = KADFeatures(type="AFxRep", sample_rate=44100, AFxRep_args=AFxRep_args, KAD_args=KAD_args)
|
| 50 |
-
elif feature=="FxEncoder":
|
| 51 |
-
fx_encoder_args = {
|
| 52 |
-
"distance_type": "cosine", # not used
|
| 53 |
-
"ckpt_path": "/scratch/work/molinee2/projects/project_mfm_eloi/src/utils/feature_extractors/ckpt/fxenc_default.pt"
|
| 54 |
-
}
|
| 55 |
-
fx_encoder_args = omegaconf.OmegaConf.create(fx_encoder_args)
|
| 56 |
-
KAD_metrics[feature] = KADFeatures(type="fx_encoder", sample_rate=44100, fx_encoder_args=fx_encoder_args, KAD_args=KAD_args)
|
| 57 |
-
elif feature=="FxEncoder++":
|
| 58 |
-
fx_encoder_plusplus_args = {
|
| 59 |
-
"distance_type": "cosine", # not used
|
| 60 |
-
"ckpt_path": "/scratch/work/molinee2/projects/project_mfm_eloi/src_clean/checkpoints/fxenc_plusplus_default.pt"
|
| 61 |
-
}
|
| 62 |
-
fx_encoder_plusplus_args = omegaconf.OmegaConf.create(fx_encoder_plusplus_args)
|
| 63 |
-
KAD_metrics[feature] = KADFeatures(type="fx_encoder_++", sample_rate=44100, fx_encoder_plusplus_args=fx_encoder_plusplus_args, KAD_args=KAD_args)
|
| 64 |
-
|
| 65 |
-
elif feature=="CLAP":
|
| 66 |
-
clap_args = {
|
| 67 |
-
"ckpt_path": "/scratch/work/molinee2/projects/project_mfm_eloi/src_clean/checkpoints/music_audioset_epoch_15_esc_90.14.patched.pt",
|
| 68 |
-
"distance_type": "cosine",
|
| 69 |
-
"normalize": True, # if True, the features will be normalized
|
| 70 |
-
"use_adaptor": False, # if True, the features will be adapted to the CLAP space
|
| 71 |
-
"adaptor_checkpoint":None,
|
| 72 |
-
"adaptor_type":None,
|
| 73 |
-
"add_noise": False, # if True, the features will be augmented with orthogonal noise
|
| 74 |
-
"noise_sigma": 0 # sigma of the orthogonal noise to
|
| 75 |
-
}
|
| 76 |
-
|
| 77 |
-
clap_args = omegaconf.OmegaConf.create(clap_args)
|
| 78 |
-
KAD_metrics[feature] = KADFeatures(type="CLAP", sample_rate=44100, CLAP_args=clap_args, KAD_args=KAD_args)
|
| 79 |
-
|
| 80 |
-
elif feature=="bark":
|
| 81 |
-
bark_args = {
|
| 82 |
-
"distance_type": "cosine", # not used
|
| 83 |
-
"normalize": True, # if True, the features will be normalized
|
| 84 |
-
}
|
| 85 |
-
bark_args = omegaconf.OmegaConf.create(bark_args)
|
| 86 |
-
KAD_metrics[feature] = KADFeatures(type="bark", sample_rate=44100, bark_args=bark_args, KAD_args=KAD_args, normalize=True)
|
| 87 |
-
|
| 88 |
-
else:
|
| 89 |
-
raise ValueError(f"Unknown feature: {feature}")
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
def get_ref_file_path(dir):
|
| 93 |
-
ref_mixture = os.path.join(dir,"mix", "mix.wav")
|
| 94 |
-
return ref_mixture
|
| 95 |
-
|
| 96 |
-
pylnmeter = pyln.Meter(44100) # Create a meter for 44100 Hz sampling rate
|
| 97 |
-
|
| 98 |
-
def loudness_normalize(audio, target_loudness=-23.0):
|
| 99 |
-
"""
|
| 100 |
-
Normalize the loudness of the audio to a target level.
|
| 101 |
-
"""
|
| 102 |
-
audio= np.array(audio, dtype=np.float32).T
|
| 103 |
-
loudness = pylnmeter.integrated_loudness(audio)
|
| 104 |
-
|
| 105 |
-
# loudness normalize audio to -12 dB LUFS
|
| 106 |
-
loudness_normalized_audio = pyln.normalize.loudness(audio, loudness, -14.0)
|
| 107 |
-
|
| 108 |
-
return torch.tensor(loudness_normalized_audio.T, dtype=torch.float32)
|
| 109 |
-
|
| 110 |
-
song_dirs= sorted(glob.glob(os.path.join(path_test_set, "*")))
|
| 111 |
-
|
| 112 |
-
reference_dict = {}
|
| 113 |
-
|
| 114 |
-
for song_dir in song_dirs:
|
| 115 |
-
|
| 116 |
-
song_name = os.path.basename(song_dir)
|
| 117 |
-
segment_subdirs = sorted(glob.glob(os.path.join(song_dir, "*")))
|
| 118 |
-
|
| 119 |
-
song_id=os.path.basename(song_dir)
|
| 120 |
-
|
| 121 |
-
for segment_dir in segment_subdirs:
|
| 122 |
-
|
| 123 |
-
segment= os.path.basename(segment_dir)
|
| 124 |
-
id= f"{song_id}_{segment}"
|
| 125 |
-
|
| 126 |
-
ref_file_path= get_ref_file_path(segment_dir)
|
| 127 |
-
mix_ref, fs=load_audio(str(ref_file_path), stereo=True)
|
| 128 |
-
assert fs==44100, "Expected sampling rate of 44100 Hz"
|
| 129 |
-
mix_ref = loudness_normalize(mix_ref)
|
| 130 |
-
reference_dict[id] = mix_ref
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
# create dataframe colums are features, rows are methods
|
| 134 |
-
dataframe= pd.DataFrame(columns=["method"] +list(KAD_metrics.keys()))
|
| 135 |
-
|
| 136 |
-
filename_results="results_eval/results_KAD"+f"_{set}_test.csv"
|
| 137 |
-
|
| 138 |
-
#methods=[ "fxnorm_automix_S_Lb", "fxnorm_automix_L_Lb",]
|
| 139 |
-
#methods=[ "proposed_random_churn","S3_random_churn", "S4_random_churn", "only_rms_S4_random_churn", "only_rms_random_churn" ]
|
| 140 |
-
#methods=[ "proposed_public", "only_rms_public" ]j
|
| 141 |
-
#methods=[ "S1internal_S2public"]
|
| 142 |
-
#methods=[ "S1internal_S2publicv3", "S1publicv3_S2publicv3", "S1publicv3_S2internal"]
|
| 143 |
-
#methods=[ "S1public_S2publicv3"]
|
| 144 |
-
#methods=[ "S1public_S2publicv3", "S1public_S2publicv3_oracle", "S1public_S2publicv3_rms"]
|
| 145 |
-
#methods=["mst_oracle", "equal_loudness", "diff_baseline", "proposed_oracle", "proposed_random", "only_rms_random" , "proposed_centroid_close", "proposed_centroid_far", "only_rms_centroid_close", "only_rms_centroid_far"]
|
| 146 |
-
#methods=["S4_random"]
|
| 147 |
-
#methods=["onlyrms_3108"]
|
| 148 |
-
#methods=["publicv3_2"]
|
| 149 |
-
#methods=["mst_oracle", "equal_loudness", "diff_baseline", "proposed_oracle", "proposed_random_3108_v2", "prop_random_indep_3108", "S4_random", "S3_random", "WUN_4instr", "publicv3", "DMC_14instr", "fxnorm_automix_S_Lb", "fxnorm_automix_L_Lb"]
|
| 150 |
-
methods=[ "tencydb_test", "publicv3_test"]
|
| 151 |
-
#methods=["prop_random_indep", "only_rms_indep"]
|
| 152 |
-
#methods=["WUN_4instr"]
|
| 153 |
-
#methods=["diff_baseline"]
|
| 154 |
-
#methods=["internal_2708", "equal_loudness", "internal_2708_oracle"]
|
| 155 |
-
#methods=["WUN_4instr", "WUN_14instr", "DMC_14instr"]
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
for method in methods:
|
| 159 |
-
|
| 160 |
-
if method=="mst_oracle":
|
| 161 |
-
def get_pred_file_path(dir):
|
| 162 |
-
pred_mixture= os.path.join(dir,"mst_oracle", "mixture_output.wav")
|
| 163 |
-
return pred_mixture
|
| 164 |
-
elif method=="mst_oracle_multi":
|
| 165 |
-
def get_pred_file_path(dir):
|
| 166 |
-
pred_mixture= os.path.join(dir,"mst_oracle_multi", "mixture_output.wav")
|
| 167 |
-
return pred_mixture
|
| 168 |
-
elif method=="equal_loudness":
|
| 169 |
-
def get_pred_file_path(dir):
|
| 170 |
-
pred_mixture= os.path.join(dir,"anchor_equal_loudness", "mix.wav")
|
| 171 |
-
return pred_mixture
|
| 172 |
-
elif method=="diff_baseline":
|
| 173 |
-
def get_pred_file_path(dir):
|
| 174 |
-
pred_mixture= os.path.join(dir,"diff_baseline", "pred_mixture.wav")
|
| 175 |
-
return pred_mixture
|
| 176 |
-
elif method=="fxnorm_automix_S_Lb":
|
| 177 |
-
def get_pred_file_path(dir):
|
| 178 |
-
pred_mixture= os.path.join(dir,"fxnorm_automix_S_Lb_v2", "mixture_output.wav")
|
| 179 |
-
return pred_mixture
|
| 180 |
-
elif method=="fxnorm_automix_L_Lb":
|
| 181 |
-
def get_pred_file_path(dir):
|
| 182 |
-
pred_mixture= os.path.join(dir,"fxnorm_automix_L_Lb_v2", "mixture_output.wav")
|
| 183 |
-
return pred_mixture
|
| 184 |
-
elif method=="proposed_random":
|
| 185 |
-
def get_pred_file_path(dir):
|
| 186 |
-
pred_mixture= os.path.join(dir,"stylediffpipeline_S9v6_MF3wetv6_MDX_TM_benchmark_cfg_1_T30", "random.wav")
|
| 187 |
-
return pred_mixture
|
| 188 |
-
elif method=="proposed_random_3108_v2":
|
| 189 |
-
def get_pred_file_path(dir):
|
| 190 |
-
pred_mixture= os.path.join(dir,"stylediffpipeline_S9v6_MF3wetv6_MDX_TM_benchmark_cfg_1_T30_3108_v2", "random.wav")
|
| 191 |
-
return pred_mixture
|
| 192 |
-
elif method=="onlyrms_3108":
|
| 193 |
-
def get_pred_file_path(dir):
|
| 194 |
-
pred_mixture= os.path.join(dir,"stylediffpipeline_S9v6_MF3wetv6_MDX_TM_benchmark_cfg_1_T30_3108_v2", "only_rms_random.wav")
|
| 195 |
-
return pred_mixture
|
| 196 |
-
elif method=="proposed_random_3108_v2_oracle":
|
| 197 |
-
def get_pred_file_path(dir):
|
| 198 |
-
pred_mixture= os.path.join(dir,"stylediffpipeline_S9v6_MF3wetv6_MDX_TM_benchmark_cfg_1_T30_3108_v2_oracle", "random.wav")
|
| 199 |
-
return pred_mixture
|
| 200 |
-
elif method=="only_rms_random":
|
| 201 |
-
def get_pred_file_path(dir):
|
| 202 |
-
pred_mixture= os.path.join(dir,"stylediffpipeline_S9v6_MF3wetv6_MDX_TM_benchmark_cfg_1_T30", "only_rms_random.wav")
|
| 203 |
-
return pred_mixture
|
| 204 |
-
elif method=="S3_random":
|
| 205 |
-
def get_pred_file_path(dir):
|
| 206 |
-
pred_mixture= os.path.join(dir,"stylediffpipeline_S3v6_MF3wetv6_MDX_TM_benchmark_cfg_1_T30", "random.wav")
|
| 207 |
-
return pred_mixture
|
| 208 |
-
elif method=="S4_3108":
|
| 209 |
-
def get_pred_file_path(dir):
|
| 210 |
-
pred_mixture= os.path.join(dir,"stylediffpipeline_S4v6_MF3wetv6_MDX_TM_benchmark_cfg_1_T30_3108_v2", "random.wav")
|
| 211 |
-
return pred_mixture
|
| 212 |
-
elif method=="publicv3":
|
| 213 |
-
def get_pred_file_path(dir):
|
| 214 |
-
pred_mixture= os.path.join(dir,"stylediffpipeline_publicv3_publicv3_MDX_TM_benchmark_cfg_1_T30_publicv3", "random.wav")
|
| 215 |
-
return pred_mixture
|
| 216 |
-
elif method=="publicv3_test":
|
| 217 |
-
def get_pred_file_path(dir):
|
| 218 |
-
pred_mixture= os.path.join(dir,"public_public_MDX_TM_benchmark_public_test_Sep29", "random.wav")
|
| 219 |
-
return pred_mixture
|
| 220 |
-
elif method=="tencydb_test":
|
| 221 |
-
def get_pred_file_path(dir):
|
| 222 |
-
pred_mixture= os.path.join(dir,"internal_TencyDB_internal_TencyMastering_MDX_TM_benchmark_internal_test_Sep29", "random.wav")
|
| 223 |
-
return pred_mixture
|
| 224 |
-
elif method=="prop_random_indep_3108":
|
| 225 |
-
def get_pred_file_path(dir):
|
| 226 |
-
pred_mixture= os.path.join(dir,"stylediffpipeline_S9v6_MF3wetv6_MDX_TM_benchmark_cfg_1_T30_3108_v2_independent", "random.wav")
|
| 227 |
-
return pred_mixture
|
| 228 |
-
elif method=="WUN_4instr":
|
| 229 |
-
def get_pred_file_path(dir):
|
| 230 |
-
pred_mixture= os.path.join(dir,"WUN_4instr", "pred_mixture.wav")
|
| 231 |
-
return pred_mixture
|
| 232 |
-
elif method=="DMC_14instr":
|
| 233 |
-
def get_pred_file_path(dir):
|
| 234 |
-
pred_mixture= os.path.join(dir,"DMC_14instr", "pred_mixture.wav")
|
| 235 |
-
return pred_mixture
|
| 236 |
-
elif method=="only_rms_public":
|
| 237 |
-
def get_pred_file_path(dir):
|
| 238 |
-
pred_mixture= os.path.join(dir,"ours_public", "mixture_processed_onlyrms.wav")
|
| 239 |
-
return pred_mixture
|
| 240 |
-
elif method=="proposed_oracle":
|
| 241 |
-
def get_pred_file_path(dir):
|
| 242 |
-
pred_mixture= os.path.join(dir,"oracle_proposed", "mix.wav")
|
| 243 |
-
return pred_mixture
|
| 244 |
-
elif method=="publicv3_oracle":
|
| 245 |
-
def get_pred_file_path(dir):
|
| 246 |
-
pred_mixture= os.path.join(dir,"oracle_proposed_public", "mix.wav")
|
| 247 |
-
return pred_mixture
|
| 248 |
-
else:
|
| 249 |
-
raise ValueError(f"Unknown method: {method}")
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
method_dict = {}
|
| 253 |
-
|
| 254 |
-
for song_dir in song_dirs:
|
| 255 |
-
|
| 256 |
-
song_name = os.path.basename(song_dir)
|
| 257 |
-
segment_subdirs = sorted(glob.glob(os.path.join(song_dir, "*")))
|
| 258 |
-
|
| 259 |
-
song_id=os.path.basename(song_dir)
|
| 260 |
-
|
| 261 |
-
for segment_dir in segment_subdirs:
|
| 262 |
-
|
| 263 |
-
segment= os.path.basename(segment_dir)
|
| 264 |
-
id= f"{song_id}_{segment}"
|
| 265 |
-
|
| 266 |
-
pred_file_path= get_pred_file_path(segment_dir)
|
| 267 |
-
pred_mixture, fs=load_audio(str(pred_file_path), stereo=True)
|
| 268 |
-
assert fs==44100, "Expected sampling rate of 44100 Hz"
|
| 269 |
-
pred_mixture = loudness_normalize(pred_mixture)
|
| 270 |
-
assert not np.isnan(pred_mixture).any(), f"NaN values found in predicted mixture for {id}"
|
| 271 |
-
method_dict[id] = pred_mixture
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
#create new empty row with key as "method"
|
| 275 |
-
dataframe.loc[method] = [None] * (len(KAD_metrics)+1)
|
| 276 |
-
|
| 277 |
-
for feature, metric in KAD_metrics.items():
|
| 278 |
-
|
| 279 |
-
KAD_distance, dict_output=metric.compute(reference_dict, method_dict, None)
|
| 280 |
-
|
| 281 |
-
print(f"KAD distance for method {method} and feature {feature}: {KAD_distance}")
|
| 282 |
-
|
| 283 |
-
#write the KAD distance to the dataframe
|
| 284 |
-
dataframe.at[method, "method"] = method
|
| 285 |
-
dataframe.at[method, feature] = KAD_distance
|
| 286 |
-
|
| 287 |
-
for output_key, output_value in dict_output.items():
|
| 288 |
-
if "figure" in output_key:
|
| 289 |
-
output_value.savefig(f"results_eval/{output_key}_{set}_{method}_{feature}.png")
|
| 290 |
-
print(f"Saved figure: {output_key}_{set}_{method}_{feature}.png")
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
dataframe.to_csv(filename_results, index=False)
|
| 295 |
-
print(f"Results saved to {filename_results}")
|
| 296 |
-
|
| 297 |
-
#except Exception as e:
|
| 298 |
-
|
| 299 |
-
# print(f"Error processing method {method}: {e}")
|
| 300 |
-
|
| 301 |
-
# dataframe.to_csv(filename_results, index=False)
|
| 302 |
-
# print(f"Probably incompleted Results saved to {filename_results}")
|
| 303 |
-
# continue
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/evaluation/dist_metrics.py
DELETED
|
@@ -1,971 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
from numpy.lib.scimath import sqrt as scisqrt
|
| 3 |
-
from scipy import linalg
|
| 4 |
-
import numpy as np
|
| 5 |
-
import torchaudio
|
| 6 |
-
import os
|
| 7 |
-
from importlib import import_module
|
| 8 |
-
import torch
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
from utils.feature_extractors.load_features import load_AFxRep, load_fx_encoder, load_fx_encoder_plusplus, load_CLAP
|
| 12 |
-
|
| 13 |
-
from utils.log import make_PCA_figure
|
| 14 |
-
|
| 15 |
-
SCALE_FACTOR = 100
|
| 16 |
-
|
| 17 |
-
def calc_kernel_audio_distance(
|
| 18 |
-
x: torch.Tensor,
|
| 19 |
-
y: torch.Tensor,
|
| 20 |
-
device: str,
|
| 21 |
-
bandwidth=None,
|
| 22 |
-
kernel='gaussian',
|
| 23 |
-
precision=torch.float32,
|
| 24 |
-
eps=1e-8
|
| 25 |
-
) -> torch.Tensor:
|
| 26 |
-
"""
|
| 27 |
-
Compute the Kernel Audio Distance (KAD) between two samples using PyTorch.
|
| 28 |
-
|
| 29 |
-
Args:
|
| 30 |
-
x: The first set of embeddings of shape (m, embedding_dim).
|
| 31 |
-
y: The second set of embeddings of shape (n, embedding_dim).
|
| 32 |
-
cache_dirs: Directories to cache kernel statistics.
|
| 33 |
-
bandwidth: The bandwidth value for the Gaussian RBF kernel.
|
| 34 |
-
kernel: Kernel function to use ('gaussian', 'iq', 'imq').
|
| 35 |
-
precision: Type setting for matrix calculation precision.
|
| 36 |
-
eps: Small value to prevent division by zero.
|
| 37 |
-
|
| 38 |
-
Returns:
|
| 39 |
-
The KAD between x and y embedding sets.
|
| 40 |
-
"""
|
| 41 |
-
# Ensure x and y are of the correct precision
|
| 42 |
-
x = x.to(dtype=precision, device=device)
|
| 43 |
-
y = y.to(dtype=precision, device=device)
|
| 44 |
-
|
| 45 |
-
assert bandwidth is not None, "Bandwidth must be provided for KAD calculation"
|
| 46 |
-
|
| 47 |
-
m, n = x.shape[0], y.shape[0]
|
| 48 |
-
|
| 49 |
-
# Define kernel functions
|
| 50 |
-
gamma = 1 / (2 * bandwidth**2 + eps)
|
| 51 |
-
if kernel == 'gaussian': # Gaussian Kernel
|
| 52 |
-
kernel = lambda a: torch.exp(-gamma * a)
|
| 53 |
-
elif kernel == 'iq': # Inverse Quadratic Kernel
|
| 54 |
-
kernel = lambda a: 1 / (1 + gamma * a)
|
| 55 |
-
elif kernel == 'imq': # Inverse Multiquadric Kernel
|
| 56 |
-
kernel = lambda a: 1 / torch.sqrt(1 + gamma * a)
|
| 57 |
-
else:
|
| 58 |
-
raise ValueError("Invalid kernel type. Valid kernels: 'gaussian', 'iq', 'imq'")
|
| 59 |
-
|
| 60 |
-
# Load x kernel statistics
|
| 61 |
-
xx = x @ x.T
|
| 62 |
-
x_sqnorms = torch.diagonal(xx)
|
| 63 |
-
d2_xx = x_sqnorms.unsqueeze(1) + x_sqnorms.unsqueeze(0) - 2 * xx # shape (m, m)
|
| 64 |
-
|
| 65 |
-
k_xx = kernel(d2_xx)
|
| 66 |
-
k_xx = k_xx - torch.diag(torch.diagonal(k_xx))
|
| 67 |
-
k_xx_mean = k_xx.sum() / (m * (m - 1))
|
| 68 |
-
|
| 69 |
-
# Load y kernel statistics
|
| 70 |
-
yy = y @ y.T
|
| 71 |
-
y_sqnorms = torch.diagonal(yy)
|
| 72 |
-
d2_yy = y_sqnorms.unsqueeze(1) + y_sqnorms.unsqueeze(0) - 2 * yy # shape (n, n)
|
| 73 |
-
|
| 74 |
-
k_yy = kernel(d2_yy)
|
| 75 |
-
k_yy = k_yy - torch.diag(torch.diagonal(k_yy))
|
| 76 |
-
k_yy_mean = k_yy.sum() / (n * (n - 1))
|
| 77 |
-
|
| 78 |
-
# Compute kernel statistics for xy
|
| 79 |
-
xy = x @ y.T
|
| 80 |
-
d2_xy = x_sqnorms.unsqueeze(1) + y_sqnorms.unsqueeze(0) - 2 * xy # shape (m, n)
|
| 81 |
-
k_xy = kernel(d2_xy)
|
| 82 |
-
k_xy_mean = k_xy.mean()
|
| 83 |
-
|
| 84 |
-
# Compute MMD
|
| 85 |
-
result = k_xx_mean + k_yy_mean - 2 * k_xy_mean
|
| 86 |
-
|
| 87 |
-
return result * SCALE_FACTOR
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
def median_pairwise_distance(x, subsample=None):
|
| 91 |
-
"""
|
| 92 |
-
Compute the median pairwise distance of an embedding set.
|
| 93 |
-
|
| 94 |
-
Args:
|
| 95 |
-
x: torch.Tensor of shape (n_samples, embedding_dim)
|
| 96 |
-
subsample: int, number of random pairs to consider (optional)
|
| 97 |
-
|
| 98 |
-
Returns:
|
| 99 |
-
The median pairwise distance between points in x.
|
| 100 |
-
"""
|
| 101 |
-
x = torch.tensor(x, dtype=torch.float32)
|
| 102 |
-
n_samples = x.shape[0]
|
| 103 |
-
|
| 104 |
-
if subsample is not None and subsample < n_samples * (n_samples - 1) / 2:
|
| 105 |
-
# Randomly select pairs of indices
|
| 106 |
-
idx1 = torch.randint(0, n_samples, (subsample,))
|
| 107 |
-
idx2 = torch.randint(0, n_samples, (subsample,))
|
| 108 |
-
|
| 109 |
-
# Ensure idx1 != idx2
|
| 110 |
-
mask = idx1 == idx2
|
| 111 |
-
idx2[mask] = (idx2[mask] + 1) % n_samples
|
| 112 |
-
|
| 113 |
-
# Compute distances for selected pairs
|
| 114 |
-
distances = torch.sqrt(torch.sum((x[idx1] - x[idx2])**2, dim=1))
|
| 115 |
-
else:
|
| 116 |
-
# Compute all pairwise distances
|
| 117 |
-
distances = torch.pdist(x)
|
| 118 |
-
|
| 119 |
-
return torch.median(distances).item()
|
| 120 |
-
|
| 121 |
-
class DistMetric:
|
| 122 |
-
"""
|
| 123 |
-
Base class for pairwise metrics.
|
| 124 |
-
|
| 125 |
-
This class should be subclassed to implement specific pairwise metrics.
|
| 126 |
-
"""
|
| 127 |
-
def __init__(self,type, sample_rate,*args, **kwargs):
|
| 128 |
-
"""
|
| 129 |
-
Initialize the PairwiseMetric instance.
|
| 130 |
-
|
| 131 |
-
Args:
|
| 132 |
-
*args: Variable length argument list.
|
| 133 |
-
**kwargs: Arbitrary keyword arguments.
|
| 134 |
-
"""
|
| 135 |
-
self.type=type
|
| 136 |
-
self.sample_rate=sample_rate
|
| 137 |
-
|
| 138 |
-
if self.type == "fx_encoder":
|
| 139 |
-
self.model_args= kwargs.get("fx_encoder_args", None)
|
| 140 |
-
|
| 141 |
-
assert self.model_args is not None, "model_args must be provided for fx_encoder type"
|
| 142 |
-
|
| 143 |
-
self.distance_type=self.model_args.distance_type
|
| 144 |
-
|
| 145 |
-
self.feat_extractor = load_fx_encoder(self.model_args, self.device)
|
| 146 |
-
|
| 147 |
-
#self.feat_extractor = load_effects_encoder(ckpt_path=ckpt_path).to(self.device)
|
| 148 |
-
|
| 149 |
-
elif self.type == "fx_encoder_++":
|
| 150 |
-
self.model_args= kwargs.get("fx_encoder_plusplus_args", None)
|
| 151 |
-
|
| 152 |
-
assert self.model_args is not None, "model_args must be provided for fx_encoder_plusplus type"
|
| 153 |
-
|
| 154 |
-
print(self.model_args)
|
| 155 |
-
|
| 156 |
-
self.distance_type=self.model_args.distance_type
|
| 157 |
-
|
| 158 |
-
self.feat_extractor = load_fx_encoder_plusplus(self.model_args, self.device)
|
| 159 |
-
|
| 160 |
-
#self.feat_extractor = load_effects_encoder(ckpt_path=ckpt_path).to(self.device)
|
| 161 |
-
|
| 162 |
-
elif self.type== "AFxRep-mid" or self.type== "AFxRep-side" or self.type== "AFxRep":
|
| 163 |
-
|
| 164 |
-
self.model_args= kwargs.get("AFxRep_args", None)
|
| 165 |
-
|
| 166 |
-
assert self.model_args is not None, "model_args must be provided for AFxRep type"
|
| 167 |
-
|
| 168 |
-
self.distance_type=self.model_args.distance_type
|
| 169 |
-
|
| 170 |
-
feat_extractor = load_AFxRep(self.model_args, self.device, sample_rate=self.sample_rate)
|
| 171 |
-
|
| 172 |
-
if self.type == "AFxRep-mid":
|
| 173 |
-
def feat_extractor_mid(x):
|
| 174 |
-
|
| 175 |
-
features= feat_extractor(x)
|
| 176 |
-
|
| 177 |
-
#print("features shape:", features.shape)
|
| 178 |
-
#divide by 2 to get mid and side features
|
| 179 |
-
|
| 180 |
-
feat_mid, feat_side = features.chunk(2, dim=-1)
|
| 181 |
-
|
| 182 |
-
return feat_mid
|
| 183 |
-
|
| 184 |
-
self.feat_extractor = feat_extractor_mid
|
| 185 |
-
|
| 186 |
-
elif self.type == "AFxRep-side":
|
| 187 |
-
def feat_extractor_side(x):
|
| 188 |
-
|
| 189 |
-
features= feat_extractor(x)
|
| 190 |
-
|
| 191 |
-
#divide by 2 to get mid and side features
|
| 192 |
-
|
| 193 |
-
feat_mid, feat_side = features.chunk(2, dim=-1)
|
| 194 |
-
|
| 195 |
-
return feat_side
|
| 196 |
-
|
| 197 |
-
self.feat_extractor = feat_extractor_side
|
| 198 |
-
else:
|
| 199 |
-
self.feat_extractor = feat_extractor
|
| 200 |
-
|
| 201 |
-
elif self.type == "CLAP":
|
| 202 |
-
self.model_args= kwargs.get("CLAP_args", None)
|
| 203 |
-
assert self.model_args is not None, "model_args must be provided for CLAP type"
|
| 204 |
-
|
| 205 |
-
CLAP_fn = load_CLAP(self.model_args, self.device)
|
| 206 |
-
self.feat_extractor=lambda x: CLAP_fn(x, None)
|
| 207 |
-
|
| 208 |
-
else:
|
| 209 |
-
raise ValueError(f"Unknown type: {self.type}. Supported types: fx_encoder, fx_encoder_plusplus, AFxRep-mid, AFxRep-side, AFxRep")
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
def compute(self, *args, **kwargs):
|
| 213 |
-
raise NotImplementedError("Subclasses should implement this method.")
|
| 214 |
-
|
| 215 |
-
def extract_features(self, y, y_hat, x=None):
|
| 216 |
-
|
| 217 |
-
y=torch.tensor(y).permute(1,0).unsqueeze(0).to(self.device)
|
| 218 |
-
y_hat=torch.tensor(y_hat).permute(1,0).unsqueeze(0).to(self.device)
|
| 219 |
-
|
| 220 |
-
with torch.no_grad():
|
| 221 |
-
feat_y= self.feat_extractor(y)
|
| 222 |
-
feat_y_hat= self.feat_extractor(y_hat)
|
| 223 |
-
|
| 224 |
-
if x is not None:
|
| 225 |
-
x=torch.tensor(x).permute(1,0).unsqueeze(0).to(self.device)
|
| 226 |
-
with torch.no_grad():
|
| 227 |
-
feat_x= self.feat_extractor(x)
|
| 228 |
-
else:
|
| 229 |
-
feat_x=None
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
return feat_y, feat_y_hat, feat_x
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
def do_TSNE_figure(self, dict_features_y, dict_features_y_hat, dict_features_x=None, fit_mode="target", dict_cluster=None):
|
| 236 |
-
"""
|
| 237 |
-
Perform PCA on the features and create a figure.
|
| 238 |
-
|
| 239 |
-
Args:
|
| 240 |
-
dict_features_y (dict): Dictionary containing features for the first set.
|
| 241 |
-
dict_features_y_hat (dict): Dictionary containing features for the second set.
|
| 242 |
-
|
| 243 |
-
Returns:
|
| 244 |
-
fig: The created figure.
|
| 245 |
-
"""
|
| 246 |
-
import matplotlib.pyplot as plt
|
| 247 |
-
from sklearn.decomposition import PCA
|
| 248 |
-
from sklearn.manifold import TSNE
|
| 249 |
-
|
| 250 |
-
y_values = list(dict_features_y.values())
|
| 251 |
-
y_values = torch.cat(y_values, dim=0)
|
| 252 |
-
|
| 253 |
-
y_hat_values = list(dict_features_y_hat.values())
|
| 254 |
-
y_hat_values = torch.cat(y_hat_values, dim=0)
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
if dict_cluster is not None:
|
| 258 |
-
clusters= list(dict_cluster.values())
|
| 259 |
-
clusters = [c.unsqueeze(0) if c.dim() == 0 else c for c in clusters]
|
| 260 |
-
clusters = torch.cat(clusters, dim=0)
|
| 261 |
-
|
| 262 |
-
#check different clusters (0,1,2,3...)
|
| 263 |
-
assert len(torch.unique(clusters)) == 2, "Only two clusters are supported for PCA visualization"
|
| 264 |
-
C0= clusters == 0
|
| 265 |
-
C1= clusters == 1
|
| 266 |
-
|
| 267 |
-
#manage Nans in y_values
|
| 268 |
-
y_values=torch.nan_to_num(y_values, nan=0)
|
| 269 |
-
y_hat_values=torch.nan_to_num(y_hat_values, nan=0)
|
| 270 |
-
|
| 271 |
-
#if self.pca is None:
|
| 272 |
-
self.tsne =TSNE(n_components=2, perplexity=30)
|
| 273 |
-
|
| 274 |
-
combined_data = torch.cat([y_values, y_hat_values], dim=0).cpu().numpy()
|
| 275 |
-
combined_result = self.tsne.fit_transform(combined_data)
|
| 276 |
-
|
| 277 |
-
# Split the results back
|
| 278 |
-
n_y = y_values.shape[0]
|
| 279 |
-
tsne_result = combined_result[:n_y]
|
| 280 |
-
tsne_result_hat = combined_result[n_y:]
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
if dict_cluster is not None:
|
| 284 |
-
data_dict = {
|
| 285 |
-
"y_C0": tsne_result[C0],
|
| 286 |
-
"y_hat_C0": tsne_result_hat[C0],
|
| 287 |
-
"y_C1": tsne_result[C1],
|
| 288 |
-
"y_hat_C1": tsne_result_hat[C1]
|
| 289 |
-
}
|
| 290 |
-
else:
|
| 291 |
-
data_dict = {
|
| 292 |
-
"y": tsne_result,
|
| 293 |
-
"y_hat": tsne_result_hat
|
| 294 |
-
}
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
fig= make_PCA_figure(data_dict, title=self.type + " TSNE")
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
return fig
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
def do_PCA_figure(self, dict_features_y, dict_features_y_hat, dict_features_x=None, fit_mode="target", dict_cluster=None):
|
| 305 |
-
"""
|
| 306 |
-
Perform PCA on the features and create a figure.
|
| 307 |
-
|
| 308 |
-
Args:
|
| 309 |
-
dict_features_y (dict): Dictionary containing features for the first set.
|
| 310 |
-
dict_features_y_hat (dict): Dictionary containing features for the second set.
|
| 311 |
-
|
| 312 |
-
Returns:
|
| 313 |
-
fig: The created figure.
|
| 314 |
-
"""
|
| 315 |
-
import matplotlib.pyplot as plt
|
| 316 |
-
from sklearn.decomposition import PCA
|
| 317 |
-
|
| 318 |
-
y_values = list(dict_features_y.values())
|
| 319 |
-
y_values = torch.cat(y_values, dim=0)
|
| 320 |
-
|
| 321 |
-
y_hat_values = list(dict_features_y_hat.values())
|
| 322 |
-
y_hat_values = torch.cat(y_hat_values, dim=0)
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
if dict_cluster is not None:
|
| 326 |
-
clusters= list(dict_cluster.values())
|
| 327 |
-
clusters = [c.unsqueeze(0) if c.dim() == 0 else c for c in clusters]
|
| 328 |
-
clusters = torch.cat(clusters, dim=0)
|
| 329 |
-
|
| 330 |
-
#check different clusters (0,1,2,3...)
|
| 331 |
-
assert len(torch.unique(clusters)) == 2, "Only two clusters are supported for PCA visualization"
|
| 332 |
-
C0= clusters == 0
|
| 333 |
-
C1= clusters == 1
|
| 334 |
-
|
| 335 |
-
#manage Nans in y_values
|
| 336 |
-
y_values=torch.nan_to_num(y_values, nan=0)
|
| 337 |
-
y_hat_values=torch.nan_to_num(y_hat_values, nan=0)
|
| 338 |
-
|
| 339 |
-
if self.pca is None:
|
| 340 |
-
self.pca = PCA(n_components=2)
|
| 341 |
-
if fit_mode == "target":
|
| 342 |
-
pca_result = self.pca.fit_transform(y_values.cpu().numpy())
|
| 343 |
-
elif fit_mode == "all":
|
| 344 |
-
self.pca = self.pca.fit(torch.cat([y_values,y_hat_values], dim=0).cpu().numpy())
|
| 345 |
-
pca_result= self.pca.transform(y_values.cpu().numpy())
|
| 346 |
-
else:
|
| 347 |
-
pca_result = self.pca.transform(y_values.cpu().numpy())
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
#project y_hat values into the same PCA space
|
| 351 |
-
pca_result_hat = self.pca.transform(y_hat_values.cpu().numpy())
|
| 352 |
-
|
| 353 |
-
if dict_cluster is not None:
|
| 354 |
-
data_dict = {
|
| 355 |
-
"y_C0": pca_result[C0],
|
| 356 |
-
"y_hat_C0": pca_result_hat[C0],
|
| 357 |
-
"y_C1": pca_result[C1],
|
| 358 |
-
"y_hat_C1": pca_result_hat[C1]
|
| 359 |
-
}
|
| 360 |
-
else:
|
| 361 |
-
data_dict = {
|
| 362 |
-
"y": pca_result,
|
| 363 |
-
"y_hat": pca_result_hat
|
| 364 |
-
}
|
| 365 |
-
|
| 366 |
-
if dict_features_x is not None:
|
| 367 |
-
x_values = list(dict_features_x.values())
|
| 368 |
-
x_values = torch.cat(x_values, dim=0)
|
| 369 |
-
|
| 370 |
-
pca_result_x = self.pca.transform(x_values.cpu().numpy())
|
| 371 |
-
|
| 372 |
-
data_dict["x"] = pca_result_x
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
fig= make_PCA_figure(data_dict, title=self.type + " PCA")
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
return fig
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
class KADFeatures(DistMetric):
|
| 385 |
-
"""
|
| 386 |
-
Class for computing the pairwise spectral metric.
|
| 387 |
-
|
| 388 |
-
This class inherits from PairwiseMetric and implements the compute method
|
| 389 |
-
to calculate the pairwise spectral metric.
|
| 390 |
-
"""
|
| 391 |
-
def __init__(self,
|
| 392 |
-
type=None,
|
| 393 |
-
sample_rate=44100,
|
| 394 |
-
KAD_args=None,
|
| 395 |
-
classwise=False,
|
| 396 |
-
*args, **kwargs):
|
| 397 |
-
"""
|
| 398 |
-
Initialize the PairwiseSpectral instance.
|
| 399 |
-
|
| 400 |
-
Args:
|
| 401 |
-
*args: Variable length argument list.
|
| 402 |
-
**kwargs: Arbitrary keyword arguments.
|
| 403 |
-
"""
|
| 404 |
-
self.type = type
|
| 405 |
-
|
| 406 |
-
self.classwise = classwise
|
| 407 |
-
|
| 408 |
-
self.device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
self.KAD_args = KAD_args
|
| 412 |
-
|
| 413 |
-
assert self.KAD_args is not None, "FAD_args must be provided"
|
| 414 |
-
|
| 415 |
-
if self.KAD_args.do_PCA_figure:
|
| 416 |
-
self.pca = None
|
| 417 |
-
|
| 418 |
-
self.bandwidth = None
|
| 419 |
-
|
| 420 |
-
super().__init__(self.type, sample_rate, *args, **kwargs)
|
| 421 |
-
|
| 422 |
-
def calculate_KAD_distance_classwise(self, dict_features_y, dict_features_y_hat, dict_cluster):
|
| 423 |
-
|
| 424 |
-
if dict_cluster is not None:
|
| 425 |
-
clusters= list(dict_cluster.values())
|
| 426 |
-
clusters = [c.unsqueeze(0) if c.dim() == 0 else c for c in clusters]
|
| 427 |
-
clusters = torch.cat(clusters, dim=0)
|
| 428 |
-
|
| 429 |
-
#check different clusters (0,1,2,3...)
|
| 430 |
-
assert len(torch.unique(clusters)) == 2, "Only two clusters are supported for PCA visualization"
|
| 431 |
-
C0= clusters == 0
|
| 432 |
-
C1= clusters == 1
|
| 433 |
-
|
| 434 |
-
y= torch.cat(list(dict_features_y.values()), dim=0)
|
| 435 |
-
y_hat= torch.cat(list(dict_features_y_hat.values()), dim=0)
|
| 436 |
-
|
| 437 |
-
y_C0= y[C0]
|
| 438 |
-
y_hat_C0= y_hat[C0]
|
| 439 |
-
|
| 440 |
-
with torch.no_grad():
|
| 441 |
-
KAD_C0=calc_kernel_audio_distance(y_C0, y_hat_C0, device=self.device, bandwidth=self.bandwidth, kernel=self.KAD_args.kernel, precision=torch.float32)
|
| 442 |
-
|
| 443 |
-
y_C1= y[C1]
|
| 444 |
-
y_hat_C1= y_hat[C1]
|
| 445 |
-
with torch.no_grad():
|
| 446 |
-
KAD_C1=calc_kernel_audio_distance(y_C1, y_hat_C1, device=self.device, bandwidth=self.bandwidth, kernel=self.KAD_args.kernel, precision=torch.float32)
|
| 447 |
-
|
| 448 |
-
KAD= (KAD_C0 + KAD_C1) / 2
|
| 449 |
-
|
| 450 |
-
return KAD.cpu().item()
|
| 451 |
-
def calculate_KAD_distance(self, dict_features_y, dict_features_y_hat):
|
| 452 |
-
y= torch.cat(list(dict_features_y.values()), dim=0)
|
| 453 |
-
y_hat= torch.cat(list(dict_features_y_hat.values()), dim=0)
|
| 454 |
-
with torch.no_grad():
|
| 455 |
-
KAD=calc_kernel_audio_distance(y, y_hat, device=self.device, bandwidth=self.bandwidth, kernel=self.KAD_args.kernel, precision=torch.float32)
|
| 456 |
-
return KAD.cpu().item()
|
| 457 |
-
|
| 458 |
-
def calculate_bandwidth(self, dict_features_y):
|
| 459 |
-
print("Calculating bandwidth...")
|
| 460 |
-
y= torch.cat(list(dict_features_y.values()), dim=0)
|
| 461 |
-
print("y shape:", y.shape)
|
| 462 |
-
self.bandwidth = median_pairwise_distance(y)
|
| 463 |
-
print("Bandwidth:", self.bandwidth)
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
def compute(self, dict_y, dict_y_hat, dict_x, dict_p_hat=None, dict_cluster=None, normalize=False, *args, **kwargs):
|
| 468 |
-
"""
|
| 469 |
-
Compute the pairwise spectral metric.
|
| 470 |
-
|
| 471 |
-
Args:
|
| 472 |
-
*args: Variable length argument list.
|
| 473 |
-
**kwargs: Arbitrary keyword arguments.
|
| 474 |
-
|
| 475 |
-
Returns:
|
| 476 |
-
The computed pairwise spectral metric.
|
| 477 |
-
"""
|
| 478 |
-
|
| 479 |
-
print("Computing KAD distance...")
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
if self.classwise:
|
| 483 |
-
assert dict_cluster is not None, "dict_cluster must be provided if classwise is True"
|
| 484 |
-
|
| 485 |
-
dict_features_y={}
|
| 486 |
-
dict_features_y_hat={}
|
| 487 |
-
#dict_features_x={}
|
| 488 |
-
|
| 489 |
-
if dict_y_hat is None:
|
| 490 |
-
print("computing KAD with style embeddings")
|
| 491 |
-
assert dict_p_hat is not None, "dict_p_hat must be provided if dict_y_hat is None"
|
| 492 |
-
|
| 493 |
-
for key in dict_y.keys():
|
| 494 |
-
y= dict_y[key]
|
| 495 |
-
#x= dict_x[key]
|
| 496 |
-
|
| 497 |
-
embed= dict_p_hat[key]
|
| 498 |
-
embed=torch.tensor(embed).to(self.device).unsqueeze(0)
|
| 499 |
-
|
| 500 |
-
embed_mid, embed_side = torch.chunk(embed, 2, dim=-1)
|
| 501 |
-
|
| 502 |
-
if self.type== "AFxRep-mid":
|
| 503 |
-
p_hat= embed_mid
|
| 504 |
-
elif self.type== "AFxRep-side":
|
| 505 |
-
p_hat= embed_side
|
| 506 |
-
elif self.type== "AFxRep":
|
| 507 |
-
p_hat= embed
|
| 508 |
-
|
| 509 |
-
#if x.shape[-2] == 1:
|
| 510 |
-
# x = x.repeat( 2, 1)
|
| 511 |
-
|
| 512 |
-
#assert x.shape == y.shape, f"Shape mismatch for key {key}: {x.shape} vs {y.shape}"
|
| 513 |
-
|
| 514 |
-
c, d=y.shape
|
| 515 |
-
#assert b==1, f"Expected batch size of 1, got {b} for key {key}"
|
| 516 |
-
|
| 517 |
-
assert c==2, f"Expected 2 channels, got {c} for key {key}"
|
| 518 |
-
|
| 519 |
-
y=y.T
|
| 520 |
-
#x=x.T
|
| 521 |
-
|
| 522 |
-
y=torch.tensor(y).permute(1,0).unsqueeze(0).to(self.device)
|
| 523 |
-
#x=torch.tensor(x).permute(1,0).unsqueeze(0).to(self.device)
|
| 524 |
-
|
| 525 |
-
with torch.no_grad():
|
| 526 |
-
feat_y= self.feat_extractor(y)
|
| 527 |
-
#feat_x= self.feat_extractor(x)
|
| 528 |
-
|
| 529 |
-
assert p_hat.shape == feat_y.shape, f"Shape mismatch for key {key}: {p_hat.shape} vs {feat_y.shape}"
|
| 530 |
-
#assert p_hat.shape == feat_x.shape, f"Shape mismatch for key {key}: {p_hat.shape} vs {feat_x.shape}"
|
| 531 |
-
|
| 532 |
-
dict_features_y[key] = feat_y
|
| 533 |
-
#dict_features_x[key] = feat_x
|
| 534 |
-
dict_features_y_hat[key] = p_hat
|
| 535 |
-
|
| 536 |
-
else:
|
| 537 |
-
for key in dict_y.keys():
|
| 538 |
-
y= dict_y[key]
|
| 539 |
-
#x= dict_x[key]
|
| 540 |
-
y_hat= dict_y_hat[key]
|
| 541 |
-
|
| 542 |
-
#if x.shape[-2] == 1:
|
| 543 |
-
# x = x.repeat( 2, 1)
|
| 544 |
-
|
| 545 |
-
assert y.shape == y_hat.shape, f"Shape mismatch for key {key}: {y.shape} vs {y_hat.shape}"
|
| 546 |
-
#assert x.shape == y.shape, f"Shape mismatch for key {key}: {x.shape} vs {y.shape}"
|
| 547 |
-
|
| 548 |
-
c, d=y.shape
|
| 549 |
-
#assert b==1, f"Expected batch size of 1, got {b} for key {key}"
|
| 550 |
-
|
| 551 |
-
assert c==2, f"Expected 2 channels, got {c} for key {key}"
|
| 552 |
-
|
| 553 |
-
y=y.T
|
| 554 |
-
y_hat=y_hat.T
|
| 555 |
-
#x=x.T
|
| 556 |
-
|
| 557 |
-
y=torch.tensor(y).permute(1,0).unsqueeze(0).to(self.device)
|
| 558 |
-
y_hat=torch.tensor(y_hat).permute(1,0).unsqueeze(0).to(self.device)
|
| 559 |
-
#x=torch.tensor(x).permute(1,0).unsqueeze(0).to(self.device)
|
| 560 |
-
|
| 561 |
-
with torch.no_grad():
|
| 562 |
-
#try:
|
| 563 |
-
feat_y= self.feat_extractor(y)
|
| 564 |
-
feat_y_hat= self.feat_extractor(y_hat)
|
| 565 |
-
#except Exception as e:
|
| 566 |
-
#print(f"Error extracting features for key {key}: {e}")
|
| 567 |
-
# continue
|
| 568 |
-
#feat_x= self.feat_extractor(x)
|
| 569 |
-
|
| 570 |
-
dict_features_y[key] = feat_y
|
| 571 |
-
dict_features_y_hat[key] = feat_y_hat
|
| 572 |
-
#dict_features_x[key] = feat_x
|
| 573 |
-
|
| 574 |
-
if normalize:
|
| 575 |
-
#compute normalization statistics wrt to reference features
|
| 576 |
-
y_values = list(dict_features_y.values())
|
| 577 |
-
y_values = torch.cat(y_values, dim=0)
|
| 578 |
-
mean= y_values.mean(dim=0, keepdim=True)
|
| 579 |
-
std= y_values.std(dim=0, keepdim=True)
|
| 580 |
-
|
| 581 |
-
#normalize features
|
| 582 |
-
for key in dict_features_y.keys():
|
| 583 |
-
dict_features_y[key] = (dict_features_y[key] - mean) / (std + 1e-8)
|
| 584 |
-
dict_features_y_hat[key] = (dict_features_y_hat[key] - mean) / (std + 1e-8)
|
| 585 |
-
|
| 586 |
-
|
| 587 |
-
dict_output = {}
|
| 588 |
-
if self.KAD_args.do_PCA_figure:
|
| 589 |
-
fig=self.do_PCA_figure(dict_features_y, dict_features_y_hat, fit_mode=self.KAD_args.PCA_fit_mode, dict_cluster=dict_cluster)
|
| 590 |
-
key= self.type+ "_PCA_figure"
|
| 591 |
-
dict_output = {key: fig}
|
| 592 |
-
|
| 593 |
-
if self.KAD_args.do_TSNE_figure:
|
| 594 |
-
fig=self.do_TSNE_figure(dict_features_y, dict_features_y_hat, fit_mode="all", dict_cluster=dict_cluster)
|
| 595 |
-
key= self.type+ "_TSNE_figure"
|
| 596 |
-
dict_output[key] = fig
|
| 597 |
-
|
| 598 |
-
|
| 599 |
-
# Compute mean features
|
| 600 |
-
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
# Compute the distance between the mean features
|
| 604 |
-
#FAD_distance = self.calculate_FAD_distance(y_mean, y_cov, y_hat_mean, y_hat_cov)
|
| 605 |
-
if self.bandwidth is None:
|
| 606 |
-
self.calculate_bandwidth(dict_features_y)
|
| 607 |
-
|
| 608 |
-
if self.classwise:
|
| 609 |
-
#calculate KAD distance for each class
|
| 610 |
-
KAD_distance = self.calculate_KAD_distance_classwise(dict_features_y, dict_features_y_hat, dict_cluster)
|
| 611 |
-
else:
|
| 612 |
-
|
| 613 |
-
KAD_distance = self.calculate_KAD_distance(dict_features_y, dict_features_y_hat)
|
| 614 |
-
|
| 615 |
-
|
| 616 |
-
return KAD_distance, dict_output
|
| 617 |
-
|
| 618 |
-
class FADFeatures(DistMetric):
|
| 619 |
-
"""
|
| 620 |
-
Class for computing the pairwise spectral metric.
|
| 621 |
-
|
| 622 |
-
This class inherits from PairwiseMetric and implements the compute method
|
| 623 |
-
to calculate the pairwise spectral metric.
|
| 624 |
-
"""
|
| 625 |
-
def __init__(self,
|
| 626 |
-
type=None,
|
| 627 |
-
sample_rate=44100,
|
| 628 |
-
FAD_args=None,
|
| 629 |
-
*args, **kwargs):
|
| 630 |
-
"""
|
| 631 |
-
Initialize the PairwiseSpectral instance.
|
| 632 |
-
|
| 633 |
-
Args:
|
| 634 |
-
*args: Variable length argument list.
|
| 635 |
-
**kwargs: Arbitrary keyword arguments.
|
| 636 |
-
"""
|
| 637 |
-
self.type = type
|
| 638 |
-
|
| 639 |
-
self.device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
self.FAD_args = FAD_args
|
| 643 |
-
|
| 644 |
-
assert self.FAD_args is not None, "FAD_args must be provided"
|
| 645 |
-
|
| 646 |
-
if self.FAD_args.do_PCA_figure:
|
| 647 |
-
self.pca = None
|
| 648 |
-
|
| 649 |
-
super().__init__(self.type, sample_rate, *args, **kwargs)
|
| 650 |
-
|
| 651 |
-
def calculate_FAD_distance(self, mu1, sigma1, mu2, sigma2, eps=1e-6):
|
| 652 |
-
|
| 653 |
-
"""Numpy implementation of the Frechet Distance.
|
| 654 |
-
The Frechet distance between two multivariate Gaussians X_1 ~ N(mu_1, C_1)
|
| 655 |
-
and X_2 ~ N(mu_2, C_2) is
|
| 656 |
-
d^2 = ||mu_1 - mu_2||^2 + Tr(C_1 + C_2 - 2*sqrt(C_1*C_2)).
|
| 657 |
-
|
| 658 |
-
Stable version by Dougal J. Sutherland.
|
| 659 |
-
|
| 660 |
-
Params:
|
| 661 |
-
-- mu1 : Numpy array containing the activations of a layer of the
|
| 662 |
-
inception net (like returned by the function 'get_predictions')
|
| 663 |
-
for generated samples.
|
| 664 |
-
-- mu2 : The sample mean over activations, precalculated on an
|
| 665 |
-
representative data set.
|
| 666 |
-
-- sigma1: The covariance matrix over activations for generated samples.
|
| 667 |
-
-- sigma2: The covariance matrix over activations, precalculated on an
|
| 668 |
-
representative data set.
|
| 669 |
-
|
| 670 |
-
Returns:
|
| 671 |
-
-- : The Frechet Distance.
|
| 672 |
-
"""
|
| 673 |
-
mu1=mu1.detach().cpu().numpy()
|
| 674 |
-
mu2=mu2.detach().cpu().numpy()
|
| 675 |
-
|
| 676 |
-
sigma1=sigma1.detach().cpu().numpy()
|
| 677 |
-
sigma2=sigma2.detach().cpu().numpy()
|
| 678 |
-
|
| 679 |
-
|
| 680 |
-
mu1 = np.atleast_1d(mu1)
|
| 681 |
-
mu2 = np.atleast_1d(mu2)
|
| 682 |
-
|
| 683 |
-
sigma1 = np.atleast_2d(sigma1)
|
| 684 |
-
sigma2 = np.atleast_2d(sigma2)
|
| 685 |
-
|
| 686 |
-
assert (
|
| 687 |
-
mu1.shape == mu2.shape
|
| 688 |
-
), "Training and test mean vectors have different lengths"
|
| 689 |
-
assert (
|
| 690 |
-
sigma1.shape == sigma2.shape
|
| 691 |
-
), "Training and test covariances have different dimensions"
|
| 692 |
-
|
| 693 |
-
diff = mu1 - mu2
|
| 694 |
-
|
| 695 |
-
# Product might be almost singular
|
| 696 |
-
covmean, _ = linalg.sqrtm(sigma1.dot(sigma2), disp=False)
|
| 697 |
-
if not np.isfinite(covmean).all():
|
| 698 |
-
msg = (
|
| 699 |
-
"fid calculation produces singular product; "
|
| 700 |
-
"adding %s to diagonal of cov estimates"
|
| 701 |
-
) % eps
|
| 702 |
-
offset = np.eye(sigma1.shape[0]) * eps
|
| 703 |
-
covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset))
|
| 704 |
-
|
| 705 |
-
# Numerical error might give slight imaginary component
|
| 706 |
-
if np.iscomplexobj(covmean):
|
| 707 |
-
if not np.allclose(np.diagonal(covmean).imag, 0, atol=1e-3):
|
| 708 |
-
m = np.max(np.abs(covmean.imag))
|
| 709 |
-
raise ValueError("Imaginary component {}".format(m))
|
| 710 |
-
covmean = covmean.real
|
| 711 |
-
|
| 712 |
-
tr_covmean = np.trace(covmean)
|
| 713 |
-
|
| 714 |
-
return diff.dot(diff) + np.trace(sigma1) + np.trace(sigma2) - 2 * tr_covmean
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
def calculate_emb_statistics(self, features_dicto):
|
| 718 |
-
"""
|
| 719 |
-
Calculate the mean and standard deviation of the features.
|
| 720 |
-
|
| 721 |
-
Args:
|
| 722 |
-
features_dicto (dict): Dictionary containing features for each key.
|
| 723 |
-
|
| 724 |
-
Returns:
|
| 725 |
-
mean_features (torch.Tensor): Mean of the features.
|
| 726 |
-
std_features (torch.Tensor): Standard deviation of the features.
|
| 727 |
-
"""
|
| 728 |
-
|
| 729 |
-
all_features = torch.cat(list(features_dicto.values()), dim=0)
|
| 730 |
-
|
| 731 |
-
|
| 732 |
-
#mean
|
| 733 |
-
#mean_features = all_features.mean(dim=0)
|
| 734 |
-
|
| 735 |
-
#cov
|
| 736 |
-
#cov_features = torch.cov(all_features.T)
|
| 737 |
-
mean_features = np.mean(all_features.cpu().numpy(), axis=0)
|
| 738 |
-
|
| 739 |
-
cov_features= np.cov(all_features.cpu().numpy(), rowvar=False)
|
| 740 |
-
|
| 741 |
-
|
| 742 |
-
|
| 743 |
-
return torch.tensor(mean_features), torch.tensor(cov_features)
|
| 744 |
-
|
| 745 |
-
|
| 746 |
-
def do_PCA_figure(self, dict_features_y, dict_features_y_hat, dict_features_x=None):
|
| 747 |
-
"""
|
| 748 |
-
Perform PCA on the features and create a figure.
|
| 749 |
-
|
| 750 |
-
Args:
|
| 751 |
-
dict_features_y (dict): Dictionary containing features for the first set.
|
| 752 |
-
dict_features_y_hat (dict): Dictionary containing features for the second set.
|
| 753 |
-
|
| 754 |
-
Returns:
|
| 755 |
-
fig: The created figure.
|
| 756 |
-
"""
|
| 757 |
-
import matplotlib.pyplot as plt
|
| 758 |
-
from sklearn.decomposition import PCA
|
| 759 |
-
|
| 760 |
-
y_values = list(dict_features_y.values())
|
| 761 |
-
y_values = torch.cat(y_values, dim=0)
|
| 762 |
-
|
| 763 |
-
|
| 764 |
-
|
| 765 |
-
if self.pca is None:
|
| 766 |
-
self.pca = PCA(n_components=2)
|
| 767 |
-
pca_result = self.pca.fit_transform(y_values.cpu().numpy())
|
| 768 |
-
else:
|
| 769 |
-
pca_result = self.pca.transform(y_values.cpu().numpy())
|
| 770 |
-
|
| 771 |
-
|
| 772 |
-
y_hat_values = list(dict_features_y_hat.values())
|
| 773 |
-
y_hat_values = torch.cat(y_hat_values, dim=0)
|
| 774 |
-
|
| 775 |
-
#project y_hat values into the same PCA space
|
| 776 |
-
pca_result_hat = self.pca.transform(y_hat_values.cpu().numpy())
|
| 777 |
-
|
| 778 |
-
data_dict = {
|
| 779 |
-
"y": pca_result,
|
| 780 |
-
"y_hat": pca_result_hat
|
| 781 |
-
}
|
| 782 |
-
|
| 783 |
-
if dict_features_x is not None:
|
| 784 |
-
x_values = list(dict_features_x.values())
|
| 785 |
-
x_values = torch.cat(x_values, dim=0)
|
| 786 |
-
|
| 787 |
-
pca_result_x = self.pca.transform(x_values.cpu().numpy())
|
| 788 |
-
|
| 789 |
-
data_dict["x"] = pca_result_x
|
| 790 |
-
|
| 791 |
-
|
| 792 |
-
|
| 793 |
-
fig= make_PCA_figure(data_dict, title=self.type + " PCA")
|
| 794 |
-
|
| 795 |
-
|
| 796 |
-
return fig
|
| 797 |
-
|
| 798 |
-
|
| 799 |
-
|
| 800 |
-
|
| 801 |
-
|
| 802 |
-
def compute(self, dict_y, dict_y_hat, dict_x, dict_p_hat=None, dict_cluster=None, *args, **kwargs):
|
| 803 |
-
"""
|
| 804 |
-
Compute the pairwise spectral metric.
|
| 805 |
-
|
| 806 |
-
Args:
|
| 807 |
-
*args: Variable length argument list.
|
| 808 |
-
**kwargs: Arbitrary keyword arguments.
|
| 809 |
-
|
| 810 |
-
Returns:
|
| 811 |
-
The computed pairwise spectral metric.
|
| 812 |
-
"""
|
| 813 |
-
|
| 814 |
-
print("Computing FAD distance...")
|
| 815 |
-
|
| 816 |
-
|
| 817 |
-
|
| 818 |
-
dict_features_y={}
|
| 819 |
-
dict_features_y_hat={}
|
| 820 |
-
#dict_features_x={}
|
| 821 |
-
|
| 822 |
-
if dict_y_hat is None:
|
| 823 |
-
print("computing FAD with style embeddings")
|
| 824 |
-
assert dict_p_hat is not None, "dict_p_hat must be provided if dict_y_hat is None"
|
| 825 |
-
|
| 826 |
-
for key in dict_y.keys():
|
| 827 |
-
y= dict_y[key]
|
| 828 |
-
#x= dict_x[key]
|
| 829 |
-
|
| 830 |
-
embed= dict_p_hat[key]
|
| 831 |
-
embed=torch.tensor(embed).to(self.device).unsqueeze(0)
|
| 832 |
-
|
| 833 |
-
print("embed shape:", embed.shape)
|
| 834 |
-
embed_mid, embed_side = torch.chunk(embed, 2, dim=-1)
|
| 835 |
-
|
| 836 |
-
if self.type== "AFxRep-mid":
|
| 837 |
-
p_hat= embed_mid
|
| 838 |
-
elif self.type== "AFxRep-side":
|
| 839 |
-
p_hat= embed_side
|
| 840 |
-
elif self.type== "AFxRep":
|
| 841 |
-
p_hat= embed
|
| 842 |
-
|
| 843 |
-
#if x.shape[-2] == 1:
|
| 844 |
-
# x = x.repeat( 2, 1)
|
| 845 |
-
|
| 846 |
-
#assert x.shape == y.shape, f"Shape mismatch for key {key}: {x.shape} vs {y.shape}"
|
| 847 |
-
|
| 848 |
-
c, d=y.shape
|
| 849 |
-
#assert b==1, f"Expected batch size of 1, got {b} for key {key}"
|
| 850 |
-
|
| 851 |
-
assert c==2, f"Expected 2 channels, got {c} for key {key}"
|
| 852 |
-
|
| 853 |
-
y=y.T
|
| 854 |
-
#x=x.T
|
| 855 |
-
|
| 856 |
-
y=torch.tensor(y).permute(1,0).unsqueeze(0).to(self.device)
|
| 857 |
-
#x=torch.tensor(x).permute(1,0).unsqueeze(0).to(self.device)
|
| 858 |
-
|
| 859 |
-
with torch.no_grad():
|
| 860 |
-
feat_y= self.feat_extractor(y)
|
| 861 |
-
#feat_x= self.feat_extractor(x)
|
| 862 |
-
|
| 863 |
-
assert p_hat.shape == feat_y.shape, f"Shape mismatch for key {key}: {p_hat.shape} vs {feat_y.shape}"
|
| 864 |
-
#assert p_hat.shape == feat_x.shape, f"Shape mismatch for key {key}: {p_hat.shape} vs {feat_x.shape}"
|
| 865 |
-
|
| 866 |
-
dict_features_y[key] = feat_y
|
| 867 |
-
#dict_features_x[key] = feat_x
|
| 868 |
-
dict_features_y_hat[key] = p_hat
|
| 869 |
-
|
| 870 |
-
else:
|
| 871 |
-
for key in dict_y.keys():
|
| 872 |
-
y= dict_y[key]
|
| 873 |
-
#x= dict_x[key]
|
| 874 |
-
y_hat= dict_y_hat[key]
|
| 875 |
-
|
| 876 |
-
#if x.shape[-2] == 1:
|
| 877 |
-
# x = x.repeat( 2, 1)
|
| 878 |
-
|
| 879 |
-
assert y.shape == y_hat.shape, f"Shape mismatch for key {key}: {y.shape} vs {y_hat.shape}"
|
| 880 |
-
#assert x.shape == y.shape, f"Shape mismatch for key {key}: {x.shape} vs {y.shape}"
|
| 881 |
-
|
| 882 |
-
c, d=y.shape
|
| 883 |
-
#assert b==1, f"Expected batch size of 1, got {b} for key {key}"
|
| 884 |
-
|
| 885 |
-
assert c==2, f"Expected 2 channels, got {c} for key {key}"
|
| 886 |
-
|
| 887 |
-
y=y.T
|
| 888 |
-
y_hat=y_hat.T
|
| 889 |
-
#x=x.T
|
| 890 |
-
|
| 891 |
-
y=torch.tensor(y).permute(1,0).unsqueeze(0).to(self.device)
|
| 892 |
-
y_hat=torch.tensor(y_hat).permute(1,0).unsqueeze(0).to(self.device)
|
| 893 |
-
#x=torch.tensor(x).permute(1,0).unsqueeze(0).to(self.device)
|
| 894 |
-
|
| 895 |
-
with torch.no_grad():
|
| 896 |
-
feat_y= self.feat_extractor(y)
|
| 897 |
-
feat_y_hat= self.feat_extractor(y_hat)
|
| 898 |
-
#feat_x= self.feat_extractor(x)
|
| 899 |
-
|
| 900 |
-
dict_features_y[key] = feat_y
|
| 901 |
-
dict_features_y_hat[key] = feat_y_hat
|
| 902 |
-
#dict_features_x[key] = feat_x
|
| 903 |
-
|
| 904 |
-
dict_output = {}
|
| 905 |
-
if self.FAD_args.do_PCA_figure:
|
| 906 |
-
fig=self.do_PCA_figure(dict_features_y, dict_features_y_hat, fit_mode="target")
|
| 907 |
-
key= self.type+ "_PCA_figure"
|
| 908 |
-
dict_output[key] = fig
|
| 909 |
-
|
| 910 |
-
if self.FAD_args.do_TSNE_figure:
|
| 911 |
-
fig=self.do_TSNE_figure(dict_features_y, dict_features_y_hat, fit_mode="target")
|
| 912 |
-
key= self.type+ "_TSNE_figure"
|
| 913 |
-
dict_output[key] = fig
|
| 914 |
-
|
| 915 |
-
# Compute mean features
|
| 916 |
-
|
| 917 |
-
y_mean, y_cov = self.calculate_emb_statistics(dict_features_y)
|
| 918 |
-
|
| 919 |
-
y_hat_mean, y_hat_cov = self.calculate_emb_statistics(dict_features_y_hat)
|
| 920 |
-
|
| 921 |
-
|
| 922 |
-
# Compute the distance between the mean features
|
| 923 |
-
#FAD_distance = self.calculate_FAD_distance(y_mean, y_cov, y_hat_mean, y_hat_cov)
|
| 924 |
-
FAD_distance = self.calculate_FAD_distance(y_mean, y_cov, y_hat_mean, y_hat_cov)
|
| 925 |
-
|
| 926 |
-
|
| 927 |
-
return FAD_distance, dict_output
|
| 928 |
-
|
| 929 |
-
|
| 930 |
-
def metric_factory(metric_name, sample_rate, *args, **kwargs):
|
| 931 |
-
"""
|
| 932 |
-
Factory function to create a metric function based on the metric name.
|
| 933 |
-
|
| 934 |
-
Args:
|
| 935 |
-
metric_name (str): The name of the metric to create.
|
| 936 |
-
*args: Variable length argument list.
|
| 937 |
-
**kwargs: Arbitrary keyword arguments.
|
| 938 |
-
|
| 939 |
-
Returns:
|
| 940 |
-
An instance of a class that implements the metric function.
|
| 941 |
-
"""
|
| 942 |
-
if metric_name == "fad-fx_encoder":
|
| 943 |
-
return FADFeatures(*args, **kwargs, type="fx_encoder", sample_rate=sample_rate )
|
| 944 |
-
elif metric_name == "fad-AFxRep":
|
| 945 |
-
return FADFeatures(*args, **kwargs, type="AFxRep", sample_rate=sample_rate)
|
| 946 |
-
elif metric_name == "fad-AFxRep-mid":
|
| 947 |
-
return FADFeatures(*args, **kwargs, type="AFxRep-side", sample_rate=sample_rate)
|
| 948 |
-
elif metric_name == "fad-AFxRep-side":
|
| 949 |
-
return FADFeatures(*args, **kwargs, type="AFxRep-mid", sample_rate=sample_rate)
|
| 950 |
-
elif metric_name == "kad-fx_encoder":
|
| 951 |
-
return KADFeatures(*args, **kwargs, type="fx_encoder", sample_rate=sample_rate)
|
| 952 |
-
elif metric_name == "kad-AFxRep":
|
| 953 |
-
return KADFeatures(*args, **kwargs, type="AFxRep", sample_rate=sample_rate)
|
| 954 |
-
elif metric_name == "kad-AFxRep-mid":
|
| 955 |
-
return KADFeatures(*args, **kwargs, type="AFxRep-side", sample_rate=sample_rate)
|
| 956 |
-
elif metric_name == "kad-AFxRep-side":
|
| 957 |
-
return KADFeatures(*args, **kwargs, type="AFxRep-mid", sample_rate=sample_rate)
|
| 958 |
-
elif metric_name == "kad-class-fx_encoder":
|
| 959 |
-
return KADFeatures(*args, **kwargs, type="fx_encoder", sample_rate=sample_rate, classwise=True)
|
| 960 |
-
elif metric_name == "kad-class-AFxRep":
|
| 961 |
-
return KADFeatures(*args, **kwargs, type="AFxRep", sample_rate=sample_rate, classwise=True)
|
| 962 |
-
elif metric_name == "kad-class-AFxRep-mid":
|
| 963 |
-
return KADFeatures(*args, **kwargs, type="AFxRep-side", sample_rate=sample_rate, classwise=True)
|
| 964 |
-
elif metric_name == "kad-class-AFxRep-side":
|
| 965 |
-
return KADFeatures(*args, **kwargs, type="AFxRep-mid", sample_rate=sample_rate, classwise=True)
|
| 966 |
-
else:
|
| 967 |
-
raise ValueError(f"Unknown metric: {metric_name}")
|
| 968 |
-
|
| 969 |
-
# Example usage:
|
| 970 |
-
#metric_instance = metric_factory("pairwise-spectral")
|
| 971 |
-
#```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/evaluation/dist_metrics_multitrack.py
DELETED
|
@@ -1,619 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import math
|
| 3 |
-
from numpy.lib.scimath import sqrt as scisqrt
|
| 4 |
-
from scipy import linalg
|
| 5 |
-
import numpy as np
|
| 6 |
-
import torchaudio
|
| 7 |
-
import os
|
| 8 |
-
from importlib import import_module
|
| 9 |
-
import torch
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
from utils.feature_extractors.load_features import load_AFxRep, load_fx_encoder, load_fx_encoder_plusplus, load_MERT, load_CLAP, load_fx_encoder_plusplus_2048
|
| 13 |
-
|
| 14 |
-
from utils.log import make_PCA_figure
|
| 15 |
-
|
| 16 |
-
SCALE_FACTOR = 100
|
| 17 |
-
|
| 18 |
-
def calc_kernel_audio_distance(
|
| 19 |
-
x: torch.Tensor,
|
| 20 |
-
y: torch.Tensor,
|
| 21 |
-
device: str,
|
| 22 |
-
bandwidth=None,
|
| 23 |
-
kernel='gaussian',
|
| 24 |
-
precision=torch.float32,
|
| 25 |
-
eps=1e-8
|
| 26 |
-
) -> torch.Tensor:
|
| 27 |
-
"""
|
| 28 |
-
Compute the Kernel Audio Distance (KAD) between two samples using PyTorch.
|
| 29 |
-
|
| 30 |
-
Args:
|
| 31 |
-
x: The first set of embeddings of shape (m, embedding_dim).
|
| 32 |
-
y: The second set of embeddings of shape (n, embedding_dim).
|
| 33 |
-
cache_dirs: Directories to cache kernel statistics.
|
| 34 |
-
bandwidth: The bandwidth value for the Gaussian RBF kernel.
|
| 35 |
-
kernel: Kernel function to use ('gaussian', 'iq', 'imq').
|
| 36 |
-
precision: Type setting for matrix calculation precision.
|
| 37 |
-
eps: Small value to prevent division by zero.
|
| 38 |
-
|
| 39 |
-
Returns:
|
| 40 |
-
The KAD between x and y embedding sets.
|
| 41 |
-
"""
|
| 42 |
-
# Ensure x and y are of the correct precision
|
| 43 |
-
x = x.to(dtype=precision, device=device)
|
| 44 |
-
y = y.to(dtype=precision, device=device)
|
| 45 |
-
|
| 46 |
-
assert bandwidth is not None, "Bandwidth must be provided for KAD calculation"
|
| 47 |
-
|
| 48 |
-
m, n = x.shape[0], y.shape[0]
|
| 49 |
-
|
| 50 |
-
# Define kernel functions
|
| 51 |
-
gamma = 1 / (2 * bandwidth**2 + eps)
|
| 52 |
-
if kernel == 'gaussian': # Gaussian Kernel
|
| 53 |
-
kernel = lambda a: torch.exp(-gamma * a)
|
| 54 |
-
elif kernel == 'iq': # Inverse Quadratic Kernel
|
| 55 |
-
kernel = lambda a: 1 / (1 + gamma * a)
|
| 56 |
-
elif kernel == 'imq': # Inverse Multiquadric Kernel
|
| 57 |
-
kernel = lambda a: 1 / torch.sqrt(1 + gamma * a)
|
| 58 |
-
else:
|
| 59 |
-
raise ValueError("Invalid kernel type. Valid kernels: 'gaussian', 'iq', 'imq'")
|
| 60 |
-
|
| 61 |
-
# Load x kernel statistics
|
| 62 |
-
xx = x @ x.T
|
| 63 |
-
x_sqnorms = torch.diagonal(xx)
|
| 64 |
-
d2_xx = x_sqnorms.unsqueeze(1) + x_sqnorms.unsqueeze(0) - 2 * xx # shape (m, m)
|
| 65 |
-
|
| 66 |
-
k_xx = kernel(d2_xx)
|
| 67 |
-
k_xx = k_xx - torch.diag(torch.diagonal(k_xx))
|
| 68 |
-
k_xx_mean = k_xx.sum() / (m * (m - 1))
|
| 69 |
-
|
| 70 |
-
# Load y kernel statistics
|
| 71 |
-
yy = y @ y.T
|
| 72 |
-
y_sqnorms = torch.diagonal(yy)
|
| 73 |
-
d2_yy = y_sqnorms.unsqueeze(1) + y_sqnorms.unsqueeze(0) - 2 * yy # shape (n, n)
|
| 74 |
-
|
| 75 |
-
k_yy = kernel(d2_yy)
|
| 76 |
-
k_yy = k_yy - torch.diag(torch.diagonal(k_yy))
|
| 77 |
-
k_yy_mean = k_yy.sum() / (n * (n - 1))
|
| 78 |
-
|
| 79 |
-
# Compute kernel statistics for xy
|
| 80 |
-
xy = x @ y.T
|
| 81 |
-
d2_xy = x_sqnorms.unsqueeze(1) + y_sqnorms.unsqueeze(0) - 2 * xy # shape (m, n)
|
| 82 |
-
k_xy = kernel(d2_xy)
|
| 83 |
-
k_xy_mean = k_xy.mean()
|
| 84 |
-
|
| 85 |
-
# Compute MMD
|
| 86 |
-
result = k_xx_mean + k_yy_mean - 2 * k_xy_mean
|
| 87 |
-
|
| 88 |
-
return result * SCALE_FACTOR
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
def median_pairwise_distance(x, subsample=None):
|
| 92 |
-
"""
|
| 93 |
-
Compute the median pairwise distance of an embedding set.
|
| 94 |
-
|
| 95 |
-
Args:
|
| 96 |
-
x: torch.Tensor of shape (n_samples, embedding_dim)
|
| 97 |
-
subsample: int, number of random pairs to consider (optional)
|
| 98 |
-
|
| 99 |
-
Returns:
|
| 100 |
-
The median pairwise distance between points in x.
|
| 101 |
-
"""
|
| 102 |
-
x = torch.tensor(x, dtype=torch.float32)
|
| 103 |
-
n_samples = x.shape[0]
|
| 104 |
-
|
| 105 |
-
if subsample is not None and subsample < n_samples * (n_samples - 1) / 2:
|
| 106 |
-
# Randomly select pairs of indices
|
| 107 |
-
idx1 = torch.randint(0, n_samples, (subsample,))
|
| 108 |
-
idx2 = torch.randint(0, n_samples, (subsample,))
|
| 109 |
-
|
| 110 |
-
# Ensure idx1 != idx2
|
| 111 |
-
mask = idx1 == idx2
|
| 112 |
-
idx2[mask] = (idx2[mask] + 1) % n_samples
|
| 113 |
-
|
| 114 |
-
# Compute distances for selected pairs
|
| 115 |
-
distances = torch.sqrt(torch.sum((x[idx1] - x[idx2])**2, dim=1))
|
| 116 |
-
else:
|
| 117 |
-
# Compute all pairwise distances
|
| 118 |
-
distances = torch.pdist(x)
|
| 119 |
-
|
| 120 |
-
return torch.median(distances).item()
|
| 121 |
-
|
| 122 |
-
class DistMetric:
|
| 123 |
-
"""
|
| 124 |
-
Base class for pairwise metrics.
|
| 125 |
-
|
| 126 |
-
This class should be subclassed to implement specific pairwise metrics.
|
| 127 |
-
"""
|
| 128 |
-
def __init__(self,type, sample_rate,*args, **kwargs):
|
| 129 |
-
"""
|
| 130 |
-
Initialize the PairwiseMetric instance.
|
| 131 |
-
|
| 132 |
-
Args:
|
| 133 |
-
*args: Variable length argument list.
|
| 134 |
-
**kwargs: Arbitrary keyword arguments.
|
| 135 |
-
"""
|
| 136 |
-
self.type=type
|
| 137 |
-
self.sample_rate=sample_rate
|
| 138 |
-
|
| 139 |
-
self.taxonomy_ref= kwargs.get("taxonomy_ref", None)
|
| 140 |
-
|
| 141 |
-
if self.type == "DynamicFeatures":
|
| 142 |
-
from utils.feature_extractors.AF_features_embedding import AF_fourier_embedding
|
| 143 |
-
AFembedding= AF_fourier_embedding(device=self.device)
|
| 144 |
-
|
| 145 |
-
def feat_extracfor_fn(x):
|
| 146 |
-
z, _ = AFembedding.encode(x)
|
| 147 |
-
return z
|
| 148 |
-
|
| 149 |
-
self.feat_extractor = feat_extracfor_fn
|
| 150 |
-
elif self.type == "FxEncoder++":
|
| 151 |
-
self.model_args= kwargs.get("fx_encoder_plusplus_args", None)
|
| 152 |
-
assert self.model_args is not None, "model_args must be provided for fxencAFv2-fxenc++ type"
|
| 153 |
-
|
| 154 |
-
self.distance_type=self.model_args.distance_type
|
| 155 |
-
fxencoder = load_fx_encoder_plusplus_2048(self.model_args, self.device)
|
| 156 |
-
|
| 157 |
-
def feat_extractor_fn(x):
|
| 158 |
-
z= fxencoder(x)
|
| 159 |
-
z=torch.nn.functional.normalize(z, dim=-1, p=2) # normalize to unit variance
|
| 160 |
-
return z
|
| 161 |
-
|
| 162 |
-
self.feat_extractor = feat_extractor_fn
|
| 163 |
-
|
| 164 |
-
else:
|
| 165 |
-
raise ValueError(f"Unknown type: {self.type}. Supported types: fx_encoder, fx_encoder_plusplus, AFxRep-mid, AFxRep-side, AFxRep")
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
def compute(self, *args, **kwargs):
|
| 169 |
-
raise NotImplementedError("Subclasses should implement this method.")
|
| 170 |
-
|
| 171 |
-
def extract_features(self, y, y_hat, x=None):
|
| 172 |
-
|
| 173 |
-
y=torch.tensor(y).permute(1,0).unsqueeze(0).to(self.device)
|
| 174 |
-
y_hat=torch.tensor(y_hat).permute(1,0).unsqueeze(0).to(self.device)
|
| 175 |
-
|
| 176 |
-
with torch.no_grad():
|
| 177 |
-
feat_y= self.feat_extractor(y)
|
| 178 |
-
feat_y_hat= self.feat_extractor(y_hat)
|
| 179 |
-
|
| 180 |
-
if x is not None:
|
| 181 |
-
x=torch.tensor(x).permute(1,0).unsqueeze(0).to(self.device)
|
| 182 |
-
with torch.no_grad():
|
| 183 |
-
feat_x= self.feat_extractor(x)
|
| 184 |
-
else:
|
| 185 |
-
feat_x=None
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
return feat_y, feat_y_hat, feat_x
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
def do_TSNE_figure(self, dict_features_y, dict_features_y_hat, dict_taxonomy=None):
|
| 192 |
-
"""
|
| 193 |
-
Perform PCA on the features and create a figure.
|
| 194 |
-
|
| 195 |
-
Args:
|
| 196 |
-
dict_features_y (dict): Dictionary containing features for the first set.
|
| 197 |
-
dict_features_y_hat (dict): Dictionary containing features for the second set.
|
| 198 |
-
|
| 199 |
-
Returns:
|
| 200 |
-
fig: The created figure.
|
| 201 |
-
"""
|
| 202 |
-
import matplotlib.pyplot as plt
|
| 203 |
-
from sklearn.manifold import TSNE
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
y_values = {}
|
| 207 |
-
y_hat_values = {}
|
| 208 |
-
for key, track_name in self.taxonomy_ref.items():
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
y_values[track_name] = []
|
| 212 |
-
for k in range(len(dict_features_y.keys())):
|
| 213 |
-
index = [i for i, tax_id in enumerate(dict_taxonomy[k]) if tax_id == key]
|
| 214 |
-
print("taxonomy",k, dict_taxonomy[k])
|
| 215 |
-
if index: # If any matches were found
|
| 216 |
-
# Select the corresponding rows from dict_features_y[k]
|
| 217 |
-
selected_features = dict_features_y[k][index]
|
| 218 |
-
y_values[track_name].append(selected_features)
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
if not y_values[track_name]:
|
| 222 |
-
print(f"Warning: No features found for track {track_name} with key {key}. Skipping this track.")
|
| 223 |
-
#remove the track from the dictionaries
|
| 224 |
-
y_values.pop(track_name, None)
|
| 225 |
-
continue
|
| 226 |
-
|
| 227 |
-
y_values[track_name]=torch.cat(y_values[track_name], dim=0)
|
| 228 |
-
y_values[track_name]=torch.nan_to_num(y_values[track_name], nan=0)
|
| 229 |
-
|
| 230 |
-
#print(clusters[track_name])
|
| 231 |
-
|
| 232 |
-
y_hat_values[track_name] = []
|
| 233 |
-
|
| 234 |
-
for k in range(len(dict_features_y_hat.keys())):
|
| 235 |
-
index = [i for i, tax_id in enumerate(dict_taxonomy[k]) if tax_id == key]
|
| 236 |
-
if index: # If any matches were found
|
| 237 |
-
# Select the corresponding rows from dict_features_y[k]
|
| 238 |
-
selected_features = dict_features_y_hat[k][index]
|
| 239 |
-
y_hat_values[track_name].append(selected_features)
|
| 240 |
-
#else:
|
| 241 |
-
# y_hat_values[track_name].append(None) # Append zeros if no match found
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
y_hat_values[track_name]=torch.cat(y_hat_values[track_name], dim=0)
|
| 245 |
-
y_hat_values[track_name]=torch.nan_to_num(y_hat_values[track_name], nan=0)
|
| 246 |
-
|
| 247 |
-
#print(clusters_hat[track_name])
|
| 248 |
-
|
| 249 |
-
if self.pca is None:
|
| 250 |
-
self.pca= {}
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
tsne_result = {}
|
| 254 |
-
tsne_result_hat = {}
|
| 255 |
-
data_dict = {}
|
| 256 |
-
figs= {}
|
| 257 |
-
|
| 258 |
-
for k in y_values.keys():
|
| 259 |
-
print("Processing track:", k)
|
| 260 |
-
self.tsne =TSNE(n_components=2, perplexity=30)
|
| 261 |
-
combined_data = torch.cat([y_values[k], y_hat_values[k]], dim=0).cpu().numpy()
|
| 262 |
-
combined_result = self.tsne.fit_transform(combined_data)
|
| 263 |
-
|
| 264 |
-
n_y = y_values[k].shape[0]
|
| 265 |
-
tsne_result[k] = combined_result[:n_y]
|
| 266 |
-
tsne_result_hat[k] = combined_result[n_y:]
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
data_dict[k] = {
|
| 270 |
-
"y": tsne_result[k],
|
| 271 |
-
"y_hat": tsne_result_hat[k] }
|
| 272 |
-
|
| 273 |
-
figs[k]= make_PCA_figure(data_dict[k], title=self.type + " TSNE; track: "+k)
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
return figs
|
| 277 |
-
|
| 278 |
-
def do_PCA_figure(self, dict_features_y, dict_features_y_hat, fit_mode="target", dict_cluster=None, dict_taxonomy=None):
|
| 279 |
-
"""
|
| 280 |
-
Perform PCA on the features and create a figure.
|
| 281 |
-
|
| 282 |
-
Args:
|
| 283 |
-
dict_features_y (dict): Dictionary containing features for the first set.
|
| 284 |
-
dict_features_y_hat (dict): Dictionary containing features for the second set.
|
| 285 |
-
|
| 286 |
-
Returns:
|
| 287 |
-
fig: The created figure.
|
| 288 |
-
"""
|
| 289 |
-
import matplotlib.pyplot as plt
|
| 290 |
-
from sklearn.decomposition import PCA
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
y_values = {}
|
| 294 |
-
y_hat_values = {}
|
| 295 |
-
if dict_cluster is not None:
|
| 296 |
-
clusters={}
|
| 297 |
-
clusters_hat= {}
|
| 298 |
-
|
| 299 |
-
for key, track_name in self.taxonomy_ref.items():
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
y_values[track_name] = []
|
| 303 |
-
if dict_cluster is not None:
|
| 304 |
-
clusters[track_name] = []
|
| 305 |
-
for k in range(len(dict_features_y.keys())):
|
| 306 |
-
print("taxonomy",k, dict_taxonomy[k])
|
| 307 |
-
index = [i for i, tax_id in enumerate(dict_taxonomy[k]) if tax_id == key]
|
| 308 |
-
if index: # If any matches were found
|
| 309 |
-
# Select the corresponding rows from dict_features_y[k]
|
| 310 |
-
selected_features = dict_features_y[k][index]
|
| 311 |
-
y_values[track_name].append(selected_features)
|
| 312 |
-
|
| 313 |
-
if dict_cluster is not None:
|
| 314 |
-
clusters[track_name].append(dict_cluster[k].unsqueeze(0))
|
| 315 |
-
|
| 316 |
-
#check if y_values[track_name] is empty
|
| 317 |
-
if not y_values[track_name]:
|
| 318 |
-
print(f"Warning: No features found for track {track_name} with key {key}. Skipping this track.")
|
| 319 |
-
#remove the track from the dictionaries
|
| 320 |
-
y_values.pop(track_name, None)
|
| 321 |
-
continue
|
| 322 |
-
|
| 323 |
-
y_values[track_name]=torch.cat(y_values[track_name], dim=0)
|
| 324 |
-
y_values[track_name]=torch.nan_to_num(y_values[track_name], nan=0)
|
| 325 |
-
|
| 326 |
-
if dict_cluster is not None:
|
| 327 |
-
clusters[track_name]=torch.cat(clusters[track_name], dim=0)
|
| 328 |
-
|
| 329 |
-
y_hat_values[track_name] = []
|
| 330 |
-
if dict_cluster is not None:
|
| 331 |
-
clusters_hat[track_name] = []
|
| 332 |
-
|
| 333 |
-
for k in range(len(dict_features_y_hat.keys())):
|
| 334 |
-
index = [i for i, tax_id in enumerate(dict_taxonomy[k]) if tax_id == key]
|
| 335 |
-
if index: # If any matches were found
|
| 336 |
-
# Select the corresponding rows from dict_features_y[k]
|
| 337 |
-
selected_features = dict_features_y_hat[k][index]
|
| 338 |
-
y_hat_values[track_name].append(selected_features)
|
| 339 |
-
if dict_cluster is not None:
|
| 340 |
-
clusters_hat[track_name].append(dict_cluster[k].unsqueeze(0))
|
| 341 |
-
|
| 342 |
-
y_hat_values[track_name]=torch.cat(y_hat_values[track_name], dim=0)
|
| 343 |
-
y_hat_values[track_name]=torch.nan_to_num(y_hat_values[track_name], nan=0)
|
| 344 |
-
|
| 345 |
-
if dict_cluster is not None:
|
| 346 |
-
clusters_hat[track_name]=torch.cat(clusters_hat[track_name], dim=0)
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
if self.pca is None:
|
| 350 |
-
self.pca= {}
|
| 351 |
-
|
| 352 |
-
pca_result = {}
|
| 353 |
-
pca_result_hat = {}
|
| 354 |
-
data_dict = {}
|
| 355 |
-
figs= {}
|
| 356 |
-
|
| 357 |
-
for k in y_values.keys():
|
| 358 |
-
print("Processing track:", k)
|
| 359 |
-
#check if pca already exists for this key
|
| 360 |
-
if k not in self.pca.keys():
|
| 361 |
-
if fit_mode == "target":
|
| 362 |
-
print("Fitting PCA for target values only track:", k)
|
| 363 |
-
self.pca[k] = PCA(n_components=2).fit(y_values[k].cpu().numpy())
|
| 364 |
-
elif fit_mode == "all":
|
| 365 |
-
print("Fitting PCA for all values track:", k)
|
| 366 |
-
self.pca[k] = PCA(n_components=2).fit(torch.cat([y_values[k], y_hat_values[k]], dim=0).cpu().numpy())
|
| 367 |
-
else:
|
| 368 |
-
raise ValueError(f"Unknown fit_mode: {fit_mode}. Supported modes: target, all")
|
| 369 |
-
|
| 370 |
-
pca_result[k]=self.pca[k].transform(y_values[k].cpu().numpy())
|
| 371 |
-
pca_result_hat[k] = self.pca[k].transform(y_hat_values[k].cpu().numpy())
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
if dict_cluster is not None:
|
| 375 |
-
data_dict[k] = {
|
| 376 |
-
"y_C0": pca_result[k][clusters[k] == 0],
|
| 377 |
-
"y_hat_C0": pca_result_hat[k][clusters_hat[k] == 0],
|
| 378 |
-
"y_C1": pca_result[k][clusters[k] == 1],
|
| 379 |
-
"y_hat_C1": pca_result_hat[k][clusters_hat[k] == 1]
|
| 380 |
-
}
|
| 381 |
-
else:
|
| 382 |
-
data_dict[k] = {
|
| 383 |
-
"y": pca_result[k],
|
| 384 |
-
"y_hat": pca_result_hat[k]
|
| 385 |
-
}
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
figs[k]= make_PCA_figure(data_dict[k], title=self.type + " PCA; track: "+k)
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
return figs
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
class KADFeatures(DistMetric):
|
| 395 |
-
"""
|
| 396 |
-
Class for computing the pairwise spectral metric.
|
| 397 |
-
|
| 398 |
-
This class inherits from PairwiseMetric and implements the compute method
|
| 399 |
-
to calculate the pairwise spectral metric.
|
| 400 |
-
"""
|
| 401 |
-
def __init__(self,
|
| 402 |
-
type=None,
|
| 403 |
-
sample_rate=44100,
|
| 404 |
-
KAD_args=None,
|
| 405 |
-
*args, **kwargs):
|
| 406 |
-
"""
|
| 407 |
-
Initialize the PairwiseSpectral instance.
|
| 408 |
-
|
| 409 |
-
Args:
|
| 410 |
-
*args: Variable length argument list.
|
| 411 |
-
**kwargs: Arbitrary keyword arguments.
|
| 412 |
-
"""
|
| 413 |
-
self.type = type
|
| 414 |
-
|
| 415 |
-
self.device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 416 |
-
|
| 417 |
-
self.KAD_args = KAD_args
|
| 418 |
-
|
| 419 |
-
assert self.KAD_args is not None, "FAD_args must be provided"
|
| 420 |
-
|
| 421 |
-
if self.KAD_args.do_PCA_figure:
|
| 422 |
-
self.pca = None
|
| 423 |
-
|
| 424 |
-
self.bandwidth = None
|
| 425 |
-
|
| 426 |
-
super().__init__(self.type, sample_rate, *args, **kwargs)
|
| 427 |
-
|
| 428 |
-
def calculate_KAD_distance(self, dict_features_y, dict_features_y_hat, dict_taxonomy=None):
|
| 429 |
-
|
| 430 |
-
y_values = {}
|
| 431 |
-
y_hat_values = {}
|
| 432 |
-
for key, track_name in self.taxonomy_ref.items():
|
| 433 |
-
y_values[track_name] = []
|
| 434 |
-
for k in range(len(dict_features_y.keys())):
|
| 435 |
-
index = [i for i, tax_id in enumerate(dict_taxonomy[k]) if tax_id == key]
|
| 436 |
-
if index: # If any matches were found
|
| 437 |
-
# Select the corresponding rows from dict_features_y[k]
|
| 438 |
-
selected_features = dict_features_y[k][index]
|
| 439 |
-
y_values[track_name].append(selected_features)
|
| 440 |
-
#else:
|
| 441 |
-
# y_values[track_name].append(None) # Append zeros if no match found
|
| 442 |
-
if not y_values[track_name]:
|
| 443 |
-
print(f"Warning: No features found for track {track_name} with key {key}. Skipping this track.")
|
| 444 |
-
#remove the track from the dictionaries
|
| 445 |
-
y_values.pop(track_name, None)
|
| 446 |
-
continue
|
| 447 |
-
|
| 448 |
-
y_values[track_name]=torch.cat(y_values[track_name], dim=0)
|
| 449 |
-
y_values[track_name]=torch.nan_to_num(y_values[track_name], nan=0)
|
| 450 |
-
|
| 451 |
-
y_hat_values[track_name] = []
|
| 452 |
-
for k in range(len(dict_features_y_hat.keys())):
|
| 453 |
-
index = [i for i, tax_id in enumerate(dict_taxonomy[k]) if tax_id == key]
|
| 454 |
-
if index: # If any matches were found
|
| 455 |
-
# Select the corresponding rows from dict_features_y[k]
|
| 456 |
-
selected_features = dict_features_y_hat[k][index]
|
| 457 |
-
y_hat_values[track_name].append(selected_features)
|
| 458 |
-
#else:
|
| 459 |
-
# y_hat_values[track_name].append(None) # Append zeros if no match found
|
| 460 |
-
|
| 461 |
-
y_hat_values[track_name]=torch.cat(y_hat_values[track_name], dim=0)
|
| 462 |
-
y_hat_values[track_name]=torch.nan_to_num(y_hat_values[track_name], nan=0)
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
KAD={}
|
| 466 |
-
weight={}
|
| 467 |
-
total_examples= 0
|
| 468 |
-
for key in y_values.keys():
|
| 469 |
-
y= y_values[key]
|
| 470 |
-
y_hat= y_hat_values[key]
|
| 471 |
-
with torch.no_grad():
|
| 472 |
-
KAD[key]=calc_kernel_audio_distance(y, y_hat, device=self.device, bandwidth=self.bandwidth, kernel=self.KAD_args.kernel, precision=torch.float32)
|
| 473 |
-
|
| 474 |
-
weight[key]= y.shape[0]
|
| 475 |
-
total_examples += weight[key]
|
| 476 |
-
|
| 477 |
-
KAD= sum([KAD[k] * weight[k] for k in KAD.keys()]) / total_examples
|
| 478 |
-
|
| 479 |
-
return KAD.cpu().item()
|
| 480 |
-
|
| 481 |
-
def calculate_bandwidth(self, dict_features_y):
|
| 482 |
-
|
| 483 |
-
print("Calculating bandwidth...")
|
| 484 |
-
y= torch.cat(list(dict_features_y.values()), dim=0)
|
| 485 |
-
print("y shape:", y.shape)
|
| 486 |
-
self.bandwidth = median_pairwise_distance(y)
|
| 487 |
-
print("Bandwidth:", self.bandwidth)
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
def compute(self, dict_y, dict_y_hat, dict_taxonomy=None, dict_p_hat=None, *args, **kwargs):
|
| 492 |
-
"""
|
| 493 |
-
Compute the pairwise spectral metric.
|
| 494 |
-
|
| 495 |
-
Args:
|
| 496 |
-
*args: Variable length argument list.
|
| 497 |
-
**kwargs: Arbitrary keyword arguments.
|
| 498 |
-
|
| 499 |
-
Returns:
|
| 500 |
-
The computed pairwise spectral metric.
|
| 501 |
-
"""
|
| 502 |
-
|
| 503 |
-
print("Computing KAD distance...")
|
| 504 |
-
|
| 505 |
-
dict_features_y={}
|
| 506 |
-
dict_features_y_hat={}
|
| 507 |
-
|
| 508 |
-
if dict_y_hat is None and dict_p_hat is not None:
|
| 509 |
-
|
| 510 |
-
for key in dict_y.keys():
|
| 511 |
-
y= dict_y[key]
|
| 512 |
-
|
| 513 |
-
embed= dict_p_hat[key]
|
| 514 |
-
embed=torch.tensor(embed).to(self.device)
|
| 515 |
-
|
| 516 |
-
embed=embed*math.sqrt(embed.shape[-1]) # Scale the embedding
|
| 517 |
-
|
| 518 |
-
embed_fxenc=embed[...,:2048]/ math.sqrt(2048) # Scale the first 128 dimensions
|
| 519 |
-
embed_AF=embed[...,2048:]/ math.sqrt(64) # Scale the last 128 dimensions
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
if self.type == "FxEncoder++":
|
| 523 |
-
p_hat= embed_fxenc
|
| 524 |
-
|
| 525 |
-
elif self.type == "DynamicFeatures":
|
| 526 |
-
p_hat= embed_AF
|
| 527 |
-
|
| 528 |
-
n, c, d=y.shape
|
| 529 |
-
|
| 530 |
-
assert c==2, f"Expected 2 channels, got {c} for key {key}"
|
| 531 |
-
|
| 532 |
-
y=torch.tensor(y).to(self.device)
|
| 533 |
-
|
| 534 |
-
with torch.no_grad():
|
| 535 |
-
feat_y= self.feat_extractor(y)
|
| 536 |
-
|
| 537 |
-
assert p_hat.shape == feat_y.shape, f"Shape mismatch for key {key}: {p_hat.shape} vs {feat_y.shape}"
|
| 538 |
-
|
| 539 |
-
dict_features_y[key] = feat_y
|
| 540 |
-
dict_features_y_hat[key] = p_hat
|
| 541 |
-
|
| 542 |
-
else:
|
| 543 |
-
for key in dict_y.keys():
|
| 544 |
-
y= dict_y[key]
|
| 545 |
-
y_hat= dict_y_hat[key]
|
| 546 |
-
|
| 547 |
-
assert y.shape == y_hat.shape, f"Shape mismatch for key {key}: {y.shape} vs {y_hat.shape}"
|
| 548 |
-
|
| 549 |
-
n, c, d=y.shape
|
| 550 |
-
if dict_taxonomy is not None:
|
| 551 |
-
taxonomy= dict_taxonomy[key]
|
| 552 |
-
assert len(taxonomy) == n, f"Taxonomy length mismatch for key {key}: {len(taxonomy)} vs {n}"
|
| 553 |
-
|
| 554 |
-
assert c==2, f"Expected 2 channels, got {c} for key {key}"
|
| 555 |
-
|
| 556 |
-
y=torch.tensor(y).to(self.device)
|
| 557 |
-
y_hat=torch.tensor(y_hat).to(self.device)
|
| 558 |
-
|
| 559 |
-
with torch.no_grad():
|
| 560 |
-
feat_y= self.feat_extractor(y)
|
| 561 |
-
feat_y_hat= self.feat_extractor(y_hat)
|
| 562 |
-
|
| 563 |
-
dict_features_y[key] = feat_y
|
| 564 |
-
dict_features_y_hat[key] = feat_y_hat
|
| 565 |
-
|
| 566 |
-
dict_output = {}
|
| 567 |
-
if self.KAD_args.do_PCA_figure:
|
| 568 |
-
dict_figs=self.do_PCA_figure(dict_features_y, dict_features_y_hat, dict_taxonomy=dict_taxonomy, fit_mode=self.KAD_args.PCA_fit_mode)
|
| 569 |
-
#returns a dictionary with figures for each taxonomy key
|
| 570 |
-
#modify the key to include the type
|
| 571 |
-
|
| 572 |
-
for k, v in dict_figs.items():
|
| 573 |
-
#modify the key to include the type
|
| 574 |
-
key= self.type+ "_PCA_figure_" + k
|
| 575 |
-
dict_output[key] = v
|
| 576 |
-
|
| 577 |
-
if self.KAD_args.do_TSNE_figure:
|
| 578 |
-
dict_figs=self.do_TSNE_figure(dict_features_y, dict_features_y_hat, dict_taxonomy=dict_taxonomy)
|
| 579 |
-
for k, fig in dict_figs.items():
|
| 580 |
-
key= self.type+ "_TSNE_figure"+k
|
| 581 |
-
dict_output[key] = fig
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
# Compute the distance between the mean features
|
| 586 |
-
#FAD_distance = self.calculate_FAD_distance(y_mean, y_cov, y_hat_mean, y_hat_cov)
|
| 587 |
-
if self.bandwidth is None:
|
| 588 |
-
self.calculate_bandwidth(dict_features_y)
|
| 589 |
-
|
| 590 |
-
|
| 591 |
-
KAD_distance = self.calculate_KAD_distance(dict_features_y, dict_features_y_hat, dict_taxonomy)
|
| 592 |
-
|
| 593 |
-
|
| 594 |
-
return KAD_distance, dict_output
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
def metric_factory(metric_name, sample_rate, *args, **kwargs):
|
| 599 |
-
"""
|
| 600 |
-
Factory function to create a metric function based on the metric name.
|
| 601 |
-
|
| 602 |
-
Args:
|
| 603 |
-
metric_name (str): The name of the metric to create.
|
| 604 |
-
*args: Variable length argument list.
|
| 605 |
-
**kwargs: Arbitrary keyword arguments.
|
| 606 |
-
|
| 607 |
-
Returns:
|
| 608 |
-
An instance of a class that implements the metric function.
|
| 609 |
-
"""
|
| 610 |
-
if metric_name == "kad-FxEncoder++":
|
| 611 |
-
return KADFeatures(*args, **kwargs, type="FxEncoder++", sample_rate=sample_rate)
|
| 612 |
-
elif metric_name == "kad-DynamicFeatures":
|
| 613 |
-
return KADFeatures(*args, **kwargs, type="DynamicFeatures", sample_rate=sample_rate)
|
| 614 |
-
else:
|
| 615 |
-
raise ValueError(f"Unknown metric: {metric_name}")
|
| 616 |
-
|
| 617 |
-
# Example usage:
|
| 618 |
-
#metric_instance = metric_factory("pairwise-spectral")
|
| 619 |
-
#```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/evaluation/pairwise_metrics.py
DELETED
|
@@ -1,958 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import math
|
| 3 |
-
import torchaudio
|
| 4 |
-
import os
|
| 5 |
-
from importlib import import_module
|
| 6 |
-
import yaml
|
| 7 |
-
import torch
|
| 8 |
-
from utils.feature_extractors.load_features import load_AFxRep, load_fx_encoder, load_fx_encoder_plusplus, load_fx_encoder_plusplus_2048
|
| 9 |
-
from utils.MSS_loss import MultiScale_Spectral_Loss_MidSide_DDSP
|
| 10 |
-
|
| 11 |
-
class PairwiseMetric:
|
| 12 |
-
"""
|
| 13 |
-
Base class for pairwise metrics.
|
| 14 |
-
|
| 15 |
-
This class should be subclassed to implement specific pairwise metrics.
|
| 16 |
-
"""
|
| 17 |
-
def __init__(self, *args, **kwargs):
|
| 18 |
-
"""
|
| 19 |
-
Initialize the PairwiseMetric instance.
|
| 20 |
-
|
| 21 |
-
Args:
|
| 22 |
-
*args: Variable length argument list.
|
| 23 |
-
**kwargs: Arbitrary keyword arguments.
|
| 24 |
-
"""
|
| 25 |
-
pass
|
| 26 |
-
|
| 27 |
-
def compute(self, *args, **kwargs):
|
| 28 |
-
raise NotImplementedError("Subclasses should implement this method.")
|
| 29 |
-
|
| 30 |
-
class PairwiseFeatures(PairwiseMetric):
|
| 31 |
-
"""
|
| 32 |
-
Class for computing the pairwise spectral metric.
|
| 33 |
-
|
| 34 |
-
This class inherits from PairwiseMetric and implements the compute method
|
| 35 |
-
to calculate the pairwise spectral metric.
|
| 36 |
-
"""
|
| 37 |
-
def __init__(self,
|
| 38 |
-
type=None,
|
| 39 |
-
sample_rate=44100,
|
| 40 |
-
*args, **kwargs):
|
| 41 |
-
"""
|
| 42 |
-
Initialize the PairwiseSpectral instance.
|
| 43 |
-
|
| 44 |
-
Args:
|
| 45 |
-
*args: Variable length argument list.
|
| 46 |
-
**kwargs: Arbitrary keyword arguments.
|
| 47 |
-
"""
|
| 48 |
-
self.type = type
|
| 49 |
-
self.sample_rate = sample_rate
|
| 50 |
-
|
| 51 |
-
self.device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 52 |
-
|
| 53 |
-
if self.type == "fx_encoder":
|
| 54 |
-
self.model_args= kwargs.get("fx_encoder_args", None)
|
| 55 |
-
|
| 56 |
-
assert self.model_args is not None, "model_args must be provided for fx_encoder type"
|
| 57 |
-
|
| 58 |
-
self.distance_type=self.model_args.distance_type
|
| 59 |
-
|
| 60 |
-
self.feat_extractor = load_fx_encoder(self.model_args, self.device)
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
#self.feat_extractor = load_effects_encoder(ckpt_path=ckpt_path).to(self.device)
|
| 64 |
-
elif self.type == "fxenc++":
|
| 65 |
-
self.model_args= kwargs.get("fx_encoder_plusplus_args", None)
|
| 66 |
-
|
| 67 |
-
assert self.model_args is not None, "model_args must be provided for fx_encoder type"
|
| 68 |
-
|
| 69 |
-
self.distance_type=self.model_args.distance_type
|
| 70 |
-
|
| 71 |
-
self.feat_extractor = load_fx_encoder_plusplus(self.model_args, self.device)
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
#self.feat_extractor = load_effects_encoder(ckpt_path=ckpt_path).to(self.device)
|
| 75 |
-
|
| 76 |
-
elif self.type == "logrms":
|
| 77 |
-
self.model_args= kwargs.get("logrms_args", None)
|
| 78 |
-
|
| 79 |
-
assert self.model_args is not None, "model_args must be provided for logrms type"
|
| 80 |
-
self.distance_type=self.model_args.distance_type
|
| 81 |
-
|
| 82 |
-
from utils.feature_extractors.dsp_features import compute_log_rms
|
| 83 |
-
|
| 84 |
-
self.feat_extractor = lambda x: compute_log_rms(x)
|
| 85 |
-
|
| 86 |
-
elif self.type == "crestfactor":
|
| 87 |
-
self.model_args= kwargs.get("crestfactor_args", None)
|
| 88 |
-
assert self.model_args is not None, "model_args must be provided for crestfactor type"
|
| 89 |
-
|
| 90 |
-
self.distance_type=self.model_args.distance_type
|
| 91 |
-
|
| 92 |
-
from utils.feature_extractors.dsp_features import compute_crest_factor
|
| 93 |
-
|
| 94 |
-
self.feat_extractor = lambda x: compute_crest_factor(x)
|
| 95 |
-
elif self.type == "logspread":
|
| 96 |
-
self.model_args= kwargs.get("logspread_args", None)
|
| 97 |
-
assert self.model_args is not None, "model_args must be provided for logspread type"
|
| 98 |
-
|
| 99 |
-
self.distance_type=self.model_args.distance_type
|
| 100 |
-
|
| 101 |
-
from utils.feature_extractors.dsp_features import compute_log_spread
|
| 102 |
-
|
| 103 |
-
self.feat_extractor = lambda x: compute_log_spread(x).view(-1, 1)
|
| 104 |
-
elif self.type == "stereowidth":
|
| 105 |
-
self.model_args= kwargs.get("stereowidth_args", None)
|
| 106 |
-
assert self.model_args is not None, "model_args must be provided for stereowidth type"
|
| 107 |
-
|
| 108 |
-
self.distance_type=self.model_args.distance_type
|
| 109 |
-
|
| 110 |
-
from utils.feature_extractors.dsp_features import compute_stereo_width
|
| 111 |
-
self.feat_extractor = lambda x: compute_stereo_width(x).view(-1, 1)
|
| 112 |
-
|
| 113 |
-
elif self.type == "stereoimbalance":
|
| 114 |
-
self.model_args= kwargs.get("stereoimbalance_args", None)
|
| 115 |
-
assert self.model_args is not None, "model_args must be provided for stereoimbalance type"
|
| 116 |
-
self.distance_type=self.model_args.distance_type
|
| 117 |
-
from utils.feature_extractors.dsp_features import compute_stereo_imbalance
|
| 118 |
-
self.feat_extractor = lambda x: compute_stereo_imbalance(x).view(-1, 1)
|
| 119 |
-
|
| 120 |
-
elif self.type== "AFxRep-mid" or self.type== "AFxRep-side" or self.type== "AFxRep":
|
| 121 |
-
|
| 122 |
-
self.model_args= kwargs.get("AFxRep_args", None)
|
| 123 |
-
|
| 124 |
-
assert self.model_args is not None, "model_args must be provided for AFxRep type"
|
| 125 |
-
|
| 126 |
-
self.distance_type=self.model_args.distance_type
|
| 127 |
-
|
| 128 |
-
feat_extractor = load_AFxRep(self.model_args, self.device)
|
| 129 |
-
|
| 130 |
-
if self.type == "AFxRep-mid":
|
| 131 |
-
def feat_extractor_mid(x):
|
| 132 |
-
|
| 133 |
-
features= feat_extractor(x)
|
| 134 |
-
|
| 135 |
-
#divide by 2 to get mid and side features
|
| 136 |
-
|
| 137 |
-
feat_mid, feat_side = features.chunk(2, dim=-1)
|
| 138 |
-
|
| 139 |
-
return feat_mid
|
| 140 |
-
|
| 141 |
-
self.feat_extractor = feat_extractor_mid
|
| 142 |
-
|
| 143 |
-
elif self.type == "AFxRep-side":
|
| 144 |
-
def feat_extractor_side(x):
|
| 145 |
-
|
| 146 |
-
features= feat_extractor(x)
|
| 147 |
-
|
| 148 |
-
#divide by 2 to get mid and side features
|
| 149 |
-
|
| 150 |
-
feat_mid, feat_side = features.chunk(2, dim=-1)
|
| 151 |
-
|
| 152 |
-
return feat_side
|
| 153 |
-
|
| 154 |
-
self.feat_extractor = feat_extractor_side
|
| 155 |
-
else:
|
| 156 |
-
self.feat_extractor = feat_extractor
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
super().__init__(*args, **kwargs)
|
| 160 |
-
|
| 161 |
-
def compute_feature_distance(self, y, y_hat, sample_rate, type):
|
| 162 |
-
|
| 163 |
-
y=torch.tensor(y).permute(1,0).unsqueeze(0).to(self.device)
|
| 164 |
-
y_hat=torch.tensor(y_hat).permute(1,0).unsqueeze(0).to(self.device)
|
| 165 |
-
|
| 166 |
-
with torch.no_grad():
|
| 167 |
-
feat_y= self.feat_extractor(y)
|
| 168 |
-
feat_y_hat= self.feat_extractor(y_hat)
|
| 169 |
-
|
| 170 |
-
if self.distance_type == "cosine":
|
| 171 |
-
cos_dist= 1- torch.cosine_similarity(feat_y_hat, feat_y, dim=1)
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
return {"distance": cos_dist.mean().item()}
|
| 175 |
-
elif self.distance_type == "l1":
|
| 176 |
-
l1_dist = torch.abs(feat_y_hat - feat_y).mean(dim=1)
|
| 177 |
-
|
| 178 |
-
return {"distance": l1_dist.mean().item()}
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
def compute(self, dict_y, dict_y_hat, dict_x, *args, **kwargs):
|
| 182 |
-
"""
|
| 183 |
-
Compute the pairwise spectral metric.
|
| 184 |
-
|
| 185 |
-
Args:
|
| 186 |
-
*args: Variable length argument list.
|
| 187 |
-
**kwargs: Arbitrary keyword arguments.
|
| 188 |
-
|
| 189 |
-
Returns:
|
| 190 |
-
The computed pairwise spectral metric.
|
| 191 |
-
"""
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
dict_features={}
|
| 196 |
-
|
| 197 |
-
for key in dict_y.keys():
|
| 198 |
-
y= dict_y[key]
|
| 199 |
-
y_hat= dict_y_hat[key]
|
| 200 |
-
|
| 201 |
-
assert y.shape == y_hat.shape, f"Shape mismatch for key {key}: {y.shape} vs {y_hat.shape}"
|
| 202 |
-
|
| 203 |
-
c, d=y.shape
|
| 204 |
-
#assert b==1, f"Expected batch size of 1, got {b} for key {key}"
|
| 205 |
-
|
| 206 |
-
assert c==2, f"Expected 2 channels, got {c} for key {key}"
|
| 207 |
-
|
| 208 |
-
y=y.T
|
| 209 |
-
y_hat=y_hat.T
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
if self.type == "spectral":
|
| 213 |
-
from evaluation.automix_evaluation import compute_spectral_features
|
| 214 |
-
dict_features_out= compute_spectral_features(y_hat, y ,self.sample_rate)
|
| 215 |
-
dict_features[key] = dict_features_out['mean_mape_spectral']
|
| 216 |
-
elif self.type=="panning":
|
| 217 |
-
from evaluation.automix_evaluation import compute_panning_features
|
| 218 |
-
dict_features_out = compute_panning_features(y_hat, y, self.sample_rate)
|
| 219 |
-
dict_features[key] = dict_features_out['mean_mape_panning']
|
| 220 |
-
elif self.type=="loudness":
|
| 221 |
-
from evaluation.automix_evaluation import compute_loudness_features
|
| 222 |
-
dict_features_out = compute_loudness_features(y_hat, y, self.sample_rate)
|
| 223 |
-
dict_features[key] = dict_features_out['mean_mape_loudness']
|
| 224 |
-
elif self.type=="dynamic":
|
| 225 |
-
from evaluation.automix_evaluation import compute_dynamic_features
|
| 226 |
-
dict_features_out = compute_dynamic_features(y_hat, y, self.sample_rate)
|
| 227 |
-
dict_features[key] = dict_features_out['mean_mape_dynamic']
|
| 228 |
-
elif self.type=="fx_encoder":
|
| 229 |
-
dict_features_out = self.compute_feature_distance(y, y_hat, self.sample_rate, type=self.type)
|
| 230 |
-
dict_features[key] = dict_features_out["distance"]
|
| 231 |
-
elif self.type=="AFxRep":
|
| 232 |
-
dict_features_out = self.compute_feature_distance(y, y_hat, self.sample_rate, type=self.type)
|
| 233 |
-
dict_features[key] = dict_features_out["distance"]
|
| 234 |
-
elif self.type=="AFxRep-mid":
|
| 235 |
-
dict_features_out = self.compute_feature_distance(y, y_hat, self.sample_rate, type=self.type)
|
| 236 |
-
dict_features[key] = dict_features_out["distance"]
|
| 237 |
-
elif self.type=="AFxRep-side":
|
| 238 |
-
dict_features_out = self.compute_feature_distance(y, y_hat, self.sample_rate, type=self.type)
|
| 239 |
-
dict_features[key] = dict_features_out["distance"]
|
| 240 |
-
elif self.type=="fxenc++":
|
| 241 |
-
dict_features_out = self.compute_feature_distance(y, y_hat, self.sample_rate, type=self.type)
|
| 242 |
-
dict_features[key] = dict_features_out["distance"]
|
| 243 |
-
elif self.type=="logrms":
|
| 244 |
-
dict_features_out = self.compute_feature_distance(y, y_hat, self.sample_rate, type=self.type)
|
| 245 |
-
dict_features[key] = dict_features_out["distance"]
|
| 246 |
-
elif self.type=="crestfactor":
|
| 247 |
-
dict_features_out = self.compute_feature_distance(y, y_hat, self.sample_rate, type=self.type)
|
| 248 |
-
dict_features[key] = dict_features_out["distance"]
|
| 249 |
-
elif self.type=="logspread":
|
| 250 |
-
dict_features_out = self.compute_feature_distance(y, y_hat, self.sample_rate, type=self.type)
|
| 251 |
-
dict_features[key] = dict_features_out["distance"]
|
| 252 |
-
elif self.type=="stereowidth":
|
| 253 |
-
dict_features_out = self.compute_feature_distance(y, y_hat, self.sample_rate, type=self.type)
|
| 254 |
-
dict_features[key] = dict_features_out["distance"]
|
| 255 |
-
elif self.type=="stereoimbalance":
|
| 256 |
-
dict_features_out = self.compute_feature_distance(y, y_hat, self.sample_rate, type=self.type)
|
| 257 |
-
dict_features[key] = dict_features_out["distance"]
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
# Compute the mean of the features across all keys
|
| 261 |
-
|
| 262 |
-
mean_features = sum(dict_features.values()) / len(dict_features)
|
| 263 |
-
|
| 264 |
-
return mean_features, {}
|
| 265 |
-
|
| 266 |
-
class PairwiseStyleMultitrackFeatures(PairwiseMetric):
|
| 267 |
-
"""
|
| 268 |
-
Class for computing the pairwise spectral metric.
|
| 269 |
-
|
| 270 |
-
This class inherits from PairwiseMetric and implements the compute method
|
| 271 |
-
to calculate the pairwise spectral metric.
|
| 272 |
-
"""
|
| 273 |
-
def __init__(self,
|
| 274 |
-
type=None,
|
| 275 |
-
sample_rate=44100,
|
| 276 |
-
*args, **kwargs):
|
| 277 |
-
"""
|
| 278 |
-
Initialize the PairwiseSpectral instance.
|
| 279 |
-
|
| 280 |
-
Args:
|
| 281 |
-
*args: Variable length argument list.
|
| 282 |
-
**kwargs: Arbitrary keyword arguments.
|
| 283 |
-
"""
|
| 284 |
-
self.type = type
|
| 285 |
-
self.sample_rate = sample_rate
|
| 286 |
-
|
| 287 |
-
self.device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 288 |
-
|
| 289 |
-
if self.type == "fx_encoder":
|
| 290 |
-
raise NotImplementedError("Style features for fx_encoder not implemented yet")
|
| 291 |
-
self.model_args= kwargs.get("fx_encoder_args", None)
|
| 292 |
-
|
| 293 |
-
assert self.model_args is not None, "model_args must be provided for fx_encoder type"
|
| 294 |
-
|
| 295 |
-
self.distance_type=self.model_args.distance_type
|
| 296 |
-
|
| 297 |
-
self.feat_extractor = load_fx_encoder(self.model_args, self.device)
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
#self.feat_extractor = load_effects_encoder(ckpt_path=ckpt_path).to(self.device)
|
| 301 |
-
|
| 302 |
-
elif self.type== "AFxRep-mid" or self.type== "AFxRep-side" or self.type== "AFxRep":
|
| 303 |
-
|
| 304 |
-
self.model_args= kwargs.get("AFxRep_args", None)
|
| 305 |
-
|
| 306 |
-
assert self.model_args is not None, "model_args must be provided for AFxRep type"
|
| 307 |
-
|
| 308 |
-
self.distance_type=self.model_args.distance_type
|
| 309 |
-
|
| 310 |
-
feat_extractor = load_AFxRep(self.model_args, self.device)
|
| 311 |
-
|
| 312 |
-
if self.type == "AFxRep-mid":
|
| 313 |
-
def feat_extractor_mid(x):
|
| 314 |
-
|
| 315 |
-
features= feat_extractor(x)
|
| 316 |
-
|
| 317 |
-
#divide by 2 to get mid and side features
|
| 318 |
-
|
| 319 |
-
feat_mid, feat_side = features.chunk(2, dim=-1)
|
| 320 |
-
|
| 321 |
-
return feat_mid
|
| 322 |
-
|
| 323 |
-
self.feat_extractor = feat_extractor_mid
|
| 324 |
-
|
| 325 |
-
elif self.type == "AFxRep-side":
|
| 326 |
-
def feat_extractor_side(x):
|
| 327 |
-
|
| 328 |
-
features= feat_extractor(x)
|
| 329 |
-
|
| 330 |
-
#divide by 2 to get mid and side features
|
| 331 |
-
|
| 332 |
-
feat_mid, feat_side = features.chunk(2, dim=-1)
|
| 333 |
-
|
| 334 |
-
return feat_side
|
| 335 |
-
|
| 336 |
-
self.feat_extractor = feat_extractor_side
|
| 337 |
-
else:
|
| 338 |
-
self.feat_extractor = feat_extractor
|
| 339 |
-
|
| 340 |
-
elif self.type == "fxenc2048AFv3-fxenc++":
|
| 341 |
-
|
| 342 |
-
self.model_args= kwargs.get("fx_encoder_plusplus_args", None)
|
| 343 |
-
assert self.model_args is not None, "model_args must be provided for fxencAFv2-fxenc++ type"
|
| 344 |
-
|
| 345 |
-
self.distance_type= self.model_args.distance_type
|
| 346 |
-
|
| 347 |
-
fxencoder = load_fx_encoder_plusplus_2048(self.model_args, self.device)
|
| 348 |
-
|
| 349 |
-
def feat_extractor_fn(x):
|
| 350 |
-
z= fxencoder(x)
|
| 351 |
-
z=torch.nn.functional.normalize(z, dim=-1, p=2) # normalize to unit variance
|
| 352 |
-
return z
|
| 353 |
-
|
| 354 |
-
self.feat_extractor = feat_extractor_fn
|
| 355 |
-
|
| 356 |
-
elif self.type == "fxenc2048AFv3-AF":
|
| 357 |
-
|
| 358 |
-
from utils.AF_features_embedding_v2 import AF_fourier_embedding
|
| 359 |
-
AFembedding= AF_fourier_embedding(device=self.device)
|
| 360 |
-
|
| 361 |
-
self.distance_type="cosine"
|
| 362 |
-
|
| 363 |
-
def feat_extracfor_fn(x):
|
| 364 |
-
z, _ = AFembedding.encode(x)
|
| 365 |
-
return z
|
| 366 |
-
|
| 367 |
-
self.feat_extractor = feat_extracfor_fn
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
super().__init__(*args, **kwargs)
|
| 371 |
-
|
| 372 |
-
def compute_feature_distance(self, y, p_hat, sample_rate, type):
|
| 373 |
-
|
| 374 |
-
y=torch.tensor(y).permute(1,0).unsqueeze(0).to(self.device)
|
| 375 |
-
#y_hat=torch.tensor(y_hat).permute(1,0).unsqueeze(0).to(self.device)
|
| 376 |
-
|
| 377 |
-
with torch.no_grad():
|
| 378 |
-
feat_y= self.feat_extractor(y)
|
| 379 |
-
#feat_y_hat= self.feat_extractor(y_hat)
|
| 380 |
-
|
| 381 |
-
assert p_hat.shape == feat_y.shape, f"Shape mismatch: p_hat {p_hat.shape} vs feat_y {feat_y.shape}"
|
| 382 |
-
|
| 383 |
-
if self.distance_type == "cosine":
|
| 384 |
-
cos_dist= 1- torch.cosine_similarity(p_hat, feat_y, dim=1)
|
| 385 |
-
|
| 386 |
-
return {"distance": cos_dist.mean().item()}
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
def compute(self, dict_y, dict_y_hat, dict_x, dict_p_hat=None, *args, **kwargs):
|
| 391 |
-
"""
|
| 392 |
-
Compute the pairwise spectral metric.
|
| 393 |
-
|
| 394 |
-
Args:
|
| 395 |
-
*args: Variable length argument list.
|
| 396 |
-
**kwargs: Arbitrary keyword arguments.
|
| 397 |
-
|
| 398 |
-
Returns:
|
| 399 |
-
The computed pairwise spectral metric.
|
| 400 |
-
"""
|
| 401 |
-
|
| 402 |
-
dict_features={}
|
| 403 |
-
|
| 404 |
-
index=0
|
| 405 |
-
|
| 406 |
-
for key in dict_y.keys():
|
| 407 |
-
y= dict_y[key]
|
| 408 |
-
|
| 409 |
-
embed= dict_p_hat[key]
|
| 410 |
-
embed=torch.tensor(embed).to(self.device).unsqueeze(0)
|
| 411 |
-
|
| 412 |
-
print(f"Processing key: {key}, embed shape: {embed.shape}")
|
| 413 |
-
if "AFxRep" in self.type:
|
| 414 |
-
embed_mid, embed_side = torch.chunk(embed, 2, dim=-1)
|
| 415 |
-
|
| 416 |
-
if self.type== "AFxRep-mid":
|
| 417 |
-
p_hat= embed_mid
|
| 418 |
-
elif self.type== "AFxRep-side":
|
| 419 |
-
p_hat= embed_side
|
| 420 |
-
elif self.type== "AFxRep":
|
| 421 |
-
p_hat= embed
|
| 422 |
-
elif "fxenc2048AFv3" in self.type:
|
| 423 |
-
|
| 424 |
-
embed=embed*math.sqrt(embed.shape[-1]) # Scale the embedding
|
| 425 |
-
embed_fxenc=embed[...,:2048]/ math.sqrt(2048) # Scale the first 128 dimensions
|
| 426 |
-
embed_AF=embed[...,2048:]/ math.sqrt(64) # Scale the last 128 dimensions
|
| 427 |
-
|
| 428 |
-
if self.type == "fxenc2048AFv3-fxenc++":
|
| 429 |
-
p_hat= embed_fxenc
|
| 430 |
-
elif self.type == "fxenc2048AFv3-AF":
|
| 431 |
-
p_hat= embed_AF
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
n, c, d=y.shape
|
| 435 |
-
|
| 436 |
-
#assert b==1, f"Expected batch size of 1, got {b} for key {key}"
|
| 437 |
-
|
| 438 |
-
assert c==2, f"Expected 2 channels, got {c} for key {key}"
|
| 439 |
-
|
| 440 |
-
for i in range(n):
|
| 441 |
-
y_i=y[i].T
|
| 442 |
-
|
| 443 |
-
if self.type=="fx_encoder":
|
| 444 |
-
raise NotImplementedError("Style features for fx_encoder not implemented yet")
|
| 445 |
-
dict_features_out = self.compute_feature_distance(y, p_hat, self.sample_rate, type=self.type)
|
| 446 |
-
dict_features[key] = dict_features_out["distance"]
|
| 447 |
-
elif self.type=="AFxRep":
|
| 448 |
-
dict_features_out = self.compute_feature_distance(y_i, p_hat[i], self.sample_rate, type=self.type)
|
| 449 |
-
dict_features[index] = dict_features_out["distance"]
|
| 450 |
-
elif self.type=="AFxRep-mid":
|
| 451 |
-
dict_features_out = self.compute_feature_distance(y_i, p_hat[i], self.sample_rate, type=self.type)
|
| 452 |
-
dict_features[index] = dict_features_out["distance"]
|
| 453 |
-
elif self.type=="AFxRep-side":
|
| 454 |
-
dict_features_out = self.compute_feature_distance(y_i, p_hat[i], self.sample_rate, type=self.type)
|
| 455 |
-
dict_features[index] = dict_features_out["distance"]
|
| 456 |
-
elif self.type=="fxenc2048AFv3-fxenc++":
|
| 457 |
-
dict_features_out = self.compute_feature_distance(y_i, p_hat[i], self.sample_rate, type=self.type)
|
| 458 |
-
dict_features[index] = dict_features_out["distance"]
|
| 459 |
-
elif self.type=="fxenc2048AFv3-AF":
|
| 460 |
-
dict_features_out = self.compute_feature_distance(y_i, p_hat, self.sample_rate, type=self.type)
|
| 461 |
-
dict_features[index] = dict_features_out["distance"]
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
mean_features = sum(dict_features.values()) / len(dict_features)
|
| 465 |
-
|
| 466 |
-
return mean_features, {}
|
| 467 |
-
class PairwiseStyleFeatures(PairwiseMetric):
|
| 468 |
-
"""
|
| 469 |
-
Class for computing the pairwise spectral metric.
|
| 470 |
-
|
| 471 |
-
This class inherits from PairwiseMetric and implements the compute method
|
| 472 |
-
to calculate the pairwise spectral metric.
|
| 473 |
-
"""
|
| 474 |
-
def __init__(self,
|
| 475 |
-
type=None,
|
| 476 |
-
sample_rate=44100,
|
| 477 |
-
*args, **kwargs):
|
| 478 |
-
"""
|
| 479 |
-
Initialize the PairwiseSpectral instance.
|
| 480 |
-
|
| 481 |
-
Args:
|
| 482 |
-
*args: Variable length argument list.
|
| 483 |
-
**kwargs: Arbitrary keyword arguments.
|
| 484 |
-
"""
|
| 485 |
-
self.type = type
|
| 486 |
-
self.sample_rate = sample_rate
|
| 487 |
-
|
| 488 |
-
self.device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 489 |
-
|
| 490 |
-
if self.type == "fx_encoder":
|
| 491 |
-
raise NotImplementedError("Style features for fx_encoder not implemented yet")
|
| 492 |
-
self.model_args= kwargs.get("fx_encoder_args", None)
|
| 493 |
-
|
| 494 |
-
assert self.model_args is not None, "model_args must be provided for fx_encoder type"
|
| 495 |
-
|
| 496 |
-
self.distance_type=self.model_args.distance_type
|
| 497 |
-
|
| 498 |
-
self.feat_extractor = load_fx_encoder(self.model_args, self.device)
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
#self.feat_extractor = load_effects_encoder(ckpt_path=ckpt_path).to(self.device)
|
| 502 |
-
|
| 503 |
-
elif self.type== "AFxRep-mid" or self.type== "AFxRep-side" or self.type== "AFxRep":
|
| 504 |
-
|
| 505 |
-
self.model_args= kwargs.get("AFxRep_args", None)
|
| 506 |
-
|
| 507 |
-
assert self.model_args is not None, "model_args must be provided for AFxRep type"
|
| 508 |
-
|
| 509 |
-
self.distance_type=self.model_args.distance_type
|
| 510 |
-
|
| 511 |
-
feat_extractor = load_AFxRep(self.model_args, self.device)
|
| 512 |
-
|
| 513 |
-
if self.type == "AFxRep-mid":
|
| 514 |
-
def feat_extractor_mid(x):
|
| 515 |
-
|
| 516 |
-
features= feat_extractor(x)
|
| 517 |
-
|
| 518 |
-
#divide by 2 to get mid and side features
|
| 519 |
-
|
| 520 |
-
feat_mid, feat_side = features.chunk(2, dim=-1)
|
| 521 |
-
|
| 522 |
-
return feat_mid
|
| 523 |
-
|
| 524 |
-
self.feat_extractor = feat_extractor_mid
|
| 525 |
-
|
| 526 |
-
elif self.type == "AFxRep-side":
|
| 527 |
-
def feat_extractor_side(x):
|
| 528 |
-
|
| 529 |
-
features= feat_extractor(x)
|
| 530 |
-
|
| 531 |
-
#divide by 2 to get mid and side features
|
| 532 |
-
|
| 533 |
-
feat_mid, feat_side = features.chunk(2, dim=-1)
|
| 534 |
-
|
| 535 |
-
return feat_side
|
| 536 |
-
|
| 537 |
-
self.feat_extractor = feat_extractor_side
|
| 538 |
-
else:
|
| 539 |
-
self.feat_extractor = feat_extractor
|
| 540 |
-
|
| 541 |
-
elif self.type == "fxenc2048AFv3-fxenc++":
|
| 542 |
-
|
| 543 |
-
self.model_args= kwargs.get("fx_encoder_plusplus_args", None)
|
| 544 |
-
assert self.model_args is not None, "model_args must be provided for fxencAFv2-fxenc++ type"
|
| 545 |
-
|
| 546 |
-
self.distance_type= self.model_args.distance_type
|
| 547 |
-
|
| 548 |
-
fxencoder = load_fx_encoder_plusplus_2048(self.model_args, self.device)
|
| 549 |
-
|
| 550 |
-
def feat_extractor_fn(x):
|
| 551 |
-
z= fxencoder(x)
|
| 552 |
-
z=torch.nn.functional.normalize(z, dim=-1, p=2) # normalize to unit variance
|
| 553 |
-
return z
|
| 554 |
-
|
| 555 |
-
self.feat_extractor = feat_extractor_fn
|
| 556 |
-
|
| 557 |
-
elif self.type == "fxenc2048AFv3-AF":
|
| 558 |
-
|
| 559 |
-
from utils.AF_features_embedding_v2 import AF_fourier_embedding
|
| 560 |
-
AFembedding= AF_fourier_embedding(device=self.device)
|
| 561 |
-
|
| 562 |
-
self.distance_type="cosine"
|
| 563 |
-
|
| 564 |
-
def feat_extracfor_fn(x):
|
| 565 |
-
z, _ = AFembedding.encode(x)
|
| 566 |
-
return z
|
| 567 |
-
|
| 568 |
-
self.feat_extractor = feat_extracfor_fn
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
super().__init__(*args, **kwargs)
|
| 572 |
-
|
| 573 |
-
def compute_feature_distance(self, y, p_hat, sample_rate, type):
|
| 574 |
-
|
| 575 |
-
y=torch.tensor(y).permute(1,0).unsqueeze(0).to(self.device)
|
| 576 |
-
#y_hat=torch.tensor(y_hat).permute(1,0).unsqueeze(0).to(self.device)
|
| 577 |
-
|
| 578 |
-
with torch.no_grad():
|
| 579 |
-
feat_y= self.feat_extractor(y)
|
| 580 |
-
#feat_y_hat= self.feat_extractor(y_hat)
|
| 581 |
-
|
| 582 |
-
assert p_hat.shape == feat_y.shape, f"Shape mismatch: p_hat {p_hat.shape} vs feat_y {feat_y.shape}"
|
| 583 |
-
|
| 584 |
-
if self.distance_type == "cosine":
|
| 585 |
-
cos_dist= 1- torch.cosine_similarity(p_hat, feat_y, dim=1)
|
| 586 |
-
|
| 587 |
-
return {"distance": cos_dist.mean().item()}
|
| 588 |
-
|
| 589 |
-
|
| 590 |
-
|
| 591 |
-
def compute(self, dict_y, dict_y_hat, dict_x, dict_p_hat=None, *args, **kwargs):
|
| 592 |
-
"""
|
| 593 |
-
Compute the pairwise spectral metric.
|
| 594 |
-
|
| 595 |
-
Args:
|
| 596 |
-
*args: Variable length argument list.
|
| 597 |
-
**kwargs: Arbitrary keyword arguments.
|
| 598 |
-
|
| 599 |
-
Returns:
|
| 600 |
-
The computed pairwise spectral metric.
|
| 601 |
-
"""
|
| 602 |
-
|
| 603 |
-
dict_features={}
|
| 604 |
-
|
| 605 |
-
for key in dict_y.keys():
|
| 606 |
-
y= dict_y[key]
|
| 607 |
-
|
| 608 |
-
embed= dict_p_hat[key]
|
| 609 |
-
embed=torch.tensor(embed).to(self.device).unsqueeze(0)
|
| 610 |
-
|
| 611 |
-
print(f"Processing key: {key}, embed shape: {embed.shape}")
|
| 612 |
-
if "AFxRep" in self.type:
|
| 613 |
-
embed_mid, embed_side = torch.chunk(embed, 2, dim=-1)
|
| 614 |
-
|
| 615 |
-
if self.type== "AFxRep-mid":
|
| 616 |
-
p_hat= embed_mid
|
| 617 |
-
elif self.type== "AFxRep-side":
|
| 618 |
-
p_hat= embed_side
|
| 619 |
-
elif self.type== "AFxRep":
|
| 620 |
-
p_hat= embed
|
| 621 |
-
elif "fxenc2048AFv3" in self.type:
|
| 622 |
-
|
| 623 |
-
embed=embed*math.sqrt(embed.shape[-1]) # Scale the embedding
|
| 624 |
-
embed_fxenc=embed[...,:2048]/ math.sqrt(2048) # Scale the first 128 dimensions
|
| 625 |
-
embed_AF=embed[...,2048:]/ math.sqrt(64) # Scale the last 128 dimensions
|
| 626 |
-
|
| 627 |
-
if self.type == "fxenc2048AFv3-fxenc++":
|
| 628 |
-
p_hat= embed_fxenc
|
| 629 |
-
elif self.type == "fxenc2048AFv3-AF":
|
| 630 |
-
p_hat= embed_AF
|
| 631 |
-
|
| 632 |
-
|
| 633 |
-
c, d=y.shape
|
| 634 |
-
#assert b==1, f"Expected batch size of 1, got {b} for key {key}"
|
| 635 |
-
|
| 636 |
-
assert c==2, f"Expected 2 channels, got {c} for key {key}"
|
| 637 |
-
|
| 638 |
-
y=y.T
|
| 639 |
-
#y_hat=y_hat.T
|
| 640 |
-
|
| 641 |
-
if self.type=="fx_encoder":
|
| 642 |
-
raise NotImplementedError("Style features for fx_encoder not implemented yet")
|
| 643 |
-
dict_features_out = self.compute_feature_distance(y, p_hat, self.sample_rate, type=self.type)
|
| 644 |
-
dict_features[key] = dict_features_out["distance"]
|
| 645 |
-
elif self.type=="AFxRep":
|
| 646 |
-
dict_features_out = self.compute_feature_distance(y, p_hat, self.sample_rate, type=self.type)
|
| 647 |
-
dict_features[key] = dict_features_out["distance"]
|
| 648 |
-
elif self.type=="AFxRep-mid":
|
| 649 |
-
dict_features_out = self.compute_feature_distance(y, p_hat, self.sample_rate, type=self.type)
|
| 650 |
-
dict_features[key] = dict_features_out["distance"]
|
| 651 |
-
elif self.type=="AFxRep-side":
|
| 652 |
-
dict_features_out = self.compute_feature_distance(y, p_hat, self.sample_rate, type=self.type)
|
| 653 |
-
dict_features[key] = dict_features_out["distance"]
|
| 654 |
-
elif self.type=="fxenc2048AFv3-fxenc++":
|
| 655 |
-
dict_features_out = self.compute_feature_distance(y, p_hat, self.sample_rate, type=self.type)
|
| 656 |
-
dict_features[key] = dict_features_out["distance"]
|
| 657 |
-
elif self.type=="fxenc2048AFv3-AF":
|
| 658 |
-
dict_features_out = self.compute_feature_distance(y, p_hat, self.sample_rate, type=self.type)
|
| 659 |
-
dict_features[key] = dict_features_out["distance"]
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
# Compute the mean of the features across all keys
|
| 664 |
-
|
| 665 |
-
mean_features = sum(dict_features.values()) / len(dict_features)
|
| 666 |
-
|
| 667 |
-
return mean_features, {}
|
| 668 |
-
|
| 669 |
-
class PairwiseIMMSS(PairwiseMetric):
|
| 670 |
-
"""
|
| 671 |
-
Class for computing the pairwise LDR metric.
|
| 672 |
-
|
| 673 |
-
This class inherits from PairwiseMetric and implements the compute method
|
| 674 |
-
to calculate the pairwise LDR metric.
|
| 675 |
-
"""
|
| 676 |
-
def __init__(self, mode=None, *args, **kwargs):
|
| 677 |
-
"""
|
| 678 |
-
Initialize the PairwiseLDR instance.
|
| 679 |
-
|
| 680 |
-
Args:
|
| 681 |
-
*args: Variable length argument list.
|
| 682 |
-
**kwargs: Arbitrary keyword arguments.
|
| 683 |
-
"""
|
| 684 |
-
super().__init__(*args, **kwargs)
|
| 685 |
-
|
| 686 |
-
assert mode is not None, "Mode must be specified for PairwiseLDR"
|
| 687 |
-
|
| 688 |
-
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 689 |
-
if mode == "mss-lr":
|
| 690 |
-
multi_scale_spectral_ori = MultiScale_Spectral_Loss_MidSide_DDSP(mode='ori', eps=1e-6, device=device)
|
| 691 |
-
self.metric= lambda y_hat, y: multi_scale_spectral_ori(y_hat, y)
|
| 692 |
-
elif mode == "mss-ms":
|
| 693 |
-
multi_scale_spectral_midside = MultiScale_Spectral_Loss_MidSide_DDSP(mode='midside', eps=1e-6, device=device)
|
| 694 |
-
self.metric= lambda y_hat, y: multi_scale_spectral_midside(y_hat, y)
|
| 695 |
-
|
| 696 |
-
def compute(self, dict_y, dict_y_hat, dict_x, *args, **kwargs):
|
| 697 |
-
"""
|
| 698 |
-
Compute the pairwise spectral metric.
|
| 699 |
-
|
| 700 |
-
Args:
|
| 701 |
-
*args: Variable length argument list.
|
| 702 |
-
**kwargs: Arbitrary keyword arguments.
|
| 703 |
-
|
| 704 |
-
Returns:
|
| 705 |
-
The computed pairwise spectral metric.
|
| 706 |
-
"""
|
| 707 |
-
|
| 708 |
-
dict_metrics={}
|
| 709 |
-
|
| 710 |
-
for key in dict_y.keys():
|
| 711 |
-
y= dict_y[key]
|
| 712 |
-
y_hat= dict_y_hat[key]
|
| 713 |
-
|
| 714 |
-
assert y.shape == y_hat.shape, f"Shape mismatch for key {key}: {y.shape} vs {y_hat.shape}"
|
| 715 |
-
|
| 716 |
-
c, d=y.shape
|
| 717 |
-
#assert b==1, f"Expected batch size of 1, got {b} for key {key}"
|
| 718 |
-
|
| 719 |
-
assert c==2, f"Expected 2 channels, got {c} for key {key}"
|
| 720 |
-
|
| 721 |
-
y=torch.from_numpy(y).cuda().unsqueeze(0)
|
| 722 |
-
y_hat=torch.from_numpy(y_hat).cuda().unsqueeze(0)
|
| 723 |
-
|
| 724 |
-
metric=self.metric(y_hat, y)
|
| 725 |
-
|
| 726 |
-
dict_metrics[key] = metric.item()
|
| 727 |
-
|
| 728 |
-
mean_features = sum(dict_metrics.values()) / len(dict_metrics)
|
| 729 |
-
|
| 730 |
-
return mean_features, {}
|
| 731 |
-
class PairwiseLDR(PairwiseMetric):
|
| 732 |
-
"""
|
| 733 |
-
Class for computing the pairwise LDR metric.
|
| 734 |
-
|
| 735 |
-
This class inherits from PairwiseMetric and implements the compute method
|
| 736 |
-
to calculate the pairwise LDR metric.
|
| 737 |
-
"""
|
| 738 |
-
def __init__(self, mode=None, *args, **kwargs):
|
| 739 |
-
"""
|
| 740 |
-
Initialize the PairwiseLDR instance.
|
| 741 |
-
|
| 742 |
-
Args:
|
| 743 |
-
*args: Variable length argument list.
|
| 744 |
-
**kwargs: Arbitrary keyword arguments.
|
| 745 |
-
"""
|
| 746 |
-
super().__init__(*args, **kwargs)
|
| 747 |
-
|
| 748 |
-
assert mode is not None, "Mode must be specified for PairwiseLDR"
|
| 749 |
-
|
| 750 |
-
if mode == "mldr-lr":
|
| 751 |
-
from evaluation.ldr import MLDRLoss
|
| 752 |
-
raw_metric= MLDRLoss(
|
| 753 |
-
sr=44100,
|
| 754 |
-
s_taus=[50, 100],
|
| 755 |
-
l_taus=[1000, 2000],
|
| 756 |
-
).cuda()
|
| 757 |
-
self.metric=lambda y_hat, y: 0.5*raw_metric(y_hat, y)
|
| 758 |
-
elif mode == "mldr-ms":
|
| 759 |
-
from evaluation.ldr import MLDRLoss
|
| 760 |
-
raw_metric= MLDRLoss(
|
| 761 |
-
sr=44100,
|
| 762 |
-
s_taus=[50, 100],
|
| 763 |
-
l_taus=[1000, 2000],
|
| 764 |
-
mid_side=True
|
| 765 |
-
).cuda()
|
| 766 |
-
self.metric=lambda y_hat, y: 0.25*raw_metric(y_hat, y)
|
| 767 |
-
|
| 768 |
-
def compute(self, dict_y, dict_y_hat, dict_x, *args, **kwargs):
|
| 769 |
-
"""
|
| 770 |
-
Compute the pairwise spectral metric.
|
| 771 |
-
|
| 772 |
-
Args:
|
| 773 |
-
*args: Variable length argument list.
|
| 774 |
-
**kwargs: Arbitrary keyword arguments.
|
| 775 |
-
|
| 776 |
-
Returns:
|
| 777 |
-
The computed pairwise spectral metric.
|
| 778 |
-
"""
|
| 779 |
-
|
| 780 |
-
dict_metrics={}
|
| 781 |
-
|
| 782 |
-
for key in dict_y.keys():
|
| 783 |
-
y= dict_y[key]
|
| 784 |
-
y_hat= dict_y_hat[key]
|
| 785 |
-
|
| 786 |
-
assert y.shape == y_hat.shape, f"Shape mismatch for key {key}: {y.shape} vs {y_hat.shape}"
|
| 787 |
-
|
| 788 |
-
c, d=y.shape
|
| 789 |
-
#assert b==1, f"Expected batch size of 1, got {b} for key {key}"
|
| 790 |
-
|
| 791 |
-
assert c==2, f"Expected 2 channels, got {c} for key {key}"
|
| 792 |
-
|
| 793 |
-
y=y.T
|
| 794 |
-
y_hat=y_hat.T
|
| 795 |
-
|
| 796 |
-
y=torch.from_numpy(y).cuda().unsqueeze(0)
|
| 797 |
-
y_hat=torch.from_numpy(y_hat).cuda().unsqueeze(0)
|
| 798 |
-
|
| 799 |
-
metric=self.metric(y_hat, y)
|
| 800 |
-
|
| 801 |
-
dict_metrics[key] = metric.item()
|
| 802 |
-
|
| 803 |
-
mean_features = sum(dict_metrics.values()) / len(dict_metrics)
|
| 804 |
-
|
| 805 |
-
return mean_features, {}
|
| 806 |
-
|
| 807 |
-
|
| 808 |
-
class PairwiseAuraloss(PairwiseMetric):
|
| 809 |
-
"""
|
| 810 |
-
Class for computing the pairwise MSS metric.
|
| 811 |
-
|
| 812 |
-
This class inherits from PairwiseMetric and implements the compute method
|
| 813 |
-
to calculate the pairwise MSS metric.
|
| 814 |
-
"""
|
| 815 |
-
def __init__(self, mode=None, *args, **kwargs):
|
| 816 |
-
"""
|
| 817 |
-
Initialize the PairwiseMSS instance.
|
| 818 |
-
|
| 819 |
-
Args:
|
| 820 |
-
*args: Variable length argument list.
|
| 821 |
-
**kwargs: Arbitrary keyword arguments.
|
| 822 |
-
"""
|
| 823 |
-
super().__init__(*args, **kwargs)
|
| 824 |
-
|
| 825 |
-
assert mode is not None, "Mode must be specified for PairwiseMSS"
|
| 826 |
-
|
| 827 |
-
if mode == "mss-lr":
|
| 828 |
-
from auraloss.freq import MultiResolutionSTFTLoss
|
| 829 |
-
raw_metric=MultiResolutionSTFTLoss(
|
| 830 |
-
[128, 512, 2048],
|
| 831 |
-
[32, 128, 512],
|
| 832 |
-
[128, 512, 2048],
|
| 833 |
-
sample_rate=44100,
|
| 834 |
-
perceptual_weighting=True,
|
| 835 |
-
).cuda()
|
| 836 |
-
self.metric=lambda y_hat, y: raw_metric(y_hat, y)
|
| 837 |
-
elif mode == "mss-ms":
|
| 838 |
-
from auraloss.freq import SumAndDifferenceSTFTLoss
|
| 839 |
-
raw_metric=SumAndDifferenceSTFTLoss(
|
| 840 |
-
[128, 512, 2048],
|
| 841 |
-
[32, 128, 512],
|
| 842 |
-
[128, 512, 2048],
|
| 843 |
-
sample_rate=44100,
|
| 844 |
-
perceptual_weighting=True,
|
| 845 |
-
).cuda()
|
| 846 |
-
self.metric=lambda y_hat, y: 0.5*raw_metric(y_hat, y)
|
| 847 |
-
|
| 848 |
-
def compute(self, dict_y, dict_y_hat, dict_x, *args, **kwargs):
|
| 849 |
-
"""
|
| 850 |
-
Compute the pairwise spectral metric.
|
| 851 |
-
|
| 852 |
-
Args:
|
| 853 |
-
*args: Variable length argument list.
|
| 854 |
-
**kwargs: Arbitrary keyword arguments.
|
| 855 |
-
|
| 856 |
-
Returns:
|
| 857 |
-
The computed pairwise spectral metric.
|
| 858 |
-
"""
|
| 859 |
-
|
| 860 |
-
dict_metrics={}
|
| 861 |
-
|
| 862 |
-
for key in dict_y.keys():
|
| 863 |
-
y= dict_y[key]
|
| 864 |
-
y_hat= dict_y_hat[key]
|
| 865 |
-
|
| 866 |
-
assert y.shape == y_hat.shape, f"Shape mismatch for key {key}: {y.shape} vs {y_hat.shape}"
|
| 867 |
-
|
| 868 |
-
c, d=y.shape
|
| 869 |
-
#assert b==1, f"Expected batch size of 1, got {b} for key {key}"
|
| 870 |
-
|
| 871 |
-
assert c==2, f"Expected 2 channels, got {c} for key {key}"
|
| 872 |
-
|
| 873 |
-
#y=y.T
|
| 874 |
-
#y_hat=y_hat.T
|
| 875 |
-
|
| 876 |
-
y=torch.from_numpy(y).cuda().unsqueeze(0)
|
| 877 |
-
y_hat=torch.from_numpy(y_hat).cuda().unsqueeze(0)
|
| 878 |
-
|
| 879 |
-
metric=self.metric(y_hat, y)
|
| 880 |
-
|
| 881 |
-
dict_metrics[key] = metric.item()
|
| 882 |
-
|
| 883 |
-
mean_features = sum(dict_metrics.values()) / len(dict_metrics)
|
| 884 |
-
|
| 885 |
-
return mean_features, {}
|
| 886 |
-
|
| 887 |
-
def metric_factory(metric_name, sample_rate, *args, **kwargs):
|
| 888 |
-
"""
|
| 889 |
-
Factory function to create a metric function based on the metric name.
|
| 890 |
-
|
| 891 |
-
Args:
|
| 892 |
-
metric_name (str): The name of the metric to create.
|
| 893 |
-
*args: Variable length argument list.
|
| 894 |
-
**kwargs: Arbitrary keyword arguments.
|
| 895 |
-
|
| 896 |
-
Returns:
|
| 897 |
-
An instance of a class that implements the metric function.
|
| 898 |
-
"""
|
| 899 |
-
if metric_name == "pairwise-spectral":
|
| 900 |
-
return PairwiseFeatures(*args, **kwargs, type="spectral", sample_rate=sample_rate)
|
| 901 |
-
elif metric_name == "pairwise-panning":
|
| 902 |
-
return PairwiseFeatures(*args, **kwargs, type="panning", sample_rate=sample_rate)
|
| 903 |
-
elif metric_name == "pairwise-loudness":
|
| 904 |
-
return PairwiseFeatures(*args, **kwargs, type="loudness", sample_rate=sample_rate)
|
| 905 |
-
elif metric_name == "pairwise-dynamic":
|
| 906 |
-
return PairwiseFeatures(*args, **kwargs, type="dynamic", sample_rate=sample_rate)
|
| 907 |
-
elif metric_name == "pairwise-mss-lr":
|
| 908 |
-
return PairwiseAuraloss(mode="mss-lr",*args, **kwargs)
|
| 909 |
-
elif metric_name == "pairwise-mss-ms":
|
| 910 |
-
return PairwiseAuraloss(mode="mss-ms",*args, **kwargs)
|
| 911 |
-
elif metric_name == "pairwise-IM-mss-lr":
|
| 912 |
-
return PairwiseIMMSS(mode="mss-lr",*args, **kwargs)
|
| 913 |
-
elif metric_name == "pairwise-IM-mss-ms":
|
| 914 |
-
return PairwiseIMMSS(mode="mss-ms",*args, **kwargs)
|
| 915 |
-
elif metric_name == "pairwise-mldr-lr":
|
| 916 |
-
return PairwiseLDR(mode="mldr-lr",*args, **kwargs)
|
| 917 |
-
elif metric_name == "pairwise-mldr-ms":
|
| 918 |
-
return PairwiseLDR(mode="mldr-ms",*args, **kwargs)
|
| 919 |
-
elif metric_name == "pairwise-fx_encoder":
|
| 920 |
-
return PairwiseFeatures(*args, **kwargs, type="fx_encoder", sample_rate=sample_rate, model_args=kwargs.get('fx_encoder_args', None))
|
| 921 |
-
elif metric_name == "pairwise-AFxRep":
|
| 922 |
-
return PairwiseFeatures(*args, **kwargs, type="AFxRep", sample_rate=sample_rate, model_args=kwargs.get('AFxRep_args', None))
|
| 923 |
-
elif metric_name == "pairwise-AFxRep-mid":
|
| 924 |
-
return PairwiseFeatures(*args, **kwargs, type="AFxRep-mid", sample_rate=sample_rate, model_args=kwargs.get('AFxRep_args', None))
|
| 925 |
-
elif metric_name == "pairwise-AFxRep-side":
|
| 926 |
-
return PairwiseFeatures(*args, **kwargs, type="AFxRep-side", sample_rate=sample_rate, model_args=kwargs.get('AFxRep_args', None))
|
| 927 |
-
elif metric_name == "pairwise-style-AFxRep":
|
| 928 |
-
return PairwiseStyleFeatures(*args, **kwargs, type="AFxRep", sample_rate=sample_rate, model_args=kwargs.get('AFxRep_args', None))
|
| 929 |
-
elif metric_name == "pairwise-style-AFxRep-mid":
|
| 930 |
-
return PairwiseStyleFeatures(*args, **kwargs, type="AFxRep-mid", sample_rate=sample_rate, model_args=kwargs.get('AFxRep_args', None))
|
| 931 |
-
elif metric_name == "pairwise-style-AFxRep-side":
|
| 932 |
-
return PairwiseStyleFeatures(*args, **kwargs, type="AFxRep-side", sample_rate=sample_rate, model_args=kwargs.get('AFxRep_args', None))
|
| 933 |
-
elif metric_name == "pairwise-style-fxenc2048AFv3-fxenc++":
|
| 934 |
-
return PairwiseStyleFeatures(*args, **kwargs, type="fxenc2048AFv3-fxenc++", sample_rate=sample_rate, model_args=kwargs.get('AFxRep_args', None))
|
| 935 |
-
elif metric_name == "pairwise-style-fxenc2048AFv3-AF":
|
| 936 |
-
return PairwiseStyleFeatures(*args, **kwargs, type="fxenc2048AFv3-AF", sample_rate=sample_rate, model_args=kwargs.get('AFxRep_args', None))
|
| 937 |
-
elif metric_name == "pairwise-style-multitrack-fxenc2048AFv3-fxenc++":
|
| 938 |
-
return PairwiseStyleMultitrackFeatures(*args, **kwargs, type="fxenc2048AFv3-fxenc++", sample_rate=sample_rate, model_args=kwargs.get('AFxRep_args', None))
|
| 939 |
-
elif metric_name == "pairwise-style-multitrack-fxenc2048AFv3-AF":
|
| 940 |
-
return PairwiseStyleMultitrackFeatures(*args, **kwargs, type="fxenc2048AFv3-AF", sample_rate=sample_rate, model_args=kwargs.get('AFxRep_args', None))
|
| 941 |
-
elif metric_name == "pairwise-fxenc++":
|
| 942 |
-
return PairwiseFeatures(*args, **kwargs, type="fxenc++", sample_rate=sample_rate, model_args=kwargs.get('fx_encoder_plusplus_args', None))
|
| 943 |
-
elif metric_name == "pairwise-logrms":
|
| 944 |
-
return PairwiseFeatures(*args, **kwargs, type="logrms", sample_rate=sample_rate, model_args=kwargs.get('logrms_args', None))
|
| 945 |
-
elif metric_name == "pairwise-crestfactor":
|
| 946 |
-
return PairwiseFeatures(*args, **kwargs, type="crestfactor", sample_rate=sample_rate, model_args=kwargs.get('crestfactor_args', None))
|
| 947 |
-
elif metric_name == "pairwise-logspread":
|
| 948 |
-
return PairwiseFeatures(*args, **kwargs, type="logspread", sample_rate=sample_rate, model_args=kwargs.get('logspread_args', None))
|
| 949 |
-
elif metric_name == "pairwise-stereowidth":
|
| 950 |
-
return PairwiseFeatures(*args, **kwargs, type="stereowidth", sample_rate=sample_rate, model_args=kwargs.get('stereowidth_args', None))
|
| 951 |
-
elif metric_name == "pairwise-stereoimbalance":
|
| 952 |
-
return PairwiseFeatures(*args, **kwargs, type="stereoimbalance", sample_rate=sample_rate, model_args=kwargs.get('stereoimbalance_args', None))
|
| 953 |
-
else:
|
| 954 |
-
raise ValueError(f"Unknown metric: {metric_name}")
|
| 955 |
-
|
| 956 |
-
# Example usage:
|
| 957 |
-
#metric_instance = metric_factory("pairwise-spectral")
|
| 958 |
-
#```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/feature_extractors/audio_features.py
DELETED
|
@@ -1,76 +0,0 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
-
import librosa
|
| 3 |
-
|
| 4 |
-
def compute_spectrogram(file_path, set_limit=False, limit_seconds=10):
|
| 5 |
-
"""Compute the spectrogram of an audio file."""
|
| 6 |
-
y, sr = librosa.load(file_path, sr=None)
|
| 7 |
-
if set_limit:
|
| 8 |
-
y = y[:int(sr*limit_seconds)]
|
| 9 |
-
S = librosa.stft(y)
|
| 10 |
-
S_db = librosa.amplitude_to_db(np.abs(S), ref=np.max)
|
| 11 |
-
return S_db, sr, y
|
| 12 |
-
|
| 13 |
-
def smooth_curve(data, window_size=30):
|
| 14 |
-
"""Smooth a curve using a moving average."""
|
| 15 |
-
return np.convolve(data, np.ones(window_size)/window_size, mode='same')
|
| 16 |
-
|
| 17 |
-
def compute_crest_factor(y, sr, frame_length=2048, hop_length=512):
|
| 18 |
-
# compute crest factor by windowing
|
| 19 |
-
crest_factor_total = []
|
| 20 |
-
for i in range(0, len(y), hop_length):
|
| 21 |
-
frame = y[i:i+frame_length]
|
| 22 |
-
peak = np.max(np.abs(frame))
|
| 23 |
-
rms = np.sqrt(np.mean(frame**2))
|
| 24 |
-
cur_crest_factor = peak / (rms + 1e-6)
|
| 25 |
-
crest_factor_total.append(cur_crest_factor)
|
| 26 |
-
crest_factor = np.asarray(crest_factor_total)
|
| 27 |
-
return crest_factor
|
| 28 |
-
|
| 29 |
-
def compute_audio_features(y, sr, smooth_window_size=1, average_time=True):
|
| 30 |
-
# features to compute: RMS energy, crest factor, dynamic spread, spectral centroid, spectral contrast, spectral flatness, spectral balance, and spectral bandwidth
|
| 31 |
-
y_mono = y.mean(axis=0)
|
| 32 |
-
spectral_centroid = smooth_curve(librosa.feature.spectral_centroid(y=y_mono, sr=sr)[0], window_size=smooth_window_size)
|
| 33 |
-
spectral_contrast = smooth_curve(np.mean(librosa.feature.spectral_contrast(y=y_mono, sr=sr), axis=0), window_size=smooth_window_size)
|
| 34 |
-
spectral_flatness = smooth_curve(librosa.feature.spectral_flatness(y=y_mono)[0], window_size=smooth_window_size)
|
| 35 |
-
rms_energy = smooth_curve(librosa.feature.rms(y=y_mono)[0], window_size=smooth_window_size)
|
| 36 |
-
# Crest Factor: Peak-to-RMS ratio
|
| 37 |
-
crest_factor = smooth_curve(compute_crest_factor(y_mono, sr, frame_length=2048, hop_length=512), window_size=smooth_window_size)
|
| 38 |
-
# Dynamic Spread
|
| 39 |
-
dynamic_spread = np.std(rms_energy)
|
| 40 |
-
# Spectral Balance (Low, Mid, High Frequency Energy Ratios)
|
| 41 |
-
spec = np.abs(librosa.stft(y_mono))**2
|
| 42 |
-
freqs = librosa.fft_frequencies(sr=sr)
|
| 43 |
-
low_energy = np.sum(spec[freqs < 200])
|
| 44 |
-
mid_energy = np.sum(spec[(freqs >= 200) & (freqs < 2000)])
|
| 45 |
-
high_energy = np.sum(spec[freqs >= 2000])
|
| 46 |
-
total_energy = low_energy + mid_energy + high_energy
|
| 47 |
-
spectral_balance = np.array([low_energy / total_energy, mid_energy / total_energy, high_energy / total_energy])
|
| 48 |
-
spectral_bandwidth = smooth_curve(librosa.feature.spectral_bandwidth(y=y_mono, sr=sr)[0], window_size=smooth_window_size)
|
| 49 |
-
# Stereo Width + Mid-Side Ratio
|
| 50 |
-
y_mid = (y[:len(y)//2] + y[len(y)//2:]) / 2
|
| 51 |
-
y_side = (y[:len(y)//2] - y[len(y)//2:]) / 2
|
| 52 |
-
stereo_width = np.mean(np.abs(y_side)) / (np.mean(np.abs(y_mid)) + 1e-6)
|
| 53 |
-
mid_side_ratio = np.mean(np.abs(y_mid)) / (np.mean(np.abs(y_side)) + 1e-6)
|
| 54 |
-
# average across time
|
| 55 |
-
if average_time:
|
| 56 |
-
spectral_centroid = np.mean(spectral_centroid)
|
| 57 |
-
spectral_flatness = np.mean(spectral_flatness)
|
| 58 |
-
spectral_contrast = np.mean(spectral_contrast)
|
| 59 |
-
spectral_flatness = np.mean(spectral_flatness)
|
| 60 |
-
rms_energy = np.mean(rms_energy)
|
| 61 |
-
crest_factor = np.mean(crest_factor)
|
| 62 |
-
spectral_bandwidth = np.mean(spectral_bandwidth)
|
| 63 |
-
|
| 64 |
-
# return as a dictionary
|
| 65 |
-
return {'spectral_centroid': spectral_centroid,
|
| 66 |
-
'spectral_contrast': spectral_contrast,
|
| 67 |
-
'spectral_flatness': spectral_flatness,
|
| 68 |
-
'rms_energy': rms_energy,
|
| 69 |
-
'crest_factor': crest_factor,
|
| 70 |
-
'dynamic_spread': dynamic_spread,
|
| 71 |
-
'spectral_balance': spectral_balance,
|
| 72 |
-
'spectral_bandwidth': spectral_bandwidth,
|
| 73 |
-
'stereo_width': stereo_width,
|
| 74 |
-
'mid_side_ratio': mid_side_ratio}
|
| 75 |
-
|
| 76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/feature_extractors/load_features.py
CHANGED
|
@@ -1,37 +1,8 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import math
|
| 3 |
import sys
|
| 4 |
import torchaudio
|
| 5 |
-
from importlib import import_module
|
| 6 |
import torch
|
| 7 |
-
import yaml
|
| 8 |
import torch.nn.functional as F
|
| 9 |
|
| 10 |
-
def load_fx_encoder_plusplus(model_args, device, *args, **kwargs):
|
| 11 |
-
from utils.feature_extractors.fx_encoder_plus_plus import load_model
|
| 12 |
-
|
| 13 |
-
assert model_args is not None, "model_args must be provided for fx_encoder type"
|
| 14 |
-
|
| 15 |
-
ckpt_path=model_args.ckpt_path
|
| 16 |
-
|
| 17 |
-
model=load_model(
|
| 18 |
-
model_path=ckpt_path,
|
| 19 |
-
device=device,
|
| 20 |
-
)
|
| 21 |
-
|
| 22 |
-
def effects_encoder_fn(x):
|
| 23 |
-
assert x.ndim == 3, f"Input tensor x must be 2D, got {x.ndim}D"
|
| 24 |
-
assert x.shape[1] == 2, f"Input tensor x must have 2 channels, got {x.shape[1]} channels"
|
| 25 |
-
|
| 26 |
-
emb=model.get_fx_embedding(x)
|
| 27 |
-
#l2 normalize the embeddings
|
| 28 |
-
emb = torch.nn.functional.normalize(emb, p=2, dim=-1)
|
| 29 |
-
|
| 30 |
-
return emb
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
return lambda x: effects_encoder_fn(x)
|
| 34 |
-
|
| 35 |
def load_fx_encoder_plusplus_2048(model_args, device, *args, **kwargs):
|
| 36 |
from utils.feature_extractors.fx_encoder_plus_plus import load_model
|
| 37 |
|
|
@@ -124,137 +95,3 @@ def load_CLAP(model_args, device, *args, **kwargs):
|
|
| 124 |
|
| 125 |
return lambda x, type: clap_fn(x, type=type)
|
| 126 |
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
def load_fx_encoder(model_args, device, *args, **kwargs):
|
| 130 |
-
"""
|
| 131 |
-
Load the FX Encoder model.
|
| 132 |
-
|
| 133 |
-
Args:
|
| 134 |
-
model_args: Arguments for the FX Encoder model.
|
| 135 |
-
device: Device to load the model on (CPU or GPU).
|
| 136 |
-
|
| 137 |
-
Returns:
|
| 138 |
-
a function that extracts features from audio.
|
| 139 |
-
"""
|
| 140 |
-
assert model_args is not None, "model_args must be provided for fx_encoder type"
|
| 141 |
-
|
| 142 |
-
ckpt_path=model_args.ckpt_path
|
| 143 |
-
|
| 144 |
-
#from utils.feature_extractors.fx_encoder import load_effects_encoder
|
| 145 |
-
from utils.feature_extractors.networks import Effects_Encoder
|
| 146 |
-
|
| 147 |
-
def reload_weights(model, ckpt_path, device):
|
| 148 |
-
checkpoint = torch.load(ckpt_path, map_location=device)
|
| 149 |
-
|
| 150 |
-
from collections import OrderedDict
|
| 151 |
-
new_state_dict = OrderedDict()
|
| 152 |
-
for k, v in checkpoint["model"].items():
|
| 153 |
-
name = k[7:] # remove `module.`
|
| 154 |
-
new_state_dict[name] = v
|
| 155 |
-
model.load_state_dict(new_state_dict, strict=False)
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
try:
|
| 159 |
-
with open(os.path.join('.','utils','feature_extractors', 'networks', 'configs.yaml'), 'r') as f:
|
| 160 |
-
configs = yaml.full_load(f)
|
| 161 |
-
except:
|
| 162 |
-
with open(model_args.config_file, 'r') as f:
|
| 163 |
-
configs = yaml.full_load(f)
|
| 164 |
-
|
| 165 |
-
cfg_enc = configs['Effects_Encoder']['default']
|
| 166 |
-
|
| 167 |
-
effects_encoder = Effects_Encoder(cfg_enc)
|
| 168 |
-
reload_weights(effects_encoder, ckpt_path, device)
|
| 169 |
-
effects_encoder.to(device)
|
| 170 |
-
effects_encoder.eval()
|
| 171 |
-
|
| 172 |
-
def effects_encoder_fn(x):
|
| 173 |
-
emb=effects_encoder(x)
|
| 174 |
-
#l2 normalize the embeddings
|
| 175 |
-
emb = torch.nn.functional.normalize(emb, p=2, dim=-1)
|
| 176 |
-
return emb
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
return lambda x, *args: effects_encoder_fn(x)
|
| 180 |
-
|
| 181 |
-
def load_AFxRep(model_args, device, sample_rate=44100, peak_scaling=True, *args, **kwargs):
|
| 182 |
-
|
| 183 |
-
assert model_args is not None, "model_args must be provided for AFxRep type"
|
| 184 |
-
|
| 185 |
-
ckpt_path=model_args.ckpt_path
|
| 186 |
-
|
| 187 |
-
config_path = os.path.join(os.path.dirname(ckpt_path), "config.yaml")
|
| 188 |
-
|
| 189 |
-
with open(config_path) as f:
|
| 190 |
-
config = yaml.safe_load(f)
|
| 191 |
-
|
| 192 |
-
encoder_configs = config["model"]["init_args"]["encoder"]
|
| 193 |
-
|
| 194 |
-
module_path, class_name = encoder_configs["class_path"].rsplit(".", 1)
|
| 195 |
-
module_path = module_path.replace("lcap", "utils.st_ito")
|
| 196 |
-
|
| 197 |
-
module = import_module(module_path)
|
| 198 |
-
|
| 199 |
-
model = getattr(module, class_name)(**encoder_configs["init_args"])
|
| 200 |
-
|
| 201 |
-
checkpoint = torch.load(ckpt_path, map_location="cpu")
|
| 202 |
-
|
| 203 |
-
# load state dicts
|
| 204 |
-
state_dict = {}
|
| 205 |
-
for k, v in checkpoint["state_dict"].items():
|
| 206 |
-
if k.startswith("encoder"):
|
| 207 |
-
state_dict[k.replace("encoder.", "", 1)] = v
|
| 208 |
-
|
| 209 |
-
model.load_state_dict(state_dict)
|
| 210 |
-
|
| 211 |
-
model.eval()
|
| 212 |
-
|
| 213 |
-
model.to(device)
|
| 214 |
-
|
| 215 |
-
def wrapper_fn(x, sample_rate):
|
| 216 |
-
|
| 217 |
-
x=x.to(device)
|
| 218 |
-
|
| 219 |
-
#x=torch.transpose(x,-1,-2)
|
| 220 |
-
|
| 221 |
-
if sample_rate != 48000:
|
| 222 |
-
x=torchaudio.functional.resample(x, sample_rate, 48000)
|
| 223 |
-
|
| 224 |
-
bs= x.shape[0]
|
| 225 |
-
#peak normalization. I do it because this is what ST-ITO get_param_embeds does. Not sure if it is good that this representation is invariant to gain
|
| 226 |
-
if peak_scaling:
|
| 227 |
-
x_max=[]
|
| 228 |
-
for batch_idx in range(bs):
|
| 229 |
-
#x[batch_idx, ...] /= x[batch_idx, ...].abs().max().clamp(1e-8)
|
| 230 |
-
x_max.append( x[batch_idx, ...].abs().max().clamp(1e-8) )
|
| 231 |
-
|
| 232 |
-
if x.ndim == 3:
|
| 233 |
-
x_max=torch.stack(x_max, dim=0).view(bs, 1, 1)
|
| 234 |
-
elif x.ndim == 2:
|
| 235 |
-
x_max=torch.stack(x_max, dim=0).view(bs, 1)
|
| 236 |
-
|
| 237 |
-
x=x/ x_max
|
| 238 |
-
|
| 239 |
-
mid_embeddings, side_embeddings = model(x)
|
| 240 |
-
|
| 241 |
-
# check for nan
|
| 242 |
-
if torch.isnan(mid_embeddings).any():
|
| 243 |
-
print("Warning: NaNs found in mid_embeddings")
|
| 244 |
-
mid_embeddings = torch.nan_to_num(mid_embeddings)
|
| 245 |
-
elif torch.isnan(side_embeddings).any():
|
| 246 |
-
print("Warning: NaNs found in side_embeddings")
|
| 247 |
-
side_embeddings = torch.nan_to_num(side_embeddings)
|
| 248 |
-
|
| 249 |
-
mid_embeddings = torch.nn.functional.normalize(mid_embeddings, p=2, dim=-1)
|
| 250 |
-
side_embeddings = torch.nn.functional.normalize(side_embeddings, p=2, dim=-1)
|
| 251 |
-
|
| 252 |
-
embeddings_all= torch.cat([mid_embeddings, side_embeddings], dim=-1)
|
| 253 |
-
|
| 254 |
-
return embeddings_all
|
| 255 |
-
|
| 256 |
-
feat_extractor = lambda x, *args: wrapper_fn(x, sample_rate=sample_rate)
|
| 257 |
-
|
| 258 |
-
return feat_extractor
|
| 259 |
-
|
| 260 |
-
|
|
|
|
|
|
|
|
|
|
| 1 |
import sys
|
| 2 |
import torchaudio
|
|
|
|
| 3 |
import torch
|
|
|
|
| 4 |
import torch.nn.functional as F
|
| 5 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
def load_fx_encoder_plusplus_2048(model_args, device, *args, **kwargs):
|
| 7 |
from utils.feature_extractors.fx_encoder_plus_plus import load_model
|
| 8 |
|
|
|
|
| 95 |
|
| 96 |
return lambda x, type: clap_fn(x, type=type)
|
| 97 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/feature_extractors/networks/pytorch_utils.py
DELETED
|
@@ -1,256 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
From: https://github.com/qiuqiangkong/audioset_tagging_cnn/blob/master/pytorch/pytorch_utils.py
|
| 3 |
-
|
| 4 |
-
Copyright (c) 2018-2020 Qiuqiang Kong
|
| 5 |
-
"""
|
| 6 |
-
import numpy as np
|
| 7 |
-
import time
|
| 8 |
-
import torch
|
| 9 |
-
import torch.nn as nn
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
def move_data_to_device(x, device):
|
| 13 |
-
if 'float' in str(x.dtype):
|
| 14 |
-
x = torch.Tensor(x)
|
| 15 |
-
elif 'int' in str(x.dtype):
|
| 16 |
-
x = torch.LongTensor(x)
|
| 17 |
-
else:
|
| 18 |
-
return x
|
| 19 |
-
|
| 20 |
-
return x.to(device)
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
def do_mixup(x, mixup_lambda):
|
| 24 |
-
"""Mixup x of even indexes (0, 2, 4, ...) with x of odd indexes
|
| 25 |
-
(1, 3, 5, ...).
|
| 26 |
-
|
| 27 |
-
Args:
|
| 28 |
-
x: (batch_size * 2, ...)
|
| 29 |
-
mixup_lambda: (batch_size * 2,)
|
| 30 |
-
|
| 31 |
-
Returns:
|
| 32 |
-
out: (batch_size, ...)
|
| 33 |
-
"""
|
| 34 |
-
out = (x[0 :: 2].transpose(0, -1) * mixup_lambda[0 :: 2] + \
|
| 35 |
-
x[1 :: 2].transpose(0, -1) * mixup_lambda[1 :: 2]).transpose(0, -1)
|
| 36 |
-
return out
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
def append_to_dict(dict, key, value):
|
| 40 |
-
if key in dict.keys():
|
| 41 |
-
dict[key].append(value)
|
| 42 |
-
else:
|
| 43 |
-
dict[key] = [value]
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
def forward(model, generator, return_input=False,
|
| 47 |
-
return_target=False):
|
| 48 |
-
"""Forward data to a model.
|
| 49 |
-
|
| 50 |
-
Args:
|
| 51 |
-
model: object
|
| 52 |
-
generator: object
|
| 53 |
-
return_input: bool
|
| 54 |
-
return_target: bool
|
| 55 |
-
|
| 56 |
-
Returns:
|
| 57 |
-
audio_name: (audios_num,)
|
| 58 |
-
clipwise_output: (audios_num, classes_num)
|
| 59 |
-
(ifexist) segmentwise_output: (audios_num, segments_num, classes_num)
|
| 60 |
-
(ifexist) framewise_output: (audios_num, frames_num, classes_num)
|
| 61 |
-
(optional) return_input: (audios_num, segment_samples)
|
| 62 |
-
(optional) return_target: (audios_num, classes_num)
|
| 63 |
-
"""
|
| 64 |
-
output_dict = {}
|
| 65 |
-
device = next(model.parameters()).device
|
| 66 |
-
time1 = time.time()
|
| 67 |
-
|
| 68 |
-
# Forward data to a model in mini-batches
|
| 69 |
-
for n, batch_data_dict in enumerate(generator):
|
| 70 |
-
print(n)
|
| 71 |
-
batch_waveform = move_data_to_device(batch_data_dict['waveform'], device)
|
| 72 |
-
|
| 73 |
-
with torch.no_grad():
|
| 74 |
-
model.eval()
|
| 75 |
-
batch_output = model(batch_waveform)
|
| 76 |
-
|
| 77 |
-
append_to_dict(output_dict, 'audio_name', batch_data_dict['audio_name'])
|
| 78 |
-
|
| 79 |
-
append_to_dict(output_dict, 'clipwise_output',
|
| 80 |
-
batch_output['clipwise_output'].data.cpu().numpy())
|
| 81 |
-
|
| 82 |
-
if 'segmentwise_output' in batch_output.keys():
|
| 83 |
-
append_to_dict(output_dict, 'segmentwise_output',
|
| 84 |
-
batch_output['segmentwise_output'].data.cpu().numpy())
|
| 85 |
-
|
| 86 |
-
if 'framewise_output' in batch_output.keys():
|
| 87 |
-
append_to_dict(output_dict, 'framewise_output',
|
| 88 |
-
batch_output['framewise_output'].data.cpu().numpy())
|
| 89 |
-
|
| 90 |
-
if return_input:
|
| 91 |
-
append_to_dict(output_dict, 'waveform', batch_data_dict['waveform'])
|
| 92 |
-
|
| 93 |
-
if return_target:
|
| 94 |
-
if 'target' in batch_data_dict.keys():
|
| 95 |
-
append_to_dict(output_dict, 'target', batch_data_dict['target'])
|
| 96 |
-
|
| 97 |
-
if n % 10 == 0:
|
| 98 |
-
print(' --- Inference time: {:.3f} s / 10 iterations ---'.format(
|
| 99 |
-
time.time() - time1))
|
| 100 |
-
time1 = time.time()
|
| 101 |
-
|
| 102 |
-
for key in output_dict.keys():
|
| 103 |
-
output_dict[key] = np.concatenate(output_dict[key], axis=0)
|
| 104 |
-
|
| 105 |
-
return output_dict
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
def interpolate(x, ratio):
|
| 109 |
-
"""Interpolate data in time domain. This is used to compensate the
|
| 110 |
-
resolution reduction in downsampling of a CNN.
|
| 111 |
-
|
| 112 |
-
Args:
|
| 113 |
-
x: (batch_size, time_steps, classes_num)
|
| 114 |
-
ratio: int, ratio to interpolate
|
| 115 |
-
|
| 116 |
-
Returns:
|
| 117 |
-
upsampled: (batch_size, time_steps * ratio, classes_num)
|
| 118 |
-
"""
|
| 119 |
-
(batch_size, time_steps, classes_num) = x.shape
|
| 120 |
-
upsampled = x[:, :, None, :].repeat(1, 1, ratio, 1)
|
| 121 |
-
upsampled = upsampled.reshape(batch_size, time_steps * ratio, classes_num)
|
| 122 |
-
return upsampled
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
def pad_framewise_output(framewise_output, frames_num):
|
| 126 |
-
"""Pad framewise_output to the same length as input frames. The pad value
|
| 127 |
-
is the same as the value of the last frame.
|
| 128 |
-
|
| 129 |
-
Args:
|
| 130 |
-
framewise_output: (batch_size, frames_num, classes_num)
|
| 131 |
-
frames_num: int, number of frames to pad
|
| 132 |
-
|
| 133 |
-
Outputs:
|
| 134 |
-
output: (batch_size, frames_num, classes_num)
|
| 135 |
-
"""
|
| 136 |
-
pad = framewise_output[:, -1 :, :].repeat(1, frames_num - framewise_output.shape[1], 1)
|
| 137 |
-
"""tensor for padding"""
|
| 138 |
-
|
| 139 |
-
output = torch.cat((framewise_output, pad), dim=1)
|
| 140 |
-
"""(batch_size, frames_num, classes_num)"""
|
| 141 |
-
|
| 142 |
-
return output
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
def count_parameters(model):
|
| 146 |
-
return sum(p.numel() for p in model.parameters() if p.requires_grad)
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
def count_flops(model, audio_length):
|
| 150 |
-
"""Count flops. Code modified from others' implementation.
|
| 151 |
-
"""
|
| 152 |
-
multiply_adds = True
|
| 153 |
-
list_conv2d=[]
|
| 154 |
-
def conv2d_hook(self, input, output):
|
| 155 |
-
batch_size, input_channels, input_height, input_width = input[0].size()
|
| 156 |
-
output_channels, output_height, output_width = output[0].size()
|
| 157 |
-
|
| 158 |
-
kernel_ops = self.kernel_size[0] * self.kernel_size[1] * (self.in_channels / self.groups) * (2 if multiply_adds else 1)
|
| 159 |
-
bias_ops = 1 if self.bias is not None else 0
|
| 160 |
-
|
| 161 |
-
params = output_channels * (kernel_ops + bias_ops)
|
| 162 |
-
flops = batch_size * params * output_height * output_width
|
| 163 |
-
|
| 164 |
-
list_conv2d.append(flops)
|
| 165 |
-
|
| 166 |
-
list_conv1d=[]
|
| 167 |
-
def conv1d_hook(self, input, output):
|
| 168 |
-
batch_size, input_channels, input_length = input[0].size()
|
| 169 |
-
output_channels, output_length = output[0].size()
|
| 170 |
-
|
| 171 |
-
kernel_ops = self.kernel_size[0] * (self.in_channels / self.groups) * (2 if multiply_adds else 1)
|
| 172 |
-
bias_ops = 1 if self.bias is not None else 0
|
| 173 |
-
|
| 174 |
-
params = output_channels * (kernel_ops + bias_ops)
|
| 175 |
-
flops = batch_size * params * output_length
|
| 176 |
-
|
| 177 |
-
list_conv1d.append(flops)
|
| 178 |
-
|
| 179 |
-
list_linear=[]
|
| 180 |
-
def linear_hook(self, input, output):
|
| 181 |
-
batch_size = input[0].size(0) if input[0].dim() == 2 else 1
|
| 182 |
-
|
| 183 |
-
weight_ops = self.weight.nelement() * (2 if multiply_adds else 1)
|
| 184 |
-
bias_ops = self.bias.nelement()
|
| 185 |
-
|
| 186 |
-
flops = batch_size * (weight_ops + bias_ops)
|
| 187 |
-
list_linear.append(flops)
|
| 188 |
-
|
| 189 |
-
list_bn=[]
|
| 190 |
-
def bn_hook(self, input, output):
|
| 191 |
-
list_bn.append(input[0].nelement() * 2)
|
| 192 |
-
|
| 193 |
-
list_relu=[]
|
| 194 |
-
def relu_hook(self, input, output):
|
| 195 |
-
list_relu.append(input[0].nelement() * 2)
|
| 196 |
-
|
| 197 |
-
list_pooling2d=[]
|
| 198 |
-
def pooling2d_hook(self, input, output):
|
| 199 |
-
batch_size, input_channels, input_height, input_width = input[0].size()
|
| 200 |
-
output_channels, output_height, output_width = output[0].size()
|
| 201 |
-
|
| 202 |
-
kernel_ops = self.kernel_size * self.kernel_size
|
| 203 |
-
bias_ops = 0
|
| 204 |
-
params = output_channels * (kernel_ops + bias_ops)
|
| 205 |
-
flops = batch_size * params * output_height * output_width
|
| 206 |
-
|
| 207 |
-
list_pooling2d.append(flops)
|
| 208 |
-
|
| 209 |
-
list_pooling1d=[]
|
| 210 |
-
def pooling1d_hook(self, input, output):
|
| 211 |
-
batch_size, input_channels, input_length = input[0].size()
|
| 212 |
-
output_channels, output_length = output[0].size()
|
| 213 |
-
|
| 214 |
-
kernel_ops = self.kernel_size[0]
|
| 215 |
-
bias_ops = 0
|
| 216 |
-
|
| 217 |
-
params = output_channels * (kernel_ops + bias_ops)
|
| 218 |
-
flops = batch_size * params * output_length
|
| 219 |
-
|
| 220 |
-
list_pooling2d.append(flops)
|
| 221 |
-
|
| 222 |
-
def foo(net):
|
| 223 |
-
childrens = list(net.children())
|
| 224 |
-
if not childrens:
|
| 225 |
-
if isinstance(net, nn.Conv2d):
|
| 226 |
-
net.register_forward_hook(conv2d_hook)
|
| 227 |
-
elif isinstance(net, nn.Conv1d):
|
| 228 |
-
net.register_forward_hook(conv1d_hook)
|
| 229 |
-
elif isinstance(net, nn.Linear):
|
| 230 |
-
net.register_forward_hook(linear_hook)
|
| 231 |
-
elif isinstance(net, nn.BatchNorm2d) or isinstance(net, nn.BatchNorm1d):
|
| 232 |
-
net.register_forward_hook(bn_hook)
|
| 233 |
-
elif isinstance(net, nn.ReLU):
|
| 234 |
-
net.register_forward_hook(relu_hook)
|
| 235 |
-
elif isinstance(net, nn.AvgPool2d) or isinstance(net, nn.MaxPool2d):
|
| 236 |
-
net.register_forward_hook(pooling2d_hook)
|
| 237 |
-
elif isinstance(net, nn.AvgPool1d) or isinstance(net, nn.MaxPool1d):
|
| 238 |
-
net.register_forward_hook(pooling1d_hook)
|
| 239 |
-
else:
|
| 240 |
-
print('Warning: flop of module {} is not counted!'.format(net))
|
| 241 |
-
return
|
| 242 |
-
for c in childrens:
|
| 243 |
-
foo(c)
|
| 244 |
-
|
| 245 |
-
# Register hook
|
| 246 |
-
foo(model)
|
| 247 |
-
|
| 248 |
-
device = device = next(model.parameters()).device
|
| 249 |
-
input = torch.rand(1, audio_length).to(device)
|
| 250 |
-
|
| 251 |
-
out = model(input)
|
| 252 |
-
|
| 253 |
-
total_flops = sum(list_conv2d) + sum(list_conv1d) + sum(list_linear) + \
|
| 254 |
-
sum(list_bn) + sum(list_relu) + sum(list_pooling2d) + sum(list_pooling1d)
|
| 255 |
-
|
| 256 |
-
return total_flops
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/fx_normalization/fxnorm.py
DELETED
|
@@ -1,154 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import numpy as np
|
| 3 |
-
|
| 4 |
-
from utils.training_utils import Gauss_smooth_vectorized, prepare_smooth_filter
|
| 5 |
-
|
| 6 |
-
def T602logmag(t60, sample_rate=44100, hop_length=512):
|
| 7 |
-
return 6.908 / (t60 * (sample_rate / hop_length)) # Convert T60 to delta log magnitude
|
| 8 |
-
|
| 9 |
-
from utils.data_utils import apply_RMS_normalization
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
class FxNormAug:
|
| 13 |
-
|
| 14 |
-
def __init__(self,
|
| 15 |
-
sample_rate=44100, # Sample rate of the audio
|
| 16 |
-
device="cuda" if torch.cuda.is_available() else "cpu",
|
| 17 |
-
mode="train", # Mode can be "train" or "eval"
|
| 18 |
-
seed=42,
|
| 19 |
-
features_path="features_tency1_4instr_v4.npy", # Path to the features file
|
| 20 |
-
):
|
| 21 |
-
|
| 22 |
-
torch.random.manual_seed(seed)
|
| 23 |
-
|
| 24 |
-
#the path is in the same directory as this file
|
| 25 |
-
self.features_path = features_path
|
| 26 |
-
|
| 27 |
-
self.sample_rate = sample_rate
|
| 28 |
-
self.device = device
|
| 29 |
-
self.train_setup()
|
| 30 |
-
|
| 31 |
-
self.EQ_normalize_setup() # Initialize EQ normalization function
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
def train_setup(self):
|
| 35 |
-
#hardcoded setuo used for training
|
| 36 |
-
|
| 37 |
-
self.RMS_norm=-25 # Target RMS level in dB, used for normalization
|
| 38 |
-
|
| 39 |
-
def EQ_normalize_setup(self ):
|
| 40 |
-
|
| 41 |
-
features_mean = np.load(self.features_path, allow_pickle='TRUE')[()]
|
| 42 |
-
|
| 43 |
-
target_cuves_original= {
|
| 44 |
-
"vocals": torch.tensor(features_mean["eq"]["vocals"]).to(torch.float32).to(self.device),
|
| 45 |
-
"drums": torch.tensor(features_mean["eq"]["drums"]).to(torch.float32).to(self.device),
|
| 46 |
-
"bass": torch.tensor(features_mean["eq"]["bass"]).to(torch.float32).to(self.device),
|
| 47 |
-
"other": torch.tensor(features_mean["eq"]["other"]).to(torch.float32).to(self.device),
|
| 48 |
-
}
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
nfft=4096 # FFT size hardcoded
|
| 52 |
-
nfft_orig = 65536 # FFT size for the smoothing filter
|
| 53 |
-
|
| 54 |
-
win_length=2048 # Window length hardcoded
|
| 55 |
-
hop_length=1024 # Hop length hardcoded
|
| 56 |
-
|
| 57 |
-
window = torch.sqrt(torch.hann_window(win_length, device=self.device))
|
| 58 |
-
window_energy = window.pow(2).sum().sqrt() # Energy of the window
|
| 59 |
-
|
| 60 |
-
freqs = torch.fft.rfftfreq(nfft, d=1.0).to(self.device)
|
| 61 |
-
freqs_Hz=torch.fft.rfftfreq(nfft, d=1.0).to(self.device) * self.sample_rate
|
| 62 |
-
|
| 63 |
-
smooth_filter = prepare_smooth_filter(freqs_Hz, Noct=3).to(self.device) # Prepare the smoothing filter
|
| 64 |
-
|
| 65 |
-
freqs_Hz_orig=torch.fft.rfftfreq(nfft_orig, d=1.0).to(self.device) * self.sample_rate
|
| 66 |
-
smooth_filter_orig = prepare_smooth_filter(freqs_Hz_orig, Noct=3).to(self.device) # Prepare the smoothing filter
|
| 67 |
-
|
| 68 |
-
def downsample_curve(x):
|
| 69 |
-
return torch.nn.functional.interpolate(
|
| 70 |
-
x.unsqueeze(0).unsqueeze(0),
|
| 71 |
-
size=(nfft // 2 + 1,),
|
| 72 |
-
mode='linear',
|
| 73 |
-
align_corners=False
|
| 74 |
-
).squeeze(0).squeeze(0)
|
| 75 |
-
|
| 76 |
-
target_curves = {
|
| 77 |
-
"vocals": downsample_curve(Gauss_smooth_vectorized(target_cuves_original["vocals"], freqs_Hz_orig, Noct=3, smooth_filter=smooth_filter_orig)),
|
| 78 |
-
"drums": downsample_curve(Gauss_smooth_vectorized(target_cuves_original["drums"], freqs_Hz_orig, Noct=3, smooth_filter=smooth_filter_orig)),
|
| 79 |
-
"bass": downsample_curve(Gauss_smooth_vectorized(target_cuves_original["bass"], freqs_Hz_orig, Noct=3, smooth_filter=smooth_filter_orig)),
|
| 80 |
-
"other": downsample_curve(Gauss_smooth_vectorized(target_cuves_original["other"], freqs_Hz_orig, Noct=3, smooth_filter=smooth_filter_orig)),
|
| 81 |
-
}
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
def EQ_normalize_fn(x):
|
| 85 |
-
|
| 86 |
-
shape= x.shape
|
| 87 |
-
|
| 88 |
-
target_curves_tensor=torch.zeros((shape[0], nfft // 2 + 1), device=self.device, dtype=torch.float32)
|
| 89 |
-
|
| 90 |
-
for i in range(shape[0]):
|
| 91 |
-
track_class = "other"
|
| 92 |
-
|
| 93 |
-
assert track_class in target_curves, f"track_class {track_class} not found in target_curves"
|
| 94 |
-
target_curves_tensor[i] = target_curves[track_class]
|
| 95 |
-
|
| 96 |
-
x=x.view(-1, shape[-1])
|
| 97 |
-
#ensure x.shape[-1] is divisible by hop_length
|
| 98 |
-
if x.shape[-1] % hop_length != 0:
|
| 99 |
-
# Pad the input to make it divisible by hop_length
|
| 100 |
-
pad_length = hop_length - (x.shape[-1] % hop_length)
|
| 101 |
-
x = torch.nn.functional.pad(x, (0, pad_length), mode='constant', value=0)
|
| 102 |
-
X=torch.stft(x, n_fft=nfft, hop_length=hop_length, win_length=win_length, window=window, return_complex=True)/ window_energy
|
| 103 |
-
X_pow=X.abs().pow(2)
|
| 104 |
-
X_mean= torch.sqrt(X_pow.mean(dim=-1, keepdim=False)) # Mean power spectrum
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
ratio= target_curves_tensor / (X_mean + 1e-6)
|
| 109 |
-
|
| 110 |
-
ratio = torch.clamp(ratio, max=10.0**(20.0/20.0))
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
ratio_smooth = Gauss_smooth_vectorized(ratio, freqs_Hz, Noct=3, smooth_filter=smooth_filter)
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
X= X * ratio_smooth.unsqueeze(-1)
|
| 118 |
-
|
| 119 |
-
X_unnormalized=X* window_energy
|
| 120 |
-
|
| 121 |
-
x_reconstructed = torch.istft(X_unnormalized,
|
| 122 |
-
n_fft=nfft,
|
| 123 |
-
hop_length=hop_length,
|
| 124 |
-
win_length=win_length,
|
| 125 |
-
window=window,
|
| 126 |
-
return_complex=False) # Set to True if you want complex outpu
|
| 127 |
-
|
| 128 |
-
#remove the padding if it was added
|
| 129 |
-
if x_reconstructed.shape[-1] > shape[-1]:
|
| 130 |
-
x_reconstructed = x_reconstructed[..., :shape[-1]]
|
| 131 |
-
x_reconstructed = x_reconstructed.view(shape)
|
| 132 |
-
return x_reconstructed
|
| 133 |
-
|
| 134 |
-
self.EQ_normalize = EQ_normalize_fn
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
def __call__(self, x, use_gate=False, RMS=None):
|
| 138 |
-
|
| 139 |
-
B, C, T = x.shape
|
| 140 |
-
if C > 1:
|
| 141 |
-
x = x.mean(dim=1, keepdim=True)
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
x= apply_RMS_normalization(x, self.RMS_norm , use_gate=use_gate) # Apply RMS normalization to the input
|
| 145 |
-
|
| 146 |
-
x=self.EQ_normalize(x)
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
x= apply_RMS_normalization(x, self.RMS_norm, use_gate=use_gate)
|
| 151 |
-
assert not torch.isnan(x).any(), "NaN detected in x after EQ normalization"
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
return x
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/clap_module/__init__.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
| 1 |
from .factory import list_models, create_model, create_model_and_transforms, add_model_config
|
| 2 |
-
from .loss import ClipLoss, gather_features, LPLoss, lp_gather_features, LPMetrics
|
| 3 |
from .model import CLAP, CLAPTextCfg, CLAPVisionCfg, CLAPAudioCfp, convert_weights_to_fp16, trace_model
|
| 4 |
from .openai import load_openai_model, list_openai_models
|
| 5 |
from .pretrained import list_pretrained, list_pretrained_tag_models, list_pretrained_model_tags,\
|
| 6 |
get_pretrained_url, download_pretrained
|
| 7 |
-
from .tokenizer import SimpleTokenizer, tokenize
|
| 8 |
from .transform import image_transform
|
|
|
|
| 1 |
from .factory import list_models, create_model, create_model_and_transforms, add_model_config
|
|
|
|
| 2 |
from .model import CLAP, CLAPTextCfg, CLAPVisionCfg, CLAPAudioCfp, convert_weights_to_fp16, trace_model
|
| 3 |
from .openai import load_openai_model, list_openai_models
|
| 4 |
from .pretrained import list_pretrained, list_pretrained_tag_models, list_pretrained_model_tags,\
|
| 5 |
get_pretrained_url, download_pretrained
|
|
|
|
| 6 |
from .transform import image_transform
|
utils/laion_clap/clap_module/bert.py
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
from transformers import BertTokenizer, BertModel
|
| 2 |
-
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
|
| 3 |
-
model = BertModel.from_pretrained("bert-base-uncased")
|
| 4 |
-
text = "Replace me by any text you'd like."
|
| 5 |
-
|
| 6 |
-
def bert_embeddings(text):
|
| 7 |
-
# text = "Replace me by any text you'd like."
|
| 8 |
-
encoded_input = tokenizer(text, return_tensors='pt')
|
| 9 |
-
output = model(**encoded_input)
|
| 10 |
-
return output
|
| 11 |
-
|
| 12 |
-
from transformers import RobertaTokenizer, RobertaModel
|
| 13 |
-
|
| 14 |
-
tokenizer = RobertaTokenizer.from_pretrained('roberta-base')
|
| 15 |
-
model = RobertaModel.from_pretrained('roberta-base')
|
| 16 |
-
text = "Replace me by any text you'd like."
|
| 17 |
-
def Roberta_embeddings(text):
|
| 18 |
-
# text = "Replace me by any text you'd like."
|
| 19 |
-
encoded_input = tokenizer(text, return_tensors='pt')
|
| 20 |
-
output = model(**encoded_input)
|
| 21 |
-
return output
|
| 22 |
-
|
| 23 |
-
from transformers import BartTokenizer, BartModel
|
| 24 |
-
|
| 25 |
-
tokenizer = BartTokenizer.from_pretrained('facebook/bart-base')
|
| 26 |
-
model = BartModel.from_pretrained('facebook/bart-base')
|
| 27 |
-
text = "Replace me by any text you'd like."
|
| 28 |
-
def bart_embeddings(text):
|
| 29 |
-
# text = "Replace me by any text you'd like."
|
| 30 |
-
encoded_input = tokenizer(text, return_tensors='pt')
|
| 31 |
-
output = model(**encoded_input)
|
| 32 |
-
return output
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/clap_module/bpe_simple_vocab_16e6.txt.gz
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:924691ac288e54409236115652ad4aa250f48203de50a9e4722a6ecd48d6804a
|
| 3 |
-
size 1356917
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/clap_module/linear_probe.py
DELETED
|
@@ -1,63 +0,0 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
-
import torch.nn.functional as F
|
| 3 |
-
from torch import nn
|
| 4 |
-
from .model import MLPLayers
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
class LinearProbe(nn.Module):
|
| 8 |
-
def __init__(self, model, mlp, freeze, in_ch, out_ch, act=None):
|
| 9 |
-
"""
|
| 10 |
-
Args:
|
| 11 |
-
model: nn.Module
|
| 12 |
-
mlp: bool, if True, then use the MLP layer as the linear probe module
|
| 13 |
-
freeze: bool, if Ture, then freeze all the CLAP model's layers when training the linear probe
|
| 14 |
-
in_ch: int, the output channel from CLAP model
|
| 15 |
-
out_ch: int, the output channel from linear probe (class_num)
|
| 16 |
-
act: torch.nn.functional, the activation function before the loss function
|
| 17 |
-
"""
|
| 18 |
-
super().__init__()
|
| 19 |
-
in_ch = 512
|
| 20 |
-
self.clap_model = model
|
| 21 |
-
self.clap_model.text_branch = None # to save memory
|
| 22 |
-
self.freeze = freeze
|
| 23 |
-
if mlp:
|
| 24 |
-
self.lp_layer = MLPLayers(units=[in_ch, in_ch * 2, out_ch])
|
| 25 |
-
else:
|
| 26 |
-
self.lp_layer = nn.Linear(in_ch, out_ch)
|
| 27 |
-
|
| 28 |
-
if self.freeze:
|
| 29 |
-
for param in self.clap_model.parameters():
|
| 30 |
-
param.requires_grad = False
|
| 31 |
-
|
| 32 |
-
if act == 'None':
|
| 33 |
-
self.act = None
|
| 34 |
-
elif act == 'relu':
|
| 35 |
-
self.act = nn.ReLU()
|
| 36 |
-
elif act == 'elu':
|
| 37 |
-
self.act = nn.ELU()
|
| 38 |
-
elif act == 'prelu':
|
| 39 |
-
self.act = nn.PReLU(num_parameters=in_ch)
|
| 40 |
-
elif act == 'softmax':
|
| 41 |
-
self.act = nn.Softmax(dim=-1)
|
| 42 |
-
elif act == 'sigmoid':
|
| 43 |
-
self.act = nn.Sigmoid()
|
| 44 |
-
|
| 45 |
-
def forward(self, x, mix_lambda=None, device=None):
|
| 46 |
-
"""
|
| 47 |
-
Args:
|
| 48 |
-
x: waveform, torch.tensor [batch, t_samples] / batch of mel_spec and longer list
|
| 49 |
-
mix_lambda: torch.tensor [batch], the mixup lambda
|
| 50 |
-
Returns:
|
| 51 |
-
class_prob: torch.tensor [batch, class_num]
|
| 52 |
-
|
| 53 |
-
"""
|
| 54 |
-
# batchnorm cancel grandient
|
| 55 |
-
if self.freeze:
|
| 56 |
-
self.clap_model.eval()
|
| 57 |
-
|
| 58 |
-
x = self.clap_model.audio_projection(
|
| 59 |
-
self.clap_model.audio_branch(x, mixup_lambda=mix_lambda, device=device)["embedding"])
|
| 60 |
-
out = self.lp_layer(x)
|
| 61 |
-
if self.act is not None:
|
| 62 |
-
out = self.act(out)
|
| 63 |
-
return out
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/clap_module/loss.py
DELETED
|
@@ -1,307 +0,0 @@
|
|
| 1 |
-
from multiprocessing.sharedctypes import Value
|
| 2 |
-
import torch
|
| 3 |
-
import torch.distributed.nn
|
| 4 |
-
from torch import distributed as dist, nn as nn
|
| 5 |
-
from torch.nn import functional as F
|
| 6 |
-
import numpy as np
|
| 7 |
-
from sklearn.metrics import average_precision_score, roc_auc_score, accuracy_score
|
| 8 |
-
|
| 9 |
-
try:
|
| 10 |
-
import horovod.torch as hvd
|
| 11 |
-
except ImportError:
|
| 12 |
-
hvd = None
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
def gather_features(
|
| 16 |
-
audio_features,
|
| 17 |
-
text_features,
|
| 18 |
-
audio_features_mlp=None,
|
| 19 |
-
text_features_mlp=None,
|
| 20 |
-
local_loss=False,
|
| 21 |
-
gather_with_grad=False,
|
| 22 |
-
rank=0,
|
| 23 |
-
world_size=1,
|
| 24 |
-
use_horovod=False,
|
| 25 |
-
mlp_loss=False
|
| 26 |
-
):
|
| 27 |
-
if use_horovod:
|
| 28 |
-
assert hvd is not None, 'Please install horovod'
|
| 29 |
-
if gather_with_grad:
|
| 30 |
-
all_audio_features = hvd.allgather(audio_features)
|
| 31 |
-
all_text_features = hvd.allgather(text_features)
|
| 32 |
-
if mlp_loss:
|
| 33 |
-
all_audio_features_mlp = hvd.allgather(audio_features_mlp)
|
| 34 |
-
all_text_features_mlp = hvd.allgather(text_features_mlp)
|
| 35 |
-
else:
|
| 36 |
-
with torch.no_grad():
|
| 37 |
-
all_audio_features = hvd.allgather(audio_features)
|
| 38 |
-
all_text_features = hvd.allgather(text_features)
|
| 39 |
-
if mlp_loss:
|
| 40 |
-
all_audio_features_mlp = hvd.allgather(audio_features_mlp)
|
| 41 |
-
all_text_features_mlp = hvd.allgather(text_features_mlp)
|
| 42 |
-
if not local_loss:
|
| 43 |
-
# ensure grads for local rank when all_* features don't have a gradient
|
| 44 |
-
gathered_audio_features = list(all_audio_features.chunk(world_size, dim=0))
|
| 45 |
-
gathered_text_features = list(all_text_features.chunk(world_size, dim=0))
|
| 46 |
-
gathered_audio_features[rank] = audio_features
|
| 47 |
-
gathered_text_features[rank] = text_features
|
| 48 |
-
all_audio_features = torch.cat(gathered_audio_features, dim=0)
|
| 49 |
-
all_text_features = torch.cat(gathered_text_features, dim=0)
|
| 50 |
-
if mlp_loss:
|
| 51 |
-
gathered_audio_features_mlp = list(all_audio_features_mlp.chunk(world_size, dim=0))
|
| 52 |
-
gathered_text_features_mlp = list(all_text_features_mlp.chunk(world_size, dim=0))
|
| 53 |
-
gathered_audio_features_mlp[rank] = audio_features_mlp
|
| 54 |
-
gathered_text_features_mlp[rank] = text_features_mlp
|
| 55 |
-
all_audio_features_mlp = torch.cat(gathered_audio_features_mlp, dim=0)
|
| 56 |
-
all_text_features_mlp = torch.cat(gathered_text_features_mlp, dim=0)
|
| 57 |
-
else:
|
| 58 |
-
# We gather tensors from all gpus
|
| 59 |
-
if gather_with_grad:
|
| 60 |
-
all_audio_features = torch.cat(torch.distributed.nn.all_gather(audio_features), dim=0)
|
| 61 |
-
all_text_features = torch.cat(torch.distributed.nn.all_gather(text_features), dim=0)
|
| 62 |
-
if mlp_loss:
|
| 63 |
-
all_audio_features_mlp = torch.cat(torch.distributed.nn.all_gather(audio_features_mlp), dim=0)
|
| 64 |
-
all_text_features_mlp = torch.cat(torch.distributed.nn.all_gather(text_features_mlp), dim=0)
|
| 65 |
-
else:
|
| 66 |
-
gathered_audio_features = [torch.zeros_like(audio_features) for _ in range(world_size)]
|
| 67 |
-
gathered_text_features = [torch.zeros_like(text_features) for _ in range(world_size)]
|
| 68 |
-
dist.all_gather(gathered_audio_features, audio_features)
|
| 69 |
-
dist.all_gather(gathered_text_features, text_features)
|
| 70 |
-
if mlp_loss:
|
| 71 |
-
gathered_audio_features_mlp = [torch.zeros_like(audio_features_mlp) for _ in range(world_size)]
|
| 72 |
-
gathered_text_features_mlp = [torch.zeros_like(text_features_mlp) for _ in range(world_size)]
|
| 73 |
-
dist.all_gather(gathered_audio_features_mlp, audio_features_mlp)
|
| 74 |
-
dist.all_gather(gathered_text_features_mlp, text_features_mlp)
|
| 75 |
-
if not local_loss:
|
| 76 |
-
# ensure grads for local rank when all_* features don't have a gradient
|
| 77 |
-
gathered_audio_features[rank] = audio_features
|
| 78 |
-
gathered_text_features[rank] = text_features
|
| 79 |
-
if mlp_loss:
|
| 80 |
-
gathered_audio_features_mlp[rank] = audio_features_mlp
|
| 81 |
-
gathered_text_features_mlp[rank] = text_features_mlp
|
| 82 |
-
|
| 83 |
-
all_audio_features = torch.cat(gathered_audio_features, dim=0)
|
| 84 |
-
all_text_features = torch.cat(gathered_text_features, dim=0)
|
| 85 |
-
if mlp_loss:
|
| 86 |
-
all_audio_features_mlp = torch.cat(gathered_audio_features_mlp, dim=0)
|
| 87 |
-
all_text_features_mlp = torch.cat(gathered_text_features_mlp, dim=0)
|
| 88 |
-
if mlp_loss:
|
| 89 |
-
return all_audio_features, all_text_features, all_audio_features_mlp, all_text_features_mlp
|
| 90 |
-
else:
|
| 91 |
-
return all_audio_features, all_text_features
|
| 92 |
-
|
| 93 |
-
class ClipLoss(nn.Module):
|
| 94 |
-
|
| 95 |
-
def __init__(
|
| 96 |
-
self,
|
| 97 |
-
local_loss=False,
|
| 98 |
-
gather_with_grad=False,
|
| 99 |
-
cache_labels=False,
|
| 100 |
-
rank=0,
|
| 101 |
-
world_size=1,
|
| 102 |
-
use_horovod=False,
|
| 103 |
-
mlp_loss=False,
|
| 104 |
-
weight_loss_kappa=0,
|
| 105 |
-
):
|
| 106 |
-
super().__init__()
|
| 107 |
-
self.local_loss = local_loss
|
| 108 |
-
self.gather_with_grad = gather_with_grad
|
| 109 |
-
self.cache_labels = cache_labels
|
| 110 |
-
self.rank = rank
|
| 111 |
-
self.world_size = world_size
|
| 112 |
-
self.use_horovod = use_horovod
|
| 113 |
-
self.mlp_loss = mlp_loss
|
| 114 |
-
self.weighted_loss = bool(weight_loss_kappa!=0)
|
| 115 |
-
self.weight_loss_kappa = weight_loss_kappa
|
| 116 |
-
# cache state
|
| 117 |
-
self.prev_num_logits = 0
|
| 118 |
-
self.labels = {}
|
| 119 |
-
|
| 120 |
-
def forward(self, audio_features, text_features, logit_scale_a, logit_scale_t=None, audio_features_mlp=None, text_features_mlp=None):
|
| 121 |
-
device = audio_features.device
|
| 122 |
-
if self.mlp_loss:
|
| 123 |
-
if self.world_size > 1:
|
| 124 |
-
all_audio_features, all_text_features, all_audio_features_mlp, all_text_features_mlp = gather_features(
|
| 125 |
-
audio_features=audio_features,text_features=text_features,
|
| 126 |
-
audio_features_mlp=audio_features_mlp,text_features_mlp=text_features_mlp,
|
| 127 |
-
local_loss=self.local_loss,gather_with_grad=self.gather_with_grad,
|
| 128 |
-
rank=self.rank,world_size=self.world_size,use_horovod=self.use_horovod,
|
| 129 |
-
mlp_loss=self.mlp_loss
|
| 130 |
-
)
|
| 131 |
-
if self.local_loss:
|
| 132 |
-
a_logits_per_audio = logit_scale_a * audio_features @ all_text_features_mlp.T
|
| 133 |
-
a_logits_per_text = logit_scale_a * text_features_mlp @ all_audio_features.T
|
| 134 |
-
t_logits_per_audio = logit_scale_t * audio_features_mlp @ all_text_features.T
|
| 135 |
-
t_logits_per_text = logit_scale_t * text_features @ all_audio_features_mlp.T
|
| 136 |
-
else:
|
| 137 |
-
a_logits_per_audio = logit_scale_a * all_audio_features @ all_text_features_mlp.T
|
| 138 |
-
a_logits_per_text = a_logits_per_audio.T
|
| 139 |
-
t_logits_per_audio = logit_scale_t * all_audio_features_mlp @ all_text_features.T
|
| 140 |
-
t_logits_per_text = t_logits_per_audio.T
|
| 141 |
-
else:
|
| 142 |
-
a_logits_per_audio = logit_scale_a * audio_features @ text_features_mlp.T
|
| 143 |
-
a_logits_per_text = logit_scale_a * text_features_mlp @ audio_features.T
|
| 144 |
-
t_logits_per_audio = logit_scale_t * audio_features_mlp @ text_features.T
|
| 145 |
-
t_logits_per_text = logit_scale_t * text_features @ audio_features_mlp.T
|
| 146 |
-
|
| 147 |
-
# calculated ground-truth and cache if enabled
|
| 148 |
-
num_logits = a_logits_per_audio.shape[0]
|
| 149 |
-
if self.prev_num_logits != num_logits or device not in self.labels:
|
| 150 |
-
labels = torch.arange(num_logits, device=device, dtype=torch.long)
|
| 151 |
-
if self.world_size > 1 and self.local_loss:
|
| 152 |
-
labels = labels + num_logits * self.rank
|
| 153 |
-
if self.cache_labels:
|
| 154 |
-
self.labels[device] = labels
|
| 155 |
-
self.prev_num_logits = num_logits
|
| 156 |
-
else:
|
| 157 |
-
labels = self.labels[device]
|
| 158 |
-
|
| 159 |
-
if not self.weighted_loss:
|
| 160 |
-
total_loss = (
|
| 161 |
-
F.cross_entropy(a_logits_per_audio, labels) +
|
| 162 |
-
F.cross_entropy(a_logits_per_text, labels) +
|
| 163 |
-
F.cross_entropy(t_logits_per_audio, labels) +
|
| 164 |
-
F.cross_entropy(t_logits_per_text, labels)
|
| 165 |
-
) / 4
|
| 166 |
-
else:
|
| 167 |
-
audio_weight = (audio_features@audio_features.T).detach()
|
| 168 |
-
audio_weight = (torch.exp(torch.sum(audio_weight, axis=1)/(self.weight_loss_kappa*len(audio_weight)))).detach()
|
| 169 |
-
text_weight = (text_features@text_features.T).detach()
|
| 170 |
-
text_weight = (torch.exp(torch.sum(text_weight, axis=1)/(self.weight_loss_kappa*len(text_features)))).detach()
|
| 171 |
-
total_loss = (
|
| 172 |
-
F.cross_entropy(a_logits_per_audio, labels, weight=audio_weight) +
|
| 173 |
-
F.cross_entropy(a_logits_per_text, labels, weight=audio_weight) +
|
| 174 |
-
F.cross_entropy(t_logits_per_audio, labels, weight=text_weight) +
|
| 175 |
-
F.cross_entropy(t_logits_per_text, labels, weight=text_weight)
|
| 176 |
-
) / 4
|
| 177 |
-
else:
|
| 178 |
-
if self.world_size > 1:
|
| 179 |
-
all_audio_features, all_text_features = gather_features(
|
| 180 |
-
audio_features=audio_features,text_features=text_features,
|
| 181 |
-
local_loss=self.local_loss,gather_with_grad=self.gather_with_grad,
|
| 182 |
-
rank=self.rank,world_size=self.world_size,use_horovod=self.use_horovod,
|
| 183 |
-
mlp_loss=self.mlp_loss
|
| 184 |
-
)
|
| 185 |
-
|
| 186 |
-
if self.local_loss:
|
| 187 |
-
logits_per_audio = logit_scale_a * audio_features @ all_text_features.T
|
| 188 |
-
logits_per_text = logit_scale_a * text_features @ all_audio_features.T
|
| 189 |
-
else:
|
| 190 |
-
logits_per_audio = logit_scale_a * all_audio_features @ all_text_features.T
|
| 191 |
-
logits_per_text = logits_per_audio.T
|
| 192 |
-
else:
|
| 193 |
-
logits_per_audio = logit_scale_a * audio_features @ text_features.T
|
| 194 |
-
logits_per_text = logit_scale_a * text_features @ audio_features.T
|
| 195 |
-
|
| 196 |
-
# calculated ground-truth and cache if enabled
|
| 197 |
-
num_logits = logits_per_audio.shape[0]
|
| 198 |
-
if self.prev_num_logits != num_logits or device not in self.labels:
|
| 199 |
-
labels = torch.arange(num_logits, device=device, dtype=torch.long)
|
| 200 |
-
if self.world_size > 1 and self.local_loss:
|
| 201 |
-
labels = labels + num_logits * self.rank
|
| 202 |
-
if self.cache_labels:
|
| 203 |
-
self.labels[device] = labels
|
| 204 |
-
self.prev_num_logits = num_logits
|
| 205 |
-
else:
|
| 206 |
-
labels = self.labels[device]
|
| 207 |
-
if not self.weighted_loss:
|
| 208 |
-
total_loss = (
|
| 209 |
-
F.cross_entropy(logits_per_audio, labels) +
|
| 210 |
-
F.cross_entropy(logits_per_text, labels)
|
| 211 |
-
) / 2
|
| 212 |
-
else:
|
| 213 |
-
audio_weight = (all_audio_features@all_audio_features.T).detach()
|
| 214 |
-
audio_weight = (torch.exp(torch.sum(audio_weight, axis=1)/(self.weight_loss_kappa*len(all_audio_features)))).detach()
|
| 215 |
-
text_weight = (all_text_features@all_text_features.T).detach()
|
| 216 |
-
text_weight = (torch.exp(torch.sum(text_weight, axis=1)/(self.weight_loss_kappa*len(all_text_features)))).detach()
|
| 217 |
-
total_loss = (
|
| 218 |
-
F.cross_entropy(logits_per_audio, labels, weight=text_weight) +
|
| 219 |
-
F.cross_entropy(logits_per_text, labels, weight=audio_weight)
|
| 220 |
-
) / 2
|
| 221 |
-
return total_loss
|
| 222 |
-
|
| 223 |
-
def lp_gather_features(
|
| 224 |
-
pred,
|
| 225 |
-
target,
|
| 226 |
-
world_size=1,
|
| 227 |
-
use_horovod=False
|
| 228 |
-
):
|
| 229 |
-
if use_horovod:
|
| 230 |
-
assert hvd is not None, 'Please install horovod'
|
| 231 |
-
with torch.no_grad():
|
| 232 |
-
all_preds = hvd.allgather(pred)
|
| 233 |
-
all_targets = hvd.allgath(target)
|
| 234 |
-
else:
|
| 235 |
-
gathered_preds = [torch.zeros_like(pred) for _ in range(world_size)]
|
| 236 |
-
gathered_targets = [torch.zeros_like(target) for _ in range(world_size)]
|
| 237 |
-
|
| 238 |
-
dist.all_gather(gathered_preds, pred)
|
| 239 |
-
dist.all_gather(gathered_targets, target)
|
| 240 |
-
all_preds = torch.cat(gathered_preds, dim=0)
|
| 241 |
-
all_targets = torch.cat(gathered_targets, dim=0)
|
| 242 |
-
|
| 243 |
-
return all_preds, all_targets
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
def get_map(pred, target):
|
| 247 |
-
pred = torch.sigmoid(pred).numpy()
|
| 248 |
-
target = target.numpy()
|
| 249 |
-
return np.mean(average_precision_score(target, pred, average=None))
|
| 250 |
-
|
| 251 |
-
def get_acc(pred, target):
|
| 252 |
-
pred = torch.argmax(pred,1).numpy()
|
| 253 |
-
target = torch.argmax(target,1).numpy()
|
| 254 |
-
return accuracy_score(target, pred)
|
| 255 |
-
|
| 256 |
-
def get_mauc(pred, target):
|
| 257 |
-
pred = torch.sigmoid(pred).numpy()
|
| 258 |
-
target = target.numpy()
|
| 259 |
-
return np.mean(roc_auc_score(target, pred, average=None))
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
class LPMetrics(object):
|
| 263 |
-
def __init__(self, metric_names = ['map','acc','mauc']):
|
| 264 |
-
self.metrics = []
|
| 265 |
-
for name in metric_names:
|
| 266 |
-
self.metrics.append(self.get_metric(name))
|
| 267 |
-
self.metric_names = metric_names
|
| 268 |
-
|
| 269 |
-
def get_metric(self,name):
|
| 270 |
-
if name == 'map':
|
| 271 |
-
return get_map
|
| 272 |
-
elif name == 'acc':
|
| 273 |
-
return get_acc
|
| 274 |
-
elif name == 'mauc':
|
| 275 |
-
return get_mauc
|
| 276 |
-
else:
|
| 277 |
-
raise ValueError(f'the metric should be at least one of [map, acc, mauc]')
|
| 278 |
-
|
| 279 |
-
def evaluate_mertics(self, pred, target):
|
| 280 |
-
metric_dict = {}
|
| 281 |
-
for i in range(len(self.metric_names)):
|
| 282 |
-
metric_dict[self.metric_names[i]] = self.metrics[i](pred, target)
|
| 283 |
-
return metric_dict
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
def calc_celoss(pred, target):
|
| 287 |
-
target = torch.argmax(target, 1).long()
|
| 288 |
-
return nn.CrossEntropyLoss()(pred, target)
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
class LPLoss(nn.Module):
|
| 292 |
-
|
| 293 |
-
def __init__(self, loss_name):
|
| 294 |
-
super().__init__()
|
| 295 |
-
if loss_name == 'bce':
|
| 296 |
-
self.loss_func = nn.BCEWithLogitsLoss()
|
| 297 |
-
elif loss_name == 'ce':
|
| 298 |
-
self.loss_func = calc_celoss
|
| 299 |
-
elif loss_name == 'mse':
|
| 300 |
-
self.loss_func = nn.MSELoss()
|
| 301 |
-
else:
|
| 302 |
-
raise ValueError(f'the loss func should be at least one of [bce, ce, mse]')
|
| 303 |
-
|
| 304 |
-
def forward(self, pred, target):
|
| 305 |
-
loss = self.loss_func(pred, target)
|
| 306 |
-
return loss
|
| 307 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/clap_module/tokenizer.py
DELETED
|
@@ -1,180 +0,0 @@
|
|
| 1 |
-
""" CLIP tokenizer
|
| 2 |
-
|
| 3 |
-
Copied from https://github.com/openai/CLIP. Originally MIT License, Copyright (c) 2021 OpenAI.
|
| 4 |
-
"""
|
| 5 |
-
import gzip
|
| 6 |
-
import html
|
| 7 |
-
import os
|
| 8 |
-
from functools import lru_cache
|
| 9 |
-
from typing import Union, List
|
| 10 |
-
|
| 11 |
-
import ftfy
|
| 12 |
-
import regex as re
|
| 13 |
-
import torch
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
@lru_cache()
|
| 17 |
-
def default_bpe():
|
| 18 |
-
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "bpe_simple_vocab_16e6.txt.gz")
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
@lru_cache()
|
| 22 |
-
def bytes_to_unicode():
|
| 23 |
-
"""
|
| 24 |
-
Returns list of utf-8 byte and a corresponding list of unicode strings.
|
| 25 |
-
The reversible bpe codes work on unicode strings.
|
| 26 |
-
This means you need a large # of unicode characters in your vocab if you want to avoid UNKs.
|
| 27 |
-
When you're at something like a 10B token dataset you end up needing around 5K for decent coverage.
|
| 28 |
-
This is a signficant percentage of your normal, say, 32K bpe vocab.
|
| 29 |
-
To avoid that, we want lookup tables between utf-8 bytes and unicode strings.
|
| 30 |
-
And avoids mapping to whitespace/control characters the bpe code barfs on.
|
| 31 |
-
"""
|
| 32 |
-
bs = list(range(ord("!"), ord("~")+1))+list(range(ord("¡"), ord("¬")+1))+list(range(ord("®"), ord("ÿ")+1))
|
| 33 |
-
cs = bs[:]
|
| 34 |
-
n = 0
|
| 35 |
-
for b in range(2**8):
|
| 36 |
-
if b not in bs:
|
| 37 |
-
bs.append(b)
|
| 38 |
-
cs.append(2**8+n)
|
| 39 |
-
n += 1
|
| 40 |
-
cs = [chr(n) for n in cs]
|
| 41 |
-
return dict(zip(bs, cs))
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
def get_pairs(word):
|
| 45 |
-
"""Return set of symbol pairs in a word.
|
| 46 |
-
Word is represented as tuple of symbols (symbols being variable-length strings).
|
| 47 |
-
"""
|
| 48 |
-
pairs = set()
|
| 49 |
-
prev_char = word[0]
|
| 50 |
-
for char in word[1:]:
|
| 51 |
-
pairs.add((prev_char, char))
|
| 52 |
-
prev_char = char
|
| 53 |
-
return pairs
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
def basic_clean(text):
|
| 57 |
-
text = ftfy.fix_text(text)
|
| 58 |
-
text = html.unescape(html.unescape(text))
|
| 59 |
-
return text.strip()
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
def whitespace_clean(text):
|
| 63 |
-
text = re.sub(r'\s+', ' ', text)
|
| 64 |
-
text = text.strip()
|
| 65 |
-
return text
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
class SimpleTokenizer(object):
|
| 69 |
-
def __init__(self, bpe_path: str = default_bpe(), special_tokens=None):
|
| 70 |
-
self.byte_encoder = bytes_to_unicode()
|
| 71 |
-
self.byte_decoder = {v: k for k, v in self.byte_encoder.items()}
|
| 72 |
-
merges = gzip.open(bpe_path).read().decode("utf-8").split('\n')
|
| 73 |
-
merges = merges[1:49152-256-2+1]
|
| 74 |
-
merges = [tuple(merge.split()) for merge in merges]
|
| 75 |
-
vocab = list(bytes_to_unicode().values())
|
| 76 |
-
vocab = vocab + [v+'</w>' for v in vocab]
|
| 77 |
-
for merge in merges:
|
| 78 |
-
vocab.append(''.join(merge))
|
| 79 |
-
if not special_tokens:
|
| 80 |
-
special_tokens = ['<start_of_text>', '<end_of_text>']
|
| 81 |
-
else:
|
| 82 |
-
special_tokens = ['<start_of_text>', '<end_of_text>'] + special_tokens
|
| 83 |
-
vocab.extend(special_tokens)
|
| 84 |
-
self.encoder = dict(zip(vocab, range(len(vocab))))
|
| 85 |
-
self.decoder = {v: k for k, v in self.encoder.items()}
|
| 86 |
-
self.bpe_ranks = dict(zip(merges, range(len(merges))))
|
| 87 |
-
self.cache = {t:t for t in special_tokens}
|
| 88 |
-
special = "|".join(special_tokens)
|
| 89 |
-
self.pat = re.compile(special + r"""|'s|'t|'re|'ve|'m|'ll|'d|[\p{L}]+|[\p{N}]|[^\s\p{L}\p{N}]+""", re.IGNORECASE)
|
| 90 |
-
|
| 91 |
-
self.vocab_size = len(self.encoder)
|
| 92 |
-
self.all_special_ids = [self.encoder[t] for t in special_tokens]
|
| 93 |
-
|
| 94 |
-
def bpe(self, token):
|
| 95 |
-
if token in self.cache:
|
| 96 |
-
return self.cache[token]
|
| 97 |
-
word = tuple(token[:-1]) + ( token[-1] + '</w>',)
|
| 98 |
-
pairs = get_pairs(word)
|
| 99 |
-
|
| 100 |
-
if not pairs:
|
| 101 |
-
return token+'</w>'
|
| 102 |
-
|
| 103 |
-
while True:
|
| 104 |
-
bigram = min(pairs, key = lambda pair: self.bpe_ranks.get(pair, float('inf')))
|
| 105 |
-
if bigram not in self.bpe_ranks:
|
| 106 |
-
break
|
| 107 |
-
first, second = bigram
|
| 108 |
-
new_word = []
|
| 109 |
-
i = 0
|
| 110 |
-
while i < len(word):
|
| 111 |
-
try:
|
| 112 |
-
j = word.index(first, i)
|
| 113 |
-
new_word.extend(word[i:j])
|
| 114 |
-
i = j
|
| 115 |
-
except:
|
| 116 |
-
new_word.extend(word[i:])
|
| 117 |
-
break
|
| 118 |
-
|
| 119 |
-
if word[i] == first and i < len(word)-1 and word[i+1] == second:
|
| 120 |
-
new_word.append(first+second)
|
| 121 |
-
i += 2
|
| 122 |
-
else:
|
| 123 |
-
new_word.append(word[i])
|
| 124 |
-
i += 1
|
| 125 |
-
new_word = tuple(new_word)
|
| 126 |
-
word = new_word
|
| 127 |
-
if len(word) == 1:
|
| 128 |
-
break
|
| 129 |
-
else:
|
| 130 |
-
pairs = get_pairs(word)
|
| 131 |
-
word = ' '.join(word)
|
| 132 |
-
self.cache[token] = word
|
| 133 |
-
return word
|
| 134 |
-
|
| 135 |
-
def encode(self, text):
|
| 136 |
-
bpe_tokens = []
|
| 137 |
-
text = whitespace_clean(basic_clean(text)).lower()
|
| 138 |
-
for token in re.findall(self.pat, text):
|
| 139 |
-
token = ''.join(self.byte_encoder[b] for b in token.encode('utf-8'))
|
| 140 |
-
bpe_tokens.extend(self.encoder[bpe_token] for bpe_token in self.bpe(token).split(' '))
|
| 141 |
-
return bpe_tokens
|
| 142 |
-
|
| 143 |
-
def decode(self, tokens):
|
| 144 |
-
text = ''.join([self.decoder[token] for token in tokens])
|
| 145 |
-
text = bytearray([self.byte_decoder[c] for c in text]).decode('utf-8', errors="replace").replace('</w>', ' ')
|
| 146 |
-
return text
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
_tokenizer = SimpleTokenizer()
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
def tokenize(texts: Union[str, List[str]], context_length: int = 77) -> torch.LongTensor:
|
| 153 |
-
"""
|
| 154 |
-
Returns the tokenized representation of given input string(s)
|
| 155 |
-
|
| 156 |
-
Parameters
|
| 157 |
-
----------
|
| 158 |
-
texts : Union[str, List[str]]
|
| 159 |
-
An input string or a list of input strings to tokenize
|
| 160 |
-
context_length : int
|
| 161 |
-
The context length to use; all CLIP models use 77 as the context length
|
| 162 |
-
|
| 163 |
-
Returns
|
| 164 |
-
-------
|
| 165 |
-
A two-dimensional tensor containing the resulting tokens, shape = [number of input strings, context_length]
|
| 166 |
-
"""
|
| 167 |
-
if isinstance(texts, str):
|
| 168 |
-
texts = [texts]
|
| 169 |
-
|
| 170 |
-
sot_token = _tokenizer.encoder["<start_of_text>"]
|
| 171 |
-
eot_token = _tokenizer.encoder["<end_of_text>"]
|
| 172 |
-
all_tokens = [[sot_token] + _tokenizer.encode(text) + [eot_token] for text in texts]
|
| 173 |
-
result = torch.zeros(len(all_tokens), context_length, dtype=torch.long)
|
| 174 |
-
|
| 175 |
-
for i, tokens in enumerate(all_tokens):
|
| 176 |
-
if len(tokens) > context_length:
|
| 177 |
-
tokens = tokens[:context_length] # Truncate
|
| 178 |
-
result[i, :len(tokens)] = torch.tensor(tokens)
|
| 179 |
-
|
| 180 |
-
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/clap_module/utils.py
CHANGED
|
@@ -1,62 +1,6 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
import torch
|
| 3 |
from torch import nn as nn
|
| 4 |
from torchvision.ops.misc import FrozenBatchNorm2d
|
| 5 |
-
import logging
|
| 6 |
-
import h5py
|
| 7 |
-
from tqdm import tqdm
|
| 8 |
-
import random
|
| 9 |
-
import json
|
| 10 |
-
import os
|
| 11 |
-
import pathlib
|
| 12 |
-
|
| 13 |
-
# TODO: (yusong) this not a good place to store those information and does not scale. Need to be fixed later.
|
| 14 |
-
dataset_split = {
|
| 15 |
-
"audiocaps": ["train", "valid", "test"],
|
| 16 |
-
"audioset": ["balanced_train", "unbalanced_train", "eval"],
|
| 17 |
-
"BBCSoundEffects": ["train", "test"],
|
| 18 |
-
"Clotho": ["train", "test", "valid"],
|
| 19 |
-
"free_to_use_sounds": ["train", "test"],
|
| 20 |
-
"paramount_motion": ["train", "test"],
|
| 21 |
-
"sonniss_game_effects": ["train", "test"],
|
| 22 |
-
"wesoundeffects": ["train", "test"],
|
| 23 |
-
"MACS": ["train", "test"],
|
| 24 |
-
"freesound": ["train", "test"],
|
| 25 |
-
"FSD50K": ["train", "test", "valid"],
|
| 26 |
-
"fsd50k_class_label": ["train", "test", "valid"],
|
| 27 |
-
"esc50": ["train", "test"],
|
| 28 |
-
"ESC50_1": ["train", "test"],
|
| 29 |
-
"ESC50_2": ["train", "test"],
|
| 30 |
-
"ESC50_3": ["train", "test"],
|
| 31 |
-
"ESC50_4": ["train", "test"],
|
| 32 |
-
"ESC50_5": ["train", "test"],
|
| 33 |
-
"audiostock": ["train", "test"],
|
| 34 |
-
"freesound_no_overlap_noesc50": ["train", "test"],
|
| 35 |
-
"epidemic_sound_effects": ["train", "test"],
|
| 36 |
-
"VGGSound": ["train", "test"],
|
| 37 |
-
"urbansound8k_class_label": ["train", "test"],
|
| 38 |
-
"audioset_t5": ["balanced_train", "unbalanced_train", "eval"],
|
| 39 |
-
"audioset_t5_debiased": ["balanced_train", "unbalanced_train", "eval"],
|
| 40 |
-
"epidemic_sound_effects_t5": ["train", "test"],
|
| 41 |
-
"epidemic_sound_effects_t5_debiased": ["train", "test"],
|
| 42 |
-
"WavText5K": ["train", "test"],
|
| 43 |
-
"esc50_no_overlap": ["train", "test"],
|
| 44 |
-
"usd8k_no_overlap": ["train", "test"],
|
| 45 |
-
"fsd50k_200_class_label": ["train", "test", "valid"],
|
| 46 |
-
"fma_full": ["train", "test"],
|
| 47 |
-
"Genius": ["train", "test"],
|
| 48 |
-
"Jamendo": ["train", "test"],
|
| 49 |
-
"juno": ["train", "test"],
|
| 50 |
-
"CMU_Arctic": ["train", "test"],
|
| 51 |
-
"ravdess": ["train", "test"],
|
| 52 |
-
"Europarl-st": ["train", "test"],
|
| 53 |
-
"common_voice": ["train", "test"],
|
| 54 |
-
"Jamendo_16bit": ["train", "test"],
|
| 55 |
-
"genius_16bit_128": ["train", "test"],
|
| 56 |
-
"juno_16bit": ["train", "test"],
|
| 57 |
-
"fma_full_16bit_128": ["train", "test"],
|
| 58 |
-
"GTZAN": ["train", "test"],
|
| 59 |
-
}
|
| 60 |
|
| 61 |
|
| 62 |
def freeze_batch_norm_2d(module, module_match={}, name=""):
|
|
@@ -100,99 +44,6 @@ def freeze_batch_norm_2d(module, module_match={}, name=""):
|
|
| 100 |
return res
|
| 101 |
|
| 102 |
|
| 103 |
-
def exist(dataset_name, dataset_type):
|
| 104 |
-
"""
|
| 105 |
-
Check if dataset exists
|
| 106 |
-
"""
|
| 107 |
-
if dataset_type in dataset_split[dataset_name]:
|
| 108 |
-
return True
|
| 109 |
-
else:
|
| 110 |
-
return False
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
def get_tar_path_from_dataset_name(
|
| 114 |
-
dataset_names,
|
| 115 |
-
dataset_types,
|
| 116 |
-
islocal,
|
| 117 |
-
dataset_path,
|
| 118 |
-
proportion=1,
|
| 119 |
-
full_dataset=None
|
| 120 |
-
):
|
| 121 |
-
"""
|
| 122 |
-
Get tar path from dataset name and type
|
| 123 |
-
"""
|
| 124 |
-
output = []
|
| 125 |
-
for n in dataset_names:
|
| 126 |
-
if full_dataset is not None and n in full_dataset:
|
| 127 |
-
current_dataset_types = dataset_split[n]
|
| 128 |
-
else:
|
| 129 |
-
current_dataset_types = dataset_types
|
| 130 |
-
for s in current_dataset_types:
|
| 131 |
-
tmp = []
|
| 132 |
-
if islocal:
|
| 133 |
-
sizefilepath_ = f"{dataset_path}/{n}/{s}/sizes.json"
|
| 134 |
-
if not os.path.exists(sizefilepath_):
|
| 135 |
-
sizefilepath_ = f"./json_files/{n}/{s}/sizes.json"
|
| 136 |
-
else:
|
| 137 |
-
sizefilepath_ = f"./json_files/{n}/{s}/sizes.json"
|
| 138 |
-
if not os.path.exists(sizefilepath_):
|
| 139 |
-
continue
|
| 140 |
-
sizes = json.load(open(sizefilepath_, "r"))
|
| 141 |
-
for k in sizes.keys():
|
| 142 |
-
if islocal:
|
| 143 |
-
tmp.append(f"{dataset_path}/{n}/{s}/{k}")
|
| 144 |
-
else:
|
| 145 |
-
tmp.append(
|
| 146 |
-
f"pipe:aws s3 --cli-connect-timeout 0 cp s3://s-laion-audio/webdataset_tar/{n}/{s}/{k} -"
|
| 147 |
-
)
|
| 148 |
-
if proportion != 1:
|
| 149 |
-
tmp = random.sample(tmp, int(proportion * len(tmp)))
|
| 150 |
-
output.append(tmp)
|
| 151 |
-
return sum(output, [])
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
def get_tar_path_from_txts(txt_path, islocal, proportion=1):
|
| 155 |
-
"""
|
| 156 |
-
Get tar path from txt path
|
| 157 |
-
"""
|
| 158 |
-
if isinstance(txt_path, (list, tuple)):
|
| 159 |
-
return sum(
|
| 160 |
-
[
|
| 161 |
-
get_tar_path_from_txts(
|
| 162 |
-
txt_path[i], islocal=islocal, proportion=proportion
|
| 163 |
-
)
|
| 164 |
-
for i in range(len(txt_path))
|
| 165 |
-
],
|
| 166 |
-
[],
|
| 167 |
-
)
|
| 168 |
-
if isinstance(txt_path, str):
|
| 169 |
-
with open(txt_path) as f:
|
| 170 |
-
lines = f.readlines()
|
| 171 |
-
if islocal:
|
| 172 |
-
lines = [
|
| 173 |
-
lines[i]
|
| 174 |
-
.split("\n")[0]
|
| 175 |
-
.replace("pipe:aws s3 cp s3://s-laion-audio/", "/mnt/audio_clip/")
|
| 176 |
-
for i in range(len(lines))
|
| 177 |
-
]
|
| 178 |
-
else:
|
| 179 |
-
lines = [
|
| 180 |
-
lines[i].split("\n")[0].replace(".tar", ".tar -")
|
| 181 |
-
for i in range(len(lines))
|
| 182 |
-
]
|
| 183 |
-
if proportion != 1:
|
| 184 |
-
print("Sampling tars with proportion of {}".format(proportion))
|
| 185 |
-
lines = random.sample(lines, int(proportion * len(lines)))
|
| 186 |
-
return lines
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
def get_mix_lambda(mixup_alpha, batch_size):
|
| 190 |
-
mixup_lambdas = [
|
| 191 |
-
np.random.beta(mixup_alpha, mixup_alpha, 1)[0] for _ in range(batch_size)
|
| 192 |
-
]
|
| 193 |
-
return np.array(mixup_lambdas).astype(np.float32)
|
| 194 |
-
|
| 195 |
-
|
| 196 |
def do_mixup(x, mixup_lambda):
|
| 197 |
"""
|
| 198 |
Args:
|
|
@@ -240,150 +91,3 @@ def pad_framewise_output(framewise_output, frames_num):
|
|
| 240 |
|
| 241 |
output = torch.cat((framewise_output, pad), dim=1)
|
| 242 |
"""(batch_size, frames_num, classes_num)"""
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
def process_ipc(index_path, classes_num, filename):
|
| 246 |
-
# load data
|
| 247 |
-
logging.info("Load Data...............")
|
| 248 |
-
ipc = [[] for _ in range(classes_num)]
|
| 249 |
-
with h5py.File(index_path, "r") as f:
|
| 250 |
-
for i in tqdm(range(len(f["target"]))):
|
| 251 |
-
t_class = np.where(f["target"][i])[0]
|
| 252 |
-
for t in t_class:
|
| 253 |
-
ipc[t].append(i)
|
| 254 |
-
print(ipc)
|
| 255 |
-
np.save(filename, ipc)
|
| 256 |
-
logging.info("Load Data Succeed...............")
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
def save_to_dict(s, o_={}):
|
| 260 |
-
sp = s.split(": ")
|
| 261 |
-
o_.update({sp[0]: float(sp[1])})
|
| 262 |
-
return o_
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
def get_data_from_log(txt_path):
|
| 266 |
-
"""
|
| 267 |
-
Output dictionary from out.txt log file
|
| 268 |
-
"""
|
| 269 |
-
with open(txt_path) as f:
|
| 270 |
-
lines = f.readlines()
|
| 271 |
-
val_data = {}
|
| 272 |
-
train_data = {}
|
| 273 |
-
train_losses = []
|
| 274 |
-
train_losses_epoch = []
|
| 275 |
-
for i in range(len(lines)):
|
| 276 |
-
if "| INFO |" in lines[i]:
|
| 277 |
-
if "Eval Epoch" in lines[i]:
|
| 278 |
-
if "val_loss" in lines[i]:
|
| 279 |
-
# float(regex.sub("", lines[310].split(" ")[-1]).replace(" ", ""))
|
| 280 |
-
line = lines[i].split("Eval Epoch: ")[-1]
|
| 281 |
-
num_epoch = int(line.split(" ")[0].split(" ")[0])
|
| 282 |
-
d = {
|
| 283 |
-
line.split(" ")[0]
|
| 284 |
-
.split(" ")[1]
|
| 285 |
-
.replace(":", ""): float(line.split(" ")[0].split(" ")[-1])
|
| 286 |
-
}
|
| 287 |
-
for i in range(1, len(line.split(" "))):
|
| 288 |
-
d = save_to_dict(line.split(" ")[i], d)
|
| 289 |
-
val_data[num_epoch] = d
|
| 290 |
-
elif "Train Epoch" in lines[i]:
|
| 291 |
-
num_epoch = int(lines[i].split("Train Epoch: ")[1][0])
|
| 292 |
-
loss = float(lines[i].split("Loss: ")[-1].split(" (")[0])
|
| 293 |
-
train_losses.append(loss)
|
| 294 |
-
train_losses_epoch.append(num_epoch)
|
| 295 |
-
for i in range(len(train_losses)):
|
| 296 |
-
train_data[i] = {
|
| 297 |
-
"num_epoch": train_losses_epoch[i],
|
| 298 |
-
"train_loss": train_losses[i],
|
| 299 |
-
}
|
| 300 |
-
return train_data, val_data
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
def save_p(obj, filename):
|
| 304 |
-
import pickle
|
| 305 |
-
|
| 306 |
-
try:
|
| 307 |
-
from deepdiff import DeepDiff
|
| 308 |
-
except:
|
| 309 |
-
os.system("pip install deepdiff")
|
| 310 |
-
from deepdiff import DeepDiff
|
| 311 |
-
with open(filename, "wb") as file:
|
| 312 |
-
pickle.dump(obj, file, protocol=pickle.HIGHEST_PROTOCOL) # highest protocol
|
| 313 |
-
with open(filename, "rb") as file:
|
| 314 |
-
z = pickle.load(file)
|
| 315 |
-
assert (
|
| 316 |
-
DeepDiff(obj, z, ignore_string_case=True) == {}
|
| 317 |
-
), "there is something wrong with the saving process"
|
| 318 |
-
return
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
def load_p(filename):
|
| 322 |
-
import pickle
|
| 323 |
-
|
| 324 |
-
with open(filename, "rb") as file:
|
| 325 |
-
z = pickle.load(file)
|
| 326 |
-
return z
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
def save_json(data, name="data.json"):
|
| 330 |
-
import json
|
| 331 |
-
with open(name, 'w') as fp:
|
| 332 |
-
json.dump(data, fp)
|
| 333 |
-
return
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
def load_json(name):
|
| 337 |
-
import json
|
| 338 |
-
with open(name, 'r') as fp:
|
| 339 |
-
data = json.load(fp)
|
| 340 |
-
return data
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
from multiprocessing import Process, Manager
|
| 344 |
-
from multiprocessing import Process, Value, Array
|
| 345 |
-
from ctypes import c_wchar
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
def load_class_label(path):
|
| 349 |
-
# https://stackoverflow.com/questions/48004243/how-to-share-large-read-only-dictionary-list-across-processes-in-multiprocessing
|
| 350 |
-
# https://stackoverflow.com/questions/45693949/storing-strings-in-a-multiprocessing-sharedctypes-array
|
| 351 |
-
out = None
|
| 352 |
-
if path is not None:
|
| 353 |
-
if pathlib.Path(path).suffix in [".pkl", ".pickle"]:
|
| 354 |
-
out = load_p(path)
|
| 355 |
-
elif pathlib.Path(path).suffix in [".json", ".txt"]:
|
| 356 |
-
out = load_json(path)
|
| 357 |
-
elif pathlib.Path(path).suffix in [".npy", ".npz"]:
|
| 358 |
-
out = np.load(path)
|
| 359 |
-
elif pathlib.Path(path).suffix in [".csv"]:
|
| 360 |
-
import pandas as pd
|
| 361 |
-
out = pd.read_csv(path)
|
| 362 |
-
return out
|
| 363 |
-
# if out is None:
|
| 364 |
-
# return None
|
| 365 |
-
# else:
|
| 366 |
-
# key = Array(c_wchar, '\n'.join(list(out.keys())), lock=False)
|
| 367 |
-
# val = Array('i', out.values(), lock=False)
|
| 368 |
-
# return (key, val)
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
from torch import optim
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
def get_optimizer(params, lr, betas, eps, momentum, optimizer_name):
|
| 375 |
-
if optimizer_name.lower() == "adamw":
|
| 376 |
-
optimizer = optim.AdamW(
|
| 377 |
-
params, lr=lr, betas=betas, eps=eps
|
| 378 |
-
)
|
| 379 |
-
elif optimizer_name.lower() == "sgd":
|
| 380 |
-
optimizer = optim.SGD(
|
| 381 |
-
params, lr=lr, momentum=momentum
|
| 382 |
-
)
|
| 383 |
-
elif optimizer_name.lower() == "adam":
|
| 384 |
-
optimizer = optim.Adam(
|
| 385 |
-
params, lr=lr, betas=betas, eps=eps
|
| 386 |
-
)
|
| 387 |
-
else:
|
| 388 |
-
raise ValueError("optimizer name is not correct")
|
| 389 |
-
return optimizer
|
|
|
|
|
|
|
| 1 |
import torch
|
| 2 |
from torch import nn as nn
|
| 3 |
from torchvision.ops.misc import FrozenBatchNorm2d
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
|
| 6 |
def freeze_batch_norm_2d(module, module_match={}, name=""):
|
|
|
|
| 44 |
return res
|
| 45 |
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
def do_mixup(x, mixup_lambda):
|
| 48 |
"""
|
| 49 |
Args:
|
|
|
|
| 91 |
|
| 92 |
output = torch.cat((framewise_output, pad), dim=1)
|
| 93 |
"""(batch_size, frames_num, classes_num)"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/clap_module/version.py
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
__version__ = '0.2.1'
|
|
|
|
|
|
utils/laion_clap/evaluate/__init__.py
DELETED
|
File without changes
|
utils/laion_clap/evaluate/eval_dcase.py
DELETED
|
@@ -1,150 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import torch.nn.functional as F
|
| 3 |
-
import torch.backends.cudnn as cudnn
|
| 4 |
-
from open_clip import create_model
|
| 5 |
-
from open_clip import tokenize
|
| 6 |
-
import glob
|
| 7 |
-
import json
|
| 8 |
-
import librosa
|
| 9 |
-
from tqdm import tqdm
|
| 10 |
-
import numpy as np
|
| 11 |
-
import os
|
| 12 |
-
from laion_clap.training.params import parse_args
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
def get_output_from_single_audio(audio, text, model, device):
|
| 16 |
-
|
| 17 |
-
# audio_embedding = model.audio_infer(audio, hopsize=5 * 48000, key="embedding", device=device)['embedding']
|
| 18 |
-
# if audio_embedding.ndim > 1:
|
| 19 |
-
# audio_embedding = audio_embedding.mean(dim=0, keepdim=True)
|
| 20 |
-
# else:
|
| 21 |
-
# audio_embedding = audio_embedding.unsqueeze(0)
|
| 22 |
-
audio_features = model(audio, None, device)
|
| 23 |
-
audio_features = F.normalize(audio_features, dim=-1)
|
| 24 |
-
text_features = model(None, text, device=device)
|
| 25 |
-
text_features = F.normalize(text_features, dim=-1)
|
| 26 |
-
|
| 27 |
-
# CHANGE: before normalize or after
|
| 28 |
-
audio_features_mlp = model.audio_transform(audio_features)
|
| 29 |
-
text_features_mlp = model.text_transform(text_features)
|
| 30 |
-
return audio_features, text_features, audio_features_mlp, text_features_mlp, model.logit_scale_a.exp(), model.logit_scale_t.exp()
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
def get_metrics(text_to_audio_logits):
|
| 34 |
-
metrics = {}
|
| 35 |
-
|
| 36 |
-
# repeat ground truth 5 times because Clotho has 5 text for 1 audio
|
| 37 |
-
ground_truth = torch.repeat_interleave(torch.arange(len(text_features) // 5), 5).view(-1, 1)
|
| 38 |
-
|
| 39 |
-
ranking = torch.argsort(text_to_audio_logits, descending=True)
|
| 40 |
-
preds = torch.where(ranking == ground_truth)[1] # (yusong) this line is slow because it uses single thread
|
| 41 |
-
preds = preds.detach().cpu().numpy()
|
| 42 |
-
metrics[f"mean_rank"] = preds.mean() + 1
|
| 43 |
-
metrics[f"median_rank"] = np.floor(np.median(preds)) + 1
|
| 44 |
-
for k in [1, 5, 10]:
|
| 45 |
-
metrics[f"R@{k}"] = np.mean(preds < k)
|
| 46 |
-
# map@10
|
| 47 |
-
metrics[f"mAP@10"] = np.mean(np.where(preds < 10, 1 / (preds + 1), 0.0))
|
| 48 |
-
return metrics
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
if __name__ == '__main__':
|
| 52 |
-
args = parse_args()
|
| 53 |
-
|
| 54 |
-
model_path = args.pretrained
|
| 55 |
-
|
| 56 |
-
clotho_test_preprocessed_dir = "/fsx/yusong/clotho_test_set/test"
|
| 57 |
-
|
| 58 |
-
cudnn.benchmark = True
|
| 59 |
-
cudnn.deterministic = False
|
| 60 |
-
|
| 61 |
-
audio_features_ensemble_all = []
|
| 62 |
-
text_features_ensemble_all = []
|
| 63 |
-
audio_features_mlp_ensemble_all = []
|
| 64 |
-
text_features_mlp_ensemble_all = []
|
| 65 |
-
logit_scale_a_ensemble_all = []
|
| 66 |
-
logit_scale_t_ensemble_all = []
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
device = torch.device('cuda')
|
| 70 |
-
model, clap_model_cfg = create_model(
|
| 71 |
-
args.amodel,
|
| 72 |
-
args.tmodel,
|
| 73 |
-
args.pretrained,
|
| 74 |
-
precision=args.precision,
|
| 75 |
-
device=device,
|
| 76 |
-
jit=args.torchscript,
|
| 77 |
-
force_quick_gelu=args.force_quick_gelu,
|
| 78 |
-
openai_model_cache_dir=os.path.expanduser(args.openai_model_cache_dir),
|
| 79 |
-
skip_params=False
|
| 80 |
-
)
|
| 81 |
-
|
| 82 |
-
# load model
|
| 83 |
-
checkpoint = torch.load(model_path, map_location=device)
|
| 84 |
-
if "epoch" in checkpoint:
|
| 85 |
-
# resuming a train checkpoint w/ epoch and optimizer state
|
| 86 |
-
start_epoch = checkpoint["epoch"]
|
| 87 |
-
sd = checkpoint["state_dict"]
|
| 88 |
-
if next(iter(sd.items()))[0].startswith(
|
| 89 |
-
"module"
|
| 90 |
-
):
|
| 91 |
-
sd = {k[len("module."):]: v for k, v in sd.items()}
|
| 92 |
-
model.load_state_dict(sd)
|
| 93 |
-
else:
|
| 94 |
-
# loading a bare (model only) checkpoint for fine-tune or evaluation
|
| 95 |
-
model.load_state_dict(checkpoint)
|
| 96 |
-
|
| 97 |
-
model.to(device)
|
| 98 |
-
model.eval()
|
| 99 |
-
for param in model.parameters():
|
| 100 |
-
param.requires_grad = False
|
| 101 |
-
|
| 102 |
-
# take every 5th file because clotho has 5 texts for 1 audio
|
| 103 |
-
test_file_list = sorted(glob.glob(f"{clotho_test_preprocessed_dir}/*.flac"))
|
| 104 |
-
|
| 105 |
-
audio_features_all = []
|
| 106 |
-
text_features_all = []
|
| 107 |
-
audio_features_mlp_all = []
|
| 108 |
-
text_features_mlp_all = []
|
| 109 |
-
logit_scale_a_all = []
|
| 110 |
-
logit_scale_t_all = []
|
| 111 |
-
|
| 112 |
-
with torch.no_grad():
|
| 113 |
-
for file_path in tqdm(test_file_list):
|
| 114 |
-
json_path = file_path.replace(".flac", ".json")
|
| 115 |
-
with open(json_path, "r") as f:
|
| 116 |
-
json_data = json.load(f)
|
| 117 |
-
audio, sr = librosa.load(file_path, sr=48000, mono=True)
|
| 118 |
-
audio = torch.from_numpy(audio).to(device)
|
| 119 |
-
audio = {'waveform': audio.unsqueeze(0), 'sample_rate': sr}
|
| 120 |
-
text = json_data["text"]
|
| 121 |
-
|
| 122 |
-
if args.tmodel == "transformer":
|
| 123 |
-
from open_clip import tokenize
|
| 124 |
-
text = tokenize(text)
|
| 125 |
-
else:
|
| 126 |
-
from laion_clap.training.data import tokenizer
|
| 127 |
-
text = tokenizer(text, tmodel=args.tmodel) # 5 texts for each audio
|
| 128 |
-
|
| 129 |
-
audio_features, text_features, audio_features_mlp, text_features_mlp, logit_scale_a, logit_scale_t = \
|
| 130 |
-
get_output_from_single_audio(audio, text, model, device)
|
| 131 |
-
|
| 132 |
-
audio_features_all.append(audio_features.detach().cpu())
|
| 133 |
-
text_features_all.append(text_features.detach().cpu())
|
| 134 |
-
audio_features_mlp_all.append(audio_features_mlp.detach().cpu())
|
| 135 |
-
text_features_mlp_all.append(text_features_mlp.detach().cpu())
|
| 136 |
-
logit_scale_a_all.append(logit_scale_a.detach().cpu())
|
| 137 |
-
logit_scale_t_all.append(logit_scale_t.detach().cpu())
|
| 138 |
-
|
| 139 |
-
audio_features = torch.cat(audio_features_all)
|
| 140 |
-
text_features = torch.cat(text_features_all)
|
| 141 |
-
logit_scale_a = logit_scale_a_all[0]
|
| 142 |
-
|
| 143 |
-
logits_per_audio = (logit_scale_a * audio_features @ text_features.t()).detach().cpu()
|
| 144 |
-
logits_per_text = logits_per_audio.t().detach().cpu()
|
| 145 |
-
|
| 146 |
-
metrics = get_metrics(
|
| 147 |
-
logits_per_text
|
| 148 |
-
)
|
| 149 |
-
|
| 150 |
-
print(metrics)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/evaluate/eval_linear_probe.py
DELETED
|
@@ -1,515 +0,0 @@
|
|
| 1 |
-
'''
|
| 2 |
-
Evalute the linear probe performance on different checkpoints
|
| 3 |
-
'''
|
| 4 |
-
import logging
|
| 5 |
-
import os
|
| 6 |
-
import random
|
| 7 |
-
from datetime import datetime
|
| 8 |
-
import copy
|
| 9 |
-
import numpy as np
|
| 10 |
-
import torch
|
| 11 |
-
import torch.backends.cudnn as cudnn
|
| 12 |
-
from torch.cuda.amp import GradScaler
|
| 13 |
-
import glob
|
| 14 |
-
|
| 15 |
-
try:
|
| 16 |
-
import wandb
|
| 17 |
-
except ImportError:
|
| 18 |
-
wandb = None
|
| 19 |
-
|
| 20 |
-
try:
|
| 21 |
-
import torch.utils.tensorboard as tensorboard
|
| 22 |
-
except ImportError:
|
| 23 |
-
tensorboard = None
|
| 24 |
-
|
| 25 |
-
try:
|
| 26 |
-
import horovod.torch as hvd
|
| 27 |
-
except ImportError:
|
| 28 |
-
hvd = None
|
| 29 |
-
|
| 30 |
-
from clap_module import create_model_and_transforms, trace_model, create_model
|
| 31 |
-
from training.data import get_data
|
| 32 |
-
from training.params import parse_args
|
| 33 |
-
from training.distributed import is_master, init_distributed_device, world_info_from_env
|
| 34 |
-
from training.logger import setup_logging
|
| 35 |
-
from training.scheduler import cosine_lr
|
| 36 |
-
from training.lp_main import config_lp_optimizer
|
| 37 |
-
from training.lp_train import train_one_epoch, evaluate
|
| 38 |
-
from clap_module.utils import get_tar_path_from_dataset_name, dataset_split
|
| 39 |
-
from clap_module.utils import load_p, load_class_label
|
| 40 |
-
from clap_module.linear_probe import LinearProbe
|
| 41 |
-
|
| 42 |
-
def maintain_ckpts(args, startidx, all_idx_len):
|
| 43 |
-
for i in reversed(range(startidx, all_idx_len)):
|
| 44 |
-
if os.path.exists(os.path.join(args.checkpoint_path, f"epoch_top_{i}.pt")):
|
| 45 |
-
os.rename(
|
| 46 |
-
os.path.join(args.checkpoint_path, f"epoch_top_{i}.pt"),
|
| 47 |
-
os.path.join(args.checkpoint_path, f"epoch_top_{i+1}.pt"),
|
| 48 |
-
)
|
| 49 |
-
if os.path.exists(
|
| 50 |
-
os.path.join(args.checkpoint_path, f"epoch_top_{all_idx_len}.pt")
|
| 51 |
-
):
|
| 52 |
-
os.remove(os.path.join(args.checkpoint_path, f"epoch_top_{all_idx_len}.pt"))
|
| 53 |
-
return
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
def update_top_k_performance(
|
| 57 |
-
new_metrics_inputs, current_top_k_ckpt_metrics, args, ckpt, bignumbetter=True, pretrain_epoch=0
|
| 58 |
-
):
|
| 59 |
-
"""
|
| 60 |
-
Record the top-k performance of the current epoch.
|
| 61 |
-
current_top_k_metrics is a dictionary of the form: {1: top_1_ckpt_measure, 2: top_2_ckpt_measure, ...}
|
| 62 |
-
"""
|
| 63 |
-
if isinstance(new_metrics_inputs, (list, tuple)):
|
| 64 |
-
new_metrics_inputs = np.mean(new_metrics_inputs)
|
| 65 |
-
return update_top_k_performance(
|
| 66 |
-
new_metrics_inputs,
|
| 67 |
-
current_top_k_ckpt_metrics,
|
| 68 |
-
args=args,
|
| 69 |
-
ckpt=ckpt,
|
| 70 |
-
bignumbetter=bignumbetter,
|
| 71 |
-
pretrain_epoch=pretrain_epoch
|
| 72 |
-
)
|
| 73 |
-
elif isinstance(new_metrics_inputs, dict):
|
| 74 |
-
new_metrics_inputs = np.mean(list(new_metrics_inputs.values()))
|
| 75 |
-
return update_top_k_performance(
|
| 76 |
-
new_metrics_inputs,
|
| 77 |
-
current_top_k_ckpt_metrics,
|
| 78 |
-
args=args,
|
| 79 |
-
ckpt=ckpt,
|
| 80 |
-
bignumbetter=bignumbetter,
|
| 81 |
-
pretrain_epoch=pretrain_epoch
|
| 82 |
-
)
|
| 83 |
-
elif isinstance(new_metrics_inputs, (float, int)):
|
| 84 |
-
update_flag = {k: False for k in current_top_k_ckpt_metrics.keys()}
|
| 85 |
-
sorted_keys = sorted(current_top_k_ckpt_metrics.keys())
|
| 86 |
-
sorted_values = sorted(
|
| 87 |
-
current_top_k_ckpt_metrics.values(), reverse=bignumbetter
|
| 88 |
-
)
|
| 89 |
-
sorted_values_ = copy.deepcopy(sorted_values)
|
| 90 |
-
sorted_values.append(new_metrics_inputs)
|
| 91 |
-
sorted_values = sorted(sorted_values, reverse=bignumbetter)
|
| 92 |
-
sorted_values = sorted_values[:-1]
|
| 93 |
-
|
| 94 |
-
if sorted_values == sorted_values_:
|
| 95 |
-
return current_top_k_ckpt_metrics, new_metrics_inputs
|
| 96 |
-
else:
|
| 97 |
-
for i in range(len(sorted_keys)):
|
| 98 |
-
if current_top_k_ckpt_metrics[sorted_keys[i]] != sorted_values[i]:
|
| 99 |
-
current_top_k_ckpt_metrics[sorted_keys[i]] = sorted_values[i]
|
| 100 |
-
update_flag[sorted_keys[i]] = True
|
| 101 |
-
for i in range(len(update_flag)):
|
| 102 |
-
if update_flag[i]:
|
| 103 |
-
maintain_ckpts(args, i, len(sorted_keys))
|
| 104 |
-
torch.save(
|
| 105 |
-
ckpt,
|
| 106 |
-
os.path.join(args.checkpoint_path, f"pretrain_epoch_{pretrain_epoch}_lp_epoch_top_{i}.pt"),
|
| 107 |
-
)
|
| 108 |
-
break
|
| 109 |
-
return current_top_k_ckpt_metrics, new_metrics_inputs
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
# def updateifNone(a, b):
|
| 113 |
-
# a = b if None else a
|
| 114 |
-
# return a
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
def is_pretrained_params(n):
|
| 118 |
-
return (
|
| 119 |
-
n.startswith("clap_model.transformer")
|
| 120 |
-
or n in ["clap_model.positional_embedding", "clap_model.text_projection"]
|
| 121 |
-
or n.startswith("clap_model.token_embedding")
|
| 122 |
-
or n.startswith("clap_model.ln_final")
|
| 123 |
-
or n.startswith("clap_model.logit_scale_t")
|
| 124 |
-
)
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
def random_seed(seed=42, rank=0):
|
| 128 |
-
torch.manual_seed(seed + rank)
|
| 129 |
-
np.random.seed(seed + rank)
|
| 130 |
-
random.seed(seed + rank)
|
| 131 |
-
|
| 132 |
-
def main():
|
| 133 |
-
args = parse_args()
|
| 134 |
-
# sanitize model name for filesystem / uri use, easier if we don't use / in name as a rule?
|
| 135 |
-
args.amodel = args.amodel.replace("/", "-")
|
| 136 |
-
|
| 137 |
-
pretrained_ckpts = sorted(glob.glob(os.path.join(args.pretrained, "*.pt")), key=os.path.getmtime)
|
| 138 |
-
|
| 139 |
-
if args.name is None:
|
| 140 |
-
args.name = "-".join(
|
| 141 |
-
[
|
| 142 |
-
datetime.now().strftime("%Y_%m_%d-%H_%M_%S"),
|
| 143 |
-
f"linear_probe"
|
| 144 |
-
f"model_{args.amodel}",
|
| 145 |
-
f"lr_{args.lr}",
|
| 146 |
-
f"b_{args.batch_size}",
|
| 147 |
-
f"j_{args.workers}",
|
| 148 |
-
f"p_{args.precision}",
|
| 149 |
-
]
|
| 150 |
-
)
|
| 151 |
-
|
| 152 |
-
# discover initial world args early so we can log properly
|
| 153 |
-
args.distributed = False
|
| 154 |
-
args.local_rank, args.rank, args.world_size = world_info_from_env()
|
| 155 |
-
|
| 156 |
-
if args.remotedata and is_master(args):
|
| 157 |
-
for dataset_name in args.datasetnames:
|
| 158 |
-
for split in dataset_split[dataset_name]:
|
| 159 |
-
if not os.path.exists(f"./json_files/{dataset_name}/{split}"):
|
| 160 |
-
os.makedirs(f"./json_files/{dataset_name}/{split}")
|
| 161 |
-
os.system(
|
| 162 |
-
f"aws s3 cp s3://s-laion-audio/webdataset_tar/{dataset_name}/{split}/sizes.json ./json_files/{dataset_name}/{split}/sizes.json"
|
| 163 |
-
)
|
| 164 |
-
args.log_path = None
|
| 165 |
-
if is_master(args, local=args.log_local):
|
| 166 |
-
log_base_path = os.path.join(args.logs, args.name)
|
| 167 |
-
os.makedirs(log_base_path, exist_ok=True)
|
| 168 |
-
log_filename = f"out-{args.rank}" if args.log_local else "out.log"
|
| 169 |
-
args.log_path = os.path.join(log_base_path, log_filename)
|
| 170 |
-
|
| 171 |
-
# avoid log dir in same name:
|
| 172 |
-
postfix = 0
|
| 173 |
-
while os.path.exists(args.log_path):
|
| 174 |
-
postfix += 1
|
| 175 |
-
log_base_path_new = log_base_path+'-'+str(postfix)
|
| 176 |
-
os.makedirs(log_base_path_new, exist_ok=True)
|
| 177 |
-
log_filename = f"out-{args.rank}" if args.log_local else "out.log"
|
| 178 |
-
args.log_path = os.path.join(log_base_path_new, log_filename)
|
| 179 |
-
# print(
|
| 180 |
-
# "Error. Experiment already exists. Use --name {} to specify a new experiment."
|
| 181 |
-
# )
|
| 182 |
-
# return -1
|
| 183 |
-
|
| 184 |
-
# Set logger
|
| 185 |
-
args.log_level = logging.DEBUG if args.debug else logging.INFO
|
| 186 |
-
setup_logging(args.log_path, args.log_level)
|
| 187 |
-
|
| 188 |
-
# fully initialize distributed device environment
|
| 189 |
-
device = init_distributed_device(args)
|
| 190 |
-
|
| 191 |
-
args.wandb = "wandb" in args.report_to or "all" in args.report_to
|
| 192 |
-
args.tensorboard = "tensorboard" in args.report_to or "all" in args.report_to
|
| 193 |
-
if is_master(args):
|
| 194 |
-
args.tensorboard_path = (
|
| 195 |
-
os.path.join(args.logs, args.name, "tensorboard")
|
| 196 |
-
if args.tensorboard
|
| 197 |
-
else ""
|
| 198 |
-
)
|
| 199 |
-
args.checkpoint_path = os.path.join(args.logs, args.name, "checkpoints")
|
| 200 |
-
for dirname in [args.tensorboard_path, args.checkpoint_path]:
|
| 201 |
-
if dirname:
|
| 202 |
-
os.makedirs(dirname, exist_ok=True)
|
| 203 |
-
else:
|
| 204 |
-
args.tensorboard_path = ""
|
| 205 |
-
args.checkpoint_path = ""
|
| 206 |
-
|
| 207 |
-
if args.copy_codebase:
|
| 208 |
-
copy_codebase(args)
|
| 209 |
-
|
| 210 |
-
assert args.precision in ["amp", "fp16", "fp32"]
|
| 211 |
-
if args.precision == "fp16":
|
| 212 |
-
logging.warning(
|
| 213 |
-
"It is recommended to use AMP mixed-precision instead of FP16. "
|
| 214 |
-
"FP16 support needs further verification and tuning, especially for train."
|
| 215 |
-
)
|
| 216 |
-
|
| 217 |
-
if args.horovod:
|
| 218 |
-
logging.info(
|
| 219 |
-
f"Running in horovod mode with multiple processes / nodes. Device: {args.device}."
|
| 220 |
-
f"Process (global: {args.rank}, local {args.local_rank}), total {args.world_size}."
|
| 221 |
-
)
|
| 222 |
-
elif args.distributed:
|
| 223 |
-
logging.info(
|
| 224 |
-
f"Running in distributed mode with multiple processes. Device: {args.device}."
|
| 225 |
-
f"Process (global: {args.rank}, local {args.local_rank}), total {args.world_size}."
|
| 226 |
-
)
|
| 227 |
-
else:
|
| 228 |
-
logging.info(f"Running with a single process. Device {args.device}.")
|
| 229 |
-
|
| 230 |
-
logging.info(f'openai cache dir: {os.path.expanduser(args.openai_model_cache_dir)}')
|
| 231 |
-
|
| 232 |
-
# determine if this worker should save logs and checkpoints. only do so if it is rank == 0
|
| 233 |
-
args.save_logs = args.logs and args.logs.lower() != "none" and is_master(args)
|
| 234 |
-
writer = None
|
| 235 |
-
if args.save_logs and args.tensorboard:
|
| 236 |
-
assert tensorboard is not None, "Please install tensorboard."
|
| 237 |
-
writer = tensorboard.SummaryWriter(args.tensorboard_path)
|
| 238 |
-
|
| 239 |
-
if args.wandb and is_master(args):
|
| 240 |
-
assert wandb is not None, "Please install wandb."
|
| 241 |
-
logging.debug("Starting wandb.")
|
| 242 |
-
# you will have to configure this for your project!
|
| 243 |
-
wandb.init(
|
| 244 |
-
project="clap",
|
| 245 |
-
notes=args.wandb_notes,
|
| 246 |
-
name=args.wandb_notes,
|
| 247 |
-
tags=[],
|
| 248 |
-
config=vars(args),
|
| 249 |
-
)
|
| 250 |
-
logging.debug("Finished loading wandb.")
|
| 251 |
-
|
| 252 |
-
for idx, f in enumerate(pretrained_ckpts):
|
| 253 |
-
logging.info(f"pretrained on {f}")
|
| 254 |
-
args.pretrained = f
|
| 255 |
-
ckpt = torch.load(f, map_location='cpu')
|
| 256 |
-
pretrain_epoch = 0
|
| 257 |
-
if 'epoch' in ckpt:
|
| 258 |
-
pretrain_epoch = ckpt['epoch']
|
| 259 |
-
# train
|
| 260 |
-
best_metrics = lp_main(args, device, writer, pretrain_epoch, idx)
|
| 261 |
-
|
| 262 |
-
if args.wandb and is_master(args):
|
| 263 |
-
assert wandb is not None, "Please install wandb."
|
| 264 |
-
for name, val in best_metrics.items():
|
| 265 |
-
wandb.log({f"val/summary/{name}": val, "epoch": pretrain_epoch})
|
| 266 |
-
|
| 267 |
-
if args.wandb and is_master(args):
|
| 268 |
-
wandb.finish()
|
| 269 |
-
|
| 270 |
-
def update_metric(best_metric, new_metric):
|
| 271 |
-
for key in new_metric:
|
| 272 |
-
if key not in best_metric:
|
| 273 |
-
best_metric[key] = new_metric[key]
|
| 274 |
-
else:
|
| 275 |
-
best_metric[key] = max(best_metric[key], new_metric[key])
|
| 276 |
-
return best_metric
|
| 277 |
-
|
| 278 |
-
def lp_main(args, device, writer, pretrain_epoch, idx):
|
| 279 |
-
|
| 280 |
-
random.seed(args.seed)
|
| 281 |
-
torch.manual_seed(args.seed)
|
| 282 |
-
torch.cuda.manual_seed(args.seed)
|
| 283 |
-
torch.cuda.manual_seed_all(args.seed)
|
| 284 |
-
np.random.seed(args.seed)
|
| 285 |
-
args.class_index_dict = load_class_label(args.class_label_path)
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
# Create CLAP model
|
| 289 |
-
clap_model, clap_model_cfg = create_model(
|
| 290 |
-
args.amodel,
|
| 291 |
-
args.tmodel,
|
| 292 |
-
args.pretrained,
|
| 293 |
-
precision=args.precision,
|
| 294 |
-
device=device,
|
| 295 |
-
jit=args.torchscript,
|
| 296 |
-
force_quick_gelu=args.force_quick_gelu,
|
| 297 |
-
openai_model_cache_dir=os.path.expanduser(args.openai_model_cache_dir),
|
| 298 |
-
skip_params=False,
|
| 299 |
-
enable_fusion=args.enable_fusion,
|
| 300 |
-
fusion_type=args.fusion_type
|
| 301 |
-
)
|
| 302 |
-
|
| 303 |
-
args.lp_out_ch = len(list(args.class_index_dict.keys()))
|
| 304 |
-
# Linear Probe
|
| 305 |
-
if idx == 0:
|
| 306 |
-
logging.info(f"linear probe using mlp: {args.lp_mlp}")
|
| 307 |
-
logging.info(f"linear probe using freeze: {args.lp_freeze}")
|
| 308 |
-
logging.info(f"linear probe act layer: {args.lp_act}")
|
| 309 |
-
logging.info(f"linear probe out ch: {args.lp_out_ch}")
|
| 310 |
-
logging.info(f"linear probe learning rate (if applicable): {args.lp_lr}")
|
| 311 |
-
logging.info(f"linear probe loss func: {args.lp_loss}")
|
| 312 |
-
logging.info(f"linear probe lp_metrics: {args.lp_metrics}")
|
| 313 |
-
|
| 314 |
-
model = LinearProbe(
|
| 315 |
-
clap_model,
|
| 316 |
-
mlp=args.lp_mlp, freeze=args.lp_freeze,
|
| 317 |
-
in_ch=512, out_ch=args.lp_out_ch,
|
| 318 |
-
act=args.lp_act
|
| 319 |
-
) # in_ch is fixed (i.e., 512)
|
| 320 |
-
model = model.to(device)
|
| 321 |
-
|
| 322 |
-
if args.horovod:
|
| 323 |
-
with torch.no_grad():
|
| 324 |
-
for param in model.parameters():
|
| 325 |
-
param.set_(param.contiguous())
|
| 326 |
-
|
| 327 |
-
if args.trace:
|
| 328 |
-
model = trace_model(model, batch_size=args.batch_size, device=device)
|
| 329 |
-
|
| 330 |
-
if is_master(args) and idx == 0:
|
| 331 |
-
logging.info("Linear Probe CLAP Model:")
|
| 332 |
-
logging.info(f"{str(clap_model)}")
|
| 333 |
-
logging.info("Params:")
|
| 334 |
-
params_file = os.path.join(args.logs, args.name, "params.txt")
|
| 335 |
-
with open(params_file, "w") as f:
|
| 336 |
-
for name in sorted(vars(args)):
|
| 337 |
-
val = getattr(args, name)
|
| 338 |
-
logging.info(f" {name}: {val}")
|
| 339 |
-
f.write(f"{name}: {val}\n")
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
if args.distributed and not args.horovod:
|
| 343 |
-
if args.use_bn_sync:
|
| 344 |
-
model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model)
|
| 345 |
-
ddp_args = {}
|
| 346 |
-
if args.ddp_static_graph:
|
| 347 |
-
# this doesn't exist in older PyTorch, arg only added if enabled
|
| 348 |
-
ddp_args["static_graph"] = True
|
| 349 |
-
model = torch.nn.parallel.DistributedDataParallel(
|
| 350 |
-
model, device_ids=[device], find_unused_parameters=True, **ddp_args
|
| 351 |
-
)
|
| 352 |
-
|
| 353 |
-
data = get_data(args, clap_model_cfg)
|
| 354 |
-
assert len(data), "At least one train or eval dataset must be specified."
|
| 355 |
-
if args.trace:
|
| 356 |
-
assert "train" not in data, "Cannot train with traced model"
|
| 357 |
-
|
| 358 |
-
optimizer, scheduler, text_freeze_parameters = config_lp_optimizer(model, data, args)
|
| 359 |
-
|
| 360 |
-
scaler = GradScaler() if args.precision == "amp" else None
|
| 361 |
-
|
| 362 |
-
# optionally resume from a checkpoint
|
| 363 |
-
start_epoch = 0
|
| 364 |
-
if args.resume is not None:
|
| 365 |
-
if os.path.isfile(args.resume):
|
| 366 |
-
checkpoint = torch.load(args.resume, map_location=device)
|
| 367 |
-
if "epoch" in checkpoint:
|
| 368 |
-
# resuming a train checkpoint w/ epoch and optimizer state
|
| 369 |
-
start_epoch = checkpoint["epoch"]
|
| 370 |
-
sd = checkpoint["state_dict"]
|
| 371 |
-
if not args.distributed and next(iter(sd.items()))[0].startswith(
|
| 372 |
-
"module"
|
| 373 |
-
):
|
| 374 |
-
sd = {k[len("module.") :]: v for k, v in sd.items()}
|
| 375 |
-
model.load_state_dict(sd)
|
| 376 |
-
if args.split_opt:
|
| 377 |
-
if optimizer is not None:
|
| 378 |
-
for k, o_ in optimizer.items():
|
| 379 |
-
o_.load_state_dict(checkpoint[k + "_" + "optimizer"])
|
| 380 |
-
if optimizer is not None:
|
| 381 |
-
optimizer.load_state_dict(checkpoint["optimizer"])
|
| 382 |
-
if scaler is not None and "scaler" in checkpoint:
|
| 383 |
-
scaler.load_state_dict(checkpoint["scaler"])
|
| 384 |
-
logging.info(
|
| 385 |
-
f"=> resuming checkpoint '{args.resume}' (epoch {start_epoch})"
|
| 386 |
-
)
|
| 387 |
-
else:
|
| 388 |
-
# loading a bare (model only) checkpoint for fine-tune or evaluation
|
| 389 |
-
model.load_state_dict(checkpoint)
|
| 390 |
-
logging.info(
|
| 391 |
-
f"=> loaded checkpoint '{args.resume}' (epoch {start_epoch})"
|
| 392 |
-
)
|
| 393 |
-
if args.freeze_text:
|
| 394 |
-
print("Freeze Text!!!!")
|
| 395 |
-
for k in text_freeze_parameters:
|
| 396 |
-
k.requires_grad = False
|
| 397 |
-
else:
|
| 398 |
-
logging.info("=> no checkpoint found at '{}'".format(args.resume))
|
| 399 |
-
|
| 400 |
-
cudnn.benchmark = True
|
| 401 |
-
cudnn.deterministic = False
|
| 402 |
-
|
| 403 |
-
if args.wandb and is_master(args):
|
| 404 |
-
args.train_sz = data["train"].dataloader.num_samples
|
| 405 |
-
if args.val_data is not None:
|
| 406 |
-
args.val_sz = data["val"].dataloader.num_samples
|
| 407 |
-
if args.debug:
|
| 408 |
-
wandb.watch(model, log="all")
|
| 409 |
-
if idx == 0:
|
| 410 |
-
wandb.save(params_file)
|
| 411 |
-
|
| 412 |
-
best_metrics = {}
|
| 413 |
-
|
| 414 |
-
if "train" not in data:
|
| 415 |
-
metric = evaluate(model, data, start_epoch, args, writer, extra_suffix="_pe@" + str(pretrain_epoch))
|
| 416 |
-
if is_master(args):
|
| 417 |
-
best_metrics = update_metric(best_metrics, metric)
|
| 418 |
-
return
|
| 419 |
-
elif start_epoch == 0 and "val" in data and not args.no_eval:
|
| 420 |
-
metric = evaluate(model, data, 0, args, writer, extra_suffix="_pe@" + str(pretrain_epoch))
|
| 421 |
-
if is_master(args):
|
| 422 |
-
best_metrics = update_metric(best_metrics, metric)
|
| 423 |
-
if args.save_top_performance:
|
| 424 |
-
current_top_k_ckpt_metrics = {
|
| 425 |
-
i: 0 for i in range(args.save_top_performance)
|
| 426 |
-
} # initialize the top-k metric for ckpts to 0
|
| 427 |
-
|
| 428 |
-
for epoch in range(start_epoch, args.epochs):
|
| 429 |
-
# freeze the text param after (include) args.freeze_text_after, this is -1 by default
|
| 430 |
-
if epoch == args.freeze_text_after:
|
| 431 |
-
print("Text pretrained parameters are freezed since this epoch.")
|
| 432 |
-
for k in text_freeze_parameters:
|
| 433 |
-
k.requires_grad = False
|
| 434 |
-
if is_master(args):
|
| 435 |
-
logging.info(f"Start epoch {epoch}")
|
| 436 |
-
|
| 437 |
-
train_one_epoch(model, data, epoch, optimizer, scaler, scheduler, args, writer, extra_suffix="_pe@" + str(pretrain_epoch))
|
| 438 |
-
completed_epoch = epoch + 1
|
| 439 |
-
|
| 440 |
-
if any(v in data for v in ("val", "imagenet-val", "imagenet-v2")) and not args.no_eval:
|
| 441 |
-
metric = evaluate(model, data, completed_epoch, args, writer, extra_suffix="_pe@" + str(pretrain_epoch))
|
| 442 |
-
if is_master(args):
|
| 443 |
-
best_metrics = update_metric(best_metrics, metric)
|
| 444 |
-
if args.save_top_performance:
|
| 445 |
-
top_k_dataset = args.top_k_checkpoint_select_dataset
|
| 446 |
-
top_k_metric = args.top_k_checkpoint_select_metric
|
| 447 |
-
filtered_metrics = [
|
| 448 |
-
v
|
| 449 |
-
for k, v in metric.items()
|
| 450 |
-
if top_k_metric in k and top_k_dataset in k
|
| 451 |
-
] # check all R@10 metrics (all dataset) and use it to update the ckpt
|
| 452 |
-
# Saving checkpoints.
|
| 453 |
-
if args.save_logs:
|
| 454 |
-
opt_dict = {
|
| 455 |
-
k + "_" + "optimizer": v.state_dict() for k, v in optimizer.items()
|
| 456 |
-
}
|
| 457 |
-
checkpoint_dict = {
|
| 458 |
-
"epoch": completed_epoch,
|
| 459 |
-
"pretrain_epoch": pretrain_epoch,
|
| 460 |
-
"name": args.name,
|
| 461 |
-
"state_dict": model.state_dict(),
|
| 462 |
-
}
|
| 463 |
-
checkpoint_dict.update(opt_dict)
|
| 464 |
-
if scaler is not None:
|
| 465 |
-
checkpoint_dict["scaler"] = scaler.state_dict()
|
| 466 |
-
|
| 467 |
-
if completed_epoch == args.epochs or (
|
| 468 |
-
args.save_frequency > 0 and (completed_epoch % args.save_frequency) == 0
|
| 469 |
-
):
|
| 470 |
-
torch.save(
|
| 471 |
-
checkpoint_dict,
|
| 472 |
-
os.path.join(args.checkpoint_path, f"pretrain_epoch_{pretrain_epoch}_lp_epoch_{completed_epoch}.pt"),
|
| 473 |
-
)
|
| 474 |
-
if args.save_most_recent:
|
| 475 |
-
torch.save(
|
| 476 |
-
checkpoint_dict,
|
| 477 |
-
os.path.join(args.checkpoint_path, f"pretrain_epoch_{pretrain_epoch}_lp_epoch_latest.pt"),
|
| 478 |
-
)
|
| 479 |
-
if args.save_top_performance and not args.no_eval:
|
| 480 |
-
update_top_k_performance(
|
| 481 |
-
filtered_metrics,
|
| 482 |
-
current_top_k_ckpt_metrics,
|
| 483 |
-
args,
|
| 484 |
-
checkpoint_dict,
|
| 485 |
-
bignumbetter=True,
|
| 486 |
-
pretrain_epoch=pretrain_epoch
|
| 487 |
-
)
|
| 488 |
-
del clap_model
|
| 489 |
-
return best_metrics
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
def copy_codebase(args):
|
| 493 |
-
from shutil import copytree, ignore_patterns
|
| 494 |
-
|
| 495 |
-
new_code_path = os.path.join(args.logs, args.name, "code")
|
| 496 |
-
if os.path.exists(new_code_path):
|
| 497 |
-
print(
|
| 498 |
-
f"Error. Experiment already exists at {new_code_path}. Use --name to specify a new experiment."
|
| 499 |
-
)
|
| 500 |
-
return -1
|
| 501 |
-
print(f"Copying codebase to {new_code_path}")
|
| 502 |
-
current_code_path = os.path.realpath(__file__)
|
| 503 |
-
for _ in range(3):
|
| 504 |
-
current_code_path = os.path.dirname(current_code_path)
|
| 505 |
-
copytree(
|
| 506 |
-
current_code_path, new_code_path, ignore=ignore_patterns("log", "logs", "wandb")
|
| 507 |
-
)
|
| 508 |
-
print("Done copying code.")
|
| 509 |
-
return 1
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
if __name__ == "__main__":
|
| 513 |
-
main()
|
| 514 |
-
|
| 515 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/evaluate/eval_retrieval.py
DELETED
|
@@ -1,192 +0,0 @@
|
|
| 1 |
-
import os.path
|
| 2 |
-
import glob
|
| 3 |
-
import random
|
| 4 |
-
import numpy as np
|
| 5 |
-
import logging
|
| 6 |
-
import wandb
|
| 7 |
-
import torch
|
| 8 |
-
import torch.backends.cudnn as cudnn
|
| 9 |
-
from laion_clap import create_model
|
| 10 |
-
from laion_clap.training.logger import setup_logging
|
| 11 |
-
from laion_clap.training.data import get_data
|
| 12 |
-
from laion_clap.training.train import evaluate
|
| 13 |
-
from laion_clap.utils import get_tar_path_from_dataset_name, dataset_split
|
| 14 |
-
from laion_clap.training.params import parse_args
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
def find_params_value(file, key):
|
| 18 |
-
# find value of params in params_file
|
| 19 |
-
with open(file, 'r') as f:
|
| 20 |
-
for line in f:
|
| 21 |
-
if key + ': ' in line:
|
| 22 |
-
return line.split(': ')[1].strip()
|
| 23 |
-
return None
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
if __name__ == '__main__':
|
| 27 |
-
# (yusong) repeated run might have different metric results.
|
| 28 |
-
# This is because we randomly select crop 10s for each audio.
|
| 29 |
-
args = parse_args()
|
| 30 |
-
|
| 31 |
-
if os.path.isdir(args.pretrained):
|
| 32 |
-
log_dir = os.path.dirname(args.pretrained)
|
| 33 |
-
else:
|
| 34 |
-
log_dir = os.path.dirname(os.path.dirname(args.pretrained))
|
| 35 |
-
|
| 36 |
-
args.log_level = logging.DEBUG if args.debug else logging.INFO
|
| 37 |
-
log_path = os.path.join(log_dir, 'out.log')
|
| 38 |
-
setup_logging(log_path, args.log_level)
|
| 39 |
-
params_file = os.path.join(log_dir, 'params.txt')
|
| 40 |
-
|
| 41 |
-
seed = 3407
|
| 42 |
-
random.seed(seed)
|
| 43 |
-
torch.manual_seed(seed)
|
| 44 |
-
torch.cuda.manual_seed(seed)
|
| 45 |
-
torch.cuda.manual_seed_all(seed)
|
| 46 |
-
np.random.seed(seed)
|
| 47 |
-
|
| 48 |
-
cudnn.benchmark = True
|
| 49 |
-
cudnn.deterministic = False
|
| 50 |
-
pretrained = 'openai'
|
| 51 |
-
amodel = find_params_value(params_file, 'amodel')
|
| 52 |
-
tmodel = find_params_value(params_file, 'tmodel')
|
| 53 |
-
|
| 54 |
-
if amodel is None or tmodel is None:
|
| 55 |
-
raise ValueError('model type not found in params file')
|
| 56 |
-
|
| 57 |
-
# set up dummy values for args
|
| 58 |
-
args.parallel_eval = False
|
| 59 |
-
args.rank = 0
|
| 60 |
-
args.local_rank = 0
|
| 61 |
-
args.world_size = 1
|
| 62 |
-
args.val_frequency = 1
|
| 63 |
-
args.epochs = 1
|
| 64 |
-
args.precision = 'fp32'
|
| 65 |
-
args.save_logs = True
|
| 66 |
-
args.wandb = True
|
| 67 |
-
args.class_index_dict = None
|
| 68 |
-
|
| 69 |
-
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
| 70 |
-
args.device = device
|
| 71 |
-
|
| 72 |
-
if args.remotedata:
|
| 73 |
-
for dataset_name in args.datasetnames:
|
| 74 |
-
for split in dataset_split[dataset_name]:
|
| 75 |
-
if not os.path.exists(f"./json_files/{dataset_name}/{split}"):
|
| 76 |
-
os.makedirs(f"./json_files/{dataset_name}/{split}")
|
| 77 |
-
os.system(
|
| 78 |
-
f"aws s3 cp s3://s-laion-audio/webdataset_tar/{dataset_name}/{split}/sizes.json ./json_files/{dataset_name}/{split}/sizes.json"
|
| 79 |
-
)
|
| 80 |
-
|
| 81 |
-
if args.datasetinfos is None:
|
| 82 |
-
args.datasetinfos = ["train", "unbalanced_train", "balanced_train"]
|
| 83 |
-
if args.dataset_type == "webdataset":
|
| 84 |
-
args.train_data = get_tar_path_from_dataset_name(
|
| 85 |
-
args.datasetnames,
|
| 86 |
-
args.datasetinfos,
|
| 87 |
-
islocal=not args.remotedata,
|
| 88 |
-
proportion=args.dataset_proportion,
|
| 89 |
-
dataset_path=args.datasetpath,
|
| 90 |
-
)
|
| 91 |
-
args.val_data = get_tar_path_from_dataset_name(
|
| 92 |
-
args.datasetnames,
|
| 93 |
-
["valid", "test", "eval"],
|
| 94 |
-
islocal=not args.remotedata,
|
| 95 |
-
proportion=1,
|
| 96 |
-
dataset_path=args.datasetpath,
|
| 97 |
-
)
|
| 98 |
-
model, model_cfg = create_model(
|
| 99 |
-
amodel,
|
| 100 |
-
tmodel,
|
| 101 |
-
pretrained,
|
| 102 |
-
precision='fp32',
|
| 103 |
-
device=device,
|
| 104 |
-
jit=False,
|
| 105 |
-
force_quick_gelu=False,
|
| 106 |
-
openai_model_cache_dir=os.path.expanduser(args.openai_model_cache_dir),
|
| 107 |
-
skip_params=False,
|
| 108 |
-
enable_fusion=args.enable_fusion,
|
| 109 |
-
fusion_type=args.fusion_type
|
| 110 |
-
) # a hack to get model_cfg
|
| 111 |
-
|
| 112 |
-
data = get_data(args, model_cfg=model_cfg) # (yusong): hack: no model_cfg needed to get data
|
| 113 |
-
|
| 114 |
-
writer = None # if use tensorboard, initalize writer here
|
| 115 |
-
|
| 116 |
-
if args.wandb:
|
| 117 |
-
assert wandb is not None, "Please install wandb."
|
| 118 |
-
|
| 119 |
-
# # find the line with "wandb_notes" and get the value
|
| 120 |
-
# wandb_notes = find_params_value(params_file, 'wandb_notes')
|
| 121 |
-
# if wandb_notes is None:
|
| 122 |
-
# print(f'wandb_notes not found in params file: {params_file}, set to timestamp.')
|
| 123 |
-
# wandb_notes = f'experiment_{time.strftime("%Y%m%d-%H%M%S")}'
|
| 124 |
-
# wandb_notes = wandb_notes + '-eval-retrieval'
|
| 125 |
-
wandb_notes = args.wandb_notes
|
| 126 |
-
|
| 127 |
-
logging.debug("Starting wandb.")
|
| 128 |
-
args.train_sz = data["train"].dataloader.num_samples
|
| 129 |
-
if args.val_data is not None:
|
| 130 |
-
args.val_sz = data["val"].dataloader.num_samples
|
| 131 |
-
# you will have to configure this for your project!
|
| 132 |
-
if args.wandb_id is not None:
|
| 133 |
-
wandb.init(
|
| 134 |
-
project="clap",
|
| 135 |
-
id=args.wandb_id,
|
| 136 |
-
resume=True
|
| 137 |
-
)
|
| 138 |
-
else:
|
| 139 |
-
wandb.init(
|
| 140 |
-
project="clap",
|
| 141 |
-
notes=wandb_notes,
|
| 142 |
-
name=wandb_notes,
|
| 143 |
-
tags=[],
|
| 144 |
-
config=vars(args),
|
| 145 |
-
)
|
| 146 |
-
logging.debug("Finished loading wandb.")
|
| 147 |
-
|
| 148 |
-
if os.path.isdir(args.pretrained):
|
| 149 |
-
all_model_checkpoints = sorted(glob.glob(os.path.join(log_dir, 'checkpoints', '*.pt')), key=os.path.getmtime)
|
| 150 |
-
else:
|
| 151 |
-
all_model_checkpoints = [args.pretrained]
|
| 152 |
-
for model_path in all_model_checkpoints:
|
| 153 |
-
args.checkpoint_path = os.path.dirname(model_path)
|
| 154 |
-
model, model_cfg = create_model(
|
| 155 |
-
amodel,
|
| 156 |
-
tmodel,
|
| 157 |
-
pretrained,
|
| 158 |
-
precision='fp32',
|
| 159 |
-
device=device,
|
| 160 |
-
jit=False,
|
| 161 |
-
force_quick_gelu=False,
|
| 162 |
-
openai_model_cache_dir=os.path.expanduser(args.openai_model_cache_dir),
|
| 163 |
-
skip_params=False,
|
| 164 |
-
enable_fusion=args.enable_fusion,
|
| 165 |
-
fusion_type=args.fusion_type
|
| 166 |
-
)
|
| 167 |
-
|
| 168 |
-
# load model
|
| 169 |
-
checkpoint = torch.load(model_path, map_location=device)
|
| 170 |
-
if "epoch" in checkpoint:
|
| 171 |
-
# resuming a train checkpoint w/ epoch and optimizer state
|
| 172 |
-
start_epoch = checkpoint["epoch"]
|
| 173 |
-
sd = checkpoint["state_dict"]
|
| 174 |
-
if next(iter(sd.items()))[0].startswith(
|
| 175 |
-
"module"
|
| 176 |
-
):
|
| 177 |
-
sd = {k[len("module."):]: v for k, v in sd.items()}
|
| 178 |
-
model.load_state_dict(sd)
|
| 179 |
-
logging.info(
|
| 180 |
-
f"=> resuming checkpoint '{model_path}' (epoch {start_epoch})"
|
| 181 |
-
)
|
| 182 |
-
else:
|
| 183 |
-
# loading a bare (model only) checkpoint for fine-tune or evaluation
|
| 184 |
-
model.load_state_dict(checkpoint)
|
| 185 |
-
start_epoch = 0
|
| 186 |
-
|
| 187 |
-
model.to(device)
|
| 188 |
-
model.eval()
|
| 189 |
-
for param in model.parameters():
|
| 190 |
-
param.requires_grad = False
|
| 191 |
-
|
| 192 |
-
evaluate(model, data, start_epoch, args, writer)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/evaluate/eval_retrieval_main.py
DELETED
|
@@ -1,257 +0,0 @@
|
|
| 1 |
-
import os.path
|
| 2 |
-
import glob
|
| 3 |
-
import random
|
| 4 |
-
import numpy as np
|
| 5 |
-
import logging
|
| 6 |
-
import wandb
|
| 7 |
-
import torch
|
| 8 |
-
import torch.nn.functional as F
|
| 9 |
-
import torch.backends.cudnn as cudnn
|
| 10 |
-
from clap_module import create_model
|
| 11 |
-
from clap_module import tokenize
|
| 12 |
-
from training.logger import setup_logging
|
| 13 |
-
from training.data import get_data
|
| 14 |
-
from training.train import evaluate
|
| 15 |
-
from clap_module.utils import get_tar_path_from_dataset_name, dataset_split
|
| 16 |
-
from training.params import parse_args
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
def find_params_value(file, key):
|
| 20 |
-
# find value of params in params_file
|
| 21 |
-
with open(file, 'r') as f:
|
| 22 |
-
for line in f:
|
| 23 |
-
if key + ': ' in line:
|
| 24 |
-
return line.split(': ')[1].strip()
|
| 25 |
-
return None
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
def evaluate_zeroshot(model, data, start_epoch, args, writer):
|
| 29 |
-
dataloader = data["val"].dataloader
|
| 30 |
-
metrics = {}
|
| 31 |
-
device = torch.device(args.device)
|
| 32 |
-
model.eval()
|
| 33 |
-
metrics.update({"epoch": start_epoch})
|
| 34 |
-
|
| 35 |
-
all_audio_features = []
|
| 36 |
-
all_class_labels = []
|
| 37 |
-
with torch.no_grad():
|
| 38 |
-
for i, batch in enumerate(dataloader):
|
| 39 |
-
audios = batch # contains mel_spec, wavform, and longer list
|
| 40 |
-
audio_features = model(audios, None, device)
|
| 41 |
-
audio_features = F.normalize(audio_features, dim=-1)
|
| 42 |
-
all_audio_features.append(audio_features.detach().cpu())
|
| 43 |
-
all_class_labels.append(torch.argmax(batch["class_label"], 1).long())
|
| 44 |
-
all_audio_features = torch.cat(all_audio_features, dim=0)
|
| 45 |
-
all_class_labels = torch.cat(all_class_labels, dim=0)
|
| 46 |
-
metrics["num_samples"] = all_audio_features.shape[0]
|
| 47 |
-
|
| 48 |
-
# get text features
|
| 49 |
-
all_texts = ["This is a sound of " + t for t in args.class_index_dict.keys()]
|
| 50 |
-
# (yusong): a hack, can make it better
|
| 51 |
-
if args.tmodel == "transformer":
|
| 52 |
-
from clap_module.tokenizer import tokenize
|
| 53 |
-
all_texts = tokenize(all_texts)
|
| 54 |
-
else:
|
| 55 |
-
from training.data import tokenizer
|
| 56 |
-
all_texts = tokenizer(all_texts)
|
| 57 |
-
all_text_features = model(None, all_texts, device)
|
| 58 |
-
all_text_features = F.normalize(all_text_features, dim=-1).detach().cpu()
|
| 59 |
-
|
| 60 |
-
# compute similarity
|
| 61 |
-
logit_scale_a, logit_scale_t = model(None, None, device)
|
| 62 |
-
logit_scale_a = logit_scale_a.cpu()
|
| 63 |
-
|
| 64 |
-
logits_per_audio = (logit_scale_a * all_audio_features @ all_text_features.t()).detach().cpu()
|
| 65 |
-
logits_per_text = logits_per_audio.t().detach().cpu()
|
| 66 |
-
|
| 67 |
-
ground_truth = all_class_labels.view(-1, 1)
|
| 68 |
-
logit = logits_per_audio
|
| 69 |
-
|
| 70 |
-
ranking = torch.argsort(logit, descending=True)
|
| 71 |
-
preds = torch.where(ranking == ground_truth)[1] # (yusong) this line is slow because it uses single thread
|
| 72 |
-
preds = preds.detach().cpu().numpy()
|
| 73 |
-
metrics[f"{args.datasetnames[0]}_mean_rank"] = preds.mean() + 1
|
| 74 |
-
metrics[f"{args.datasetnames[0]}_median_rank"] = np.floor(np.median(preds)) + 1
|
| 75 |
-
for k in [1, 5, 10]:
|
| 76 |
-
metrics[f"{args.datasetnames[0]}_R@{k}"] = np.mean(preds < k)
|
| 77 |
-
# map@10
|
| 78 |
-
metrics[f"{args.datasetnames[0]}_mAP@10"] = np.mean(np.where(preds < 10, 1 / (preds + 1), 0.0))
|
| 79 |
-
|
| 80 |
-
logging.info(
|
| 81 |
-
f"Eval Epoch: {start_epoch} "
|
| 82 |
-
+ "\t".join([f"{k}: {round(v, 4):.4f}" for k, v in metrics.items()])
|
| 83 |
-
)
|
| 84 |
-
|
| 85 |
-
if args.wandb:
|
| 86 |
-
assert wandb is not None, "Please install wandb."
|
| 87 |
-
for name, val in metrics.items():
|
| 88 |
-
wandb.log({f"val/{name}": val, "epoch": start_epoch})
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
if __name__ == '__main__':
|
| 92 |
-
# (yusong) repeated run might have different metric results.
|
| 93 |
-
# This is because we randomly select crop 10s for each audio.
|
| 94 |
-
args = parse_args()
|
| 95 |
-
|
| 96 |
-
if os.path.isdir(args.pretrained):
|
| 97 |
-
log_dir = os.path.dirname(args.pretrained)
|
| 98 |
-
else:
|
| 99 |
-
log_dir = os.path.dirname(os.path.dirname(args.pretrained))
|
| 100 |
-
|
| 101 |
-
args.log_level = logging.DEBUG if args.debug else logging.INFO
|
| 102 |
-
log_path = os.path.join(log_dir, 'out.log')
|
| 103 |
-
setup_logging(log_path, args.log_level)
|
| 104 |
-
params_file = os.path.join(log_dir, 'params.txt')
|
| 105 |
-
|
| 106 |
-
seed = 3407
|
| 107 |
-
random.seed(seed)
|
| 108 |
-
torch.manual_seed(seed)
|
| 109 |
-
torch.cuda.manual_seed(seed)
|
| 110 |
-
torch.cuda.manual_seed_all(seed)
|
| 111 |
-
np.random.seed(seed)
|
| 112 |
-
|
| 113 |
-
cudnn.benchmark = True
|
| 114 |
-
cudnn.deterministic = False
|
| 115 |
-
pretrained = 'openai'
|
| 116 |
-
amodel = find_params_value(params_file, 'amodel')
|
| 117 |
-
tmodel = find_params_value(params_file, 'tmodel')
|
| 118 |
-
|
| 119 |
-
if amodel is None or tmodel is None:
|
| 120 |
-
raise ValueError('model type not found in params file')
|
| 121 |
-
|
| 122 |
-
# set up dummy values for args
|
| 123 |
-
args.parallel_eval = False
|
| 124 |
-
args.rank = 0
|
| 125 |
-
args.local_rank = 0
|
| 126 |
-
args.world_size = 1
|
| 127 |
-
args.val_frequency = 1
|
| 128 |
-
args.epochs = 1
|
| 129 |
-
args.precision = 'fp32'
|
| 130 |
-
args.save_logs = True
|
| 131 |
-
args.wandb = args.report_to == 'wandb'
|
| 132 |
-
args.class_index_dict = None
|
| 133 |
-
|
| 134 |
-
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
| 135 |
-
args.device = device
|
| 136 |
-
|
| 137 |
-
if args.remotedata:
|
| 138 |
-
for dataset_name in args.datasetnames:
|
| 139 |
-
for split in dataset_split[dataset_name]:
|
| 140 |
-
if not os.path.exists(f"./json_files/{dataset_name}/{split}"):
|
| 141 |
-
os.makedirs(f"./json_files/{dataset_name}/{split}")
|
| 142 |
-
os.system(
|
| 143 |
-
f"aws s3 cp s3://s-laion-audio/webdataset_tar/{dataset_name}/{split}/sizes.json ./json_files/{dataset_name}/{split}/sizes.json"
|
| 144 |
-
)
|
| 145 |
-
|
| 146 |
-
if args.datasetinfos is None:
|
| 147 |
-
args.datasetinfos = ["train", "unbalanced_train", "balanced_train"]
|
| 148 |
-
if args.dataset_type == "webdataset":
|
| 149 |
-
args.train_data = get_tar_path_from_dataset_name(
|
| 150 |
-
args.datasetnames,
|
| 151 |
-
args.datasetinfos,
|
| 152 |
-
islocal=not args.remotedata,
|
| 153 |
-
proportion=args.dataset_proportion,
|
| 154 |
-
dataset_path=args.datasetpath,
|
| 155 |
-
)
|
| 156 |
-
args.val_data = get_tar_path_from_dataset_name(
|
| 157 |
-
args.datasetnames,
|
| 158 |
-
["valid", "test", "eval"],
|
| 159 |
-
islocal=not args.remotedata,
|
| 160 |
-
proportion=1,
|
| 161 |
-
dataset_path=args.datasetpath,
|
| 162 |
-
)
|
| 163 |
-
model, model_cfg = create_model(
|
| 164 |
-
amodel,
|
| 165 |
-
tmodel,
|
| 166 |
-
pretrained,
|
| 167 |
-
precision='fp32',
|
| 168 |
-
device=device,
|
| 169 |
-
jit=False,
|
| 170 |
-
force_quick_gelu=False,
|
| 171 |
-
openai_model_cache_dir=os.path.expanduser(args.openai_model_cache_dir),
|
| 172 |
-
skip_params=False,
|
| 173 |
-
enable_fusion=args.enable_fusion,
|
| 174 |
-
fusion_type=args.fusion_type
|
| 175 |
-
) # a hack to get model_cfg
|
| 176 |
-
|
| 177 |
-
data = get_data(args, model_cfg=model_cfg) # (yusong): hack: no model_cfg needed to get data
|
| 178 |
-
|
| 179 |
-
writer = None # if use tensorboard, initalize writer here
|
| 180 |
-
|
| 181 |
-
if args.wandb:
|
| 182 |
-
assert wandb is not None, "Please install wandb."
|
| 183 |
-
|
| 184 |
-
# # find the line with "wandb_notes" and get the value
|
| 185 |
-
# wandb_notes = find_params_value(params_file, 'wandb_notes')
|
| 186 |
-
# if wandb_notes is None:
|
| 187 |
-
# print(f'wandb_notes not found in params file: {params_file}, set to timestamp.')
|
| 188 |
-
# wandb_notes = f'experiment_{time.strftime("%Y%m%d-%H%M%S")}'
|
| 189 |
-
# wandb_notes = wandb_notes + '-eval-retrieval'
|
| 190 |
-
wandb_notes = args.wandb_notes
|
| 191 |
-
|
| 192 |
-
logging.debug("Starting wandb.")
|
| 193 |
-
args.train_sz = data["train"].dataloader.num_samples
|
| 194 |
-
if args.val_data is not None:
|
| 195 |
-
args.val_sz = data["val"].dataloader.num_samples
|
| 196 |
-
# you will have to configure this for your project!
|
| 197 |
-
if args.wandb_id is not None:
|
| 198 |
-
wandb.init(
|
| 199 |
-
project="clap",
|
| 200 |
-
id=args.wandb_id,
|
| 201 |
-
resume=True
|
| 202 |
-
)
|
| 203 |
-
else:
|
| 204 |
-
wandb.init(
|
| 205 |
-
project="clap",
|
| 206 |
-
notes=wandb_notes,
|
| 207 |
-
name=wandb_notes,
|
| 208 |
-
tags=[],
|
| 209 |
-
config=vars(args),
|
| 210 |
-
)
|
| 211 |
-
logging.debug("Finished loading wandb.")
|
| 212 |
-
|
| 213 |
-
if os.path.isdir(args.pretrained):
|
| 214 |
-
all_model_checkpoints = sorted(glob.glob(os.path.join(log_dir, 'checkpoints', '*.pt')), key=os.path.getmtime)
|
| 215 |
-
else:
|
| 216 |
-
all_model_checkpoints = [args.pretrained]
|
| 217 |
-
for model_path in all_model_checkpoints:
|
| 218 |
-
args.checkpoint_path = os.path.dirname(model_path)
|
| 219 |
-
model, model_cfg = create_model(
|
| 220 |
-
amodel,
|
| 221 |
-
tmodel,
|
| 222 |
-
pretrained,
|
| 223 |
-
precision='fp32',
|
| 224 |
-
device=device,
|
| 225 |
-
jit=False,
|
| 226 |
-
force_quick_gelu=False,
|
| 227 |
-
openai_model_cache_dir=os.path.expanduser(args.openai_model_cache_dir),
|
| 228 |
-
skip_params=False,
|
| 229 |
-
enable_fusion=args.enable_fusion,
|
| 230 |
-
fusion_type=args.fusion_type
|
| 231 |
-
)
|
| 232 |
-
|
| 233 |
-
# load model
|
| 234 |
-
checkpoint = torch.load(model_path, map_location=device)
|
| 235 |
-
if "epoch" in checkpoint:
|
| 236 |
-
# resuming a train checkpoint w/ epoch and optimizer state
|
| 237 |
-
start_epoch = checkpoint["epoch"]
|
| 238 |
-
sd = checkpoint["state_dict"]
|
| 239 |
-
if next(iter(sd.items()))[0].startswith(
|
| 240 |
-
"module"
|
| 241 |
-
):
|
| 242 |
-
sd = {k[len("module."):]: v for k, v in sd.items()}
|
| 243 |
-
model.load_state_dict(sd)
|
| 244 |
-
logging.info(
|
| 245 |
-
f"=> resuming checkpoint '{model_path}' (epoch {start_epoch})"
|
| 246 |
-
)
|
| 247 |
-
else:
|
| 248 |
-
# loading a bare (model only) checkpoint for fine-tune or evaluation
|
| 249 |
-
model.load_state_dict(checkpoint)
|
| 250 |
-
start_epoch = 0
|
| 251 |
-
|
| 252 |
-
model.to(device)
|
| 253 |
-
model.eval()
|
| 254 |
-
for param in model.parameters():
|
| 255 |
-
param.requires_grad = False
|
| 256 |
-
|
| 257 |
-
evaluate_zeroshot(model, data, start_epoch, args, writer)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/evaluate/eval_zeroshot_classification.py
DELETED
|
@@ -1,261 +0,0 @@
|
|
| 1 |
-
import os.path
|
| 2 |
-
import glob
|
| 3 |
-
import random
|
| 4 |
-
import numpy as np
|
| 5 |
-
import logging
|
| 6 |
-
import wandb
|
| 7 |
-
import torch
|
| 8 |
-
import torch.nn.functional as F
|
| 9 |
-
import torch.backends.cudnn as cudnn
|
| 10 |
-
from clap_module import create_model
|
| 11 |
-
from clap_module import tokenize
|
| 12 |
-
from training.logger import setup_logging
|
| 13 |
-
from training.data import get_data
|
| 14 |
-
from training.train import evaluate
|
| 15 |
-
from clap_module.utils import get_tar_path_from_dataset_name, dataset_split
|
| 16 |
-
from training.params import parse_args
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
def find_params_value(file, key):
|
| 20 |
-
# find value of params in params_file
|
| 21 |
-
with open(file, 'r') as f:
|
| 22 |
-
for line in f:
|
| 23 |
-
if key + ': ' in line:
|
| 24 |
-
return line.split(': ')[1].strip()
|
| 25 |
-
return None
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
def evaluate_zeroshot(model, data, start_epoch, args, writer):
|
| 29 |
-
dataloader = data["val"].dataloader
|
| 30 |
-
metrics = {}
|
| 31 |
-
device = torch.device(args.device)
|
| 32 |
-
model.eval()
|
| 33 |
-
metrics.update({"epoch": start_epoch})
|
| 34 |
-
|
| 35 |
-
all_audio_features = []
|
| 36 |
-
all_class_labels = []
|
| 37 |
-
with torch.no_grad():
|
| 38 |
-
for i, batch in enumerate(dataloader):
|
| 39 |
-
audios = batch # contains mel_spec, wavform, and longer list
|
| 40 |
-
audio_features = model(audios, None, device)
|
| 41 |
-
audio_features = F.normalize(audio_features, dim=-1)
|
| 42 |
-
all_audio_features.append(audio_features.detach().cpu())
|
| 43 |
-
all_class_labels.append(torch.argmax(batch["class_label"], 1).long())
|
| 44 |
-
all_audio_features = torch.cat(all_audio_features, dim=0)
|
| 45 |
-
all_class_labels = torch.cat(all_class_labels, dim=0)
|
| 46 |
-
metrics["num_samples"] = all_audio_features.shape[0]
|
| 47 |
-
|
| 48 |
-
# get text features
|
| 49 |
-
if args.val_dataset_names == ['GTZAN']:
|
| 50 |
-
all_texts = [f"This is a {t} song." for t in args.class_index_dict.keys()]
|
| 51 |
-
else:
|
| 52 |
-
all_texts = [f"This is a sound of {t}." for t in args.class_index_dict.keys()]
|
| 53 |
-
logging.info(f'class label prompts: {all_texts}')
|
| 54 |
-
# (yusong): a hack, can make it better
|
| 55 |
-
if args.tmodel == "transformer":
|
| 56 |
-
from clap_module.tokenizer import tokenize
|
| 57 |
-
all_texts = tokenize(all_texts)
|
| 58 |
-
else:
|
| 59 |
-
from training.data import tokenizer
|
| 60 |
-
all_texts = tokenizer(all_texts)
|
| 61 |
-
all_text_features = model(None, all_texts, device)
|
| 62 |
-
all_text_features = F.normalize(all_text_features, dim=-1).detach().cpu()
|
| 63 |
-
|
| 64 |
-
# compute similarity
|
| 65 |
-
logit_scale_a, logit_scale_t = model(None, None, device)
|
| 66 |
-
logit_scale_a = logit_scale_a.cpu()
|
| 67 |
-
|
| 68 |
-
logits_per_audio = (logit_scale_a * all_audio_features @ all_text_features.t()).detach().cpu()
|
| 69 |
-
logits_per_text = logits_per_audio.t().detach().cpu()
|
| 70 |
-
|
| 71 |
-
ground_truth = all_class_labels.view(-1, 1)
|
| 72 |
-
logit = logits_per_audio
|
| 73 |
-
|
| 74 |
-
ranking = torch.argsort(logit, descending=True)
|
| 75 |
-
preds = torch.where(ranking == ground_truth)[1] # (yusong) this line is slow because it uses single thread
|
| 76 |
-
preds = preds.detach().cpu().numpy()
|
| 77 |
-
metrics[f"{args.datasetnames[0]}_mean_rank"] = preds.mean() + 1
|
| 78 |
-
metrics[f"{args.datasetnames[0]}_median_rank"] = np.floor(np.median(preds)) + 1
|
| 79 |
-
for k in [1, 5, 10]:
|
| 80 |
-
metrics[f"{args.datasetnames[0]}_R@{k}"] = np.mean(preds < k)
|
| 81 |
-
# map@10
|
| 82 |
-
metrics[f"{args.datasetnames[0]}_mAP@10"] = np.mean(np.where(preds < 10, 1 / (preds + 1), 0.0))
|
| 83 |
-
|
| 84 |
-
logging.info(
|
| 85 |
-
f"Eval Epoch: {start_epoch} "
|
| 86 |
-
+ "\t".join([f"{k}: {round(v, 4):.4f}" for k, v in metrics.items()])
|
| 87 |
-
)
|
| 88 |
-
|
| 89 |
-
if args.wandb:
|
| 90 |
-
assert wandb is not None, "Please install wandb."
|
| 91 |
-
for name, val in metrics.items():
|
| 92 |
-
wandb.log({f"val/{name}": val, "epoch": start_epoch})
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
if __name__ == '__main__':
|
| 96 |
-
# (yusong) repeated run might have different metric results.
|
| 97 |
-
# This is because we randomly select crop 10s for each audio.
|
| 98 |
-
args = parse_args()
|
| 99 |
-
|
| 100 |
-
if os.path.isdir(args.pretrained):
|
| 101 |
-
log_dir = os.path.dirname(args.pretrained)
|
| 102 |
-
else:
|
| 103 |
-
log_dir = os.path.dirname(os.path.dirname(args.pretrained))
|
| 104 |
-
|
| 105 |
-
args.log_level = logging.DEBUG if args.debug else logging.INFO
|
| 106 |
-
log_path = os.path.join(log_dir, 'out.log')
|
| 107 |
-
setup_logging(log_path, args.log_level)
|
| 108 |
-
params_file = os.path.join(log_dir, 'params.txt')
|
| 109 |
-
|
| 110 |
-
seed = 3407
|
| 111 |
-
random.seed(seed)
|
| 112 |
-
torch.manual_seed(seed)
|
| 113 |
-
torch.cuda.manual_seed(seed)
|
| 114 |
-
torch.cuda.manual_seed_all(seed)
|
| 115 |
-
np.random.seed(seed)
|
| 116 |
-
|
| 117 |
-
cudnn.benchmark = True
|
| 118 |
-
cudnn.deterministic = False
|
| 119 |
-
pretrained = 'openai'
|
| 120 |
-
amodel = find_params_value(params_file, 'amodel')
|
| 121 |
-
tmodel = find_params_value(params_file, 'tmodel')
|
| 122 |
-
|
| 123 |
-
if amodel is None or tmodel is None:
|
| 124 |
-
raise ValueError('model type not found in params file')
|
| 125 |
-
|
| 126 |
-
# set up dummy values for args
|
| 127 |
-
args.parallel_eval = False
|
| 128 |
-
args.rank = 0
|
| 129 |
-
args.local_rank = 0
|
| 130 |
-
args.world_size = 1
|
| 131 |
-
args.val_frequency = 1
|
| 132 |
-
args.epochs = 1
|
| 133 |
-
args.precision = 'fp32'
|
| 134 |
-
args.save_logs = True
|
| 135 |
-
args.wandb = args.report_to == 'wandb'
|
| 136 |
-
args.class_index_dict = None
|
| 137 |
-
|
| 138 |
-
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
| 139 |
-
args.device = device
|
| 140 |
-
|
| 141 |
-
if args.remotedata:
|
| 142 |
-
for dataset_name in args.datasetnames:
|
| 143 |
-
for split in dataset_split[dataset_name]:
|
| 144 |
-
if not os.path.exists(f"./json_files/{dataset_name}/{split}"):
|
| 145 |
-
os.makedirs(f"./json_files/{dataset_name}/{split}")
|
| 146 |
-
os.system(
|
| 147 |
-
f"aws s3 cp s3://s-laion-audio/webdataset_tar/{dataset_name}/{split}/sizes.json ./json_files/{dataset_name}/{split}/sizes.json"
|
| 148 |
-
)
|
| 149 |
-
|
| 150 |
-
if args.datasetinfos is None:
|
| 151 |
-
args.datasetinfos = ["train", "unbalanced_train", "balanced_train"]
|
| 152 |
-
if args.dataset_type == "webdataset":
|
| 153 |
-
args.train_data = get_tar_path_from_dataset_name(
|
| 154 |
-
args.datasetnames,
|
| 155 |
-
args.datasetinfos,
|
| 156 |
-
islocal=not args.remotedata,
|
| 157 |
-
proportion=args.dataset_proportion,
|
| 158 |
-
dataset_path=args.datasetpath,
|
| 159 |
-
)
|
| 160 |
-
args.val_data = get_tar_path_from_dataset_name(
|
| 161 |
-
args.datasetnames,
|
| 162 |
-
["valid", "test", "eval"],
|
| 163 |
-
islocal=not args.remotedata,
|
| 164 |
-
proportion=1,
|
| 165 |
-
dataset_path=args.datasetpath,
|
| 166 |
-
)
|
| 167 |
-
model, model_cfg = create_model(
|
| 168 |
-
amodel,
|
| 169 |
-
tmodel,
|
| 170 |
-
pretrained,
|
| 171 |
-
precision='fp32',
|
| 172 |
-
device=device,
|
| 173 |
-
jit=False,
|
| 174 |
-
force_quick_gelu=False,
|
| 175 |
-
openai_model_cache_dir=os.path.expanduser(args.openai_model_cache_dir),
|
| 176 |
-
skip_params=False,
|
| 177 |
-
enable_fusion=args.enable_fusion,
|
| 178 |
-
fusion_type=args.fusion_type
|
| 179 |
-
) # a hack to get model_cfg
|
| 180 |
-
|
| 181 |
-
data = get_data(args, model_cfg=model_cfg) # (yusong): hack: no model_cfg needed to get data
|
| 182 |
-
|
| 183 |
-
writer = None # if use tensorboard, initalize writer here
|
| 184 |
-
|
| 185 |
-
if args.wandb:
|
| 186 |
-
assert wandb is not None, "Please install wandb."
|
| 187 |
-
|
| 188 |
-
# # find the line with "wandb_notes" and get the value
|
| 189 |
-
# wandb_notes = find_params_value(params_file, 'wandb_notes')
|
| 190 |
-
# if wandb_notes is None:
|
| 191 |
-
# print(f'wandb_notes not found in params file: {params_file}, set to timestamp.')
|
| 192 |
-
# wandb_notes = f'experiment_{time.strftime("%Y%m%d-%H%M%S")}'
|
| 193 |
-
# wandb_notes = wandb_notes + '-eval-retrieval'
|
| 194 |
-
wandb_notes = args.wandb_notes
|
| 195 |
-
|
| 196 |
-
logging.debug("Starting wandb.")
|
| 197 |
-
args.train_sz = data["train"].dataloader.num_samples
|
| 198 |
-
if args.val_data is not None:
|
| 199 |
-
args.val_sz = data["val"].dataloader.num_samples
|
| 200 |
-
# you will have to configure this for your project!
|
| 201 |
-
if args.wandb_id is not None:
|
| 202 |
-
wandb.init(
|
| 203 |
-
project="clap",
|
| 204 |
-
id=args.wandb_id,
|
| 205 |
-
resume=True
|
| 206 |
-
)
|
| 207 |
-
else:
|
| 208 |
-
wandb.init(
|
| 209 |
-
project="clap",
|
| 210 |
-
notes=wandb_notes,
|
| 211 |
-
name=wandb_notes,
|
| 212 |
-
tags=[],
|
| 213 |
-
config=vars(args),
|
| 214 |
-
)
|
| 215 |
-
logging.debug("Finished loading wandb.")
|
| 216 |
-
|
| 217 |
-
if os.path.isdir(args.pretrained):
|
| 218 |
-
all_model_checkpoints = sorted(glob.glob(os.path.join(log_dir, 'checkpoints', '*.pt')), key=os.path.getmtime)
|
| 219 |
-
else:
|
| 220 |
-
all_model_checkpoints = [args.pretrained]
|
| 221 |
-
for model_path in all_model_checkpoints:
|
| 222 |
-
args.checkpoint_path = os.path.dirname(model_path)
|
| 223 |
-
model, model_cfg = create_model(
|
| 224 |
-
amodel,
|
| 225 |
-
tmodel,
|
| 226 |
-
pretrained,
|
| 227 |
-
precision='fp32',
|
| 228 |
-
device=device,
|
| 229 |
-
jit=False,
|
| 230 |
-
force_quick_gelu=False,
|
| 231 |
-
openai_model_cache_dir=os.path.expanduser(args.openai_model_cache_dir),
|
| 232 |
-
skip_params=False,
|
| 233 |
-
enable_fusion=args.enable_fusion,
|
| 234 |
-
fusion_type=args.fusion_type
|
| 235 |
-
)
|
| 236 |
-
|
| 237 |
-
# load model
|
| 238 |
-
checkpoint = torch.load(model_path, map_location=device)
|
| 239 |
-
if "epoch" in checkpoint:
|
| 240 |
-
# resuming a train checkpoint w/ epoch and optimizer state
|
| 241 |
-
start_epoch = checkpoint["epoch"]
|
| 242 |
-
sd = checkpoint["state_dict"]
|
| 243 |
-
if next(iter(sd.items()))[0].startswith(
|
| 244 |
-
"module"
|
| 245 |
-
):
|
| 246 |
-
sd = {k[len("module."):]: v for k, v in sd.items()}
|
| 247 |
-
model.load_state_dict(sd)
|
| 248 |
-
logging.info(
|
| 249 |
-
f"=> resuming checkpoint '{model_path}' (epoch {start_epoch})"
|
| 250 |
-
)
|
| 251 |
-
else:
|
| 252 |
-
# loading a bare (model only) checkpoint for fine-tune or evaluation
|
| 253 |
-
model.load_state_dict(checkpoint)
|
| 254 |
-
start_epoch = 0
|
| 255 |
-
|
| 256 |
-
model.to(device)
|
| 257 |
-
model.eval()
|
| 258 |
-
for param in model.parameters():
|
| 259 |
-
param.requires_grad = False
|
| 260 |
-
|
| 261 |
-
evaluate_zeroshot(model, data, start_epoch, args, writer)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/training/data.py
CHANGED
|
@@ -1,94 +1,15 @@
|
|
| 1 |
-
import
|
| 2 |
-
|
| 3 |
-
import logging
|
| 4 |
-
import math
|
| 5 |
-
import os
|
| 6 |
-
import random
|
| 7 |
-
import h5py
|
| 8 |
-
from dataclasses import dataclass
|
| 9 |
-
import braceexpand
|
| 10 |
import numpy as np
|
| 11 |
-
import pandas as pd
|
| 12 |
import torch
|
| 13 |
import torch.nn.functional as F
|
| 14 |
-
import torchvision.datasets as datasets
|
| 15 |
import torchvision.transforms
|
| 16 |
-
import webdataset as wds
|
| 17 |
-
from PIL import Image
|
| 18 |
-
from torch.utils.data import Dataset, DataLoader, SubsetRandomSampler
|
| 19 |
-
from torch.utils.data.distributed import DistributedSampler
|
| 20 |
-
from functools import partial
|
| 21 |
-
from pathlib import Path
|
| 22 |
-
import wget
|
| 23 |
-
import tempfile
|
| 24 |
-
import copy
|
| 25 |
-
from contextlib import suppress
|
| 26 |
-
|
| 27 |
-
from utils.laion_clap.clap_module.utils import get_tar_path_from_dataset_name, dataset_split
|
| 28 |
-
from utils.laion_clap.clap_module.utils import load_p, load_class_label
|
| 29 |
-
from utils.laion_clap.clap_module import tokenize as clip_tokenizer
|
| 30 |
-
from transformers import BertTokenizer
|
| 31 |
-
from transformers import RobertaTokenizer
|
| 32 |
-
from transformers import BartTokenizer
|
| 33 |
-
|
| 34 |
-
try:
|
| 35 |
-
import horovod.torch as hvd
|
| 36 |
-
except ImportError:
|
| 37 |
-
hvd = None
|
| 38 |
|
| 39 |
try:
|
| 40 |
import torchaudio
|
| 41 |
except ImportError:
|
| 42 |
torchaudio = None
|
| 43 |
|
| 44 |
-
bert_tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
|
| 45 |
-
roberta_tokenizer = RobertaTokenizer.from_pretrained("roberta-base")
|
| 46 |
-
bart_tokenizer = BartTokenizer.from_pretrained("facebook/bart-base")
|
| 47 |
-
|
| 48 |
-
def tokenizer(text, tmodel="roberta", max_length=77):
|
| 49 |
-
"""tokenizer for different models
|
| 50 |
-
tmodel is default to roberta as it is the best model for our task
|
| 51 |
-
max_length is default to 77 from the OpenAI CLIP parameters
|
| 52 |
-
We assume text to be a single string, but it can also be a list of strings
|
| 53 |
-
"""
|
| 54 |
-
if tmodel == "transformer":
|
| 55 |
-
return clip_tokenizer(text).squeeze(0)
|
| 56 |
-
|
| 57 |
-
elif tmodel == "bert":
|
| 58 |
-
result = bert_tokenizer(
|
| 59 |
-
text,
|
| 60 |
-
padding="max_length",
|
| 61 |
-
truncation=True,
|
| 62 |
-
max_length=max_length,
|
| 63 |
-
return_tensors="pt",
|
| 64 |
-
)
|
| 65 |
-
return {k: v.squeeze(0) for k, v in result.items()}
|
| 66 |
-
|
| 67 |
-
elif tmodel == "roberta":
|
| 68 |
-
result = roberta_tokenizer(
|
| 69 |
-
text,
|
| 70 |
-
padding="max_length",
|
| 71 |
-
truncation=True,
|
| 72 |
-
max_length=max_length,
|
| 73 |
-
return_tensors="pt",
|
| 74 |
-
)
|
| 75 |
-
return {k: v.squeeze(0) for k, v in result.items()}
|
| 76 |
-
|
| 77 |
-
elif tmodel == "bart":
|
| 78 |
-
result = bart_tokenizer(
|
| 79 |
-
text,
|
| 80 |
-
padding="max_length",
|
| 81 |
-
truncation=True,
|
| 82 |
-
max_length=max_length,
|
| 83 |
-
return_tensors="pt",
|
| 84 |
-
)
|
| 85 |
-
return {k: v.squeeze(0) for k, v in result.items()}
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
# initizlied the audioset map
|
| 89 |
-
_AUDIOSET_MAP_PATH = os.path.join(Path(__file__).parent, "audioset_textmap.npy")
|
| 90 |
-
_AUDIOSET_MAP = np.load(_AUDIOSET_MAP_PATH, allow_pickle=True)
|
| 91 |
-
|
| 92 |
|
| 93 |
def int16_to_float32(x):
|
| 94 |
return (x / 32767.0).astype(np.float32)
|
|
@@ -99,267 +20,6 @@ def float32_to_int16(x):
|
|
| 99 |
return (x * 32767.).astype(np.int16)
|
| 100 |
|
| 101 |
|
| 102 |
-
def int16_to_float32_torch(x):
|
| 103 |
-
return (x / 32767.0).type(torch.float32)
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
def float32_to_int16_torch(x):
|
| 107 |
-
x = torch.clamp(x, min=-1., max=1.)
|
| 108 |
-
return (x * 32767.).type(torch.int16)
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
# For Toy Dataset
|
| 112 |
-
class ToyDataset(Dataset):
|
| 113 |
-
def __init__(self, index_path, ipc, config, eval_mode=False):
|
| 114 |
-
"""Toy Dataset for testing the audioset input with text labels
|
| 115 |
-
Parameters
|
| 116 |
-
----------
|
| 117 |
-
index_path: str
|
| 118 |
-
the link to the h5 file of each audio
|
| 119 |
-
idc: str
|
| 120 |
-
the link to the npy file, the number of samples in each class
|
| 121 |
-
config: dict
|
| 122 |
-
the audio cfg file
|
| 123 |
-
eval_model (bool): to indicate if the dataset is a testing dataset
|
| 124 |
-
"""
|
| 125 |
-
self.audio_cfg = config["audio_cfg"]
|
| 126 |
-
self.text_cfg = config["text_cfg"]
|
| 127 |
-
self.fp = h5py.File(index_path, "r")
|
| 128 |
-
self.ipc = np.load(ipc, allow_pickle=True)
|
| 129 |
-
self.total_size = len(self.fp["audio_name"])
|
| 130 |
-
self.classes_num = self.audio_cfg["class_num"]
|
| 131 |
-
self.eval_mode = eval_mode
|
| 132 |
-
|
| 133 |
-
if not eval_mode:
|
| 134 |
-
self.generate_queue()
|
| 135 |
-
else:
|
| 136 |
-
self.queue = []
|
| 137 |
-
for i in range(self.total_size):
|
| 138 |
-
target = self.fp["target"][i]
|
| 139 |
-
if np.sum(target) > 0:
|
| 140 |
-
self.queue.append(i)
|
| 141 |
-
self.total_size = len(self.queue)
|
| 142 |
-
logging.info("total dataset size: %d" % (self.total_size))
|
| 143 |
-
logging.info("class num: %d" % (self.classes_num))
|
| 144 |
-
|
| 145 |
-
def time_shifting(self, x):
|
| 146 |
-
frame_num = len(x)
|
| 147 |
-
shift_len = random.randint(0, frame_num - 1)
|
| 148 |
-
new_sample = np.concatenate([x[shift_len:], x[:shift_len]], axis=0)
|
| 149 |
-
return new_sample
|
| 150 |
-
|
| 151 |
-
def generate_queue(self):
|
| 152 |
-
self.queue = []
|
| 153 |
-
while len(self.queue) < self.total_size:
|
| 154 |
-
class_set = [*range(self.classes_num)]
|
| 155 |
-
random.shuffle(class_set)
|
| 156 |
-
self.queue += [
|
| 157 |
-
self.ipc[d][random.randint(0, len(self.ipc[d]) - 1)] for d in class_set
|
| 158 |
-
]
|
| 159 |
-
self.queue = self.queue[: self.total_size]
|
| 160 |
-
|
| 161 |
-
logging.info("queue regenerated:%s" % (self.queue[-5:]))
|
| 162 |
-
|
| 163 |
-
def crop_wav(self, x):
|
| 164 |
-
crop_size = self.audio_cfg["crop_size"]
|
| 165 |
-
crop_pos = random.randint(0, len(x) - crop_size - 1)
|
| 166 |
-
return x[crop_pos: crop_pos + crop_size]
|
| 167 |
-
|
| 168 |
-
def prompt_text(self, target):
|
| 169 |
-
events = _AUDIOSET_MAP[np.where(target > 0)]
|
| 170 |
-
event_text = "The sounds of " + ", ".join(events[:-1]) + " and " + events[-1]
|
| 171 |
-
text = tokenizer(event_text)[0]
|
| 172 |
-
return text
|
| 173 |
-
|
| 174 |
-
def __getitem__(self, index):
|
| 175 |
-
"""Load waveform, text, and target of an audio clip
|
| 176 |
-
|
| 177 |
-
Parameters
|
| 178 |
-
----------
|
| 179 |
-
index: int
|
| 180 |
-
the index number
|
| 181 |
-
Return
|
| 182 |
-
------
|
| 183 |
-
output: dict {
|
| 184 |
-
"hdf5_path": str,
|
| 185 |
-
"index_in_hdf5": int,
|
| 186 |
-
"audio_name": str,
|
| 187 |
-
"waveform": list (audio_length,),
|
| 188 |
-
"target": list (class_num, ),
|
| 189 |
-
"text": torch.tensor (context_length,)
|
| 190 |
-
}
|
| 191 |
-
the output dictionary
|
| 192 |
-
"""
|
| 193 |
-
s_index = self.queue[index]
|
| 194 |
-
|
| 195 |
-
audio_name = self.fp["audio_name"][s_index].decode()
|
| 196 |
-
# Hardcode here CHANGE
|
| 197 |
-
hdf5_path = (
|
| 198 |
-
self.fp["hdf5_path"][s_index]
|
| 199 |
-
.decode()
|
| 200 |
-
.replace(
|
| 201 |
-
"../workspace",
|
| 202 |
-
"/home/la/kechen/Research/ke_zsasp/workspace",
|
| 203 |
-
)
|
| 204 |
-
)
|
| 205 |
-
r_idx = self.fp["index_in_hdf5"][s_index]
|
| 206 |
-
target = self.fp["target"][s_index].astype(np.float32)
|
| 207 |
-
text = self.prompt_text(target)
|
| 208 |
-
with h5py.File(hdf5_path, "r") as f:
|
| 209 |
-
waveform = int16_to_float32(f["waveform"][r_idx])[
|
| 210 |
-
: self.audio_cfg["clip_samples"]
|
| 211 |
-
]
|
| 212 |
-
assert (
|
| 213 |
-
len(waveform) == self.audio_cfg["clip_samples"]
|
| 214 |
-
), "The sample length is not match"
|
| 215 |
-
# Time shift
|
| 216 |
-
# if (self.config.enable_time_shift) and (not self.eval_mode):
|
| 217 |
-
# waveform = self.time_shifting(waveform)
|
| 218 |
-
# # Label Enhance
|
| 219 |
-
# if (self.config.crop_size is not None) and (not self.eval_mode):
|
| 220 |
-
# waveform = self.crop_wav(waveform)
|
| 221 |
-
# # the label enhance rate is fixed 0.5
|
| 222 |
-
# if (self.config.enable_label_enhance) and (not self.eval_mode) and random.random() < 0.5:
|
| 223 |
-
# kidx = np.where(target)[0]
|
| 224 |
-
# for k in kidx:
|
| 225 |
-
# for add_key in self.class_map[k][1]:
|
| 226 |
-
# target[add_key] = 1.0
|
| 227 |
-
# if len(self.class_map[k][2]) > 0:
|
| 228 |
-
# add_key = random.choice(self.class_map[k][2])
|
| 229 |
-
# target[add_key] = 1.0
|
| 230 |
-
|
| 231 |
-
# missing the text input
|
| 232 |
-
mel_spec = get_mel(torch.from_numpy(waveform), self.audio_cfg)[None, :, :]
|
| 233 |
-
mel_spec = torch.cat([mel_spec, mel_spec.clone(), mel_spec.clone(), mel_spec.clone()], dim=0).cpu().numpy()
|
| 234 |
-
longer = random.choice([True, False])
|
| 235 |
-
if longer == False:
|
| 236 |
-
mel_spec[1:, :, :] = 0.0
|
| 237 |
-
data_dict = {
|
| 238 |
-
"hdf5_path": hdf5_path,
|
| 239 |
-
"index_in_hdf5": r_idx,
|
| 240 |
-
"audio_name": audio_name,
|
| 241 |
-
"waveform": waveform,
|
| 242 |
-
"class_label": target,
|
| 243 |
-
"text": text,
|
| 244 |
-
"longer": longer,
|
| 245 |
-
"mel_fusion": mel_spec
|
| 246 |
-
}
|
| 247 |
-
return data_dict
|
| 248 |
-
|
| 249 |
-
def __len__(self):
|
| 250 |
-
return self.total_size
|
| 251 |
-
|
| 252 |
-
@dataclass
|
| 253 |
-
class DataInfo:
|
| 254 |
-
dataloader: DataLoader
|
| 255 |
-
sampler: DistributedSampler
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
def get_dataset_size(shards, sizefilepath_=None, is_local=True):
|
| 259 |
-
if isinstance(shards, list):
|
| 260 |
-
size_list = []
|
| 261 |
-
for s in shards:
|
| 262 |
-
size_list.append(
|
| 263 |
-
get_dataset_size(s, sizefilepath_=sizefilepath_, is_local=is_local)[0]
|
| 264 |
-
)
|
| 265 |
-
else:
|
| 266 |
-
if not is_local:
|
| 267 |
-
for n in dataset_split.keys():
|
| 268 |
-
if n in shards.split("/"):
|
| 269 |
-
break
|
| 270 |
-
for s in dataset_split[n]:
|
| 271 |
-
if s in shards.split("/"):
|
| 272 |
-
break
|
| 273 |
-
sizefilepath_ = f"./json_files/{n}/{s}/sizes.json"
|
| 274 |
-
shards_list = list(braceexpand.braceexpand(shards))
|
| 275 |
-
dir_path = os.path.dirname(shards)
|
| 276 |
-
if sizefilepath_ is not None:
|
| 277 |
-
sizes = json.load(open(sizefilepath_, "r"))
|
| 278 |
-
total_size = sum(
|
| 279 |
-
[
|
| 280 |
-
int(sizes[os.path.basename(shard.replace(".tar -", ".tar"))])
|
| 281 |
-
for shard in shards_list
|
| 282 |
-
]
|
| 283 |
-
)
|
| 284 |
-
else:
|
| 285 |
-
sizes_filename = os.path.join(dir_path, "sizes.json")
|
| 286 |
-
len_filename = os.path.join(dir_path, "__len__")
|
| 287 |
-
if os.path.exists(sizes_filename):
|
| 288 |
-
sizes = json.load(open(sizes_filename, "r"))
|
| 289 |
-
total_size = sum(
|
| 290 |
-
[int(sizes[os.path.basename(shard)]) for shard in shards_list]
|
| 291 |
-
)
|
| 292 |
-
elif os.path.exists(len_filename):
|
| 293 |
-
# FIXME this used to be eval(open(...)) but that seemed rather unsafe
|
| 294 |
-
total_size = ast.literal_eval(open(len_filename, "r").read())
|
| 295 |
-
else:
|
| 296 |
-
raise Exception(
|
| 297 |
-
f"Cannot find sizes file for dataset {shards}. Please specify the path to the file."
|
| 298 |
-
)
|
| 299 |
-
# total_size = None # num samples undefined
|
| 300 |
-
# some common dataset sizes (at time of authors last download)
|
| 301 |
-
# cc3m-train: 2905954
|
| 302 |
-
# cc12m: 10968539
|
| 303 |
-
# LAION-400m: 407332084
|
| 304 |
-
num_shards = len(shards_list)
|
| 305 |
-
if isinstance(shards, list):
|
| 306 |
-
return sum(size_list), len(shards)
|
| 307 |
-
else:
|
| 308 |
-
return total_size, num_shards
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
def count_samples(dataloader):
|
| 312 |
-
os.environ["WDS_EPOCH"] = "0"
|
| 313 |
-
n_elements, n_batches = 0, 0
|
| 314 |
-
for images, texts in dataloader:
|
| 315 |
-
n_batches += 1
|
| 316 |
-
n_elements += len(images)
|
| 317 |
-
assert len(images) == len(texts)
|
| 318 |
-
return n_elements, n_batches
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
def log_and_continue(exn):
|
| 322 |
-
"""Call in an exception handler to ignore any exception, isssue a warning, and continue."""
|
| 323 |
-
logging.warning(f"Handling webdataset error ({repr(exn)}). Ignoring.")
|
| 324 |
-
return True
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
_SHARD_SHUFFLE_SIZE = 2000
|
| 328 |
-
_SHARD_SHUFFLE_INITIAL = 500
|
| 329 |
-
_SAMPLE_SHUFFLE_SIZE = 5000
|
| 330 |
-
_SAMPLE_SHUFFLE_INITIAL = 1000
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
def sample_prop(sizefile, inputs, proportion, is_local=True):
|
| 334 |
-
"""
|
| 335 |
-
Sample a proportion of the data.
|
| 336 |
-
"""
|
| 337 |
-
file_path_dict = {
|
| 338 |
-
os.path.split(inputs[i])[1]: os.path.split(inputs[i])[0]
|
| 339 |
-
for i in range(len(inputs))
|
| 340 |
-
}
|
| 341 |
-
sampled_filepath_dict = {}
|
| 342 |
-
sampled_size_dict = {}
|
| 343 |
-
if not is_local:
|
| 344 |
-
if os.path.exists("sizes.json"):
|
| 345 |
-
os.remove("sizes.json")
|
| 346 |
-
wget.download(sizefile, "sizes.json")
|
| 347 |
-
sizefile = "sizes.json"
|
| 348 |
-
with open(sizefile, "r", encoding="UTF-8") as f:
|
| 349 |
-
load_dict = json.load(f)
|
| 350 |
-
L = int(len(file_path_dict) * proportion)
|
| 351 |
-
subkeys = random.sample(file_path_dict.keys(), L)
|
| 352 |
-
for k in subkeys:
|
| 353 |
-
sampled_size_dict[k] = load_dict[k]
|
| 354 |
-
sampled_filepath_dict[k] = file_path_dict[k]
|
| 355 |
-
return (
|
| 356 |
-
sum(sampled_size_dict.values()),
|
| 357 |
-
L,
|
| 358 |
-
[os.path.join(v, k) for k, v in sampled_filepath_dict.items()],
|
| 359 |
-
sampled_size_dict,
|
| 360 |
-
)
|
| 361 |
-
|
| 362 |
-
|
| 363 |
def get_mel(audio_data, audio_cfg):
|
| 364 |
# mel shape: (n_mels, T)
|
| 365 |
mel_tf = torchaudio.transforms.MelSpectrogram(
|
|
@@ -376,24 +36,8 @@ def get_mel(audio_data, audio_cfg):
|
|
| 376 |
f_min=audio_cfg['fmin'],
|
| 377 |
f_max=audio_cfg['fmax']
|
| 378 |
).to(audio_data.device)
|
| 379 |
-
|
| 380 |
mel = mel_tf(audio_data)
|
| 381 |
-
# Align to librosa:
|
| 382 |
-
# librosa_melspec = librosa.feature.melspectrogram(
|
| 383 |
-
# waveform,
|
| 384 |
-
# sr=audio_cfg['sample_rate'],
|
| 385 |
-
# n_fft=audio_cfg['window_size'],
|
| 386 |
-
# hop_length=audio_cfg['hop_size'],
|
| 387 |
-
# win_length=audio_cfg['window_size'],
|
| 388 |
-
# center=True,
|
| 389 |
-
# pad_mode="reflect",
|
| 390 |
-
# power=2.0,
|
| 391 |
-
# n_mels=audio_cfg['mel_bins'],
|
| 392 |
-
# norm=None,
|
| 393 |
-
# htk=True,
|
| 394 |
-
# f_min=audio_cfg['fmin'],
|
| 395 |
-
# f_max=audio_cfg['fmax']
|
| 396 |
-
# )
|
| 397 |
# we use log mel spectrogram as input
|
| 398 |
mel = torchaudio.transforms.AmplitudeToDB(top_db=None)(mel)
|
| 399 |
return mel.T # (T, n_mels)
|
|
@@ -431,10 +75,6 @@ def get_audio_features(sample, audio_data, max_len, data_truncating, data_fillin
|
|
| 431 |
longer = torch.tensor([False])
|
| 432 |
else:
|
| 433 |
ranges = np.array_split(list(range(0, total_frames - chunk_frames + 1)), 3)
|
| 434 |
-
# print('total_frames-chunk_frames:', total_frames-chunk_frames,
|
| 435 |
-
# 'len(audio_data):', len(audio_data),
|
| 436 |
-
# 'chunk_frames:', chunk_frames,
|
| 437 |
-
# 'total_frames:', total_frames)
|
| 438 |
if len(ranges[1]) == 0:
|
| 439 |
# if the audio is too short, we just use the first chunk
|
| 440 |
ranges[1] = [0]
|
|
@@ -452,7 +92,6 @@ def get_audio_features(sample, audio_data, max_len, data_truncating, data_fillin
|
|
| 452 |
|
| 453 |
# shrink the mel
|
| 454 |
mel_shrink = torchvision.transforms.Resize(size=[chunk_frames, audio_cfg['mel_bins']])(mel[None])[0]
|
| 455 |
-
# logging.info(f"mel_shrink.shape: {mel_shrink.shape}")
|
| 456 |
|
| 457 |
# stack
|
| 458 |
mel_fusion = torch.stack([mel_shrink, mel_chunk_front, mel_chunk_middle, mel_chunk_back], dim=0)
|
|
@@ -472,8 +111,6 @@ def get_audio_features(sample, audio_data, max_len, data_truncating, data_fillin
|
|
| 472 |
if data_filling == "repeatpad":
|
| 473 |
n_repeat = int(max_len / len(audio_data))
|
| 474 |
audio_data = audio_data.repeat(n_repeat)
|
| 475 |
-
# audio_data = audio_data.unsqueeze(0).unsqueeze(0).unsqueeze(0)
|
| 476 |
-
# audio_data = F.interpolate(audio_data,size=max_len,mode="bicubic")[0,0,0]
|
| 477 |
audio_data = F.pad(
|
| 478 |
audio_data,
|
| 479 |
(0, max_len - len(audio_data)),
|
|
@@ -504,392 +141,3 @@ def get_audio_features(sample, audio_data, max_len, data_truncating, data_fillin
|
|
| 504 |
sample["waveform"] = audio_data
|
| 505 |
|
| 506 |
return sample
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
def select_text(json_dict_raw, text_augment_selection):
|
| 510 |
-
# For selecting augmented text from dataset
|
| 511 |
-
if text_augment_selection is None or text_augment_selection == "none":
|
| 512 |
-
texts = json_dict_raw["text"]
|
| 513 |
-
elif text_augment_selection == "all":
|
| 514 |
-
if "text_augment_all" in json_dict_raw.keys():
|
| 515 |
-
texts = json_dict_raw["text_augment_all"]
|
| 516 |
-
else:
|
| 517 |
-
texts = json_dict_raw["text"]
|
| 518 |
-
elif text_augment_selection == "augment_only":
|
| 519 |
-
if "text_augment_all" in json_dict_raw.keys():
|
| 520 |
-
if json_dict_raw["text_augment_t5"] is None:
|
| 521 |
-
texts = json_dict_raw["text"]
|
| 522 |
-
else:
|
| 523 |
-
texts = json_dict_raw["text_augment_t5"]
|
| 524 |
-
else:
|
| 525 |
-
texts = json_dict_raw["text"]
|
| 526 |
-
else:
|
| 527 |
-
raise NotImplementedError(
|
| 528 |
-
f"text_augment_selection {text_augment_selection} not implemented"
|
| 529 |
-
)
|
| 530 |
-
return texts
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
def preprocess_single(
|
| 534 |
-
sample,
|
| 535 |
-
audio_ext,
|
| 536 |
-
text_ext,
|
| 537 |
-
max_len,
|
| 538 |
-
audio_cfg,
|
| 539 |
-
tmodel,
|
| 540 |
-
class_index_dict,
|
| 541 |
-
data_filling,
|
| 542 |
-
data_truncating,
|
| 543 |
-
text_augment_selection,
|
| 544 |
-
):
|
| 545 |
-
"""
|
| 546 |
-
Preprocess a single sample for wdsdataloader.
|
| 547 |
-
"""
|
| 548 |
-
audio_data, orig_sr = sample[audio_ext]
|
| 549 |
-
audio_data = int16_to_float32_torch(float32_to_int16_torch(audio_data[0]))
|
| 550 |
-
|
| 551 |
-
sample = get_audio_features(sample, audio_data, max_len, data_truncating, data_filling, audio_cfg)
|
| 552 |
-
del sample[audio_ext]
|
| 553 |
-
|
| 554 |
-
json_dict_raw = sample[text_ext]
|
| 555 |
-
|
| 556 |
-
texts = select_text(json_dict_raw, text_augment_selection)
|
| 557 |
-
sample["full_text"] = texts
|
| 558 |
-
|
| 559 |
-
if isinstance(texts, list) and isinstance(texts[0], str) and len(texts) > 1:
|
| 560 |
-
texts = random.choice(texts)
|
| 561 |
-
sample["raw_text"] = texts
|
| 562 |
-
sample["text"] = tokenizer(texts, tmodel=tmodel) # text shape: [num_token]
|
| 563 |
-
if class_index_dict is not None:
|
| 564 |
-
# https://stackoverflow.com/questions/48004243/how-to-share-large-read-only-dictionary-list-across-processes-in-multiprocessing
|
| 565 |
-
# https://stackoverflow.com/questions/45693949/storing-strings-in-a-multiprocessing-sharedctypes-array
|
| 566 |
-
|
| 567 |
-
# in case the re-written version is wrong, here is the old version:
|
| 568 |
-
# sample["class_label"] = np.zeros(len(class_index_dict.keys()))
|
| 569 |
-
# for x in json_dict_raw["tag"]:
|
| 570 |
-
# sample["class_label"][class_index_dict[x]] = 1
|
| 571 |
-
# sample["class_label"] = torch.tensor(sample["class_label"]).float()
|
| 572 |
-
|
| 573 |
-
class_labels = np.zeros(len(class_index_dict))
|
| 574 |
-
class_labels[np.in1d(list(class_index_dict.keys()), json_dict_raw["tag"])] = 1
|
| 575 |
-
sample["class_label"] = torch.tensor(class_labels).float()
|
| 576 |
-
|
| 577 |
-
del sample[text_ext]
|
| 578 |
-
sample["audio_name"] = sample["__key__"].split("/")[-1] + "." + audio_ext
|
| 579 |
-
sample["text_name"] = sample["__key__"].split("/")[-1] + "." + text_ext
|
| 580 |
-
sample["audio_orig_sr"] = orig_sr
|
| 581 |
-
return sample
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
def collate_fn_with_preprocess(batch,
|
| 585 |
-
audio_ext,
|
| 586 |
-
text_ext,
|
| 587 |
-
max_len,
|
| 588 |
-
audio_cfg,
|
| 589 |
-
args,
|
| 590 |
-
):
|
| 591 |
-
"""
|
| 592 |
-
Collate function for wdsdataloader.
|
| 593 |
-
batch: a list of dict, each dict is a sample
|
| 594 |
-
"""
|
| 595 |
-
|
| 596 |
-
class_index_dict = copy.deepcopy(args.class_index_dict) # To avoid deadlock in multiprocessing
|
| 597 |
-
data_filling = args.data_filling
|
| 598 |
-
data_truncating = args.data_truncating
|
| 599 |
-
text_augment_selection = args.text_augment_selection
|
| 600 |
-
tmodel = args.tmodel
|
| 601 |
-
|
| 602 |
-
# concatenate values in each dictionary. if it is a tensor, concatenate. if it is a list, extend.
|
| 603 |
-
data_preprocessed = []
|
| 604 |
-
|
| 605 |
-
for sample in batch:
|
| 606 |
-
data_preprocessed.append(
|
| 607 |
-
preprocess_single(sample, audio_ext, text_ext, max_len, audio_cfg, tmodel, class_index_dict, data_filling,
|
| 608 |
-
data_truncating, text_augment_selection))
|
| 609 |
-
|
| 610 |
-
batch_dict = {}
|
| 611 |
-
for k in data_preprocessed[0].keys():
|
| 612 |
-
if isinstance(data_preprocessed[0][k], dict): # dealwith bert tokenizer output
|
| 613 |
-
batch_dict[k] = {}
|
| 614 |
-
for kk in data_preprocessed[0][k].keys():
|
| 615 |
-
tmp = []
|
| 616 |
-
for i in range(len(data_preprocessed)):
|
| 617 |
-
tmp.append(data_preprocessed[i][k][kk])
|
| 618 |
-
batch_dict[k][kk] = torch.vstack(tmp)
|
| 619 |
-
elif isinstance(data_preprocessed[0][k], torch.Tensor):
|
| 620 |
-
batch_dict[k] = torch.stack([sample[k] for sample in data_preprocessed])
|
| 621 |
-
elif isinstance(data_preprocessed[0][k], np.ndarray):
|
| 622 |
-
batch_dict[k] = torch.tensor(np.stack([sample[k] for sample in data_preprocessed]))
|
| 623 |
-
else:
|
| 624 |
-
batch_dict[k] = [sample[k] for sample in data_preprocessed]
|
| 625 |
-
del data_preprocessed
|
| 626 |
-
return batch_dict
|
| 627 |
-
|
| 628 |
-
|
| 629 |
-
def get_wds_dataset(
|
| 630 |
-
args,
|
| 631 |
-
model_cfg,
|
| 632 |
-
is_train,
|
| 633 |
-
audio_ext="flac",
|
| 634 |
-
text_ext="json",
|
| 635 |
-
max_len=480000,
|
| 636 |
-
proportion=1.0,
|
| 637 |
-
sizefilepath_=None,
|
| 638 |
-
is_local=None,
|
| 639 |
-
):
|
| 640 |
-
"""
|
| 641 |
-
Get a dataset for wdsdataloader.
|
| 642 |
-
"""
|
| 643 |
-
if is_local is None and (not args.remotedata is None):
|
| 644 |
-
is_local = not args.remotedata
|
| 645 |
-
|
| 646 |
-
input_shards = args.train_data if is_train else args.val_data
|
| 647 |
-
assert input_shards is not None
|
| 648 |
-
|
| 649 |
-
if not sizefilepath_ is None:
|
| 650 |
-
sizefilepath = sizefilepath_
|
| 651 |
-
else:
|
| 652 |
-
sizefilepath = os.path.join(os.path.dirname(input_shards[0]), "sizes.json")
|
| 653 |
-
|
| 654 |
-
if proportion != 1.0:
|
| 655 |
-
num_samples, num_shards, input_shards, _ = sample_prop(
|
| 656 |
-
sizefilepath, input_shards, proportion, is_local=is_local
|
| 657 |
-
)
|
| 658 |
-
else:
|
| 659 |
-
num_samples, num_shards = get_dataset_size(
|
| 660 |
-
input_shards, sizefilepath_=sizefilepath_, is_local=is_local
|
| 661 |
-
)
|
| 662 |
-
|
| 663 |
-
if not num_samples:
|
| 664 |
-
if is_train:
|
| 665 |
-
num_samples = args.train_num_samples
|
| 666 |
-
if not num_samples:
|
| 667 |
-
raise RuntimeError(
|
| 668 |
-
"Currently, number of dataset samples must be specified for training dataset. "
|
| 669 |
-
"Please specify via `--train-num-samples` if no dataset length info present."
|
| 670 |
-
)
|
| 671 |
-
else:
|
| 672 |
-
num_samples = (
|
| 673 |
-
args.val_num_samples or 0
|
| 674 |
-
) # eval will just exhaust the iterator if not specified
|
| 675 |
-
|
| 676 |
-
pipeline = [wds.SimpleShardList(input_shards)]
|
| 677 |
-
# at this point we have an iterator over all the shards
|
| 678 |
-
# TODO: (yusong): add a if statement of distributed. If not, we don't need to split_by_node
|
| 679 |
-
if is_train or args.parallel_eval:
|
| 680 |
-
pipeline.extend(
|
| 681 |
-
[
|
| 682 |
-
wds.detshuffle(
|
| 683 |
-
bufsize=_SHARD_SHUFFLE_SIZE,
|
| 684 |
-
initial=_SHARD_SHUFFLE_INITIAL,
|
| 685 |
-
seed=args.seed,
|
| 686 |
-
),
|
| 687 |
-
wds.split_by_node,
|
| 688 |
-
wds.split_by_worker,
|
| 689 |
-
# at this point, we have an iterator over the shards assigned to each worker at each node
|
| 690 |
-
wds.tarfile_to_samples(handler=log_and_continue),
|
| 691 |
-
wds.shuffle(
|
| 692 |
-
bufsize=_SAMPLE_SHUFFLE_SIZE,
|
| 693 |
-
initial=_SAMPLE_SHUFFLE_INITIAL,
|
| 694 |
-
rng=random.Random(args.seed),
|
| 695 |
-
),
|
| 696 |
-
# wds.repeatedly, # FIXME determine if this is beneficial
|
| 697 |
-
]
|
| 698 |
-
)
|
| 699 |
-
else:
|
| 700 |
-
pipeline.extend(
|
| 701 |
-
[
|
| 702 |
-
wds.split_by_worker,
|
| 703 |
-
# at this point, we have an iterator over the shards assigned to each worker
|
| 704 |
-
wds.tarfile_to_samples(handler=log_and_continue),
|
| 705 |
-
]
|
| 706 |
-
)
|
| 707 |
-
|
| 708 |
-
pipeline.append(
|
| 709 |
-
wds.decode(wds.torch_audio),
|
| 710 |
-
)
|
| 711 |
-
|
| 712 |
-
pipeline.append(
|
| 713 |
-
wds.batched(
|
| 714 |
-
args.batch_size,
|
| 715 |
-
partial=not (is_train or args.parallel_eval),
|
| 716 |
-
collation_fn=partial(collate_fn_with_preprocess,
|
| 717 |
-
audio_ext=audio_ext,
|
| 718 |
-
text_ext=text_ext,
|
| 719 |
-
max_len=max_len,
|
| 720 |
-
audio_cfg=model_cfg['audio_cfg'],
|
| 721 |
-
args=args,
|
| 722 |
-
),
|
| 723 |
-
|
| 724 |
-
)
|
| 725 |
-
)
|
| 726 |
-
|
| 727 |
-
dataset = wds.DataPipeline(*pipeline)
|
| 728 |
-
if is_train or args.parallel_eval:
|
| 729 |
-
# (yusong): Currently parallel evaluation will be not precise as we are repeat the last few samples.
|
| 730 |
-
# (yusong): See comments below.
|
| 731 |
-
# roll over and repeat a few samples to get same number of full batches on each node
|
| 732 |
-
global_batch_size = args.batch_size * args.world_size
|
| 733 |
-
num_batches = math.ceil(num_samples / global_batch_size)
|
| 734 |
-
num_workers = max(1, args.workers)
|
| 735 |
-
num_worker_batches = math.ceil(
|
| 736 |
-
num_batches / num_workers
|
| 737 |
-
) # per dataloader worker
|
| 738 |
-
num_batches = num_worker_batches * num_workers
|
| 739 |
-
num_samples = num_batches * global_batch_size
|
| 740 |
-
dataset = dataset.with_epoch(
|
| 741 |
-
num_worker_batches
|
| 742 |
-
) # each worker is iterating over this
|
| 743 |
-
else:
|
| 744 |
-
# last batches are partial, eval is done on single (master) node
|
| 745 |
-
num_batches = math.ceil(num_samples / args.batch_size)
|
| 746 |
-
|
| 747 |
-
kwargs = {}
|
| 748 |
-
if args.horovod: # multi-node training on summit
|
| 749 |
-
kwargs["multiprocessing_context"] = "forkserver"
|
| 750 |
-
|
| 751 |
-
if is_train:
|
| 752 |
-
if args.prefetch_factor:
|
| 753 |
-
prefetch_factor = args.prefetch_factor
|
| 754 |
-
else:
|
| 755 |
-
prefetch_factor = max(2, args.batch_size // args.workers)
|
| 756 |
-
else:
|
| 757 |
-
prefetch_factor = 2
|
| 758 |
-
|
| 759 |
-
dataloader = wds.WebLoader(
|
| 760 |
-
dataset,
|
| 761 |
-
batch_size=None,
|
| 762 |
-
shuffle=False,
|
| 763 |
-
num_workers=args.workers,
|
| 764 |
-
pin_memory=True,
|
| 765 |
-
prefetch_factor=prefetch_factor,
|
| 766 |
-
**kwargs
|
| 767 |
-
)
|
| 768 |
-
|
| 769 |
-
# FIXME not clear which approach is better, with_epoch before vs after dataloader?
|
| 770 |
-
# hoping to resolve via https://github.com/webdataset/webdataset/issues/169
|
| 771 |
-
# if is_train:
|
| 772 |
-
# # roll over and repeat a few samples to get same number of full batches on each node
|
| 773 |
-
# global_batch_size = args.batch_size * args.world_size
|
| 774 |
-
# num_batches = math.ceil(num_samples / global_batch_size)
|
| 775 |
-
# num_workers = max(1, args.workers)
|
| 776 |
-
# num_batches = math.ceil(num_batches / num_workers) * num_workers
|
| 777 |
-
# num_samples = num_batches * global_batch_size
|
| 778 |
-
# dataloader = dataloader.with_epoch(num_batches)
|
| 779 |
-
# else:
|
| 780 |
-
# # last batches are partial, eval is done on single (master) node
|
| 781 |
-
# num_batches = math.ceil(num_samples / args.batch_size)
|
| 782 |
-
|
| 783 |
-
# add meta-data to dataloader instance for convenience
|
| 784 |
-
dataloader.num_batches = num_batches
|
| 785 |
-
dataloader.num_samples = num_samples
|
| 786 |
-
|
| 787 |
-
return DataInfo(dataloader, None)
|
| 788 |
-
|
| 789 |
-
|
| 790 |
-
def wds_batch_list2dict(
|
| 791 |
-
batch,
|
| 792 |
-
keys=[
|
| 793 |
-
"__url__",
|
| 794 |
-
"__key__",
|
| 795 |
-
"waveform",
|
| 796 |
-
"text",
|
| 797 |
-
"raw_text",
|
| 798 |
-
"audio_name",
|
| 799 |
-
"text_name",
|
| 800 |
-
"audio_orig_sr",
|
| 801 |
-
],
|
| 802 |
-
):
|
| 803 |
-
"""
|
| 804 |
-
Return a dictionary of the batch, with keys as the names of the fields.
|
| 805 |
-
"""
|
| 806 |
-
assert len(keys) == len(
|
| 807 |
-
batch
|
| 808 |
-
), "batch must have same number of keys as keys argument"
|
| 809 |
-
return {keys[i]: batch[i] for i in range(len(batch))}
|
| 810 |
-
|
| 811 |
-
|
| 812 |
-
|
| 813 |
-
def get_toy_dataset(args, model_cfg, is_train):
|
| 814 |
-
index_path = args.train_data if is_train else args.val_data
|
| 815 |
-
ipc_path = args.train_ipc if is_train else args.val_ipc
|
| 816 |
-
assert index_path and ipc_path
|
| 817 |
-
eval_mode = not is_train
|
| 818 |
-
dataset = ToyDataset(index_path, ipc_path, model_cfg, eval_mode=eval_mode)
|
| 819 |
-
|
| 820 |
-
num_samples = len(dataset)
|
| 821 |
-
sampler = (
|
| 822 |
-
DistributedSampler(dataset, shuffle=False)
|
| 823 |
-
if args.distributed and is_train
|
| 824 |
-
else None
|
| 825 |
-
)
|
| 826 |
-
|
| 827 |
-
dataloader = DataLoader(
|
| 828 |
-
dataset,
|
| 829 |
-
batch_size=args.batch_size,
|
| 830 |
-
shuffle=False,
|
| 831 |
-
num_workers=args.workers,
|
| 832 |
-
sampler=sampler,
|
| 833 |
-
drop_last=is_train,
|
| 834 |
-
)
|
| 835 |
-
dataloader.num_samples = num_samples
|
| 836 |
-
dataloader.num_batches = len(dataloader)
|
| 837 |
-
|
| 838 |
-
return DataInfo(dataloader, sampler)
|
| 839 |
-
|
| 840 |
-
|
| 841 |
-
def get_dataset_fn(dataset_type):
|
| 842 |
-
if dataset_type == "webdataset":
|
| 843 |
-
return get_wds_dataset
|
| 844 |
-
elif dataset_type == "toy":
|
| 845 |
-
return get_toy_dataset
|
| 846 |
-
else:
|
| 847 |
-
raise ValueError(f"Unsupported dataset type: {dataset_type}")
|
| 848 |
-
|
| 849 |
-
|
| 850 |
-
def get_data(args, model_cfg):
|
| 851 |
-
data = {}
|
| 852 |
-
|
| 853 |
-
args.class_index_dict = load_class_label(args.class_label_path)
|
| 854 |
-
|
| 855 |
-
if args.datasetinfos is None:
|
| 856 |
-
args.datasetinfos = ["train", "unbalanced_train", "balanced_train"]
|
| 857 |
-
if args.dataset_type == "webdataset":
|
| 858 |
-
args.train_data = get_tar_path_from_dataset_name(
|
| 859 |
-
args.datasetnames,
|
| 860 |
-
args.datasetinfos,
|
| 861 |
-
islocal=not args.remotedata,
|
| 862 |
-
proportion=args.dataset_proportion,
|
| 863 |
-
dataset_path=args.datasetpath,
|
| 864 |
-
full_dataset=args.full_train_dataset,
|
| 865 |
-
)
|
| 866 |
-
|
| 867 |
-
if args.full_train_dataset is None:
|
| 868 |
-
args.full_train_dataset = []
|
| 869 |
-
if args.exclude_eval_dataset is None:
|
| 870 |
-
args.exclude_eval_dataset = []
|
| 871 |
-
excluded_eval_datasets = args.full_train_dataset + args.exclude_eval_dataset
|
| 872 |
-
|
| 873 |
-
val_dataset_names = [n for n in args.datasetnames if n not in excluded_eval_datasets] \
|
| 874 |
-
if excluded_eval_datasets else args.datasetnames
|
| 875 |
-
args.val_dataset_names = val_dataset_names
|
| 876 |
-
args.val_data = get_tar_path_from_dataset_name(
|
| 877 |
-
val_dataset_names,
|
| 878 |
-
["valid", "test", "eval"],
|
| 879 |
-
islocal=not args.remotedata,
|
| 880 |
-
proportion=1,
|
| 881 |
-
dataset_path=args.datasetpath,
|
| 882 |
-
full_dataset=None,
|
| 883 |
-
)
|
| 884 |
-
|
| 885 |
-
if args.train_data:
|
| 886 |
-
data["train"] = get_dataset_fn(args.dataset_type)(
|
| 887 |
-
args, model_cfg, is_train=True
|
| 888 |
-
)
|
| 889 |
-
|
| 890 |
-
if args.val_data:
|
| 891 |
-
data["val"] = get_dataset_fn(args.dataset_type)(
|
| 892 |
-
args, model_cfg, is_train=False
|
| 893 |
-
)
|
| 894 |
-
|
| 895 |
-
return data
|
|
|
|
| 1 |
+
from contextlib import suppress
|
| 2 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
import numpy as np
|
|
|
|
| 4 |
import torch
|
| 5 |
import torch.nn.functional as F
|
|
|
|
| 6 |
import torchvision.transforms
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
try:
|
| 9 |
import torchaudio
|
| 10 |
except ImportError:
|
| 11 |
torchaudio = None
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
def int16_to_float32(x):
|
| 15 |
return (x / 32767.0).astype(np.float32)
|
|
|
|
| 20 |
return (x * 32767.).astype(np.int16)
|
| 21 |
|
| 22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
def get_mel(audio_data, audio_cfg):
|
| 24 |
# mel shape: (n_mels, T)
|
| 25 |
mel_tf = torchaudio.transforms.MelSpectrogram(
|
|
|
|
| 36 |
f_min=audio_cfg['fmin'],
|
| 37 |
f_max=audio_cfg['fmax']
|
| 38 |
).to(audio_data.device)
|
| 39 |
+
|
| 40 |
mel = mel_tf(audio_data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
# we use log mel spectrogram as input
|
| 42 |
mel = torchaudio.transforms.AmplitudeToDB(top_db=None)(mel)
|
| 43 |
return mel.T # (T, n_mels)
|
|
|
|
| 75 |
longer = torch.tensor([False])
|
| 76 |
else:
|
| 77 |
ranges = np.array_split(list(range(0, total_frames - chunk_frames + 1)), 3)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
if len(ranges[1]) == 0:
|
| 79 |
# if the audio is too short, we just use the first chunk
|
| 80 |
ranges[1] = [0]
|
|
|
|
| 92 |
|
| 93 |
# shrink the mel
|
| 94 |
mel_shrink = torchvision.transforms.Resize(size=[chunk_frames, audio_cfg['mel_bins']])(mel[None])[0]
|
|
|
|
| 95 |
|
| 96 |
# stack
|
| 97 |
mel_fusion = torch.stack([mel_shrink, mel_chunk_front, mel_chunk_middle, mel_chunk_back], dim=0)
|
|
|
|
| 111 |
if data_filling == "repeatpad":
|
| 112 |
n_repeat = int(max_len / len(audio_data))
|
| 113 |
audio_data = audio_data.repeat(n_repeat)
|
|
|
|
|
|
|
| 114 |
audio_data = F.pad(
|
| 115 |
audio_data,
|
| 116 |
(0, max_len - len(audio_data)),
|
|
|
|
| 141 |
sample["waveform"] = audio_data
|
| 142 |
|
| 143 |
return sample
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/training/distributed.py
DELETED
|
@@ -1,139 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
|
| 3 |
-
import torch
|
| 4 |
-
import socket
|
| 5 |
-
|
| 6 |
-
try:
|
| 7 |
-
import horovod.torch as hvd
|
| 8 |
-
except ImportError:
|
| 9 |
-
hvd = None
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
def is_global_master(args):
|
| 13 |
-
return args.rank == 0
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
def is_local_master(args):
|
| 17 |
-
return args.local_rank == 0
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
def is_master(args, local=False):
|
| 21 |
-
return is_local_master(args) if local else is_global_master(args)
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
def is_using_horovod():
|
| 25 |
-
# NOTE w/ horovod run, OMPI vars should be set, but w/ SLURM PMI vars will be set
|
| 26 |
-
# Differentiating between horovod and DDP use via SLURM may not be possible, so horovod arg still required...
|
| 27 |
-
ompi_vars = ["OMPI_COMM_WORLD_RANK", "OMPI_COMM_WORLD_SIZE"]
|
| 28 |
-
pmi_vars = ["PMI_RANK", "PMI_SIZE"]
|
| 29 |
-
if all([var in os.environ for var in ompi_vars]) or all([var in os.environ for var in pmi_vars]):
|
| 30 |
-
return True
|
| 31 |
-
else:
|
| 32 |
-
return False
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
def is_using_distributed():
|
| 36 |
-
if 'WORLD_SIZE' in os.environ:
|
| 37 |
-
return int(os.environ['WORLD_SIZE']) > 1
|
| 38 |
-
if 'SLURM_NTASKS' in os.environ:
|
| 39 |
-
return int(os.environ['SLURM_NTASKS']) > 1
|
| 40 |
-
return False
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
def world_info_from_env():
|
| 44 |
-
local_rank = 0
|
| 45 |
-
for v in ('SLURM_LOCALID', 'MPI_LOCALRANKID', 'OMPI_COMM_WORLD_LOCAL_RANK', 'LOCAL_RANK'):
|
| 46 |
-
if v in os.environ:
|
| 47 |
-
local_rank = int(os.environ[v])
|
| 48 |
-
break
|
| 49 |
-
global_rank = 0
|
| 50 |
-
for v in ('SLURM_PROCID', 'PMI_RANK', 'OMPI_COMM_WORLD_RANK', 'RANK'):
|
| 51 |
-
if v in os.environ:
|
| 52 |
-
global_rank = int(os.environ[v])
|
| 53 |
-
break
|
| 54 |
-
world_size = 1
|
| 55 |
-
for v in ('SLURM_NTASKS', 'PMI_SIZE', 'OMPI_COMM_WORLD_SIZE', 'WORLD_SIZE'):
|
| 56 |
-
if v in os.environ:
|
| 57 |
-
world_size = int(os.environ[v])
|
| 58 |
-
break
|
| 59 |
-
|
| 60 |
-
return local_rank, global_rank, world_size
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
def init_distributed_device(args):
|
| 64 |
-
# Distributed training = training on more than one GPU.
|
| 65 |
-
# Works in both single and multi-node scenarios.
|
| 66 |
-
args.distributed = False
|
| 67 |
-
args.world_size = 1
|
| 68 |
-
args.rank = 0 # global rank
|
| 69 |
-
args.local_rank = 0
|
| 70 |
-
if args.horovod:
|
| 71 |
-
assert hvd is not None, "Horovod is not installed"
|
| 72 |
-
hvd.init()
|
| 73 |
-
world_size = int(os.environ['OMPI_COMM_WORLD_SIZE'])
|
| 74 |
-
world_rank = int(os.environ['OMPI_COMM_WORLD_RANK'])
|
| 75 |
-
local_rank = int(os.environ['OMPI_COMM_WORLD_LOCAL_RANK'])
|
| 76 |
-
args.local_rank = local_rank
|
| 77 |
-
args.rank = world_rank
|
| 78 |
-
args.world_size = world_size
|
| 79 |
-
# args.local_rank = int(hvd.local_rank())
|
| 80 |
-
# args.rank = hvd.rank()
|
| 81 |
-
# args.world_size = hvd.size()
|
| 82 |
-
args.distributed = True
|
| 83 |
-
os.environ['LOCAL_RANK'] = str(args.local_rank)
|
| 84 |
-
os.environ['RANK'] = str(args.rank)
|
| 85 |
-
os.environ['WORLD_SIZE'] = str(args.world_size)
|
| 86 |
-
print(f"Distributed training: local_rank={args.local_rank}, "
|
| 87 |
-
f"rank={args.rank}, world_size={args.world_size}, "
|
| 88 |
-
f"hostname={socket.gethostname()}, pid={os.getpid()}")
|
| 89 |
-
elif is_using_distributed():
|
| 90 |
-
if 'SLURM_PROCID' in os.environ:
|
| 91 |
-
# DDP via SLURM
|
| 92 |
-
args.local_rank, args.rank, args.world_size = world_info_from_env()
|
| 93 |
-
# SLURM var -> torch.distributed vars in case needed
|
| 94 |
-
os.environ['LOCAL_RANK'] = str(args.local_rank)
|
| 95 |
-
os.environ['RANK'] = str(args.rank)
|
| 96 |
-
os.environ['WORLD_SIZE'] = str(args.world_size)
|
| 97 |
-
torch.distributed.init_process_group(
|
| 98 |
-
backend=args.dist_backend,
|
| 99 |
-
init_method=args.dist_url,
|
| 100 |
-
world_size=args.world_size,
|
| 101 |
-
rank=args.rank,
|
| 102 |
-
)
|
| 103 |
-
elif 'OMPI_COMM_WORLD_SIZE' in os.environ: # using Summit cluster
|
| 104 |
-
world_size = int(os.environ['OMPI_COMM_WORLD_SIZE'])
|
| 105 |
-
world_rank = int(os.environ['OMPI_COMM_WORLD_RANK'])
|
| 106 |
-
local_rank = int(os.environ['OMPI_COMM_WORLD_LOCAL_RANK'])
|
| 107 |
-
args.local_rank = local_rank
|
| 108 |
-
args.rank = world_rank
|
| 109 |
-
args.world_size = world_size
|
| 110 |
-
torch.distributed.init_process_group(
|
| 111 |
-
backend=args.dist_backend,
|
| 112 |
-
init_method=args.dist_url,
|
| 113 |
-
world_size=args.world_size,
|
| 114 |
-
rank=args.rank,
|
| 115 |
-
)
|
| 116 |
-
else:
|
| 117 |
-
# DDP via torchrun, torch.distributed.launch
|
| 118 |
-
args.local_rank, _, _ = world_info_from_env()
|
| 119 |
-
torch.distributed.init_process_group(
|
| 120 |
-
backend=args.dist_backend,
|
| 121 |
-
init_method=args.dist_url)
|
| 122 |
-
args.world_size = torch.distributed.get_world_size()
|
| 123 |
-
args.rank = torch.distributed.get_rank()
|
| 124 |
-
args.distributed = True
|
| 125 |
-
print(f"Distributed training: local_rank={args.local_rank}, "
|
| 126 |
-
f"rank={args.rank}, world_size={args.world_size}, "
|
| 127 |
-
f"hostname={socket.gethostname()}, pid={os.getpid()}")
|
| 128 |
-
|
| 129 |
-
if torch.cuda.is_available():
|
| 130 |
-
if args.distributed and not args.no_set_device_rank:
|
| 131 |
-
device = 'cuda:%d' % args.local_rank
|
| 132 |
-
else:
|
| 133 |
-
device = 'cuda:0'
|
| 134 |
-
torch.cuda.set_device(device)
|
| 135 |
-
else:
|
| 136 |
-
device = 'cpu'
|
| 137 |
-
args.device = device
|
| 138 |
-
device = torch.device(device)
|
| 139 |
-
return device
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/training/imagenet_zeroshot_data.py
DELETED
|
@@ -1,254 +0,0 @@
|
|
| 1 |
-
# NOTE: This script is currently not supported for CLAP.
|
| 2 |
-
|
| 3 |
-
imagenet_classnames = ["tench", "goldfish", "great white shark", "tiger shark", "hammerhead shark", "electric ray",
|
| 4 |
-
"stingray", "rooster", "hen", "ostrich", "brambling", "goldfinch", "house finch", "junco",
|
| 5 |
-
"indigo bunting", "American robin", "bulbul", "jay", "magpie", "chickadee", "American dipper",
|
| 6 |
-
"kite (bird of prey)", "bald eagle", "vulture", "great grey owl", "fire salamander",
|
| 7 |
-
"smooth newt", "newt", "spotted salamander", "axolotl", "American bullfrog", "tree frog",
|
| 8 |
-
"tailed frog", "loggerhead sea turtle", "leatherback sea turtle", "mud turtle", "terrapin",
|
| 9 |
-
"box turtle", "banded gecko", "green iguana", "Carolina anole",
|
| 10 |
-
"desert grassland whiptail lizard", "agama", "frilled-necked lizard", "alligator lizard",
|
| 11 |
-
"Gila monster", "European green lizard", "chameleon", "Komodo dragon", "Nile crocodile",
|
| 12 |
-
"American alligator", "triceratops", "worm snake", "ring-necked snake",
|
| 13 |
-
"eastern hog-nosed snake", "smooth green snake", "kingsnake", "garter snake", "water snake",
|
| 14 |
-
"vine snake", "night snake", "boa constrictor", "African rock python", "Indian cobra",
|
| 15 |
-
"green mamba", "sea snake", "Saharan horned viper", "eastern diamondback rattlesnake",
|
| 16 |
-
"sidewinder rattlesnake", "trilobite", "harvestman", "scorpion", "yellow garden spider",
|
| 17 |
-
"barn spider", "European garden spider", "southern black widow", "tarantula", "wolf spider",
|
| 18 |
-
"tick", "centipede", "black grouse", "ptarmigan", "ruffed grouse", "prairie grouse", "peafowl",
|
| 19 |
-
"quail", "partridge", "african grey parrot", "macaw", "sulphur-crested cockatoo", "lorikeet",
|
| 20 |
-
"coucal", "bee eater", "hornbill", "hummingbird", "jacamar", "toucan", "duck",
|
| 21 |
-
"red-breasted merganser", "goose", "black swan", "tusker", "echidna", "platypus", "wallaby",
|
| 22 |
-
"koala", "wombat", "jellyfish", "sea anemone", "brain coral", "flatworm", "nematode", "conch",
|
| 23 |
-
"snail", "slug", "sea slug", "chiton", "chambered nautilus", "Dungeness crab", "rock crab",
|
| 24 |
-
"fiddler crab", "red king crab", "American lobster", "spiny lobster", "crayfish", "hermit crab",
|
| 25 |
-
"isopod", "white stork", "black stork", "spoonbill", "flamingo", "little blue heron",
|
| 26 |
-
"great egret", "bittern bird", "crane bird", "limpkin", "common gallinule", "American coot",
|
| 27 |
-
"bustard", "ruddy turnstone", "dunlin", "common redshank", "dowitcher", "oystercatcher",
|
| 28 |
-
"pelican", "king penguin", "albatross", "grey whale", "killer whale", "dugong", "sea lion",
|
| 29 |
-
"Chihuahua", "Japanese Chin", "Maltese", "Pekingese", "Shih Tzu", "King Charles Spaniel",
|
| 30 |
-
"Papillon", "toy terrier", "Rhodesian Ridgeback", "Afghan Hound", "Basset Hound", "Beagle",
|
| 31 |
-
"Bloodhound", "Bluetick Coonhound", "Black and Tan Coonhound", "Treeing Walker Coonhound",
|
| 32 |
-
"English foxhound", "Redbone Coonhound", "borzoi", "Irish Wolfhound", "Italian Greyhound",
|
| 33 |
-
"Whippet", "Ibizan Hound", "Norwegian Elkhound", "Otterhound", "Saluki", "Scottish Deerhound",
|
| 34 |
-
"Weimaraner", "Staffordshire Bull Terrier", "American Staffordshire Terrier",
|
| 35 |
-
"Bedlington Terrier", "Border Terrier", "Kerry Blue Terrier", "Irish Terrier",
|
| 36 |
-
"Norfolk Terrier", "Norwich Terrier", "Yorkshire Terrier", "Wire Fox Terrier",
|
| 37 |
-
"Lakeland Terrier", "Sealyham Terrier", "Airedale Terrier", "Cairn Terrier",
|
| 38 |
-
"Australian Terrier", "Dandie Dinmont Terrier", "Boston Terrier", "Miniature Schnauzer",
|
| 39 |
-
"Giant Schnauzer", "Standard Schnauzer", "Scottish Terrier", "Tibetan Terrier",
|
| 40 |
-
"Australian Silky Terrier", "Soft-coated Wheaten Terrier", "West Highland White Terrier",
|
| 41 |
-
"Lhasa Apso", "Flat-Coated Retriever", "Curly-coated Retriever", "Golden Retriever",
|
| 42 |
-
"Labrador Retriever", "Chesapeake Bay Retriever", "German Shorthaired Pointer", "Vizsla",
|
| 43 |
-
"English Setter", "Irish Setter", "Gordon Setter", "Brittany dog", "Clumber Spaniel",
|
| 44 |
-
"English Springer Spaniel", "Welsh Springer Spaniel", "Cocker Spaniel", "Sussex Spaniel",
|
| 45 |
-
"Irish Water Spaniel", "Kuvasz", "Schipperke", "Groenendael dog", "Malinois", "Briard",
|
| 46 |
-
"Australian Kelpie", "Komondor", "Old English Sheepdog", "Shetland Sheepdog", "collie",
|
| 47 |
-
"Border Collie", "Bouvier des Flandres dog", "Rottweiler", "German Shepherd Dog", "Dobermann",
|
| 48 |
-
"Miniature Pinscher", "Greater Swiss Mountain Dog", "Bernese Mountain Dog",
|
| 49 |
-
"Appenzeller Sennenhund", "Entlebucher Sennenhund", "Boxer", "Bullmastiff", "Tibetan Mastiff",
|
| 50 |
-
"French Bulldog", "Great Dane", "St. Bernard", "husky", "Alaskan Malamute", "Siberian Husky",
|
| 51 |
-
"Dalmatian", "Affenpinscher", "Basenji", "pug", "Leonberger", "Newfoundland dog",
|
| 52 |
-
"Great Pyrenees dog", "Samoyed", "Pomeranian", "Chow Chow", "Keeshond", "brussels griffon",
|
| 53 |
-
"Pembroke Welsh Corgi", "Cardigan Welsh Corgi", "Toy Poodle", "Miniature Poodle",
|
| 54 |
-
"Standard Poodle", "Mexican hairless dog (xoloitzcuintli)", "grey wolf", "Alaskan tundra wolf",
|
| 55 |
-
"red wolf or maned wolf", "coyote", "dingo", "dhole", "African wild dog", "hyena", "red fox",
|
| 56 |
-
"kit fox", "Arctic fox", "grey fox", "tabby cat", "tiger cat", "Persian cat", "Siamese cat",
|
| 57 |
-
"Egyptian Mau", "cougar", "lynx", "leopard", "snow leopard", "jaguar", "lion", "tiger",
|
| 58 |
-
"cheetah", "brown bear", "American black bear", "polar bear", "sloth bear", "mongoose",
|
| 59 |
-
"meerkat", "tiger beetle", "ladybug", "ground beetle", "longhorn beetle", "leaf beetle",
|
| 60 |
-
"dung beetle", "rhinoceros beetle", "weevil", "fly", "bee", "ant", "grasshopper",
|
| 61 |
-
"cricket insect", "stick insect", "cockroach", "praying mantis", "cicada", "leafhopper",
|
| 62 |
-
"lacewing", "dragonfly", "damselfly", "red admiral butterfly", "ringlet butterfly",
|
| 63 |
-
"monarch butterfly", "small white butterfly", "sulphur butterfly", "gossamer-winged butterfly",
|
| 64 |
-
"starfish", "sea urchin", "sea cucumber", "cottontail rabbit", "hare", "Angora rabbit",
|
| 65 |
-
"hamster", "porcupine", "fox squirrel", "marmot", "beaver", "guinea pig", "common sorrel horse",
|
| 66 |
-
"zebra", "pig", "wild boar", "warthog", "hippopotamus", "ox", "water buffalo", "bison",
|
| 67 |
-
"ram (adult male sheep)", "bighorn sheep", "Alpine ibex", "hartebeest", "impala (antelope)",
|
| 68 |
-
"gazelle", "arabian camel", "llama", "weasel", "mink", "European polecat",
|
| 69 |
-
"black-footed ferret", "otter", "skunk", "badger", "armadillo", "three-toed sloth", "orangutan",
|
| 70 |
-
"gorilla", "chimpanzee", "gibbon", "siamang", "guenon", "patas monkey", "baboon", "macaque",
|
| 71 |
-
"langur", "black-and-white colobus", "proboscis monkey", "marmoset", "white-headed capuchin",
|
| 72 |
-
"howler monkey", "titi monkey", "Geoffroy's spider monkey", "common squirrel monkey",
|
| 73 |
-
"ring-tailed lemur", "indri", "Asian elephant", "African bush elephant", "red panda",
|
| 74 |
-
"giant panda", "snoek fish", "eel", "silver salmon", "rock beauty fish", "clownfish",
|
| 75 |
-
"sturgeon", "gar fish", "lionfish", "pufferfish", "abacus", "abaya", "academic gown",
|
| 76 |
-
"accordion", "acoustic guitar", "aircraft carrier", "airliner", "airship", "altar", "ambulance",
|
| 77 |
-
"amphibious vehicle", "analog clock", "apiary", "apron", "trash can", "assault rifle",
|
| 78 |
-
"backpack", "bakery", "balance beam", "balloon", "ballpoint pen", "Band-Aid", "banjo",
|
| 79 |
-
"baluster / handrail", "barbell", "barber chair", "barbershop", "barn", "barometer", "barrel",
|
| 80 |
-
"wheelbarrow", "baseball", "basketball", "bassinet", "bassoon", "swimming cap", "bath towel",
|
| 81 |
-
"bathtub", "station wagon", "lighthouse", "beaker", "military hat (bearskin or shako)",
|
| 82 |
-
"beer bottle", "beer glass", "bell tower", "baby bib", "tandem bicycle", "bikini",
|
| 83 |
-
"ring binder", "binoculars", "birdhouse", "boathouse", "bobsleigh", "bolo tie", "poke bonnet",
|
| 84 |
-
"bookcase", "bookstore", "bottle cap", "hunting bow", "bow tie", "brass memorial plaque", "bra",
|
| 85 |
-
"breakwater", "breastplate", "broom", "bucket", "buckle", "bulletproof vest",
|
| 86 |
-
"high-speed train", "butcher shop", "taxicab", "cauldron", "candle", "cannon", "canoe",
|
| 87 |
-
"can opener", "cardigan", "car mirror", "carousel", "tool kit", "cardboard box / carton",
|
| 88 |
-
"car wheel", "automated teller machine", "cassette", "cassette player", "castle", "catamaran",
|
| 89 |
-
"CD player", "cello", "mobile phone", "chain", "chain-link fence", "chain mail", "chainsaw",
|
| 90 |
-
"storage chest", "chiffonier", "bell or wind chime", "china cabinet", "Christmas stocking",
|
| 91 |
-
"church", "movie theater", "cleaver", "cliff dwelling", "cloak", "clogs", "cocktail shaker",
|
| 92 |
-
"coffee mug", "coffeemaker", "spiral or coil", "combination lock", "computer keyboard",
|
| 93 |
-
"candy store", "container ship", "convertible", "corkscrew", "cornet", "cowboy boot",
|
| 94 |
-
"cowboy hat", "cradle", "construction crane", "crash helmet", "crate", "infant bed",
|
| 95 |
-
"Crock Pot", "croquet ball", "crutch", "cuirass", "dam", "desk", "desktop computer",
|
| 96 |
-
"rotary dial telephone", "diaper", "digital clock", "digital watch", "dining table",
|
| 97 |
-
"dishcloth", "dishwasher", "disc brake", "dock", "dog sled", "dome", "doormat", "drilling rig",
|
| 98 |
-
"drum", "drumstick", "dumbbell", "Dutch oven", "electric fan", "electric guitar",
|
| 99 |
-
"electric locomotive", "entertainment center", "envelope", "espresso machine", "face powder",
|
| 100 |
-
"feather boa", "filing cabinet", "fireboat", "fire truck", "fire screen", "flagpole", "flute",
|
| 101 |
-
"folding chair", "football helmet", "forklift", "fountain", "fountain pen", "four-poster bed",
|
| 102 |
-
"freight car", "French horn", "frying pan", "fur coat", "garbage truck",
|
| 103 |
-
"gas mask or respirator", "gas pump", "goblet", "go-kart", "golf ball", "golf cart", "gondola",
|
| 104 |
-
"gong", "gown", "grand piano", "greenhouse", "radiator grille", "grocery store", "guillotine",
|
| 105 |
-
"hair clip", "hair spray", "half-track", "hammer", "hamper", "hair dryer", "hand-held computer",
|
| 106 |
-
"handkerchief", "hard disk drive", "harmonica", "harp", "combine harvester", "hatchet",
|
| 107 |
-
"holster", "home theater", "honeycomb", "hook", "hoop skirt", "gymnastic horizontal bar",
|
| 108 |
-
"horse-drawn vehicle", "hourglass", "iPod", "clothes iron", "carved pumpkin", "jeans", "jeep",
|
| 109 |
-
"T-shirt", "jigsaw puzzle", "rickshaw", "joystick", "kimono", "knee pad", "knot", "lab coat",
|
| 110 |
-
"ladle", "lampshade", "laptop computer", "lawn mower", "lens cap", "letter opener", "library",
|
| 111 |
-
"lifeboat", "lighter", "limousine", "ocean liner", "lipstick", "slip-on shoe", "lotion",
|
| 112 |
-
"music speaker", "loupe magnifying glass", "sawmill", "magnetic compass", "messenger bag",
|
| 113 |
-
"mailbox", "tights", "one-piece bathing suit", "manhole cover", "maraca", "marimba", "mask",
|
| 114 |
-
"matchstick", "maypole", "maze", "measuring cup", "medicine cabinet", "megalith", "microphone",
|
| 115 |
-
"microwave oven", "military uniform", "milk can", "minibus", "miniskirt", "minivan", "missile",
|
| 116 |
-
"mitten", "mixing bowl", "mobile home", "ford model t", "modem", "monastery", "monitor",
|
| 117 |
-
"moped", "mortar and pestle", "graduation cap", "mosque", "mosquito net", "vespa",
|
| 118 |
-
"mountain bike", "tent", "computer mouse", "mousetrap", "moving van", "muzzle", "metal nail",
|
| 119 |
-
"neck brace", "necklace", "baby pacifier", "notebook computer", "obelisk", "oboe", "ocarina",
|
| 120 |
-
"odometer", "oil filter", "pipe organ", "oscilloscope", "overskirt", "bullock cart",
|
| 121 |
-
"oxygen mask", "product packet / packaging", "paddle", "paddle wheel", "padlock", "paintbrush",
|
| 122 |
-
"pajamas", "palace", "pan flute", "paper towel", "parachute", "parallel bars", "park bench",
|
| 123 |
-
"parking meter", "railroad car", "patio", "payphone", "pedestal", "pencil case",
|
| 124 |
-
"pencil sharpener", "perfume", "Petri dish", "photocopier", "plectrum", "Pickelhaube",
|
| 125 |
-
"picket fence", "pickup truck", "pier", "piggy bank", "pill bottle", "pillow", "ping-pong ball",
|
| 126 |
-
"pinwheel", "pirate ship", "drink pitcher", "block plane", "planetarium", "plastic bag",
|
| 127 |
-
"plate rack", "farm plow", "plunger", "Polaroid camera", "pole", "police van", "poncho",
|
| 128 |
-
"pool table", "soda bottle", "plant pot", "potter's wheel", "power drill", "prayer rug",
|
| 129 |
-
"printer", "prison", "missile", "projector", "hockey puck", "punching bag", "purse", "quill",
|
| 130 |
-
"quilt", "race car", "racket", "radiator", "radio", "radio telescope", "rain barrel",
|
| 131 |
-
"recreational vehicle", "fishing casting reel", "reflex camera", "refrigerator",
|
| 132 |
-
"remote control", "restaurant", "revolver", "rifle", "rocking chair", "rotisserie", "eraser",
|
| 133 |
-
"rugby ball", "ruler measuring stick", "sneaker", "safe", "safety pin", "salt shaker", "sandal",
|
| 134 |
-
"sarong", "saxophone", "scabbard", "weighing scale", "school bus", "schooner", "scoreboard",
|
| 135 |
-
"CRT monitor", "screw", "screwdriver", "seat belt", "sewing machine", "shield", "shoe store",
|
| 136 |
-
"shoji screen / room divider", "shopping basket", "shopping cart", "shovel", "shower cap",
|
| 137 |
-
"shower curtain", "ski", "balaclava ski mask", "sleeping bag", "slide rule", "sliding door",
|
| 138 |
-
"slot machine", "snorkel", "snowmobile", "snowplow", "soap dispenser", "soccer ball", "sock",
|
| 139 |
-
"solar thermal collector", "sombrero", "soup bowl", "keyboard space bar", "space heater",
|
| 140 |
-
"space shuttle", "spatula", "motorboat", "spider web", "spindle", "sports car", "spotlight",
|
| 141 |
-
"stage", "steam locomotive", "through arch bridge", "steel drum", "stethoscope", "scarf",
|
| 142 |
-
"stone wall", "stopwatch", "stove", "strainer", "tram", "stretcher", "couch", "stupa",
|
| 143 |
-
"submarine", "suit", "sundial", "sunglasses", "sunglasses", "sunscreen", "suspension bridge",
|
| 144 |
-
"mop", "sweatshirt", "swim trunks / shorts", "swing", "electrical switch", "syringe",
|
| 145 |
-
"table lamp", "tank", "tape player", "teapot", "teddy bear", "television", "tennis ball",
|
| 146 |
-
"thatched roof", "front curtain", "thimble", "threshing machine", "throne", "tile roof",
|
| 147 |
-
"toaster", "tobacco shop", "toilet seat", "torch", "totem pole", "tow truck", "toy store",
|
| 148 |
-
"tractor", "semi-trailer truck", "tray", "trench coat", "tricycle", "trimaran", "tripod",
|
| 149 |
-
"triumphal arch", "trolleybus", "trombone", "hot tub", "turnstile", "typewriter keyboard",
|
| 150 |
-
"umbrella", "unicycle", "upright piano", "vacuum cleaner", "vase", "vaulted or arched ceiling",
|
| 151 |
-
"velvet fabric", "vending machine", "vestment", "viaduct", "violin", "volleyball",
|
| 152 |
-
"waffle iron", "wall clock", "wallet", "wardrobe", "military aircraft", "sink",
|
| 153 |
-
"washing machine", "water bottle", "water jug", "water tower", "whiskey jug", "whistle",
|
| 154 |
-
"hair wig", "window screen", "window shade", "Windsor tie", "wine bottle", "airplane wing",
|
| 155 |
-
"wok", "wooden spoon", "wool", "split-rail fence", "shipwreck", "sailboat", "yurt", "website",
|
| 156 |
-
"comic book", "crossword", "traffic or street sign", "traffic light", "dust jacket", "menu",
|
| 157 |
-
"plate", "guacamole", "consomme", "hot pot", "trifle", "ice cream", "popsicle", "baguette",
|
| 158 |
-
"bagel", "pretzel", "cheeseburger", "hot dog", "mashed potatoes", "cabbage", "broccoli",
|
| 159 |
-
"cauliflower", "zucchini", "spaghetti squash", "acorn squash", "butternut squash", "cucumber",
|
| 160 |
-
"artichoke", "bell pepper", "cardoon", "mushroom", "Granny Smith apple", "strawberry", "orange",
|
| 161 |
-
"lemon", "fig", "pineapple", "banana", "jackfruit", "cherimoya (custard apple)", "pomegranate",
|
| 162 |
-
"hay", "carbonara", "chocolate syrup", "dough", "meatloaf", "pizza", "pot pie", "burrito",
|
| 163 |
-
"red wine", "espresso", "tea cup", "eggnog", "mountain", "bubble", "cliff", "coral reef",
|
| 164 |
-
"geyser", "lakeshore", "promontory", "sandbar", "beach", "valley", "volcano", "baseball player",
|
| 165 |
-
"bridegroom", "scuba diver", "rapeseed", "daisy", "yellow lady's slipper", "corn", "acorn",
|
| 166 |
-
"rose hip", "horse chestnut seed", "coral fungus", "agaric", "gyromitra", "stinkhorn mushroom",
|
| 167 |
-
"earth star fungus", "hen of the woods mushroom", "bolete", "corn cob", "toilet paper"]
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
openai_imagenet_template = [
|
| 174 |
-
lambda c: f'a bad photo of a {c}.',
|
| 175 |
-
lambda c: f'a photo of many {c}.',
|
| 176 |
-
lambda c: f'a sculpture of a {c}.',
|
| 177 |
-
lambda c: f'a photo of the hard to see {c}.',
|
| 178 |
-
lambda c: f'a low resolution photo of the {c}.',
|
| 179 |
-
lambda c: f'a rendering of a {c}.',
|
| 180 |
-
lambda c: f'graffiti of a {c}.',
|
| 181 |
-
lambda c: f'a bad photo of the {c}.',
|
| 182 |
-
lambda c: f'a cropped photo of the {c}.',
|
| 183 |
-
lambda c: f'a tattoo of a {c}.',
|
| 184 |
-
lambda c: f'the embroidered {c}.',
|
| 185 |
-
lambda c: f'a photo of a hard to see {c}.',
|
| 186 |
-
lambda c: f'a bright photo of a {c}.',
|
| 187 |
-
lambda c: f'a photo of a clean {c}.',
|
| 188 |
-
lambda c: f'a photo of a dirty {c}.',
|
| 189 |
-
lambda c: f'a dark photo of the {c}.',
|
| 190 |
-
lambda c: f'a drawing of a {c}.',
|
| 191 |
-
lambda c: f'a photo of my {c}.',
|
| 192 |
-
lambda c: f'the plastic {c}.',
|
| 193 |
-
lambda c: f'a photo of the cool {c}.',
|
| 194 |
-
lambda c: f'a close-up photo of a {c}.',
|
| 195 |
-
lambda c: f'a black and white photo of the {c}.',
|
| 196 |
-
lambda c: f'a painting of the {c}.',
|
| 197 |
-
lambda c: f'a painting of a {c}.',
|
| 198 |
-
lambda c: f'a pixelated photo of the {c}.',
|
| 199 |
-
lambda c: f'a sculpture of the {c}.',
|
| 200 |
-
lambda c: f'a bright photo of the {c}.',
|
| 201 |
-
lambda c: f'a cropped photo of a {c}.',
|
| 202 |
-
lambda c: f'a plastic {c}.',
|
| 203 |
-
lambda c: f'a photo of the dirty {c}.',
|
| 204 |
-
lambda c: f'a jpeg corrupted photo of a {c}.',
|
| 205 |
-
lambda c: f'a blurry photo of the {c}.',
|
| 206 |
-
lambda c: f'a photo of the {c}.',
|
| 207 |
-
lambda c: f'a good photo of the {c}.',
|
| 208 |
-
lambda c: f'a rendering of the {c}.',
|
| 209 |
-
lambda c: f'a {c} in a video game.',
|
| 210 |
-
lambda c: f'a photo of one {c}.',
|
| 211 |
-
lambda c: f'a doodle of a {c}.',
|
| 212 |
-
lambda c: f'a close-up photo of the {c}.',
|
| 213 |
-
lambda c: f'a photo of a {c}.',
|
| 214 |
-
lambda c: f'the origami {c}.',
|
| 215 |
-
lambda c: f'the {c} in a video game.',
|
| 216 |
-
lambda c: f'a sketch of a {c}.',
|
| 217 |
-
lambda c: f'a doodle of the {c}.',
|
| 218 |
-
lambda c: f'a origami {c}.',
|
| 219 |
-
lambda c: f'a low resolution photo of a {c}.',
|
| 220 |
-
lambda c: f'the toy {c}.',
|
| 221 |
-
lambda c: f'a rendition of the {c}.',
|
| 222 |
-
lambda c: f'a photo of the clean {c}.',
|
| 223 |
-
lambda c: f'a photo of a large {c}.',
|
| 224 |
-
lambda c: f'a rendition of a {c}.',
|
| 225 |
-
lambda c: f'a photo of a nice {c}.',
|
| 226 |
-
lambda c: f'a photo of a weird {c}.',
|
| 227 |
-
lambda c: f'a blurry photo of a {c}.',
|
| 228 |
-
lambda c: f'a cartoon {c}.',
|
| 229 |
-
lambda c: f'art of a {c}.',
|
| 230 |
-
lambda c: f'a sketch of the {c}.',
|
| 231 |
-
lambda c: f'a embroidered {c}.',
|
| 232 |
-
lambda c: f'a pixelated photo of a {c}.',
|
| 233 |
-
lambda c: f'itap of the {c}.',
|
| 234 |
-
lambda c: f'a jpeg corrupted photo of the {c}.',
|
| 235 |
-
lambda c: f'a good photo of a {c}.',
|
| 236 |
-
lambda c: f'a plushie {c}.',
|
| 237 |
-
lambda c: f'a photo of the nice {c}.',
|
| 238 |
-
lambda c: f'a photo of the small {c}.',
|
| 239 |
-
lambda c: f'a photo of the weird {c}.',
|
| 240 |
-
lambda c: f'the cartoon {c}.',
|
| 241 |
-
lambda c: f'art of the {c}.',
|
| 242 |
-
lambda c: f'a drawing of the {c}.',
|
| 243 |
-
lambda c: f'a photo of the large {c}.',
|
| 244 |
-
lambda c: f'a black and white photo of a {c}.',
|
| 245 |
-
lambda c: f'the plushie {c}.',
|
| 246 |
-
lambda c: f'a dark photo of a {c}.',
|
| 247 |
-
lambda c: f'itap of a {c}.',
|
| 248 |
-
lambda c: f'graffiti of the {c}.',
|
| 249 |
-
lambda c: f'a toy {c}.',
|
| 250 |
-
lambda c: f'itap of my {c}.',
|
| 251 |
-
lambda c: f'a photo of a cool {c}.',
|
| 252 |
-
lambda c: f'a photo of a small {c}.',
|
| 253 |
-
lambda c: f'a tattoo of the {c}.',
|
| 254 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/training/infer_demo.py
DELETED
|
@@ -1,92 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import librosa
|
| 3 |
-
from clap_module import create_model
|
| 4 |
-
from training.data import get_audio_features
|
| 5 |
-
from training.data import int16_to_float32, float32_to_int16
|
| 6 |
-
from transformers import RobertaTokenizer
|
| 7 |
-
|
| 8 |
-
tokenize = RobertaTokenizer.from_pretrained('roberta-base')
|
| 9 |
-
def tokenizer(text):
|
| 10 |
-
result = tokenize(
|
| 11 |
-
text,
|
| 12 |
-
padding="max_length",
|
| 13 |
-
truncation=True,
|
| 14 |
-
max_length=77,
|
| 15 |
-
return_tensors="pt",
|
| 16 |
-
)
|
| 17 |
-
return {k: v.squeeze(0) for k, v in result.items()}
|
| 18 |
-
|
| 19 |
-
def infer_text():
|
| 20 |
-
device = 'cuda:0' if torch.cuda.is_available() else 'cpu'
|
| 21 |
-
precision = 'fp32'
|
| 22 |
-
amodel = 'HTSAT-tiny' # or 'PANN-14'
|
| 23 |
-
tmodel = 'roberta' # the best text encoder in our training
|
| 24 |
-
enable_fusion = True # False if you do not want to use the fusion model
|
| 25 |
-
fusion_type = 'aff_2d'
|
| 26 |
-
pretrained = "/home/la/kechen/Research/KE_CLAP/ckpt/fusion_best.pt" # the checkpoint name, the unfusion model can also be loaded.
|
| 27 |
-
|
| 28 |
-
model, model_cfg = create_model(
|
| 29 |
-
amodel,
|
| 30 |
-
tmodel,
|
| 31 |
-
pretrained,
|
| 32 |
-
precision=precision,
|
| 33 |
-
device=device,
|
| 34 |
-
enable_fusion=enable_fusion,
|
| 35 |
-
fusion_type=fusion_type
|
| 36 |
-
)
|
| 37 |
-
# load the text, can be a list (i.e. batch size)
|
| 38 |
-
text_data = ["I love the contrastive learning", "I love the pretrain model"]
|
| 39 |
-
# tokenize for roberta, if you want to tokenize for another text encoder, please refer to data.py#L43-90
|
| 40 |
-
text_data = tokenizer(text_data)
|
| 41 |
-
model.eval()
|
| 42 |
-
text_embed = model.get_text_embedding(text_data)
|
| 43 |
-
text_embed = text_embed.detach().cpu().numpy()
|
| 44 |
-
print(text_embed)
|
| 45 |
-
print(text_embed.shape)
|
| 46 |
-
|
| 47 |
-
def infer_audio():
|
| 48 |
-
|
| 49 |
-
device = 'cuda:0' if torch.cuda.is_available() else 'cpu'
|
| 50 |
-
precision = 'fp32'
|
| 51 |
-
amodel = 'HTSAT-tiny' # or 'PANN-14'
|
| 52 |
-
tmodel = 'roberta' # the best text encoder in our training
|
| 53 |
-
enable_fusion = True # False if you do not want to use the fusion model
|
| 54 |
-
fusion_type = 'aff_2d'
|
| 55 |
-
pretrained = "/home/la/kechen/Research/KE_CLAP/ckpt/fusion_best.pt" # the checkpoint name, the unfusion model can also be loaded.
|
| 56 |
-
|
| 57 |
-
model, model_cfg = create_model(
|
| 58 |
-
amodel,
|
| 59 |
-
tmodel,
|
| 60 |
-
pretrained,
|
| 61 |
-
precision=precision,
|
| 62 |
-
device=device,
|
| 63 |
-
enable_fusion=enable_fusion,
|
| 64 |
-
fusion_type=fusion_type
|
| 65 |
-
)
|
| 66 |
-
|
| 67 |
-
# load the waveform of the shape (T,), should resample to 48000
|
| 68 |
-
audio_waveform, sr = librosa.load('/home/la/kechen/Research/KE_CLAP/ckpt/test_clap_short.wav', sr=48000)
|
| 69 |
-
# quantize
|
| 70 |
-
audio_waveform = int16_to_float32(float32_to_int16(audio_waveform))
|
| 71 |
-
audio_waveform = torch.from_numpy(audio_waveform).float()
|
| 72 |
-
audio_dict = {}
|
| 73 |
-
|
| 74 |
-
# the 'fusion' truncate mode can be changed to 'rand_trunc' if run in unfusion mode
|
| 75 |
-
audio_dict = get_audio_features(
|
| 76 |
-
audio_dict, audio_waveform, 480000,
|
| 77 |
-
data_truncating='fusion',
|
| 78 |
-
data_filling='repeatpad',
|
| 79 |
-
audio_cfg=model_cfg['audio_cfg']
|
| 80 |
-
)
|
| 81 |
-
model.eval()
|
| 82 |
-
# can send a list to the model, to process many audio tracks in one time (i.e. batch size)
|
| 83 |
-
audio_embed = model.get_audio_embedding([audio_dict])
|
| 84 |
-
audio_embed = audio_embed.detach().cpu().numpy()
|
| 85 |
-
print(audio_embed)
|
| 86 |
-
print(audio_embed.shape)
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
if __name__ == "__main__":
|
| 91 |
-
infer_text()
|
| 92 |
-
# infer_audio()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/training/logger.py
DELETED
|
@@ -1,26 +0,0 @@
|
|
| 1 |
-
import logging
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
def setup_logging(log_file, level, include_host=False):
|
| 5 |
-
if include_host:
|
| 6 |
-
import socket
|
| 7 |
-
hostname = socket.gethostname()
|
| 8 |
-
formatter = logging.Formatter(
|
| 9 |
-
f'%(asctime)s | {hostname} | %(levelname)s | %(message)s', datefmt='%Y-%m-%d,%H:%M:%S')
|
| 10 |
-
else:
|
| 11 |
-
formatter = logging.Formatter('%(asctime)s | %(levelname)s | %(message)s', datefmt='%Y-%m-%d,%H:%M:%S')
|
| 12 |
-
|
| 13 |
-
logging.root.setLevel(level)
|
| 14 |
-
loggers = [logging.getLogger(name) for name in logging.root.manager.loggerDict]
|
| 15 |
-
for logger in loggers:
|
| 16 |
-
logger.setLevel(level)
|
| 17 |
-
|
| 18 |
-
stream_handler = logging.StreamHandler()
|
| 19 |
-
stream_handler.setFormatter(formatter)
|
| 20 |
-
logging.root.addHandler(stream_handler)
|
| 21 |
-
|
| 22 |
-
if log_file:
|
| 23 |
-
file_handler = logging.FileHandler(filename=log_file)
|
| 24 |
-
file_handler.setFormatter(formatter)
|
| 25 |
-
logging.root.addHandler(file_handler)
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/training/lp_main.py
DELETED
|
@@ -1,643 +0,0 @@
|
|
| 1 |
-
import logging
|
| 2 |
-
import os
|
| 3 |
-
import random
|
| 4 |
-
from datetime import datetime
|
| 5 |
-
import copy
|
| 6 |
-
import numpy as np
|
| 7 |
-
import torch
|
| 8 |
-
import torch.backends.cudnn as cudnn
|
| 9 |
-
from torch.cuda.amp import GradScaler
|
| 10 |
-
import time
|
| 11 |
-
|
| 12 |
-
try:
|
| 13 |
-
import wandb
|
| 14 |
-
except ImportError:
|
| 15 |
-
wandb = None
|
| 16 |
-
|
| 17 |
-
try:
|
| 18 |
-
import torch.utils.tensorboard as tensorboard
|
| 19 |
-
except ImportError:
|
| 20 |
-
tensorboard = None
|
| 21 |
-
|
| 22 |
-
try:
|
| 23 |
-
import horovod.torch as hvd
|
| 24 |
-
except ImportError:
|
| 25 |
-
hvd = None
|
| 26 |
-
|
| 27 |
-
from clap_module import create_model_and_transforms, trace_model, create_model
|
| 28 |
-
from training.data import get_data
|
| 29 |
-
from training.params import parse_args
|
| 30 |
-
from training.distributed import is_master, init_distributed_device, world_info_from_env
|
| 31 |
-
from training.logger import setup_logging
|
| 32 |
-
from training.scheduler import cosine_lr
|
| 33 |
-
from training.lp_train import train_one_epoch, evaluate
|
| 34 |
-
from clap_module.utils import get_tar_path_from_dataset_name, dataset_split, get_optimizer
|
| 35 |
-
from clap_module.utils import load_p, load_class_label
|
| 36 |
-
from clap_module.linear_probe import LinearProbe
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
def maintain_ckpts(args, startidx, all_idx_len):
|
| 40 |
-
for i in reversed(range(startidx, all_idx_len)):
|
| 41 |
-
if os.path.exists(os.path.join(args.checkpoint_path, f"epoch_top_{i}.pt")):
|
| 42 |
-
os.rename(
|
| 43 |
-
os.path.join(args.checkpoint_path, f"epoch_top_{i}.pt"),
|
| 44 |
-
os.path.join(args.checkpoint_path, f"epoch_top_{i+1}.pt"),
|
| 45 |
-
)
|
| 46 |
-
if os.path.exists(
|
| 47 |
-
os.path.join(args.checkpoint_path, f"epoch_top_{all_idx_len}.pt")
|
| 48 |
-
):
|
| 49 |
-
os.remove(os.path.join(args.checkpoint_path, f"epoch_top_{all_idx_len}.pt"))
|
| 50 |
-
return
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
def update_top_k_performance(
|
| 54 |
-
new_metrics_inputs, current_top_k_ckpt_metrics, args, ckpt, bignumbetter=True
|
| 55 |
-
):
|
| 56 |
-
"""
|
| 57 |
-
Record the top-k performance of the current epoch.
|
| 58 |
-
current_top_k_metrics is a dictionary of the form: {1: top_1_ckpt_measure, 2: top_2_ckpt_measure, ...}
|
| 59 |
-
"""
|
| 60 |
-
if isinstance(new_metrics_inputs, (list, tuple)):
|
| 61 |
-
new_metrics_inputs = np.mean(new_metrics_inputs)
|
| 62 |
-
return update_top_k_performance(
|
| 63 |
-
new_metrics_inputs,
|
| 64 |
-
current_top_k_ckpt_metrics,
|
| 65 |
-
args=args,
|
| 66 |
-
ckpt=ckpt,
|
| 67 |
-
bignumbetter=bignumbetter,
|
| 68 |
-
)
|
| 69 |
-
elif isinstance(new_metrics_inputs, dict):
|
| 70 |
-
new_metrics_inputs = np.mean(list(new_metrics_inputs.values()))
|
| 71 |
-
return update_top_k_performance(
|
| 72 |
-
new_metrics_inputs,
|
| 73 |
-
current_top_k_ckpt_metrics,
|
| 74 |
-
args=args,
|
| 75 |
-
ckpt=ckpt,
|
| 76 |
-
bignumbetter=bignumbetter,
|
| 77 |
-
)
|
| 78 |
-
elif isinstance(new_metrics_inputs, (float, int)):
|
| 79 |
-
update_flag = {k: False for k in current_top_k_ckpt_metrics.keys()}
|
| 80 |
-
sorted_keys = sorted(current_top_k_ckpt_metrics.keys())
|
| 81 |
-
sorted_values = sorted(
|
| 82 |
-
current_top_k_ckpt_metrics.values(), reverse=bignumbetter
|
| 83 |
-
)
|
| 84 |
-
sorted_values_ = copy.deepcopy(sorted_values)
|
| 85 |
-
sorted_values.append(new_metrics_inputs)
|
| 86 |
-
sorted_values = sorted(sorted_values, reverse=bignumbetter)
|
| 87 |
-
sorted_values = sorted_values[:-1]
|
| 88 |
-
|
| 89 |
-
if sorted_values == sorted_values_:
|
| 90 |
-
return current_top_k_ckpt_metrics, new_metrics_inputs
|
| 91 |
-
else:
|
| 92 |
-
for i in range(len(sorted_keys)):
|
| 93 |
-
if current_top_k_ckpt_metrics[sorted_keys[i]] != sorted_values[i]:
|
| 94 |
-
current_top_k_ckpt_metrics[sorted_keys[i]] = sorted_values[i]
|
| 95 |
-
update_flag[sorted_keys[i]] = True
|
| 96 |
-
for i in range(len(update_flag)):
|
| 97 |
-
if update_flag[i]:
|
| 98 |
-
maintain_ckpts(args, i, len(sorted_keys))
|
| 99 |
-
torch.save(
|
| 100 |
-
ckpt,
|
| 101 |
-
os.path.join(args.checkpoint_path, f"epoch_top_{i}.pt"),
|
| 102 |
-
)
|
| 103 |
-
break
|
| 104 |
-
return current_top_k_ckpt_metrics, new_metrics_inputs
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
# def updateifNone(a, b):
|
| 108 |
-
# a = b if None else a
|
| 109 |
-
# return a
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
def is_pretrained_params(n):
|
| 113 |
-
return (
|
| 114 |
-
n.startswith("clap_model.transformer")
|
| 115 |
-
or n in ["clap_model.positional_embedding", "clap_model.text_projection"]
|
| 116 |
-
or n.startswith("clap_model.token_embedding")
|
| 117 |
-
or n.startswith("clap_model.ln_final")
|
| 118 |
-
or n.startswith("clap_model.logit_scale_t")
|
| 119 |
-
)
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
def random_seed(seed=42, rank=0):
|
| 123 |
-
torch.manual_seed(seed + rank)
|
| 124 |
-
np.random.seed(seed + rank)
|
| 125 |
-
random.seed(seed + rank)
|
| 126 |
-
|
| 127 |
-
def config_lp_optimizer(model, data, args):
|
| 128 |
-
# set wd-related params to 0 if use adam optimizer
|
| 129 |
-
if args.optimizer == "adam":
|
| 130 |
-
args.wd = 0
|
| 131 |
-
args.wd_pretrained = 0
|
| 132 |
-
args.wd_new = 0
|
| 133 |
-
|
| 134 |
-
in_clap = (
|
| 135 |
-
lambda n, p: n.startswith("clap_model")
|
| 136 |
-
)
|
| 137 |
-
|
| 138 |
-
named_parameters = list(model.named_parameters())
|
| 139 |
-
|
| 140 |
-
optimizer = {}
|
| 141 |
-
scheduler = {}
|
| 142 |
-
|
| 143 |
-
# freeze text encoder
|
| 144 |
-
text_freeze_parameters = [
|
| 145 |
-
p
|
| 146 |
-
for n, p in named_parameters
|
| 147 |
-
if n.startswith("clap_model.transformer")
|
| 148 |
-
or n in ["clap_model.positional_embedding", "clap_model.text_projection"]
|
| 149 |
-
or n.startswith("clap_model.token_embedding")
|
| 150 |
-
or n.startswith("clap_model.ln_final")
|
| 151 |
-
]
|
| 152 |
-
|
| 153 |
-
if args.freeze_text:
|
| 154 |
-
logging.info("Freeze Text!!!!")
|
| 155 |
-
for k in text_freeze_parameters:
|
| 156 |
-
k.requires_grad = False
|
| 157 |
-
|
| 158 |
-
if not args.lp_freeze:
|
| 159 |
-
exclude = (
|
| 160 |
-
lambda n, p: p.ndim < 2
|
| 161 |
-
or "bn" in n
|
| 162 |
-
or "ln" in n
|
| 163 |
-
or "bias" in n
|
| 164 |
-
or "logit_scale" in n
|
| 165 |
-
)
|
| 166 |
-
include = lambda n, p: not exclude(n, p)
|
| 167 |
-
|
| 168 |
-
# (yusong): we do not split the learning rate anymore
|
| 169 |
-
# p for n, p in named_parameters if in_clap(n,p) and exclude(n, p) and p.requires_grad
|
| 170 |
-
gain_or_bias_params = [
|
| 171 |
-
p for n, p in named_parameters if exclude(n, p) and p.requires_grad
|
| 172 |
-
]
|
| 173 |
-
# rest_params = [p for n, p in named_parameters if in_clap(n,p) and include(n, p) and p.requires_grad]
|
| 174 |
-
rest_params = [p for n, p in named_parameters if include(n, p) and p.requires_grad]
|
| 175 |
-
|
| 176 |
-
if args.train_data is None:
|
| 177 |
-
optimizer = None
|
| 178 |
-
scheduler = None
|
| 179 |
-
else:
|
| 180 |
-
total_steps = data["train"].dataloader.num_batches * args.epochs
|
| 181 |
-
|
| 182 |
-
if args.split_opt:
|
| 183 |
-
for x in ["lr", "beta1", "beta2", "eps", "wd"]:
|
| 184 |
-
for y in ["_new", "_pretrained"]:
|
| 185 |
-
if getattr(args, x + y) is None:
|
| 186 |
-
setattr(args, x + y, getattr(args, x))
|
| 187 |
-
|
| 188 |
-
gain_or_bias_pretrained_params = [
|
| 189 |
-
p
|
| 190 |
-
for n, p in named_parameters
|
| 191 |
-
if (exclude(n, p) and p.requires_grad) and is_pretrained_params(n)
|
| 192 |
-
]
|
| 193 |
-
rest_pretrained_params = [
|
| 194 |
-
p
|
| 195 |
-
for n, p in named_parameters
|
| 196 |
-
if (include(n, p) and p.requires_grad) and is_pretrained_params(n)
|
| 197 |
-
]
|
| 198 |
-
gain_or_bias_new_params = [
|
| 199 |
-
p
|
| 200 |
-
for n, p in named_parameters
|
| 201 |
-
if (exclude(n, p) and p.requires_grad) and (not is_pretrained_params(n))
|
| 202 |
-
]
|
| 203 |
-
rest_new_params = [
|
| 204 |
-
p
|
| 205 |
-
for n, p in named_parameters
|
| 206 |
-
if (include(n, p) and p.requires_grad) and (not is_pretrained_params(n))
|
| 207 |
-
]
|
| 208 |
-
|
| 209 |
-
pretrained_params_optimizer = get_optimizer(
|
| 210 |
-
[
|
| 211 |
-
{"params": gain_or_bias_pretrained_params, "weight_decay": 0.0},
|
| 212 |
-
{
|
| 213 |
-
"params": rest_pretrained_params,
|
| 214 |
-
"weight_decay": args.wd_pretrained,
|
| 215 |
-
},
|
| 216 |
-
],
|
| 217 |
-
lr=args.lr_pretrained,
|
| 218 |
-
betas=(args.beta1_pretrained, args.beta2_pretrained),
|
| 219 |
-
eps=args.eps_pretrained,
|
| 220 |
-
momentum=args.momentum_pretrained,
|
| 221 |
-
optimizer_name=args.optimizer,
|
| 222 |
-
)
|
| 223 |
-
pretrained_params_scheduler = cosine_lr(
|
| 224 |
-
pretrained_params_optimizer,
|
| 225 |
-
args.lr_pretrained,
|
| 226 |
-
args.warmup,
|
| 227 |
-
total_steps,
|
| 228 |
-
)
|
| 229 |
-
|
| 230 |
-
new_params_optimizer = get_optimizer(
|
| 231 |
-
[
|
| 232 |
-
{"params": gain_or_bias_new_params, "weight_decay": 0.0},
|
| 233 |
-
{"params": rest_new_params, "weight_decay": args.wd_new},
|
| 234 |
-
],
|
| 235 |
-
lr=args.lr_new,
|
| 236 |
-
betas=(args.beta1_new, args.beta2_new),
|
| 237 |
-
eps=args.eps_new,
|
| 238 |
-
momentum=args.momentum_new,
|
| 239 |
-
optimizer_name=args.optimizer,
|
| 240 |
-
)
|
| 241 |
-
new_params_scheduler = cosine_lr(
|
| 242 |
-
new_params_optimizer, args.lr_new, args.warmup, total_steps
|
| 243 |
-
)
|
| 244 |
-
|
| 245 |
-
optimizer["text"] = pretrained_params_optimizer
|
| 246 |
-
optimizer["audio"] = new_params_optimizer
|
| 247 |
-
scheduler["text"] = pretrained_params_scheduler
|
| 248 |
-
scheduler["audio"] = new_params_scheduler
|
| 249 |
-
|
| 250 |
-
if args.horovod:
|
| 251 |
-
pretrained_params_optimizer = hvd.DistributedOptimizer(
|
| 252 |
-
pretrained_params_optimizer,
|
| 253 |
-
named_parameters=model.named_parameters(),
|
| 254 |
-
)
|
| 255 |
-
new_params_optimizer = hvd.DistributedOptimizer(
|
| 256 |
-
new_params_optimizer, named_parameters=model.named_parameters()
|
| 257 |
-
)
|
| 258 |
-
hvd.broadcast_parameters(model.state_dict(), root_rank=0)
|
| 259 |
-
hvd.broadcast_optimizer_state(pretrained_params_optimizer, root_rank=0)
|
| 260 |
-
hvd.broadcast_optimizer_state(new_params_optimizer, root_rank=0)
|
| 261 |
-
else:
|
| 262 |
-
|
| 263 |
-
optimizer["clap"] = get_optimizer(
|
| 264 |
-
[
|
| 265 |
-
{"params": gain_or_bias_params, "weight_decay": 0.0},
|
| 266 |
-
{"params": rest_params, "weight_decay": args.wd},
|
| 267 |
-
],
|
| 268 |
-
lr=args.lr,
|
| 269 |
-
betas=(args.beta1, args.beta2),
|
| 270 |
-
eps=args.eps,
|
| 271 |
-
momentum=args.momentum,
|
| 272 |
-
optimizer_name=args.optimizer,
|
| 273 |
-
)
|
| 274 |
-
scheduler["clap"] = cosine_lr(optimizer["clap"], args.lr, args.warmup, total_steps)
|
| 275 |
-
|
| 276 |
-
if args.horovod:
|
| 277 |
-
optimizer["clap"] = hvd.DistributedOptimizer(
|
| 278 |
-
optimizer["clap"], named_parameters=model.named_parameters()
|
| 279 |
-
)
|
| 280 |
-
hvd.broadcast_parameters(model.state_dict(), root_rank=0)
|
| 281 |
-
hvd.broadcast_optimizer_state(optimizer["clap"], root_rank=0)
|
| 282 |
-
|
| 283 |
-
# linear probe optimizer
|
| 284 |
-
else:
|
| 285 |
-
lp_params = [p for n, p in named_parameters if (not in_clap(n, p)) and p.requires_grad]
|
| 286 |
-
lp_optim = get_optimizer(lp_params, lr=args.lp_lr, betas=(args.beta1, args.beta2), eps=args.eps, momentum=0.9,
|
| 287 |
-
optimizer_name=args.optimizer)
|
| 288 |
-
optimizer["lp"] = lp_optim
|
| 289 |
-
|
| 290 |
-
return optimizer, scheduler, text_freeze_parameters
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
def main():
|
| 294 |
-
args = parse_args()
|
| 295 |
-
|
| 296 |
-
time.sleep(args.sleep)
|
| 297 |
-
|
| 298 |
-
# sanitize model name for filesystem / uri use, easier if we don't use / in name as a rule?
|
| 299 |
-
args.amodel = args.amodel.replace("/", "-")
|
| 300 |
-
# download sizes.json file
|
| 301 |
-
|
| 302 |
-
# (yusong): the below two lines are for debug
|
| 303 |
-
# print("setting up faulthandler")
|
| 304 |
-
# faulthandler.register(10)
|
| 305 |
-
|
| 306 |
-
random.seed(args.seed)
|
| 307 |
-
torch.manual_seed(args.seed)
|
| 308 |
-
torch.cuda.manual_seed(args.seed)
|
| 309 |
-
torch.cuda.manual_seed_all(args.seed)
|
| 310 |
-
np.random.seed(args.seed)
|
| 311 |
-
args.class_index_dict = load_class_label(args.class_label_path)
|
| 312 |
-
|
| 313 |
-
# get the name of the experiments
|
| 314 |
-
if args.name is None:
|
| 315 |
-
args.name = "-".join(
|
| 316 |
-
[
|
| 317 |
-
datetime.now().strftime("%Y_%m_%d-%H_%M_%S"),
|
| 318 |
-
f"linear_probe"
|
| 319 |
-
f"model_{args.amodel}",
|
| 320 |
-
f"lr_{args.lr}",
|
| 321 |
-
f"b_{args.batch_size}",
|
| 322 |
-
f"j_{args.workers}",
|
| 323 |
-
f"p_{args.precision}",
|
| 324 |
-
]
|
| 325 |
-
)
|
| 326 |
-
|
| 327 |
-
# discover initial world args early so we can log properly
|
| 328 |
-
args.distributed = False
|
| 329 |
-
args.local_rank, args.rank, args.world_size = world_info_from_env()
|
| 330 |
-
|
| 331 |
-
if args.remotedata and is_master(args):
|
| 332 |
-
for dataset_name in args.datasetnames:
|
| 333 |
-
for split in dataset_split[dataset_name]:
|
| 334 |
-
if not os.path.exists(f"./json_files/{dataset_name}/{split}"):
|
| 335 |
-
os.makedirs(f"./json_files/{dataset_name}/{split}")
|
| 336 |
-
os.system(
|
| 337 |
-
f"aws s3 cp s3://s-laion-audio/webdataset_tar/{dataset_name}/{split}/sizes.json ./json_files/{dataset_name}/{split}/sizes.json"
|
| 338 |
-
)
|
| 339 |
-
|
| 340 |
-
args.log_path = None
|
| 341 |
-
if is_master(args, local=args.log_local):
|
| 342 |
-
log_base_path = os.path.join(args.logs, args.name)
|
| 343 |
-
os.makedirs(log_base_path, exist_ok=True)
|
| 344 |
-
log_filename = f"out-{args.rank}" if args.log_local else "out.log"
|
| 345 |
-
args.log_path = os.path.join(log_base_path, log_filename)
|
| 346 |
-
|
| 347 |
-
# avoid log dir in same name:
|
| 348 |
-
postfix = 0
|
| 349 |
-
while os.path.exists(args.log_path):
|
| 350 |
-
postfix += 1
|
| 351 |
-
log_base_path_new = log_base_path+'-'+str(postfix)
|
| 352 |
-
os.makedirs(log_base_path_new, exist_ok=True)
|
| 353 |
-
log_filename = f"out-{args.rank}" if args.log_local else "out.log"
|
| 354 |
-
args.log_path = os.path.join(log_base_path_new, log_filename)
|
| 355 |
-
# print(
|
| 356 |
-
# "Error. Experiment already exists. Use --name {} to specify a new experiment."
|
| 357 |
-
# )
|
| 358 |
-
# return -1
|
| 359 |
-
|
| 360 |
-
# Set logger
|
| 361 |
-
args.log_level = logging.DEBUG if args.debug else logging.INFO
|
| 362 |
-
setup_logging(args.log_path, args.log_level)
|
| 363 |
-
|
| 364 |
-
# fully initialize distributed device environment
|
| 365 |
-
device = init_distributed_device(args)
|
| 366 |
-
|
| 367 |
-
args.wandb = "wandb" in args.report_to or "all" in args.report_to
|
| 368 |
-
args.tensorboard = "tensorboard" in args.report_to or "all" in args.report_to
|
| 369 |
-
if is_master(args):
|
| 370 |
-
args.tensorboard_path = (
|
| 371 |
-
os.path.join(args.logs, args.name, "tensorboard")
|
| 372 |
-
if args.tensorboard
|
| 373 |
-
else ""
|
| 374 |
-
)
|
| 375 |
-
args.checkpoint_path = os.path.join(args.logs, args.name, "checkpoints")
|
| 376 |
-
for dirname in [args.tensorboard_path, args.checkpoint_path]:
|
| 377 |
-
if dirname:
|
| 378 |
-
os.makedirs(dirname, exist_ok=True)
|
| 379 |
-
else:
|
| 380 |
-
args.tensorboard_path = ""
|
| 381 |
-
args.checkpoint_path = ""
|
| 382 |
-
|
| 383 |
-
if args.copy_codebase:
|
| 384 |
-
copy_codebase(args)
|
| 385 |
-
|
| 386 |
-
assert args.precision in ["amp", "fp16", "fp32"]
|
| 387 |
-
if args.precision == "fp16":
|
| 388 |
-
logging.warning(
|
| 389 |
-
"It is recommended to use AMP mixed-precision instead of FP16. "
|
| 390 |
-
"FP16 support needs further verification and tuning, especially for train."
|
| 391 |
-
)
|
| 392 |
-
|
| 393 |
-
if args.horovod:
|
| 394 |
-
logging.info(
|
| 395 |
-
f"Running in horovod mode with multiple processes / nodes. Device: {args.device}."
|
| 396 |
-
f"Process (global: {args.rank}, local {args.local_rank}), total {args.world_size}."
|
| 397 |
-
)
|
| 398 |
-
elif args.distributed:
|
| 399 |
-
logging.info(
|
| 400 |
-
f"Running in distributed mode with multiple processes. Device: {args.device}."
|
| 401 |
-
f"Process (global: {args.rank}, local {args.local_rank}), total {args.world_size}."
|
| 402 |
-
)
|
| 403 |
-
else:
|
| 404 |
-
logging.info(f"Running with a single process. Device {args.device}.")
|
| 405 |
-
|
| 406 |
-
logging.info(f'openai cache dir: {os.path.expanduser(args.openai_model_cache_dir)}')
|
| 407 |
-
|
| 408 |
-
# Create CLAP model
|
| 409 |
-
clap_model, clap_model_cfg = create_model(
|
| 410 |
-
args.amodel,
|
| 411 |
-
args.tmodel,
|
| 412 |
-
args.pretrained,
|
| 413 |
-
precision=args.precision,
|
| 414 |
-
device=device,
|
| 415 |
-
jit=args.torchscript,
|
| 416 |
-
force_quick_gelu=args.force_quick_gelu,
|
| 417 |
-
openai_model_cache_dir=os.path.expanduser(args.openai_model_cache_dir),
|
| 418 |
-
skip_params=False,
|
| 419 |
-
pretrained_audio=args.pretrained_audio,
|
| 420 |
-
pretrained_text=args.pretrained_text,
|
| 421 |
-
enable_fusion=args.enable_fusion,
|
| 422 |
-
fusion_type=args.fusion_type
|
| 423 |
-
)
|
| 424 |
-
|
| 425 |
-
args.lp_out_ch = len(list(args.class_index_dict.keys()))
|
| 426 |
-
# Linear Probe
|
| 427 |
-
logging.info(f"linear probe using mlp: {args.lp_mlp}")
|
| 428 |
-
logging.info(f"linear probe using freeze: {args.lp_freeze}")
|
| 429 |
-
logging.info(f"linear probe act layer: {args.lp_act}")
|
| 430 |
-
logging.info(f"linear probe out ch: {args.lp_out_ch}")
|
| 431 |
-
logging.info(f"linear probe learning rate (if applicable): {args.lp_lr}")
|
| 432 |
-
logging.info(f"linear probe loss func: {args.lp_loss}")
|
| 433 |
-
logging.info(f"linear probe lp_metrics: {args.lp_metrics}")
|
| 434 |
-
|
| 435 |
-
model = LinearProbe(
|
| 436 |
-
clap_model,
|
| 437 |
-
mlp=args.lp_mlp, freeze=args.lp_freeze,
|
| 438 |
-
in_ch=512, out_ch=args.lp_out_ch,
|
| 439 |
-
act=args.lp_act
|
| 440 |
-
) # in_ch is fixed (i.e., 512)
|
| 441 |
-
model = model.to(device)
|
| 442 |
-
|
| 443 |
-
if args.horovod:
|
| 444 |
-
with torch.no_grad():
|
| 445 |
-
for param in model.parameters():
|
| 446 |
-
param.set_(param.contiguous())
|
| 447 |
-
|
| 448 |
-
if args.trace:
|
| 449 |
-
model = trace_model(model, batch_size=args.batch_size, device=device)
|
| 450 |
-
|
| 451 |
-
if is_master(args):
|
| 452 |
-
logging.info("Linear Probe CLAP Model:")
|
| 453 |
-
logging.info(f"{str(clap_model)}")
|
| 454 |
-
logging.info("Params:")
|
| 455 |
-
params_file = os.path.join(args.logs, args.name, "params.txt")
|
| 456 |
-
with open(params_file, "w") as f:
|
| 457 |
-
for name in sorted(vars(args)):
|
| 458 |
-
val = getattr(args, name)
|
| 459 |
-
logging.info(f" {name}: {val}")
|
| 460 |
-
f.write(f"{name}: {val}\n")
|
| 461 |
-
|
| 462 |
-
if args.distributed and not args.horovod:
|
| 463 |
-
if args.use_bn_sync:
|
| 464 |
-
model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model)
|
| 465 |
-
ddp_args = {}
|
| 466 |
-
if args.ddp_static_graph:
|
| 467 |
-
# this doesn't exist in older PyTorch, arg only added if enabled
|
| 468 |
-
ddp_args["static_graph"] = True
|
| 469 |
-
model = torch.nn.parallel.DistributedDataParallel(
|
| 470 |
-
model, device_ids=[device], find_unused_parameters=True, **ddp_args
|
| 471 |
-
)
|
| 472 |
-
|
| 473 |
-
data = get_data(args, clap_model_cfg)
|
| 474 |
-
assert len(data), "At least one train or eval dataset must be specified."
|
| 475 |
-
if args.trace:
|
| 476 |
-
assert "train" not in data, "Cannot train with traced model"
|
| 477 |
-
|
| 478 |
-
|
| 479 |
-
optimizer, scheduler, text_freeze_parameters = config_lp_optimizer(model, data, args)
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
scaler = GradScaler() if args.precision == "amp" else None
|
| 483 |
-
|
| 484 |
-
# optionally resume from a checkpoint
|
| 485 |
-
start_epoch = 0
|
| 486 |
-
if args.resume is not None:
|
| 487 |
-
if os.path.isfile(args.resume):
|
| 488 |
-
checkpoint = torch.load(args.resume, map_location=device)
|
| 489 |
-
if "epoch" in checkpoint:
|
| 490 |
-
# resuming a train checkpoint w/ epoch and optimizer state
|
| 491 |
-
start_epoch = checkpoint["epoch"]
|
| 492 |
-
sd = checkpoint["state_dict"]
|
| 493 |
-
if not args.distributed and next(iter(sd.items()))[0].startswith(
|
| 494 |
-
"module"
|
| 495 |
-
):
|
| 496 |
-
sd = {k[len("module.") :]: v for k, v in sd.items()}
|
| 497 |
-
model.load_state_dict(sd)
|
| 498 |
-
if args.split_opt:
|
| 499 |
-
if optimizer is not None:
|
| 500 |
-
for k, o_ in optimizer.items():
|
| 501 |
-
o_.load_state_dict(checkpoint[k + "_" + "optimizer"])
|
| 502 |
-
if optimizer is not None:
|
| 503 |
-
optimizer.load_state_dict(checkpoint["optimizer"])
|
| 504 |
-
if scaler is not None and "scaler" in checkpoint:
|
| 505 |
-
scaler.load_state_dict(checkpoint["scaler"])
|
| 506 |
-
logging.info(
|
| 507 |
-
f"=> resuming checkpoint '{args.resume}' (epoch {start_epoch})"
|
| 508 |
-
)
|
| 509 |
-
else:
|
| 510 |
-
# loading a bare (model only) checkpoint for fine-tune or evaluation
|
| 511 |
-
model.load_state_dict(checkpoint)
|
| 512 |
-
logging.info(
|
| 513 |
-
f"=> loaded checkpoint '{args.resume}' (epoch {start_epoch})"
|
| 514 |
-
)
|
| 515 |
-
if args.freeze_text:
|
| 516 |
-
print("Freeze Text!!!!")
|
| 517 |
-
for k in text_freeze_parameters:
|
| 518 |
-
k.requires_grad = False
|
| 519 |
-
else:
|
| 520 |
-
logging.info("=> no checkpoint found at '{}'".format(args.resume))
|
| 521 |
-
|
| 522 |
-
cudnn.benchmark = True
|
| 523 |
-
cudnn.deterministic = False
|
| 524 |
-
|
| 525 |
-
# determine if this worker should save logs and checkpoints. only do so if it is rank == 0
|
| 526 |
-
args.save_logs = args.logs and args.logs.lower() != "none" and is_master(args)
|
| 527 |
-
writer = None
|
| 528 |
-
if args.save_logs and args.tensorboard:
|
| 529 |
-
assert tensorboard is not None, "Please install tensorboard."
|
| 530 |
-
writer = tensorboard.SummaryWriter(args.tensorboard_path)
|
| 531 |
-
|
| 532 |
-
if args.wandb and is_master(args):
|
| 533 |
-
assert wandb is not None, "Please install wandb."
|
| 534 |
-
logging.debug("Starting wandb.")
|
| 535 |
-
args.train_sz = data["train"].dataloader.num_samples
|
| 536 |
-
if args.val_data is not None:
|
| 537 |
-
args.val_sz = data["val"].dataloader.num_samples
|
| 538 |
-
# you will have to configure this for your project!
|
| 539 |
-
wandb.init(
|
| 540 |
-
project="clap",
|
| 541 |
-
notes=args.wandb_notes,
|
| 542 |
-
name=args.wandb_notes,
|
| 543 |
-
tags=[],
|
| 544 |
-
config=vars(args),
|
| 545 |
-
)
|
| 546 |
-
if args.debug:
|
| 547 |
-
wandb.watch(model, log="all")
|
| 548 |
-
wandb.save(params_file)
|
| 549 |
-
logging.debug("Finished loading wandb.")
|
| 550 |
-
|
| 551 |
-
if "train" not in data:
|
| 552 |
-
evaluate(model, data, start_epoch, args, writer)
|
| 553 |
-
return
|
| 554 |
-
elif start_epoch == 0 and "val" in data and not args.no_eval:
|
| 555 |
-
evaluate(model, data, 0, args, writer)
|
| 556 |
-
if args.save_top_performance:
|
| 557 |
-
current_top_k_ckpt_metrics = {
|
| 558 |
-
i: 0 for i in range(args.save_top_performance)
|
| 559 |
-
} # initialize the top-k metric for ckpts to 0
|
| 560 |
-
|
| 561 |
-
for epoch in range(start_epoch, args.epochs):
|
| 562 |
-
# freeze the text param after (include) args.freeze_text_after, this is -1 by default
|
| 563 |
-
if epoch == args.freeze_text_after:
|
| 564 |
-
print("Text pretrained parameters are freezed since this epoch.")
|
| 565 |
-
for k in text_freeze_parameters:
|
| 566 |
-
k.requires_grad = False
|
| 567 |
-
if is_master(args):
|
| 568 |
-
logging.info(f"Start epoch {epoch}")
|
| 569 |
-
|
| 570 |
-
train_one_epoch(model, data, epoch, optimizer, scaler, scheduler, args, writer)
|
| 571 |
-
completed_epoch = epoch + 1
|
| 572 |
-
|
| 573 |
-
if any(v in data for v in ("val", "imagenet-val", "imagenet-v2")) and not args.no_eval:
|
| 574 |
-
metrics = evaluate(model, data, completed_epoch, args, writer)
|
| 575 |
-
if args.save_top_performance:
|
| 576 |
-
top_k_dataset = args.top_k_checkpoint_select_dataset
|
| 577 |
-
top_k_metric = args.top_k_checkpoint_select_metric
|
| 578 |
-
filtered_metrics = [
|
| 579 |
-
v
|
| 580 |
-
for k, v in metrics.items()
|
| 581 |
-
if top_k_metric in k and top_k_dataset in k
|
| 582 |
-
] # check all R@10 metrics (all dataset) and use it to update the ckpt
|
| 583 |
-
# Saving checkpoints.
|
| 584 |
-
if args.save_logs:
|
| 585 |
-
opt_dict = {
|
| 586 |
-
k + "_" + "optimizer": v.state_dict() for k, v in optimizer.items()
|
| 587 |
-
}
|
| 588 |
-
checkpoint_dict = {
|
| 589 |
-
"epoch": completed_epoch,
|
| 590 |
-
"name": args.name,
|
| 591 |
-
"state_dict": model.state_dict(),
|
| 592 |
-
}
|
| 593 |
-
checkpoint_dict.update(opt_dict)
|
| 594 |
-
if scaler is not None:
|
| 595 |
-
checkpoint_dict["scaler"] = scaler.state_dict()
|
| 596 |
-
|
| 597 |
-
if completed_epoch == args.epochs or (
|
| 598 |
-
args.save_frequency > 0 and (completed_epoch % args.save_frequency) == 0
|
| 599 |
-
):
|
| 600 |
-
torch.save(
|
| 601 |
-
checkpoint_dict,
|
| 602 |
-
os.path.join(args.checkpoint_path, f"epoch_{completed_epoch}.pt"),
|
| 603 |
-
)
|
| 604 |
-
if args.save_most_recent:
|
| 605 |
-
torch.save(
|
| 606 |
-
checkpoint_dict,
|
| 607 |
-
os.path.join(args.checkpoint_path, f"epoch_latest.pt"),
|
| 608 |
-
)
|
| 609 |
-
if args.save_top_performance and not args.no_eval:
|
| 610 |
-
update_top_k_performance(
|
| 611 |
-
filtered_metrics,
|
| 612 |
-
current_top_k_ckpt_metrics,
|
| 613 |
-
args,
|
| 614 |
-
checkpoint_dict,
|
| 615 |
-
bignumbetter=True,
|
| 616 |
-
)
|
| 617 |
-
|
| 618 |
-
if args.wandb and is_master(args):
|
| 619 |
-
wandb.finish()
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
def copy_codebase(args):
|
| 623 |
-
from shutil import copytree, ignore_patterns
|
| 624 |
-
|
| 625 |
-
new_code_path = os.path.join(args.logs, args.name, "code")
|
| 626 |
-
if os.path.exists(new_code_path):
|
| 627 |
-
print(
|
| 628 |
-
f"Error. Experiment already exists at {new_code_path}. Use --name to specify a new experiment."
|
| 629 |
-
)
|
| 630 |
-
return -1
|
| 631 |
-
print(f"Copying codebase to {new_code_path}")
|
| 632 |
-
current_code_path = os.path.realpath(__file__)
|
| 633 |
-
for _ in range(3):
|
| 634 |
-
current_code_path = os.path.dirname(current_code_path)
|
| 635 |
-
copytree(
|
| 636 |
-
current_code_path, new_code_path, ignore=ignore_patterns("log", "logs", "wandb")
|
| 637 |
-
)
|
| 638 |
-
print("Done copying code.")
|
| 639 |
-
return 1
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
if __name__ == "__main__":
|
| 643 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/training/lp_train.py
DELETED
|
@@ -1,292 +0,0 @@
|
|
| 1 |
-
import json
|
| 2 |
-
import logging
|
| 3 |
-
import math
|
| 4 |
-
import os
|
| 5 |
-
import time
|
| 6 |
-
from contextlib import suppress
|
| 7 |
-
|
| 8 |
-
import numpy as np
|
| 9 |
-
import torch
|
| 10 |
-
import torch.nn.functional as F
|
| 11 |
-
|
| 12 |
-
try:
|
| 13 |
-
import wandb
|
| 14 |
-
except ImportError:
|
| 15 |
-
wandb = None
|
| 16 |
-
|
| 17 |
-
from clap_module import LPLoss, LPMetrics, lp_gather_features
|
| 18 |
-
from clap_module.utils import do_mixup, get_mix_lambda
|
| 19 |
-
from .distributed import is_master
|
| 20 |
-
from .zero_shot import zero_shot_eval
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
class AverageMeter(object):
|
| 24 |
-
"""Computes and stores the average and current value"""
|
| 25 |
-
|
| 26 |
-
def __init__(self):
|
| 27 |
-
self.reset()
|
| 28 |
-
|
| 29 |
-
def reset(self):
|
| 30 |
-
self.val = 0
|
| 31 |
-
self.avg = 0
|
| 32 |
-
self.sum = 0
|
| 33 |
-
self.count = 0
|
| 34 |
-
|
| 35 |
-
def update(self, val, n=1):
|
| 36 |
-
self.val = val
|
| 37 |
-
self.sum += val * n
|
| 38 |
-
self.count += n
|
| 39 |
-
self.avg = self.sum / self.count
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
def unwrap_model(model):
|
| 43 |
-
if hasattr(model, "module"):
|
| 44 |
-
return model.module
|
| 45 |
-
else:
|
| 46 |
-
return model
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
def train_one_epoch(
|
| 50 |
-
model, data, epoch, optimizer, scaler, scheduler, args, tb_writer=None, extra_suffix=""
|
| 51 |
-
):
|
| 52 |
-
device = torch.device(args.device)
|
| 53 |
-
autocast = torch.cuda.amp.autocast if args.precision == "amp" else suppress
|
| 54 |
-
model.train()
|
| 55 |
-
loss = LPLoss(args.lp_loss)
|
| 56 |
-
|
| 57 |
-
dataloader, sampler = data["train"].dataloader, data["train"].sampler
|
| 58 |
-
if args.distributed and sampler is not None:
|
| 59 |
-
sampler.set_epoch(epoch)
|
| 60 |
-
num_batches_per_epoch = dataloader.num_batches
|
| 61 |
-
sample_digits = math.ceil(math.log(dataloader.num_samples + 1, 10))
|
| 62 |
-
|
| 63 |
-
# for toy dataset
|
| 64 |
-
if args.dataset_type == "toy":
|
| 65 |
-
dataloader.dataset.generate_queue()
|
| 66 |
-
|
| 67 |
-
loss_m = AverageMeter()
|
| 68 |
-
batch_time_m = AverageMeter()
|
| 69 |
-
data_time_m = AverageMeter()
|
| 70 |
-
end = time.time()
|
| 71 |
-
|
| 72 |
-
for i, batch in enumerate(dataloader):
|
| 73 |
-
step = num_batches_per_epoch * epoch + i
|
| 74 |
-
|
| 75 |
-
if isinstance(scheduler, dict):
|
| 76 |
-
for s in scheduler.values():
|
| 77 |
-
s(step)
|
| 78 |
-
else:
|
| 79 |
-
scheduler(step)
|
| 80 |
-
|
| 81 |
-
audio = batch # contains mel_spec, wavform, and longer list
|
| 82 |
-
class_label = batch['class_label']
|
| 83 |
-
# audio = audio.to(device=device, non_blocking=True)
|
| 84 |
-
class_label = class_label.to(device=device, non_blocking=True)
|
| 85 |
-
|
| 86 |
-
if args.mixup:
|
| 87 |
-
# https://github.com/RetroCirce/HTS-Audio-Transformer/blob/main/utils.py#L146
|
| 88 |
-
mix_lambda = torch.from_numpy(get_mix_lambda(0.5, len(audio["waveform"]))).to(device)
|
| 89 |
-
class_label = do_mixup(class_label, mix_lambda)
|
| 90 |
-
else:
|
| 91 |
-
mix_lambda = None
|
| 92 |
-
|
| 93 |
-
data_time_m.update(time.time() - end)
|
| 94 |
-
if isinstance(optimizer, dict):
|
| 95 |
-
for o_ in optimizer.values():
|
| 96 |
-
o_.zero_grad()
|
| 97 |
-
else:
|
| 98 |
-
optimizer.zero_grad()
|
| 99 |
-
|
| 100 |
-
with autocast():
|
| 101 |
-
pred = model(audio, mix_lambda=mix_lambda, device=device)
|
| 102 |
-
total_loss = loss(pred, class_label)
|
| 103 |
-
|
| 104 |
-
if isinstance(optimizer, dict):
|
| 105 |
-
if scaler is not None:
|
| 106 |
-
scaler.scale(total_loss).backward()
|
| 107 |
-
for o_ in optimizer.values():
|
| 108 |
-
if args.horovod:
|
| 109 |
-
o_.synchronize()
|
| 110 |
-
scaler.unscale_(o_)
|
| 111 |
-
with o_.skip_synchronize():
|
| 112 |
-
scaler.step(o_)
|
| 113 |
-
else:
|
| 114 |
-
scaler.step(o_)
|
| 115 |
-
scaler.update()
|
| 116 |
-
else:
|
| 117 |
-
total_loss.backward()
|
| 118 |
-
for o_ in optimizer.values():
|
| 119 |
-
o_.step()
|
| 120 |
-
else:
|
| 121 |
-
if scaler is not None:
|
| 122 |
-
scaler.scale(total_loss).backward()
|
| 123 |
-
if args.horovod:
|
| 124 |
-
optimizer.synchronize()
|
| 125 |
-
scaler.unscale_(optimizer)
|
| 126 |
-
with optimizer.skip_synchronize():
|
| 127 |
-
scaler.step(optimizer)
|
| 128 |
-
else:
|
| 129 |
-
scaler.step(optimizer)
|
| 130 |
-
scaler.update()
|
| 131 |
-
else:
|
| 132 |
-
total_loss.backward()
|
| 133 |
-
optimizer.step()
|
| 134 |
-
|
| 135 |
-
# Note: we clamp to 4.6052 = ln(100), as in the original paper.
|
| 136 |
-
with torch.no_grad():
|
| 137 |
-
unwrap_model(model).clap_model.logit_scale_a.clamp_(0, math.log(100))
|
| 138 |
-
unwrap_model(model).clap_model.logit_scale_t.clamp_(0, math.log(100))
|
| 139 |
-
|
| 140 |
-
batch_time_m.update(time.time() - end)
|
| 141 |
-
end = time.time()
|
| 142 |
-
batch_count = i + 1
|
| 143 |
-
|
| 144 |
-
if is_master(args) and (i % 100 == 0 or batch_count == num_batches_per_epoch):
|
| 145 |
-
if isinstance(audio, dict):
|
| 146 |
-
batch_size = len(audio["waveform"])
|
| 147 |
-
else:
|
| 148 |
-
batch_size = len(audio)
|
| 149 |
-
num_samples = batch_count * batch_size * args.world_size
|
| 150 |
-
samples_per_epoch = dataloader.num_samples
|
| 151 |
-
percent_complete = 100.0 * batch_count / num_batches_per_epoch
|
| 152 |
-
|
| 153 |
-
# NOTE loss is coarsely sampled, just master node and per log update
|
| 154 |
-
loss_m.update(total_loss.item(), batch_size)
|
| 155 |
-
if isinstance(optimizer, dict):
|
| 156 |
-
logging.info(
|
| 157 |
-
f"Train Epoch: {epoch} [{num_samples:>{sample_digits}}/{samples_per_epoch} ({percent_complete:.0f}%)] "
|
| 158 |
-
f"Loss: {loss_m.val:#.5g} ({loss_m.avg:#.4g}) "
|
| 159 |
-
f"Data (t): {data_time_m.avg:.3f} "
|
| 160 |
-
f"Batch (t): {batch_time_m.avg:.3f} "
|
| 161 |
-
f"LR: {[o_.param_groups[0]['lr'] for o_ in optimizer.values()]}"
|
| 162 |
-
)
|
| 163 |
-
log_data = {
|
| 164 |
-
"loss": loss_m.val,
|
| 165 |
-
"data_time": data_time_m.val,
|
| 166 |
-
"batch_time": batch_time_m.val,
|
| 167 |
-
"lr": [o_.param_groups[0]["lr"] for o_ in optimizer.values()],
|
| 168 |
-
}
|
| 169 |
-
else:
|
| 170 |
-
logging.info(
|
| 171 |
-
f"Train Epoch: {epoch} [{num_samples:>{sample_digits}}/{samples_per_epoch} ({percent_complete:.0f}%)] "
|
| 172 |
-
f"Loss: {loss_m.val:#.5g} ({loss_m.avg:#.4g}) "
|
| 173 |
-
f"Data (t): {data_time_m.avg:.3f} "
|
| 174 |
-
f"Batch (t): {batch_time_m.avg:.3f} "
|
| 175 |
-
f"LR: {optimizer.param_groups[0]['lr']:5f} "
|
| 176 |
-
)
|
| 177 |
-
|
| 178 |
-
# Save train loss / etc. Using non avg meter values as loggers have their own smoothing
|
| 179 |
-
log_data = {
|
| 180 |
-
"loss": loss_m.val,
|
| 181 |
-
"data_time": data_time_m.val,
|
| 182 |
-
"batch_time": batch_time_m.val,
|
| 183 |
-
"lr": optimizer.param_groups[0]["lr"],
|
| 184 |
-
}
|
| 185 |
-
for name, val in log_data.items():
|
| 186 |
-
name = f"train{extra_suffix}/{name}"
|
| 187 |
-
if tb_writer is not None:
|
| 188 |
-
tb_writer.add_scalar(name, val, step)
|
| 189 |
-
if args.wandb:
|
| 190 |
-
assert wandb is not None, "Please install wandb."
|
| 191 |
-
wandb.log({name: val, "step": step})
|
| 192 |
-
|
| 193 |
-
# resetting batch / data time meters per log window
|
| 194 |
-
batch_time_m.reset()
|
| 195 |
-
data_time_m.reset()
|
| 196 |
-
# end for
|
| 197 |
-
|
| 198 |
-
def evaluate(model, data, epoch, args, tb_writer=None, extra_suffix=""):
|
| 199 |
-
metrics = {}
|
| 200 |
-
if not args.parallel_eval:
|
| 201 |
-
if not is_master(args):
|
| 202 |
-
return metrics
|
| 203 |
-
device = torch.device(args.device)
|
| 204 |
-
model.eval()
|
| 205 |
-
|
| 206 |
-
# CHANGE
|
| 207 |
-
# zero_shot_metrics = zero_shot_eval(model, data, epoch, args)
|
| 208 |
-
# metrics.update(zero_shot_metrics)
|
| 209 |
-
if is_master(args):
|
| 210 |
-
print('Evaluating...')
|
| 211 |
-
metric_names = args.lp_metrics.split(',')
|
| 212 |
-
eval_tool = LPMetrics(metric_names=metric_names)
|
| 213 |
-
|
| 214 |
-
autocast = torch.cuda.amp.autocast if args.precision == "amp" else suppress
|
| 215 |
-
if "val" in data and (
|
| 216 |
-
args.val_frequency
|
| 217 |
-
and ((epoch % args.val_frequency) == 0 or epoch == args.epochs)
|
| 218 |
-
):
|
| 219 |
-
if args.parallel_eval:
|
| 220 |
-
dataloader, sampler = data["val"].dataloader, data["val"].sampler
|
| 221 |
-
if args.distributed and sampler is not None:
|
| 222 |
-
sampler.set_epoch(epoch)
|
| 223 |
-
samples_per_val = dataloader.num_samples
|
| 224 |
-
else:
|
| 225 |
-
dataloader = data["val"].dataloader
|
| 226 |
-
num_samples = 0
|
| 227 |
-
samples_per_val = dataloader.num_samples
|
| 228 |
-
|
| 229 |
-
eval_info = {
|
| 230 |
-
'pred': [],
|
| 231 |
-
'target': []
|
| 232 |
-
}
|
| 233 |
-
with torch.no_grad():
|
| 234 |
-
for i, batch in enumerate(dataloader):
|
| 235 |
-
audio = batch # contains mel_spec, wavform, and longer list
|
| 236 |
-
class_label = batch['class_label']
|
| 237 |
-
|
| 238 |
-
# audio = audio.to(device=device, non_blocking=True)
|
| 239 |
-
class_label = class_label.to(device=device, non_blocking=True)
|
| 240 |
-
|
| 241 |
-
with autocast():
|
| 242 |
-
pred = model(audio, device=device)
|
| 243 |
-
if args.parallel_eval:
|
| 244 |
-
pred, class_label = lp_gather_features(pred, class_label, args.world_size, args.horovod)
|
| 245 |
-
eval_info['pred'].append(pred)
|
| 246 |
-
eval_info['target'].append(class_label)
|
| 247 |
-
|
| 248 |
-
num_samples += class_label.shape[0]
|
| 249 |
-
|
| 250 |
-
if (i % 100) == 0: # and i != 0:
|
| 251 |
-
logging.info(
|
| 252 |
-
f"Eval Epoch: {epoch} [{num_samples} / {samples_per_val}]"
|
| 253 |
-
)
|
| 254 |
-
|
| 255 |
-
if is_master(args):
|
| 256 |
-
eval_info['pred'] = torch.cat(eval_info['pred'], 0).cpu()
|
| 257 |
-
eval_info['target'] = torch.cat(eval_info['target'], 0).cpu()
|
| 258 |
-
metric_dict = eval_tool.evaluate_mertics(eval_info['pred'], eval_info['target'])
|
| 259 |
-
metrics.update(metric_dict)
|
| 260 |
-
if "epoch" not in metrics.keys():
|
| 261 |
-
metrics.update({"epoch": epoch})
|
| 262 |
-
|
| 263 |
-
if is_master(args):
|
| 264 |
-
if not metrics:
|
| 265 |
-
return metrics
|
| 266 |
-
|
| 267 |
-
logging.info(
|
| 268 |
-
f"Eval Epoch: {epoch} "
|
| 269 |
-
+ "\n".join(
|
| 270 |
-
[
|
| 271 |
-
"\t".join([f"{m}: {round(metrics[m], 4):.4f}" ])
|
| 272 |
-
for m in metrics
|
| 273 |
-
]
|
| 274 |
-
)
|
| 275 |
-
)
|
| 276 |
-
if args.save_logs:
|
| 277 |
-
for name, val in metrics.items():
|
| 278 |
-
if tb_writer is not None:
|
| 279 |
-
tb_writer.add_scalar(f"val{extra_suffix}/{name}", val, epoch)
|
| 280 |
-
|
| 281 |
-
with open(os.path.join(args.checkpoint_path, "results.jsonl"), "a+") as f:
|
| 282 |
-
f.write(json.dumps(metrics))
|
| 283 |
-
f.write("\n")
|
| 284 |
-
|
| 285 |
-
if args.wandb:
|
| 286 |
-
assert wandb is not None, "Please install wandb."
|
| 287 |
-
for name, val in metrics.items():
|
| 288 |
-
wandb.log({f"val{extra_suffix}/{name}": val, "epoch": epoch})
|
| 289 |
-
|
| 290 |
-
return metrics
|
| 291 |
-
else:
|
| 292 |
-
return metrics
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/training/main.py
DELETED
|
@@ -1,597 +0,0 @@
|
|
| 1 |
-
import logging
|
| 2 |
-
import os
|
| 3 |
-
import random
|
| 4 |
-
from datetime import datetime
|
| 5 |
-
import copy
|
| 6 |
-
import numpy as np
|
| 7 |
-
import torch
|
| 8 |
-
import torch.backends.cudnn as cudnn
|
| 9 |
-
from torch.cuda.amp import GradScaler
|
| 10 |
-
|
| 11 |
-
try:
|
| 12 |
-
import wandb
|
| 13 |
-
except ImportError:
|
| 14 |
-
wandb = None
|
| 15 |
-
|
| 16 |
-
try:
|
| 17 |
-
import torch.utils.tensorboard as tensorboard
|
| 18 |
-
except ImportError:
|
| 19 |
-
tensorboard = None
|
| 20 |
-
|
| 21 |
-
try:
|
| 22 |
-
import horovod.torch as hvd
|
| 23 |
-
except ImportError:
|
| 24 |
-
hvd = None
|
| 25 |
-
|
| 26 |
-
from clap_module import create_model_and_transforms, trace_model, create_model
|
| 27 |
-
from training.data import get_data
|
| 28 |
-
from training.distributed import is_master, init_distributed_device, world_info_from_env
|
| 29 |
-
from training.logger import setup_logging
|
| 30 |
-
from training.params import parse_args
|
| 31 |
-
from training.scheduler import cosine_lr
|
| 32 |
-
from training.train import train_one_epoch, evaluate
|
| 33 |
-
from clap_module.utils import dataset_split, get_optimizer
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
def maintain_ckpts(args, startidx, all_idx_len):
|
| 37 |
-
for i in reversed(range(startidx, all_idx_len)):
|
| 38 |
-
if os.path.exists(os.path.join(args.checkpoint_path, f"epoch_top_{i}.pt")):
|
| 39 |
-
os.rename(
|
| 40 |
-
os.path.join(args.checkpoint_path, f"epoch_top_{i}.pt"),
|
| 41 |
-
os.path.join(args.checkpoint_path, f"epoch_top_{i+1}.pt"),
|
| 42 |
-
)
|
| 43 |
-
if os.path.exists(
|
| 44 |
-
os.path.join(args.checkpoint_path, f"epoch_top_{all_idx_len}.pt")
|
| 45 |
-
):
|
| 46 |
-
os.remove(os.path.join(args.checkpoint_path, f"epoch_top_{all_idx_len}.pt"))
|
| 47 |
-
return
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
def update_top_k_performance(
|
| 51 |
-
new_metrics_inputs, current_top_k_ckpt_metrics, args, ckpt, bignumbetter=True
|
| 52 |
-
):
|
| 53 |
-
"""
|
| 54 |
-
Record the top-k performance of the current epoch.
|
| 55 |
-
current_top_k_metrics is a dictionary of the form: {1: top_1_ckpt_measure, 2: top_2_ckpt_measure, ...}
|
| 56 |
-
"""
|
| 57 |
-
if isinstance(new_metrics_inputs, (list, tuple)):
|
| 58 |
-
new_metrics_inputs = np.mean(new_metrics_inputs)
|
| 59 |
-
return update_top_k_performance(
|
| 60 |
-
new_metrics_inputs,
|
| 61 |
-
current_top_k_ckpt_metrics,
|
| 62 |
-
args=args,
|
| 63 |
-
ckpt=ckpt,
|
| 64 |
-
bignumbetter=bignumbetter,
|
| 65 |
-
)
|
| 66 |
-
elif isinstance(new_metrics_inputs, dict):
|
| 67 |
-
new_metrics_inputs = np.mean(list(new_metrics_inputs.values()))
|
| 68 |
-
return update_top_k_performance(
|
| 69 |
-
new_metrics_inputs,
|
| 70 |
-
current_top_k_ckpt_metrics,
|
| 71 |
-
args=args,
|
| 72 |
-
ckpt=ckpt,
|
| 73 |
-
bignumbetter=bignumbetter,
|
| 74 |
-
)
|
| 75 |
-
elif isinstance(new_metrics_inputs, (float, int)):
|
| 76 |
-
update_flag = {k: False for k in current_top_k_ckpt_metrics.keys()}
|
| 77 |
-
sorted_keys = sorted(current_top_k_ckpt_metrics.keys())
|
| 78 |
-
sorted_values = sorted(
|
| 79 |
-
current_top_k_ckpt_metrics.values(), reverse=bignumbetter
|
| 80 |
-
)
|
| 81 |
-
sorted_values_ = copy.deepcopy(sorted_values)
|
| 82 |
-
sorted_values.append(new_metrics_inputs)
|
| 83 |
-
sorted_values = sorted(sorted_values, reverse=bignumbetter)
|
| 84 |
-
sorted_values = sorted_values[:-1]
|
| 85 |
-
|
| 86 |
-
if sorted_values == sorted_values_:
|
| 87 |
-
return current_top_k_ckpt_metrics, new_metrics_inputs
|
| 88 |
-
else:
|
| 89 |
-
for i in range(len(sorted_keys)):
|
| 90 |
-
if current_top_k_ckpt_metrics[sorted_keys[i]] != sorted_values[i]:
|
| 91 |
-
current_top_k_ckpt_metrics[sorted_keys[i]] = sorted_values[i]
|
| 92 |
-
update_flag[sorted_keys[i]] = True
|
| 93 |
-
for i in range(len(update_flag)):
|
| 94 |
-
if update_flag[i]:
|
| 95 |
-
maintain_ckpts(args, i, len(sorted_keys))
|
| 96 |
-
torch.save(
|
| 97 |
-
ckpt,
|
| 98 |
-
os.path.join(args.checkpoint_path, f"epoch_top_{i}.pt"),
|
| 99 |
-
)
|
| 100 |
-
break
|
| 101 |
-
return current_top_k_ckpt_metrics, new_metrics_inputs
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
# def updateifNone(a, b):
|
| 105 |
-
# a = b if None else a
|
| 106 |
-
# return a
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
def is_pretrained_params(n):
|
| 110 |
-
return (
|
| 111 |
-
n.startswith("transformer")
|
| 112 |
-
or n in ["positional_embedding", "text_projection"]
|
| 113 |
-
or n.startswith("token_embedding")
|
| 114 |
-
or n.startswith("ln_final")
|
| 115 |
-
or n.startswith("logit_scale_t")
|
| 116 |
-
)
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
def random_seed(seed=42, rank=0):
|
| 120 |
-
torch.manual_seed(seed + rank)
|
| 121 |
-
np.random.seed(seed + rank)
|
| 122 |
-
random.seed(seed + rank)
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
def main():
|
| 126 |
-
args = parse_args()
|
| 127 |
-
# sanitize model name for filesystem / uri use, easier if we don't use / in name as a rule?
|
| 128 |
-
args.amodel = args.amodel.replace("/", "-")
|
| 129 |
-
# download sizes.json file
|
| 130 |
-
|
| 131 |
-
# (yusong): the below two lines are for debug
|
| 132 |
-
# print("setting up faulthandler")
|
| 133 |
-
# faulthandler.register(10)
|
| 134 |
-
|
| 135 |
-
random.seed(args.seed)
|
| 136 |
-
torch.manual_seed(args.seed)
|
| 137 |
-
torch.cuda.manual_seed(args.seed)
|
| 138 |
-
torch.cuda.manual_seed_all(args.seed)
|
| 139 |
-
np.random.seed(args.seed)
|
| 140 |
-
if args.tmodel == "bert" or args.tmodel == "roberta" or args.tmodel == "bart":
|
| 141 |
-
assert (
|
| 142 |
-
args.pretrained == "" or args.pretrained is None
|
| 143 |
-
), "bert/roberta/bart text encoder does not support pretrained models."
|
| 144 |
-
|
| 145 |
-
# get the name of the experiments
|
| 146 |
-
if args.name is None:
|
| 147 |
-
args.name = "-".join(
|
| 148 |
-
[
|
| 149 |
-
datetime.now().strftime("%Y_%m_%d-%H_%M_%S"),
|
| 150 |
-
f"model_{args.amodel}",
|
| 151 |
-
f"lr_{args.lr}",
|
| 152 |
-
f"b_{args.batch_size}",
|
| 153 |
-
f"j_{args.workers}",
|
| 154 |
-
f"p_{args.precision}",
|
| 155 |
-
]
|
| 156 |
-
)
|
| 157 |
-
|
| 158 |
-
# discover initial world args early so we can log properly
|
| 159 |
-
args.distributed = False
|
| 160 |
-
args.local_rank, args.rank, args.world_size = world_info_from_env()
|
| 161 |
-
|
| 162 |
-
if args.remotedata and is_master(args):
|
| 163 |
-
for dataset_name in args.datasetnames:
|
| 164 |
-
for split in dataset_split[dataset_name]:
|
| 165 |
-
if not os.path.exists(f"./json_files/{dataset_name}/{split}"):
|
| 166 |
-
os.makedirs(f"./json_files/{dataset_name}/{split}")
|
| 167 |
-
os.system(
|
| 168 |
-
f"aws s3 cp s3://s-laion-audio/webdataset_tar/{dataset_name}/{split}/sizes.json ./json_files/{dataset_name}/{split}/sizes.json"
|
| 169 |
-
)
|
| 170 |
-
|
| 171 |
-
args.log_path = None
|
| 172 |
-
if is_master(args, local=args.log_local):
|
| 173 |
-
log_base_path = os.path.join(args.logs, args.name)
|
| 174 |
-
os.makedirs(log_base_path, exist_ok=True)
|
| 175 |
-
log_filename = f"out-{args.rank}" if args.log_local else "out.log"
|
| 176 |
-
args.log_path = os.path.join(log_base_path, log_filename)
|
| 177 |
-
if os.path.exists(args.log_path):
|
| 178 |
-
print(
|
| 179 |
-
"Error. Experiment already exists. Use --name {} to specify a new experiment."
|
| 180 |
-
)
|
| 181 |
-
return -1
|
| 182 |
-
|
| 183 |
-
# Set logger
|
| 184 |
-
args.log_level = logging.DEBUG if args.debug else logging.INFO
|
| 185 |
-
setup_logging(args.log_path, args.log_level)
|
| 186 |
-
|
| 187 |
-
# fully initialize distributed device environment
|
| 188 |
-
device = init_distributed_device(args)
|
| 189 |
-
|
| 190 |
-
args.wandb = "wandb" in args.report_to or "all" in args.report_to
|
| 191 |
-
args.tensorboard = "tensorboard" in args.report_to or "all" in args.report_to
|
| 192 |
-
if is_master(args):
|
| 193 |
-
args.tensorboard_path = (
|
| 194 |
-
os.path.join(args.logs, args.name, "tensorboard")
|
| 195 |
-
if args.tensorboard
|
| 196 |
-
else ""
|
| 197 |
-
)
|
| 198 |
-
args.checkpoint_path = os.path.join(args.logs, args.name, "checkpoints")
|
| 199 |
-
for dirname in [args.tensorboard_path, args.checkpoint_path]:
|
| 200 |
-
if dirname:
|
| 201 |
-
os.makedirs(dirname, exist_ok=True)
|
| 202 |
-
else:
|
| 203 |
-
args.tensorboard_path = ""
|
| 204 |
-
args.checkpoint_path = ""
|
| 205 |
-
|
| 206 |
-
if args.copy_codebase:
|
| 207 |
-
copy_codebase(args)
|
| 208 |
-
|
| 209 |
-
assert args.precision in ["amp", "fp16", "fp32"]
|
| 210 |
-
if args.precision == "fp16":
|
| 211 |
-
logging.warning(
|
| 212 |
-
"It is recommended to use fp32 mixed-precision instead of FP16 and AMP in this model. "
|
| 213 |
-
"They will cause NaN loss and NaN gradients. "
|
| 214 |
-
"FP16 and AMP support needs further verification and tuning, especially for train."
|
| 215 |
-
)
|
| 216 |
-
|
| 217 |
-
if args.horovod:
|
| 218 |
-
logging.info(
|
| 219 |
-
f"Running in horovod mode with multiple processes / nodes. Device: {args.device}."
|
| 220 |
-
f"Process (global: {args.rank}, local {args.local_rank}), total {args.world_size}."
|
| 221 |
-
)
|
| 222 |
-
elif args.distributed:
|
| 223 |
-
logging.info(
|
| 224 |
-
f"Running in distributed mode with multiple processes. Device: {args.device}."
|
| 225 |
-
f"Process (global: {args.rank}, local {args.local_rank}), total {args.world_size}."
|
| 226 |
-
)
|
| 227 |
-
else:
|
| 228 |
-
logging.info(f"Running with a single process. Device {args.device}.")
|
| 229 |
-
|
| 230 |
-
logging.info(f"openai cache dir: {os.path.expanduser(args.openai_model_cache_dir)}")
|
| 231 |
-
|
| 232 |
-
model, model_cfg = create_model(
|
| 233 |
-
args.amodel,
|
| 234 |
-
args.tmodel,
|
| 235 |
-
args.pretrained,
|
| 236 |
-
precision=args.precision,
|
| 237 |
-
device=device,
|
| 238 |
-
jit=args.torchscript,
|
| 239 |
-
force_quick_gelu=args.force_quick_gelu,
|
| 240 |
-
openai_model_cache_dir=os.path.expanduser(args.openai_model_cache_dir),
|
| 241 |
-
skip_params=True,
|
| 242 |
-
pretrained_audio=args.pretrained_audio,
|
| 243 |
-
pretrained_text=args.pretrained_text,
|
| 244 |
-
enable_fusion=args.enable_fusion,
|
| 245 |
-
fusion_type=args.fusion_type
|
| 246 |
-
)
|
| 247 |
-
|
| 248 |
-
if args.horovod:
|
| 249 |
-
with torch.no_grad():
|
| 250 |
-
for param in model.parameters():
|
| 251 |
-
param.set_(param.contiguous())
|
| 252 |
-
|
| 253 |
-
if args.trace:
|
| 254 |
-
model = trace_model(model, batch_size=args.batch_size, device=device)
|
| 255 |
-
|
| 256 |
-
if is_master(args):
|
| 257 |
-
logging.info("Model:")
|
| 258 |
-
logging.info(f"{str(model)}")
|
| 259 |
-
logging.info("Params:")
|
| 260 |
-
params_file = os.path.join(args.logs, args.name, "params.txt")
|
| 261 |
-
with open(params_file, "w") as f:
|
| 262 |
-
for name in sorted(vars(args)):
|
| 263 |
-
val = getattr(args, name)
|
| 264 |
-
logging.info(f" {name}: {val}")
|
| 265 |
-
f.write(f"{name}: {val}\n")
|
| 266 |
-
|
| 267 |
-
if args.distributed and not args.horovod:
|
| 268 |
-
if args.use_bn_sync:
|
| 269 |
-
model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model)
|
| 270 |
-
ddp_args = {}
|
| 271 |
-
if args.ddp_static_graph:
|
| 272 |
-
# this doesn't exist in older PyTorch, arg only added if enabled
|
| 273 |
-
ddp_args["static_graph"] = True
|
| 274 |
-
model = torch.nn.parallel.DistributedDataParallel(
|
| 275 |
-
model, device_ids=[device], find_unused_parameters=True, **ddp_args
|
| 276 |
-
)
|
| 277 |
-
|
| 278 |
-
data = get_data(args, model_cfg)
|
| 279 |
-
assert len(data), "At least one train or eval dataset must be specified."
|
| 280 |
-
if args.trace:
|
| 281 |
-
assert "train" not in data, "Cannot train with traced model"
|
| 282 |
-
|
| 283 |
-
exclude = (
|
| 284 |
-
lambda n, p: p.ndim < 2
|
| 285 |
-
or "bn" in n
|
| 286 |
-
or "ln" in n
|
| 287 |
-
or "bias" in n
|
| 288 |
-
or "logit_scale" in n
|
| 289 |
-
)
|
| 290 |
-
include = lambda n, p: not exclude(n, p)
|
| 291 |
-
|
| 292 |
-
named_parameters = list(model.named_parameters())
|
| 293 |
-
|
| 294 |
-
# freeze text encoder
|
| 295 |
-
text_freeze_parameters = [
|
| 296 |
-
p
|
| 297 |
-
for n, p in named_parameters
|
| 298 |
-
if 'text_branch' in n
|
| 299 |
-
]
|
| 300 |
-
|
| 301 |
-
if args.freeze_text:
|
| 302 |
-
print("Freeze Text!!!!")
|
| 303 |
-
for k in text_freeze_parameters:
|
| 304 |
-
k.requires_grad = False
|
| 305 |
-
|
| 306 |
-
gain_or_bias_params = [
|
| 307 |
-
p for n, p in named_parameters if exclude(n, p) and p.requires_grad
|
| 308 |
-
]
|
| 309 |
-
rest_params = [p for n, p in named_parameters if include(n, p) and p.requires_grad]
|
| 310 |
-
|
| 311 |
-
# set wd-related params to 0 if use adam optimizer
|
| 312 |
-
if args.optimizer == "adam":
|
| 313 |
-
args.wd = 0
|
| 314 |
-
args.wd_pretrained = 0
|
| 315 |
-
args.wd_new = 0
|
| 316 |
-
|
| 317 |
-
if args.train_data is None:
|
| 318 |
-
optimizer = None
|
| 319 |
-
scheduler = None
|
| 320 |
-
else:
|
| 321 |
-
total_steps = data["train"].dataloader.num_batches * args.epochs
|
| 322 |
-
|
| 323 |
-
if args.split_opt:
|
| 324 |
-
for x in ["lr", "beta1", "beta2", "eps", "wd"]:
|
| 325 |
-
for y in ["_new", "_pretrained"]:
|
| 326 |
-
if getattr(args, x + y) is None:
|
| 327 |
-
setattr(args, x + y, getattr(args, x))
|
| 328 |
-
|
| 329 |
-
gain_or_bias_pretrained_params = [
|
| 330 |
-
p
|
| 331 |
-
for n, p in named_parameters
|
| 332 |
-
if (exclude(n, p) and p.requires_grad) and is_pretrained_params(n)
|
| 333 |
-
]
|
| 334 |
-
rest_pretrained_params = [
|
| 335 |
-
p
|
| 336 |
-
for n, p in named_parameters
|
| 337 |
-
if (include(n, p) and p.requires_grad) and is_pretrained_params(n)
|
| 338 |
-
]
|
| 339 |
-
gain_or_bias_new_params = [
|
| 340 |
-
p
|
| 341 |
-
for n, p in named_parameters
|
| 342 |
-
if (exclude(n, p) and p.requires_grad) and (not is_pretrained_params(n))
|
| 343 |
-
]
|
| 344 |
-
rest_new_params = [
|
| 345 |
-
p
|
| 346 |
-
for n, p in named_parameters
|
| 347 |
-
if (include(n, p) and p.requires_grad) and (not is_pretrained_params(n))
|
| 348 |
-
]
|
| 349 |
-
pretrained_params_optimizer = get_optimizer(
|
| 350 |
-
[
|
| 351 |
-
{"params": gain_or_bias_pretrained_params, "weight_decay": 0.0},
|
| 352 |
-
{
|
| 353 |
-
"params": rest_pretrained_params,
|
| 354 |
-
"weight_decay": args.wd_pretrained,
|
| 355 |
-
},
|
| 356 |
-
],
|
| 357 |
-
lr=args.lr_pretrained,
|
| 358 |
-
betas=(args.beta1_pretrained, args.beta2_pretrained),
|
| 359 |
-
eps=args.eps_pretrained,
|
| 360 |
-
momentum=args.momentum_pretrained,
|
| 361 |
-
optimizer_name=args.optimizer,
|
| 362 |
-
)
|
| 363 |
-
pretrained_params_scheduler = cosine_lr(
|
| 364 |
-
pretrained_params_optimizer,
|
| 365 |
-
args.lr_pretrained,
|
| 366 |
-
args.warmup,
|
| 367 |
-
total_steps,
|
| 368 |
-
)
|
| 369 |
-
new_params_optimizer = get_optimizer(
|
| 370 |
-
[
|
| 371 |
-
{"params": gain_or_bias_new_params, "weight_decay": 0.0},
|
| 372 |
-
{"params": rest_new_params, "weight_decay": args.wd_new},
|
| 373 |
-
],
|
| 374 |
-
lr=args.lr_new,
|
| 375 |
-
betas=(args.beta1_new, args.beta2_new),
|
| 376 |
-
eps=args.eps_new,
|
| 377 |
-
momentum=args.momentum_new,
|
| 378 |
-
optimizer_name=args.optimizer,
|
| 379 |
-
)
|
| 380 |
-
|
| 381 |
-
new_params_scheduler = cosine_lr(
|
| 382 |
-
new_params_optimizer, args.lr_new, args.warmup, total_steps
|
| 383 |
-
)
|
| 384 |
-
|
| 385 |
-
optimizer = {
|
| 386 |
-
"pretrained": pretrained_params_optimizer,
|
| 387 |
-
"new": new_params_optimizer,
|
| 388 |
-
}
|
| 389 |
-
scheduler = {
|
| 390 |
-
"pretrained": pretrained_params_scheduler,
|
| 391 |
-
"new": new_params_scheduler,
|
| 392 |
-
}
|
| 393 |
-
|
| 394 |
-
if args.horovod:
|
| 395 |
-
pretrained_params_optimizer = hvd.DistributedOptimizer(
|
| 396 |
-
pretrained_params_optimizer,
|
| 397 |
-
named_parameters=model.named_parameters(),
|
| 398 |
-
)
|
| 399 |
-
new_params_optimizer = hvd.DistributedOptimizer(
|
| 400 |
-
new_params_optimizer, named_parameters=model.named_parameters()
|
| 401 |
-
)
|
| 402 |
-
hvd.broadcast_parameters(model.state_dict(), root_rank=0)
|
| 403 |
-
hvd.broadcast_optimizer_state(pretrained_params_optimizer, root_rank=0)
|
| 404 |
-
hvd.broadcast_optimizer_state(new_params_optimizer, root_rank=0)
|
| 405 |
-
else:
|
| 406 |
-
optimizer = get_optimizer(
|
| 407 |
-
[
|
| 408 |
-
{"params": gain_or_bias_params, "weight_decay": 0.0},
|
| 409 |
-
{"params": rest_params, "weight_decay": args.wd},
|
| 410 |
-
],
|
| 411 |
-
lr=args.lr,
|
| 412 |
-
betas=(args.beta1, args.beta2),
|
| 413 |
-
eps=args.eps,
|
| 414 |
-
momentum=args.momentum,
|
| 415 |
-
optimizer_name=args.optimizer,
|
| 416 |
-
)
|
| 417 |
-
|
| 418 |
-
scheduler = cosine_lr(optimizer, args.lr, args.warmup, total_steps)
|
| 419 |
-
|
| 420 |
-
if args.horovod:
|
| 421 |
-
optimizer = hvd.DistributedOptimizer(
|
| 422 |
-
optimizer, named_parameters=model.named_parameters()
|
| 423 |
-
)
|
| 424 |
-
hvd.broadcast_parameters(model.state_dict(), root_rank=0)
|
| 425 |
-
hvd.broadcast_optimizer_state(optimizer, root_rank=0)
|
| 426 |
-
|
| 427 |
-
scaler = GradScaler() if args.precision == "amp" else None
|
| 428 |
-
|
| 429 |
-
# optionally resume from a checkpoint
|
| 430 |
-
start_epoch = 0
|
| 431 |
-
if args.resume is not None:
|
| 432 |
-
if os.path.isfile(args.resume):
|
| 433 |
-
checkpoint = torch.load(args.resume, map_location=device)
|
| 434 |
-
if "epoch" in checkpoint:
|
| 435 |
-
# resuming a train checkpoint w/ epoch and optimizer state
|
| 436 |
-
start_epoch = checkpoint["epoch"]
|
| 437 |
-
sd = checkpoint["state_dict"]
|
| 438 |
-
if not args.distributed and next(iter(sd.items()))[0].startswith(
|
| 439 |
-
"module"
|
| 440 |
-
):
|
| 441 |
-
sd = {k[len("module.") :]: v for k, v in sd.items()}
|
| 442 |
-
model.load_state_dict(sd)
|
| 443 |
-
if args.split_opt:
|
| 444 |
-
if optimizer is not None:
|
| 445 |
-
for k, o_ in optimizer.items():
|
| 446 |
-
o_.load_state_dict(checkpoint[k + "_" + "optimizer"])
|
| 447 |
-
if optimizer is not None:
|
| 448 |
-
optimizer.load_state_dict(checkpoint["optimizer"])
|
| 449 |
-
if scaler is not None and "scaler" in checkpoint:
|
| 450 |
-
scaler.load_state_dict(checkpoint["scaler"])
|
| 451 |
-
logging.info(
|
| 452 |
-
f"=> resuming checkpoint '{args.resume}' (epoch {start_epoch})"
|
| 453 |
-
)
|
| 454 |
-
else:
|
| 455 |
-
# loading a bare (model only) checkpoint for fine-tune or evaluation
|
| 456 |
-
model.load_state_dict(checkpoint)
|
| 457 |
-
logging.info(
|
| 458 |
-
f"=> loaded checkpoint '{args.resume}' (epoch {start_epoch})"
|
| 459 |
-
)
|
| 460 |
-
if args.freeze_text:
|
| 461 |
-
print("Freeze Text!!!!")
|
| 462 |
-
for k in text_freeze_parameters:
|
| 463 |
-
k.requires_grad = False
|
| 464 |
-
else:
|
| 465 |
-
logging.info("=> no checkpoint found at '{}'".format(args.resume))
|
| 466 |
-
|
| 467 |
-
cudnn.benchmark = True
|
| 468 |
-
cudnn.deterministic = False
|
| 469 |
-
|
| 470 |
-
# determine if this worker should save logs and checkpoints. only do so if it is rank == 0
|
| 471 |
-
args.save_logs = args.logs and args.logs.lower() != "none" and is_master(args)
|
| 472 |
-
writer = None
|
| 473 |
-
if args.save_logs and args.tensorboard:
|
| 474 |
-
assert tensorboard is not None, "Please install tensorboard."
|
| 475 |
-
writer = tensorboard.SummaryWriter(args.tensorboard_path)
|
| 476 |
-
|
| 477 |
-
if args.wandb and is_master(args):
|
| 478 |
-
assert wandb is not None, "Please install wandb."
|
| 479 |
-
logging.debug("Starting wandb.")
|
| 480 |
-
args.train_sz = data["train"].dataloader.num_samples
|
| 481 |
-
if args.val_data is not None:
|
| 482 |
-
args.val_sz = data["val"].dataloader.num_samples
|
| 483 |
-
# you will have to configure this for your project!
|
| 484 |
-
wandb.init(
|
| 485 |
-
entity="clap",
|
| 486 |
-
project="clap",
|
| 487 |
-
notes=args.wandb_notes,
|
| 488 |
-
name=args.wandb_notes,
|
| 489 |
-
tags=[],
|
| 490 |
-
config=vars(args),
|
| 491 |
-
)
|
| 492 |
-
if args.debug:
|
| 493 |
-
wandb.watch(model, log="all")
|
| 494 |
-
wandb.save(params_file)
|
| 495 |
-
logging.debug("Finished loading wandb.")
|
| 496 |
-
|
| 497 |
-
if "train" not in data:
|
| 498 |
-
evaluate(model, data, start_epoch, args, writer)
|
| 499 |
-
return
|
| 500 |
-
elif start_epoch == 0 and "val" in data and not args.no_eval:
|
| 501 |
-
evaluate(model, data, 0, args, writer)
|
| 502 |
-
# print(f'rank {args.rank}, Start First Evaluation')# (yusong): for debug
|
| 503 |
-
if args.save_top_performance:
|
| 504 |
-
current_top_k_ckpt_metrics = {
|
| 505 |
-
i: 0 for i in range(args.save_top_performance)
|
| 506 |
-
} # initialize the top-k metric for ckpts to 0
|
| 507 |
-
|
| 508 |
-
# print(f'rank {args.rank}, Start Training') # (yusong): for debug
|
| 509 |
-
for epoch in range(start_epoch, args.epochs):
|
| 510 |
-
# freeze the text param after (include) args.freeze_text_after, this is -1 by default
|
| 511 |
-
if epoch == args.freeze_text_after:
|
| 512 |
-
print("Text pretrained parameters are freezed since this epoch.")
|
| 513 |
-
for k in text_freeze_parameters:
|
| 514 |
-
k.requires_grad = False
|
| 515 |
-
if is_master(args):
|
| 516 |
-
logging.info(f"Start epoch {epoch}")
|
| 517 |
-
|
| 518 |
-
train_one_epoch(model, data, epoch, optimizer, scaler, scheduler, args, writer)
|
| 519 |
-
completed_epoch = epoch + 1
|
| 520 |
-
|
| 521 |
-
if (
|
| 522 |
-
any(v in data for v in ("val", "imagenet-val", "imagenet-v2"))
|
| 523 |
-
and not args.no_eval
|
| 524 |
-
):
|
| 525 |
-
metrics = evaluate(model, data, completed_epoch, args, writer)
|
| 526 |
-
if args.save_top_performance:
|
| 527 |
-
top_k_dataset = args.top_k_checkpoint_select_dataset
|
| 528 |
-
top_k_metric = args.top_k_checkpoint_select_metric
|
| 529 |
-
filtered_metrics = [
|
| 530 |
-
v
|
| 531 |
-
for k, v in metrics.items()
|
| 532 |
-
if top_k_metric in k and top_k_dataset in k
|
| 533 |
-
] # check all R@10 metrics (all dataset) and use it to update the ckpt
|
| 534 |
-
# Saving checkpoints.
|
| 535 |
-
if args.save_logs:
|
| 536 |
-
if args.split_opt:
|
| 537 |
-
opt_dict = {
|
| 538 |
-
k + "_" + "optimizer": v.state_dict() for k, v in optimizer.items()
|
| 539 |
-
}
|
| 540 |
-
else:
|
| 541 |
-
opt_dict = {"optimizer": optimizer.state_dict()}
|
| 542 |
-
checkpoint_dict = {
|
| 543 |
-
"epoch": completed_epoch,
|
| 544 |
-
"name": args.name,
|
| 545 |
-
"state_dict": model.state_dict(),
|
| 546 |
-
}
|
| 547 |
-
checkpoint_dict.update(opt_dict)
|
| 548 |
-
if scaler is not None:
|
| 549 |
-
checkpoint_dict["scaler"] = scaler.state_dict()
|
| 550 |
-
|
| 551 |
-
if completed_epoch == args.epochs or (
|
| 552 |
-
args.save_frequency > 0 and (completed_epoch % args.save_frequency) == 0
|
| 553 |
-
):
|
| 554 |
-
torch.save(
|
| 555 |
-
checkpoint_dict,
|
| 556 |
-
os.path.join(args.checkpoint_path, f"epoch_{completed_epoch}.pt"),
|
| 557 |
-
)
|
| 558 |
-
if args.save_most_recent:
|
| 559 |
-
torch.save(
|
| 560 |
-
checkpoint_dict,
|
| 561 |
-
os.path.join(args.checkpoint_path, f"epoch_latest.pt"),
|
| 562 |
-
)
|
| 563 |
-
if args.save_top_performance and not args.no_eval:
|
| 564 |
-
update_top_k_performance(
|
| 565 |
-
filtered_metrics,
|
| 566 |
-
current_top_k_ckpt_metrics,
|
| 567 |
-
args,
|
| 568 |
-
checkpoint_dict,
|
| 569 |
-
bignumbetter=True,
|
| 570 |
-
)
|
| 571 |
-
|
| 572 |
-
if args.wandb and is_master(args):
|
| 573 |
-
wandb.finish()
|
| 574 |
-
|
| 575 |
-
|
| 576 |
-
def copy_codebase(args):
|
| 577 |
-
from shutil import copytree, ignore_patterns
|
| 578 |
-
|
| 579 |
-
new_code_path = os.path.join(args.logs, args.name, "code")
|
| 580 |
-
if os.path.exists(new_code_path):
|
| 581 |
-
print(
|
| 582 |
-
f"Error. Experiment already exists at {new_code_path}. Use --name to specify a new experiment."
|
| 583 |
-
)
|
| 584 |
-
return -1
|
| 585 |
-
print(f"Copying codebase to {new_code_path}")
|
| 586 |
-
current_code_path = os.path.realpath(__file__)
|
| 587 |
-
for _ in range(3):
|
| 588 |
-
current_code_path = os.path.dirname(current_code_path)
|
| 589 |
-
copytree(
|
| 590 |
-
current_code_path, new_code_path, ignore=ignore_patterns("log", "logs", "wandb")
|
| 591 |
-
)
|
| 592 |
-
print("Done copying code.")
|
| 593 |
-
return 1
|
| 594 |
-
|
| 595 |
-
|
| 596 |
-
if __name__ == "__main__":
|
| 597 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/training/params.py
DELETED
|
@@ -1,567 +0,0 @@
|
|
| 1 |
-
import argparse
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
def get_default_params(model_name):
|
| 5 |
-
# Params from paper (https://arxiv.org/pdf/2103.00020.pdf)
|
| 6 |
-
model_name = model_name.lower()
|
| 7 |
-
if "vit" in model_name:
|
| 8 |
-
return {"lr": 5.0e-4, "beta1": 0.9, "beta2": 0.98, "eps": 1.0e-6}
|
| 9 |
-
else:
|
| 10 |
-
return {"lr": 5.0e-4, "beta1": 0.9, "beta2": 0.999, "eps": 1.0e-8}
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
def parse_args():
|
| 14 |
-
parser = argparse.ArgumentParser()
|
| 15 |
-
parser.add_argument(
|
| 16 |
-
"--train-data",
|
| 17 |
-
type=str,
|
| 18 |
-
default=None,
|
| 19 |
-
help="Path to h5 filewith training data",
|
| 20 |
-
)
|
| 21 |
-
parser.add_argument(
|
| 22 |
-
"--val-data",
|
| 23 |
-
type=str,
|
| 24 |
-
default=None,
|
| 25 |
-
help="Path to h5 file with validation data",
|
| 26 |
-
)
|
| 27 |
-
parser.add_argument(
|
| 28 |
-
"--freeze-text",
|
| 29 |
-
default=False,
|
| 30 |
-
action="store_true",
|
| 31 |
-
help="if you need to freeze the text encoder, make this True",
|
| 32 |
-
)
|
| 33 |
-
parser.add_argument(
|
| 34 |
-
"--freeze-text-after",
|
| 35 |
-
type=int,
|
| 36 |
-
default=-1,
|
| 37 |
-
help="if you need to freeze the text encoder after (include) epoch x, set this param to x. Set -1 to disable it",
|
| 38 |
-
)
|
| 39 |
-
parser.add_argument(
|
| 40 |
-
"--train-ipc",
|
| 41 |
-
type=str,
|
| 42 |
-
default=None,
|
| 43 |
-
help="Path to npy file of the number of instance per class in training data",
|
| 44 |
-
)
|
| 45 |
-
parser.add_argument(
|
| 46 |
-
"--val-ipc",
|
| 47 |
-
type=str,
|
| 48 |
-
default=None,
|
| 49 |
-
help="Path to npy file of the number of instance per class in validation data",
|
| 50 |
-
)
|
| 51 |
-
parser.add_argument(
|
| 52 |
-
"--train-num-samples",
|
| 53 |
-
type=int,
|
| 54 |
-
default=None,
|
| 55 |
-
help="Number of samples in dataset. Required for webdataset if not available in info file.",
|
| 56 |
-
)
|
| 57 |
-
parser.add_argument(
|
| 58 |
-
"--val-num-samples",
|
| 59 |
-
type=int,
|
| 60 |
-
default=None,
|
| 61 |
-
help="Number of samples in dataset. Useful for webdataset if not available in info file.",
|
| 62 |
-
)
|
| 63 |
-
parser.add_argument(
|
| 64 |
-
"--dataset-type",
|
| 65 |
-
choices=["webdataset", "csv", "auto", "toy"],
|
| 66 |
-
default="auto",
|
| 67 |
-
help="Which type of dataset to process.",
|
| 68 |
-
)
|
| 69 |
-
parser.add_argument(
|
| 70 |
-
"--csv-separator",
|
| 71 |
-
type=str,
|
| 72 |
-
default="\t",
|
| 73 |
-
help="For csv-like datasets, which separator to use.",
|
| 74 |
-
)
|
| 75 |
-
parser.add_argument(
|
| 76 |
-
"--csv-img-key",
|
| 77 |
-
type=str,
|
| 78 |
-
default="filepath",
|
| 79 |
-
help="For csv-like datasets, the name of the key for the image paths.",
|
| 80 |
-
)
|
| 81 |
-
parser.add_argument(
|
| 82 |
-
"--csv-caption-key",
|
| 83 |
-
type=str,
|
| 84 |
-
default="title",
|
| 85 |
-
help="For csv-like datasets, the name of the key for the captions.",
|
| 86 |
-
)
|
| 87 |
-
parser.add_argument(
|
| 88 |
-
"--imagenet-val",
|
| 89 |
-
type=str,
|
| 90 |
-
default=None,
|
| 91 |
-
help="Path to imagenet val set for conducting zero shot evaluation.",
|
| 92 |
-
)
|
| 93 |
-
parser.add_argument(
|
| 94 |
-
"--imagenet-v2",
|
| 95 |
-
type=str,
|
| 96 |
-
default=None,
|
| 97 |
-
help="Path to imagenet v2 for conducting zero shot evaluation.",
|
| 98 |
-
)
|
| 99 |
-
parser.add_argument(
|
| 100 |
-
"--datasetnames",
|
| 101 |
-
nargs="+",
|
| 102 |
-
default=None,
|
| 103 |
-
help="If loading webdataset, spedify the dataset names to load. Can be some of these: Clotho, audioset, audiocaps, BBCSoundEffects",
|
| 104 |
-
)
|
| 105 |
-
parser.add_argument(
|
| 106 |
-
"--full-train-dataset",
|
| 107 |
-
nargs="+",
|
| 108 |
-
default=None,
|
| 109 |
-
help="Which dataset will be trained with all the subsets. (train+test)",
|
| 110 |
-
)
|
| 111 |
-
parser.add_argument(
|
| 112 |
-
"--exclude-eval-dataset",
|
| 113 |
-
nargs="+",
|
| 114 |
-
default=None,
|
| 115 |
-
help="Which dataset will be excluded with evaluation",
|
| 116 |
-
)
|
| 117 |
-
parser.add_argument(
|
| 118 |
-
"--datasetinfos",
|
| 119 |
-
nargs="+",
|
| 120 |
-
default=None,
|
| 121 |
-
help="If loading webdataset, spedify the dataset types to load. Can be some of these: train, test, valid, unbalanced_train, balanced_train, eval",
|
| 122 |
-
)
|
| 123 |
-
parser.add_argument(
|
| 124 |
-
"--dataset-proportion",
|
| 125 |
-
type=float,
|
| 126 |
-
default=1.0,
|
| 127 |
-
help="How much proportion of dataset we want to train.",
|
| 128 |
-
)
|
| 129 |
-
parser.add_argument(
|
| 130 |
-
"--remotedata",
|
| 131 |
-
default=False,
|
| 132 |
-
action="store_true",
|
| 133 |
-
help="if the dataset is remote, set this flag",
|
| 134 |
-
)
|
| 135 |
-
parser.add_argument(
|
| 136 |
-
"--class-label-path",
|
| 137 |
-
type=str,
|
| 138 |
-
default=None,
|
| 139 |
-
help="The path of the class label pickle or csv.",
|
| 140 |
-
)
|
| 141 |
-
parser.add_argument(
|
| 142 |
-
"--datasetpath",
|
| 143 |
-
type=str,
|
| 144 |
-
default="/mnt/audio_clip/webdataset_tar",
|
| 145 |
-
help="The path to the dataset",
|
| 146 |
-
)
|
| 147 |
-
parser.add_argument(
|
| 148 |
-
"--logs",
|
| 149 |
-
type=str,
|
| 150 |
-
default="./logs/",
|
| 151 |
-
help="Where to store tensorboard logs. Use None to avoid storing logs.",
|
| 152 |
-
)
|
| 153 |
-
parser.add_argument(
|
| 154 |
-
"--log-local",
|
| 155 |
-
action="store_true",
|
| 156 |
-
default=False,
|
| 157 |
-
help="log files on local master, otherwise global master only.",
|
| 158 |
-
)
|
| 159 |
-
parser.add_argument(
|
| 160 |
-
"--name",
|
| 161 |
-
type=str,
|
| 162 |
-
default=None,
|
| 163 |
-
help="Optional identifier for the experiment when storing logs. Otherwise use current time.",
|
| 164 |
-
)
|
| 165 |
-
parser.add_argument(
|
| 166 |
-
"--workers", type=int, default=1, help="Number of workers per GPU."
|
| 167 |
-
)
|
| 168 |
-
parser.add_argument(
|
| 169 |
-
"--batch-size", type=int, default=64, help="Batch size per GPU."
|
| 170 |
-
)
|
| 171 |
-
parser.add_argument(
|
| 172 |
-
"--epochs", type=int, default=32, help="Number of epochs to train for."
|
| 173 |
-
)
|
| 174 |
-
parser.add_argument("--lr", type=float, default=None, help="Learning rate.")
|
| 175 |
-
parser.add_argument("--beta1", type=float, default=None, help="Adam beta 1.")
|
| 176 |
-
parser.add_argument("--beta2", type=float, default=None, help="Adam beta 2.")
|
| 177 |
-
parser.add_argument("--eps", type=float, default=None, help="Adam epsilon.")
|
| 178 |
-
parser.add_argument("--momentum", type=float, default=None, help="SGD epsilon.")
|
| 179 |
-
parser.add_argument("--wd", type=float, default=0.2, help="Weight decay.")
|
| 180 |
-
|
| 181 |
-
parser.add_argument(
|
| 182 |
-
"--split-opt",
|
| 183 |
-
action="store_true",
|
| 184 |
-
default=False,
|
| 185 |
-
help="Use this flag to skip the learning rate decay.",
|
| 186 |
-
)
|
| 187 |
-
parser.add_argument(
|
| 188 |
-
"--lr-pretrained", type=float, default=None, help="Learning rate for text."
|
| 189 |
-
)
|
| 190 |
-
parser.add_argument(
|
| 191 |
-
"--beta1-pretrained", type=float, default=None, help="Adam beta 1 for text."
|
| 192 |
-
)
|
| 193 |
-
parser.add_argument(
|
| 194 |
-
"--beta2-pretrained", type=float, default=None, help="Adam beta 2 for text."
|
| 195 |
-
)
|
| 196 |
-
parser.add_argument(
|
| 197 |
-
"--eps-pretrained", type=float, default=None, help="Adam epsilon for text."
|
| 198 |
-
)
|
| 199 |
-
parser.add_argument(
|
| 200 |
-
"--wd-pretrained", type=float, default=0.2, help="Weight decay for text."
|
| 201 |
-
)
|
| 202 |
-
parser.add_argument(
|
| 203 |
-
"--momentum-pretrained", type=float, default=0.9, help="Momentum for text."
|
| 204 |
-
)
|
| 205 |
-
parser.add_argument(
|
| 206 |
-
"--lr-new", type=float, default=None, help="Learning rate for audio."
|
| 207 |
-
)
|
| 208 |
-
parser.add_argument(
|
| 209 |
-
"--beta1-new", type=float, default=None, help="Adam beta 1 for audio."
|
| 210 |
-
)
|
| 211 |
-
parser.add_argument(
|
| 212 |
-
"--beta2-new", type=float, default=None, help="Adam beta 2 for audio."
|
| 213 |
-
)
|
| 214 |
-
parser.add_argument(
|
| 215 |
-
"--eps-new", type=float, default=None, help="Adam epsilon for audio."
|
| 216 |
-
)
|
| 217 |
-
parser.add_argument(
|
| 218 |
-
"--wd-new", type=float, default=0.2, help="Weight decay for audio."
|
| 219 |
-
)
|
| 220 |
-
parser.add_argument(
|
| 221 |
-
"--momentum-new", type=float, default=0.9, help="Momentum for audio."
|
| 222 |
-
)
|
| 223 |
-
parser.add_argument(
|
| 224 |
-
"--warmup", type=int, default=10000, help="Number of steps to warmup for."
|
| 225 |
-
)
|
| 226 |
-
parser.add_argument(
|
| 227 |
-
"--use-bn-sync",
|
| 228 |
-
default=False,
|
| 229 |
-
action="store_true",
|
| 230 |
-
help="Whether to use batch norm sync.",
|
| 231 |
-
)
|
| 232 |
-
parser.add_argument(
|
| 233 |
-
"--skip-scheduler",
|
| 234 |
-
action="store_true",
|
| 235 |
-
default=False,
|
| 236 |
-
help="Use this flag to skip the learning rate decay.",
|
| 237 |
-
)
|
| 238 |
-
parser.add_argument(
|
| 239 |
-
"--save-frequency", type=int, default=1, help="How often to save checkpoints."
|
| 240 |
-
)
|
| 241 |
-
parser.add_argument(
|
| 242 |
-
"--save-top-performance",
|
| 243 |
-
type=int,
|
| 244 |
-
default=0,
|
| 245 |
-
help="Save the top x performance weights if the value >0",
|
| 246 |
-
)
|
| 247 |
-
parser.add_argument(
|
| 248 |
-
"--save-most-recent",
|
| 249 |
-
action="store_true",
|
| 250 |
-
default=False,
|
| 251 |
-
help="Always save the most recent model trained to epoch_latest.pt.",
|
| 252 |
-
)
|
| 253 |
-
parser.add_argument(
|
| 254 |
-
"--zeroshot-frequency", type=int, default=2, help="How often to run zero shot."
|
| 255 |
-
)
|
| 256 |
-
parser.add_argument(
|
| 257 |
-
"--val-frequency",
|
| 258 |
-
type=int,
|
| 259 |
-
default=1,
|
| 260 |
-
help="How often to run evaluation with val data.",
|
| 261 |
-
)
|
| 262 |
-
parser.add_argument(
|
| 263 |
-
"--resume",
|
| 264 |
-
default=None,
|
| 265 |
-
type=str,
|
| 266 |
-
help="path to latest checkpoint (default: none)",
|
| 267 |
-
)
|
| 268 |
-
parser.add_argument(
|
| 269 |
-
"--precision",
|
| 270 |
-
choices=["amp", "fp16", "fp32"],
|
| 271 |
-
default="amp",
|
| 272 |
-
help="Floating point precision.",
|
| 273 |
-
)
|
| 274 |
-
parser.add_argument(
|
| 275 |
-
"--amodel",
|
| 276 |
-
type=str,
|
| 277 |
-
default="RN50",
|
| 278 |
-
help="Name of the audio backbone to use.",
|
| 279 |
-
)
|
| 280 |
-
parser.add_argument(
|
| 281 |
-
"--tmodel",
|
| 282 |
-
type=str,
|
| 283 |
-
default="transformer",
|
| 284 |
-
help="Name of the text backbone to use. Can be [transformer, bert, roberta, bart]",
|
| 285 |
-
)
|
| 286 |
-
parser.add_argument(
|
| 287 |
-
"--pretrained-audio",
|
| 288 |
-
default="",
|
| 289 |
-
type=str,
|
| 290 |
-
help="Use a pretrained audio model weights for the audio encoder of CLAP",
|
| 291 |
-
)
|
| 292 |
-
parser.add_argument(
|
| 293 |
-
"--pretrained-text",
|
| 294 |
-
default="",
|
| 295 |
-
type=str,
|
| 296 |
-
help="Use a pretrained text model weights for the text encoder of CLAP",
|
| 297 |
-
)
|
| 298 |
-
parser.add_argument(
|
| 299 |
-
"--pretrained",
|
| 300 |
-
default="",
|
| 301 |
-
type=str,
|
| 302 |
-
help="Use a pretrained CLIP model weights with the specified tag or file path.",
|
| 303 |
-
)
|
| 304 |
-
parser.add_argument(
|
| 305 |
-
"--pretrained-image",
|
| 306 |
-
default=False,
|
| 307 |
-
action="store_true",
|
| 308 |
-
help="Load imagenet pretrained weights for image tower backbone if available.",
|
| 309 |
-
)
|
| 310 |
-
parser.add_argument(
|
| 311 |
-
"--lock-image",
|
| 312 |
-
default=False,
|
| 313 |
-
action="store_true",
|
| 314 |
-
help="Lock full image tower by disabling gradients.",
|
| 315 |
-
)
|
| 316 |
-
parser.add_argument(
|
| 317 |
-
"--lock-image-unlocked-groups",
|
| 318 |
-
type=int,
|
| 319 |
-
default=0,
|
| 320 |
-
help="Leave last n image tower layer groups unlocked.",
|
| 321 |
-
)
|
| 322 |
-
parser.add_argument(
|
| 323 |
-
"--lock-image-freeze-bn-stats",
|
| 324 |
-
default=False,
|
| 325 |
-
action="store_true",
|
| 326 |
-
help="Freeze BatchNorm running stats in image tower for any locked layers.",
|
| 327 |
-
)
|
| 328 |
-
parser.add_argument(
|
| 329 |
-
"--local-loss",
|
| 330 |
-
default=False,
|
| 331 |
-
action="store_true",
|
| 332 |
-
help="calculate loss w/ local features @ global (instead of realizing full global @ global matrix)",
|
| 333 |
-
)
|
| 334 |
-
parser.add_argument(
|
| 335 |
-
"--gather-with-grad",
|
| 336 |
-
default=False,
|
| 337 |
-
action="store_true",
|
| 338 |
-
help="enable full distributed gradient for feature gather",
|
| 339 |
-
)
|
| 340 |
-
parser.add_argument(
|
| 341 |
-
"--force-quick-gelu",
|
| 342 |
-
default=False,
|
| 343 |
-
action="store_true",
|
| 344 |
-
help="Force use of QuickGELU activation for non-OpenAI transformer models.",
|
| 345 |
-
)
|
| 346 |
-
parser.add_argument(
|
| 347 |
-
"--torchscript",
|
| 348 |
-
default=False,
|
| 349 |
-
action="store_true",
|
| 350 |
-
help="torch.jit.script the model, also uses jit version of OpenAI models if pretrained=='openai'",
|
| 351 |
-
)
|
| 352 |
-
parser.add_argument(
|
| 353 |
-
"--trace",
|
| 354 |
-
default=False,
|
| 355 |
-
action="store_true",
|
| 356 |
-
help="torch.jit.trace the model for inference / eval only",
|
| 357 |
-
)
|
| 358 |
-
# arguments for distributed training
|
| 359 |
-
parser.add_argument(
|
| 360 |
-
"--dist-url",
|
| 361 |
-
default="env://",
|
| 362 |
-
type=str,
|
| 363 |
-
help="url used to set up distributed training",
|
| 364 |
-
)
|
| 365 |
-
parser.add_argument(
|
| 366 |
-
"--dist-backend", default="nccl", type=str, help="distributed backend"
|
| 367 |
-
)
|
| 368 |
-
parser.add_argument(
|
| 369 |
-
"--report-to",
|
| 370 |
-
default="",
|
| 371 |
-
type=str,
|
| 372 |
-
help="Options are ['wandb', 'tensorboard', 'wandb,tensorboard']",
|
| 373 |
-
)
|
| 374 |
-
parser.add_argument(
|
| 375 |
-
"--wandb-notes", default="", type=str, help="Notes if logging with wandb"
|
| 376 |
-
)
|
| 377 |
-
parser.add_argument(
|
| 378 |
-
"--C", type=float, default=3.16, help="inverse regularizer for logistic reg."
|
| 379 |
-
)
|
| 380 |
-
parser.add_argument(
|
| 381 |
-
"--debug",
|
| 382 |
-
default=False,
|
| 383 |
-
action="store_true",
|
| 384 |
-
help="If true, more information is logged.",
|
| 385 |
-
)
|
| 386 |
-
parser.add_argument(
|
| 387 |
-
"--copy-codebase",
|
| 388 |
-
default=False,
|
| 389 |
-
action="store_true",
|
| 390 |
-
help="If true, we copy the entire base on the log diretory, and execute from there.",
|
| 391 |
-
)
|
| 392 |
-
parser.add_argument(
|
| 393 |
-
"--horovod",
|
| 394 |
-
default=False,
|
| 395 |
-
action="store_true",
|
| 396 |
-
help="Use horovod for distributed training.",
|
| 397 |
-
)
|
| 398 |
-
parser.add_argument(
|
| 399 |
-
"--ddp-static-graph",
|
| 400 |
-
default=False,
|
| 401 |
-
action="store_true",
|
| 402 |
-
help="Enable static graph optimization for DDP in PyTorch >= 1.11.",
|
| 403 |
-
)
|
| 404 |
-
parser.add_argument(
|
| 405 |
-
"--no-set-device-rank",
|
| 406 |
-
default=False,
|
| 407 |
-
action="store_true",
|
| 408 |
-
help="Don't set device index from local rank (when CUDA_VISIBLE_DEVICES restricted to one per proc).",
|
| 409 |
-
)
|
| 410 |
-
parser.add_argument("--seed", type=int, default=4242, help="Default random seed.")
|
| 411 |
-
|
| 412 |
-
parser.add_argument(
|
| 413 |
-
"--top-k-checkpoint-select-dataset",
|
| 414 |
-
type=str,
|
| 415 |
-
default="all",
|
| 416 |
-
help="The dataset of selecting top-k checkpoint.",
|
| 417 |
-
)
|
| 418 |
-
|
| 419 |
-
# @R10, @R@5, @R1, mAP@10
|
| 420 |
-
parser.add_argument(
|
| 421 |
-
"--top-k-checkpoint-select-metric",
|
| 422 |
-
type=str,
|
| 423 |
-
default="_R@10",
|
| 424 |
-
help="The metric for selecting top-k checkpoint.",
|
| 425 |
-
)
|
| 426 |
-
parser.add_argument(
|
| 427 |
-
"--openai-model-cache-dir",
|
| 428 |
-
type=str,
|
| 429 |
-
default="~/.cache/clip",
|
| 430 |
-
help="Directory to download OpenAI models.",
|
| 431 |
-
)
|
| 432 |
-
parser.add_argument(
|
| 433 |
-
"--optimizer",
|
| 434 |
-
type=str,
|
| 435 |
-
default="adamw",
|
| 436 |
-
help="can be AdamW or SGD",
|
| 437 |
-
)
|
| 438 |
-
parser.add_argument(
|
| 439 |
-
"--parallel-eval",
|
| 440 |
-
default=False,
|
| 441 |
-
action="store_true",
|
| 442 |
-
help="Eval in parallel (multi-GPU, multi-node).",
|
| 443 |
-
)
|
| 444 |
-
|
| 445 |
-
parser.add_argument(
|
| 446 |
-
"--no-eval",
|
| 447 |
-
default=False,
|
| 448 |
-
action="store_true",
|
| 449 |
-
help="Training without evaluation.",
|
| 450 |
-
)
|
| 451 |
-
|
| 452 |
-
parser.add_argument(
|
| 453 |
-
"--lp-mlp",
|
| 454 |
-
default=False,
|
| 455 |
-
action="store_true",
|
| 456 |
-
help="Linear Probe using MLP layer or not.",
|
| 457 |
-
)
|
| 458 |
-
|
| 459 |
-
parser.add_argument(
|
| 460 |
-
"--lp-freeze",
|
| 461 |
-
default=False,
|
| 462 |
-
action="store_true",
|
| 463 |
-
help="Linear Probe using Freeze CLAP or not",
|
| 464 |
-
)
|
| 465 |
-
|
| 466 |
-
parser.add_argument(
|
| 467 |
-
"--lp-act",
|
| 468 |
-
default="None",
|
| 469 |
-
type=str,
|
| 470 |
-
help="Options are ['relu','elu','prelu','softmax','sigmoid']",
|
| 471 |
-
)
|
| 472 |
-
|
| 473 |
-
parser.add_argument(
|
| 474 |
-
"--lp-loss", type=str, default="bce", help="Loss func of Linear Probe."
|
| 475 |
-
)
|
| 476 |
-
|
| 477 |
-
parser.add_argument(
|
| 478 |
-
"--lp-metrics",
|
| 479 |
-
type=str,
|
| 480 |
-
default="map,mauc,acc",
|
| 481 |
-
help="Metrics of Linear Probe.",
|
| 482 |
-
)
|
| 483 |
-
|
| 484 |
-
parser.add_argument(
|
| 485 |
-
"--lp-lr", type=float, default=1e-4, help="learning rate of linear probe"
|
| 486 |
-
)
|
| 487 |
-
parser.add_argument(
|
| 488 |
-
"--kappa", type=float, default=0,
|
| 489 |
-
help="the kappa in the weighted contrastive loss, default is to turn off the weighted contrastive loss"
|
| 490 |
-
)
|
| 491 |
-
|
| 492 |
-
parser.add_argument(
|
| 493 |
-
"--data-filling",
|
| 494 |
-
type=str,
|
| 495 |
-
default="pad",
|
| 496 |
-
help="type of data filling when the audio length is shorter than the max length."
|
| 497 |
-
"Can be one of the following: repeat, repeatpad, pad",
|
| 498 |
-
)
|
| 499 |
-
parser.add_argument(
|
| 500 |
-
"--data-truncating",
|
| 501 |
-
type=str,
|
| 502 |
-
default="rand_trunc",
|
| 503 |
-
help="type of data truncation when the audio length is longer than the max length."
|
| 504 |
-
"Can be one of the following: rand_trunc, fusion",
|
| 505 |
-
)
|
| 506 |
-
|
| 507 |
-
parser.add_argument(
|
| 508 |
-
"--clap-mlploss",
|
| 509 |
-
default=False,
|
| 510 |
-
action="store_true",
|
| 511 |
-
help="Using MLP loss for CLAP model or not",
|
| 512 |
-
)
|
| 513 |
-
|
| 514 |
-
parser.add_argument(
|
| 515 |
-
"--wandb-id",
|
| 516 |
-
type=str,
|
| 517 |
-
default=None,
|
| 518 |
-
help="the id of wandb experiment to restore.",
|
| 519 |
-
)
|
| 520 |
-
|
| 521 |
-
parser.add_argument(
|
| 522 |
-
"--sleep", type=float, default=0, help="sleep n seconds before start training"
|
| 523 |
-
)
|
| 524 |
-
|
| 525 |
-
# variable length processing
|
| 526 |
-
parser.add_argument(
|
| 527 |
-
"--enable-fusion",
|
| 528 |
-
default=False,
|
| 529 |
-
action="store_true",
|
| 530 |
-
help="Enable feature funsion for variable-length data",
|
| 531 |
-
)
|
| 532 |
-
|
| 533 |
-
parser.add_argument(
|
| 534 |
-
"--fusion-type",
|
| 535 |
-
type=str,
|
| 536 |
-
default='None',
|
| 537 |
-
help="Type is among ['channel_map', 'daf_1d','aff_1d','iaff_1d','daf_2d','aff_2d','iaff_2d']",
|
| 538 |
-
)
|
| 539 |
-
|
| 540 |
-
parser.add_argument(
|
| 541 |
-
"--mixup",
|
| 542 |
-
default=False,
|
| 543 |
-
action="store_true",
|
| 544 |
-
help="Enable mixup in finetuning training.",
|
| 545 |
-
)
|
| 546 |
-
parser.add_argument(
|
| 547 |
-
"--text-augment-selection",
|
| 548 |
-
type=str,
|
| 549 |
-
default=None,
|
| 550 |
-
help="For selecting levels of augmented text. Type is among ['all', 'augment_only', 'none']",
|
| 551 |
-
)
|
| 552 |
-
parser.add_argument(
|
| 553 |
-
"--prefetch-factor",
|
| 554 |
-
type=int,
|
| 555 |
-
default=None,
|
| 556 |
-
help="The prefetch factor for dataloader. Larger value will use more memory and CPU but faster.",
|
| 557 |
-
)
|
| 558 |
-
|
| 559 |
-
args = parser.parse_args()
|
| 560 |
-
|
| 561 |
-
# If some params are not passed, we use the default values based on model name.
|
| 562 |
-
default_params = get_default_params(args.amodel)
|
| 563 |
-
for name, val in default_params.items():
|
| 564 |
-
if getattr(args, name) is None:
|
| 565 |
-
setattr(args, name, val)
|
| 566 |
-
|
| 567 |
-
return args
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/training/scheduler.py
DELETED
|
@@ -1,23 +0,0 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
def assign_learning_rate(optimizer, new_lr):
|
| 5 |
-
for param_group in optimizer.param_groups:
|
| 6 |
-
param_group["lr"] = new_lr
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
def _warmup_lr(base_lr, warmup_length, step):
|
| 10 |
-
return base_lr * (step + 1) / warmup_length
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
def cosine_lr(optimizer, base_lr, warmup_length, steps):
|
| 14 |
-
def _lr_adjuster(step):
|
| 15 |
-
if step < warmup_length:
|
| 16 |
-
lr = _warmup_lr(base_lr, warmup_length, step)
|
| 17 |
-
else:
|
| 18 |
-
e = step - warmup_length
|
| 19 |
-
es = steps - warmup_length
|
| 20 |
-
lr = 0.5 * (1 + np.cos(np.pi * e / es)) * base_lr
|
| 21 |
-
assign_learning_rate(optimizer, lr)
|
| 22 |
-
return lr
|
| 23 |
-
return _lr_adjuster
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/training/train.py
DELETED
|
@@ -1,781 +0,0 @@
|
|
| 1 |
-
import json
|
| 2 |
-
import logging
|
| 3 |
-
import math
|
| 4 |
-
import os
|
| 5 |
-
import time
|
| 6 |
-
from contextlib import suppress
|
| 7 |
-
|
| 8 |
-
import numpy as np
|
| 9 |
-
import torch
|
| 10 |
-
import torch.nn.functional as F
|
| 11 |
-
|
| 12 |
-
try:
|
| 13 |
-
import wandb
|
| 14 |
-
except ImportError:
|
| 15 |
-
wandb = None
|
| 16 |
-
|
| 17 |
-
from clap_module import ClipLoss, gather_features
|
| 18 |
-
from .distributed import is_master
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
class AverageMeter(object):
|
| 22 |
-
"""Computes and stores the average and current value"""
|
| 23 |
-
|
| 24 |
-
def __init__(self):
|
| 25 |
-
self.reset()
|
| 26 |
-
|
| 27 |
-
def reset(self):
|
| 28 |
-
self.val = 0
|
| 29 |
-
self.avg = 0
|
| 30 |
-
self.sum = 0
|
| 31 |
-
self.count = 0
|
| 32 |
-
|
| 33 |
-
def update(self, val, n=1):
|
| 34 |
-
self.val = val
|
| 35 |
-
self.sum += val * n
|
| 36 |
-
self.count += n
|
| 37 |
-
self.avg = self.sum / self.count
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
def unwrap_model(model):
|
| 41 |
-
if hasattr(model, "module"):
|
| 42 |
-
return model.module
|
| 43 |
-
else:
|
| 44 |
-
return model
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
def train_one_epoch(
|
| 48 |
-
model, data, epoch, optimizer, scaler, scheduler, args, tb_writer=None
|
| 49 |
-
):
|
| 50 |
-
device = torch.device(args.device)
|
| 51 |
-
autocast = torch.cuda.amp.autocast if args.precision == "amp" else suppress
|
| 52 |
-
model.train()
|
| 53 |
-
loss = ClipLoss(
|
| 54 |
-
local_loss=args.local_loss,
|
| 55 |
-
gather_with_grad=args.gather_with_grad,
|
| 56 |
-
cache_labels=True,
|
| 57 |
-
rank=args.rank,
|
| 58 |
-
world_size=args.world_size,
|
| 59 |
-
use_horovod=args.horovod,
|
| 60 |
-
mlp_loss=args.clap_mlploss,
|
| 61 |
-
weight_loss_kappa=args.kappa,
|
| 62 |
-
)
|
| 63 |
-
|
| 64 |
-
dataloader, sampler = data["train"].dataloader, data["train"].sampler
|
| 65 |
-
if args.distributed and sampler is not None:
|
| 66 |
-
sampler.set_epoch(epoch)
|
| 67 |
-
num_batches_per_epoch = dataloader.num_batches
|
| 68 |
-
sample_digits = math.ceil(math.log(dataloader.num_samples + 1, 10))
|
| 69 |
-
|
| 70 |
-
# for toy dataset
|
| 71 |
-
if args.dataset_type == "toy":
|
| 72 |
-
dataloader.dataset.generate_queue()
|
| 73 |
-
|
| 74 |
-
loss_m = AverageMeter()
|
| 75 |
-
batch_time_m = AverageMeter()
|
| 76 |
-
data_time_m = AverageMeter()
|
| 77 |
-
end = time.time()
|
| 78 |
-
|
| 79 |
-
for i, batch in enumerate(dataloader):
|
| 80 |
-
# logging.info(f"batch {i} of {num_batches_per_epoch}")
|
| 81 |
-
step = num_batches_per_epoch * epoch + i
|
| 82 |
-
if isinstance(scheduler, dict):
|
| 83 |
-
for s in scheduler.values():
|
| 84 |
-
s(step)
|
| 85 |
-
else:
|
| 86 |
-
scheduler(step)
|
| 87 |
-
audios = batch # contains mel_spec, wavform, and longer list
|
| 88 |
-
texts = batch['text']
|
| 89 |
-
# audios = audios.to(device=device, non_blocking=True)
|
| 90 |
-
# texts = texts.to(device=device, non_blocking=True)
|
| 91 |
-
|
| 92 |
-
data_time_m.update(time.time() - end)
|
| 93 |
-
if isinstance(optimizer, dict):
|
| 94 |
-
for o_ in optimizer.values():
|
| 95 |
-
o_.zero_grad()
|
| 96 |
-
else:
|
| 97 |
-
optimizer.zero_grad()
|
| 98 |
-
|
| 99 |
-
with autocast():
|
| 100 |
-
(
|
| 101 |
-
audio_features,
|
| 102 |
-
text_features,
|
| 103 |
-
audio_features_mlp,
|
| 104 |
-
text_features_mlp,
|
| 105 |
-
logit_scale_a,
|
| 106 |
-
logit_scale_t,
|
| 107 |
-
) = model(audios, texts, device)
|
| 108 |
-
|
| 109 |
-
if args.clap_mlploss:
|
| 110 |
-
total_loss = loss(
|
| 111 |
-
audio_features=audio_features,
|
| 112 |
-
text_features=text_features,
|
| 113 |
-
logit_scale_a=logit_scale_a,
|
| 114 |
-
logit_scale_t=logit_scale_t,
|
| 115 |
-
audio_features_mlp=audio_features_mlp,
|
| 116 |
-
text_features_mlp=text_features_mlp
|
| 117 |
-
)
|
| 118 |
-
else:
|
| 119 |
-
total_loss = loss(
|
| 120 |
-
audio_features=audio_features,
|
| 121 |
-
text_features=text_features,
|
| 122 |
-
logit_scale_a=logit_scale_a
|
| 123 |
-
)
|
| 124 |
-
if isinstance(optimizer, dict):
|
| 125 |
-
if scaler is not None:
|
| 126 |
-
scaler.scale(total_loss).backward()
|
| 127 |
-
for o_ in optimizer.values():
|
| 128 |
-
if args.horovod:
|
| 129 |
-
o_.synchronize()
|
| 130 |
-
scaler.unscale_(o_)
|
| 131 |
-
with o_.skip_synchronize():
|
| 132 |
-
scaler.step(o_)
|
| 133 |
-
else:
|
| 134 |
-
scaler.step(o_)
|
| 135 |
-
scaler.update()
|
| 136 |
-
else:
|
| 137 |
-
total_loss.backward()
|
| 138 |
-
for o_ in optimizer.values():
|
| 139 |
-
o_.step()
|
| 140 |
-
else:
|
| 141 |
-
if scaler is not None:
|
| 142 |
-
scaler.scale(total_loss).backward()
|
| 143 |
-
if args.horovod:
|
| 144 |
-
optimizer.synchronize()
|
| 145 |
-
scaler.unscale_(optimizer)
|
| 146 |
-
with optimizer.skip_synchronize():
|
| 147 |
-
scaler.step(optimizer)
|
| 148 |
-
else:
|
| 149 |
-
scaler.step(optimizer)
|
| 150 |
-
scaler.update()
|
| 151 |
-
else:
|
| 152 |
-
total_loss.backward()
|
| 153 |
-
optimizer.step()
|
| 154 |
-
|
| 155 |
-
# Note: we clamp to 4.6052 = ln(100), as in the original paper.
|
| 156 |
-
with torch.no_grad():
|
| 157 |
-
unwrap_model(model).logit_scale_a.clamp_(0, math.log(100))
|
| 158 |
-
if args.clap_mlploss:
|
| 159 |
-
unwrap_model(model).logit_scale_t.clamp_(0, math.log(100))
|
| 160 |
-
|
| 161 |
-
batch_time_m.update(time.time() - end)
|
| 162 |
-
end = time.time()
|
| 163 |
-
batch_count = i + 1
|
| 164 |
-
if is_master(args) and (i % 100 == 0 or batch_count == num_batches_per_epoch):
|
| 165 |
-
if isinstance(audios, dict):
|
| 166 |
-
batch_size = len(audios["waveform"])
|
| 167 |
-
else:
|
| 168 |
-
batch_size = len(audios)
|
| 169 |
-
num_samples = batch_count * batch_size * args.world_size
|
| 170 |
-
samples_per_epoch = dataloader.num_samples
|
| 171 |
-
percent_complete = 100.0 * batch_count / num_batches_per_epoch
|
| 172 |
-
|
| 173 |
-
# NOTE loss is coarsely sampled, just master node and per log update
|
| 174 |
-
loss_m.update(total_loss.item(), batch_size)
|
| 175 |
-
logit_scale_scalar_a = logit_scale_a.item()
|
| 176 |
-
logit_scale_scalar_t = logit_scale_t.item()
|
| 177 |
-
if isinstance(optimizer, dict):
|
| 178 |
-
if args.clap_mlploss:
|
| 179 |
-
logging.info(
|
| 180 |
-
f"Train Epoch: {epoch} [{num_samples:>{sample_digits}}/{samples_per_epoch} ({percent_complete:.0f}%)] "
|
| 181 |
-
f"Loss: {loss_m.val:#.5g} ({loss_m.avg:#.4g}) "
|
| 182 |
-
f"Data (t): {data_time_m.avg:.3f} "
|
| 183 |
-
f"Batch (t): {batch_time_m.avg:.3f} "
|
| 184 |
-
f"LR: {[o_.param_groups[0]['lr'] for o_ in optimizer.values()]} "
|
| 185 |
-
f"Logit Scale Audio: {logit_scale_scalar_a:.3f}"
|
| 186 |
-
f"Logit Scale Text: {logit_scale_scalar_t:.3f}"
|
| 187 |
-
)
|
| 188 |
-
log_data = {
|
| 189 |
-
"loss": loss_m.val,
|
| 190 |
-
"data_time": data_time_m.val,
|
| 191 |
-
"batch_time": batch_time_m.val,
|
| 192 |
-
"scale_audio": logit_scale_scalar_a,
|
| 193 |
-
"scale_text": logit_scale_scalar_t,
|
| 194 |
-
"lr": [o_.param_groups[0]["lr"] for o_ in optimizer.values()],
|
| 195 |
-
}
|
| 196 |
-
else:
|
| 197 |
-
logging.info(
|
| 198 |
-
f"Train Epoch: {epoch} [{num_samples:>{sample_digits}}/{samples_per_epoch} ({percent_complete:.0f}%)] "
|
| 199 |
-
f"Loss: {loss_m.val:#.5g} ({loss_m.avg:#.4g}) "
|
| 200 |
-
f"Data (t): {data_time_m.avg:.3f} "
|
| 201 |
-
f"Batch (t): {batch_time_m.avg:.3f} "
|
| 202 |
-
f"LR: {[o_.param_groups[0]['lr'] for o_ in optimizer.values()]} "
|
| 203 |
-
f"Logit Scale Audio: {logit_scale_scalar_a:.3f}"
|
| 204 |
-
)
|
| 205 |
-
log_data = {
|
| 206 |
-
"loss": loss_m.val,
|
| 207 |
-
"data_time": data_time_m.val,
|
| 208 |
-
"batch_time": batch_time_m.val,
|
| 209 |
-
"scale_audio": logit_scale_scalar_a,
|
| 210 |
-
"lr": [o_.param_groups[0]["lr"] for o_ in optimizer.values()],
|
| 211 |
-
}
|
| 212 |
-
|
| 213 |
-
else:
|
| 214 |
-
if args.clap_mlploss:
|
| 215 |
-
logging.info(
|
| 216 |
-
f"Train Epoch: {epoch} [{num_samples:>{sample_digits}}/{samples_per_epoch} ({percent_complete:.0f}%)] "
|
| 217 |
-
f"Loss: {loss_m.val:#.5g} ({loss_m.avg:#.4g}) "
|
| 218 |
-
f"Data (t): {data_time_m.avg:.3f} "
|
| 219 |
-
f"Batch (t): {batch_time_m.avg:.3f} "
|
| 220 |
-
f"LR: {optimizer.param_groups[0]['lr']:5f} "
|
| 221 |
-
f"Logit Scale Audio: {logit_scale_scalar_a:.3f}"
|
| 222 |
-
f"Logit Scale Text: {logit_scale_scalar_t:.3f}"
|
| 223 |
-
)
|
| 224 |
-
|
| 225 |
-
# Save train loss / etc. Using non avg meter values as loggers have their own smoothing
|
| 226 |
-
log_data = {
|
| 227 |
-
"loss": loss_m.val,
|
| 228 |
-
"data_time": data_time_m.val,
|
| 229 |
-
"batch_time": batch_time_m.val,
|
| 230 |
-
"scale_audio": logit_scale_scalar_a,
|
| 231 |
-
"scale_text": logit_scale_scalar_t,
|
| 232 |
-
"lr": optimizer.param_groups[0]["lr"],
|
| 233 |
-
}
|
| 234 |
-
else:
|
| 235 |
-
logging.info(
|
| 236 |
-
f"Train Epoch: {epoch} [{num_samples:>{sample_digits}}/{samples_per_epoch} ({percent_complete:.0f}%)] "
|
| 237 |
-
f"Loss: {loss_m.val:#.5g} ({loss_m.avg:#.4g}) "
|
| 238 |
-
f"Data (t): {data_time_m.avg:.3f} "
|
| 239 |
-
f"Batch (t): {batch_time_m.avg:.3f} "
|
| 240 |
-
f"LR: {optimizer.param_groups[0]['lr']:5f} "
|
| 241 |
-
f"Logit Scale Audio: {logit_scale_scalar_a:.3f}"
|
| 242 |
-
)
|
| 243 |
-
|
| 244 |
-
# Save train loss / etc. Using non avg meter values as loggers have their own smoothing
|
| 245 |
-
log_data = {
|
| 246 |
-
"loss": loss_m.val,
|
| 247 |
-
"data_time": data_time_m.val,
|
| 248 |
-
"batch_time": batch_time_m.val,
|
| 249 |
-
"scale_audio": logit_scale_scalar_a,
|
| 250 |
-
"lr": optimizer.param_groups[0]["lr"],
|
| 251 |
-
}
|
| 252 |
-
for name, val in log_data.items():
|
| 253 |
-
name = "train/" + name
|
| 254 |
-
if tb_writer is not None:
|
| 255 |
-
tb_writer.add_scalar(name, val, step)
|
| 256 |
-
if args.wandb:
|
| 257 |
-
assert wandb is not None, "Please install wandb."
|
| 258 |
-
wandb.log({name: val, "step": step})
|
| 259 |
-
|
| 260 |
-
# resetting batch / data time meters per log window
|
| 261 |
-
batch_time_m.reset()
|
| 262 |
-
data_time_m.reset()
|
| 263 |
-
# end for
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
def evaluate(model, data, epoch, args, tb_writer=None):
|
| 267 |
-
metrics = {}
|
| 268 |
-
if not args.parallel_eval:
|
| 269 |
-
if not is_master(args):
|
| 270 |
-
return metrics
|
| 271 |
-
device = torch.device(args.device)
|
| 272 |
-
model.eval()
|
| 273 |
-
|
| 274 |
-
# CHANGE
|
| 275 |
-
# zero_shot_metrics = zero_shot_eval(model, data, epoch, args)
|
| 276 |
-
# metrics.update(zero_shot_metrics)
|
| 277 |
-
if is_master(args):
|
| 278 |
-
print('Evaluating...')
|
| 279 |
-
autocast = torch.cuda.amp.autocast if args.precision == "amp" else suppress
|
| 280 |
-
if args.val_dataset_names == ['Clotho', 'audiocaps']:
|
| 281 |
-
# if only clotho and audiocaps are used, then we will use a different evaluation function.
|
| 282 |
-
# This is because in the Clotho and audiocaps valid and test set, there are 5 text for 1 audio.
|
| 283 |
-
if args.parallel_eval:
|
| 284 |
-
# (yusong): just a hack here. Don't use parallel eval when evaluating only clotho and audiocaps.
|
| 285 |
-
raise NotImplementedError("Parallel evaluation not supported for eval only Clotho and audiocaps.")
|
| 286 |
-
val_metrics_per_dataset = evaluate_clotho_audiocaps(model, data, epoch, args, autocast, device, tb_writer)
|
| 287 |
-
for m in val_metrics_per_dataset.values():
|
| 288 |
-
metrics.update(m)
|
| 289 |
-
if "epoch" not in metrics.keys():
|
| 290 |
-
metrics.update({"epoch": epoch})
|
| 291 |
-
metrics = select_top_metric_clotho_audiocaps(metrics, val_metrics_per_dataset, args)
|
| 292 |
-
elif "val" in data and (
|
| 293 |
-
args.val_frequency
|
| 294 |
-
and ((epoch % args.val_frequency) == 0 or epoch == args.epochs)
|
| 295 |
-
):
|
| 296 |
-
dataloader = data["val"].dataloader
|
| 297 |
-
num_samples = 0
|
| 298 |
-
samples_per_val = dataloader.num_samples
|
| 299 |
-
|
| 300 |
-
# FIXME this does not scale past small eval datasets
|
| 301 |
-
# all_audio_features @ all_text_features will blow up memory and compute very quickly
|
| 302 |
-
eval_info = {}
|
| 303 |
-
if args.clap_mlploss:
|
| 304 |
-
eval_info["all"] = {
|
| 305 |
-
"cumulative_loss": 0.0,
|
| 306 |
-
"num_samples": 0,
|
| 307 |
-
"all_audio_features": [],
|
| 308 |
-
"all_text_features": [],
|
| 309 |
-
"all_audio_features_mlp": [],
|
| 310 |
-
"all_text_features_mlp": []
|
| 311 |
-
} # cumulative_loss = 0.0
|
| 312 |
-
else:
|
| 313 |
-
eval_info["all"] = {
|
| 314 |
-
"cumulative_loss": 0.0,
|
| 315 |
-
"num_samples": 0,
|
| 316 |
-
"all_audio_features": [],
|
| 317 |
-
"all_text_features": []
|
| 318 |
-
} # cumu
|
| 319 |
-
# all_audio_features, all_text_features, all_audio_features_mlp, all_text_features_mlp = [], [], [], []
|
| 320 |
-
with torch.no_grad():
|
| 321 |
-
for i, batch in enumerate(dataloader):
|
| 322 |
-
audios = batch # contains mel_spec, wavform, and longer list
|
| 323 |
-
texts = batch['text']
|
| 324 |
-
# audios = audios.to(device=device, non_blocking=True)
|
| 325 |
-
|
| 326 |
-
all_names = list(set(["-".join(b.split("/")[-3:-1]) for b in batch['__url__']]))
|
| 327 |
-
for name in all_names:
|
| 328 |
-
if name not in eval_info.keys():
|
| 329 |
-
if args.clap_mlploss:
|
| 330 |
-
eval_info[name] = {
|
| 331 |
-
"cumulative_loss": 0.0,
|
| 332 |
-
"num_samples": 0,
|
| 333 |
-
"all_audio_features": [],
|
| 334 |
-
"all_text_features": [],
|
| 335 |
-
"all_audio_features_mlp": [],
|
| 336 |
-
"all_text_features_mlp": [],
|
| 337 |
-
}
|
| 338 |
-
else:
|
| 339 |
-
eval_info[name] = {
|
| 340 |
-
"cumulative_loss": 0.0,
|
| 341 |
-
"num_samples": 0,
|
| 342 |
-
"all_audio_features": [],
|
| 343 |
-
"all_text_features": []
|
| 344 |
-
}
|
| 345 |
-
with autocast():
|
| 346 |
-
(
|
| 347 |
-
audio_features,
|
| 348 |
-
text_features,
|
| 349 |
-
audio_features_mlp,
|
| 350 |
-
text_features_mlp,
|
| 351 |
-
logit_scale_a,
|
| 352 |
-
logit_scale_t,
|
| 353 |
-
) = model(audios, texts, device)
|
| 354 |
-
|
| 355 |
-
if args.parallel_eval:
|
| 356 |
-
# multi-GPU eval
|
| 357 |
-
if args.clap_mlploss:
|
| 358 |
-
(
|
| 359 |
-
audio_features,
|
| 360 |
-
text_features,
|
| 361 |
-
audio_features_mlp,
|
| 362 |
-
text_features_mlp,
|
| 363 |
-
) = gather_features(
|
| 364 |
-
audio_features=audio_features,
|
| 365 |
-
text_features=text_features,
|
| 366 |
-
audio_features_mlp=audio_features_mlp,
|
| 367 |
-
text_features_mlp=text_features_mlp,
|
| 368 |
-
local_loss=False,
|
| 369 |
-
gather_with_grad=False,
|
| 370 |
-
rank=args.rank,
|
| 371 |
-
world_size=args.world_size,
|
| 372 |
-
use_horovod=args.horovod,
|
| 373 |
-
mlp_loss=args.clap_mlploss
|
| 374 |
-
)
|
| 375 |
-
else:
|
| 376 |
-
(
|
| 377 |
-
audio_features,
|
| 378 |
-
text_features,
|
| 379 |
-
) = gather_features(
|
| 380 |
-
audio_features=audio_features,
|
| 381 |
-
text_features=text_features,
|
| 382 |
-
local_loss=False,
|
| 383 |
-
gather_with_grad=False,
|
| 384 |
-
rank=args.rank,
|
| 385 |
-
world_size=args.world_size,
|
| 386 |
-
use_horovod=args.horovod,
|
| 387 |
-
mlp_loss=args.clap_mlploss
|
| 388 |
-
)
|
| 389 |
-
|
| 390 |
-
if is_master(args):
|
| 391 |
-
num_samples += audio_features.shape[0]
|
| 392 |
-
for n in [*all_names, "all"]:
|
| 393 |
-
if n == "all":
|
| 394 |
-
eval_info[n]["all_audio_features"].append(
|
| 395 |
-
audio_features.cpu()
|
| 396 |
-
)
|
| 397 |
-
eval_info[n]["all_text_features"].append(
|
| 398 |
-
text_features.cpu()
|
| 399 |
-
)
|
| 400 |
-
if args.clap_mlploss:
|
| 401 |
-
eval_info[n]["all_audio_features_mlp"].append(
|
| 402 |
-
audio_features_mlp.cpu()
|
| 403 |
-
)
|
| 404 |
-
eval_info[n]["all_text_features_mlp"].append(
|
| 405 |
-
text_features_mlp.cpu()
|
| 406 |
-
)
|
| 407 |
-
else:
|
| 408 |
-
idx = np.where(
|
| 409 |
-
np.array(
|
| 410 |
-
["-".join(b.split("/")[-3:-1]) for b in batch['__url__']]
|
| 411 |
-
)
|
| 412 |
-
== n
|
| 413 |
-
)[0]
|
| 414 |
-
eval_info[n]["all_audio_features"].append(
|
| 415 |
-
audio_features.cpu().index_select(
|
| 416 |
-
0, torch.tensor(idx).long()
|
| 417 |
-
)
|
| 418 |
-
)
|
| 419 |
-
eval_info[n]["all_text_features"].append(
|
| 420 |
-
text_features.cpu().index_select(
|
| 421 |
-
0, torch.tensor(idx).long()
|
| 422 |
-
)
|
| 423 |
-
)
|
| 424 |
-
if args.clap_mlploss:
|
| 425 |
-
eval_info[n]["all_audio_features_mlp"].append(
|
| 426 |
-
audio_features_mlp.cpu().index_select(
|
| 427 |
-
0, torch.tensor(idx).long()
|
| 428 |
-
)
|
| 429 |
-
)
|
| 430 |
-
eval_info[n]["all_text_features_mlp"].append(
|
| 431 |
-
text_features_mlp.cpu().index_select(
|
| 432 |
-
0, torch.tensor(idx).long()
|
| 433 |
-
)
|
| 434 |
-
)
|
| 435 |
-
# print(f'eval step {i}') # (yusong): for debug
|
| 436 |
-
|
| 437 |
-
# cumulative_loss += total_loss * batch_size
|
| 438 |
-
# num_samples += batch_size
|
| 439 |
-
if is_master(args) and (i % 100) == 0: # and i != 0:
|
| 440 |
-
logging.info(
|
| 441 |
-
f"Eval Epoch: {epoch} [{num_samples} / {samples_per_val}]"
|
| 442 |
-
)
|
| 443 |
-
if is_master(args):
|
| 444 |
-
val_metrics_per_dataset = {}
|
| 445 |
-
for n in eval_info.keys():
|
| 446 |
-
if args.clap_mlploss:
|
| 447 |
-
metrics_single_dataset = get_metrics(
|
| 448 |
-
audio_features=torch.cat(eval_info[n]["all_audio_features"]),
|
| 449 |
-
text_features=torch.cat(eval_info[n]["all_text_features"]),
|
| 450 |
-
logit_scale_a=logit_scale_a.cpu(),
|
| 451 |
-
audio_features_mlp=torch.cat(
|
| 452 |
-
eval_info[n]["all_audio_features_mlp"]
|
| 453 |
-
),
|
| 454 |
-
text_features_mlp=torch.cat(eval_info[n]["all_text_features_mlp"]),
|
| 455 |
-
logit_scale_t=logit_scale_t.cpu(),
|
| 456 |
-
mlp_loss=args.clap_mlploss
|
| 457 |
-
)
|
| 458 |
-
else:
|
| 459 |
-
metrics_single_dataset = get_metrics(
|
| 460 |
-
audio_features=torch.cat(eval_info[n]["all_audio_features"]),
|
| 461 |
-
text_features=torch.cat(eval_info[n]["all_text_features"]),
|
| 462 |
-
logit_scale_a=logit_scale_a.cpu(),
|
| 463 |
-
mlp_loss=args.clap_mlploss
|
| 464 |
-
)
|
| 465 |
-
val_metrics_per_dataset[n] = {
|
| 466 |
-
n + "/" + k: v for k, v in metrics_single_dataset.items()
|
| 467 |
-
}
|
| 468 |
-
metrics.update(val_metrics_per_dataset[n])
|
| 469 |
-
if "epoch" not in metrics.keys():
|
| 470 |
-
metrics.update({"epoch": epoch})
|
| 471 |
-
if is_master(args):
|
| 472 |
-
if not metrics:
|
| 473 |
-
return metrics
|
| 474 |
-
|
| 475 |
-
logging.info(
|
| 476 |
-
f"Eval Epoch: {epoch} "
|
| 477 |
-
+ "\n".join(
|
| 478 |
-
[
|
| 479 |
-
"\t".join([f"{k}: {round(v, 4):.4f}" for k, v in m.items()])
|
| 480 |
-
for m in val_metrics_per_dataset.values()
|
| 481 |
-
]
|
| 482 |
-
)
|
| 483 |
-
)
|
| 484 |
-
|
| 485 |
-
if args.save_logs:
|
| 486 |
-
for name, val in metrics.items():
|
| 487 |
-
if tb_writer is not None:
|
| 488 |
-
tb_writer.add_scalar(f"val/{name}", val, epoch)
|
| 489 |
-
|
| 490 |
-
with open(os.path.join(args.checkpoint_path, "results.jsonl"), "a+") as f:
|
| 491 |
-
f.write(json.dumps(metrics))
|
| 492 |
-
f.write("\n")
|
| 493 |
-
|
| 494 |
-
if args.wandb:
|
| 495 |
-
assert wandb is not None, "Please install wandb."
|
| 496 |
-
for name, val in metrics.items():
|
| 497 |
-
wandb.log({f"val/{name}": val, "epoch": epoch})
|
| 498 |
-
|
| 499 |
-
return metrics
|
| 500 |
-
else:
|
| 501 |
-
return metrics
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
def get_metrics(
|
| 505 |
-
audio_features,
|
| 506 |
-
text_features,
|
| 507 |
-
logit_scale_a,
|
| 508 |
-
audio_features_mlp=None,
|
| 509 |
-
text_features_mlp=None,
|
| 510 |
-
logit_scale_t=None,
|
| 511 |
-
mlp_loss=False
|
| 512 |
-
):
|
| 513 |
-
metrics = {}
|
| 514 |
-
if mlp_loss:
|
| 515 |
-
# Set up audio to text & text to audio similary matrice
|
| 516 |
-
a_logits_per_audio = (
|
| 517 |
-
(logit_scale_a * audio_features @ text_features_mlp.t()).detach().cpu()
|
| 518 |
-
)
|
| 519 |
-
a_logits_per_text = a_logits_per_audio.t().detach().cpu()
|
| 520 |
-
t_logits_per_audio = (
|
| 521 |
-
(logit_scale_t * audio_features_mlp @ text_features.t()).detach().cpu()
|
| 522 |
-
)
|
| 523 |
-
t_logits_per_text = t_logits_per_audio.t().detach().cpu()
|
| 524 |
-
|
| 525 |
-
labels = torch.arange(audio_features.shape[0]).long()
|
| 526 |
-
# Change the loss from two terms into four terms with 2x2 combined CE loss
|
| 527 |
-
total_loss = (
|
| 528 |
-
F.cross_entropy(a_logits_per_audio, labels)
|
| 529 |
-
+ F.cross_entropy(a_logits_per_text, labels)
|
| 530 |
-
+ F.cross_entropy(t_logits_per_audio, labels)
|
| 531 |
-
+ F.cross_entropy(t_logits_per_text, labels)
|
| 532 |
-
) / 4
|
| 533 |
-
|
| 534 |
-
metrics[f"cumulative_loss"] = total_loss.item()
|
| 535 |
-
metrics[f"num_samples"] = audio_features.shape[0]
|
| 536 |
-
|
| 537 |
-
logits = {
|
| 538 |
-
"audio_to_text": (a_logits_per_audio + t_logits_per_audio) / 2,
|
| 539 |
-
"text_to_audio": (a_logits_per_text + t_logits_per_text) / 2,
|
| 540 |
-
}
|
| 541 |
-
ground_truth = torch.arange(len(text_features)).view(-1, 1)
|
| 542 |
-
|
| 543 |
-
else:
|
| 544 |
-
# print("text_features", text_features)
|
| 545 |
-
# print("text_features.shape", text_features.shape)
|
| 546 |
-
logits_per_audio = (logit_scale_a * audio_features @ text_features.t()).detach().cpu()
|
| 547 |
-
logits_per_text = logits_per_audio.t().detach().cpu()
|
| 548 |
-
|
| 549 |
-
labels = torch.arange(audio_features.shape[0]).long()
|
| 550 |
-
# Change the loss from two terms into four terms with 2x2 combined CE loss
|
| 551 |
-
total_loss = (
|
| 552 |
-
F.cross_entropy(logits_per_audio, labels)
|
| 553 |
-
+ F.cross_entropy(logits_per_text, labels)
|
| 554 |
-
) / 2
|
| 555 |
-
|
| 556 |
-
metrics[f"cumulative_loss"] = total_loss.item()
|
| 557 |
-
metrics[f"num_samples"] = audio_features.shape[0]
|
| 558 |
-
|
| 559 |
-
logits = {"audio_to_text": logits_per_audio, "text_to_audio": logits_per_text}
|
| 560 |
-
|
| 561 |
-
ground_truth = torch.arange(len(text_features)).view(-1, 1)
|
| 562 |
-
|
| 563 |
-
for name, logit in logits.items():
|
| 564 |
-
ranking = torch.argsort(logit, descending=True)
|
| 565 |
-
preds = torch.where(ranking == ground_truth)[1] # (yusong) this line is slow because it uses single thread
|
| 566 |
-
preds = preds.detach().cpu().numpy()
|
| 567 |
-
metrics[f"{name}_mean_rank"] = preds.mean() + 1
|
| 568 |
-
metrics[f"{name}_median_rank"] = np.floor(np.median(preds)) + 1
|
| 569 |
-
for k in [1, 5, 10]:
|
| 570 |
-
metrics[f"{name}_R@{k}"] = np.mean(preds < k)
|
| 571 |
-
# map@10
|
| 572 |
-
metrics[f"{name}_mAP@10"] = np.mean(np.where(preds < 10, 1 / (preds + 1), 0.0))
|
| 573 |
-
|
| 574 |
-
return metrics
|
| 575 |
-
|
| 576 |
-
|
| 577 |
-
def evaluate_clotho_audiocaps(
|
| 578 |
-
model, data, epoch, args, autocast, device, tb_writer=None
|
| 579 |
-
):
|
| 580 |
-
"""
|
| 581 |
-
Adapted from https://github.com/XinhaoMei/audio-text_retrieval/blob/main/tools/utils.py.
|
| 582 |
-
1. for text-to-audio retrieval, do 5 times and average the results
|
| 583 |
-
2. for R@1, R@5, R@10 in audio-to-text retrieval, take the best rank among 5 text
|
| 584 |
-
3. for map@10 in audio-to-text retrieval:
|
| 585 |
-
3.1: sort the rank of 5 text
|
| 586 |
-
3.2: exclude the rank >=10 (0-index)
|
| 587 |
-
3.3: compute the map regarding the remaining ranks: np.mean(np.arange(1, len(ranks)+1) / ranks).
|
| 588 |
-
(3.3) That is, take the top ranks of 5 text that is < 10, and assign the descending number as ground truth.
|
| 589 |
-
(3.3) E.g.: the ground truth of first rank of the 5 text should be 1, the second rank should be 2, etc.
|
| 590 |
-
"""
|
| 591 |
-
# TODO: (yusong) only support single GPU evaluation and only support non-mlp case for now.
|
| 592 |
-
dataloader = data["val"].dataloader
|
| 593 |
-
with torch.no_grad():
|
| 594 |
-
eval_info = {}
|
| 595 |
-
for i, batch in enumerate(dataloader):
|
| 596 |
-
audios = batch # contains mel_spec, wavform, and longer list
|
| 597 |
-
|
| 598 |
-
# each item in the list has 5 texts
|
| 599 |
-
if args.tmodel == "transformer":
|
| 600 |
-
from clap_module import tokenize
|
| 601 |
-
texts = [tokenize(t) for t in batch['full_text']]
|
| 602 |
-
texts = torch.cat(texts)
|
| 603 |
-
else:
|
| 604 |
-
from .data import tokenizer
|
| 605 |
-
texts = [tokenizer(t, tmodel=args.tmodel) for t in batch['full_text']] # 5 texts for each audio
|
| 606 |
-
texts = {k: torch.cat([t[k] for t in texts]) for k in texts[0].keys()} # 5 x batch
|
| 607 |
-
|
| 608 |
-
# audios = audios.to(device=device, non_blocking=True)
|
| 609 |
-
|
| 610 |
-
# batch['__url__'] contains the path to the data tar this sample is from
|
| 611 |
-
# So, b.split("/")[-3:-1] will get you '<dataset_name>-<dataset-split>'
|
| 612 |
-
all_names = list(set(["-".join(b.split("/")[-3:-1]) for b in batch['__url__']]))
|
| 613 |
-
for name in all_names:
|
| 614 |
-
if name not in eval_info.keys():
|
| 615 |
-
# we will not use mlp outputs even if args.clap_mlploss=True
|
| 616 |
-
eval_info[name] = {
|
| 617 |
-
"cumulative_loss": 0.0,
|
| 618 |
-
"num_samples": 0,
|
| 619 |
-
"all_audio_features": [],
|
| 620 |
-
"all_text_features": []
|
| 621 |
-
}
|
| 622 |
-
with autocast():
|
| 623 |
-
audio_features = model(audios, None, device)
|
| 624 |
-
text_features = model(None, texts, device)
|
| 625 |
-
audio_features = F.normalize(audio_features, dim=-1)
|
| 626 |
-
text_features = F.normalize(text_features, dim=-1)
|
| 627 |
-
|
| 628 |
-
all_names = list(set(["-".join(b.split("/")[-3:-1]) for b in batch['__url__']]))
|
| 629 |
-
for n in all_names:
|
| 630 |
-
idx = np.where(
|
| 631 |
-
np.array(
|
| 632 |
-
["-".join(b.split("/")[-3:-1]) for b in batch['__url__']]
|
| 633 |
-
)
|
| 634 |
-
== n
|
| 635 |
-
)[0]
|
| 636 |
-
eval_info[n]["all_audio_features"].append(
|
| 637 |
-
audio_features.cpu().index_select(
|
| 638 |
-
0, torch.tensor(idx).long()
|
| 639 |
-
)
|
| 640 |
-
)
|
| 641 |
-
# (yusong) please double-check. This is for selecting 5 text features at once.
|
| 642 |
-
# because idx is a list of indices in size of num_samples,
|
| 643 |
-
# and text_features is a tensor of size (5*num_samples, dim)
|
| 644 |
-
# so we need to select 5 consecutive indices at once for a single index in idx.
|
| 645 |
-
eval_info[n]["all_text_features"].append(
|
| 646 |
-
text_features.cpu().reshape([-1, 5, text_features.shape[1]]).index_select(
|
| 647 |
-
0, torch.tensor(idx).long()
|
| 648 |
-
).reshape([-1, text_features.shape[1]])
|
| 649 |
-
)
|
| 650 |
-
|
| 651 |
-
val_metrics_all = {}
|
| 652 |
-
|
| 653 |
-
for n in eval_info.keys():
|
| 654 |
-
logit_scale_a, logit_scale_t = model(None, None, device)
|
| 655 |
-
logit_scale_a = logit_scale_a.cpu()
|
| 656 |
-
|
| 657 |
-
audio_features = torch.cat(eval_info[n]["all_audio_features"], dim=0)
|
| 658 |
-
text_features = torch.cat(eval_info[n]["all_text_features"], dim=0)
|
| 659 |
-
|
| 660 |
-
logits_per_audio = (logit_scale_a * audio_features @ text_features.t()).detach().cpu()
|
| 661 |
-
logits_per_text = logits_per_audio.t().detach().cpu()
|
| 662 |
-
|
| 663 |
-
# logits_per_audio shape: [num_samples, num_samples*5]
|
| 664 |
-
# logits_per_text shape: [num_samples*5, num_samples]
|
| 665 |
-
|
| 666 |
-
logging.info(f"dataset {n}, logits_per_audio shape: {logits_per_audio.shape}, "
|
| 667 |
-
f"logits_per_text shape: {logits_per_text.shape}")
|
| 668 |
-
|
| 669 |
-
metrics = {}
|
| 670 |
-
num_samples = audio_features.shape[0]
|
| 671 |
-
metrics[f"num_samples"] = num_samples
|
| 672 |
-
|
| 673 |
-
# (yusong) the following code is very important, please double-check:
|
| 674 |
-
# logits_per_audio.reshape(num_samples, num_samples, 5)[:, :, d]
|
| 675 |
-
# logits_per_text.reshape(num_samples, 5, num_samples)[:, d, :]
|
| 676 |
-
# Those two are retrieving one of the 5 text for each audio.
|
| 677 |
-
labels = torch.arange(audio_features.shape[0]).long()
|
| 678 |
-
audio_to_text_loss = [
|
| 679 |
-
F.cross_entropy(
|
| 680 |
-
logits_per_audio.reshape(num_samples, num_samples, 5)[:, :, d], labels) for d in range(5)
|
| 681 |
-
]
|
| 682 |
-
text_to_audio_loss = [
|
| 683 |
-
F.cross_entropy(
|
| 684 |
-
logits_per_text.reshape(num_samples, 5, num_samples)[:, d, :], labels) for d in range(5)
|
| 685 |
-
]
|
| 686 |
-
total_loss = (
|
| 687 |
-
np.mean(audio_to_text_loss) + np.mean(text_to_audio_loss)
|
| 688 |
-
) / 2
|
| 689 |
-
|
| 690 |
-
metrics[f"cumulative_loss"] = total_loss.item()
|
| 691 |
-
|
| 692 |
-
# text to audio: do 5 times
|
| 693 |
-
pred_text = []
|
| 694 |
-
for d in range(5):
|
| 695 |
-
logit = logits_per_text.reshape(num_samples, 5, num_samples)[:, d, :]
|
| 696 |
-
ground_truth = torch.arange(len(logit)).view(-1, 1)
|
| 697 |
-
ranking = torch.argsort(logit, descending=True) # [num_samples, num_samples]
|
| 698 |
-
preds = torch.where(ranking == ground_truth)[1]
|
| 699 |
-
pred_text.append(preds.detach().cpu().numpy())
|
| 700 |
-
pred_text_concat = np.concatenate(pred_text, axis=0) # [5*num_samples]
|
| 701 |
-
metrics[f"text_to_audio_mean_rank"] = pred_text_concat.mean() + 1
|
| 702 |
-
metrics[f"text_to_audio_median_rank"] = np.floor(np.median(pred_text_concat)) + 1
|
| 703 |
-
for k in [1, 5, 10]:
|
| 704 |
-
metrics[f"text_to_audio_R@{k}"] = np.mean(pred_text_concat < k)
|
| 705 |
-
# map@10
|
| 706 |
-
metrics[f"text_to_audio_mAP@10"] = np.mean(np.where(pred_text_concat < 10, 1 / (pred_text_concat + 1), 0.0))
|
| 707 |
-
|
| 708 |
-
# audio to text: take the best result
|
| 709 |
-
# for audio to text map 10, sort and assign descending ground truth.
|
| 710 |
-
# see https://github.com/XinhaoMei/audio-text_retrieval/blob/main/tools/utils.py#L103
|
| 711 |
-
# map@10
|
| 712 |
-
map_all = []
|
| 713 |
-
pred_audio_all = []
|
| 714 |
-
for d in range(num_samples):
|
| 715 |
-
# logits_per_audio: [num_samples, num_samples*5]
|
| 716 |
-
logit_single = logits_per_audio[d, :] # [5*num_samples]
|
| 717 |
-
# Ground-truth index: [d*5, d*5+1, d*5+2, d*5+3, d*5+4]
|
| 718 |
-
ranking = torch.argsort(logit_single, descending=True) # [5*num_samples]
|
| 719 |
-
# ranking: the index of first match, second match, ...
|
| 720 |
-
ground_truth = torch.arange(d * 5, d * 5 + 5)[None]
|
| 721 |
-
all_pred = torch.where(torch.stack([ranking] * 5) == ground_truth.view(-1, 1))[1]
|
| 722 |
-
min_pred = torch.min(all_pred)
|
| 723 |
-
pred_audio_all.append(min_pred.detach().cpu().numpy())
|
| 724 |
-
all_pred_filter = all_pred[all_pred < 10].detach().cpu().numpy()
|
| 725 |
-
# /5 because we have 5 text, so it means for the text rank >=10 we count as 0.
|
| 726 |
-
map_single = np.sum((np.arange(1, len(all_pred_filter) + 1) / (all_pred_filter + 1))) / 5
|
| 727 |
-
map_all.append(map_single)
|
| 728 |
-
metrics[f"audio_to_text_mAP@10"] = np.mean(map_all)
|
| 729 |
-
for k in [1, 5, 10]:
|
| 730 |
-
metrics[f"audio_to_text_R@{k}"] = np.mean(np.array(pred_audio_all) < k)
|
| 731 |
-
|
| 732 |
-
val_metrics_all[n] = {
|
| 733 |
-
n + "/" + k: v for k, v in metrics.items()
|
| 734 |
-
}
|
| 735 |
-
return val_metrics_all
|
| 736 |
-
|
| 737 |
-
|
| 738 |
-
def calculate_selection_performance_clotho_audiocaps(val_metrics_per_dataset):
|
| 739 |
-
"""
|
| 740 |
-
Calculate performance for Clotho+AudioCaps for model selection.
|
| 741 |
-
"""
|
| 742 |
-
selection_performance_all = []
|
| 743 |
-
for n in val_metrics_per_dataset.keys():
|
| 744 |
-
selection_performance = (val_metrics_per_dataset[n][f"{n}/audio_to_text_mAP@10"] +
|
| 745 |
-
val_metrics_per_dataset[n][f"{n}/text_to_audio_mAP@10"]) / 2
|
| 746 |
-
selection_performance_all.append(selection_performance)
|
| 747 |
-
return np.mean(selection_performance_all)
|
| 748 |
-
|
| 749 |
-
|
| 750 |
-
def select_top_metric_clotho_audiocaps(metrics, val_metrics_per_dataset, args):
|
| 751 |
-
# val_metrics_per_dataset: dict, key: dataset name, value: dict, key: metric name, value: metric value
|
| 752 |
-
# metrics: dict, key: metric name, value: metric value
|
| 753 |
-
# Hack: use args to save the top performance
|
| 754 |
-
if not hasattr(args, "top_selection_performance"):
|
| 755 |
-
selection_performance = calculate_selection_performance_clotho_audiocaps(val_metrics_per_dataset)
|
| 756 |
-
# TODO: write the if and else together
|
| 757 |
-
metric_update = {}
|
| 758 |
-
for n in val_metrics_per_dataset.keys():
|
| 759 |
-
for k in val_metrics_per_dataset[n].keys():
|
| 760 |
-
metric_update[k.split('/')[0] + '-top' + '/' + k.split('/')[1]] = val_metrics_per_dataset[n][k]
|
| 761 |
-
metric_update['top_selection_performance'] = selection_performance
|
| 762 |
-
metric_update['top-selection-epoch'] = metrics['epoch']
|
| 763 |
-
metrics.update(metric_update)
|
| 764 |
-
args.top_metric = metric_update
|
| 765 |
-
args.top_selection_performance = selection_performance
|
| 766 |
-
else:
|
| 767 |
-
selection_performance_new = calculate_selection_performance_clotho_audiocaps(val_metrics_per_dataset)
|
| 768 |
-
selection_performance_old = args.top_selection_performance
|
| 769 |
-
if selection_performance_new > selection_performance_old:
|
| 770 |
-
metric_update = {}
|
| 771 |
-
for n in val_metrics_per_dataset.keys():
|
| 772 |
-
for k in val_metrics_per_dataset[n].keys():
|
| 773 |
-
metric_update[k.split('/')[0] + '-top' + '/' + k.split('/')[1]] = val_metrics_per_dataset[n][k]
|
| 774 |
-
metric_update['top_selection_performance'] = selection_performance_new
|
| 775 |
-
metric_update['top-selection-epoch'] = metrics['epoch']
|
| 776 |
-
metrics.update(metric_update)
|
| 777 |
-
args.top_metric = metric_update
|
| 778 |
-
args.top_selection_performance = selection_performance_new
|
| 779 |
-
else:
|
| 780 |
-
metrics.update(args.top_metric)
|
| 781 |
-
return metrics
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/training/zero_shot.py
DELETED
|
@@ -1,90 +0,0 @@
|
|
| 1 |
-
# NOTE: This script is currently not supported for CLAP.
|
| 2 |
-
import logging
|
| 3 |
-
from contextlib import suppress
|
| 4 |
-
|
| 5 |
-
import torch
|
| 6 |
-
import torch.nn.functional as F
|
| 7 |
-
from tqdm import tqdm
|
| 8 |
-
|
| 9 |
-
from clap_module import tokenize
|
| 10 |
-
from .imagenet_zeroshot_data import imagenet_classnames, openai_imagenet_template
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
def zero_shot_classifier(model, classnames, templates, args):
|
| 14 |
-
with torch.no_grad():
|
| 15 |
-
zeroshot_weights = []
|
| 16 |
-
for classname in tqdm(classnames):
|
| 17 |
-
texts = [template(classname) for template in templates] # format with class
|
| 18 |
-
texts = tokenize(texts).to(args.device) # tokenize
|
| 19 |
-
if args.distributed and not args.horovod:
|
| 20 |
-
class_embeddings = model.module.encode_text(texts)
|
| 21 |
-
else:
|
| 22 |
-
class_embeddings = model.encode_text(texts)
|
| 23 |
-
class_embedding = F.normalize(class_embeddings, dim=-1).mean(dim=0)
|
| 24 |
-
class_embedding /= class_embedding.norm()
|
| 25 |
-
zeroshot_weights.append(class_embedding)
|
| 26 |
-
zeroshot_weights = torch.stack(zeroshot_weights, dim=1).to(args.device)
|
| 27 |
-
return zeroshot_weights
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
def accuracy(output, target, topk=(1,)):
|
| 31 |
-
pred = output.topk(max(topk), 1, True, True)[1].t()
|
| 32 |
-
correct = pred.eq(target.view(1, -1).expand_as(pred))
|
| 33 |
-
return [float(correct[:k].reshape(-1).float().sum(0, keepdim=True).cpu().numpy()) for k in topk]
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
def run(model, classifier, dataloader, args):
|
| 37 |
-
autocast = torch.cuda.amp.autocast if args.precision == 'amp' else suppress
|
| 38 |
-
with torch.no_grad():
|
| 39 |
-
top1, top5, n = 0., 0., 0.
|
| 40 |
-
for images, target in tqdm(dataloader, unit_scale=args.batch_size):
|
| 41 |
-
images = images.to(args.device)
|
| 42 |
-
target = target.to(args.device)
|
| 43 |
-
|
| 44 |
-
with autocast():
|
| 45 |
-
# predict
|
| 46 |
-
if args.distributed and not args.horovod:
|
| 47 |
-
image_features = model.module.encode_image(images)
|
| 48 |
-
else:
|
| 49 |
-
image_features = model.encode_image(images)
|
| 50 |
-
image_features = F.normalize(image_features, dim=-1)
|
| 51 |
-
logits = 100. * image_features @ classifier
|
| 52 |
-
|
| 53 |
-
# measure accuracy
|
| 54 |
-
acc1, acc5 = accuracy(logits, target, topk=(1, 5))
|
| 55 |
-
top1 += acc1
|
| 56 |
-
top5 += acc5
|
| 57 |
-
n += images.size(0)
|
| 58 |
-
|
| 59 |
-
top1 = (top1 / n)
|
| 60 |
-
top5 = (top5 / n)
|
| 61 |
-
return top1, top5
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
def zero_shot_eval(model, data, epoch, args):
|
| 65 |
-
if 'imagenet-val' not in data and 'imagenet-v2' not in data:
|
| 66 |
-
return {}
|
| 67 |
-
if args.zeroshot_frequency == 0:
|
| 68 |
-
return {}
|
| 69 |
-
if (epoch % args.zeroshot_frequency) != 0 and epoch != args.epochs:
|
| 70 |
-
return {}
|
| 71 |
-
|
| 72 |
-
logging.info('Starting zero-shot imagenet.')
|
| 73 |
-
|
| 74 |
-
logging.info('Building zero-shot classifier')
|
| 75 |
-
classifier = zero_shot_classifier(model, imagenet_classnames, openai_imagenet_template, args)
|
| 76 |
-
|
| 77 |
-
logging.info('Using classifier')
|
| 78 |
-
results = {}
|
| 79 |
-
if 'imagenet-val' in data:
|
| 80 |
-
top1, top5 = run(model, classifier, data['imagenet-val'].dataloader, args)
|
| 81 |
-
results['imagenet-zeroshot-val-top1'] = top1
|
| 82 |
-
results['imagenet-zeroshot-val-top5'] = top5
|
| 83 |
-
if 'imagenet-v2' in data:
|
| 84 |
-
top1, top5 = run(model, classifier, data['imagenet-v2'].dataloader, args)
|
| 85 |
-
results['imagenetv2-zeroshot-val-top1'] = top1
|
| 86 |
-
results['imagenetv2-zeroshot-val-top5'] = top5
|
| 87 |
-
|
| 88 |
-
logging.info('Finished zero-shot imagenet.')
|
| 89 |
-
|
| 90 |
-
return results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/laion_clap/unit_test.py
DELETED
|
@@ -1,75 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Contrastive Language-Audio Pretraining Model from LAION
|
| 3 |
-
--------------------------------------------------------
|
| 4 |
-
Paper: https://arxiv.org/abs/2211.06687
|
| 5 |
-
Authors (equal contributions): Ke Chen, Yusong Wu, Tianyu Zhang, Yuchen Hui
|
| 6 |
-
Support: LAION
|
| 7 |
-
"""
|
| 8 |
-
|
| 9 |
-
import numpy as np
|
| 10 |
-
import librosa
|
| 11 |
-
import torch
|
| 12 |
-
import laion_clap
|
| 13 |
-
|
| 14 |
-
# quantization
|
| 15 |
-
def int16_to_float32(x):
|
| 16 |
-
return (x / 32767.0).astype(np.float32)
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
def float32_to_int16(x):
|
| 20 |
-
x = np.clip(x, a_min=-1., a_max=1.)
|
| 21 |
-
return (x * 32767.).astype(np.int16)
|
| 22 |
-
|
| 23 |
-
model = laion_clap.CLAP_Module(enable_fusion=False)
|
| 24 |
-
model.load_ckpt()
|
| 25 |
-
|
| 26 |
-
# Directly get audio embeddings from audio files
|
| 27 |
-
audio_file = [
|
| 28 |
-
'/home/la/kechen/Research/KE_CLAP/ckpt/test_clap_short.wav',
|
| 29 |
-
'/home/la/kechen/Research/KE_CLAP/ckpt/test_clap_long.wav'
|
| 30 |
-
]
|
| 31 |
-
audio_embed = model.get_audio_embedding_from_filelist(x = audio_file, use_tensor=False)
|
| 32 |
-
print(audio_embed[:,-20:])
|
| 33 |
-
print(audio_embed.shape)
|
| 34 |
-
|
| 35 |
-
# Get audio embeddings from audio data
|
| 36 |
-
audio_data, _ = librosa.load('/home/la/kechen/Research/KE_CLAP/ckpt/test_clap_short.wav', sr=48000) # sample rate should be 48000
|
| 37 |
-
audio_data = audio_data.reshape(1, -1) # Make it (1,T) or (N,T)
|
| 38 |
-
audio_embed = model.get_audio_embedding_from_data(x = audio_data, use_tensor=False)
|
| 39 |
-
print(audio_embed[:,-20:])
|
| 40 |
-
print(audio_embed.shape)
|
| 41 |
-
|
| 42 |
-
# Directly get audio embeddings from audio files, but return torch tensor
|
| 43 |
-
audio_file = [
|
| 44 |
-
'/home/la/kechen/Research/KE_CLAP/ckpt/test_clap_short.wav',
|
| 45 |
-
'/home/la/kechen/Research/KE_CLAP/ckpt/test_clap_long.wav'
|
| 46 |
-
]
|
| 47 |
-
audio_embed = model.get_audio_embedding_from_filelist(x = audio_file, use_tensor=True)
|
| 48 |
-
print(audio_embed[:,-20:])
|
| 49 |
-
print(audio_embed.shape)
|
| 50 |
-
|
| 51 |
-
# Get audio embeddings from audio data
|
| 52 |
-
audio_data, _ = librosa.load('/home/la/kechen/Research/KE_CLAP/ckpt/test_clap_short.wav', sr=48000) # sample rate should be 48000
|
| 53 |
-
audio_data = audio_data.reshape(1, -1) # Make it (1,T) or (N,T)
|
| 54 |
-
audio_data = torch.from_numpy(int16_to_float32(float32_to_int16(audio_data))).float() # quantize before send it in to the model
|
| 55 |
-
audio_embed = model.get_audio_embedding_from_data(x = audio_data, use_tensor=True)
|
| 56 |
-
print(audio_embed[:,-20:])
|
| 57 |
-
print(audio_embed.shape)
|
| 58 |
-
|
| 59 |
-
# Get text embedings from texts:
|
| 60 |
-
text_data = ["I love the contrastive learning", "I love the pretrain model"]
|
| 61 |
-
text_embed = model.get_text_embedding(text_data)
|
| 62 |
-
print(text_embed)
|
| 63 |
-
print(text_embed.shape)
|
| 64 |
-
|
| 65 |
-
# Get text embedings from texts, but return torch tensor:
|
| 66 |
-
text_data = ["I love the contrastive learning", "I love the pretrain model"]
|
| 67 |
-
text_embed = model.get_text_embedding(text_data, use_tensor=True)
|
| 68 |
-
print(text_embed)
|
| 69 |
-
print(text_embed.shape)
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/log.py
DELETED
|
@@ -1,976 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import torch
|
| 3 |
-
import time
|
| 4 |
-
import numpy as np
|
| 5 |
-
#import torchaudio
|
| 6 |
-
import plotly.express as px
|
| 7 |
-
import soundfile as sf
|
| 8 |
-
#import plotly.graph_objects as go
|
| 9 |
-
import pandas as pd
|
| 10 |
-
import plotly
|
| 11 |
-
import plotly.graph_objects as go
|
| 12 |
-
import matplotlib.pyplot as plt
|
| 13 |
-
|
| 14 |
-
#from notebooks.shared_log_filter import frequencies
|
| 15 |
-
|
| 16 |
-
"""
|
| 17 |
-
Logging related functions that I wrote for my own use
|
| 18 |
-
This is quite a mess, but I'm too lazy to clean it up
|
| 19 |
-
"""
|
| 20 |
-
#from src.CQT_nsgt import CQT_cpx
|
| 21 |
-
|
| 22 |
-
def do_stft(noisy, clean=None, win_size=2048, hop_size=512, device="cpu", DC=True):
|
| 23 |
-
"""
|
| 24 |
-
applies the stft, this an ugly old function, but I'm using it for logging and I'm to lazy to modify it
|
| 25 |
-
"""
|
| 26 |
-
|
| 27 |
-
#window_fn = tf.signal.hamming_window
|
| 28 |
-
|
| 29 |
-
#win_size=args.stft.win_size
|
| 30 |
-
#hop_size=args.stft.hop_size
|
| 31 |
-
window=torch.hamming_window(window_length=win_size)
|
| 32 |
-
window=window.to(noisy.device)
|
| 33 |
-
noisy=torch.cat((noisy, torch.zeros(noisy.shape[0],win_size).to(noisy.device)),1)
|
| 34 |
-
stft_signal_noisy=torch.stft(noisy, win_size, hop_length=hop_size,window=window,center=False,return_complex=False)
|
| 35 |
-
stft_signal_noisy=stft_signal_noisy.permute(0,3,2,1)
|
| 36 |
-
#stft_signal_noisy=tf.signal.stft(noisy,frame_length=win_size, window_fn=window_fn, frame_step=hop_size)
|
| 37 |
-
#stft_noisy_stacked=tf.stack( values=[tf.math.real(stft_signal_noisy), tf.math.imag(stft_signal_noisy)], axis=-1)
|
| 38 |
-
|
| 39 |
-
if clean!=None:
|
| 40 |
-
|
| 41 |
-
# stft_signal_clean=tf.signal.stft(clean,frame_length=win_size, window_fn=window_fn, frame_step=hop_size)
|
| 42 |
-
clean=torch.cat((clean, torch.zeros(clean.shape[0],win_size).to(device)),1)
|
| 43 |
-
stft_signal_clean=torch.stft(clean, win_size, hop_length=hop_size,window=window, center=False,return_complex=False)
|
| 44 |
-
stft_signal_clean=stft_signal_clean.permute(0,3,2,1)
|
| 45 |
-
#stft_clean_stacked=tf.stack( values=[tf.math.real(stft_signal_clean), tf.math.imag(stft_signal_clean)], axis=-1)
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
if DC:
|
| 49 |
-
return stft_signal_noisy, stft_signal_clean
|
| 50 |
-
else:
|
| 51 |
-
return stft_signal_noisy[...,1:], stft_signal_clean[...,1:]
|
| 52 |
-
else:
|
| 53 |
-
|
| 54 |
-
if DC:
|
| 55 |
-
return stft_signal_noisy
|
| 56 |
-
else:
|
| 57 |
-
return stft_signal_noisy[...,1:]
|
| 58 |
-
|
| 59 |
-
def plot_norms(path, normsscores, normsguides, t, name):
|
| 60 |
-
values=t.cpu().numpy()
|
| 61 |
-
|
| 62 |
-
df=pd.DataFrame.from_dict(
|
| 63 |
-
{"sigma": values[0:-1], "score": normsscores.cpu().numpy(), "guidance": normsguides.cpu().numpy()}
|
| 64 |
-
)
|
| 65 |
-
fig= px.line(df, x="sigma", y=["score", "guidance"],log_x=True, log_y=True, markers=True)
|
| 66 |
-
|
| 67 |
-
path_to_plotly_html = path+"/"+name+".html"
|
| 68 |
-
|
| 69 |
-
fig.write_html(path_to_plotly_html, auto_play = False)
|
| 70 |
-
return fig
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
def plot_spectral_analysis_sampling( avgspecNF,avgspecDEN, ts, fs=22050, nfft=1024):
|
| 74 |
-
T,F=avgspecNF.shape
|
| 75 |
-
f=torch.arange(0, F)*fs/nfft
|
| 76 |
-
f=f.unsqueeze(1).repeat(1,T).view(-1)
|
| 77 |
-
avgspecNF=avgspecNF.permute(1,0).reshape(-1)
|
| 78 |
-
avgspecDEN=avgspecDEN.permute(1,0).reshape(-1)
|
| 79 |
-
ts=ts.cpu().numpy().tolist()
|
| 80 |
-
ts=513*ts
|
| 81 |
-
#ts=np.repeat(ts,513)
|
| 82 |
-
print(f.shape, avgspecDEN.shape, avgspecNF.shape, len(ts))
|
| 83 |
-
df=pd.DataFrame.from_dict(
|
| 84 |
-
{"f": f, "noisy": avgspecNF, "denoised": avgspecDEN, "sigma":ts
|
| 85 |
-
}
|
| 86 |
-
)
|
| 87 |
-
|
| 88 |
-
fig= px.line(df, x="f", y=["noisy", "denoised"], animation_frame="sigma", log_x=False,log_y=False, markers=False)
|
| 89 |
-
return fig
|
| 90 |
-
|
| 91 |
-
def plot_spectral_analysis(avgspecY, avgspecNF,avgspecDEN, ts, fs=22050, nfft=1024):
|
| 92 |
-
T,F=avgspecNF.shape
|
| 93 |
-
f=torch.arange(0, F)*fs/nfft
|
| 94 |
-
f=f.unsqueeze(1).repeat(1,T).view(-1)
|
| 95 |
-
avgspecY=avgspecY.squeeze(0).unsqueeze(1).repeat(1,T).view(-1)
|
| 96 |
-
avgspecNF=avgspecNF.permute(1,0).reshape(-1)
|
| 97 |
-
avgspecDEN=avgspecDEN.permute(1,0).reshape(-1)
|
| 98 |
-
ts=ts.cpu().numpy().tolist()
|
| 99 |
-
ts=513*ts
|
| 100 |
-
#ts=np.repeat(ts,513)
|
| 101 |
-
print(f.shape, avgspecY.shape, avgspecNF.shape, len(ts))
|
| 102 |
-
df=pd.DataFrame.from_dict(
|
| 103 |
-
{"f": f, "y": avgspecY, "noisy": avgspecNF, "denoised": avgspecDEN, "sigma":ts
|
| 104 |
-
}
|
| 105 |
-
)
|
| 106 |
-
|
| 107 |
-
fig= px.line(df, x="f", y=["y", "noisy", "denoised"], animation_frame="sigma", log_x=False,log_y=False, markers=False)
|
| 108 |
-
return fig
|
| 109 |
-
|
| 110 |
-
def plot_loss_by_sigma_test_snr(average_snr, average_snr_out, t):
|
| 111 |
-
#write a fancy plot to log in wandb
|
| 112 |
-
values=np.array(t)
|
| 113 |
-
df=pd.DataFrame.from_dict(
|
| 114 |
-
{"sigma": values, "SNR": average_snr, "SNR_denoised": average_snr_out
|
| 115 |
-
}
|
| 116 |
-
)
|
| 117 |
-
|
| 118 |
-
fig= px.line(df, x="sigma", y=["SNR", "SNR_denoised"],log_x=True, markers=True)
|
| 119 |
-
return fig
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
# Create and style traces
|
| 125 |
-
|
| 126 |
-
def plot_loss_by_sigma(sigma_means, sigma_stds, sigma_bins, log_scale=True):
|
| 127 |
-
df=pd.DataFrame.from_dict(
|
| 128 |
-
{"sigma": sigma_bins, "loss": sigma_means, "std": sigma_stds
|
| 129 |
-
}
|
| 130 |
-
)
|
| 131 |
-
|
| 132 |
-
fig= error_line('bar', data_frame=df, x="sigma", y="loss", error_y="std", log_x=log_scale, markers=True, range_y=[0, 2])
|
| 133 |
-
|
| 134 |
-
return fig
|
| 135 |
-
|
| 136 |
-
def plot_loss_by_sigma_and_freq(sigma_freq_means, sigma_freq_stds, sigma_bins, freq_bins):
|
| 137 |
-
df=pd.DataFrame.from_dict(
|
| 138 |
-
{"sigma": np.tile(sigma_bins, len(freq_bins))})
|
| 139 |
-
names=[]
|
| 140 |
-
means=[]
|
| 141 |
-
stds=[]
|
| 142 |
-
for i in range(len(freq_bins)):
|
| 143 |
-
name=[str(freq_bins[i])+"Hz" for j in range(len(sigma_bins))]
|
| 144 |
-
names.extend(name)
|
| 145 |
-
means.extend(sigma_freq_means[i])
|
| 146 |
-
stds.extend(sigma_freq_stds[i])
|
| 147 |
-
df['freq']=names
|
| 148 |
-
df['means']=means
|
| 149 |
-
df['stds']=stds
|
| 150 |
-
print(df)
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
#basically, we want to plot the loss as a function of sigma and freq. We do it by plotting a line for each freq.
|
| 154 |
-
fig= error_line('bar', data_frame=df, x="sigma", y="means", error_y="stds", color="freq", log_x=True, markers=True, range_y=[0, 2])
|
| 155 |
-
|
| 156 |
-
return fig
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
def plot_melspectrogram(X, refr=1):
|
| 161 |
-
X=X.squeeze(1) #??
|
| 162 |
-
X=X.cpu().numpy()
|
| 163 |
-
if refr==None:
|
| 164 |
-
refr=np.max(np.abs(X))+1e-8
|
| 165 |
-
|
| 166 |
-
S_db = 10*np.log10(np.abs(X)/refr)
|
| 167 |
-
|
| 168 |
-
S_db=np.transpose(S_db, (0,2,1))
|
| 169 |
-
S_db=np.flip(S_db, axis=1)
|
| 170 |
-
|
| 171 |
-
for i in range(X.shape[0]): #iterate over batch size, shity way of ploting all the batched spectrograms
|
| 172 |
-
o=S_db[i]
|
| 173 |
-
if i==0:
|
| 174 |
-
res=o
|
| 175 |
-
else:
|
| 176 |
-
res=np.concatenate((res,o), axis=1)
|
| 177 |
-
|
| 178 |
-
fig=px.imshow( res, zmin=-40, zmax=20)
|
| 179 |
-
|
| 180 |
-
fig.update_layout(coloraxis_showscale=False)
|
| 181 |
-
|
| 182 |
-
return fig
|
| 183 |
-
|
| 184 |
-
def plot_cpxspectrogram(X):
|
| 185 |
-
X=X.squeeze(1)
|
| 186 |
-
X=X.cpu().numpy()
|
| 187 |
-
fig=px.imshow(X, facet_col=3, animation_frame=0)
|
| 188 |
-
fig.update_layout(coloraxis_showscale=False)
|
| 189 |
-
return fig
|
| 190 |
-
def print_cuda_memory():
|
| 191 |
-
t = torch.cuda.get_device_properties(0).total_memory
|
| 192 |
-
r = torch.cuda.memory_reserved(0)
|
| 193 |
-
a = torch.cuda.memory_allocated(0)
|
| 194 |
-
f = r-a # free inside reservedk
|
| 195 |
-
print("memrylog",t,r,a,f)
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
def plot_mag_spectrogram(X, refr=None, path=None,name="spec"):
|
| 200 |
-
X=X.cpu().numpy()
|
| 201 |
-
if refr==None:
|
| 202 |
-
refr=np.max(np.abs(X))+1e-8
|
| 203 |
-
|
| 204 |
-
S_db = 10*np.log10(np.abs(X)/refr)
|
| 205 |
-
S_db=np.flip(S_db, axis=1)
|
| 206 |
-
|
| 207 |
-
for i in range(X.shape[0]): #iterate over batch size, shity way of ploting all the batched spectrograms
|
| 208 |
-
o=S_db[i]
|
| 209 |
-
if i==0:
|
| 210 |
-
res=o
|
| 211 |
-
else:
|
| 212 |
-
res=np.concatenate((res,o), axis=1)
|
| 213 |
-
|
| 214 |
-
fig = px.imshow(res, zmin=-40, zmax=20)
|
| 215 |
-
|
| 216 |
-
fig.update_layout(coloraxis_showscale=False)
|
| 217 |
-
|
| 218 |
-
path_to_plotly_png = path+"/"+name+".png"
|
| 219 |
-
plotly.io.write_image(fig, path_to_plotly_png)
|
| 220 |
-
|
| 221 |
-
return fig
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
def plot_spectrogram(X, refr=None, cplx=True, freqs=None, time=None):
|
| 225 |
-
X=X.squeeze(1)
|
| 226 |
-
X=X.cpu().numpy()
|
| 227 |
-
if cplx:
|
| 228 |
-
X=np.sqrt(X[:,:,:,0]**2 + X[:,:,:,1]**2)
|
| 229 |
-
|
| 230 |
-
if refr==None:
|
| 231 |
-
refr=np.max(np.abs(X))+1e-8
|
| 232 |
-
|
| 233 |
-
S_db = 10*np.log10(np.abs(X)/refr)
|
| 234 |
-
|
| 235 |
-
S_db=np.transpose(S_db, (0,2,1))
|
| 236 |
-
S_db=np.flip(S_db, axis=1)
|
| 237 |
-
|
| 238 |
-
for i in range(X.shape[0]): #iterate over batch size, shity way of ploting all the batched spectrograms
|
| 239 |
-
o=S_db[i]
|
| 240 |
-
if i==0:
|
| 241 |
-
res=o
|
| 242 |
-
else:
|
| 243 |
-
res=np.concatenate((res,o), axis=1)
|
| 244 |
-
#freqs ia a tensor of shape (F,)
|
| 245 |
-
#time is a tensor of shape (T,)
|
| 246 |
-
#add the frequency and time axis correctly
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
fig=px.imshow( res, zmin=-40, zmax=20)
|
| 250 |
-
fig.update_layout(coloraxis_showscale=False)
|
| 251 |
-
|
| 252 |
-
return fig
|
| 253 |
-
|
| 254 |
-
def write_audio_file(x, sr, string: str, path='tmp', stereo=False, normalize=False):
|
| 255 |
-
if normalize:
|
| 256 |
-
x=x/torch.max(torch.abs(x))
|
| 257 |
-
if not(os.path.exists(path)):
|
| 258 |
-
os.makedirs(path)
|
| 259 |
-
|
| 260 |
-
path=os.path.join(path,string+".wav")
|
| 261 |
-
if stereo:
|
| 262 |
-
'''
|
| 263 |
-
x has shape (B,2,T)
|
| 264 |
-
'''
|
| 265 |
-
x=x.permute(0,2,1) #B,T,2
|
| 266 |
-
x=x.flatten(0,1) #B*T,2
|
| 267 |
-
x=x.cpu().numpy()
|
| 268 |
-
#if np.abs(np.max(x))>=1:
|
| 269 |
-
# #normalize to avoid clipping
|
| 270 |
-
# x=x/np.abs(np.max(x))
|
| 271 |
-
else:
|
| 272 |
-
x=x.flatten()
|
| 273 |
-
x=x.unsqueeze(1)
|
| 274 |
-
x=x.cpu().numpy()
|
| 275 |
-
#if np.abs(np.max(x))>=1:
|
| 276 |
-
# #normalize to avoid clipping
|
| 277 |
-
# x=x/np.abs(np.max(x))
|
| 278 |
-
sf.write(path,x,sr)
|
| 279 |
-
return path
|
| 280 |
-
|
| 281 |
-
def plot_trajectories(x,sr, sigma_data):
|
| 282 |
-
"""
|
| 283 |
-
x: (B,T) Batch of trajectories
|
| 284 |
-
sr: sampling rate
|
| 285 |
-
args: args object
|
| 286 |
-
"""
|
| 287 |
-
x=x.cpu()
|
| 288 |
-
times=np.arange(x.shape[1])/sr
|
| 289 |
-
|
| 290 |
-
fig=plot_batch_of_lines(x/sigma_data, times, log_x=False)
|
| 291 |
-
return fig
|
| 292 |
-
|
| 293 |
-
def plot_trajectories_fft(x, sr, sigma_data):
|
| 294 |
-
"""
|
| 295 |
-
x: (B,T) Batch of trajectories
|
| 296 |
-
sr: sampling rate
|
| 297 |
-
args: args object
|
| 298 |
-
|
| 299 |
-
Apply fft to the trajectories and plot the magnitude of the fft
|
| 300 |
-
"""
|
| 301 |
-
x=x.cpu()
|
| 302 |
-
|
| 303 |
-
times=np.arange(x.shape[1])/sr
|
| 304 |
-
x=x/sigma_data
|
| 305 |
-
x_fft=np.fft.rfft(x, axis=1)
|
| 306 |
-
x_fft=np.abs(x_fft)
|
| 307 |
-
freqs=np.fft.rfftfreq(x.shape[1], d=1/sr)
|
| 308 |
-
#now do it on db scale
|
| 309 |
-
x_fft_db=10*np.log10(x_fft)
|
| 310 |
-
fig=plot_batch_of_lines(x_fft_db, freqs, log_x=False)
|
| 311 |
-
return fig
|
| 312 |
-
return fig
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
def plot_cpxCQT_from_raw_audio(x, args, refr=None ):
|
| 316 |
-
#shape of input spectrogram: ( ,T,F, )
|
| 317 |
-
fmax=args.sample_rate/2
|
| 318 |
-
fmin=fmax/(2**args.cqt.numocts)
|
| 319 |
-
fbins=int(args.cqt.binsoct*args.cqt.numocts)
|
| 320 |
-
device=x.device
|
| 321 |
-
CQTransform=CQT_cpx(fmin,fbins, args.sample_rate, args.audio_len, device=device, split_0_nyq=False)
|
| 322 |
-
|
| 323 |
-
x=x
|
| 324 |
-
X=CQTransform.fwd(x)
|
| 325 |
-
return plot_cpxspectrogram(X)
|
| 326 |
-
|
| 327 |
-
def plot_CQT_from_raw_audio(x, args, refr=None ):
|
| 328 |
-
#shape of input spectrogram: ( ,T,F, )
|
| 329 |
-
fmax=args.sample_rate/2
|
| 330 |
-
fmin=fmax/(2**args.cqt.numocts)
|
| 331 |
-
fbins=int(args.cqt.binsoct*args.cqt.numocts)
|
| 332 |
-
device=x.device
|
| 333 |
-
CQTransform=CQT_cpx(fmin,fbins, args.sample_rate, args.audio_len, device=device, split_0_nyq=False)
|
| 334 |
-
|
| 335 |
-
refr=3
|
| 336 |
-
x=x
|
| 337 |
-
X=CQTransform.fwd(x)
|
| 338 |
-
return plot_spectrogram(X, refr)
|
| 339 |
-
|
| 340 |
-
def get_spectrogram_from_raw_audio(x, stft, refr=1):
|
| 341 |
-
X=do_stft(x, win_size=stft.win_size, hop_size=stft.hop_size)
|
| 342 |
-
X=X.permute(0,2,3,1)
|
| 343 |
-
|
| 344 |
-
X=X.squeeze(1)
|
| 345 |
-
#X=X.cpu().numpy()
|
| 346 |
-
X=torch.sqrt(X[:,:,:,0]**2 + X[:,:,:,1]**2)
|
| 347 |
-
|
| 348 |
-
#if refr==None:
|
| 349 |
-
# refr=np.max(np.abs(X))+1e-8
|
| 350 |
-
|
| 351 |
-
S_db = 10*torch.log10(torch.abs(X)/refr)
|
| 352 |
-
|
| 353 |
-
S_db=S_db.permute(0,2,1)
|
| 354 |
-
#np.transpose(S_db, (0,2,1))
|
| 355 |
-
S_db=torch.flip(S_db, [1])
|
| 356 |
-
|
| 357 |
-
for i in range(X.shape[0]): #iterate over batch size, shity way of ploting all the batched spectrograms
|
| 358 |
-
o=S_db[i]
|
| 359 |
-
if i==0:
|
| 360 |
-
res=o
|
| 361 |
-
else:
|
| 362 |
-
res=torch.cat((res,o), 1)
|
| 363 |
-
return res
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
def diffusion_CQT_animation(path, x ,t, args, refr=1, name="animation_diffusion", resample_factor=1 ):
|
| 368 |
-
"""
|
| 369 |
-
Utility for creating an animation of the cqt diffusion process
|
| 370 |
-
"""
|
| 371 |
-
#shape of input spectrograms: (Nsteps,B,Time,Freq)
|
| 372 |
-
#print(noisy.shape)
|
| 373 |
-
Nsteps=x.shape[0]
|
| 374 |
-
numsteps=10
|
| 375 |
-
tt=torch.linspace(0, Nsteps-1, numsteps)
|
| 376 |
-
i_s=[]
|
| 377 |
-
allX=None
|
| 378 |
-
device=x.device
|
| 379 |
-
|
| 380 |
-
fmax=args.sample_rate/2
|
| 381 |
-
fmin=fmax/(2**args.cqt.numocts)
|
| 382 |
-
fbins=int(args.cqt.binsoct*args.cqt.numocts)
|
| 383 |
-
CQTransform=CQT_cpx(fmin,fbins, args.sample_rate, args.audio_len, device=device, split_0_nyq=False)
|
| 384 |
-
|
| 385 |
-
for i in tt:
|
| 386 |
-
i=int(torch.floor(i))
|
| 387 |
-
i_s.append(i)
|
| 388 |
-
xx=x[i]
|
| 389 |
-
X=CQTransform.fwd(xx)
|
| 390 |
-
|
| 391 |
-
X=torch.sqrt(X[...,0]**2 + X[...,1]**2)
|
| 392 |
-
|
| 393 |
-
S_db = 10*torch.log10(torch.abs(X)/refr)
|
| 394 |
-
S_db = S_db[:,1:-1,1:-1]
|
| 395 |
-
|
| 396 |
-
S_db=S_db.permute(0,2,1)
|
| 397 |
-
#np.transpose(S_db, (0,2,1))
|
| 398 |
-
S_db=torch.flip(S_db, [1])
|
| 399 |
-
S_db=S_db.unsqueeze(0)
|
| 400 |
-
S_db=torch.nn.functional.interpolate(S_db, size= (S_db.shape[2]//resample_factor, S_db.shape[3]//resample_factor), mode="bilinear")
|
| 401 |
-
S_db=S_db.squeeze(0)
|
| 402 |
-
S_db=S_db.cpu().numpy()
|
| 403 |
-
#S_db=S_db.squeeze(0)
|
| 404 |
-
|
| 405 |
-
if i==0:
|
| 406 |
-
allX=S_db
|
| 407 |
-
else:
|
| 408 |
-
allX=np.concatenate((allX,S_db), 0)
|
| 409 |
-
|
| 410 |
-
#fig=px.imshow(S_db, animation_frame=0, facet_col=3, binary_compression_level=0) #I need to incorporate t here!!!
|
| 411 |
-
fig=px.imshow(allX, animation_frame=0, zmin=-40, zmax=10, binary_compression_level=0) #I need to incorporate t here!!!
|
| 412 |
-
|
| 413 |
-
fig.update_layout(coloraxis_showscale=False)
|
| 414 |
-
fig.update_xaxes(showticklabels=False)
|
| 415 |
-
fig.update_yaxes(showticklabels=False)
|
| 416 |
-
|
| 417 |
-
t=t[i_s].cpu().numpy()
|
| 418 |
-
|
| 419 |
-
assert len(t)==len(fig.frames), print(len(t), len(fig.frames))
|
| 420 |
-
#print(fig)
|
| 421 |
-
for i, f in enumerate(fig.layout.sliders[0].steps):
|
| 422 |
-
#hacky way of changing the axis values
|
| 423 |
-
#f.args[0]=[str(t[i])]
|
| 424 |
-
f.label=str(t[i])
|
| 425 |
-
|
| 426 |
-
path_to_plotly_html = path+"/"+name+".html"
|
| 427 |
-
|
| 428 |
-
fig.write_html(path_to_plotly_html, auto_play = False)
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
return fig
|
| 432 |
-
|
| 433 |
-
def diffusion_joint_filter_animation(path, score, grads , f, t, refr=1, name="animation_diffusion", NT=15 ):
|
| 434 |
-
'''
|
| 435 |
-
plot an animation of the reverse diffusion process of filters
|
| 436 |
-
args:
|
| 437 |
-
path: path to save the animation
|
| 438 |
-
x: filters (Nsteps, F)
|
| 439 |
-
f: frequencies (F)
|
| 440 |
-
t: timesteps (sigma)
|
| 441 |
-
name: name of the animation
|
| 442 |
-
'''
|
| 443 |
-
#shape of input spectrograms: (Nsteps,B,Time,Freq)
|
| 444 |
-
#print(noisy.shape)
|
| 445 |
-
Nsteps=score.shape[0]
|
| 446 |
-
#numsteps=min(Nsteps,NT) #hardcoded, I'll probably need more!
|
| 447 |
-
|
| 448 |
-
#tt=torch.linspace(0, Nsteps-1, numsteps)
|
| 449 |
-
#i_s=[]
|
| 450 |
-
#for i in tt:
|
| 451 |
-
# i=int(torch.floor(i))
|
| 452 |
-
# i_s.append(i)
|
| 453 |
-
|
| 454 |
-
print(f.shape, score.shape, grads.shape, t.shape) #19, (100,19)
|
| 455 |
-
#fig=px.line(x=f.unsqueeze(0).numpy(),y=x, animation_frame=0) #I need to incorporate t here!!!
|
| 456 |
-
sigma=t
|
| 457 |
-
s=score.squeeze(1)# (100,19)
|
| 458 |
-
g=grads.squeeze(1)# (100,19)
|
| 459 |
-
f=f# (19,)
|
| 460 |
-
f=f.unsqueeze(0).expand(s.shape[0], -1).reshape(-1)
|
| 461 |
-
sigma=sigma.unsqueeze(-1).expand(-1, s.shape[1]).reshape(-1)
|
| 462 |
-
s=s.reshape(-1)
|
| 463 |
-
g=g.reshape(-1)
|
| 464 |
-
max_score=torch.max(s).item()
|
| 465 |
-
df=pd.DataFrame(
|
| 466 |
-
{
|
| 467 |
-
"f": f.cpu().numpy(),
|
| 468 |
-
"score": s.cpu().numpy(),
|
| 469 |
-
"grads": g.cpu().numpy(),
|
| 470 |
-
"sigma": sigma.cpu().numpy()
|
| 471 |
-
}
|
| 472 |
-
)
|
| 473 |
-
fig=px.line(df, x="f",y=["score","grads"], animation_frame="sigma", log_x=True) #I need to incorporate t here!!!
|
| 474 |
-
#set the range of th y axis
|
| 475 |
-
fig.update_yaxes(range=[-max_score,max_score])
|
| 476 |
-
#t=t[i_s].cpu().numpy()
|
| 477 |
-
#t=t.cpu().numpy()
|
| 478 |
-
|
| 479 |
-
#assert len(t)==len(fig.frames), print(len(t), len(fig.frames))
|
| 480 |
-
#print(fig)
|
| 481 |
-
#for i, f in enumerate(fig.layout.sliders[0].steps):
|
| 482 |
-
#hacky way of changing the axis values
|
| 483 |
-
#f.args[0]=[str(t[i])]
|
| 484 |
-
# f.label=str(t[i])
|
| 485 |
-
|
| 486 |
-
path_to_plotly_html = path+"/"+name+".html"
|
| 487 |
-
|
| 488 |
-
fig.write_html(path_to_plotly_html, auto_play = False)
|
| 489 |
-
|
| 490 |
-
return fig
|
| 491 |
-
|
| 492 |
-
def diffusion_filter_animation(path, x , f, t, refr=1, name="animation_diffusion", NT=15 ):
|
| 493 |
-
'''
|
| 494 |
-
plot an animation of the reverse diffusion process of filters
|
| 495 |
-
args:
|
| 496 |
-
path: path to save the animation
|
| 497 |
-
x: filters (Nsteps, F)
|
| 498 |
-
f: frequencies (F)
|
| 499 |
-
t: timesteps (sigma)
|
| 500 |
-
name: name of the animation
|
| 501 |
-
'''
|
| 502 |
-
#shape of input spectrograms: (Nsteps,B,Time,Freq)
|
| 503 |
-
#print(noisy.shape)
|
| 504 |
-
Nsteps=x.shape[0]
|
| 505 |
-
#numsteps=min(Nsteps,NT) #hardcoded, I'll probably need more!
|
| 506 |
-
|
| 507 |
-
#tt=torch.linspace(0, Nsteps-1, numsteps)
|
| 508 |
-
#i_s=[]
|
| 509 |
-
#for i in tt:
|
| 510 |
-
# i=int(torch.floor(i))
|
| 511 |
-
# i_s.append(i)
|
| 512 |
-
|
| 513 |
-
print(f.shape, x.shape, t.shape) #19, (100,19)
|
| 514 |
-
#fig=px.line(x=f.unsqueeze(0).numpy(),y=x, animation_frame=0) #I need to incorporate t here!!!
|
| 515 |
-
sigma=t
|
| 516 |
-
x=x.squeeze(1)# (100,19)
|
| 517 |
-
f=f# (19,)
|
| 518 |
-
f=f.unsqueeze(0).expand(x.shape[0], -1).reshape(-1)
|
| 519 |
-
sigma=sigma.unsqueeze(-1).expand(-1, x.shape[1]).reshape(-1)
|
| 520 |
-
x=x.reshape(-1)
|
| 521 |
-
df=pd.DataFrame(
|
| 522 |
-
{
|
| 523 |
-
"f": f.cpu().numpy(),
|
| 524 |
-
"x": x.cpu().numpy(),
|
| 525 |
-
"sigma": sigma.cpu().numpy()
|
| 526 |
-
}
|
| 527 |
-
)
|
| 528 |
-
fig=px.line(df, x="f",y="x", animation_frame="sigma", log_x=True) #I need to incorporate t here!!!
|
| 529 |
-
#t=t[i_s].cpu().numpy()
|
| 530 |
-
#t=t.cpu().numpy()
|
| 531 |
-
|
| 532 |
-
#assert len(t)==len(fig.frames), print(len(t), len(fig.frames))
|
| 533 |
-
#print(fig)
|
| 534 |
-
#for i, f in enumerate(fig.layout.sliders[0].steps):
|
| 535 |
-
#hacky way of changing the axis values
|
| 536 |
-
#f.args[0]=[str(t[i])]
|
| 537 |
-
# f.label=str(t[i])
|
| 538 |
-
|
| 539 |
-
path_to_plotly_html = path+"/"+name+".html"
|
| 540 |
-
|
| 541 |
-
fig.write_html(path_to_plotly_html, auto_play = False)
|
| 542 |
-
|
| 543 |
-
return fig
|
| 544 |
-
|
| 545 |
-
def diffusion_spec_animation(path, x ,t, stft, refr=1, name="animation_diffusion",NT=15 ):
|
| 546 |
-
'''
|
| 547 |
-
plot an animation of the reverse diffusion process of filters
|
| 548 |
-
args:
|
| 549 |
-
path: path to save the animation
|
| 550 |
-
x: input audio (N,T)
|
| 551 |
-
t: timesteps (sigma)
|
| 552 |
-
name: name of the animation
|
| 553 |
-
'''
|
| 554 |
-
#print(noisy.shape)
|
| 555 |
-
Nsteps=x.shape[0]
|
| 556 |
-
numsteps=min(Nsteps,NT) #hardcoded, I'll probably need more!
|
| 557 |
-
tt=torch.linspace(0, Nsteps-1, numsteps)
|
| 558 |
-
i_s=[]
|
| 559 |
-
allX=None
|
| 560 |
-
for i in tt:
|
| 561 |
-
i=int(torch.floor(i))
|
| 562 |
-
i_s.append(i)
|
| 563 |
-
X=get_spectrogram_from_raw_audio(x[i],stft, refr)
|
| 564 |
-
X=X.unsqueeze(0)
|
| 565 |
-
if allX==None:
|
| 566 |
-
allX=X
|
| 567 |
-
else:
|
| 568 |
-
allX=torch.cat((allX,X), 0)
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
allX=allX.cpu().numpy()
|
| 572 |
-
fig=px.imshow(allX, animation_frame=0, zmin=-40, zmax=20) #I need to incorporate t here!!!
|
| 573 |
-
|
| 574 |
-
fig.update_layout(coloraxis_showscale=False)
|
| 575 |
-
|
| 576 |
-
t=t[i_s].cpu().numpy()
|
| 577 |
-
|
| 578 |
-
assert len(t)==len(fig.frames), print(len(t), len(fig.frames))
|
| 579 |
-
#print(fig)
|
| 580 |
-
for i, f in enumerate(fig.layout.sliders[0].steps):
|
| 581 |
-
#hacky way of changing the axis values
|
| 582 |
-
#f.args[0]=[str(t[i])]
|
| 583 |
-
f.label=str(t[i])
|
| 584 |
-
|
| 585 |
-
path_to_plotly_html = path+"/"+name+".html"
|
| 586 |
-
|
| 587 |
-
fig.write_html(path_to_plotly_html, auto_play = False)
|
| 588 |
-
|
| 589 |
-
|
| 590 |
-
return fig
|
| 591 |
-
|
| 592 |
-
def plot_spectrogram_from_raw_audio(x, stft, refr=None ):
|
| 593 |
-
#shape of input spectrogram: ( ,T,F, )
|
| 594 |
-
refr=3
|
| 595 |
-
x=x
|
| 596 |
-
X=do_stft(x, win_size=stft.win_size, hop_size=stft.hop_size)
|
| 597 |
-
X=X.permute(0,2,3,1)
|
| 598 |
-
return plot_spectrogram(X, refr)
|
| 599 |
-
|
| 600 |
-
def plot_spectrogram_from_cpxspec(X, refr=None):
|
| 601 |
-
#shape of input spectrogram: ( ,T,F, )
|
| 602 |
-
return plot_spectrogram(X, refr)
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
def error_line(error_y_mode='band', **kwargs):
|
| 606 |
-
print("hello error line", error_y_mode)
|
| 607 |
-
"""Extension of `plotly.express.line` to use error bands."""
|
| 608 |
-
ERROR_MODES = {'bar','band','bars','bands',None}
|
| 609 |
-
if error_y_mode not in ERROR_MODES:
|
| 610 |
-
raise ValueError(f"'error_y_mode' must be one of {ERROR_MODES}, received {repr(error_y_mode)}.")
|
| 611 |
-
if error_y_mode in {'bar','bars',None}:
|
| 612 |
-
print("Using error bars", kwargs)
|
| 613 |
-
fig = px.line(**kwargs)
|
| 614 |
-
elif error_y_mode in {'band','bands'}:
|
| 615 |
-
if 'error_y' not in kwargs:
|
| 616 |
-
raise ValueError(f"If you provide argument 'error_y_mode' you must also provide 'error_y'.")
|
| 617 |
-
figure_with_error_bars = px.line(**kwargs)
|
| 618 |
-
fig = px.line(**{arg: val for arg,val in kwargs.items() if arg != 'error_y'})
|
| 619 |
-
for data in figure_with_error_bars.data:
|
| 620 |
-
x = list(data['x'])
|
| 621 |
-
y_upper = list(data['y'] + data['error_y']['array'])
|
| 622 |
-
y_lower = list(data['y'] - data['error_y']['array'] if data['error_y']['arrayminus'] is None else data['y'] - data['error_y']['arrayminus'])
|
| 623 |
-
color = f"rgba({tuple(int(data['line']['color'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4))},.3)".replace('((','(').replace('),',',').replace(' ','')
|
| 624 |
-
fig.add_trace(
|
| 625 |
-
go.Scatter(
|
| 626 |
-
x = x+x[::-1],
|
| 627 |
-
y = y_upper+y_lower[::-1],
|
| 628 |
-
fill = 'toself',
|
| 629 |
-
fillcolor = color,
|
| 630 |
-
line = dict(
|
| 631 |
-
color = 'rgba(255,255,255,0)'
|
| 632 |
-
),
|
| 633 |
-
hoverinfo = "skip",
|
| 634 |
-
showlegend = False,
|
| 635 |
-
legendgroup = data['legendgroup'],
|
| 636 |
-
xaxis = data['xaxis'],
|
| 637 |
-
yaxis = data['yaxis'],
|
| 638 |
-
)
|
| 639 |
-
)
|
| 640 |
-
# Reorder data as said here: https://stackoverflow.com/a/66854398/8849755
|
| 641 |
-
reordered_data = []
|
| 642 |
-
for i in range(int(len(fig.data)/2)):
|
| 643 |
-
reordered_data.append(fig.data[i+int(len(fig.data)/2)])
|
| 644 |
-
reordered_data.append(fig.data[i])
|
| 645 |
-
fig.data = tuple(reordered_data)
|
| 646 |
-
return fig
|
| 647 |
-
|
| 648 |
-
|
| 649 |
-
def plot_filters( x, freqs):
|
| 650 |
-
'''
|
| 651 |
-
This function plots a batch of lines using plotly
|
| 652 |
-
args:
|
| 653 |
-
x: (B, F)
|
| 654 |
-
'''
|
| 655 |
-
fig = px.line(x=freqs, y=x[0,:], log_x=True)
|
| 656 |
-
|
| 657 |
-
for i in range(1,x.shape[0]):
|
| 658 |
-
fig.add_trace(go.Scatter(x=freqs, y=x[i,:]))
|
| 659 |
-
return fig
|
| 660 |
-
|
| 661 |
-
def plot_batch_of_lines( x, freqs, log_x=True):
|
| 662 |
-
'''
|
| 663 |
-
This function plots a batch of lines using plotly
|
| 664 |
-
args:
|
| 665 |
-
x: (B, F)
|
| 666 |
-
'''
|
| 667 |
-
fig = px.line(x=freqs, y=x[0,:], log_x=log_x)
|
| 668 |
-
|
| 669 |
-
for i in range(1,x.shape[0]):
|
| 670 |
-
fig.add_trace(go.Scatter(x=freqs, y=x[i,:]))
|
| 671 |
-
return fig
|
| 672 |
-
|
| 673 |
-
|
| 674 |
-
|
| 675 |
-
|
| 676 |
-
def plot_rir(rir, T_separation, fs, path=None, name=None, save=True):
|
| 677 |
-
'''
|
| 678 |
-
Plot the RIR and indicate the separation time between early and late reflections
|
| 679 |
-
'''
|
| 680 |
-
rir = rir.cpu()
|
| 681 |
-
RIR = torch.stft(rir, n_fft=512, hop_length=128, window=torch.hann_window(512).to(rir.device), return_complex=True)
|
| 682 |
-
|
| 683 |
-
fig, ax = plt.subplots(1, 2)
|
| 684 |
-
|
| 685 |
-
ax[0].pcolormesh(
|
| 686 |
-
torch.linspace(0, RIR.size(-1)/128, RIR.size(-1)),
|
| 687 |
-
torch.linspace(0, fs/2, RIR.size(-2)),
|
| 688 |
-
20*torch.log10(RIR.abs() + 1e-10))
|
| 689 |
-
|
| 690 |
-
ax[1].plot(
|
| 691 |
-
torch.linspace(0, rir.size(-1)/fs, rir.size(-1)),
|
| 692 |
-
rir)
|
| 693 |
-
|
| 694 |
-
if save:
|
| 695 |
-
plt.savefig(os.path.join(path, name+".png"))
|
| 696 |
-
else:
|
| 697 |
-
return fig
|
| 698 |
-
|
| 699 |
-
|
| 700 |
-
def create_mag_phase_plot():
|
| 701 |
-
# Create subplots
|
| 702 |
-
from plotly.subplots import make_subplots
|
| 703 |
-
|
| 704 |
-
# fig = make_subplots(rows=2, cols=1, vertical_spacing=0.1,
|
| 705 |
-
# subplot_titles=('Magnitude Response', 'Phase Response'))
|
| 706 |
-
fig = make_subplots(specs=[[{"secondary_y": True}]])
|
| 707 |
-
# Update layout
|
| 708 |
-
fig.update_layout(height=600, width=800, title_text="Magnitude and Phase of Filter")
|
| 709 |
-
# fig.update_xaxes(type='log',title_text="Frequency [Hz])", row=2, col=1)
|
| 710 |
-
fig.update_xaxes(type='log', title_text="Frequency [Hz])")
|
| 711 |
-
# fig.update_yaxes(title_text="Magnitude [dB]", row=1, col=1)
|
| 712 |
-
# fig.update_yaxes(title_text="Phase [rad]", row=2, col=1)
|
| 713 |
-
# Set y-axes titles
|
| 714 |
-
fig.update_yaxes(title_text="<b>Magnitude [dB]</b>", secondary_y=False)
|
| 715 |
-
fig.update_yaxes(title_text="<b>Phase [rad]</b>", secondary_y=True)
|
| 716 |
-
return fig
|
| 717 |
-
|
| 718 |
-
|
| 719 |
-
def plot_STFT(x, operator, type='mag_dB', min_freq=100):
|
| 720 |
-
|
| 721 |
-
# Ensure that shape is (B, F, T)
|
| 722 |
-
fs = operator.sample_rate
|
| 723 |
-
nfft = operator.n_fft
|
| 724 |
-
hop_size = operator.hop_length
|
| 725 |
-
|
| 726 |
-
print(x.device, operator.device)
|
| 727 |
-
X = operator.apply_stft(x.to(operator.device))
|
| 728 |
-
|
| 729 |
-
print(X.shape, x.shape, X.device)
|
| 730 |
-
|
| 731 |
-
# , X.device)
|
| 732 |
-
if type == 'mag':
|
| 733 |
-
x_spec = X.abs()
|
| 734 |
-
if type == 'mag_dB':
|
| 735 |
-
x_spec = 20*torch.log10(X.abs()+1e-7)
|
| 736 |
-
if type == 'phase':
|
| 737 |
-
x_spec = X.angle()
|
| 738 |
-
# if type == 'phase_unwrap':
|
| 739 |
-
# x_spec = X.angle().grad()
|
| 740 |
-
|
| 741 |
-
|
| 742 |
-
if "mag" in type:
|
| 743 |
-
z_min=-100
|
| 744 |
-
z_max= 10
|
| 745 |
-
else:
|
| 746 |
-
z_min=-np.pi
|
| 747 |
-
z_max= np.pi
|
| 748 |
-
|
| 749 |
-
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
frequencies = torch.fft.rfftfreq(nfft) * fs
|
| 753 |
-
timestamps = torch.arange(X.shape[-1]) * (hop_size / fs)
|
| 754 |
-
fig = go.Figure(data=go.Heatmap(
|
| 755 |
-
z=x_spec.squeeze(0).cpu().numpy(),
|
| 756 |
-
x=timestamps.numpy(),
|
| 757 |
-
y=frequencies.numpy(),
|
| 758 |
-
colorscale='Magma',
|
| 759 |
-
zmax=z_max,
|
| 760 |
-
zmin=z_min
|
| 761 |
-
))
|
| 762 |
-
|
| 763 |
-
fig.update_layout(
|
| 764 |
-
yaxis=dict(
|
| 765 |
-
type='log',
|
| 766 |
-
title='Frequency(Hz)',
|
| 767 |
-
range= [np.log10(min_freq), np.log10(frequencies[-1].item())],
|
| 768 |
-
),
|
| 769 |
-
xaxis_title='Time(s)',
|
| 770 |
-
title='Magnitude'
|
| 771 |
-
)
|
| 772 |
-
|
| 773 |
-
#define min and max for frequency axis
|
| 774 |
-
|
| 775 |
-
return fig
|
| 776 |
-
|
| 777 |
-
def lineplot(x=None, dict=None, xaxis="linear", yaxis="log"):
|
| 778 |
-
assert dict is not None
|
| 779 |
-
|
| 780 |
-
y=dict.items()
|
| 781 |
-
y=torch.stack([v for k,v in y])
|
| 782 |
-
|
| 783 |
-
labels = [k for k,v in dict.items()]
|
| 784 |
-
|
| 785 |
-
|
| 786 |
-
|
| 787 |
-
# Ensure y is a NumPy array for compatibility with Plotly
|
| 788 |
-
if isinstance(y, torch.Tensor):
|
| 789 |
-
y = y.detach().cpu().numpy()
|
| 790 |
-
|
| 791 |
-
if x is None:
|
| 792 |
-
x = np.arange(y.shape[-1]) # Ensure x is NumPy array too
|
| 793 |
-
elif isinstance(x, torch.Tensor):
|
| 794 |
-
x = x.detach().cpu().numpy()
|
| 795 |
-
|
| 796 |
-
# Validate labels
|
| 797 |
-
num_series = y.shape[0]
|
| 798 |
-
if labels is None:
|
| 799 |
-
labels = [f"Series {i}" for i in range(num_series)]
|
| 800 |
-
elif len(labels) != num_series:
|
| 801 |
-
raise ValueError(f"Expected {num_series} labels, but got {len(labels)}.")
|
| 802 |
-
|
| 803 |
-
# Convert to DataFrame for Plotly
|
| 804 |
-
df = pd.DataFrame(y.T, index=x, columns=labels) # Use labels as column names
|
| 805 |
-
df.index.name = "x"
|
| 806 |
-
df = df.reset_index()
|
| 807 |
-
|
| 808 |
-
# Melt DataFrame for Plotly (long format)
|
| 809 |
-
df_melted = df.melt(id_vars=["x"], var_name="series", value_name="y")
|
| 810 |
-
|
| 811 |
-
fig = px.line(df_melted, x="x", y="y", color="series", labels={"y": "Value", "x": "t", "series": "Legend"})
|
| 812 |
-
|
| 813 |
-
# Set y-axis to log scale
|
| 814 |
-
if yaxis=="log":
|
| 815 |
-
fig.update_yaxes(type="log")
|
| 816 |
-
#fix range from 1e-4 to 10
|
| 817 |
-
fig.update_yaxes(range=[-4, 1])
|
| 818 |
-
if xaxis=="log":
|
| 819 |
-
fig.update_xaxes(type="log")
|
| 820 |
-
|
| 821 |
-
|
| 822 |
-
return fig
|
| 823 |
-
|
| 824 |
-
def make_PCA_figure(data_dict, num_bins=20, title="PCA"):
|
| 825 |
-
|
| 826 |
-
fig = plt.figure(figsize=(6, 5), dpi=200)
|
| 827 |
-
gs = fig.add_gridspec(
|
| 828 |
-
2,
|
| 829 |
-
2,
|
| 830 |
-
width_ratios=(4, 1),
|
| 831 |
-
height_ratios=(1, 4),
|
| 832 |
-
left=0.1,
|
| 833 |
-
right=0.9,
|
| 834 |
-
bottom=0.1,
|
| 835 |
-
top=0.9,
|
| 836 |
-
)
|
| 837 |
-
|
| 838 |
-
# Create the Axes.
|
| 839 |
-
ax = fig.add_subplot(gs[1, 0])
|
| 840 |
-
ax_histx = fig.add_subplot(gs[0, 0], sharex=ax)
|
| 841 |
-
ax_histy = fig.add_subplot(gs[1, 1], sharey=ax)
|
| 842 |
-
# Draw the scatter plot and marginals.
|
| 843 |
-
# no labels
|
| 844 |
-
ax_histx.tick_params(axis="x", labelbottom=False)
|
| 845 |
-
ax_histy.tick_params(axis="y", labelleft=False)
|
| 846 |
-
|
| 847 |
-
xymax=0
|
| 848 |
-
xymin=999999
|
| 849 |
-
markers= ["o", "x", "D", "^", "v", "x", "p", "*", "h", "+", "s"]
|
| 850 |
-
colors=["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b"]
|
| 851 |
-
|
| 852 |
-
|
| 853 |
-
for i,(k, v) in enumerate(data_dict.items()):
|
| 854 |
-
if v is None or len(v) == 0:
|
| 855 |
-
continue # Skip empty or None values
|
| 856 |
-
|
| 857 |
-
# the scatter plot:
|
| 858 |
-
ax.scatter(
|
| 859 |
-
v[:, 0],
|
| 860 |
-
v[:, 1],
|
| 861 |
-
marker=markers[i % len(markers)],
|
| 862 |
-
c=colors[i % len(colors)],
|
| 863 |
-
linewidths=0.8,
|
| 864 |
-
s=24,
|
| 865 |
-
label=k,
|
| 866 |
-
alpha=0.7,
|
| 867 |
-
)
|
| 868 |
-
|
| 869 |
-
try:
|
| 870 |
-
xymax=max(xymax, np.max(v[:, :2]))
|
| 871 |
-
xymin=min(xymin, np.min(v[:, :2]))
|
| 872 |
-
except Exception as e:
|
| 873 |
-
print(f"Error processing data for {k}: {e}")
|
| 874 |
-
print("data:",v)
|
| 875 |
-
print(f"Data shape: {v.shape}")
|
| 876 |
-
continue
|
| 877 |
-
|
| 878 |
-
ax.legend(loc="upper left")
|
| 879 |
-
ax.set_xlabel("PC 1")
|
| 880 |
-
ax.set_ylabel("PC 2")
|
| 881 |
-
|
| 882 |
-
# now determine nice limits by hand:
|
| 883 |
-
data_range = xymax - xymin
|
| 884 |
-
binwidth = data_range / num_bins # Calculate bin width based on the range and number of bins
|
| 885 |
-
|
| 886 |
-
lim = (int(xymax / binwidth) + 1) * binwidth
|
| 887 |
-
bins = np.arange(-lim, lim + binwidth, binwidth)
|
| 888 |
-
|
| 889 |
-
style = { "edgecolor": "black", "linewidth": 0.8, "alpha": 0.5 }
|
| 890 |
-
|
| 891 |
-
|
| 892 |
-
|
| 893 |
-
#linestyles=[":", "-", "--", "-.", ":", "-"]
|
| 894 |
-
facecolors=["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b"]
|
| 895 |
-
hatches = ['/', '\\', '.', '+', '*', 'o', 'O', '.', '-', '|', 'x']
|
| 896 |
-
|
| 897 |
-
for i, (k, v) in enumerate(data_dict.items()):
|
| 898 |
-
|
| 899 |
-
hist_style = style.copy()
|
| 900 |
-
hist_style["hatch"] = hatches[i % len(hatches)]
|
| 901 |
-
|
| 902 |
-
ax_histx.hist(v[:, 0], bins=bins, density=False, label=k, facecolor=facecolors[i], **hist_style)
|
| 903 |
-
|
| 904 |
-
ax_histy.hist(
|
| 905 |
-
v[:, 1],
|
| 906 |
-
bins=bins,
|
| 907 |
-
orientation="horizontal",
|
| 908 |
-
density=False,
|
| 909 |
-
facecolor=facecolors[i],
|
| 910 |
-
**hist_style
|
| 911 |
-
)
|
| 912 |
-
|
| 913 |
-
ax_histx.set_ylabel("Count")
|
| 914 |
-
ax_histy.set_xlabel("Count")
|
| 915 |
-
|
| 916 |
-
#ax_histx.legend(loc="upper right")
|
| 917 |
-
|
| 918 |
-
#add title
|
| 919 |
-
fig.suptitle(title, fontsize=16, fontweight='bold')
|
| 920 |
-
|
| 921 |
-
return fig
|
| 922 |
-
|
| 923 |
-
def make_histogram_figure(data_dict, num_bins=20):
|
| 924 |
-
|
| 925 |
-
fig = plt.figure(figsize=(5, 3), dpi=100)
|
| 926 |
-
|
| 927 |
-
# Create the Axes.
|
| 928 |
-
#ax = fig.add_subplot(gs[1, 0])
|
| 929 |
-
#ax_histx = fig.add_subplot(gs[0, 0], sharex=ax)
|
| 930 |
-
#ax_histy = fig.add_subplot(gs[1, 1], sharey=ax)
|
| 931 |
-
# Draw the scatter plot and marginals.
|
| 932 |
-
# no labels
|
| 933 |
-
|
| 934 |
-
#get ax from the figure
|
| 935 |
-
ax = fig.add_subplot(111)
|
| 936 |
-
|
| 937 |
-
ax.tick_params(axis="x", labelbottom=True)
|
| 938 |
-
|
| 939 |
-
|
| 940 |
-
xymax=0
|
| 941 |
-
xymin=999999
|
| 942 |
-
|
| 943 |
-
for i,(k, v) in enumerate(data_dict.items()):
|
| 944 |
-
|
| 945 |
-
xymax=max(xymax, np.max(v))
|
| 946 |
-
xymin=min(xymin, np.min(v))
|
| 947 |
-
|
| 948 |
-
|
| 949 |
-
data_range = xymax - xymin
|
| 950 |
-
binwidth = data_range / num_bins # Calculate bin width based on the range and number of bins
|
| 951 |
-
|
| 952 |
-
lim_up = (int(xymax / binwidth) + 1) * binwidth+binwidth
|
| 953 |
-
lim_down= (int(xymin / binwidth) - 1) * binwidth-binwidth
|
| 954 |
-
|
| 955 |
-
bins = np.arange(lim_down, lim_up, binwidth)
|
| 956 |
-
|
| 957 |
-
style = { "edgecolor": "black", "linewidth": 0.8, "alpha": 0.5 }
|
| 958 |
-
|
| 959 |
-
#linestyles=[":", "-", "--", "-.", ":", "-"]
|
| 960 |
-
facecolors=["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b"]
|
| 961 |
-
hatches = ['/', '\\', '.', '+', '*', 'o', 'O', '.', '-', '|', 'x']
|
| 962 |
-
|
| 963 |
-
for i, (k, v) in enumerate(data_dict.items()):
|
| 964 |
-
|
| 965 |
-
hist_style = style.copy()
|
| 966 |
-
hist_style["hatch"] = hatches[i % len(hatches)]
|
| 967 |
-
|
| 968 |
-
ax.hist(v[:], bins=bins, density=False, label=k, facecolor=facecolors[i], **hist_style)
|
| 969 |
-
|
| 970 |
-
ax.set_ylabel("Count")
|
| 971 |
-
|
| 972 |
-
ax.legend(loc="upper right")
|
| 973 |
-
|
| 974 |
-
|
| 975 |
-
return fig
|
| 976 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/st_ito/__init__.py
DELETED
|
File without changes
|
utils/st_ito/callbacks.py
DELETED
|
@@ -1,164 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import wandb
|
| 3 |
-
import torch
|
| 4 |
-
import shutil
|
| 5 |
-
import numpy as np
|
| 6 |
-
import pytorch_lightning as pl
|
| 7 |
-
import matplotlib.pyplot as plt
|
| 8 |
-
|
| 9 |
-
from typing import Any
|
| 10 |
-
from sklearn.metrics import confusion_matrix
|
| 11 |
-
from pytorch_lightning.utilities.types import STEP_OUTPUT
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
# trainer.logger.experiment.dir: /import/c4dm-datasets-ext/wandb/run-20230826_192342-4zjz1inp/files
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
class LogAudioCallback(pl.Callback):
|
| 18 |
-
def __init__(self) -> None:
|
| 19 |
-
super().__init__()
|
| 20 |
-
|
| 21 |
-
def on_validation_batch_end(
|
| 22 |
-
self,
|
| 23 |
-
trainer: pl.Trainer,
|
| 24 |
-
pl_module: pl.LightningModule,
|
| 25 |
-
outputs: STEP_OUTPUT,
|
| 26 |
-
batch: Any,
|
| 27 |
-
batch_idx: int,
|
| 28 |
-
dataloader_idx: int = 0,
|
| 29 |
-
) -> None:
|
| 30 |
-
# Only log audio for the first batch
|
| 31 |
-
if batch_idx > 0:
|
| 32 |
-
return
|
| 33 |
-
# Log audio to wandb
|
| 34 |
-
else:
|
| 35 |
-
data_dict = outputs
|
| 36 |
-
silence_gap = 48000
|
| 37 |
-
|
| 38 |
-
# check the batch size
|
| 39 |
-
batch_size = data_dict["input_audio_A"].shape[0]
|
| 40 |
-
|
| 41 |
-
for bidx in range(min(4, batch_size)):
|
| 42 |
-
audio_tensors = []
|
| 43 |
-
for key in ["input_audio_A", "target_audio_A", "output_audio_A"]:
|
| 44 |
-
audio_segment = data_dict[key][bidx, ...]
|
| 45 |
-
chs, seq_len = audio_segment.shape
|
| 46 |
-
|
| 47 |
-
if chs == 1: # make it stereo
|
| 48 |
-
audio_segment = audio_segment.repeat(2, 1)
|
| 49 |
-
|
| 50 |
-
audio_tensors.append(audio_segment)
|
| 51 |
-
|
| 52 |
-
# check the length of the audio tensors
|
| 53 |
-
total_audio_length = sum(
|
| 54 |
-
[audio_tensor.shape[-1] for audio_tensor in audio_tensors]
|
| 55 |
-
)
|
| 56 |
-
|
| 57 |
-
# create a single tensor to hold the audio
|
| 58 |
-
output_tensor = torch.zeros((total_audio_length + 2 * silence_gap, 2))
|
| 59 |
-
|
| 60 |
-
# copy the audio tensors into the single tensor
|
| 61 |
-
start_idx = 0
|
| 62 |
-
for audio_tensor in audio_tensors:
|
| 63 |
-
end_idx = start_idx + audio_tensor.shape[-1]
|
| 64 |
-
output_tensor[start_idx:end_idx, :] = audio_tensor.permute(1, 0)
|
| 65 |
-
start_idx = end_idx + silence_gap
|
| 66 |
-
|
| 67 |
-
trainer.logger.experiment.log(
|
| 68 |
-
{
|
| 69 |
-
f"{bidx}-audio": wandb.Audio(
|
| 70 |
-
output_tensor.cpu().numpy(), sample_rate=48000
|
| 71 |
-
),
|
| 72 |
-
}
|
| 73 |
-
)
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
class MoveConfigCallback(pl.Callback):
|
| 77 |
-
def __init__(self, project_name: str) -> None:
|
| 78 |
-
super().__init__()
|
| 79 |
-
self.project_name = project_name
|
| 80 |
-
|
| 81 |
-
def on_fit_start(self, trainer, pl_module):
|
| 82 |
-
full_run_dir = trainer.logger.experiment.dir
|
| 83 |
-
run_id = full_run_dir.split(os.sep)[-2].split("-")[-1]
|
| 84 |
-
src_dir = os.path.join(trainer.log_dir, "config.yaml")
|
| 85 |
-
dest_dir = os.path.join(
|
| 86 |
-
trainer.log_dir, self.project_name, run_id, "checkpoints", "config.yaml"
|
| 87 |
-
)
|
| 88 |
-
|
| 89 |
-
run_dir = os.path.join(trainer.log_dir, self.project_name, run_id)
|
| 90 |
-
ckpt_dir = os.path.join(run_dir, "checkpoints")
|
| 91 |
-
os.makedirs(run_dir, exist_ok=True)
|
| 92 |
-
os.makedirs(ckpt_dir, exist_ok=True)
|
| 93 |
-
|
| 94 |
-
shutil.copyfile(src_dir, dest_dir)
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
class ConfusionMatrixCallback(pl.Callback):
|
| 98 |
-
def __init__(self) -> None:
|
| 99 |
-
super().__init__()
|
| 100 |
-
self.outputs = []
|
| 101 |
-
|
| 102 |
-
def on_validation_batch_end(
|
| 103 |
-
self,
|
| 104 |
-
trainer: pl.Trainer,
|
| 105 |
-
pl_module: pl.LightningModule,
|
| 106 |
-
outputs: STEP_OUTPUT,
|
| 107 |
-
batch: Any,
|
| 108 |
-
batch_idx: int,
|
| 109 |
-
dataloader_idx: int = 0,
|
| 110 |
-
) -> None:
|
| 111 |
-
logits, labels = outputs
|
| 112 |
-
self.outputs.append(outputs)
|
| 113 |
-
|
| 114 |
-
def on_validation_epoch_end(
|
| 115 |
-
self, trainer: pl.Trainer, pl_module: pl.LightningModule
|
| 116 |
-
) -> None:
|
| 117 |
-
labels = list(trainer.datamodule.val_dataset.plugins.keys())
|
| 118 |
-
|
| 119 |
-
# Concatenate logits and labels from all batches in the validation set
|
| 120 |
-
all_logits = torch.cat([output[0] for output in self.outputs], dim=0)
|
| 121 |
-
all_labels = torch.cat([output[1] for output in self.outputs], dim=0)
|
| 122 |
-
|
| 123 |
-
# Convert logits to predicted labels
|
| 124 |
-
predicted_labels = torch.argmax(all_logits, dim=-1)
|
| 125 |
-
|
| 126 |
-
# Compute confusion matrix
|
| 127 |
-
cm = confusion_matrix(all_labels.cpu().numpy(), predicted_labels.cpu().numpy())
|
| 128 |
-
|
| 129 |
-
# Plot confusion matrix using matplotlib
|
| 130 |
-
num_classes = cm.shape[0]
|
| 131 |
-
class_labels = [str(i) for i in labels]
|
| 132 |
-
|
| 133 |
-
if num_classes != len(class_labels):
|
| 134 |
-
print(
|
| 135 |
-
f"Number of classes ({num_classes}) does not match number of class labels ({len(class_labels)}). Skipping."
|
| 136 |
-
)
|
| 137 |
-
return
|
| 138 |
-
|
| 139 |
-
fig, ax = plt.subplots(figsize=(num_classes // 2, num_classes // 2))
|
| 140 |
-
plt.imshow(cm, interpolation="nearest", cmap=plt.cm.Blues)
|
| 141 |
-
plt.title("Confusion Matrix")
|
| 142 |
-
plt.colorbar()
|
| 143 |
-
|
| 144 |
-
tick_marks = np.arange(num_classes)
|
| 145 |
-
plt.xticks(tick_marks, class_labels, rotation=90)
|
| 146 |
-
ax.xaxis.set_tick_params(top=True)
|
| 147 |
-
plt.yticks(tick_marks, class_labels)
|
| 148 |
-
|
| 149 |
-
plt.xlabel("Predicted")
|
| 150 |
-
plt.ylabel("True")
|
| 151 |
-
plt.tight_layout()
|
| 152 |
-
|
| 153 |
-
for i in range(num_classes):
|
| 154 |
-
for j in range(num_classes):
|
| 155 |
-
plt.text(
|
| 156 |
-
j, i, str(cm[i, j]), horizontalalignment="center", color="black"
|
| 157 |
-
)
|
| 158 |
-
|
| 159 |
-
# log confusion matrix to wandb
|
| 160 |
-
trainer.logger.experiment.log({"confusion_matrix": plt})
|
| 161 |
-
plt.close("all")
|
| 162 |
-
|
| 163 |
-
# reset outputs
|
| 164 |
-
self.outputs = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/st_ito/config.yaml
DELETED
|
@@ -1,151 +0,0 @@
|
|
| 1 |
-
# pytorch_lightning==2.1.2
|
| 2 |
-
seed_everything: 4000152388
|
| 3 |
-
trainer:
|
| 4 |
-
accelerator: gpu
|
| 5 |
-
strategy: ddp_find_unused_parameters_true
|
| 6 |
-
devices: -1
|
| 7 |
-
num_nodes: 1
|
| 8 |
-
precision: 32
|
| 9 |
-
logger:
|
| 10 |
-
class_path: pytorch_lightning.loggers.WandbLogger
|
| 11 |
-
init_args:
|
| 12 |
-
name: null
|
| 13 |
-
save_dir: /import/c4dm-datasets-ext/lcap/param
|
| 14 |
-
version: null
|
| 15 |
-
offline: false
|
| 16 |
-
dir: null
|
| 17 |
-
id: null
|
| 18 |
-
anonymous: null
|
| 19 |
-
project: lcap-param
|
| 20 |
-
log_model: false
|
| 21 |
-
experiment: null
|
| 22 |
-
prefix: ''
|
| 23 |
-
checkpoint_name: null
|
| 24 |
-
job_type: null
|
| 25 |
-
config: null
|
| 26 |
-
entity: null
|
| 27 |
-
reinit: null
|
| 28 |
-
tags: null
|
| 29 |
-
group: null
|
| 30 |
-
notes: null
|
| 31 |
-
magic: null
|
| 32 |
-
config_exclude_keys: null
|
| 33 |
-
config_include_keys: null
|
| 34 |
-
mode: null
|
| 35 |
-
allow_val_change: null
|
| 36 |
-
resume: null
|
| 37 |
-
force: null
|
| 38 |
-
tensorboard: null
|
| 39 |
-
sync_tensorboard: null
|
| 40 |
-
monitor_gym: null
|
| 41 |
-
save_code: null
|
| 42 |
-
settings: null
|
| 43 |
-
callbacks:
|
| 44 |
-
- class_path: pytorch_lightning.callbacks.ModelCheckpoint
|
| 45 |
-
init_args:
|
| 46 |
-
dirpath: null
|
| 47 |
-
filename: '{epoch}-{step}'
|
| 48 |
-
monitor: val_loss
|
| 49 |
-
verbose: false
|
| 50 |
-
save_last: true
|
| 51 |
-
save_top_k: 1
|
| 52 |
-
save_weights_only: false
|
| 53 |
-
mode: min
|
| 54 |
-
auto_insert_metric_name: true
|
| 55 |
-
every_n_train_steps: null
|
| 56 |
-
train_time_interval: null
|
| 57 |
-
every_n_epochs: null
|
| 58 |
-
save_on_train_epoch_end: null
|
| 59 |
-
enable_version_counter: true
|
| 60 |
-
- class_path: pytorch_lightning.callbacks.ModelSummary
|
| 61 |
-
init_args:
|
| 62 |
-
max_depth: 2
|
| 63 |
-
- class_path: pytorch_lightning.callbacks.LearningRateMonitor
|
| 64 |
-
init_args:
|
| 65 |
-
logging_interval: null
|
| 66 |
-
log_momentum: false
|
| 67 |
-
log_weight_decay: false
|
| 68 |
-
- class_path: lcap.callbacks.MoveConfigCallback
|
| 69 |
-
init_args:
|
| 70 |
-
project_name: lcap-param
|
| 71 |
-
- class_path: lcap.callbacks.ConfusionMatrixCallback
|
| 72 |
-
fast_dev_run: false
|
| 73 |
-
max_epochs: 250
|
| 74 |
-
min_epochs: null
|
| 75 |
-
max_steps: -1
|
| 76 |
-
min_steps: null
|
| 77 |
-
max_time: null
|
| 78 |
-
limit_train_batches: null
|
| 79 |
-
limit_val_batches: null
|
| 80 |
-
limit_test_batches: null
|
| 81 |
-
limit_predict_batches: null
|
| 82 |
-
overfit_batches: 0.0
|
| 83 |
-
val_check_interval: null
|
| 84 |
-
check_val_every_n_epoch: 5
|
| 85 |
-
num_sanity_val_steps: 32
|
| 86 |
-
log_every_n_steps: 100
|
| 87 |
-
enable_checkpointing: null
|
| 88 |
-
enable_progress_bar: null
|
| 89 |
-
enable_model_summary: null
|
| 90 |
-
accumulate_grad_batches: 1
|
| 91 |
-
gradient_clip_val: null
|
| 92 |
-
gradient_clip_algorithm: null
|
| 93 |
-
deterministic: null
|
| 94 |
-
benchmark: true
|
| 95 |
-
inference_mode: true
|
| 96 |
-
use_distributed_sampler: true
|
| 97 |
-
profiler: null
|
| 98 |
-
detect_anomaly: false
|
| 99 |
-
barebones: false
|
| 100 |
-
plugins: null
|
| 101 |
-
sync_batchnorm: true
|
| 102 |
-
reload_dataloaders_every_n_epochs: 0
|
| 103 |
-
default_root_dir: /import/c4dm-datasets-ext/lcap/param
|
| 104 |
-
ckpt_path: null
|
| 105 |
-
model:
|
| 106 |
-
class_path: lcap.methods.param.ParameterEstimator
|
| 107 |
-
init_args:
|
| 108 |
-
encoder:
|
| 109 |
-
class_path: lcap.models.panns.Cnn14
|
| 110 |
-
init_args:
|
| 111 |
-
embed_dim: 512
|
| 112 |
-
sample_rate: 48000.0
|
| 113 |
-
window_size: 2048
|
| 114 |
-
hop_size: 1024
|
| 115 |
-
mel_bins: 128
|
| 116 |
-
fmin: 20.0
|
| 117 |
-
fmax: 20000.0
|
| 118 |
-
use_batchnorm: true
|
| 119 |
-
input_norm: minmax
|
| 120 |
-
lr: 0.0001
|
| 121 |
-
num_instances: 63
|
| 122 |
-
num_presets: 10
|
| 123 |
-
weight_decay: 0.0001
|
| 124 |
-
max_epochs: 250
|
| 125 |
-
embed_mode: concat
|
| 126 |
-
norm: L2
|
| 127 |
-
data:
|
| 128 |
-
class_path: lcap.dataset.dataset_param.PluginTarfileDataModule
|
| 129 |
-
init_args:
|
| 130 |
-
train_tarfiles:
|
| 131 |
-
- /import/c4dm-datasets-ext/lcap-datasets/20k/musdb18-vst3-presets.tar
|
| 132 |
-
- /import/c4dm-datasets-ext/lcap-datasets/20k/mtg-jamendo-vst3-presets.tar
|
| 133 |
-
- /import/c4dm-datasets-ext/lcap-datasets/20k/ursing-vst3-presets.tar
|
| 134 |
-
- /import/c4dm-datasets-ext/lcap-datasets/20k/enst-drums-vst3-presets.tar
|
| 135 |
-
- /import/c4dm-datasets-ext/lcap-datasets/20k/fsd50k-vst3-presets.tar
|
| 136 |
-
- /import/c4dm-datasets-ext/lcap-datasets/20k/librispeech-vst3-presets.tar
|
| 137 |
-
- /import/c4dm-datasets-ext/lcap-datasets/20k/medley-solos-db-vst3-presets.tar
|
| 138 |
-
- /import/c4dm-datasets-ext/lcap-datasets/20k/guitarset-vst3-presets.tar
|
| 139 |
-
val_tarfiles:
|
| 140 |
-
- /import/c4dm-datasets-ext/lcap-datasets/20k/guitarset-vst3-presets.tar
|
| 141 |
-
plugin_json: plugins/valid_plugins.json
|
| 142 |
-
batch_size: 64
|
| 143 |
-
num_workers: 8
|
| 144 |
-
length: 262144
|
| 145 |
-
stereo: true
|
| 146 |
-
test_tarfiles: null
|
| 147 |
-
pin_memory: true
|
| 148 |
-
persistent_workers: true
|
| 149 |
-
num_train_examples: 140000
|
| 150 |
-
num_val_examples: 2000
|
| 151 |
-
num_test_examples: 2000
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/st_ito/dataset/dataset_param.py
DELETED
|
@@ -1,595 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import io
|
| 3 |
-
import sys
|
| 4 |
-
import glob
|
| 5 |
-
import json
|
| 6 |
-
import math
|
| 7 |
-
import torch
|
| 8 |
-
import tarfile
|
| 9 |
-
import torchaudio
|
| 10 |
-
import numpy as np
|
| 11 |
-
import pytorch_lightning as pl
|
| 12 |
-
|
| 13 |
-
from tqdm import tqdm
|
| 14 |
-
from typing import List
|
| 15 |
-
from torch.utils.data import Dataset, IterableDataset
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def tarfile_worker_init_fn(worker_id):
|
| 19 |
-
worker_info = torch.utils.data.get_worker_info()
|
| 20 |
-
dataset = worker_info.dataset # the dataset copy in this worker process
|
| 21 |
-
|
| 22 |
-
# if there is more than one tar file in the dataset, then we need to
|
| 23 |
-
# select one tar file for this worker
|
| 24 |
-
print(f"Worker {worker_id} creating new tar file handles.")
|
| 25 |
-
|
| 26 |
-
# reopen the tarfile handles for this worker
|
| 27 |
-
dataset.tar_handles = []
|
| 28 |
-
for tar_file in dataset.tar_files:
|
| 29 |
-
tar_handle = tarfile.open(tar_file, "r:")
|
| 30 |
-
tar_handle.next() # skip the first root directory
|
| 31 |
-
tar_handle.next() # skip the first example directory
|
| 32 |
-
dataset.tar_handles.append(tar_handle)
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
def torchaudio_decode(b):
|
| 36 |
-
x, sr = torchaudio.load(b, format="FLAC")
|
| 37 |
-
return x
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
class PluginTarfileDataset(IterableDataset):
|
| 41 |
-
def __init__(
|
| 42 |
-
self,
|
| 43 |
-
tar_files: List[str],
|
| 44 |
-
plugin_json: str,
|
| 45 |
-
length: int = 262144,
|
| 46 |
-
stereo: bool = True,
|
| 47 |
-
num_examples: int = 20000,
|
| 48 |
-
label_files: List[str] = None,
|
| 49 |
-
) -> None:
|
| 50 |
-
super().__init__()
|
| 51 |
-
""" Interable dataset that returns a single input/output pair from a tar file.
|
| 52 |
-
|
| 53 |
-
When multiple tar files are provided, we open a file handle to each tar file. Then on each
|
| 54 |
-
iteration we select a tar file at random and then select a random example from that tar file.
|
| 55 |
-
We repeat this until each tar file has been read once. Then we start over again.
|
| 56 |
-
|
| 57 |
-
Args:
|
| 58 |
-
tar_files (List[str]): List of tar files to read from.
|
| 59 |
-
plugin_json (str): Path to json file containing plugin instance details.
|
| 60 |
-
length (int): Length of audio to return. Defaults to 262144.
|
| 61 |
-
stereo (bool): whether to return stereo audio. Defaults to True.
|
| 62 |
-
When set to False, the mean of the two channels is returned.
|
| 63 |
-
num_examples (int): Number of examples to return. Defaults to 20000.
|
| 64 |
-
label_files (List[str]): List of label files to use for classification. Defaults to None.
|
| 65 |
-
Note: they must be int he same order as the associated tar_files.
|
| 66 |
-
|
| 67 |
-
"""
|
| 68 |
-
self.tar_files = tar_files
|
| 69 |
-
self.length = length
|
| 70 |
-
self.stereo = stereo
|
| 71 |
-
self.num_examples = num_examples
|
| 72 |
-
self.label_files = label_files
|
| 73 |
-
|
| 74 |
-
# first open plugin info
|
| 75 |
-
# this is used for indexing into the plugin list
|
| 76 |
-
with open(plugin_json, "r") as f:
|
| 77 |
-
self.plugins = json.load(f)
|
| 78 |
-
|
| 79 |
-
# get the tar file handles
|
| 80 |
-
self.tar_handles = []
|
| 81 |
-
for tar_file in tar_files:
|
| 82 |
-
tar_handle = tarfile.open(tar_file, "r:")
|
| 83 |
-
tar_handle.next() # skip the first root directory
|
| 84 |
-
tar_handle.next() # skip the first example directory
|
| 85 |
-
self.tar_handles.append(tar_handle)
|
| 86 |
-
|
| 87 |
-
# check if we have label files
|
| 88 |
-
if label_files is not None:
|
| 89 |
-
assert len(label_files) == len(tar_files) # must be the same length
|
| 90 |
-
self.labels = []
|
| 91 |
-
for label_file in label_files:
|
| 92 |
-
results = torch.load(label_file)
|
| 93 |
-
self.labels.append(results)
|
| 94 |
-
|
| 95 |
-
def __len__(self):
|
| 96 |
-
return self.num_examples
|
| 97 |
-
|
| 98 |
-
def next_example(self):
|
| 99 |
-
while True:
|
| 100 |
-
# select tar file at random
|
| 101 |
-
tar_index = np.random.choice(len(self.tar_handles))
|
| 102 |
-
|
| 103 |
-
# get the next member (should be root directory of this example)
|
| 104 |
-
members = {}
|
| 105 |
-
done = False
|
| 106 |
-
handle_reset = False
|
| 107 |
-
while not done:
|
| 108 |
-
# get next member
|
| 109 |
-
member = self.tar_handles[tar_index].next()
|
| 110 |
-
|
| 111 |
-
if member is None:
|
| 112 |
-
# we have reached the end of this tar file
|
| 113 |
-
# so, we will close it and remove open tar handle
|
| 114 |
-
|
| 115 |
-
# close this tar handle
|
| 116 |
-
self.tar_handles[tar_index].close()
|
| 117 |
-
|
| 118 |
-
# reopen
|
| 119 |
-
tar_handle = tarfile.open(self.tar_files[tar_index], "r:")
|
| 120 |
-
tar_handle.next() # skip the first root directory
|
| 121 |
-
tar_handle.next() # skip the first example directory
|
| 122 |
-
self.tar_handles[tar_index] = tar_handle # replace handle
|
| 123 |
-
print(f"Resetting tar handle {self.tar_files[tar_index]}.")
|
| 124 |
-
handle_reset = True
|
| 125 |
-
break
|
| 126 |
-
|
| 127 |
-
if member.isdir():
|
| 128 |
-
done = True
|
| 129 |
-
break
|
| 130 |
-
|
| 131 |
-
members[os.path.basename(member.name)] = member
|
| 132 |
-
|
| 133 |
-
if handle_reset:
|
| 134 |
-
continue
|
| 135 |
-
|
| 136 |
-
# get input file
|
| 137 |
-
input_file = self.tar_handles[tar_index].extractfile(members["input.flac"])
|
| 138 |
-
input = torchaudio_decode(input_file)
|
| 139 |
-
|
| 140 |
-
parent_dir = os.path.dirname(members["input.flac"].name).split("/")[-1]
|
| 141 |
-
|
| 142 |
-
# select at random from the processed files
|
| 143 |
-
processed_members = [
|
| 144 |
-
x for x in members.keys() if x != "input.flac" and ".json" not in x
|
| 145 |
-
]
|
| 146 |
-
processed_member = np.random.choice(processed_members)
|
| 147 |
-
processed_fp = self.tar_handles[tar_index].extractfile(
|
| 148 |
-
members[processed_member]
|
| 149 |
-
)
|
| 150 |
-
output = torchaudio_decode(processed_fp)
|
| 151 |
-
|
| 152 |
-
# load the json file
|
| 153 |
-
json_member = members[processed_member.replace(".flac", ".json")]
|
| 154 |
-
json_fp = self.tar_handles[tar_index].extractfile(json_member)
|
| 155 |
-
details = json.load(json_fp)
|
| 156 |
-
|
| 157 |
-
# if using labels, get the classification info
|
| 158 |
-
if self.label_files is not None:
|
| 159 |
-
print(
|
| 160 |
-
tar_index, members["input.flac"].name, self.label_files[tar_index]
|
| 161 |
-
)
|
| 162 |
-
label_info = self.labels[tar_index][parent_dir]
|
| 163 |
-
predicted_class_id = label_info["predicted_class_ids"]
|
| 164 |
-
predicted_label = label_info["predicted_label"]
|
| 165 |
-
logits = label_info["logits"].squeeze()
|
| 166 |
-
else:
|
| 167 |
-
logits = torch.empty(0)
|
| 168 |
-
|
| 169 |
-
# get the index of the plugin instance for classification
|
| 170 |
-
instance_index = self.plugins[details["instance"]]
|
| 171 |
-
instance_index -= 1 # zero index
|
| 172 |
-
instance_index = torch.tensor(instance_index).long()
|
| 173 |
-
preset_index = torch.tensor(details["preset"]).long()
|
| 174 |
-
|
| 175 |
-
# fit the proper length
|
| 176 |
-
if input.shape[-1] < self.length:
|
| 177 |
-
input = torch.cat(
|
| 178 |
-
[
|
| 179 |
-
input,
|
| 180 |
-
torch.zeros([input.shape[0], self.length - input.shape[-1]]),
|
| 181 |
-
],
|
| 182 |
-
dim=-1,
|
| 183 |
-
)
|
| 184 |
-
if output.shape[-1] < self.length:
|
| 185 |
-
output = torch.cat(
|
| 186 |
-
[
|
| 187 |
-
output,
|
| 188 |
-
torch.zeros([output.shape[0], self.length - output.shape[-1]]),
|
| 189 |
-
],
|
| 190 |
-
dim=-1,
|
| 191 |
-
)
|
| 192 |
-
|
| 193 |
-
# take a different random crop from each file
|
| 194 |
-
if input.shape[-1] > self.length:
|
| 195 |
-
start = torch.randint(0, (input.shape[-1] - self.length), [1])
|
| 196 |
-
input = input[..., start : start + self.length]
|
| 197 |
-
|
| 198 |
-
# take a random crop
|
| 199 |
-
if output.shape[-1] > self.length:
|
| 200 |
-
start = torch.randint(0, (output.shape[-1] - self.length), [1])
|
| 201 |
-
output = output[..., start : start + self.length]
|
| 202 |
-
|
| 203 |
-
# check if stereo
|
| 204 |
-
if self.stereo:
|
| 205 |
-
# convert mono to stereo
|
| 206 |
-
if input.shape[0] == 1:
|
| 207 |
-
input = input.repeat(2, 1)
|
| 208 |
-
if output.shape[0] == 1:
|
| 209 |
-
output = output.repeat(2, 1)
|
| 210 |
-
else:
|
| 211 |
-
# convert stereo to mono
|
| 212 |
-
if input.shape[0] == 2:
|
| 213 |
-
input = torch.mean(input, dim=0, keepdim=True)
|
| 214 |
-
if output.shape[0] == 2:
|
| 215 |
-
output = torch.mean(output, dim=0, keepdim=True)
|
| 216 |
-
|
| 217 |
-
# randomize the gain of input and output
|
| 218 |
-
input /= torch.max(torch.abs(input)).clamp(1e-8)
|
| 219 |
-
output /= torch.max(torch.abs(output)).clamp(1e-8)
|
| 220 |
-
|
| 221 |
-
# from 0 to -32 dB
|
| 222 |
-
gain_db = torch.rand(1) * -32.0
|
| 223 |
-
input *= 10.0 ** (gain_db / 20.0)
|
| 224 |
-
|
| 225 |
-
# from 0 to -32 dB
|
| 226 |
-
gain_db = torch.rand(1) * -32.0
|
| 227 |
-
output *= 10.0 ** (gain_db / 20.0)
|
| 228 |
-
|
| 229 |
-
# swap left and right channels at random
|
| 230 |
-
if torch.rand(1) > 0.5:
|
| 231 |
-
input = input.flip(0)
|
| 232 |
-
output = output.flip(0)
|
| 233 |
-
|
| 234 |
-
yield input, output, instance_index, preset_index, tar_index, logits
|
| 235 |
-
|
| 236 |
-
def __iter__(self):
|
| 237 |
-
return iter(self.next_example())
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
class PluginTarfileDataModule(pl.LightningDataModule):
|
| 241 |
-
def __init__(
|
| 242 |
-
self,
|
| 243 |
-
train_tarfiles: List[str],
|
| 244 |
-
val_tarfiles: List[str],
|
| 245 |
-
plugin_json: str,
|
| 246 |
-
batch_size: int,
|
| 247 |
-
num_workers: int,
|
| 248 |
-
length: int = 262144,
|
| 249 |
-
stereo: bool = True,
|
| 250 |
-
test_tarfiles: List[str] = None,
|
| 251 |
-
train_labels: List[str] = None,
|
| 252 |
-
val_labels: List[str] = None,
|
| 253 |
-
test_labels: List[str] = None,
|
| 254 |
-
pin_memory: bool = False,
|
| 255 |
-
persistent_workers: bool = True,
|
| 256 |
-
num_train_examples: int = 20000,
|
| 257 |
-
num_val_examples: int = 2000,
|
| 258 |
-
num_test_examples: int = 2000,
|
| 259 |
-
) -> None:
|
| 260 |
-
super().__init__()
|
| 261 |
-
""" Pytorch Lightning DataModule for loading data from tar files.
|
| 262 |
-
|
| 263 |
-
Args:
|
| 264 |
-
train_tarfiles (List[str]): List of tar files to use for training.
|
| 265 |
-
val_tarfiles (List[str]): List of tar files to use for validation.
|
| 266 |
-
plugin_json (str): Path to json file containing plugin instance details.
|
| 267 |
-
batch_size (int): Batch size.
|
| 268 |
-
num_workers (int): Number of workers.
|
| 269 |
-
length (int): Length of audio to return. Defaults to 262144.
|
| 270 |
-
stereo (bool): whether to return stereo audio. Defaults to True.
|
| 271 |
-
When set to False, the mean of the two channels is returned.
|
| 272 |
-
test_tarfiles (List[str]): List of tar files to use for testing.
|
| 273 |
-
train_labels (List[str]): List of label files to use for training. Defaults to None.
|
| 274 |
-
val_labels (List[str]): List of label files to use for validation. Defaults to None.
|
| 275 |
-
test_labels (List[str]): List of label files to use for testing. Defaults to None.
|
| 276 |
-
pin_memory (bool): Whether to pin memory. Defaults to False.
|
| 277 |
-
persistent_workers (bool): Whether to use persistent workers. Defaults to True.
|
| 278 |
-
num_train_examples (int): Number of training examples to return. Defaults to 20000.
|
| 279 |
-
num_val_examples (int): Number of validation examples to return. Defaults to 2000.
|
| 280 |
-
num_test_examples (int): Number of testing examples to return. Defaults to 2000.
|
| 281 |
-
"""
|
| 282 |
-
self.save_hyperparameters()
|
| 283 |
-
|
| 284 |
-
def setup(self, stage=None):
|
| 285 |
-
self.train_dataset = PluginTarfileDataset(
|
| 286 |
-
self.hparams.train_tarfiles,
|
| 287 |
-
self.hparams.plugin_json,
|
| 288 |
-
self.hparams.length,
|
| 289 |
-
self.hparams.stereo,
|
| 290 |
-
self.hparams.num_train_examples,
|
| 291 |
-
label_files=self.hparams.train_labels,
|
| 292 |
-
)
|
| 293 |
-
|
| 294 |
-
self.val_dataset = PluginTarfileDataset(
|
| 295 |
-
self.hparams.val_tarfiles,
|
| 296 |
-
self.hparams.plugin_json,
|
| 297 |
-
self.hparams.length,
|
| 298 |
-
self.hparams.stereo,
|
| 299 |
-
self.hparams.num_val_examples,
|
| 300 |
-
label_files=self.hparams.val_labels,
|
| 301 |
-
)
|
| 302 |
-
|
| 303 |
-
if self.hparams.test_tarfiles is not None:
|
| 304 |
-
self.test_dataset = PluginTarfileDataset(
|
| 305 |
-
self.hparams.test_tarfiles,
|
| 306 |
-
self.hparams.plugin_json,
|
| 307 |
-
self.hparams.length,
|
| 308 |
-
self.hparams.stereo,
|
| 309 |
-
self.hparams.num_test_examples,
|
| 310 |
-
label_files=self.hparams.test_labels,
|
| 311 |
-
)
|
| 312 |
-
|
| 313 |
-
def train_dataloader(self):
|
| 314 |
-
return torch.utils.data.DataLoader(
|
| 315 |
-
self.train_dataset,
|
| 316 |
-
batch_size=self.hparams.batch_size,
|
| 317 |
-
num_workers=self.hparams.num_workers,
|
| 318 |
-
drop_last=True,
|
| 319 |
-
pin_memory=self.hparams.pin_memory,
|
| 320 |
-
persistent_workers=self.hparams.persistent_workers,
|
| 321 |
-
worker_init_fn=tarfile_worker_init_fn,
|
| 322 |
-
)
|
| 323 |
-
|
| 324 |
-
def val_dataloader(self):
|
| 325 |
-
return torch.utils.data.DataLoader(
|
| 326 |
-
self.val_dataset,
|
| 327 |
-
batch_size=self.hparams.batch_size,
|
| 328 |
-
num_workers=self.hparams.num_workers,
|
| 329 |
-
pin_memory=self.hparams.pin_memory,
|
| 330 |
-
persistent_workers=self.hparams.persistent_workers,
|
| 331 |
-
worker_init_fn=tarfile_worker_init_fn,
|
| 332 |
-
)
|
| 333 |
-
|
| 334 |
-
def test_dataloader(self):
|
| 335 |
-
return torch.utils.data.DataLoader(
|
| 336 |
-
self.val_dataset,
|
| 337 |
-
batch_size=self.hparams.batch_size,
|
| 338 |
-
num_workers=self.hparams.num_workers,
|
| 339 |
-
persistent_workers=self.hparams.persistent_workers,
|
| 340 |
-
worker_init_fn=tarfile_worker_init_fn,
|
| 341 |
-
)
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
# ------------------------------ File based dataset --------------------------------
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
class PluginAudioFileWebDataset(Dataset):
|
| 348 |
-
def __init__(
|
| 349 |
-
self,
|
| 350 |
-
root_dirs: str,
|
| 351 |
-
plugin_json: str,
|
| 352 |
-
length: int = 524288,
|
| 353 |
-
stereo: bool = True,
|
| 354 |
-
) -> None:
|
| 355 |
-
"""
|
| 356 |
-
|
| 357 |
-
Args:
|
| 358 |
-
root_dirs (List[str]): List of directories containing dataset files.
|
| 359 |
-
plugin_json: Path to json file containing plugin instance details.
|
| 360 |
-
length (int): Length of audio to return. Defaults to 524288.
|
| 361 |
-
stereo (bool): whether to return stereo audio. Defaults to True.
|
| 362 |
-
When set to False, the mean of the two channels is returned.
|
| 363 |
-
|
| 364 |
-
"""
|
| 365 |
-
super().__init__()
|
| 366 |
-
self.root_dirs = root_dirs
|
| 367 |
-
self.stereo = stereo
|
| 368 |
-
self.length = length
|
| 369 |
-
|
| 370 |
-
self.examples = []
|
| 371 |
-
|
| 372 |
-
# find all segment directories
|
| 373 |
-
for root_dir in root_dirs:
|
| 374 |
-
examples = []
|
| 375 |
-
# first, check if there is an examples.json file
|
| 376 |
-
examples_json = os.path.join(root_dir, "examples.json")
|
| 377 |
-
if os.path.exists(examples_json):
|
| 378 |
-
print(f"Found {examples_json}.")
|
| 379 |
-
with open(examples_json, "r") as f:
|
| 380 |
-
examples = json.load(f)
|
| 381 |
-
else:
|
| 382 |
-
print(
|
| 383 |
-
f"No examples.json file found. Crawling to find all examples in {root_dir}..."
|
| 384 |
-
)
|
| 385 |
-
segment_dirs = glob.glob(os.path.join(root_dir, "*"))
|
| 386 |
-
segment_dirs = sorted([x for x in segment_dirs if os.path.isdir(x)])
|
| 387 |
-
|
| 388 |
-
for segment_dir in tqdm(segment_dirs):
|
| 389 |
-
# find the input file
|
| 390 |
-
input_file = os.path.join(segment_dir, "input.flac")
|
| 391 |
-
if not os.path.exists(input_file):
|
| 392 |
-
continue
|
| 393 |
-
|
| 394 |
-
# find all processed audio files
|
| 395 |
-
processed_files = glob.glob(os.path.join(segment_dir, "*.flac"))
|
| 396 |
-
|
| 397 |
-
for processed_file in processed_files:
|
| 398 |
-
if processed_file == input_file:
|
| 399 |
-
continue
|
| 400 |
-
# find the appropriate json file
|
| 401 |
-
json_file = processed_file.replace(".flac", ".json")
|
| 402 |
-
|
| 403 |
-
# check if json file exists
|
| 404 |
-
if not os.path.exists(json_file):
|
| 405 |
-
print(f"Could not find {json_file}.")
|
| 406 |
-
continue
|
| 407 |
-
|
| 408 |
-
# test open json file
|
| 409 |
-
# try:
|
| 410 |
-
# with open(json_file, "r") as f:
|
| 411 |
-
# details = json.load(f)
|
| 412 |
-
# except Exception:
|
| 413 |
-
# print(f"Error opening {json_file}.")
|
| 414 |
-
# continue
|
| 415 |
-
|
| 416 |
-
example = (input_file, processed_file, json_file)
|
| 417 |
-
examples.append(example)
|
| 418 |
-
|
| 419 |
-
self.examples += examples
|
| 420 |
-
print(
|
| 421 |
-
f"Found {len(examples)} examples in {root_dir}. Total: {len(self.examples)}"
|
| 422 |
-
)
|
| 423 |
-
|
| 424 |
-
with open(plugin_json, "r") as f:
|
| 425 |
-
self.plugins = json.load(f)
|
| 426 |
-
|
| 427 |
-
# for testing, load one example into memory
|
| 428 |
-
# input_file, processed_file, json_file = self.examples[0]
|
| 429 |
-
|
| 430 |
-
# open the json file
|
| 431 |
-
# with open(json_file, "r") as f:
|
| 432 |
-
# details = json.load(f)
|
| 433 |
-
|
| 434 |
-
# input, sr = torchaudio.load(input_file, backend="soundfile")
|
| 435 |
-
# output, sr = torchaudio.load(processed_file, backend="soundfile")
|
| 436 |
-
|
| 437 |
-
# self.input = input
|
| 438 |
-
# self.output = output
|
| 439 |
-
# self.details = details
|
| 440 |
-
|
| 441 |
-
def __len__(self):
|
| 442 |
-
return len(self.examples)
|
| 443 |
-
|
| 444 |
-
def __getitem__(self, idx):
|
| 445 |
-
"""Get a single example from the dataset."""
|
| 446 |
-
|
| 447 |
-
if True:
|
| 448 |
-
input_file, processed_file, json_file = self.examples[idx]
|
| 449 |
-
|
| 450 |
-
# open the json file
|
| 451 |
-
with open(json_file, "r") as f:
|
| 452 |
-
details = json.load(f)
|
| 453 |
-
|
| 454 |
-
input, sr = torchaudio.load(input_file, backend="soundfile")
|
| 455 |
-
output, sr = torchaudio.load(processed_file, backend="soundfile")
|
| 456 |
-
else:
|
| 457 |
-
input = self.input
|
| 458 |
-
output = self.output
|
| 459 |
-
details = self.details
|
| 460 |
-
|
| 461 |
-
# check length of input and output
|
| 462 |
-
# if input.shape[-1] != output.shape[-1]:
|
| 463 |
-
# raise RuntimeError(
|
| 464 |
-
# f"Input and output shapes do not match: {input.shape} vs {output.shape}"
|
| 465 |
-
# )
|
| 466 |
-
|
| 467 |
-
# fit the proper length
|
| 468 |
-
if input.shape[-1] < self.length:
|
| 469 |
-
input = torch.cat(
|
| 470 |
-
[input, torch.zeros([input.shape[0], self.length - input.shape[-1]])],
|
| 471 |
-
dim=-1,
|
| 472 |
-
)
|
| 473 |
-
if output.shape[-1] < self.length:
|
| 474 |
-
output = torch.cat(
|
| 475 |
-
[
|
| 476 |
-
output,
|
| 477 |
-
torch.zeros([output.shape[0], self.length - output.shape[-1]]),
|
| 478 |
-
],
|
| 479 |
-
dim=-1,
|
| 480 |
-
)
|
| 481 |
-
|
| 482 |
-
# take a random crop
|
| 483 |
-
start = torch.randint(
|
| 484 |
-
0, (min(input.shape[-1], output.shape[-1]) - self.length), [1]
|
| 485 |
-
)
|
| 486 |
-
input = input[..., start : start + self.length]
|
| 487 |
-
output = output[..., start : start + self.length]
|
| 488 |
-
|
| 489 |
-
# note: now we take a crop from the same position in both files
|
| 490 |
-
# however, it might be a good augmentation to take a different
|
| 491 |
-
# random crop from each file
|
| 492 |
-
|
| 493 |
-
# get the index of the plugin instance for classification
|
| 494 |
-
instance_index = self.plugins[details["instance"]]
|
| 495 |
-
instance_index -= 1 # zero index
|
| 496 |
-
instance_index = torch.tensor(instance_index).long()
|
| 497 |
-
preset_index = torch.tensor(details["preset"]).long()
|
| 498 |
-
|
| 499 |
-
# check if stereo
|
| 500 |
-
if self.stereo:
|
| 501 |
-
# convert mono to stereo
|
| 502 |
-
if input.shape[0] == 1:
|
| 503 |
-
input = input.repeat(2, 1)
|
| 504 |
-
if output.shape[0] == 1:
|
| 505 |
-
output = output.repeat(2, 1)
|
| 506 |
-
else:
|
| 507 |
-
# convert stereo to mono
|
| 508 |
-
if input.shape[0] == 2:
|
| 509 |
-
input = torch.mean(input, dim=0, keepdim=True)
|
| 510 |
-
if output.shape[0] == 2:
|
| 511 |
-
output = torch.mean(output, dim=0, keepdim=True)
|
| 512 |
-
|
| 513 |
-
# randomize the gain of input and output
|
| 514 |
-
input /= torch.max(torch.abs(input)).clamp(1e-8)
|
| 515 |
-
output /= torch.max(torch.abs(output)).clamp(1e-8)
|
| 516 |
-
|
| 517 |
-
# from 0 to -32 dB
|
| 518 |
-
gain_db = torch.rand(1) * -32.0
|
| 519 |
-
input *= 10.0 ** (gain_db / 20.0)
|
| 520 |
-
|
| 521 |
-
# from 0 to -32 dB
|
| 522 |
-
gain_db = torch.rand(1) * -32.0
|
| 523 |
-
output *= 10.0 ** (gain_db / 20.0)
|
| 524 |
-
|
| 525 |
-
return input, output, instance_index, preset_index
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
class PluginAudioFileWebDataModule(pl.LightningDataModule):
|
| 529 |
-
def __init__(
|
| 530 |
-
self,
|
| 531 |
-
train_root_dirs: List[str],
|
| 532 |
-
val_root_dirs: List[str],
|
| 533 |
-
plugin_json: str,
|
| 534 |
-
batch_size: int,
|
| 535 |
-
num_workers: int,
|
| 536 |
-
length: int = 262144,
|
| 537 |
-
test_root_dirs: List[str] = None,
|
| 538 |
-
pin_memory: bool = False,
|
| 539 |
-
persistent_workers: bool = False,
|
| 540 |
-
use_tarfile: bool = False,
|
| 541 |
-
) -> None:
|
| 542 |
-
super().__init__()
|
| 543 |
-
self.save_hyperparameters()
|
| 544 |
-
|
| 545 |
-
def setup(self, stage=None):
|
| 546 |
-
if not self.hparams.use_tarfile:
|
| 547 |
-
self.train_dataset = PluginAudioFileWebDataset(
|
| 548 |
-
self.hparams.train_root_dirs,
|
| 549 |
-
self.hparams.plugin_json,
|
| 550 |
-
self.hparams.length,
|
| 551 |
-
)
|
| 552 |
-
|
| 553 |
-
self.val_dataset = PluginAudioFileWebDataset(
|
| 554 |
-
self.hparams.val_root_dirs,
|
| 555 |
-
self.hparams.plugin_json,
|
| 556 |
-
self.hparams.length,
|
| 557 |
-
)
|
| 558 |
-
|
| 559 |
-
if self.hparams.test_root_dirs is not None:
|
| 560 |
-
self.test_dataset = PluginAudioFileWebDataset(
|
| 561 |
-
self.hparams.test_root_dirs,
|
| 562 |
-
self.hparams.plugin_json,
|
| 563 |
-
self.hparams.length,
|
| 564 |
-
)
|
| 565 |
-
else:
|
| 566 |
-
raise NotImplementedError
|
| 567 |
-
|
| 568 |
-
def train_dataloader(self):
|
| 569 |
-
return torch.utils.data.DataLoader(
|
| 570 |
-
self.train_dataset,
|
| 571 |
-
batch_size=self.hparams.batch_size,
|
| 572 |
-
shuffle=True,
|
| 573 |
-
num_workers=self.hparams.num_workers,
|
| 574 |
-
drop_last=True,
|
| 575 |
-
pin_memory=self.hparams.pin_memory,
|
| 576 |
-
persistent_workers=self.hparams.persistent_workers,
|
| 577 |
-
prefetch_factor=8,
|
| 578 |
-
)
|
| 579 |
-
|
| 580 |
-
def val_dataloader(self):
|
| 581 |
-
return torch.utils.data.DataLoader(
|
| 582 |
-
self.val_dataset,
|
| 583 |
-
batch_size=self.hparams.batch_size,
|
| 584 |
-
num_workers=self.hparams.num_workers,
|
| 585 |
-
pin_memory=self.hparams.pin_memory,
|
| 586 |
-
persistent_workers=self.hparams.persistent_workers,
|
| 587 |
-
)
|
| 588 |
-
|
| 589 |
-
def test_dataloader(self):
|
| 590 |
-
return torch.utils.data.DataLoader(
|
| 591 |
-
self.val_dataset,
|
| 592 |
-
batch_size=self.hparams.batch_size,
|
| 593 |
-
num_workers=self.hparams.num_workers,
|
| 594 |
-
persistent_workers=self.hparams.persistent_workers,
|
| 595 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/st_ito/dataset/dataset_sim.py
DELETED
|
@@ -1,320 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import glob
|
| 3 |
-
import torch
|
| 4 |
-
import torchaudio
|
| 5 |
-
import pedalboard
|
| 6 |
-
import numpy as np
|
| 7 |
-
import pytorch_lightning as pl
|
| 8 |
-
|
| 9 |
-
from tqdm import tqdm
|
| 10 |
-
from typing import List, Optional, Tuple, Iterable
|
| 11 |
-
from st_ito.effects import (
|
| 12 |
-
BasicChorus,
|
| 13 |
-
BasicCompressor,
|
| 14 |
-
BasicDelay,
|
| 15 |
-
BasicDistortion,
|
| 16 |
-
BasicReverb,
|
| 17 |
-
)
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
def worker_init_fn(worker_id):
|
| 21 |
-
worker_info = torch.utils.data.get_worker_info()
|
| 22 |
-
# re-init the vst_plugins for each worker
|
| 23 |
-
vst_plugins = load_plugins(worker_info.dataset.vst_dir)
|
| 24 |
-
worker_info.dataset.vst_plugins = vst_plugins
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
def find_files_with_glob(
|
| 28 |
-
directory: str,
|
| 29 |
-
):
|
| 30 |
-
"""Find all files in the given directory with specific pattern."""
|
| 31 |
-
audio_filepaths = []
|
| 32 |
-
for ext in ["*.wav", "*.mp3", "*.ogg", "*.flac"]:
|
| 33 |
-
audio_filepaths += glob.glob(os.path.join(directory, "**", ext), recursive=True)
|
| 34 |
-
|
| 35 |
-
return audio_filepaths
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
def find_files_with_extensions(
|
| 39 |
-
directory: str,
|
| 40 |
-
extensions: Tuple[str] = (".wav", ".mp3", ".ogg", ".flac"),
|
| 41 |
-
):
|
| 42 |
-
"""Find all files in the given directory with specific extensions."""
|
| 43 |
-
for entry in os.scandir(directory):
|
| 44 |
-
if entry.is_file() and entry.name.lower().endswith(extensions):
|
| 45 |
-
yield entry.path
|
| 46 |
-
elif entry.is_dir():
|
| 47 |
-
yield from find_files_with_extensions(entry.path, extensions)
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
def find_files_lazy(
|
| 51 |
-
directory: str,
|
| 52 |
-
extensions: Tuple[str] = (".wav", ".mp3", ".ogg", ".flac"),
|
| 53 |
-
):
|
| 54 |
-
"""Lazily find files with specific extensions in the given directory."""
|
| 55 |
-
for root, dirs, files in os.walk(directory):
|
| 56 |
-
for file in files:
|
| 57 |
-
if file.lower().endswith(extensions):
|
| 58 |
-
yield os.path.join(root, file)
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
def get_nonsilent_region(
|
| 62 |
-
audio_filepath: str,
|
| 63 |
-
total_num_frames: int,
|
| 64 |
-
length: int,
|
| 65 |
-
threshold: float = 1e-8,
|
| 66 |
-
max_tires: int = 100,
|
| 67 |
-
):
|
| 68 |
-
silent = True
|
| 69 |
-
tries = 0
|
| 70 |
-
while silent:
|
| 71 |
-
|
| 72 |
-
if total_num_frames > length:
|
| 73 |
-
# load a random segment of the audio file
|
| 74 |
-
audio, sr = torchaudio.load(
|
| 75 |
-
audio_filepath,
|
| 76 |
-
num_frames=length,
|
| 77 |
-
frame_offset=np.random.randint(0, total_num_frames - length),
|
| 78 |
-
backend="soundfile",
|
| 79 |
-
)
|
| 80 |
-
|
| 81 |
-
else:
|
| 82 |
-
# load the entire audio file
|
| 83 |
-
audio, sr = torchaudio.load(
|
| 84 |
-
audio_filepath,
|
| 85 |
-
num_frames=total_num_frames,
|
| 86 |
-
backend="soundfile",
|
| 87 |
-
)
|
| 88 |
-
|
| 89 |
-
# repeat the audio to reach the desired length
|
| 90 |
-
audio = torch.cat([audio] * (length // total_num_frames + 1), axis=1)
|
| 91 |
-
audio = audio[:, :length]
|
| 92 |
-
|
| 93 |
-
# check for silence
|
| 94 |
-
energy = torch.mean(audio**2)
|
| 95 |
-
print(energy)
|
| 96 |
-
if energy > threshold:
|
| 97 |
-
silent = False
|
| 98 |
-
else:
|
| 99 |
-
silent = False # TODO: remove this line
|
| 100 |
-
|
| 101 |
-
tries += 1
|
| 102 |
-
|
| 103 |
-
if tries > max_tires:
|
| 104 |
-
raise ValueError(
|
| 105 |
-
f"Could not find a non-silent region in the audio: {audio_filepath}."
|
| 106 |
-
)
|
| 107 |
-
|
| 108 |
-
return audio, sr
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
def random_gain(audio: torch.Tensor):
|
| 112 |
-
# first peak normalize
|
| 113 |
-
audio = audio / torch.max(torch.abs(audio))
|
| 114 |
-
# then apply random gain
|
| 115 |
-
gain_db = np.random.uniform(-18, 0)
|
| 116 |
-
gain_lin = 10 ** (gain_db / 20)
|
| 117 |
-
audio = audio * gain_lin
|
| 118 |
-
return audio
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
def conform_channels(audio_input: torch.Tensor, num_channels: int):
|
| 122 |
-
if audio_input.shape[0] == 1 and num_channels == 2:
|
| 123 |
-
audio_input = torch.cat((audio_input, audio_input), axis=0)
|
| 124 |
-
if audio_input.shape[0] == 2 and num_channels == 1:
|
| 125 |
-
audio_input = audio_input[0:1, :]
|
| 126 |
-
return audio_input
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
def load_plugins(vst_dir: str, sample_rate: int = 48000):
|
| 130 |
-
# find all vst3 files
|
| 131 |
-
vst_filepaths = []
|
| 132 |
-
for ext in ["*.vst3"]:
|
| 133 |
-
vst_filepaths += glob.glob(os.path.join(vst_dir, ext), recursive=True)
|
| 134 |
-
|
| 135 |
-
# vst_filepaths = vst_filepaths[:2]
|
| 136 |
-
|
| 137 |
-
vst_filepaths = sorted(vst_filepaths)
|
| 138 |
-
print(f"Found {len(vst_filepaths)} VST3 files in {vst_dir}.")
|
| 139 |
-
|
| 140 |
-
vst_plugins = {}
|
| 141 |
-
print("Loading plugins...")
|
| 142 |
-
for vst_filepath in tqdm(vst_filepaths):
|
| 143 |
-
instance_name = os.path.basename(vst_filepath).replace(".vst3", "")
|
| 144 |
-
print(f"Loading {instance_name}...")
|
| 145 |
-
# try to load each plugin
|
| 146 |
-
try:
|
| 147 |
-
plugin = pedalboard.load_plugin(vst_filepath)
|
| 148 |
-
except Exception:
|
| 149 |
-
print(f"Error loading {vst_filepath}.")
|
| 150 |
-
|
| 151 |
-
# try to pass audio through the plugin
|
| 152 |
-
failed = True
|
| 153 |
-
try:
|
| 154 |
-
num_channels = 2
|
| 155 |
-
audio_input = np.random.randn(2, sample_rate * 5)
|
| 156 |
-
audio_output = plugin(audio_input, sample_rate)
|
| 157 |
-
failed = False
|
| 158 |
-
except Exception as e:
|
| 159 |
-
num_channels = 1
|
| 160 |
-
print(e)
|
| 161 |
-
print(f"Error processing audio with 2 channels.")
|
| 162 |
-
|
| 163 |
-
try:
|
| 164 |
-
audio_input = np.random.randn(1, sample_rate * 5)
|
| 165 |
-
audio_output = plugin(audio_input, sample_rate)
|
| 166 |
-
failed = False
|
| 167 |
-
except Exception as e:
|
| 168 |
-
num_channels = 2
|
| 169 |
-
print(e)
|
| 170 |
-
print(f"Failed processing audio with 1 channel.")
|
| 171 |
-
|
| 172 |
-
if failed:
|
| 173 |
-
continue
|
| 174 |
-
|
| 175 |
-
print(f"Loaded {os.path.basename(vst_filepath)} successfully.")
|
| 176 |
-
|
| 177 |
-
vst_plugins[instance_name] = {
|
| 178 |
-
"filepath": vst_filepath,
|
| 179 |
-
"instance": plugin,
|
| 180 |
-
"num_channels": num_channels,
|
| 181 |
-
}
|
| 182 |
-
|
| 183 |
-
for idx, (instance_name, vst_plugin) in enumerate(vst_plugins.items()):
|
| 184 |
-
print(idx + 1, instance_name)
|
| 185 |
-
|
| 186 |
-
return vst_plugins
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
class PluginSimilarityDataset(torch.utils.data.Dataset):
|
| 190 |
-
def __init__(
|
| 191 |
-
self,
|
| 192 |
-
audio_filepaths: List[str],
|
| 193 |
-
vst_dir: str,
|
| 194 |
-
length: int = 262144,
|
| 195 |
-
num_examples_per_epoch: int = 10000,
|
| 196 |
-
):
|
| 197 |
-
self.audio_filepaths = audio_filepaths
|
| 198 |
-
self.vst_dir = vst_dir
|
| 199 |
-
self.length = length
|
| 200 |
-
self.num_examples_per_epoch = num_examples_per_epoch
|
| 201 |
-
self.plugins = load_plugins(self.vst_dir)
|
| 202 |
-
|
| 203 |
-
def __len__(self):
|
| 204 |
-
return self.num_examples_per_epoch
|
| 205 |
-
|
| 206 |
-
def __getitem__(self, idx: int):
|
| 207 |
-
# sample the first filepath
|
| 208 |
-
filepath_a = np.random.choice(self.audio_filepaths)
|
| 209 |
-
# sample the second filepath
|
| 210 |
-
filepath_b = np.random.choice(self.audio_filepaths)
|
| 211 |
-
|
| 212 |
-
# load the audio files
|
| 213 |
-
num_frames_a = torchaudio.info(filepath_a).num_frames
|
| 214 |
-
audio_a, sr_a = get_nonsilent_region(filepath_a, num_frames_a, self.length)
|
| 215 |
-
|
| 216 |
-
num_frames_b = torchaudio.info(filepath_b).num_frames
|
| 217 |
-
audio_b, sr_b = get_nonsilent_region(filepath_b, num_frames_b, self.length)
|
| 218 |
-
|
| 219 |
-
# sample randomly a plugin from the dictionary
|
| 220 |
-
plugin_name = np.random.choice(list(self.plugins.keys()))
|
| 221 |
-
plugin = self.plugins[plugin_name]
|
| 222 |
-
|
| 223 |
-
# randomize the parameters of the plugin
|
| 224 |
-
for name, parameter in plugin["instance"].parameters.items():
|
| 225 |
-
if "bypass" in name.lower():
|
| 226 |
-
parameter.raw_value = 0
|
| 227 |
-
elif name in ["buffer_size_frames", "sample_rate_frames"]:
|
| 228 |
-
pass
|
| 229 |
-
else: # set the parameter to the preset value
|
| 230 |
-
parameter.raw_value = np.random.rand()
|
| 231 |
-
|
| 232 |
-
audio_a = conform_channels(audio_a, plugin["num_channels"])
|
| 233 |
-
audio_b = conform_channels(audio_b, plugin["num_channels"])
|
| 234 |
-
|
| 235 |
-
# apply the effect to the two audio files
|
| 236 |
-
audio_a_out = torch.from_numpy(
|
| 237 |
-
plugin["instance"].process(audio_a.numpy(), sr_a)
|
| 238 |
-
)
|
| 239 |
-
audio_b_out = torch.from_numpy(
|
| 240 |
-
plugin["instance"].process(audio_b.numpy(), sr_b)
|
| 241 |
-
)
|
| 242 |
-
|
| 243 |
-
# peak normalize and then apply random gain
|
| 244 |
-
audio_a = random_gain(audio_a)
|
| 245 |
-
audio_b = random_gain(audio_b)
|
| 246 |
-
audio_a_out = random_gain(audio_a_out)
|
| 247 |
-
audio_b_out = random_gain(audio_b_out)
|
| 248 |
-
|
| 249 |
-
# ensure all outputs are stereo
|
| 250 |
-
audio_a = conform_channels(audio_a, 2)
|
| 251 |
-
audio_b = conform_channels(audio_b, 2)
|
| 252 |
-
audio_a_out = conform_channels(audio_a_out, 2)
|
| 253 |
-
audio_b_out = conform_channels(audio_b_out, 2)
|
| 254 |
-
|
| 255 |
-
return audio_a, audio_b, audio_a_out, audio_b_out
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
class PluginSimilarityDataModule(pl.LightningDataModule):
|
| 259 |
-
def __init__(
|
| 260 |
-
self,
|
| 261 |
-
audio_dirs: List[str],
|
| 262 |
-
vst_dir: str,
|
| 263 |
-
length: int = 262144,
|
| 264 |
-
batch_size: int = 32,
|
| 265 |
-
num_train_examples: int = 10000,
|
| 266 |
-
num_val_examples: int = 1000,
|
| 267 |
-
num_workers: int = 4,
|
| 268 |
-
persistent_workers: bool = True,
|
| 269 |
-
pin_memory: bool = True,
|
| 270 |
-
) -> None:
|
| 271 |
-
super().__init__()
|
| 272 |
-
self.save_hyperparameters()
|
| 273 |
-
|
| 274 |
-
def prepare_data(self):
|
| 275 |
-
# download, split, etc...
|
| 276 |
-
pass
|
| 277 |
-
|
| 278 |
-
def setup(self, stage: Optional[str] = None):
|
| 279 |
-
# find all audio files in the input directory based on file extensions
|
| 280 |
-
audio_filepaths = []
|
| 281 |
-
for audio_dir in self.hparams.audio_dirs:
|
| 282 |
-
audio_filepaths += find_files_with_glob(audio_dir)
|
| 283 |
-
print("Found", len(audio_filepaths), "audio files in", audio_dir)
|
| 284 |
-
|
| 285 |
-
# Assign train/val datasets for use in dataloaders
|
| 286 |
-
if stage == "fit" or stage is None:
|
| 287 |
-
self.plugin_similarity_train = PluginSimilarityDataset(
|
| 288 |
-
audio_filepaths,
|
| 289 |
-
self.hparams.vst_dir,
|
| 290 |
-
self.hparams.length,
|
| 291 |
-
self.hparams.num_train_examples,
|
| 292 |
-
)
|
| 293 |
-
self.plugin_similarity_val = PluginSimilarityDataset(
|
| 294 |
-
audio_filepaths,
|
| 295 |
-
self.hparams.vst_dir,
|
| 296 |
-
self.hparams.length,
|
| 297 |
-
self.hparams.num_val_examples,
|
| 298 |
-
)
|
| 299 |
-
|
| 300 |
-
def train_dataloader(self):
|
| 301 |
-
return torch.utils.data.DataLoader(
|
| 302 |
-
self.plugin_similarity_train,
|
| 303 |
-
batch_size=self.hparams.batch_size,
|
| 304 |
-
shuffle=True,
|
| 305 |
-
num_workers=self.hparams.num_workers,
|
| 306 |
-
persistent_workers=self.hparams.persistent_workers,
|
| 307 |
-
pin_memory=self.hparams.pin_memory,
|
| 308 |
-
# worker_init_fn=worker_init_fn,
|
| 309 |
-
)
|
| 310 |
-
|
| 311 |
-
def val_dataloader(self):
|
| 312 |
-
return torch.utils.data.DataLoader(
|
| 313 |
-
self.plugin_similarity_val,
|
| 314 |
-
batch_size=self.hparams.batch_size,
|
| 315 |
-
shuffle=False,
|
| 316 |
-
num_workers=self.hparams.num_workers,
|
| 317 |
-
persistent_workers=self.hparams.persistent_workers,
|
| 318 |
-
pin_memory=self.hparams.pin_memory,
|
| 319 |
-
# worker_init_fn=worker_init_fn,
|
| 320 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/st_ito/dataset/dataset_style.py
DELETED
|
@@ -1,351 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import glob
|
| 3 |
-
import json
|
| 4 |
-
import torch
|
| 5 |
-
import tarfile
|
| 6 |
-
import torchaudio
|
| 7 |
-
import numpy as np
|
| 8 |
-
import pytorch_lightning as pl
|
| 9 |
-
|
| 10 |
-
from typing import List, Tuple, Dict, Optional
|
| 11 |
-
from st_ito.dataset.utils import tarfile_worker_init_fn, torchaudio_decode
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
class StyleTransferTarfileDataset(torch.utils.data.IterableDataset):
|
| 15 |
-
def __init__(
|
| 16 |
-
self,
|
| 17 |
-
tar_files: List[str],
|
| 18 |
-
length: int,
|
| 19 |
-
num_examples: int = 10000,
|
| 20 |
-
audio_format: str = "wav",
|
| 21 |
-
input_only: bool = False,
|
| 22 |
-
):
|
| 23 |
-
super().__init__()
|
| 24 |
-
self.tar_files = tar_files
|
| 25 |
-
self.length = length
|
| 26 |
-
self.num_examples = num_examples
|
| 27 |
-
self.audio_format = audio_format
|
| 28 |
-
self.input_only = input_only
|
| 29 |
-
|
| 30 |
-
# get the tar file handles
|
| 31 |
-
self.tar_handles = []
|
| 32 |
-
for tar_file in tar_files:
|
| 33 |
-
tar_handle = tarfile.open(tar_file, "r:")
|
| 34 |
-
tar_handle.next() # skip the first root directory
|
| 35 |
-
tar_handle.next() # skip the first example directory
|
| 36 |
-
self.tar_handles.append(tar_handle)
|
| 37 |
-
|
| 38 |
-
def __len__(self):
|
| 39 |
-
return self.num_examples
|
| 40 |
-
|
| 41 |
-
def next_example(self):
|
| 42 |
-
while True:
|
| 43 |
-
# select tar file at random
|
| 44 |
-
tar_index = np.random.choice(len(self.tar_handles))
|
| 45 |
-
|
| 46 |
-
# get the next member (should be root directory of this example)
|
| 47 |
-
members = {}
|
| 48 |
-
done = False
|
| 49 |
-
handle_reset = False
|
| 50 |
-
while not done:
|
| 51 |
-
# get next member
|
| 52 |
-
member = self.tar_handles[tar_index].next()
|
| 53 |
-
|
| 54 |
-
if member is None:
|
| 55 |
-
# we have reached the end of this tar file
|
| 56 |
-
# so, we will close it and remove open tar handle
|
| 57 |
-
|
| 58 |
-
# close this tar handle
|
| 59 |
-
self.tar_handles[tar_index].close()
|
| 60 |
-
|
| 61 |
-
# reopen
|
| 62 |
-
tar_handle = tarfile.open(self.tar_files[tar_index], "r:")
|
| 63 |
-
tar_handle.next() # skip the first root directory
|
| 64 |
-
tar_handle.next() # skip the first example directory
|
| 65 |
-
self.tar_handles[tar_index] = tar_handle # replace handle
|
| 66 |
-
print(f"Resetting tar handle {self.tar_files[tar_index]}.")
|
| 67 |
-
handle_reset = True
|
| 68 |
-
break
|
| 69 |
-
|
| 70 |
-
if member.isdir():
|
| 71 |
-
done = True
|
| 72 |
-
break
|
| 73 |
-
|
| 74 |
-
members[os.path.basename(member.name)] = member
|
| 75 |
-
|
| 76 |
-
if handle_reset:
|
| 77 |
-
continue
|
| 78 |
-
|
| 79 |
-
# get input file
|
| 80 |
-
input_file = self.tar_handles[tar_index].extractfile(
|
| 81 |
-
members[f"input.{self.audio_format}"]
|
| 82 |
-
)
|
| 83 |
-
audio_input = torchaudio_decode(input_file)
|
| 84 |
-
|
| 85 |
-
if self.input_only:
|
| 86 |
-
|
| 87 |
-
num_frames = audio_input.shape[1]
|
| 88 |
-
start_frame = torch.randint(0, num_frames - self.length, (1,)).item()
|
| 89 |
-
audio_input = audio_input[:, start_frame : start_frame + self.length]
|
| 90 |
-
|
| 91 |
-
# we will generate the output in the system
|
| 92 |
-
audio_output = torch.empty_like(audio_input)
|
| 93 |
-
params = torch.empty(1)
|
| 94 |
-
|
| 95 |
-
else:
|
| 96 |
-
# get the output file
|
| 97 |
-
output_file = self.tar_handles[tar_index].extractfile(
|
| 98 |
-
members[f"output.{self.audio_format}"]
|
| 99 |
-
)
|
| 100 |
-
audio_output = torchaudio_decode(output_file)
|
| 101 |
-
|
| 102 |
-
# get the params file
|
| 103 |
-
params_file = self.tar_handles[tar_index].extractfile(
|
| 104 |
-
members["params.json"]
|
| 105 |
-
)
|
| 106 |
-
params = json.load(params_file)
|
| 107 |
-
# convert params to tensor
|
| 108 |
-
params = torch.tensor(
|
| 109 |
-
[val for val in params.values()], dtype=torch.float32
|
| 110 |
-
)
|
| 111 |
-
|
| 112 |
-
# crop the audio to the desired length
|
| 113 |
-
num_frames = audio_input.shape[1]
|
| 114 |
-
start_frame = torch.randint(0, num_frames - self.length, (1,)).item()
|
| 115 |
-
audio_input = audio_input[:, start_frame : start_frame + self.length]
|
| 116 |
-
audio_output = audio_output[:, start_frame : start_frame + self.length]
|
| 117 |
-
|
| 118 |
-
# check for silence in the output
|
| 119 |
-
input_energy = torch.mean(audio_input**2, dim=1)
|
| 120 |
-
output_energy = torch.mean(audio_output**2, dim=1)
|
| 121 |
-
|
| 122 |
-
if output_energy < 1e-6 or input_energy < 1e-6:
|
| 123 |
-
# skip this example
|
| 124 |
-
continue
|
| 125 |
-
|
| 126 |
-
yield audio_input, audio_output, params
|
| 127 |
-
|
| 128 |
-
def __iter__(self):
|
| 129 |
-
return iter(self.next_example())
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
class StyleTransferTarfileDataModule(pl.LightningDataModule):
|
| 133 |
-
def __init__(
|
| 134 |
-
self,
|
| 135 |
-
train_tarfiles: List[str],
|
| 136 |
-
val_tarfiles: List[str],
|
| 137 |
-
batch_size: int,
|
| 138 |
-
num_workers: int,
|
| 139 |
-
input_only: bool = False,
|
| 140 |
-
length: int = 262144,
|
| 141 |
-
test_tarfiles: List[str] = None,
|
| 142 |
-
pin_memory: bool = False,
|
| 143 |
-
persistent_workers: bool = True,
|
| 144 |
-
num_train_examples: int = 20000,
|
| 145 |
-
num_val_examples: int = 2000,
|
| 146 |
-
num_test_examples: int = 2000,
|
| 147 |
-
audio_format: str = "wav",
|
| 148 |
-
) -> None:
|
| 149 |
-
super().__init__()
|
| 150 |
-
""" Pytorch Lightning DataModule for loading data from tar files.
|
| 151 |
-
|
| 152 |
-
Args:
|
| 153 |
-
train_tarfiles (List[str]): List of tar files to use for training.
|
| 154 |
-
val_tarfiles (List[str]): List of tar files to use for validation.
|
| 155 |
-
batch_size (int): Batch size.
|
| 156 |
-
num_workers (int): Number of workers.
|
| 157 |
-
input_only (bool): Whether to use only the input audio. Defaults to False.
|
| 158 |
-
length (int): Length of audio to return. Defaults to 262144.
|
| 159 |
-
test_tarfiles (List[str]): List of tar files to use for testing.
|
| 160 |
-
pin_memory (bool): Whether to pin memory. Defaults to False.
|
| 161 |
-
persistent_workers (bool): Whether to use persistent workers. Defaults to True.
|
| 162 |
-
num_train_examples (int): Number of training examples to return. Defaults to 20000.
|
| 163 |
-
num_val_examples (int): Number of validation examples to return. Defaults to 2000.
|
| 164 |
-
num_test_examples (int): Number of testing examples to return. Defaults to 2000.
|
| 165 |
-
audio_format (str): Audio format to use. Defaults to "wav".
|
| 166 |
-
"""
|
| 167 |
-
self.save_hyperparameters()
|
| 168 |
-
|
| 169 |
-
def setup(self, stage=None):
|
| 170 |
-
self.train_dataset = StyleTransferTarfileDataset(
|
| 171 |
-
self.hparams.train_tarfiles,
|
| 172 |
-
self.hparams.length,
|
| 173 |
-
self.hparams.num_train_examples,
|
| 174 |
-
self.hparams.audio_format,
|
| 175 |
-
self.hparams.input_only,
|
| 176 |
-
)
|
| 177 |
-
|
| 178 |
-
self.val_dataset = StyleTransferTarfileDataset(
|
| 179 |
-
self.hparams.val_tarfiles,
|
| 180 |
-
self.hparams.length,
|
| 181 |
-
self.hparams.num_val_examples,
|
| 182 |
-
self.hparams.audio_format,
|
| 183 |
-
self.hparams.input_only,
|
| 184 |
-
)
|
| 185 |
-
|
| 186 |
-
if self.hparams.test_tarfiles is not None:
|
| 187 |
-
self.test_dataset = StyleTransferTarfileDataset(
|
| 188 |
-
self.hparams.test_tarfiles,
|
| 189 |
-
self.hparams.length,
|
| 190 |
-
self.hparams.num_test_examples,
|
| 191 |
-
self.hparams.audio_format,
|
| 192 |
-
self.hparams.input_only,
|
| 193 |
-
)
|
| 194 |
-
|
| 195 |
-
def train_dataloader(self):
|
| 196 |
-
return torch.utils.data.DataLoader(
|
| 197 |
-
self.train_dataset,
|
| 198 |
-
batch_size=self.hparams.batch_size,
|
| 199 |
-
num_workers=self.hparams.num_workers,
|
| 200 |
-
drop_last=True,
|
| 201 |
-
pin_memory=self.hparams.pin_memory,
|
| 202 |
-
persistent_workers=self.hparams.persistent_workers,
|
| 203 |
-
worker_init_fn=tarfile_worker_init_fn,
|
| 204 |
-
)
|
| 205 |
-
|
| 206 |
-
def val_dataloader(self):
|
| 207 |
-
return torch.utils.data.DataLoader(
|
| 208 |
-
self.val_dataset,
|
| 209 |
-
batch_size=self.hparams.batch_size,
|
| 210 |
-
num_workers=self.hparams.num_workers,
|
| 211 |
-
pin_memory=self.hparams.pin_memory,
|
| 212 |
-
persistent_workers=self.hparams.persistent_workers,
|
| 213 |
-
worker_init_fn=tarfile_worker_init_fn,
|
| 214 |
-
)
|
| 215 |
-
|
| 216 |
-
def test_dataloader(self):
|
| 217 |
-
return torch.utils.data.DataLoader(
|
| 218 |
-
self.val_dataset,
|
| 219 |
-
batch_size=self.hparams.batch_size,
|
| 220 |
-
num_workers=self.hparams.num_workers,
|
| 221 |
-
persistent_workers=self.hparams.persistent_workers,
|
| 222 |
-
worker_init_fn=tarfile_worker_init_fn,
|
| 223 |
-
)
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
# ------------------------------ File based dataset --------------------------------
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
class StyleTransferDataset(torch.utils.data.Dataset):
|
| 230 |
-
def __init__(
|
| 231 |
-
self,
|
| 232 |
-
root_dirs: List[str],
|
| 233 |
-
length: int,
|
| 234 |
-
):
|
| 235 |
-
super().__init__()
|
| 236 |
-
self.root_dirs = root_dirs
|
| 237 |
-
self.length = length
|
| 238 |
-
|
| 239 |
-
self.examples = []
|
| 240 |
-
for root_dir in root_dirs:
|
| 241 |
-
# get all examples in the root dir
|
| 242 |
-
print("Searching for examples in", root_dir)
|
| 243 |
-
examples = glob.glob(os.path.join(root_dir, "*"))
|
| 244 |
-
self.examples.extend(examples)
|
| 245 |
-
|
| 246 |
-
# for testing use only the first 100 examples
|
| 247 |
-
# self.examples = self.examples[:100]
|
| 248 |
-
# repeat those examples 10 times
|
| 249 |
-
# self.examples = self.examples * 100
|
| 250 |
-
|
| 251 |
-
print(f"Found {len(self.examples)} examples.")
|
| 252 |
-
|
| 253 |
-
def __len__(self):
|
| 254 |
-
return len(self.examples)
|
| 255 |
-
|
| 256 |
-
def __getitem__(self, idx):
|
| 257 |
-
example = self.examples[idx]
|
| 258 |
-
|
| 259 |
-
input_filepath = os.path.join(example, "input.wav")
|
| 260 |
-
output_filepath = os.path.join(example, "output.wav")
|
| 261 |
-
params_filepath = os.path.join(example, "params.json")
|
| 262 |
-
|
| 263 |
-
# check the length of the audio
|
| 264 |
-
md = torchaudio.info(input_filepath, backend="soundfile")
|
| 265 |
-
num_frames = md.num_frames
|
| 266 |
-
|
| 267 |
-
# pick random start frame
|
| 268 |
-
start_frame = torch.randint(0, num_frames - self.length, (1,)).item()
|
| 269 |
-
# start_frame = 0 # for now, just use the first frame
|
| 270 |
-
|
| 271 |
-
# load the audio
|
| 272 |
-
input_audio, sr = torchaudio.load(
|
| 273 |
-
input_filepath,
|
| 274 |
-
frame_offset=start_frame,
|
| 275 |
-
num_frames=self.length,
|
| 276 |
-
backend="soundfile",
|
| 277 |
-
)
|
| 278 |
-
output_audio, sr = torchaudio.load(
|
| 279 |
-
output_filepath,
|
| 280 |
-
frame_offset=start_frame,
|
| 281 |
-
num_frames=self.length,
|
| 282 |
-
backend="soundfile",
|
| 283 |
-
)
|
| 284 |
-
|
| 285 |
-
# load the params
|
| 286 |
-
with open(params_filepath, "r") as fp:
|
| 287 |
-
params = json.load(fp)
|
| 288 |
-
|
| 289 |
-
# convert params to tensor
|
| 290 |
-
params = torch.tensor([val for val in params.values()], dtype=torch.float32)
|
| 291 |
-
|
| 292 |
-
return input_audio, output_audio, params
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
class StyleTransferDataModule(pl.LightningDataModule):
|
| 296 |
-
def __init__(
|
| 297 |
-
self,
|
| 298 |
-
train_root_dirs: List[str],
|
| 299 |
-
val_root_dirs: List[str],
|
| 300 |
-
batch_size: int,
|
| 301 |
-
num_workers: int,
|
| 302 |
-
length: int = 262144,
|
| 303 |
-
test_root_dirs: List[str] = None,
|
| 304 |
-
pin_memory: bool = False,
|
| 305 |
-
persistent_workers: bool = False,
|
| 306 |
-
) -> None:
|
| 307 |
-
super().__init__()
|
| 308 |
-
self.save_hyperparameters()
|
| 309 |
-
|
| 310 |
-
def setup(self, stage=None):
|
| 311 |
-
self.train_dataset = StyleTransferDataset(
|
| 312 |
-
self.hparams.train_root_dirs,
|
| 313 |
-
self.hparams.length,
|
| 314 |
-
)
|
| 315 |
-
self.val_dataset = StyleTransferDataset(
|
| 316 |
-
self.hparams.val_root_dirs,
|
| 317 |
-
self.hparams.length,
|
| 318 |
-
)
|
| 319 |
-
if self.hparams.test_root_dirs is not None:
|
| 320 |
-
self.test_dataset = StyleTransferDataset(
|
| 321 |
-
self.hparams.test_root_dirs,
|
| 322 |
-
self.hparams.length,
|
| 323 |
-
)
|
| 324 |
-
|
| 325 |
-
def train_dataloader(self):
|
| 326 |
-
return torch.utils.data.DataLoader(
|
| 327 |
-
self.train_dataset,
|
| 328 |
-
batch_size=self.hparams.batch_size,
|
| 329 |
-
shuffle=True,
|
| 330 |
-
num_workers=self.hparams.num_workers,
|
| 331 |
-
drop_last=True,
|
| 332 |
-
pin_memory=self.hparams.pin_memory,
|
| 333 |
-
persistent_workers=self.hparams.persistent_workers,
|
| 334 |
-
)
|
| 335 |
-
|
| 336 |
-
def val_dataloader(self):
|
| 337 |
-
return torch.utils.data.DataLoader(
|
| 338 |
-
self.val_dataset,
|
| 339 |
-
batch_size=self.hparams.batch_size,
|
| 340 |
-
num_workers=self.hparams.num_workers,
|
| 341 |
-
pin_memory=self.hparams.pin_memory,
|
| 342 |
-
persistent_workers=self.hparams.persistent_workers,
|
| 343 |
-
)
|
| 344 |
-
|
| 345 |
-
def test_dataloader(self):
|
| 346 |
-
return torch.utils.data.DataLoader(
|
| 347 |
-
self.val_dataset,
|
| 348 |
-
batch_size=self.hparams.batch_size,
|
| 349 |
-
num_workers=self.hparams.num_workers,
|
| 350 |
-
persistent_workers=self.hparams.persistent_workers,
|
| 351 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/st_ito/dataset/utils.py
DELETED
|
@@ -1,25 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import tarfile
|
| 3 |
-
import torchaudio
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
def tarfile_worker_init_fn(worker_id):
|
| 7 |
-
worker_info = torch.utils.data.get_worker_info()
|
| 8 |
-
dataset = worker_info.dataset # the dataset copy in this worker process
|
| 9 |
-
|
| 10 |
-
# if there is more than one tar file in the dataset, then we need to
|
| 11 |
-
# select one tar file for this worker
|
| 12 |
-
print(f"Worker {worker_id} creating new tar file handles.")
|
| 13 |
-
|
| 14 |
-
# reopen the tarfile handles for this worker
|
| 15 |
-
dataset.tar_handles = []
|
| 16 |
-
for tar_file in dataset.tar_files:
|
| 17 |
-
tar_handle = tarfile.open(tar_file, "r:")
|
| 18 |
-
tar_handle.next() # skip the first root directory
|
| 19 |
-
tar_handle.next() # skip the first example directory
|
| 20 |
-
dataset.tar_handles.append(tar_handle)
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
def torchaudio_decode(b):
|
| 24 |
-
x, sr = torchaudio.load(b, format="FLAC")
|
| 25 |
-
return x
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|