| |
| |
| |
| |
|
|
|
|
| import collections |
| import glob |
| import os |
| import random |
| import time |
| import argparse |
| from collections import OrderedDict |
|
|
| import json5 |
| import numpy as np |
| import glob |
| from torch.nn import functional as F |
|
|
|
|
| try: |
| from ruamel.yaml import YAML as yaml |
| except: |
| from ruamel_yaml import YAML as yaml |
|
|
| import torch |
|
|
| from utils.hparam import HParams |
| import logging |
| from logging import handlers |
|
|
|
|
| def str2bool(v): |
| """Used in argparse.ArgumentParser.add_argument to indicate |
| that a type is a bool type and user can enter |
| |
| - yes, true, t, y, 1, to represent True |
| - no, false, f, n, 0, to represent False |
| |
| See https://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse # noqa |
| """ |
| if isinstance(v, bool): |
| return v |
| if v.lower() in ("yes", "true", "t", "y", "1"): |
| return True |
| elif v.lower() in ("no", "false", "f", "n", "0"): |
| return False |
| else: |
| raise argparse.ArgumentTypeError("Boolean value expected.") |
|
|
|
|
| def find_checkpoint_of_mapper(mapper_ckpt_dir): |
| mapper_ckpts = glob.glob(os.path.join(mapper_ckpt_dir, "ckpts/*.pt")) |
|
|
| |
| mapper_ckpts.sort() |
| mapper_weights_file = mapper_ckpts[-1] |
| return mapper_weights_file |
|
|
|
|
| def pad_f0_to_tensors(f0s, batched=None): |
| |
| tensors = [] |
|
|
| if batched == None: |
| |
| size = -1 |
| for f0 in f0s: |
| size = max(size, f0.shape[-1]) |
|
|
| tensor = torch.zeros(len(f0s), size) |
|
|
| for i, f0 in enumerate(f0s): |
| tensor[i, : f0.shape[-1]] = f0[:] |
|
|
| tensors.append(tensor) |
| else: |
| start = 0 |
| while start + batched - 1 < len(f0s): |
| end = start + batched - 1 |
|
|
| |
| size = -1 |
| for i in range(start, end + 1): |
| size = max(size, f0s[i].shape[-1]) |
|
|
| tensor = torch.zeros(batched, size) |
|
|
| for i in range(start, end + 1): |
| tensor[i - start, : f0s[i].shape[-1]] = f0s[i][:] |
|
|
| tensors.append(tensor) |
|
|
| start = start + batched |
|
|
| if start != len(f0s): |
| end = len(f0s) |
|
|
| |
| size = -1 |
| for i in range(start, end): |
| size = max(size, f0s[i].shape[-1]) |
|
|
| tensor = torch.zeros(len(f0s) - start, size) |
|
|
| for i in range(start, end): |
| tensor[i - start, : f0s[i].shape[-1]] = f0s[i][:] |
|
|
| tensors.append(tensor) |
|
|
| return tensors |
|
|
|
|
| def pad_mels_to_tensors(mels, batched=None): |
| """ |
| Args: |
| mels: A list of mel-specs |
| Returns: |
| tensors: A list of tensors containing the batched mel-specs |
| mel_frames: A list of tensors containing the frames of the original mel-specs |
| """ |
| |
| tensors = [] |
| mel_frames = [] |
|
|
| |
| if batched == None: |
| |
| size = -1 |
| for mel in mels: |
| size = max(size, mel.shape[-1]) |
|
|
| tensor = torch.zeros(len(mels), mels[0].shape[0], size) |
| mel_frame = torch.zeros(len(mels), dtype=torch.int32) |
|
|
| for i, mel in enumerate(mels): |
| tensor[i, :, : mel.shape[-1]] = mel[:] |
| mel_frame[i] = mel.shape[-1] |
|
|
| tensors.append(tensor) |
| mel_frames.append(mel_frame) |
| else: |
| start = 0 |
| while start + batched - 1 < len(mels): |
| end = start + batched - 1 |
|
|
| |
| size = -1 |
| for i in range(start, end + 1): |
| size = max(size, mels[i].shape[-1]) |
|
|
| tensor = torch.zeros(batched, mels[0].shape[0], size) |
| mel_frame = torch.zeros(batched, dtype=torch.int32) |
|
|
| for i in range(start, end + 1): |
| tensor[i - start, :, : mels[i].shape[-1]] = mels[i][:] |
| mel_frame[i - start] = mels[i].shape[-1] |
|
|
| tensors.append(tensor) |
| mel_frames.append(mel_frame) |
|
|
| start = start + batched |
|
|
| if start != len(mels): |
| end = len(mels) |
|
|
| |
| size = -1 |
| for i in range(start, end): |
| size = max(size, mels[i].shape[-1]) |
|
|
| tensor = torch.zeros(len(mels) - start, mels[0].shape[0], size) |
| mel_frame = torch.zeros(len(mels) - start, dtype=torch.int32) |
|
|
| for i in range(start, end): |
| tensor[i - start, :, : mels[i].shape[-1]] = mels[i][:] |
| mel_frame[i - start] = mels[i].shape[-1] |
|
|
| tensors.append(tensor) |
| mel_frames.append(mel_frame) |
|
|
| return tensors, mel_frames |
|
|
|
|
| def load_model_config(args): |
| """Load model configurations (in args.json under checkpoint directory) |
| |
| Args: |
| args (ArgumentParser): arguments to run bins/preprocess.py |
| |
| Returns: |
| dict: dictionary that stores model configurations |
| """ |
| if args.checkpoint_dir is None: |
| assert args.checkpoint_file is not None |
| checkpoint_dir = os.path.split(args.checkpoint_file)[0] |
| else: |
| checkpoint_dir = args.checkpoint_dir |
| config_path = os.path.join(checkpoint_dir, "args.json") |
| print("config_path: ", config_path) |
|
|
| config = load_config(config_path) |
| return config |
|
|
|
|
| def remove_and_create(dir): |
| if os.path.exists(dir): |
| os.system("rm -r {}".format(dir)) |
| os.makedirs(dir, exist_ok=True) |
|
|
|
|
| def has_existed(path, warning=False): |
| if not warning: |
| return os.path.exists(path) |
|
|
| if os.path.exists(path): |
| answer = input( |
| "The path {} has existed. \nInput 'y' (or hit Enter) to skip it, and input 'n' to re-write it [y/n]\n".format( |
| path |
| ) |
| ) |
| if not answer == "n": |
| return True |
|
|
| return False |
|
|
|
|
| def remove_older_ckpt(saved_model_name, checkpoint_dir, max_to_keep=5): |
| if os.path.exists(os.path.join(checkpoint_dir, "checkpoint")): |
| with open(os.path.join(checkpoint_dir, "checkpoint"), "r") as f: |
| ckpts = [x.strip() for x in f.readlines()] |
| else: |
| ckpts = [] |
| ckpts.append(saved_model_name) |
| for item in ckpts[:-max_to_keep]: |
| if os.path.exists(os.path.join(checkpoint_dir, item)): |
| os.remove(os.path.join(checkpoint_dir, item)) |
| with open(os.path.join(checkpoint_dir, "checkpoint"), "w") as f: |
| for item in ckpts[-max_to_keep:]: |
| f.write("{}\n".format(item)) |
|
|
|
|
| def set_all_random_seed(seed: int): |
| random.seed(seed) |
| np.random.seed(seed) |
| torch.random.manual_seed(seed) |
|
|
|
|
| def save_checkpoint( |
| args, |
| generator, |
| g_optimizer, |
| step, |
| discriminator=None, |
| d_optimizer=None, |
| max_to_keep=5, |
| ): |
| saved_model_name = "model.ckpt-{}.pt".format(step) |
| checkpoint_path = os.path.join(args.checkpoint_dir, saved_model_name) |
|
|
| if discriminator and d_optimizer: |
| torch.save( |
| { |
| "generator": generator.state_dict(), |
| "discriminator": discriminator.state_dict(), |
| "g_optimizer": g_optimizer.state_dict(), |
| "d_optimizer": d_optimizer.state_dict(), |
| "global_step": step, |
| }, |
| checkpoint_path, |
| ) |
| else: |
| torch.save( |
| { |
| "generator": generator.state_dict(), |
| "g_optimizer": g_optimizer.state_dict(), |
| "global_step": step, |
| }, |
| checkpoint_path, |
| ) |
|
|
| print("Saved checkpoint: {}".format(checkpoint_path)) |
|
|
| if os.path.exists(os.path.join(args.checkpoint_dir, "checkpoint")): |
| with open(os.path.join(args.checkpoint_dir, "checkpoint"), "r") as f: |
| ckpts = [x.strip() for x in f.readlines()] |
| else: |
| ckpts = [] |
| ckpts.append(saved_model_name) |
| for item in ckpts[:-max_to_keep]: |
| if os.path.exists(os.path.join(args.checkpoint_dir, item)): |
| os.remove(os.path.join(args.checkpoint_dir, item)) |
| with open(os.path.join(args.checkpoint_dir, "checkpoint"), "w") as f: |
| for item in ckpts[-max_to_keep:]: |
| f.write("{}\n".format(item)) |
|
|
|
|
| def attempt_to_restore( |
| generator, g_optimizer, checkpoint_dir, discriminator=None, d_optimizer=None |
| ): |
| checkpoint_list = os.path.join(checkpoint_dir, "checkpoint") |
| if os.path.exists(checkpoint_list): |
| checkpoint_filename = open(checkpoint_list).readlines()[-1].strip() |
| checkpoint_path = os.path.join(checkpoint_dir, "{}".format(checkpoint_filename)) |
| print("Restore from {}".format(checkpoint_path)) |
| checkpoint = torch.load(checkpoint_path, map_location="cpu") |
| if generator: |
| if not list(generator.state_dict().keys())[0].startswith("module."): |
| raw_dict = checkpoint["generator"] |
| clean_dict = OrderedDict() |
| for k, v in raw_dict.items(): |
| if k.startswith("module."): |
| clean_dict[k[7:]] = v |
| else: |
| clean_dict[k] = v |
| generator.load_state_dict(clean_dict) |
| else: |
| generator.load_state_dict(checkpoint["generator"]) |
| if g_optimizer: |
| g_optimizer.load_state_dict(checkpoint["g_optimizer"]) |
| global_step = 100000 |
| if discriminator and "discriminator" in checkpoint.keys(): |
| discriminator.load_state_dict(checkpoint["discriminator"]) |
| global_step = checkpoint["global_step"] |
| print("restore discriminator") |
| if d_optimizer and "d_optimizer" in checkpoint.keys(): |
| d_optimizer.load_state_dict(checkpoint["d_optimizer"]) |
| print("restore d_optimizer...") |
| else: |
| global_step = 0 |
| return global_step |
|
|
|
|
| class ExponentialMovingAverage(object): |
| def __init__(self, decay): |
| self.decay = decay |
| self.shadow = {} |
|
|
| def register(self, name, val): |
| self.shadow[name] = val.clone() |
|
|
| def update(self, name, x): |
| assert name in self.shadow |
| update_delta = self.shadow[name] - x |
| self.shadow[name] -= (1.0 - self.decay) * update_delta |
|
|
|
|
| def apply_moving_average(model, ema): |
| for name, param in model.named_parameters(): |
| if name in ema.shadow: |
| ema.update(name, param.data) |
|
|
|
|
| def register_model_to_ema(model, ema): |
| for name, param in model.named_parameters(): |
| if param.requires_grad: |
| ema.register(name, param.data) |
|
|
|
|
| class YParams(HParams): |
| def __init__(self, yaml_file): |
| if not os.path.exists(yaml_file): |
| raise IOError("yaml file: {} is not existed".format(yaml_file)) |
| super().__init__() |
| self.d = collections.OrderedDict() |
| with open(yaml_file) as fp: |
| for _, v in yaml().load(fp).items(): |
| for k1, v1 in v.items(): |
| try: |
| if self.get(k1): |
| self.set_hparam(k1, v1) |
| else: |
| self.add_hparam(k1, v1) |
| self.d[k1] = v1 |
| except Exception: |
| import traceback |
|
|
| print(traceback.format_exc()) |
|
|
| |
| def get_elements(self): |
| return self.d.items() |
|
|
|
|
| def override_config(base_config, new_config): |
| """Update new configurations in the original dict with the new dict |
| |
| Args: |
| base_config (dict): original dict to be overridden |
| new_config (dict): dict with new configurations |
| |
| Returns: |
| dict: updated configuration dict |
| """ |
| for k, v in new_config.items(): |
| if type(v) == dict: |
| if k not in base_config.keys(): |
| base_config[k] = {} |
| base_config[k] = override_config(base_config[k], v) |
| else: |
| base_config[k] = v |
| return base_config |
|
|
|
|
| def get_lowercase_keys_config(cfg): |
| """Change all keys in cfg to lower case |
| |
| Args: |
| cfg (dict): dictionary that stores configurations |
| |
| Returns: |
| dict: dictionary that stores configurations |
| """ |
| updated_cfg = dict() |
| for k, v in cfg.items(): |
| if type(v) == dict: |
| v = get_lowercase_keys_config(v) |
| updated_cfg[k.lower()] = v |
| return updated_cfg |
|
|
|
|
| def _load_config(config_fn, lowercase=False): |
| """Load configurations into a dictionary |
| |
| Args: |
| config_fn (str): path to configuration file |
| lowercase (bool, optional): whether changing keys to lower case. Defaults to False. |
| |
| Returns: |
| dict: dictionary that stores configurations |
| """ |
| with open(config_fn, "r") as f: |
| data = f.read() |
| config_ = json5.loads(data) |
| if "base_config" in config_: |
| |
| p_config_path = os.path.join(os.getenv("WORK_DIR"), config_["base_config"]) |
| p_config_ = _load_config(p_config_path) |
| config_ = override_config(p_config_, config_) |
| if lowercase: |
| |
| config_ = get_lowercase_keys_config(config_) |
| return config_ |
|
|
|
|
| def load_config(config_fn, lowercase=False): |
| """Load configurations into a dictionary |
| |
| Args: |
| config_fn (str): path to configuration file |
| lowercase (bool, optional): _description_. Defaults to False. |
| |
| Returns: |
| JsonHParams: an object that stores configurations |
| """ |
| config_ = _load_config(config_fn, lowercase=lowercase) |
| |
| cfg = JsonHParams(**config_) |
| return cfg |
|
|
|
|
| def save_config(save_path, cfg): |
| """Save configurations into a json file |
| |
| Args: |
| save_path (str): path to save configurations |
| cfg (dict): dictionary that stores configurations |
| """ |
| with open(save_path, "w") as f: |
| json5.dump( |
| cfg, f, ensure_ascii=False, indent=4, quote_keys=True, sort_keys=True |
| ) |
|
|
|
|
| class JsonHParams: |
| def __init__(self, **kwargs): |
| for k, v in kwargs.items(): |
| if type(v) == dict: |
| v = JsonHParams(**v) |
| self[k] = v |
|
|
| def keys(self): |
| return self.__dict__.keys() |
|
|
| def items(self): |
| return self.__dict__.items() |
|
|
| def values(self): |
| return self.__dict__.values() |
|
|
| def __len__(self): |
| return len(self.__dict__) |
|
|
| def __getitem__(self, key): |
| return getattr(self, key) |
|
|
| def __setitem__(self, key, value): |
| return setattr(self, key, value) |
|
|
| def __contains__(self, key): |
| return key in self.__dict__ |
|
|
| def __repr__(self): |
| return self.__dict__.__repr__() |
|
|
|
|
| class ValueWindow: |
| def __init__(self, window_size=100): |
| self._window_size = window_size |
| self._values = [] |
|
|
| def append(self, x): |
| self._values = self._values[-(self._window_size - 1) :] + [x] |
|
|
| @property |
| def sum(self): |
| return sum(self._values) |
|
|
| @property |
| def count(self): |
| return len(self._values) |
|
|
| @property |
| def average(self): |
| return self.sum / max(1, self.count) |
|
|
| def reset(self): |
| self._values = [] |
|
|
|
|
| class Logger(object): |
| def __init__( |
| self, |
| filename, |
| level="info", |
| when="D", |
| backCount=10, |
| fmt="%(asctime)s : %(message)s", |
| ): |
| self.level_relations = { |
| "debug": logging.DEBUG, |
| "info": logging.INFO, |
| "warning": logging.WARNING, |
| "error": logging.ERROR, |
| "crit": logging.CRITICAL, |
| } |
| if level == "debug": |
| fmt = "%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s" |
| self.logger = logging.getLogger(filename) |
| format_str = logging.Formatter(fmt) |
| self.logger.setLevel(self.level_relations.get(level)) |
| sh = logging.StreamHandler() |
| sh.setFormatter(format_str) |
| th = handlers.TimedRotatingFileHandler( |
| filename=filename, when=when, backupCount=backCount, encoding="utf-8" |
| ) |
| th.setFormatter(format_str) |
| self.logger.addHandler(sh) |
| self.logger.addHandler(th) |
| self.logger.info( |
| "==========================New Starting Here==============================" |
| ) |
|
|
|
|
| def init_weights(m, mean=0.0, std=0.01): |
| classname = m.__class__.__name__ |
| if classname.find("Conv") != -1: |
| m.weight.data.normal_(mean, std) |
|
|
|
|
| def get_padding(kernel_size, dilation=1): |
| return int((kernel_size * dilation - dilation) / 2) |
|
|
|
|
| def slice_segments(x, ids_str, segment_size=4): |
| ret = torch.zeros_like(x[:, :, :segment_size]) |
| for i in range(x.size(0)): |
| idx_str = ids_str[i] |
| idx_end = idx_str + segment_size |
| ret[i] = x[i, :, idx_str:idx_end] |
| return ret |
|
|
|
|
| def rand_slice_segments(x, x_lengths=None, segment_size=4): |
| b, d, t = x.size() |
| if x_lengths is None: |
| x_lengths = t |
| ids_str_max = x_lengths - segment_size + 1 |
| ids_str = (torch.rand([b]).to(device=x.device) * ids_str_max).to(dtype=torch.long) |
| ret = slice_segments(x, ids_str, segment_size) |
| return ret, ids_str |
|
|
|
|
| def subsequent_mask(length): |
| mask = torch.tril(torch.ones(length, length)).unsqueeze(0).unsqueeze(0) |
| return mask |
|
|
|
|
| @torch.jit.script |
| def fused_add_tanh_sigmoid_multiply(input_a, input_b, n_channels): |
| n_channels_int = n_channels[0] |
| in_act = input_a + input_b |
| t_act = torch.tanh(in_act[:, :n_channels_int, :]) |
| s_act = torch.sigmoid(in_act[:, n_channels_int:, :]) |
| acts = t_act * s_act |
| return acts |
|
|
|
|
| def convert_pad_shape(pad_shape): |
| l = pad_shape[::-1] |
| pad_shape = [item for sublist in l for item in sublist] |
| return pad_shape |
|
|
|
|
| def sequence_mask(length, max_length=None): |
| if max_length is None: |
| max_length = length.max() |
| x = torch.arange(max_length, dtype=length.dtype, device=length.device) |
| return x.unsqueeze(0) < length.unsqueeze(1) |
|
|
|
|
| def generate_path(duration, mask): |
| """ |
| duration: [b, 1, t_x] |
| mask: [b, 1, t_y, t_x] |
| """ |
| device = duration.device |
|
|
| b, _, t_y, t_x = mask.shape |
| cum_duration = torch.cumsum(duration, -1) |
|
|
| cum_duration_flat = cum_duration.view(b * t_x) |
| path = sequence_mask(cum_duration_flat, t_y).to(mask.dtype) |
| path = path.view(b, t_x, t_y) |
| path = path - F.pad(path, convert_pad_shape([[0, 0], [1, 0], [0, 0]]))[:, :-1] |
| path = path.unsqueeze(1).transpose(2, 3) * mask |
| return path |
|
|
|
|
| def clip_grad_value_(parameters, clip_value, norm_type=2): |
| if isinstance(parameters, torch.Tensor): |
| parameters = [parameters] |
| parameters = list(filter(lambda p: p.grad is not None, parameters)) |
| norm_type = float(norm_type) |
| if clip_value is not None: |
| clip_value = float(clip_value) |
|
|
| total_norm = 0 |
| for p in parameters: |
| param_norm = p.grad.data.norm(norm_type) |
| total_norm += param_norm.item() ** norm_type |
| if clip_value is not None: |
| p.grad.data.clamp_(min=-clip_value, max=clip_value) |
| total_norm = total_norm ** (1.0 / norm_type) |
| return total_norm |
|
|
|
|
| def get_current_time(): |
| pass |
|
|
|
|
| def make_pad_mask(lengths: torch.Tensor, max_len: int = 0) -> torch.Tensor: |
| """ |
| Args: |
| lengths: |
| A 1-D tensor containing sentence lengths. |
| max_len: |
| The length of masks. |
| Returns: |
| Return a 2-D bool tensor, where masked positions |
| are filled with `True` and non-masked positions are |
| filled with `False`. |
| |
| >>> lengths = torch.tensor([1, 3, 2, 5]) |
| >>> make_pad_mask(lengths) |
| tensor([[False, True, True, True, True], |
| [False, False, False, True, True], |
| [False, False, True, True, True], |
| [False, False, False, False, False]]) |
| """ |
| assert lengths.ndim == 1, lengths.ndim |
| max_len = max(max_len, lengths.max()) |
| n = lengths.size(0) |
| seq_range = torch.arange(0, max_len, device=lengths.device) |
| expaned_lengths = seq_range.unsqueeze(0).expand(n, max_len) |
|
|
| return expaned_lengths >= lengths.unsqueeze(-1) |
|
|
|
|