From e774f68aeefd9dfac5a09847cb8def93a5e22184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20G=C3=B6lge?= Date: Fri, 12 Feb 2021 12:03:42 +0000 Subject: [PATCH 01/12] save used model characters to the checkpoints --- TTS/bin/train_glow_tts.py | 11 +++++++---- TTS/bin/train_speedy_speech.py | 11 +++++++---- TTS/bin/train_tacotron.py | 7 ++++++- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/TTS/bin/train_glow_tts.py b/TTS/bin/train_glow_tts.py index 9db2381e..a12c5581 100644 --- a/TTS/bin/train_glow_tts.py +++ b/TTS/bin/train_glow_tts.py @@ -268,7 +268,7 @@ def train(data_loader, model, criterion, optimizer, scheduler, if global_step % c.save_step == 0: if c.checkpoint: # save model - save_checkpoint(model, optimizer, global_step, epoch, 1, OUT_PATH, + save_checkpoint(model, optimizer, global_step, epoch, 1, OUT_PATH, model_characters, model_loss=loss_dict['loss']) # wait all kernels to be completed @@ -467,7 +467,7 @@ def evaluate(data_loader, model, criterion, ap, global_step, epoch): def main(args): # pylint: disable=redefined-outer-name # pylint: disable=global-variable-undefined - global meta_data_train, meta_data_eval, symbols, phonemes, speaker_mapping + global meta_data_train, meta_data_eval, symbols, phonemes, model_characters, speaker_mapping # Audio processor ap = AudioProcessor(**c.audio) if 'characters' in c.keys(): @@ -477,7 +477,10 @@ def main(args): # pylint: disable=redefined-outer-name if num_gpus > 1: init_distributed(args.rank, num_gpus, args.group_id, c.distributed["backend"], c.distributed["url"]) - num_chars = len(phonemes) if c.use_phonemes else len(symbols) + + # set model characters + model_characters = phonemes if c.use_phonemes else symbols + num_chars = len(model_characters) # load data instances meta_data_train, meta_data_eval = load_meta_data(c.datasets) @@ -559,7 +562,7 @@ def main(args): # pylint: disable=redefined-outer-name if c.run_eval: target_loss = eval_avg_loss_dict['avg_loss'] best_loss = save_best_model(target_loss, best_loss, model, optimizer, global_step, epoch, c.r, - OUT_PATH) + OUT_PATH, model_characters) if __name__ == '__main__': diff --git a/TTS/bin/train_speedy_speech.py b/TTS/bin/train_speedy_speech.py index a9a83bbf..1f32c8f6 100644 --- a/TTS/bin/train_speedy_speech.py +++ b/TTS/bin/train_speedy_speech.py @@ -247,7 +247,7 @@ def train(data_loader, model, criterion, optimizer, scheduler, if global_step % c.save_step == 0: if c.checkpoint: # save model - save_checkpoint(model, optimizer, global_step, epoch, 1, OUT_PATH, + save_checkpoint(model, optimizer, global_step, epoch, 1, OUT_PATH, model_characters, model_loss=loss_dict['loss']) # wait all kernels to be completed @@ -431,7 +431,7 @@ def evaluate(data_loader, model, criterion, ap, global_step, epoch): # FIXME: move args definition/parsing inside of main? def main(args): # pylint: disable=redefined-outer-name # pylint: disable=global-variable-undefined - global meta_data_train, meta_data_eval, symbols, phonemes, speaker_mapping + global meta_data_train, meta_data_eval, symbols, phonemes, model_characters, speaker_mapping # Audio processor ap = AudioProcessor(**c.audio) if 'characters' in c.keys(): @@ -441,7 +441,10 @@ def main(args): # pylint: disable=redefined-outer-name if num_gpus > 1: init_distributed(args.rank, num_gpus, args.group_id, c.distributed["backend"], c.distributed["url"]) - num_chars = len(phonemes) if c.use_phonemes else len(symbols) + + # set model characters + model_characters = phonemes if c.use_phonemes else symbols + num_chars = len(model_characters) # load data instances meta_data_train, meta_data_eval = load_meta_data(c.datasets, eval_split=True) @@ -523,7 +526,7 @@ def main(args): # pylint: disable=redefined-outer-name target_loss = eval_avg_loss_dict['avg_loss'] best_loss = save_best_model(target_loss, best_loss, model, optimizer, global_step, epoch, c.r, - OUT_PATH) + OUT_PATH, model_characters) if __name__ == '__main__': diff --git a/TTS/bin/train_tacotron.py b/TTS/bin/train_tacotron.py index 0a53f2a1..a9c0881f 100644 --- a/TTS/bin/train_tacotron.py +++ b/TTS/bin/train_tacotron.py @@ -284,6 +284,7 @@ def train(data_loader, model, criterion, optimizer, optimizer_st, scheduler, save_checkpoint(model, optimizer, global_step, epoch, model.decoder.r, OUT_PATH, optimizer_st=optimizer_st, model_loss=loss_dict['postnet_loss'], + characters=model_characters, scaler=scaler.state_dict() if c.mixed_precision else None) # Diagnostic visualizations @@ -492,9 +493,11 @@ def evaluate(data_loader, model, criterion, ap, global_step, epoch): def main(args): # pylint: disable=redefined-outer-name # pylint: disable=global-variable-undefined - global meta_data_train, meta_data_eval, symbols, phonemes, speaker_mapping + global meta_data_train, meta_data_eval, speaker_mapping, symbols, phonemes, model_characters # Audio processor ap = AudioProcessor(**c.audio) + + # setup custom characters if set in config file. if 'characters' in c.keys(): symbols, phonemes = make_symbols(**c.characters) @@ -503,6 +506,7 @@ def main(args): # pylint: disable=redefined-outer-name init_distributed(args.rank, num_gpus, args.group_id, c.distributed["backend"], c.distributed["url"]) num_chars = len(phonemes) if c.use_phonemes else len(symbols) + model_characters = phonemes if c.use_phonemes else symbols # load data instances meta_data_train, meta_data_eval = load_meta_data(c.datasets) @@ -634,6 +638,7 @@ def main(args): # pylint: disable=redefined-outer-name epoch, c.r, OUT_PATH, + model_characters, scaler=scaler.state_dict() if c.mixed_precision else None ) From 918f007a11cc9fcd603bbd398fb3718be5f8b1b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20G=C3=B6lge?= Date: Fri, 12 Feb 2021 12:04:07 +0000 Subject: [PATCH 02/12] docstring update --- TTS/tts/datasets/TTSDataset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/TTS/tts/datasets/TTSDataset.py b/TTS/tts/datasets/TTSDataset.py index 3b327cbc..16329ad7 100644 --- a/TTS/tts/datasets/TTSDataset.py +++ b/TTS/tts/datasets/TTSDataset.py @@ -39,6 +39,7 @@ class MyDataset(Dataset): compute_linear_spec (bool): compute linear spectrogram if True. ap (TTS.tts.utils.AudioProcessor): audio processor object. meta_data (list): list of dataset instances. + tp (dict): dict of custom text characters used for converting texts to sequences. batch_group_size (int): (0) range of batch randomization after sorting sequences by length. min_seq_len (int): (0) minimum sequence length to be processed From 2abfff17f928789576884dea72b88127b73cb71c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20G=C3=B6lge?= Date: Fri, 12 Feb 2021 12:04:41 +0000 Subject: [PATCH 03/12] enable saving model characters in io.py --- TTS/tts/utils/io.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/TTS/tts/utils/io.py b/TTS/tts/utils/io.py index 63e04283..fe94d98d 100644 --- a/TTS/tts/utils/io.py +++ b/TTS/tts/utils/io.py @@ -38,7 +38,15 @@ def load_checkpoint(model, checkpoint_path, amp=None, use_cuda=False, eval=False return model, state -def save_model(model, optimizer, current_step, epoch, r, output_path, amp_state_dict=None, **kwargs): +def save_model(model, + optimizer, + current_step, + epoch, + r, + output_path, + characters, + amp_state_dict=None, + **kwargs): """Save ```TTS.tts.models``` states with extra fields. Args: @@ -48,6 +56,7 @@ def save_model(model, optimizer, current_step, epoch, r, output_path, amp_state_ epoch (int): current number of training epochs. r (int): model reduction rate for Tacotron models. output_path (str): output path to save the model file. + characters (list): list of characters used in the model. amp_state_dict (state_dict, optional): Apex.amp state dict if Apex is enabled. Defaults to None. """ if hasattr(model, 'module'): @@ -60,7 +69,8 @@ def save_model(model, optimizer, current_step, epoch, r, output_path, amp_state_ 'step': current_step, 'epoch': epoch, 'date': datetime.date.today().strftime("%B %d, %Y"), - 'r': r + 'r': r, + 'characters': characters } if amp_state_dict: state['amp'] = amp_state_dict @@ -68,7 +78,8 @@ def save_model(model, optimizer, current_step, epoch, r, output_path, amp_state_ torch.save(state, output_path) -def save_checkpoint(model, optimizer, current_step, epoch, r, output_folder, **kwargs): +def save_checkpoint(model, optimizer, current_step, epoch, r, output_folder, + characters, **kwargs): """Save model checkpoint, intended for saving checkpoints at training. Args: @@ -78,14 +89,16 @@ def save_checkpoint(model, optimizer, current_step, epoch, r, output_folder, **k epoch (int): current number of training epochs. r (int): model reduction rate for Tacotron models. output_path (str): output path to save the model file. + characters (list): list of characters used in the model. """ file_name = 'checkpoint_{}.pth.tar'.format(current_step) checkpoint_path = os.path.join(output_folder, file_name) print(" > CHECKPOINT : {}".format(checkpoint_path)) - save_model(model, optimizer, current_step, epoch, r, checkpoint_path, **kwargs) + save_model(model, optimizer, current_step, epoch, r, checkpoint_path, characters, **kwargs) -def save_best_model(target_loss, best_loss, model, optimizer, current_step, epoch, r, output_folder, **kwargs): +def save_best_model(target_loss, best_loss, model, optimizer, current_step, + epoch, r, output_folder, characters, **kwargs): """Save model checkpoint, intended for saving the best model after each epoch. It compares the current model loss with the best loss so far and saves the model if the current loss is better. @@ -99,6 +112,7 @@ def save_best_model(target_loss, best_loss, model, optimizer, current_step, epoc epoch (int): current number of training epochs. r (int): model reduction rate for Tacotron models. output_path (str): output path to save the model file. + characters (list): list of characters used in the model. Returns: float: updated current best loss. @@ -107,6 +121,6 @@ def save_best_model(target_loss, best_loss, model, optimizer, current_step, epoc file_name = 'best_model.pth.tar' checkpoint_path = os.path.join(output_folder, file_name) print(" >> BEST MODEL : {}".format(checkpoint_path)) - save_model(model, optimizer, current_step, epoch, r, checkpoint_path, model_loss=target_loss, **kwargs) + save_model(model, optimizer, current_step, epoch, r, checkpoint_path, characters, model_loss=target_loss, **kwargs) best_loss = target_loss return best_loss From 593cedee145a16c80b0cf6a23dab95142e71a3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20G=C3=B6lge?= Date: Fri, 12 Feb 2021 12:05:56 +0000 Subject: [PATCH 04/12] parse_characters function --- TTS/tts/utils/text/symbols.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/TTS/tts/utils/text/symbols.py b/TTS/tts/utils/text/symbols.py index 7a741a8f..e82967fb 100644 --- a/TTS/tts/utils/text/symbols.py +++ b/TTS/tts/utils/text/symbols.py @@ -41,6 +41,16 @@ symbols, phonemes = make_symbols(_characters, _phonemes, _punctuations, _pad, _e # from random import shuffle # shuffle(phonemes) + +def parse_symbols(): + return {'pad': _pad, + 'eos': _eos, + 'bos': _bos, + 'characters': _characters, + 'punctuations': _punctuations, + 'phonemes': _phonemes} + + if __name__ == '__main__': print(" > TTS symbols {}".format(len(symbols))) print(symbols) From 7ab527d17e6ea91f6c7f9919a59bd4b98148eaa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20G=C3=B6lge?= Date: Fri, 12 Feb 2021 12:06:46 +0000 Subject: [PATCH 05/12] save default model chars to the training config file --- TTS/utils/arguments.py | 71 ++++++++++++++++++++---------------------- TTS/utils/io.py | 2 +- 2 files changed, 34 insertions(+), 39 deletions(-) diff --git a/TTS/utils/arguments.py b/TTS/utils/arguments.py index 948c90d3..031a3140 100644 --- a/TTS/utils/arguments.py +++ b/TTS/utils/arguments.py @@ -3,17 +3,18 @@ """Argument parser for training scripts.""" import argparse -import re import glob import os - -from TTS.utils.generic_utils import ( - create_experiment_folder, get_git_branch) -from TTS.utils.console_logger import ConsoleLogger -from TTS.utils.io import copy_model_files, load_config -from TTS.utils.tensorboard_logger import TensorboardLogger +import re +import json from TTS.tts.utils.generic_utils import check_config_tts +from TTS.utils.console_logger import ConsoleLogger +from TTS.utils.generic_utils import create_experiment_folder, get_git_branch +from TTS.utils.io import (copy_model_files, load_config, + save_characters_to_config) +from TTS.utils.tensorboard_logger import TensorboardLogger +from TTS.tts.utils.text.symbols import parse_symbols def parse_arguments(argv): @@ -110,38 +111,27 @@ def get_last_checkpoint(path): def process_args(args, model_type): """Process parsed comand line arguments. - Parameters - ---------- - args : argparse.Namespace or dict like - Parsed input arguments. - model_type : str - Model type used to check config parameters and setup the TensorBoard - logger. One of: - - tacotron - - glow_tts - - speedy_speech - - gan - - wavegrad - - wavernn + Args: + args (argparse.Namespace or dict like): Parsed input arguments. + model_type (str): Model type used to check config parameters and setup the TensorBoard + logger. One of: + - tacotron + - glow_tts + - speedy_speech + - gan + - wavegrad + - wavernn - Raises - ------ - ValueError - If `model_type` is not one of implemented choices. - - Returns - ------- - c : TTS.utils.io.AttrDict - Config paramaters. - out_path : str - Path to save models and logging. - audio_path : str - Path to save generated test audios. - c_logger : TTS.utils.console_logger.ConsoleLogger - Class that does logging to the console. - tb_logger : TTS.utils.tensorboard.TensorboardLogger - Class that does the TensorBoard loggind. + Raises: + ValueError + If `model_type` is not one of implemented choices. + Returns: + c (TTS.utils.io.AttrDict): Config paramaters. + out_path (str): Path to save models and logging. + audio_path (str): Path to save generated test audios. + c_logger (TTS.utils.console_logger.ConsoleLogger): Class that does logging to the console. + tb_logger (TTS.utils.tensorboard.TensorboardLogger): Class that does the TensorBoard loggind. """ if args.continue_path != "": args.output_path = args.continue_path @@ -156,7 +146,6 @@ def process_args(args, model_type): # setup output paths and read configs c = load_config(args.config_path) - if model_type in "tacotron glow_tts speedy_speech": model_class = "TTS" elif model_type in "gan wavegrad wavernn": @@ -192,6 +181,12 @@ def process_args(args, model_type): if args.restore_path: new_fields["restore_path"] = args.restore_path new_fields["github_branch"] = get_git_branch() + # if model characters are not set in the config file + # save the default set to the config file for future + # compatibility. + if model_class == 'TTS' and not 'characters' in c.keys(): + used_characters = parse_symbols() + new_fields['characters'] = used_characters copy_model_files(c, args.config_path, out_path, new_fields) os.chmod(audio_path, 0o775) diff --git a/TTS/utils/io.py b/TTS/utils/io.py index 46abf1c8..1148e0fe 100644 --- a/TTS/utils/io.py +++ b/TTS/utils/io.py @@ -67,7 +67,7 @@ def copy_model_files(c, config_file, out_path, new_fields): if isinstance(value, str): new_line = '"{}":"{}",\n'.format(key, value) else: - new_line = '"{}":{},\n'.format(key, value) + new_line = '"{}":{},\n'.format(key, json.dumps(value, ensure_ascii=False)) config_lines.insert(1, new_line) config_out_file = open(copy_config_path, "w") config_out_file.writelines(config_lines) From b28c724c0458b3565507508a4394ed146343bbe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20G=C3=B6lge?= Date: Fri, 12 Feb 2021 12:10:57 +0000 Subject: [PATCH 06/12] remove _phoneme_punctuations --- TTS/tts/utils/text/__init__.py | 4 ++-- TTS/tts/utils/text/symbols.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/TTS/tts/utils/text/__init__.py b/TTS/tts/utils/text/__init__.py index 23c5ab5f..90df61c4 100644 --- a/TTS/tts/utils/text/__init__.py +++ b/TTS/tts/utils/text/__init__.py @@ -6,7 +6,7 @@ import phonemizer from packaging import version from phonemizer.phonemize import phonemize from TTS.tts.utils.text import cleaners -from TTS.tts.utils.text.symbols import (_bos, _eos, _phoneme_punctuations, +from TTS.tts.utils.text.symbols import (_bos, _eos, _punctuations, make_symbols, phonemes, symbols) @@ -24,7 +24,7 @@ _phonemes = phonemes _CURLY_RE = re.compile(r'(.*?)\{(.+?)\}(.*)') # Regular expression matching punctuations, ignoring empty space -PHONEME_PUNCTUATION_PATTERN = r'['+_phoneme_punctuations+']+' +PHONEME_PUNCTUATION_PATTERN = r'['+_punctuations+']+' def text2phone(text, language): diff --git a/TTS/tts/utils/text/symbols.py b/TTS/tts/utils/text/symbols.py index e82967fb..4bd1d6c2 100644 --- a/TTS/tts/utils/text/symbols.py +++ b/TTS/tts/utils/text/symbols.py @@ -5,6 +5,8 @@ Defines the set of symbols used in text input to the model. The default is a set of ASCII characters that works well for English or text that has been run through Unidecode. For other data, you can modify _characters. See TRAINING_DATA.md for details. ''' + + def make_symbols(characters, phonemes=None, punctuations='!\'(),-.:;? ', pad='_', eos='~', bos='^'):# pylint: disable=redefined-outer-name ''' Function to create symbols and phonemes ''' _symbols = [pad, eos, bos] + list(characters) @@ -18,15 +20,14 @@ def make_symbols(characters, phonemes=None, punctuations='!\'(),-.:;? ', pad='_' _symbols += _arpabet return _symbols, _phonemes - _pad = '_' _eos = '~' _bos = '^' _characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!\'(),-.:;? ' _punctuations = '!\'(),-.:;? ' -_phoneme_punctuations = '.!;:,?' +# _phoneme_punctuations = '.!;:,?' -# Phonemes definition +# Phonemes definition (All IPA characters) _vowels = 'iyɨʉɯuɪʏʊeøɘəɵɤoɛœɜɞʌɔæɐaɶɑɒᵻ' _non_pulmonic_consonants = 'ʘɓǀɗǃʄǂɠǁʛ' _pulmonic_consonants = 'pbtdʈɖcɟkɡqɢʔɴŋɲɳnɱmʙrʀⱱɾɽɸβfvθðszʃʒʂʐçʝxɣχʁħʕhɦɬɮʋɹɻjɰlɭʎʟ' From 4244096ccb1f53dced2db91b13c6709a5ae6c356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20G=C3=B6lge?= Date: Fri, 12 Feb 2021 14:07:26 +0000 Subject: [PATCH 07/12] update test_text_processing for espeak-ng --- TTS/tts/utils/text/__init__.py | 2 +- tests/test_text_processing.py | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/TTS/tts/utils/text/__init__.py b/TTS/tts/utils/text/__init__.py index 90df61c4..9771e691 100644 --- a/TTS/tts/utils/text/__init__.py +++ b/TTS/tts/utils/text/__init__.py @@ -24,7 +24,7 @@ _phonemes = phonemes _CURLY_RE = re.compile(r'(.*?)\{(.+?)\}(.*)') # Regular expression matching punctuations, ignoring empty space -PHONEME_PUNCTUATION_PATTERN = r'['+_punctuations+']+' +PHONEME_PUNCTUATION_PATTERN = r'['+_punctuations.replace(' ', '')+']+' def text2phone(text, language): diff --git a/tests/test_text_processing.py b/tests/test_text_processing.py index 61a83fa1..2ea8e8f9 100644 --- a/tests/test_text_processing.py +++ b/tests/test_text_processing.py @@ -19,7 +19,7 @@ def test_phoneme_to_sequence(): text_hat = sequence_to_phoneme(sequence) sequence_with_params = phoneme_to_sequence(text, text_cleaner, lang, tp=conf.characters) text_hat_with_params = sequence_to_phoneme(sequence, tp=conf.characters) - gt = "ɹiːsənt ɹɪsɜːtʃ æt hɑːɹvɚd hɐz ʃoʊn mɛdᵻteɪɾɪŋ fɔːɹ æz lɪɾəl æz eɪt wiːks kæn æktʃuːəli ɪnkɹiːs, ðə ɡɹeɪ mæɾɚɹ ɪnðə pɑːɹts ʌvðə bɹeɪn ɹɪspɑːnsəbəl fɔːɹ ɪmoʊʃənəl ɹɛɡjuːleɪʃən ænd lɜːnɪŋ!" + gt = 'ɹiːsənt ɹᵻsɜːtʃ æt hɑːɹvɚd hɐz ʃoʊn mɛdᵻteɪɾɪŋ fɔːɹ æz lɪɾəl æz eɪt wiːks kæn æktʃuːəli ɪŋkɹiːs, ðə ɡɹeɪ mæɾɚɹ ɪnðə pɑːɹts ʌvðə bɹeɪn ɹᵻspɑːnsᵻbəl fɔːɹ ɪmoʊʃənəl ɹɛɡjʊleɪʃən ænd lɜːnɪŋ!' assert text_hat == text_hat_with_params == gt # multiple punctuations @@ -28,7 +28,7 @@ def test_phoneme_to_sequence(): text_hat = sequence_to_phoneme(sequence) _ = phoneme_to_sequence(text, text_cleaner, lang, tp=conf.characters) text_hat_with_params = sequence_to_phoneme(sequence, tp=conf.characters) - gt = "biː ɐ vɔɪs, nɑːt ɐn! ɛkoʊ?" + gt = "biː ɐ vɔɪs, nɑːt æn! ɛkoʊ?" print(text_hat) print(len(sequence)) assert text_hat == text_hat_with_params == gt @@ -39,7 +39,7 @@ def test_phoneme_to_sequence(): text_hat = sequence_to_phoneme(sequence) _ = phoneme_to_sequence(text, text_cleaner, lang, tp=conf.characters) text_hat_with_params = sequence_to_phoneme(sequence, tp=conf.characters) - gt = "biː ɐ vɔɪs, nɑːt ɐn! ɛkoʊ" + gt = "biː ɐ vɔɪs, nɑːt æn! ɛkoʊ" print(text_hat) print(len(sequence)) assert text_hat == text_hat_with_params == gt @@ -61,7 +61,7 @@ def test_phoneme_to_sequence(): text_hat = sequence_to_phoneme(sequence) _ = phoneme_to_sequence(text, text_cleaner, lang, tp=conf.characters) text_hat_with_params = sequence_to_phoneme(sequence, tp=conf.characters) - gt = "biː ɐ vɔɪs, nɑːt ɐn! ɛkoʊ." + gt = "biː ɐ vɔɪs, nɑːt æn! ɛkoʊ." print(text_hat) print(len(sequence)) assert text_hat == text_hat_with_params == gt @@ -72,7 +72,7 @@ def test_phoneme_to_sequence(): text_hat = sequence_to_phoneme(sequence) _ = phoneme_to_sequence(text, text_cleaner, lang, tp=conf.characters) text_hat_with_params = sequence_to_phoneme(sequence, tp=conf.characters) - gt = "^biː ɐ vɔɪs, nɑːt ɐn! ɛkoʊ.~" + gt = "^biː ɐ vɔɪs, nɑːt æn! ɛkoʊ.~" print(text_hat) print(len(sequence)) assert text_hat == text_hat_with_params == gt @@ -83,7 +83,7 @@ def test_phoneme_to_sequence(): text_hat = sequence_to_phoneme(sequence) _ = phoneme_to_sequence(text, text_cleaner, lang, tp=conf.characters) text_hat_with_params = sequence_to_phoneme(sequence, tp=conf.characters) - gt = "biː ɐ vɔɪs, nɑːt ɐn! ɛkoʊ" + gt = "biː ɐ vɔɪs, nɑːt æn! ɛkoʊ" print(text_hat) print(len(sequence)) assert text_hat == text_hat_with_params == gt @@ -97,7 +97,7 @@ def test_phoneme_to_sequence_with_blank_token(): text_hat = sequence_to_phoneme(sequence) _ = phoneme_to_sequence(text, text_cleaner, lang, tp=conf.characters, add_blank=True) text_hat_with_params = sequence_to_phoneme(sequence, tp=conf.characters, add_blank=True) - gt = "ɹiːsənt ɹɪsɜːtʃ æt hɑːɹvɚd hɐz ʃoʊn mɛdᵻteɪɾɪŋ fɔːɹ æz lɪɾəl æz eɪt wiːks kæn æktʃuːəli ɪnkɹiːs, ðə ɡɹeɪ mæɾɚɹ ɪnðə pɑːɹts ʌvðə bɹeɪn ɹɪspɑːnsəbəl fɔːɹ ɪmoʊʃənəl ɹɛɡjuːleɪʃən ænd lɜːnɪŋ!" + gt = "ɹiːsənt ɹᵻsɜːtʃ æt hɑːɹvɚd hɐz ʃoʊn mɛdᵻteɪɾɪŋ fɔːɹ æz lɪɾəl æz eɪt wiːks kæn æktʃuːəli ɪŋkɹiːs, ðə ɡɹeɪ mæɾɚɹ ɪnðə pɑːɹts ʌvðə bɹeɪn ɹᵻspɑːnsᵻbəl fɔːɹ ɪmoʊʃənəl ɹɛɡjʊleɪʃən ænd lɜːnɪŋ!" assert text_hat == text_hat_with_params == gt # multiple punctuations @@ -106,7 +106,7 @@ def test_phoneme_to_sequence_with_blank_token(): text_hat = sequence_to_phoneme(sequence) _ = phoneme_to_sequence(text, text_cleaner, lang, tp=conf.characters, add_blank=True) text_hat_with_params = sequence_to_phoneme(sequence, tp=conf.characters, add_blank=True) - gt = "biː ɐ vɔɪs, nɑːt ɐn! ɛkoʊ?" + gt = 'biː ɐ vɔɪs, nɑːt æn! ɛkoʊ?' print(text_hat) print(len(sequence)) assert text_hat == text_hat_with_params == gt @@ -117,7 +117,7 @@ def test_phoneme_to_sequence_with_blank_token(): text_hat = sequence_to_phoneme(sequence) _ = phoneme_to_sequence(text, text_cleaner, lang, tp=conf.characters, add_blank=True) text_hat_with_params = sequence_to_phoneme(sequence, tp=conf.characters, add_blank=True) - gt = "biː ɐ vɔɪs, nɑːt ɐn! ɛkoʊ" + gt = 'biː ɐ vɔɪs, nɑːt æn! ɛkoʊ' print(text_hat) print(len(sequence)) assert text_hat == text_hat_with_params == gt @@ -128,7 +128,7 @@ def test_phoneme_to_sequence_with_blank_token(): text_hat = sequence_to_phoneme(sequence) _ = phoneme_to_sequence(text, text_cleaner, lang, tp=conf.characters, add_blank=True) text_hat_with_params = sequence_to_phoneme(sequence, tp=conf.characters, add_blank=True) - gt = "biː ɐ vɔɪs, nɑːt ɐn ɛkoʊ!" + gt = 'biː ɐ vɔɪs, nɑːt ɐn ɛkoʊ!' print(text_hat) print(len(sequence)) assert text_hat == text_hat_with_params == gt @@ -139,7 +139,7 @@ def test_phoneme_to_sequence_with_blank_token(): text_hat = sequence_to_phoneme(sequence) _ = phoneme_to_sequence(text, text_cleaner, lang, tp=conf.characters, add_blank=True) text_hat_with_params = sequence_to_phoneme(sequence, tp=conf.characters, add_blank=True) - gt = "biː ɐ vɔɪs, nɑːt ɐn! ɛkoʊ." + gt = 'biː ɐ vɔɪs, nɑːt æn! ɛkoʊ.' print(text_hat) print(len(sequence)) assert text_hat == text_hat_with_params == gt @@ -150,7 +150,7 @@ def test_phoneme_to_sequence_with_blank_token(): text_hat = sequence_to_phoneme(sequence) _ = phoneme_to_sequence(text, text_cleaner, lang, tp=conf.characters, add_blank=True) text_hat_with_params = sequence_to_phoneme(sequence, tp=conf.characters, add_blank=True) - gt = "^biː ɐ vɔɪs, nɑːt ɐn! ɛkoʊ.~" + gt = "^biː ɐ vɔɪs, nɑːt æn! ɛkoʊ.~" print(text_hat) print(len(sequence)) assert text_hat == text_hat_with_params == gt @@ -161,14 +161,14 @@ def test_phoneme_to_sequence_with_blank_token(): text_hat = sequence_to_phoneme(sequence) _ = phoneme_to_sequence(text, text_cleaner, lang, tp=conf.characters, add_blank=True) text_hat_with_params = sequence_to_phoneme(sequence, tp=conf.characters, add_blank=True) - gt = "biː ɐ vɔɪs, nɑːt ɐn! ɛkoʊ" + gt = "biː ɐ vɔɪs, nɑːt æn! ɛkoʊ" print(text_hat) print(len(sequence)) assert text_hat == text_hat_with_params == gt def test_text2phone(): text = "Recent research at Harvard has shown meditating for as little as 8 weeks can actually increase, the grey matter in the parts of the brain responsible for emotional regulation and learning!" - gt = "ɹ|iː|s|ə|n|t| |ɹ|ɪ|s|ɜː|tʃ| |æ|t| |h|ɑːɹ|v|ɚ|d| |h|ɐ|z| |ʃ|oʊ|n| |m|ɛ|d|ᵻ|t|eɪ|ɾ|ɪ|ŋ| |f|ɔː|ɹ| |æ|z| |l|ɪ|ɾ|əl| |æ|z| |eɪ|t| |w|iː|k|s| |k|æ|n| |æ|k|tʃ|uː|əl|i| |ɪ|n|k|ɹ|iː|s|,| |ð|ə| |ɡ|ɹ|eɪ| |m|æ|ɾ|ɚ|ɹ| |ɪ|n|ð|ə| |p|ɑːɹ|t|s| |ʌ|v|ð|ə| |b|ɹ|eɪ|n| |ɹ|ɪ|s|p|ɑː|n|s|ə|b|əl| |f|ɔː|ɹ| |ɪ|m|oʊ|ʃ|ə|n|əl| |ɹ|ɛ|ɡ|j|uː|l|eɪ|ʃ|ə|n| |æ|n|d| |l|ɜː|n|ɪ|ŋ|!" + gt = 'ɹ|iː|s|ə|n|t| |ɹ|ᵻ|s|ɜː|tʃ| |æ|t| |h|ɑːɹ|v|ɚ|d| |h|ɐ|z| |ʃ|oʊ|n| |m|ɛ|d|ᵻ|t|eɪ|ɾ|ɪ|ŋ| |f|ɔː|ɹ| |æ|z| |l|ɪ|ɾ|əl| |æ|z| |eɪ|t| |w|iː|k|s| |k|æ|n| |æ|k|tʃ|uː|əl|i| |ɪ|ŋ|k|ɹ|iː|s|,| |ð|ə| |ɡ|ɹ|eɪ| |m|æ|ɾ|ɚ|ɹ| |ɪ|n|ð|ə| |p|ɑːɹ|t|s| |ʌ|v|ð|ə| |b|ɹ|eɪ|n| |ɹ|ᵻ|s|p|ɑː|n|s|ᵻ|b|əl| |f|ɔː|ɹ| |ɪ|m|oʊ|ʃ|ə|n|əl| |ɹ|ɛ|ɡ|j|ʊ|l|eɪ|ʃ|ə|n| |æ|n|d| |l|ɜː|n|ɪ|ŋ|!' lang = "en-us" ph = text2phone(text, lang) assert gt == ph From 420901f4c23a2f9882dcb544cca9261f8376ec18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20G=C3=B6lge?= Date: Fri, 12 Feb 2021 14:41:17 +0000 Subject: [PATCH 08/12] linter fixes --- TTS/bin/find_unique_chars.py | 5 ++--- TTS/utils/arguments.py | 6 ++---- TTS/utils/manage.py | 3 ++- hubconf.py | 11 ++++++----- tests/test_demo_server.py | 2 +- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/TTS/bin/find_unique_chars.py b/TTS/bin/find_unique_chars.py index e6c35878..654a3ff9 100644 --- a/TTS/bin/find_unique_chars.py +++ b/TTS/bin/find_unique_chars.py @@ -7,16 +7,15 @@ from TTS.tts.datasets.preprocess import get_preprocessor_by_name def main(): + # pylint: disable=bad-continuation parser = argparse.ArgumentParser(description='''Find all the unique characters or phonemes in a dataset.\n\n''' '''Target dataset must be defined in TTS.tts.datasets.preprocess\n\n'''\ - ''' Example runs: python TTS/bin/find_unique_chars.py --dataset ljspeech --meta_file /path/to/LJSpeech/metadata.csv - ''', - formatter_class=RawTextHelpFormatter) + ''', formatter_class=RawTextHelpFormatter) parser.add_argument( '--dataset', diff --git a/TTS/utils/arguments.py b/TTS/utils/arguments.py index 031a3140..7d8f4adf 100644 --- a/TTS/utils/arguments.py +++ b/TTS/utils/arguments.py @@ -6,15 +6,13 @@ import argparse import glob import os import re -import json from TTS.tts.utils.generic_utils import check_config_tts +from TTS.tts.utils.text.symbols import parse_symbols from TTS.utils.console_logger import ConsoleLogger from TTS.utils.generic_utils import create_experiment_folder, get_git_branch -from TTS.utils.io import (copy_model_files, load_config, - save_characters_to_config) +from TTS.utils.io import copy_model_files, load_config from TTS.utils.tensorboard_logger import TensorboardLogger -from TTS.tts.utils.text.symbols import parse_symbols def parse_arguments(argv): diff --git a/TTS/utils/manage.py b/TTS/utils/manage.py index 97cdf2b6..bd236dda 100644 --- a/TTS/utils/manage.py +++ b/TTS/utils/manage.py @@ -122,7 +122,8 @@ class ModelManager(object): """Download files from GDrive using their file ids""" gdown.download(f"{self.url_prefix}{gdrive_idx}", output=output, quiet=False) - def _download_zip_file(self, file_url, output): + @staticmethod + def _download_zip_file(file_url, output): """Download the target zip file and extract the files to a folder with the same name as the zip file.""" r = requests.get(file_url) diff --git a/hubconf.py b/hubconf.py index 7fc020b5..13549dfe 100644 --- a/hubconf.py +++ b/hubconf.py @@ -1,11 +1,11 @@ -dependencies = ['torch', 'gdown', 'pysbd', 'phonemizer', 'unidecode'] # apt install espeak +dependencies = ['torch', 'gdown', 'pysbd', 'phonemizer', 'unidecode'] # apt install espeak-ng import torch from TTS.utils.synthesizer import Synthesizer from TTS.utils.manage import ModelManager -def tts(model_name='tts_models/en/ljspeech/tacotron2-DCA', vocoder_name='vocoder_models/en/ljspeech/mulitband-melgan', use_cuda=False): +def tts(model_name='tts_models/en/ljspeech/tacotron2-DCA', vocoder_name=None, use_cuda=False): """TTS entry point for PyTorch Hub that provides a Synthesizer object to synthesize speech from a give text. Example: @@ -15,7 +15,7 @@ def tts(model_name='tts_models/en/ljspeech/tacotron2-DCA', vocoder_name='vocoder Args: model_name (str, optional): One of the model names from .model.json. Defaults to 'tts_models/en/ljspeech/tacotron2-DCA'. - vocoder_name (str, optional): One of the model names from .model.json. Defaults to 'vocoder_models/en/ljspeech/mulitband-melgan'. + vocoder_name (str, optional): One of the model names from .model.json. Defaults to 'vocoder_models/en/ljspeech/multiband-melgan'. pretrained (bool, optional): [description]. Defaults to True. Returns: @@ -23,8 +23,9 @@ def tts(model_name='tts_models/en/ljspeech/tacotron2-DCA', vocoder_name='vocoder """ manager = ModelManager() - model_path, config_path = manager.download_model(model_name) - vocoder_path, vocoder_config_path = manager.download_model(vocoder_name) + model_path, config_path, model_item = manager.download_model(model_name) + vocoder_name = model_item['default_vocoder'] if vocoder_name is None else vocoder_name + vocoder_path, vocoder_config_path, _ = manager.download_model(vocoder_name) # create synthesizer synt = Synthesizer(model_path, config_path, vocoder_path, vocoder_config_path, use_cuda) diff --git a/tests/test_demo_server.py b/tests/test_demo_server.py index bccff55d..1de3f558 100644 --- a/tests/test_demo_server.py +++ b/tests/test_demo_server.py @@ -21,7 +21,7 @@ class DemoServerTest(unittest.TestCase): num_chars = len(phonemes) if config.use_phonemes else len(symbols) model = setup_model(num_chars, 0, config) output_path = os.path.join(get_tests_output_path()) - save_checkpoint(model, None, 10, 10, 1, output_path) + save_checkpoint(model, None, 10, 10, 1, output_path, None) def test_in_out(self): self._create_random_model() From c613e0142fc4e648f989e6ead42659c41ba93b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20G=C3=B6lge?= Date: Fri, 12 Feb 2021 14:55:08 +0000 Subject: [PATCH 09/12] update ci to espeak-ng --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5f6db915..6570bad6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -25,7 +25,7 @@ jobs: - checkout - run: | sudo apt update - sudo apt install espeak git + sudo apt install espeak-ng git - run: sudo pip install --upgrade pip - run: sudo pip install -e . - run: | From 3b6ce04332245e4c87bfec1896b26e403882f887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20G=C3=B6lge?= Date: Mon, 15 Feb 2021 13:02:29 +0100 Subject: [PATCH 10/12] Update TTS/bin/find_unique_chars.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Thalheim --- TTS/bin/find_unique_chars.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TTS/bin/find_unique_chars.py b/TTS/bin/find_unique_chars.py index 654a3ff9..f9b6827b 100644 --- a/TTS/bin/find_unique_chars.py +++ b/TTS/bin/find_unique_chars.py @@ -35,13 +35,13 @@ def main(): preprocessor = get_preprocessor_by_name(args.dataset) items = preprocessor(os.path.dirname(args.meta_file), os.path.basename(args.meta_file)) - texts = " ".join([item[0] for item in items]) + texts = "".join(item[0] for item in items) chars = set(texts) - lower_chars = set(texts.lower()) + lower_chars = filter(lambda c: c.islower(), chars) print(f" > Number of unique characters: {len(chars)}") print(f" > Unique characters: {''.join(sorted(chars))}") print(f" > Unique lower characters: {''.join(sorted(lower_chars))}") if __name__ == "__main__": - main() \ No newline at end of file + main() From 80af8ca5e1f7059b0a10be1ddeabf01daaae57a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20G=C3=B6lge?= Date: Mon, 15 Feb 2021 13:03:59 +0100 Subject: [PATCH 11/12] Update TTS/utils/arguments.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Thalheim --- TTS/utils/arguments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TTS/utils/arguments.py b/TTS/utils/arguments.py index 7d8f4adf..ebacab1b 100644 --- a/TTS/utils/arguments.py +++ b/TTS/utils/arguments.py @@ -182,7 +182,7 @@ def process_args(args, model_type): # if model characters are not set in the config file # save the default set to the config file for future # compatibility. - if model_class == 'TTS' and not 'characters' in c.keys(): + if model_class == 'TTS' and not 'characters' in c: used_characters = parse_symbols() new_fields['characters'] = used_characters copy_model_files(c, args.config_path, From ff218e23700a7266cd987acb690fb36d0f7dd071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20G=C3=B6lge?= Date: Mon, 15 Feb 2021 12:07:02 +0000 Subject: [PATCH 12/12] remove redundancy --- TTS/tts/utils/text/symbols.py | 1 - 1 file changed, 1 deletion(-) diff --git a/TTS/tts/utils/text/symbols.py b/TTS/tts/utils/text/symbols.py index 4bd1d6c2..83435917 100644 --- a/TTS/tts/utils/text/symbols.py +++ b/TTS/tts/utils/text/symbols.py @@ -25,7 +25,6 @@ _eos = '~' _bos = '^' _characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!\'(),-.:;? ' _punctuations = '!\'(),-.:;? ' -# _phoneme_punctuations = '.!;:,?' # Phonemes definition (All IPA characters) _vowels = 'iyɨʉɯuɪʏʊeøɘəɵɤoɛœɜɞʌɔæɐaɶɑɒᵻ'