Spaces:
Running
Running
import numpy as np | |
import torch | |
from torch import nn as nn | |
from torchvision.ops.misc import FrozenBatchNorm2d | |
import logging | |
from tqdm import tqdm | |
import random | |
import json | |
import os | |
import pathlib | |
# TODO: (yusong) this not a good place to store those information and does not scale. Need to be fixed later. | |
dataset_split = { | |
"audiocaps": ["train", "valid", "test"], | |
"audioset": ["balanced_train", "unbalanced_train", "eval"], | |
"BBCSoundEffects": ["train", "test"], | |
"Clotho": ["train", "test", "valid"], | |
"free_to_use_sounds": ["train", "test"], | |
"paramount_motion": ["train", "test"], | |
"sonniss_game_effects": ["train", "test"], | |
"wesoundeffects": ["train", "test"], | |
"MACS": ["train", "test"], | |
"freesound": ["train", "test"], | |
"FSD50K": ["train", "test", "valid"], | |
"fsd50k_class_label": ["train", "test", "valid"], | |
"esc50": ["train", "test"], | |
"audiostock": ["train", "test"], | |
"freesound_no_overlap_noesc50": ["train", "test"], | |
"epidemic_sound_effects": ["train", "test"], | |
"VGGSound": ["train", "test"], | |
"urbansound8k_class_label": ["train", "test"], | |
"audioset_t5": ["balanced_train", "unbalanced_train", "eval"], | |
"epidemic_sound_effects_t5": ["train", "test"], | |
"WavText5K": ["train", "test"], | |
"esc50_no_overlap": ["train", "test"], | |
"usd8k_no_overlap": ["train", "test"], | |
"fsd50k_200_class_label": ["train", "test", "valid"], | |
} | |
def freeze_batch_norm_2d(module, module_match={}, name=""): | |
""" | |
Converts all `BatchNorm2d` and `SyncBatchNorm` layers of provided module into `FrozenBatchNorm2d`. If `module` is | |
itself an instance of either `BatchNorm2d` or `SyncBatchNorm`, it is converted into `FrozenBatchNorm2d` and | |
returned. Otherwise, the module is walked recursively and submodules are converted in place. | |
Args: | |
module (torch.nn.Module): Any PyTorch module. | |
module_match (dict): Dictionary of full module names to freeze (all if empty) | |
name (str): Full module name (prefix) | |
Returns: | |
torch.nn.Module: Resulting module | |
Inspired by https://github.com/pytorch/pytorch/blob/a5895f85be0f10212791145bfedc0261d364f103/torch/nn/modules/batchnorm.py#L762 | |
""" | |
res = module | |
is_match = True | |
if module_match: | |
is_match = name in module_match | |
if is_match and isinstance( | |
module, (nn.modules.batchnorm.BatchNorm2d, nn.modules.batchnorm.SyncBatchNorm) | |
): | |
res = FrozenBatchNorm2d(module.num_features) | |
res.num_features = module.num_features | |
res.affine = module.affine | |
if module.affine: | |
res.weight.data = module.weight.data.clone().detach() | |
res.bias.data = module.bias.data.clone().detach() | |
res.running_mean.data = module.running_mean.data | |
res.running_var.data = module.running_var.data | |
res.eps = module.eps | |
else: | |
for child_name, child in module.named_children(): | |
full_child_name = ".".join([name, child_name]) if name else child_name | |
new_child = freeze_batch_norm_2d(child, module_match, full_child_name) | |
if new_child is not child: | |
res.add_module(child_name, new_child) | |
return res | |
def exist(dataset_name, dataset_type): | |
""" | |
Check if dataset exists | |
""" | |
if dataset_type in dataset_split[dataset_name]: | |
return True | |
else: | |
return False | |
def get_tar_path_from_dataset_name( | |
dataset_names, dataset_types, islocal, dataset_path, proportion=1, full_dataset=None | |
): | |
""" | |
Get tar path from dataset name and type | |
""" | |
output = [] | |
for n in dataset_names: | |
if full_dataset is not None and n in full_dataset: | |
current_dataset_types = dataset_split[n] | |
else: | |
current_dataset_types = dataset_types | |
for s in current_dataset_types: | |
tmp = [] | |
if islocal: | |
sizefilepath_ = f"{dataset_path}/{n}/{s}/sizes.json" | |
if not os.path.exists(sizefilepath_): | |
sizefilepath_ = f"./json_files/{n}/{s}/sizes.json" | |
else: | |
sizefilepath_ = f"./json_files/{n}/{s}/sizes.json" | |
if not os.path.exists(sizefilepath_): | |
continue | |
sizes = json.load(open(sizefilepath_, "r")) | |
for k in sizes.keys(): | |
if islocal: | |
tmp.append(f"{dataset_path}/{n}/{s}/{k}") | |
else: | |
tmp.append( | |
f"pipe:aws s3 --cli-connect-timeout 0 cp s3://s-laion-audio/webdataset_tar/{n}/{s}/{k} -" | |
) | |
if proportion != 1: | |
tmp = random.sample(tmp, int(proportion * len(tmp))) | |
output.append(tmp) | |
return sum(output, []) | |
def get_tar_path_from_txts(txt_path, islocal, proportion=1): | |
""" | |
Get tar path from txt path | |
""" | |
if isinstance(txt_path, (list, tuple)): | |
return sum( | |
[ | |
get_tar_path_from_txts( | |
txt_path[i], islocal=islocal, proportion=proportion | |
) | |
for i in range(len(txt_path)) | |
], | |
[], | |
) | |
if isinstance(txt_path, str): | |
with open(txt_path) as f: | |
lines = f.readlines() | |
if islocal: | |
lines = [ | |
lines[i] | |
.split("\n")[0] | |
.replace("pipe:aws s3 cp s3://s-laion-audio/", "/mnt/audio_clip/") | |
for i in range(len(lines)) | |
] | |
else: | |
lines = [ | |
lines[i].split("\n")[0].replace(".tar", ".tar -") | |
for i in range(len(lines)) | |
] | |
if proportion != 1: | |
print("Sampling tars with proportion of {}".format(proportion)) | |
lines = random.sample(lines, int(proportion * len(lines))) | |
return lines | |
def get_mix_lambda(mixup_alpha, batch_size): | |
mixup_lambdas = [ | |
np.random.beta(mixup_alpha, mixup_alpha, 1)[0] for _ in range(batch_size) | |
] | |
return np.array(mixup_lambdas).astype(np.float32) | |
def do_mixup(x, mixup_lambda): | |
""" | |
Args: | |
x: (batch_size , ...) | |
mixup_lambda: (batch_size,) | |
Returns: | |
out: (batch_size, ...) | |
""" | |
out = ( | |
x.transpose(0, -1) * mixup_lambda | |
+ torch.flip(x, dims=[0]).transpose(0, -1) * (1 - mixup_lambda) | |
).transpose(0, -1) | |
return out | |
def interpolate(x, ratio): | |
"""Interpolate data in time domain. This is used to compensate the | |
resolution reduction in downsampling of a CNN. | |
Args: | |
x: (batch_size, time_steps, classes_num) | |
ratio: int, ratio to interpolate | |
Returns: | |
upsampled: (batch_size, time_steps * ratio, classes_num) | |
""" | |
(batch_size, time_steps, classes_num) = x.shape | |
upsampled = x[:, :, None, :].repeat(1, 1, ratio, 1) | |
upsampled = upsampled.reshape(batch_size, time_steps * ratio, classes_num) | |
return upsampled | |
def pad_framewise_output(framewise_output, frames_num): | |
"""Pad framewise_output to the same length as input frames. The pad value | |
is the same as the value of the last frame. | |
Args: | |
framewise_output: (batch_size, frames_num, classes_num) | |
frames_num: int, number of frames to pad | |
Outputs: | |
output: (batch_size, frames_num, classes_num) | |
""" | |
pad = framewise_output[:, -1:, :].repeat( | |
1, frames_num - framewise_output.shape[1], 1 | |
) | |
"""tensor for padding""" | |
output = torch.cat((framewise_output, pad), dim=1) | |
"""(batch_size, frames_num, classes_num)""" | |
# def process_ipc(index_path, classes_num, filename): | |
# # load data | |
# logging.info("Load Data...............") | |
# ipc = [[] for _ in range(classes_num)] | |
# with h5py.File(index_path, "r") as f: | |
# for i in tqdm(range(len(f["target"]))): | |
# t_class = np.where(f["target"][i])[0] | |
# for t in t_class: | |
# ipc[t].append(i) | |
# print(ipc) | |
# np.save(filename, ipc) | |
# logging.info("Load Data Succeed...............") | |
def save_to_dict(s, o_={}): | |
sp = s.split(": ") | |
o_.update({sp[0]: float(sp[1])}) | |
return o_ | |
def get_data_from_log(txt_path): | |
""" | |
Output dictionary from out.txt log file | |
""" | |
with open(txt_path) as f: | |
lines = f.readlines() | |
val_data = {} | |
train_data = {} | |
train_losses = [] | |
train_losses_epoch = [] | |
for i in range(len(lines)): | |
if "| INFO |" in lines[i]: | |
if "Eval Epoch" in lines[i]: | |
if "val_loss" in lines[i]: | |
# float(regex.sub("", lines[310].split(" ")[-1]).replace(" ", "")) | |
line = lines[i].split("Eval Epoch: ")[-1] | |
num_epoch = int(line.split(" ")[0].split(" ")[0]) | |
d = { | |
line.split(" ")[0] | |
.split(" ")[1] | |
.replace(":", ""): float(line.split(" ")[0].split(" ")[-1]) | |
} | |
for i in range(1, len(line.split(" "))): | |
d = save_to_dict(line.split(" ")[i], d) | |
val_data[num_epoch] = d | |
elif "Train Epoch" in lines[i]: | |
num_epoch = int(lines[i].split("Train Epoch: ")[1][0]) | |
loss = float(lines[i].split("Loss: ")[-1].split(" (")[0]) | |
train_losses.append(loss) | |
train_losses_epoch.append(num_epoch) | |
for i in range(len(train_losses)): | |
train_data[i] = { | |
"num_epoch": train_losses_epoch[i], | |
"train_loss": train_losses[i], | |
} | |
return train_data, val_data | |
def save_p(obj, filename): | |
import pickle | |
try: | |
from deepdiff import DeepDiff | |
except: | |
os.system("pip install deepdiff") | |
from deepdiff import DeepDiff | |
with open(filename, "wb") as file: | |
pickle.dump(obj, file, protocol=pickle.HIGHEST_PROTOCOL) # highest protocol | |
with open(filename, "rb") as file: | |
z = pickle.load(file) | |
assert ( | |
DeepDiff(obj, z, ignore_string_case=True) == {} | |
), "there is something wrong with the saving process" | |
return | |
def load_p(filename): | |
import pickle | |
with open(filename, "rb") as file: | |
z = pickle.load(file) | |
return z | |
def save_json(data, name="data.json"): | |
import json | |
with open(name, "w") as fp: | |
json.dump(data, fp) | |
return | |
def load_json(name): | |
import json | |
with open(name, "r") as fp: | |
data = json.load(fp) | |
return data | |
def load_class_label(path): | |
# https://stackoverflow.com/questions/48004243/how-to-share-large-read-only-dictionary-list-across-processes-in-multiprocessing | |
# https://stackoverflow.com/questions/45693949/storing-strings-in-a-multiprocessing-sharedctypes-array | |
out = None | |
if path is not None: | |
if pathlib.Path(path).suffix in [".pkl", ".pickle"]: | |
out = load_p(path) | |
elif pathlib.Path(path).suffix in [".json", ".txt"]: | |
out = load_json(path) | |
elif pathlib.Path(path).suffix in [".npy", ".npz"]: | |
out = np.load(path) | |
elif pathlib.Path(path).suffix in [".csv"]: | |
import pandas as pd | |
out = pd.read_csv(path) | |
return out | |
# if out is None: | |
# return None | |
# else: | |
# key = Array(c_wchar, '\n'.join(list(out.keys())), lock=False) | |
# val = Array('i', out.values(), lock=False) | |
# return (key, val) | |
from torch import optim | |
def get_optimizer(params, lr, betas, eps, momentum, optimizer_name): | |
if optimizer_name.lower() == "adamw": | |
optimizer = optim.AdamW(params, lr=lr, betas=betas, eps=eps) | |
elif optimizer_name.lower() == "sgd": | |
optimizer = optim.SGD(params, lr=lr, momentum=momentum) | |
elif optimizer_name.lower() == "adam": | |
optimizer = optim.Adam(params, lr=lr, betas=betas, eps=eps) | |
else: | |
raise ValueError("optimizer name is not correct") | |
return optimizer | |