From 0610c661c8d1a98dff252f248f6a715f0310fa90 Mon Sep 17 00:00:00 2001 From: Hihixiaolv <3181871180@qq.com> Date: Tue, 9 Jan 2024 19:56:44 +0800 Subject: [PATCH 1/9] add readme --- README-group part.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 README-group part.txt diff --git a/README-group part.txt b/README-group part.txt new file mode 100644 index 00000000..e69de29b From 03b4d7183fe42459e923655ec245a3b790d295cf Mon Sep 17 00:00:00 2001 From: yaya <3181871180@qq.com> Date: Wed, 24 Jan 2024 19:49:14 +0800 Subject: [PATCH 2/9] the final model --- encoder/__init__.py | 0 encoder/audio.py | 117 ++ encoder/config.py | 45 + encoder/data_objects/__init__.py | 2 + encoder/data_objects/random_cycler.py | 37 + encoder/data_objects/speaker.py | 40 + encoder/data_objects/speaker_batch.py | 13 + .../speaker_verification_dataset.py | 56 + encoder/data_objects/utterance.py | 26 + encoder/inference.py | 178 +++ encoder/model.py | 135 ++ encoder/params_data.py | 29 + encoder/params_model.py | 11 + encoder/preprocess.py | 184 +++ encoder/train.py | 125 ++ encoder/visualizations.py | 179 +++ params.py | 33 + talkingface/config/configurator.py | 12 +- talkingface/config/diffVC.py | 45 + .../data/dataprocess/wav2lip_process.py | 2 +- talkingface/data/dataset/__init__.py | 6 +- .../data/dataset/data_objects/__init__.py | 4 + .../dataset/data_objects/random_cycler.py | 39 + .../data/dataset/data_objects/speaker.py | 42 + .../dataset/data_objects/speaker_batch.py | 14 + .../speaker_verification_dataset.py | 58 + .../data/dataset/data_objects/utterance.py | 28 + talkingface/data/dataset/diffvc_dataset.py | 379 ++++++ talkingface/model/.DS_Store | Bin 0 -> 6148 bytes .../audio_driven_talkingface/__init__.py | 2 +- .../model/voice_conversion/__init__.py | 1 + talkingface/model/voice_conversion/diffvc.py | 1155 +++++++++++++++++ .../model/voice_conversion/hifi-gan.py | 340 +++++ .../properties/dataset/diffvc_dataset.yaml | 19 + .../dataset/diffvc_encoder_dataset.yaml | 32 + talkingface/properties/dataset/params_data.py | 30 + talkingface/properties/model/diffvc.yaml | 39 + .../properties/model/diffvc_encoder.yaml | 14 + talkingface/properties/overall.yaml | 4 +- talkingface/quick_start/quick_start.py | 5 +- talkingface/trainer/diffvc_trainer.py | 0 talkingface/trainer/trainer.py | 445 ++++++- talkingface/utils/.DS_Store | Bin 0 -> 8196 bytes talkingface/utils/exceptions_libritts.txt | 208 +++ talkingface/utils/exceptions_vctk.txt | 359 +++++ talkingface/utils/face_detection/.DS_Store | Bin 0 -> 6148 bytes .../utils/face_detection/detection/.DS_Store | Bin 0 -> 6148 bytes talkingface/utils/utils.py | 52 +- talkingface/utils/valid.txt | 10 + .../voice_conversion_talkingface/.DS_Store | Bin 0 -> 6148 bytes .../DiffVC_hifi-gan_xutils.py | 60 + .../DiffVC_model_utils.py | 110 ++ .../DiffVC_speaker_encoder_utils.py | 303 +++++ .../DiffVC_utils.py | 27 + .../voice_conversion_talkingface/audio.py | 157 +++ .../voice_conversion_talkingface/inference.py | 209 +++ .../params_data.py | 30 + .../params_model.py | 12 + 58 files changed, 5445 insertions(+), 17 deletions(-) create mode 100644 encoder/__init__.py create mode 100644 encoder/audio.py create mode 100644 encoder/config.py create mode 100644 encoder/data_objects/__init__.py create mode 100644 encoder/data_objects/random_cycler.py create mode 100644 encoder/data_objects/speaker.py create mode 100644 encoder/data_objects/speaker_batch.py create mode 100644 encoder/data_objects/speaker_verification_dataset.py create mode 100644 encoder/data_objects/utterance.py create mode 100644 encoder/inference.py create mode 100644 encoder/model.py create mode 100644 encoder/params_data.py create mode 100644 encoder/params_model.py create mode 100644 encoder/preprocess.py create mode 100644 encoder/train.py create mode 100644 encoder/visualizations.py create mode 100644 params.py create mode 100644 talkingface/config/diffVC.py create mode 100644 talkingface/data/dataset/data_objects/__init__.py create mode 100644 talkingface/data/dataset/data_objects/random_cycler.py create mode 100644 talkingface/data/dataset/data_objects/speaker.py create mode 100644 talkingface/data/dataset/data_objects/speaker_batch.py create mode 100644 talkingface/data/dataset/data_objects/speaker_verification_dataset.py create mode 100644 talkingface/data/dataset/data_objects/utterance.py create mode 100644 talkingface/data/dataset/diffvc_dataset.py create mode 100644 talkingface/model/.DS_Store create mode 100644 talkingface/model/voice_conversion/__init__.py create mode 100644 talkingface/model/voice_conversion/diffvc.py create mode 100644 talkingface/model/voice_conversion/hifi-gan.py create mode 100644 talkingface/properties/dataset/diffvc_dataset.yaml create mode 100644 talkingface/properties/dataset/diffvc_encoder_dataset.yaml create mode 100644 talkingface/properties/dataset/params_data.py create mode 100644 talkingface/properties/model/diffvc.yaml create mode 100644 talkingface/properties/model/diffvc_encoder.yaml create mode 100644 talkingface/trainer/diffvc_trainer.py create mode 100644 talkingface/utils/.DS_Store create mode 100644 talkingface/utils/exceptions_libritts.txt create mode 100644 talkingface/utils/exceptions_vctk.txt create mode 100644 talkingface/utils/face_detection/.DS_Store create mode 100644 talkingface/utils/face_detection/detection/.DS_Store create mode 100644 talkingface/utils/valid.txt create mode 100644 talkingface/utils/voice_conversion_talkingface/.DS_Store create mode 100644 talkingface/utils/voice_conversion_talkingface/DiffVC_hifi-gan_xutils.py create mode 100644 talkingface/utils/voice_conversion_talkingface/DiffVC_model_utils.py create mode 100644 talkingface/utils/voice_conversion_talkingface/DiffVC_speaker_encoder_utils.py create mode 100644 talkingface/utils/voice_conversion_talkingface/DiffVC_utils.py create mode 100644 talkingface/utils/voice_conversion_talkingface/audio.py create mode 100644 talkingface/utils/voice_conversion_talkingface/inference.py create mode 100644 talkingface/utils/voice_conversion_talkingface/params_data.py create mode 100644 talkingface/utils/voice_conversion_talkingface/params_model.py diff --git a/encoder/__init__.py b/encoder/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/encoder/audio.py b/encoder/audio.py new file mode 100644 index 00000000..799aa835 --- /dev/null +++ b/encoder/audio.py @@ -0,0 +1,117 @@ +from scipy.ndimage.morphology import binary_dilation +from encoder.params_data import * +from pathlib import Path +from typing import Optional, Union +from warnings import warn +import numpy as np +import librosa +import struct + +try: + import webrtcvad +except: + warn("Unable to import 'webrtcvad'. This package enables noise removal and is recommended.") + webrtcvad=None + +int16_max = (2 ** 15) - 1 + + +def preprocess_wav(fpath_or_wav: Union[str, Path, np.ndarray], + source_sr: Optional[int] = None, + normalize: Optional[bool] = True, + trim_silence: Optional[bool] = True): + """ + Applies the preprocessing operations used in training the Speaker Encoder to a waveform + either on disk or in memory. The waveform will be resampled to match the data hyperparameters. + + :param fpath_or_wav: either a filepath to an audio file (many extensions are supported, not + just .wav), either the waveform as a numpy array of floats. + :param source_sr: if passing an audio waveform, the sampling rate of the waveform before + preprocessing. After preprocessing, the waveform's sampling rate will match the data + hyperparameters. If passing a filepath, the sampling rate will be automatically detected and + this argument will be ignored. + """ + # Load the wav from disk if needed + if isinstance(fpath_or_wav, str) or isinstance(fpath_or_wav, Path): + wav, source_sr = librosa.load(str(fpath_or_wav), sr=None) + else: + wav = fpath_or_wav + + # Resample the wav if needed + if source_sr is not None and source_sr != sampling_rate: + wav = librosa.resample(wav, source_sr, sampling_rate) + + # Apply the preprocessing: normalize volume and shorten long silences + if normalize: + wav = normalize_volume(wav, audio_norm_target_dBFS, increase_only=True) + if webrtcvad and trim_silence: + wav = trim_long_silences(wav) + + return wav + + +def wav_to_mel_spectrogram(wav): + """ + Derives a mel spectrogram ready to be used by the encoder from a preprocessed audio waveform. + Note: this not a log-mel spectrogram. + """ + frames = librosa.feature.melspectrogram( + wav, + sampling_rate, + n_fft=int(sampling_rate * mel_window_length / 1000), + hop_length=int(sampling_rate * mel_window_step / 1000), + n_mels=mel_n_channels + ) + return frames.astype(np.float32).T + + +def trim_long_silences(wav): + """ + Ensures that segments without voice in the waveform remain no longer than a + threshold determined by the VAD parameters in params.py. + + :param wav: the raw waveform as a numpy array of floats + :return: the same waveform with silences trimmed away (length <= original wav length) + """ + # Compute the voice detection window size + samples_per_window = (vad_window_length * sampling_rate) // 1000 + + # Trim the end of the audio to have a multiple of the window size + wav = wav[:len(wav) - (len(wav) % samples_per_window)] + + # Convert the float waveform to 16-bit mono PCM + pcm_wave = struct.pack("%dh" % len(wav), *(np.round(wav * int16_max)).astype(np.int16)) + + # Perform voice activation detection + voice_flags = [] + vad = webrtcvad.Vad(mode=3) + for window_start in range(0, len(wav), samples_per_window): + window_end = window_start + samples_per_window + voice_flags.append(vad.is_speech(pcm_wave[window_start * 2:window_end * 2], + sample_rate=sampling_rate)) + voice_flags = np.array(voice_flags) + + # Smooth the voice detection with a moving average + def moving_average(array, width): + array_padded = np.concatenate((np.zeros((width - 1) // 2), array, np.zeros(width // 2))) + ret = np.cumsum(array_padded, dtype=float) + ret[width:] = ret[width:] - ret[:-width] + return ret[width - 1:] / width + + audio_mask = moving_average(voice_flags, vad_moving_average_width) + audio_mask = np.round(audio_mask).astype(np.bool) + + # Dilate the voiced regions + audio_mask = binary_dilation(audio_mask, np.ones(vad_max_silence_length + 1)) + audio_mask = np.repeat(audio_mask, samples_per_window) + + return wav[audio_mask == True] + + +def normalize_volume(wav, target_dBFS, increase_only=False, decrease_only=False): + if increase_only and decrease_only: + raise ValueError("Both increase only and decrease only are set") + dBFS_change = target_dBFS - 10 * np.log10(np.mean(wav ** 2)) + if (dBFS_change < 0 and increase_only) or (dBFS_change > 0 and decrease_only): + return wav + return wav * (10 ** (dBFS_change / 20)) diff --git a/encoder/config.py b/encoder/config.py new file mode 100644 index 00000000..1c21312f --- /dev/null +++ b/encoder/config.py @@ -0,0 +1,45 @@ +librispeech_datasets = { + "train": { + "clean": ["LibriSpeech/train-clean-100", "LibriSpeech/train-clean-360"], + "other": ["LibriSpeech/train-other-500"] + }, + "test": { + "clean": ["LibriSpeech/test-clean"], + "other": ["LibriSpeech/test-other"] + }, + "dev": { + "clean": ["LibriSpeech/dev-clean"], + "other": ["LibriSpeech/dev-other"] + }, +} +libritts_datasets = { + "train": { + "clean": ["LibriTTS/train-clean-100", "LibriTTS/train-clean-360"], + "other": ["LibriTTS/train-other-500"] + }, + "test": { + "clean": ["LibriTTS/test-clean"], + "other": ["LibriTTS/test-other"] + }, + "dev": { + "clean": ["LibriTTS/dev-clean"], + "other": ["LibriTTS/dev-other"] + }, +} +voxceleb_datasets = { + "voxceleb1" : { + "train": ["VoxCeleb1/wav"], + "test": ["VoxCeleb1/test_wav"] + }, + "voxceleb2" : { + "train": ["VoxCeleb2/dev/aac"], + "test": ["VoxCeleb2/test_wav"] + } +} + +other_datasets = [ + "LJSpeech-1.1", + "VCTK-Corpus/wav48", +] + +anglophone_nationalites = ["australia", "canada", "ireland", "uk", "usa"] diff --git a/encoder/data_objects/__init__.py b/encoder/data_objects/__init__.py new file mode 100644 index 00000000..ef04ade6 --- /dev/null +++ b/encoder/data_objects/__init__.py @@ -0,0 +1,2 @@ +from encoder.data_objects.speaker_verification_dataset import SpeakerVerificationDataset +from encoder.data_objects.speaker_verification_dataset import SpeakerVerificationDataLoader diff --git a/encoder/data_objects/random_cycler.py b/encoder/data_objects/random_cycler.py new file mode 100644 index 00000000..c405db6b --- /dev/null +++ b/encoder/data_objects/random_cycler.py @@ -0,0 +1,37 @@ +import random + +class RandomCycler: + """ + Creates an internal copy of a sequence and allows access to its items in a constrained random + order. For a source sequence of n items and one or several consecutive queries of a total + of m items, the following guarantees hold (one implies the other): + - Each item will be returned between m // n and ((m - 1) // n) + 1 times. + - Between two appearances of the same item, there may be at most 2 * (n - 1) other items. + """ + + def __init__(self, source): + if len(source) == 0: + raise Exception("Can't create RandomCycler from an empty collection") + self.all_items = list(source) + self.next_items = [] + + def sample(self, count: int): + shuffle = lambda l: random.sample(l, len(l)) + + out = [] + while count > 0: + if count >= len(self.all_items): + out.extend(shuffle(list(self.all_items))) + count -= len(self.all_items) + continue + n = min(count, len(self.next_items)) + out.extend(self.next_items[:n]) + count -= n + self.next_items = self.next_items[n:] + if len(self.next_items) == 0: + self.next_items = shuffle(list(self.all_items)) + return out + + def __next__(self): + return self.sample(1)[0] + diff --git a/encoder/data_objects/speaker.py b/encoder/data_objects/speaker.py new file mode 100644 index 00000000..494e882f --- /dev/null +++ b/encoder/data_objects/speaker.py @@ -0,0 +1,40 @@ +from encoder.data_objects.random_cycler import RandomCycler +from encoder.data_objects.utterance import Utterance +from pathlib import Path + +# Contains the set of utterances of a single speaker +class Speaker: + def __init__(self, root: Path): + self.root = root + self.name = root.name + self.utterances = None + self.utterance_cycler = None + + def _load_utterances(self): + with self.root.joinpath("_sources.txt").open("r") as sources_file: + sources = [l.split(",") for l in sources_file] + sources = {frames_fname: wave_fpath for frames_fname, wave_fpath in sources} + self.utterances = [Utterance(self.root.joinpath(f), w) for f, w in sources.items()] + self.utterance_cycler = RandomCycler(self.utterances) + + def random_partial(self, count, n_frames): + """ + Samples a batch of unique partial utterances from the disk in a way that all + utterances come up at least once every two cycles and in a random order every time. + + :param count: The number of partial utterances to sample from the set of utterances from + that speaker. Utterances are guaranteed not to be repeated if is not larger than + the number of utterances available. + :param n_frames: The number of frames in the partial utterance. + :return: A list of tuples (utterance, frames, range) where utterance is an Utterance, + frames are the frames of the partial utterances and range is the range of the partial + utterance with regard to the complete utterance. + """ + if self.utterances is None: + self._load_utterances() + + utterances = self.utterance_cycler.sample(count) + + a = [(u,) + u.random_partial(n_frames) for u in utterances] + + return a diff --git a/encoder/data_objects/speaker_batch.py b/encoder/data_objects/speaker_batch.py new file mode 100644 index 00000000..e219b738 --- /dev/null +++ b/encoder/data_objects/speaker_batch.py @@ -0,0 +1,13 @@ +import numpy as np +from typing import List +from encoder.data_objects.speaker import Speaker + + +class SpeakerBatch: + def __init__(self, speakers: List[Speaker], utterances_per_speaker: int, n_frames: int): + self.speakers = speakers + self.partials = {s: s.random_partial(utterances_per_speaker, n_frames) for s in speakers} + + # Array of shape (n_speakers * n_utterances, n_frames, mel_n), e.g. for 3 speakers with + # 4 utterances each of 160 frames of 40 mel coefficients: (12, 160, 40) + self.data = np.array([frames for s in speakers for _, frames, _ in self.partials[s]]) diff --git a/encoder/data_objects/speaker_verification_dataset.py b/encoder/data_objects/speaker_verification_dataset.py new file mode 100644 index 00000000..77a6e05e --- /dev/null +++ b/encoder/data_objects/speaker_verification_dataset.py @@ -0,0 +1,56 @@ +from encoder.data_objects.random_cycler import RandomCycler +from encoder.data_objects.speaker_batch import SpeakerBatch +from encoder.data_objects.speaker import Speaker +from encoder.params_data import partials_n_frames +from torch.utils.data import Dataset, DataLoader +from pathlib import Path + +# TODO: improve with a pool of speakers for data efficiency + +class SpeakerVerificationDataset(Dataset): + def __init__(self, datasets_root: Path): + self.root = datasets_root + speaker_dirs = [f for f in self.root.glob("*") if f.is_dir()] + if len(speaker_dirs) == 0: + raise Exception("No speakers found. Make sure you are pointing to the directory " + "containing all preprocessed speaker directories.") + self.speakers = [Speaker(speaker_dir) for speaker_dir in speaker_dirs] + self.speaker_cycler = RandomCycler(self.speakers) + + def __len__(self): + return int(1e10) + + def __getitem__(self, index): + return next(self.speaker_cycler) + + def get_logs(self): + log_string = "" + for log_fpath in self.root.glob("*.txt"): + with log_fpath.open("r") as log_file: + log_string += "".join(log_file.readlines()) + return log_string + + +class SpeakerVerificationDataLoader(DataLoader): + def __init__(self, dataset, speakers_per_batch, utterances_per_speaker, sampler=None, + batch_sampler=None, num_workers=0, pin_memory=False, timeout=0, + worker_init_fn=None): + self.utterances_per_speaker = utterances_per_speaker + + super().__init__( + dataset=dataset, + batch_size=speakers_per_batch, + shuffle=False, + sampler=sampler, + batch_sampler=batch_sampler, + num_workers=num_workers, + collate_fn=self.collate, + pin_memory=pin_memory, + drop_last=False, + timeout=timeout, + worker_init_fn=worker_init_fn + ) + + def collate(self, speakers): + return SpeakerBatch(speakers, self.utterances_per_speaker, partials_n_frames) + \ No newline at end of file diff --git a/encoder/data_objects/utterance.py b/encoder/data_objects/utterance.py new file mode 100644 index 00000000..0768c342 --- /dev/null +++ b/encoder/data_objects/utterance.py @@ -0,0 +1,26 @@ +import numpy as np + + +class Utterance: + def __init__(self, frames_fpath, wave_fpath): + self.frames_fpath = frames_fpath + self.wave_fpath = wave_fpath + + def get_frames(self): + return np.load(self.frames_fpath) + + def random_partial(self, n_frames): + """ + Crops the frames into a partial utterance of n_frames + + :param n_frames: The number of frames of the partial utterance + :return: the partial utterance frames and a tuple indicating the start and end of the + partial utterance in the complete utterance. + """ + frames = self.get_frames() + if frames.shape[0] == n_frames: + start = 0 + else: + start = np.random.randint(0, frames.shape[0] - n_frames) + end = start + n_frames + return frames[start:end], (start, end) \ No newline at end of file diff --git a/encoder/inference.py b/encoder/inference.py new file mode 100644 index 00000000..43862e43 --- /dev/null +++ b/encoder/inference.py @@ -0,0 +1,178 @@ +from encoder.params_data import * +from encoder.model import SpeakerEncoder +from encoder.audio import preprocess_wav # We want to expose this function from here +from matplotlib import cm +from encoder import audio +from pathlib import Path +import numpy as np +import torch + +_model = None # type: SpeakerEncoder +_device = None # type: torch.device + + +def load_model(weights_fpath: Path, device=None): + """ + Loads the model in memory. If this function is not explicitely called, it will be run on the + first call to embed_frames() with the default weights file. + + :param weights_fpath: the path to saved model weights. + :param device: either a torch device or the name of a torch device (e.g. "cpu", "cuda"). The + model will be loaded and will run on this device. Outputs will however always be on the cpu. + If None, will default to your GPU if it"s available, otherwise your CPU. + """ + # TODO: I think the slow loading of the encoder might have something to do with the device it + # was saved on. Worth investigating. + global _model, _device + if device is None: + _device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + elif isinstance(device, str): + _device = torch.device(device) + _model = SpeakerEncoder(_device, torch.device("cpu")) + checkpoint = torch.load(weights_fpath, _device) + _model.load_state_dict(checkpoint["model_state"]) + _model.eval() + print("Loaded encoder \"%s\" trained to step %d" % (weights_fpath.name, checkpoint["step"])) + + +def is_loaded(): + return _model is not None + + +def embed_frames_batch(frames_batch): + """ + Computes embeddings for a batch of mel spectrogram. + + :param frames_batch: a batch mel of spectrogram as a numpy array of float32 of shape + (batch_size, n_frames, n_channels) + :return: the embeddings as a numpy array of float32 of shape (batch_size, model_embedding_size) + """ + if _model is None: + raise Exception("Model was not loaded. Call load_model() before inference.") + + frames = torch.from_numpy(frames_batch).to(_device) + embed = _model.forward(frames).detach().cpu().numpy() + return embed + + +def compute_partial_slices(n_samples, partial_utterance_n_frames=partials_n_frames, + min_pad_coverage=0.75, overlap=0.5): + """ + Computes where to split an utterance waveform and its corresponding mel spectrogram to obtain + partial utterances of each. Both the waveform and the mel + spectrogram slices are returned, so as to make each partial utterance waveform correspond to + its spectrogram. This function assumes that the mel spectrogram parameters used are those + defined in params_data.py. + + The returned ranges may be indexing further than the length of the waveform. It is + recommended that you pad the waveform with zeros up to wave_slices[-1].stop. + + :param n_samples: the number of samples in the waveform + :param partial_utterance_n_frames: the number of mel spectrogram frames in each partial + utterance + :param min_pad_coverage: when reaching the last partial utterance, it may or may not have + enough frames. If at least of are present, + then the last partial utterance will be considered, as if we padded the audio. Otherwise, + it will be discarded, as if we trimmed the audio. If there aren't enough frames for 1 partial + utterance, this parameter is ignored so that the function always returns at least 1 slice. + :param overlap: by how much the partial utterance should overlap. If set to 0, the partial + utterances are entirely disjoint. + :return: the waveform slices and mel spectrogram slices as lists of array slices. Index + respectively the waveform and the mel spectrogram with these slices to obtain the partial + utterances. + """ + assert 0 <= overlap < 1 + assert 0 < min_pad_coverage <= 1 + + samples_per_frame = int((sampling_rate * mel_window_step / 1000)) + n_frames = int(np.ceil((n_samples + 1) / samples_per_frame)) + frame_step = max(int(np.round(partial_utterance_n_frames * (1 - overlap))), 1) + + # Compute the slices + wav_slices, mel_slices = [], [] + steps = max(1, n_frames - partial_utterance_n_frames + frame_step + 1) + for i in range(0, steps, frame_step): + mel_range = np.array([i, i + partial_utterance_n_frames]) + wav_range = mel_range * samples_per_frame + mel_slices.append(slice(*mel_range)) + wav_slices.append(slice(*wav_range)) + + # Evaluate whether extra padding is warranted or not + last_wav_range = wav_slices[-1] + coverage = (n_samples - last_wav_range.start) / (last_wav_range.stop - last_wav_range.start) + if coverage < min_pad_coverage and len(mel_slices) > 1: + mel_slices = mel_slices[:-1] + wav_slices = wav_slices[:-1] + + return wav_slices, mel_slices + + +def embed_utterance(wav, using_partials=True, return_partials=False, **kwargs): + """ + Computes an embedding for a single utterance. + + # TODO: handle multiple wavs to benefit from batching on GPU + :param wav: a preprocessed (see audio.py) utterance waveform as a numpy array of float32 + :param using_partials: if True, then the utterance is split in partial utterances of + frames and the utterance embedding is computed from their + normalized average. If False, the utterance is instead computed from feeding the entire + spectogram to the network. + :param return_partials: if True, the partial embeddings will also be returned along with the + wav slices that correspond to the partial embeddings. + :param kwargs: additional arguments to compute_partial_splits() + :return: the embedding as a numpy array of float32 of shape (model_embedding_size,). If + is True, the partial utterances as a numpy array of float32 of shape + (n_partials, model_embedding_size) and the wav partials as a list of slices will also be + returned. If is simultaneously set to False, both these values will be None + instead. + """ + # Process the entire utterance if not using partials + if not using_partials: + frames = audio.wav_to_mel_spectrogram(wav) + embed = embed_frames_batch(frames[None, ...])[0] + if return_partials: + return embed, None, None + return embed + + # Compute where to split the utterance into partials and pad if necessary + wave_slices, mel_slices = compute_partial_slices(len(wav), **kwargs) + max_wave_length = wave_slices[-1].stop + if max_wave_length >= len(wav): + wav = np.pad(wav, (0, max_wave_length - len(wav)), "constant") + + # Split the utterance into partials + frames = audio.wav_to_mel_spectrogram(wav) + frames_batch = np.array([frames[s] for s in mel_slices]) + partial_embeds = embed_frames_batch(frames_batch) + + # Compute the utterance embedding from the partial embeddings + raw_embed = np.mean(partial_embeds, axis=0) + embed = raw_embed / np.linalg.norm(raw_embed, 2) + + if return_partials: + return embed, partial_embeds, wave_slices + return embed + + +def embed_speaker(wavs, **kwargs): + raise NotImplemented() + + +def plot_embedding_as_heatmap(embed, ax=None, title="", shape=None, color_range=(0, 0.30)): + import matplotlib.pyplot as plt + if ax is None: + ax = plt.gca() + + if shape is None: + height = int(np.sqrt(len(embed))) + shape = (height, -1) + embed = embed.reshape(shape) + + cmap = cm.get_cmap() + mappable = ax.imshow(embed, cmap=cmap) + cbar = plt.colorbar(mappable, ax=ax, fraction=0.046, pad=0.04) + sm = cm.ScalarMappable(cmap=cmap) + sm.set_clim(*color_range) + + ax.set_xticks([]), ax.set_yticks([]) + ax.set_title(title) diff --git a/encoder/model.py b/encoder/model.py new file mode 100644 index 00000000..e050d320 --- /dev/null +++ b/encoder/model.py @@ -0,0 +1,135 @@ +from encoder.params_model import * +from encoder.params_data import * +from scipy.interpolate import interp1d +from sklearn.metrics import roc_curve +from torch.nn.utils import clip_grad_norm_ +from scipy.optimize import brentq +from torch import nn +import numpy as np +import torch + + +class SpeakerEncoder(nn.Module): + def __init__(self, device, loss_device): + super().__init__() + self.loss_device = loss_device + + # Network defition + self.lstm = nn.LSTM(input_size=mel_n_channels, + hidden_size=model_hidden_size, + num_layers=model_num_layers, + batch_first=True).to(device) + self.linear = nn.Linear(in_features=model_hidden_size, + out_features=model_embedding_size).to(device) + self.relu = torch.nn.ReLU().to(device) + + # Cosine similarity scaling (with fixed initial parameter values) + self.similarity_weight = nn.Parameter(torch.tensor([10.])).to(loss_device) + self.similarity_bias = nn.Parameter(torch.tensor([-5.])).to(loss_device) + + # Loss + self.loss_fn = nn.CrossEntropyLoss().to(loss_device) + + def do_gradient_ops(self): + # Gradient scale + self.similarity_weight.grad *= 0.01 + self.similarity_bias.grad *= 0.01 + + # Gradient clipping + clip_grad_norm_(self.parameters(), 3, norm_type=2) + + def forward(self, utterances, hidden_init=None): + """ + Computes the embeddings of a batch of utterance spectrograms. + + :param utterances: batch of mel-scale filterbanks of same duration as a tensor of shape + (batch_size, n_frames, n_channels) + :param hidden_init: initial hidden state of the LSTM as a tensor of shape (num_layers, + batch_size, hidden_size). Will default to a tensor of zeros if None. + :return: the embeddings as a tensor of shape (batch_size, embedding_size) + """ + # Pass the input through the LSTM layers and retrieve all outputs, the final hidden state + # and the final cell state. + out, (hidden, cell) = self.lstm(utterances, hidden_init) + + # We take only the hidden state of the last layer + embeds_raw = self.relu(self.linear(hidden[-1])) + + # L2-normalize it + embeds = embeds_raw / (torch.norm(embeds_raw, dim=1, keepdim=True) + 1e-5) + + return embeds + + def similarity_matrix(self, embeds): + """ + Computes the similarity matrix according the section 2.1 of GE2E. + + :param embeds: the embeddings as a tensor of shape (speakers_per_batch, + utterances_per_speaker, embedding_size) + :return: the similarity matrix as a tensor of shape (speakers_per_batch, + utterances_per_speaker, speakers_per_batch) + """ + speakers_per_batch, utterances_per_speaker = embeds.shape[:2] + + # Inclusive centroids (1 per speaker). Cloning is needed for reverse differentiation + centroids_incl = torch.mean(embeds, dim=1, keepdim=True) + centroids_incl = centroids_incl.clone() / (torch.norm(centroids_incl, dim=2, keepdim=True) + 1e-5) + + # Exclusive centroids (1 per utterance) + centroids_excl = (torch.sum(embeds, dim=1, keepdim=True) - embeds) + centroids_excl /= (utterances_per_speaker - 1) + centroids_excl = centroids_excl.clone() / (torch.norm(centroids_excl, dim=2, keepdim=True) + 1e-5) + + # Similarity matrix. The cosine similarity of already 2-normed vectors is simply the dot + # product of these vectors (which is just an element-wise multiplication reduced by a sum). + # We vectorize the computation for efficiency. + sim_matrix = torch.zeros(speakers_per_batch, utterances_per_speaker, + speakers_per_batch).to(self.loss_device) + mask_matrix = 1 - np.eye(speakers_per_batch, dtype=np.int) + for j in range(speakers_per_batch): + mask = np.where(mask_matrix[j])[0] + sim_matrix[mask, :, j] = (embeds[mask] * centroids_incl[j]).sum(dim=2) + sim_matrix[j, :, j] = (embeds[j] * centroids_excl[j]).sum(dim=1) + + ## Even more vectorized version (slower maybe because of transpose) + # sim_matrix2 = torch.zeros(speakers_per_batch, speakers_per_batch, utterances_per_speaker + # ).to(self.loss_device) + # eye = np.eye(speakers_per_batch, dtype=np.int) + # mask = np.where(1 - eye) + # sim_matrix2[mask] = (embeds[mask[0]] * centroids_incl[mask[1]]).sum(dim=2) + # mask = np.where(eye) + # sim_matrix2[mask] = (embeds * centroids_excl).sum(dim=2) + # sim_matrix2 = sim_matrix2.transpose(1, 2) + + sim_matrix = sim_matrix * self.similarity_weight + self.similarity_bias + return sim_matrix + + def loss(self, embeds): + """ + Computes the softmax loss according the section 2.1 of GE2E. + + :param embeds: the embeddings as a tensor of shape (speakers_per_batch, + utterances_per_speaker, embedding_size) + :return: the loss and the EER for this batch of embeddings. + """ + speakers_per_batch, utterances_per_speaker = embeds.shape[:2] + + # Loss + sim_matrix = self.similarity_matrix(embeds) + sim_matrix = sim_matrix.reshape((speakers_per_batch * utterances_per_speaker, + speakers_per_batch)) + ground_truth = np.repeat(np.arange(speakers_per_batch), utterances_per_speaker) + target = torch.from_numpy(ground_truth).long().to(self.loss_device) + loss = self.loss_fn(sim_matrix, target) + + # EER (not backpropagated) + with torch.no_grad(): + inv_argmax = lambda i: np.eye(1, speakers_per_batch, i, dtype=np.int)[0] + labels = np.array([inv_argmax(i) for i in ground_truth]) + preds = sim_matrix.detach().cpu().numpy() + + # Snippet from https://yangcha.github.io/EER-ROC/ + fpr, tpr, thresholds = roc_curve(labels.flatten(), preds.flatten()) + eer = brentq(lambda x: 1. - x - interp1d(fpr, tpr)(x), 0., 1.) + + return loss, eer diff --git a/encoder/params_data.py b/encoder/params_data.py new file mode 100644 index 00000000..bdb1716e --- /dev/null +++ b/encoder/params_data.py @@ -0,0 +1,29 @@ + +## Mel-filterbank +mel_window_length = 25 # In milliseconds +mel_window_step = 10 # In milliseconds +mel_n_channels = 40 + + +## Audio +sampling_rate = 16000 +# Number of spectrogram frames in a partial utterance +partials_n_frames = 160 # 1600 ms +# Number of spectrogram frames at inference +inference_n_frames = 80 # 800 ms + + +## Voice Activation Detection +# Window size of the VAD. Must be either 10, 20 or 30 milliseconds. +# This sets the granularity of the VAD. Should not need to be changed. +vad_window_length = 30 # In milliseconds +# Number of frames to average together when performing the moving average smoothing. +# The larger this value, the larger the VAD variations must be to not get smoothed out. +vad_moving_average_width = 8 +# Maximum number of consecutive silent frames a segment can have. +vad_max_silence_length = 6 + + +## Audio volume normalization +audio_norm_target_dBFS = -30 + diff --git a/encoder/params_model.py b/encoder/params_model.py new file mode 100644 index 00000000..3e356472 --- /dev/null +++ b/encoder/params_model.py @@ -0,0 +1,11 @@ + +## Model parameters +model_hidden_size = 256 +model_embedding_size = 256 +model_num_layers = 3 + + +## Training parameters +learning_rate_init = 1e-4 +speakers_per_batch = 64 +utterances_per_speaker = 10 diff --git a/encoder/preprocess.py b/encoder/preprocess.py new file mode 100644 index 00000000..d2dfc5ec --- /dev/null +++ b/encoder/preprocess.py @@ -0,0 +1,184 @@ +from datetime import datetime +from functools import partial +from multiprocessing import Pool +from pathlib import Path + +import numpy as np +from tqdm import tqdm + +from encoder import audio +from encoder.config import librispeech_datasets, anglophone_nationalites +from encoder.params_data import * + + +_AUDIO_EXTENSIONS = ("wav", "flac", "m4a", "mp3") + +class DatasetLog: + """ + Registers metadata about the dataset in a text file. + """ + def __init__(self, root, name): + self.text_file = open(Path(root, "Log_%s.txt" % name.replace("/", "_")), "w") + self.sample_data = dict() + + start_time = str(datetime.now().strftime("%A %d %B %Y at %H:%M")) + self.write_line("Creating dataset %s on %s" % (name, start_time)) + self.write_line("-----") + self._log_params() + + def _log_params(self): + from encoder import params_data + self.write_line("Parameter values:") + for param_name in (p for p in dir(params_data) if not p.startswith("__")): + value = getattr(params_data, param_name) + self.write_line("\t%s: %s" % (param_name, value)) + self.write_line("-----") + + def write_line(self, line): + self.text_file.write("%s\n" % line) + + def add_sample(self, **kwargs): + for param_name, value in kwargs.items(): + if not param_name in self.sample_data: + self.sample_data[param_name] = [] + self.sample_data[param_name].append(value) + + def finalize(self): + self.write_line("Statistics:") + for param_name, values in self.sample_data.items(): + self.write_line("\t%s:" % param_name) + self.write_line("\t\tmin %.3f, max %.3f" % (np.min(values), np.max(values))) + self.write_line("\t\tmean %.3f, median %.3f" % (np.mean(values), np.median(values))) + self.write_line("-----") + end_time = str(datetime.now().strftime("%A %d %B %Y at %H:%M")) + self.write_line("Finished on %s" % end_time) + self.text_file.close() + + +def _init_preprocess_dataset(dataset_name, datasets_root, out_dir) -> (Path, DatasetLog): + dataset_root = datasets_root.joinpath(dataset_name) + if not dataset_root.exists(): + print("Couldn\'t find %s, skipping this dataset." % dataset_root) + return None, None + return dataset_root, DatasetLog(out_dir, dataset_name) + + +def _preprocess_speaker(speaker_dir: Path, datasets_root: Path, out_dir: Path, skip_existing: bool): + # Give a name to the speaker that includes its dataset + speaker_name = "_".join(speaker_dir.relative_to(datasets_root).parts) + + # Create an output directory with that name, as well as a txt file containing a + # reference to each source file. + speaker_out_dir = out_dir.joinpath(speaker_name) + speaker_out_dir.mkdir(exist_ok=True) + sources_fpath = speaker_out_dir.joinpath("_sources.txt") + + # There's a possibility that the preprocessing was interrupted earlier, check if + # there already is a sources file. + if sources_fpath.exists(): + try: + with sources_fpath.open("r") as sources_file: + existing_fnames = {line.split(",")[0] for line in sources_file} + except: + existing_fnames = {} + else: + existing_fnames = {} + + # Gather all audio files for that speaker recursively + sources_file = sources_fpath.open("a" if skip_existing else "w") + audio_durs = [] + for extension in _AUDIO_EXTENSIONS: + for in_fpath in speaker_dir.glob("**/*.%s" % extension): + # Check if the target output file already exists + out_fname = "_".join(in_fpath.relative_to(speaker_dir).parts) + out_fname = out_fname.replace(".%s" % extension, ".npy") + if skip_existing and out_fname in existing_fnames: + continue + + # Load and preprocess the waveform + wav = audio.preprocess_wav(in_fpath) + if len(wav) == 0: + continue + + # Create the mel spectrogram, discard those that are too short + frames = audio.wav_to_mel_spectrogram(wav) + if len(frames) < partials_n_frames: + continue + + out_fpath = speaker_out_dir.joinpath(out_fname) + np.save(out_fpath, frames) + sources_file.write("%s,%s\n" % (out_fname, in_fpath)) + audio_durs.append(len(wav) / sampling_rate) + + sources_file.close() + + return audio_durs + + +def _preprocess_speaker_dirs(speaker_dirs, dataset_name, datasets_root, out_dir, skip_existing, logger): + print("%s: Preprocessing data for %d speakers." % (dataset_name, len(speaker_dirs))) + + # Process the utterances for each speaker + work_fn = partial(_preprocess_speaker, datasets_root=datasets_root, out_dir=out_dir, skip_existing=skip_existing) + with Pool(4) as pool: + tasks = pool.imap(work_fn, speaker_dirs) + for sample_durs in tqdm(tasks, dataset_name, len(speaker_dirs), unit="speakers"): + for sample_dur in sample_durs: + logger.add_sample(duration=sample_dur) + + logger.finalize() + print("Done preprocessing %s.\n" % dataset_name) + + +def preprocess_librispeech(datasets_root: Path, out_dir: Path, skip_existing=False): + for dataset_name in librispeech_datasets["train"]["other"]: + # Initialize the preprocessing + dataset_root, logger = _init_preprocess_dataset(dataset_name, datasets_root, out_dir) + if not dataset_root: + return + + # Preprocess all speakers + speaker_dirs = list(dataset_root.glob("*")) + _preprocess_speaker_dirs(speaker_dirs, dataset_name, datasets_root, out_dir, skip_existing, logger) + + +def preprocess_voxceleb1(datasets_root: Path, out_dir: Path, skip_existing=False): + # Initialize the preprocessing + dataset_name = "VoxCeleb1" + dataset_root, logger = _init_preprocess_dataset(dataset_name, datasets_root, out_dir) + if not dataset_root: + return + + # Get the contents of the meta file + with dataset_root.joinpath("vox1_meta.csv").open("r") as metafile: + metadata = [line.split("\t") for line in metafile][1:] + + # Select the ID and the nationality, filter out non-anglophone speakers + nationalities = {line[0]: line[3] for line in metadata} + keep_speaker_ids = [speaker_id for speaker_id, nationality in nationalities.items() if + nationality.lower() in anglophone_nationalites] + print("VoxCeleb1: using samples from %d (presumed anglophone) speakers out of %d." % + (len(keep_speaker_ids), len(nationalities))) + + # Get the speaker directories for anglophone speakers only + speaker_dirs = dataset_root.joinpath("wav").glob("*") + speaker_dirs = [speaker_dir for speaker_dir in speaker_dirs if + speaker_dir.name in keep_speaker_ids] + print("VoxCeleb1: found %d anglophone speakers on the disk, %d missing (this is normal)." % + (len(speaker_dirs), len(keep_speaker_ids) - len(speaker_dirs))) + + # Preprocess all speakers + _preprocess_speaker_dirs(speaker_dirs, dataset_name, datasets_root, out_dir, skip_existing, logger) + + +def preprocess_voxceleb2(datasets_root: Path, out_dir: Path, skip_existing=False): + # Initialize the preprocessing + dataset_name = "VoxCeleb2" + dataset_root, logger = _init_preprocess_dataset(dataset_name, datasets_root, out_dir) + if not dataset_root: + return + + # Get the speaker directories + # Preprocess all speakers + speaker_dirs = list(dataset_root.joinpath("dev", "aac").glob("*")) + _preprocess_speaker_dirs(speaker_dirs, dataset_name, datasets_root, out_dir, skip_existing, logger) diff --git a/encoder/train.py b/encoder/train.py new file mode 100644 index 00000000..2bed4eb2 --- /dev/null +++ b/encoder/train.py @@ -0,0 +1,125 @@ +from pathlib import Path + +import torch + +from encoder.data_objects import SpeakerVerificationDataLoader, SpeakerVerificationDataset +from encoder.model import SpeakerEncoder +from encoder.params_model import * +from encoder.visualizations import Visualizations +from utils.profiler import Profiler + + +def sync(device: torch.device): + # For correct profiling (cuda operations are async) + if device.type == "cuda": + torch.cuda.synchronize(device) + + +def train(run_id: str, clean_data_root: Path, models_dir: Path, umap_every: int, save_every: int, + backup_every: int, vis_every: int, force_restart: bool, visdom_server: str, + no_visdom: bool): + # Create a dataset and a dataloader + dataset = SpeakerVerificationDataset(clean_data_root) + loader = SpeakerVerificationDataLoader( + dataset, + speakers_per_batch, + utterances_per_speaker, + num_workers=4, + ) + + # Setup the device on which to run the forward pass and the loss. These can be different, + # because the forward pass is faster on the GPU whereas the loss is often (depending on your + # hyperparameters) faster on the CPU. + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + # FIXME: currently, the gradient is None if loss_device is cuda + loss_device = torch.device("cpu") + + # Create the model and the optimizer + model = SpeakerEncoder(device, loss_device) + optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate_init) + init_step = 1 + + # Configure file path for the model + model_dir = models_dir / run_id + model_dir.mkdir(exist_ok=True, parents=True) + state_fpath = model_dir / "encoder.pt" + + # Load any existing model + if not force_restart: + if state_fpath.exists(): + print("Found existing model \"%s\", loading it and resuming training." % run_id) + checkpoint = torch.load(state_fpath) + init_step = checkpoint["step"] + model.load_state_dict(checkpoint["model_state"]) + optimizer.load_state_dict(checkpoint["optimizer_state"]) + optimizer.param_groups[0]["lr"] = learning_rate_init + else: + print("No model \"%s\" found, starting training from scratch." % run_id) + else: + print("Starting the training from scratch.") + model.train() + + # Initialize the visualization environment + vis = Visualizations(run_id, vis_every, server=visdom_server, disabled=no_visdom) + vis.log_dataset(dataset) + vis.log_params() + device_name = str(torch.cuda.get_device_name(0) if torch.cuda.is_available() else "CPU") + vis.log_implementation({"Device": device_name}) + + # Training loop + profiler = Profiler(summarize_every=10, disabled=False) + for step, speaker_batch in enumerate(loader, init_step): + profiler.tick("Blocking, waiting for batch (threaded)") + + # Forward pass + inputs = torch.from_numpy(speaker_batch.data).to(device) + sync(device) + profiler.tick("Data to %s" % device) + embeds = model(inputs) + sync(device) + profiler.tick("Forward pass") + embeds_loss = embeds.view((speakers_per_batch, utterances_per_speaker, -1)).to(loss_device) + loss, eer = model.loss(embeds_loss) + sync(loss_device) + profiler.tick("Loss") + + # Backward pass + model.zero_grad() + loss.backward() + profiler.tick("Backward pass") + model.do_gradient_ops() + optimizer.step() + profiler.tick("Parameter update") + + # Update visualizations + # learning_rate = optimizer.param_groups[0]["lr"] + vis.update(loss.item(), eer, step) + + # Draw projections and save them to the backup folder + if umap_every != 0 and step % umap_every == 0: + print("Drawing and saving projections (step %d)" % step) + projection_fpath = model_dir / f"umap_{step:06d}.png" + embeds = embeds.detach().cpu().numpy() + vis.draw_projections(embeds, utterances_per_speaker, step, projection_fpath) + vis.save() + + # Overwrite the latest version of the model + if save_every != 0 and step % save_every == 0: + print("Saving the model (step %d)" % step) + torch.save({ + "step": step + 1, + "model_state": model.state_dict(), + "optimizer_state": optimizer.state_dict(), + }, state_fpath) + + # Make a backup + if backup_every != 0 and step % backup_every == 0: + print("Making a backup (step %d)" % step) + backup_fpath = model_dir / f"encoder_{step:06d}.bak" + torch.save({ + "step": step + 1, + "model_state": model.state_dict(), + "optimizer_state": optimizer.state_dict(), + }, backup_fpath) + + profiler.tick("Extras (visualizations, saving)") diff --git a/encoder/visualizations.py b/encoder/visualizations.py new file mode 100644 index 00000000..d103944f --- /dev/null +++ b/encoder/visualizations.py @@ -0,0 +1,179 @@ +from datetime import datetime +from time import perf_counter as timer + +import numpy as np +import umap +import visdom + +from encoder.data_objects.speaker_verification_dataset import SpeakerVerificationDataset + + +colormap = np.array([ + [76, 255, 0], + [0, 127, 70], + [255, 0, 0], + [255, 217, 38], + [0, 135, 255], + [165, 0, 165], + [255, 167, 255], + [0, 255, 255], + [255, 96, 38], + [142, 76, 0], + [33, 0, 127], + [0, 0, 0], + [183, 183, 183], +], dtype=np.float) / 255 + + +class Visualizations: + def __init__(self, env_name=None, update_every=10, server="http://localhost", disabled=False): + # Tracking data + self.last_update_timestamp = timer() + self.update_every = update_every + self.step_times = [] + self.losses = [] + self.eers = [] + print("Updating the visualizations every %d steps." % update_every) + + # If visdom is disabled TODO: use a better paradigm for that + self.disabled = disabled + if self.disabled: + return + + # Set the environment name + now = str(datetime.now().strftime("%d-%m %Hh%M")) + if env_name is None: + self.env_name = now + else: + self.env_name = "%s (%s)" % (env_name, now) + + # Connect to visdom and open the corresponding window in the browser + try: + self.vis = visdom.Visdom(server, env=self.env_name, raise_exceptions=True) + except ConnectionError: + raise Exception("No visdom server detected. Run the command \"visdom\" in your CLI to " + "start it.") + # webbrowser.open("http://localhost:8097/env/" + self.env_name) + + # Create the windows + self.loss_win = None + self.eer_win = None + # self.lr_win = None + self.implementation_win = None + self.projection_win = None + self.implementation_string = "" + + def log_params(self): + if self.disabled: + return + from encoder import params_data + from encoder import params_model + param_string = "Model parameters:
" + for param_name in (p for p in dir(params_model) if not p.startswith("__")): + value = getattr(params_model, param_name) + param_string += "\t%s: %s
" % (param_name, value) + param_string += "Data parameters:
" + for param_name in (p for p in dir(params_data) if not p.startswith("__")): + value = getattr(params_data, param_name) + param_string += "\t%s: %s
" % (param_name, value) + self.vis.text(param_string, opts={"title": "Parameters"}) + + def log_dataset(self, dataset: SpeakerVerificationDataset): + if self.disabled: + return + dataset_string = "" + dataset_string += "Speakers: %s\n" % len(dataset.speakers) + dataset_string += "\n" + dataset.get_logs() + dataset_string = dataset_string.replace("\n", "
") + self.vis.text(dataset_string, opts={"title": "Dataset"}) + + def log_implementation(self, params): + if self.disabled: + return + implementation_string = "" + for param, value in params.items(): + implementation_string += "%s: %s\n" % (param, value) + implementation_string = implementation_string.replace("\n", "
") + self.implementation_string = implementation_string + self.implementation_win = self.vis.text( + implementation_string, + opts={"title": "Training implementation"} + ) + + def update(self, loss, eer, step): + # Update the tracking data + now = timer() + self.step_times.append(1000 * (now - self.last_update_timestamp)) + self.last_update_timestamp = now + self.losses.append(loss) + self.eers.append(eer) + print(".", end="") + + # Update the plots every steps + if step % self.update_every != 0: + return + time_string = "Step time: mean: %5dms std: %5dms" % \ + (int(np.mean(self.step_times)), int(np.std(self.step_times))) + print("\nStep %6d Loss: %.4f EER: %.4f %s" % + (step, np.mean(self.losses), np.mean(self.eers), time_string)) + if not self.disabled: + self.loss_win = self.vis.line( + [np.mean(self.losses)], + [step], + win=self.loss_win, + update="append" if self.loss_win else None, + opts=dict( + legend=["Avg. loss"], + xlabel="Step", + ylabel="Loss", + title="Loss", + ) + ) + self.eer_win = self.vis.line( + [np.mean(self.eers)], + [step], + win=self.eer_win, + update="append" if self.eer_win else None, + opts=dict( + legend=["Avg. EER"], + xlabel="Step", + ylabel="EER", + title="Equal error rate" + ) + ) + if self.implementation_win is not None: + self.vis.text( + self.implementation_string + ("%s" % time_string), + win=self.implementation_win, + opts={"title": "Training implementation"}, + ) + + # Reset the tracking + self.losses.clear() + self.eers.clear() + self.step_times.clear() + + def draw_projections(self, embeds, utterances_per_speaker, step, out_fpath=None, max_speakers=10): + import matplotlib.pyplot as plt + + max_speakers = min(max_speakers, len(colormap)) + embeds = embeds[:max_speakers * utterances_per_speaker] + + n_speakers = len(embeds) // utterances_per_speaker + ground_truth = np.repeat(np.arange(n_speakers), utterances_per_speaker) + colors = [colormap[i] for i in ground_truth] + + reducer = umap.UMAP() + projected = reducer.fit_transform(embeds) + plt.scatter(projected[:, 0], projected[:, 1], c=colors) + plt.gca().set_aspect("equal", "datalim") + plt.title("UMAP projection (step %d)" % step) + if not self.disabled: + self.projection_win = self.vis.matplot(plt, win=self.projection_win) + if out_fpath is not None: + plt.savefig(out_fpath) + plt.clf() + + def save(self): + if not self.disabled: + self.vis.save([self.env_name]) diff --git a/params.py b/params.py new file mode 100644 index 00000000..ea6ed635 --- /dev/null +++ b/params.py @@ -0,0 +1,33 @@ +n_mels = 80 +sampling_rate = 22050 +n_fft = 1024 +hop_size = 256 + +# "average voice" encoder parameters +channels = 192 +filters = 768 +layers = 6 +kernel = 3 +dropout = 0.1 +heads = 2 +window_size = 4 +enc_dim = 128 + +# diffusion-based decoder parameters +dec_dim = 256 +spk_dim = 128 +use_ref_t = True +beta_min = 0.05 +beta_max = 20.0 + +# training parameters +seed = 37 +test_size = 1 +train_frames = 128 + +data_dir = 'dataset/diffvc_data/' +#val_file: "dataset/diffvc_data/filelist/valid.txt" # 注意:修复了注释位置dataset/diffvc_data/filelist/valid.txt +#exc_file: "dataset/diffvc_data/filelist/exceptions_libritts.txt" # 注意:修复了注释位置 +val_file = "dataset/diffvc_data/filelist/valid.txt" + #val_file = r'C:\Users\liberty\Desktop\diffvc-yuanma\\filelists\\valid.txt' +exc_file = "dataset/diffvc_data/filelist/exceptions_libritts.txt" diff --git a/talkingface/config/configurator.py b/talkingface/config/configurator.py index 7b6e21d8..b8822b39 100644 --- a/talkingface/config/configurator.py +++ b/talkingface/config/configurator.py @@ -3,7 +3,11 @@ import sys import yaml from logging import getLogger -from typing import Literal +#from typing import Literal +from typing_extensions import Literal + + + from talkingface.utils import( get_model, @@ -180,6 +184,7 @@ def _merge_external_config_dict(self): def _get_model_and_dataset(self, model, dataset): if model is None: try: + print("external_config_dict") model = self.external_config_dict["model"] except KeyError: raise KeyError( @@ -203,11 +208,14 @@ def _get_model_and_dataset(self, model, dataset): ) else: final_dataset = dataset - + print(final_model) + print(final_model_class) + print(final_dataset) return final_model, final_model_class, final_dataset def _update_internal_config_dict(self, file): with open(file, "r", encoding="utf-8") as f: + print(self.yaml_loader) config_dict = yaml.load(f.read(), Loader=self.yaml_loader) if config_dict is not None: self.internal_config_dict.update(config_dict) diff --git a/talkingface/config/diffVC.py b/talkingface/config/diffVC.py new file mode 100644 index 00000000..1c21312f --- /dev/null +++ b/talkingface/config/diffVC.py @@ -0,0 +1,45 @@ +librispeech_datasets = { + "train": { + "clean": ["LibriSpeech/train-clean-100", "LibriSpeech/train-clean-360"], + "other": ["LibriSpeech/train-other-500"] + }, + "test": { + "clean": ["LibriSpeech/test-clean"], + "other": ["LibriSpeech/test-other"] + }, + "dev": { + "clean": ["LibriSpeech/dev-clean"], + "other": ["LibriSpeech/dev-other"] + }, +} +libritts_datasets = { + "train": { + "clean": ["LibriTTS/train-clean-100", "LibriTTS/train-clean-360"], + "other": ["LibriTTS/train-other-500"] + }, + "test": { + "clean": ["LibriTTS/test-clean"], + "other": ["LibriTTS/test-other"] + }, + "dev": { + "clean": ["LibriTTS/dev-clean"], + "other": ["LibriTTS/dev-other"] + }, +} +voxceleb_datasets = { + "voxceleb1" : { + "train": ["VoxCeleb1/wav"], + "test": ["VoxCeleb1/test_wav"] + }, + "voxceleb2" : { + "train": ["VoxCeleb2/dev/aac"], + "test": ["VoxCeleb2/test_wav"] + } +} + +other_datasets = [ + "LJSpeech-1.1", + "VCTK-Corpus/wav48", +] + +anglophone_nationalites = ["australia", "canada", "ireland", "uk", "usa"] diff --git a/talkingface/data/dataprocess/wav2lip_process.py b/talkingface/data/dataprocess/wav2lip_process.py index e2279a42..1dd5a8b5 100644 --- a/talkingface/data/dataprocess/wav2lip_process.py +++ b/talkingface/data/dataprocess/wav2lip_process.py @@ -1,5 +1,5 @@ import os -import cv2 +#import cv2 import numpy as np import subprocess from tqdm import tqdm diff --git a/talkingface/data/dataset/__init__.py b/talkingface/data/dataset/__init__.py index 3fd37538..17c0c4dd 100644 --- a/talkingface/data/dataset/__init__.py +++ b/talkingface/data/dataset/__init__.py @@ -1,2 +1,4 @@ -from talkingface.data.dataset.wav2lip_dataset import Wav2LipDataset -from talkingface.data.dataset.dataset import Dataset \ No newline at end of file +from talkingface.data.dataset.diffvc_dataset import diffvcDataset +from talkingface.data.dataset.diffvc_dataset import VCDecBatchCollate +#from talkingface.data.dataset.wav2lip_dataset import Wav2LipDataset +#from talkingface.data.dataset.dataset import Dataset \ No newline at end of file diff --git a/talkingface/data/dataset/data_objects/__init__.py b/talkingface/data/dataset/data_objects/__init__.py new file mode 100644 index 00000000..ec1999f1 --- /dev/null +++ b/talkingface/data/dataset/data_objects/__init__.py @@ -0,0 +1,4 @@ +""" from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ + +from encoder.data_objects.speaker_verification_dataset import SpeakerVerificationDataset +from encoder.data_objects.speaker_verification_dataset import SpeakerVerificationDataLoader diff --git a/talkingface/data/dataset/data_objects/random_cycler.py b/talkingface/data/dataset/data_objects/random_cycler.py new file mode 100644 index 00000000..6fd5bb00 --- /dev/null +++ b/talkingface/data/dataset/data_objects/random_cycler.py @@ -0,0 +1,39 @@ +""" from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ + +import random + +class RandomCycler: + """ + Creates an internal copy of a sequence and allows access to its items in a constrained random + order. For a source sequence of n items and one or several consecutive queries of a total + of m items, the following guarantees hold (one implies the other): + - Each item will be returned between m // n and ((m - 1) // n) + 1 times. + - Between two appearances of the same item, there may be at most 2 * (n - 1) other items. + """ + + def __init__(self, source): + if len(source) == 0: + raise Exception("Can't create RandomCycler from an empty collection") + self.all_items = list(source) + self.next_items = [] + + def sample(self, count: int): + shuffle = lambda l: random.sample(l, len(l)) + + out = [] + while count > 0: + if count >= len(self.all_items): + out.extend(shuffle(list(self.all_items))) + count -= len(self.all_items) + continue + n = min(count, len(self.next_items)) + out.extend(self.next_items[:n]) + count -= n + self.next_items = self.next_items[n:] + if len(self.next_items) == 0: + self.next_items = shuffle(list(self.all_items)) + return out + + def __next__(self): + return self.sample(1)[0] + diff --git a/talkingface/data/dataset/data_objects/speaker.py b/talkingface/data/dataset/data_objects/speaker.py new file mode 100644 index 00000000..53d5bc4e --- /dev/null +++ b/talkingface/data/dataset/data_objects/speaker.py @@ -0,0 +1,42 @@ +""" from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ + +from encoder.data_objects.random_cycler import RandomCycler +from encoder.data_objects.utterance import Utterance +from pathlib import Path + +# Contains the set of utterances of a single speaker +class Speaker: + def __init__(self, root: Path): + self.root = root + self.name = root.name + self.utterances = None + self.utterance_cycler = None + + def _load_utterances(self): + with self.root.joinpath("_sources.txt").open("r") as sources_file: + sources = [l.split(",") for l in sources_file] + sources = {frames_fname: wave_fpath for frames_fname, wave_fpath in sources} + self.utterances = [Utterance(self.root.joinpath(f), w) for f, w in sources.items()] + self.utterance_cycler = RandomCycler(self.utterances) + + def random_partial(self, count, n_frames): + """ + Samples a batch of unique partial utterances from the disk in a way that all + utterances come up at least once every two cycles and in a random order every time. + + :param count: The number of partial utterances to sample from the set of utterances from + that speaker. Utterances are guaranteed not to be repeated if is not larger than + the number of utterances available. + :param n_frames: The number of frames in the partial utterance. + :return: A list of tuples (utterance, frames, range) where utterance is an Utterance, + frames are the frames of the partial utterances and range is the range of the partial + utterance with regard to the complete utterance. + """ + if self.utterances is None: + self._load_utterances() + + utterances = self.utterance_cycler.sample(count) + + a = [(u,) + u.random_partial(n_frames) for u in utterances] + + return a diff --git a/talkingface/data/dataset/data_objects/speaker_batch.py b/talkingface/data/dataset/data_objects/speaker_batch.py new file mode 100644 index 00000000..d6480f1c --- /dev/null +++ b/talkingface/data/dataset/data_objects/speaker_batch.py @@ -0,0 +1,14 @@ +""" from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ + +import numpy as np +from typing import List +from encoder.data_objects.speaker import Speaker + +class SpeakerBatch: + def __init__(self, speakers: List[Speaker], utterances_per_speaker: int, n_frames: int): + self.speakers = speakers + self.partials = {s: s.random_partial(utterances_per_speaker, n_frames) for s in speakers} + + # Array of shape (n_speakers * n_utterances, n_frames, mel_n), e.g. for 3 speakers with + # 4 utterances each of 160 frames of 40 mel coefficients: (12, 160, 40) + self.data = np.array([frames for s in speakers for _, frames, _ in self.partials[s]]) diff --git a/talkingface/data/dataset/data_objects/speaker_verification_dataset.py b/talkingface/data/dataset/data_objects/speaker_verification_dataset.py new file mode 100644 index 00000000..9f79fd7f --- /dev/null +++ b/talkingface/data/dataset/data_objects/speaker_verification_dataset.py @@ -0,0 +1,58 @@ +""" from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ + +from encoder.data_objects.random_cycler import RandomCycler +from encoder.data_objects.speaker_batch import SpeakerBatch +from encoder.data_objects.speaker import Speaker +from encoder.params_data import partials_n_frames +from torch.utils.data import Dataset, DataLoader +from pathlib import Path + +# TODO: improve with a pool of speakers for data efficiency + +class SpeakerVerificationDataset(Dataset): + def __init__(self, datasets_root: Path): + self.root = datasets_root + speaker_dirs = [f for f in self.root.glob("*") if f.is_dir()] + if len(speaker_dirs) == 0: + raise Exception("No speakers found. Make sure you are pointing to the directory " + "containing all preprocessed speaker directories.") + self.speakers = [Speaker(speaker_dir) for speaker_dir in speaker_dirs] + self.speaker_cycler = RandomCycler(self.speakers) + + def __len__(self): + return int(1e10) + + def __getitem__(self, index): + return next(self.speaker_cycler) + + def get_logs(self): + log_string = "" + for log_fpath in self.root.glob("*.txt"): + with log_fpath.open("r") as log_file: + log_string += "".join(log_file.readlines()) + return log_string + + +class SpeakerVerificationDataLoader(DataLoader): + def __init__(self, dataset, speakers_per_batch, utterances_per_speaker, sampler=None, + batch_sampler=None, num_workers=0, pin_memory=False, timeout=0, + worker_init_fn=None): + self.utterances_per_speaker = utterances_per_speaker + + super().__init__( + dataset=dataset, + batch_size=speakers_per_batch, + shuffle=False, + sampler=sampler, + batch_sampler=batch_sampler, + num_workers=num_workers, + collate_fn=self.collate, + pin_memory=pin_memory, + drop_last=False, + timeout=timeout, + worker_init_fn=worker_init_fn + ) + + def collate(self, speakers): + return SpeakerBatch(speakers, self.utterances_per_speaker, partials_n_frames) + \ No newline at end of file diff --git a/talkingface/data/dataset/data_objects/utterance.py b/talkingface/data/dataset/data_objects/utterance.py new file mode 100644 index 00000000..2b878c58 --- /dev/null +++ b/talkingface/data/dataset/data_objects/utterance.py @@ -0,0 +1,28 @@ +""" from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ + +import numpy as np + + +class Utterance: + def __init__(self, frames_fpath, wave_fpath): + self.frames_fpath = frames_fpath + self.wave_fpath = wave_fpath + + def get_frames(self): + return np.load(self.frames_fpath) + + def random_partial(self, n_frames): + """ + Crops the frames into a partial utterance of n_frames + + :param n_frames: The number of frames of the partial utterance + :return: the partial utterance frames and a tuple indicating the start and end of the + partial utterance in the complete utterance. + """ + frames = self.get_frames() + if frames.shape[0] == n_frames: + start = 0 + else: + start = np.random.randint(0, frames.shape[0] - n_frames) + end = start + n_frames + return frames[start:end], (start, end) \ No newline at end of file diff --git a/talkingface/data/dataset/diffvc_dataset.py b/talkingface/data/dataset/diffvc_dataset.py new file mode 100644 index 00000000..31a65c30 --- /dev/null +++ b/talkingface/data/dataset/diffvc_dataset.py @@ -0,0 +1,379 @@ +import os +import random +import numpy as np +import torch +import tgt + +from talkingface.data.dataset.dataset import Dataset + +random_seed = 37 +n_mels = 80 +train_frames = 128 + +# 返回测试说话人的列表。 +def get_test_speakers(): + test_speakers = ['1401', '2238', '3723', '4014', '5126', + '5322', '587', '6415', '8057', '8534'] + return test_speakers + +# 返回VCTK数据集中未知说话人的列表。 +def get_vctk_unseen_speakers(): + unseen_speakers = ['p252', 'p261', 'p241', 'p238', 'p243', + 'p294', 'p334', 'p343', 'p360', 'p362'] + return unseen_speakers + +# 返回VCTK数据集中未知句子的列表。 +def get_vctk_unseen_sentences(): + unseen_sentences = ['001', '002', '003', '004', '005'] + return unseen_sentences + +# 用于排除MFA不能识别某些单词的语音。 +def exclude_spn(data_dir, spk, mel_ids): + res = [] + for mel_id in mel_ids: + textgrid = mel_id + '.TextGrid' + t = tgt.io.read_textgrid(os.path.join(data_dir, 'textgrids', spk, textgrid)) + t = t.get_tier_by_name('phones') + spn_found = False + for i in range(len(t)): + if t[i].text == 'spn': + spn_found = True + break + if not spn_found: + res.append(mel_id) + return res + +# 用于训练 "average voice" 编码器的 LibriTTS 数据集。 +class VCEncDataset(Dataset): + def __init__(self, config, datasplit, data_dir, exc_file, avg_type): + super().__init__(config, datasplit) + self.mel_x_dir = os.path.join(data_dir, 'mels') + self.mel_y_dir = os.path.join(data_dir, 'mels_%s' % avg_type) + + self.test_speakers = get_test_speakers() + self.speakers = [spk for spk in os.listdir(self.mel_x_dir) + if spk not in self.test_speakers] + with open(exc_file) as f: + exceptions = f.readlines() + self.exceptions = [e.strip() + '_mel.npy' for e in exceptions] + self.test_info = [] + self.train_info = [] + for spk in self.speakers: + mel_ids = os.listdir(os.path.join(self.mel_x_dir, spk)) + mel_ids = [m[:-8] for m in mel_ids if m not in self.exceptions] + mel_ids = exclude_spn(data_dir, spk, mel_ids) + self.train_info += [(m, spk) for m in mel_ids] + for spk in self.test_speakers: + mel_ids = os.listdir(os.path.join(self.mel_x_dir, spk)) + mel_ids = [m[:-8] for m in mel_ids] + self.test_info += [(m, spk) for m in mel_ids] + print("Total number of test wavs is %d." % len(self.test_info)) + print("Total number of training wavs is %d." % len(self.train_info)) + random.seed(random_seed) + random.shuffle(self.train_info) + + def get_vc_data(self, mel_id, spk): + mel_x_path = os.path.join(self.mel_x_dir, spk, mel_id + '_mel.npy') + mel_y_path = os.path.join(self.mel_y_dir, spk, mel_id + '_avgmel.npy') + mel_x = np.load(mel_x_path) + mel_y = np.load(mel_y_path) + mel_x = torch.from_numpy(mel_x).float() + mel_y = torch.from_numpy(mel_y).float() + return (mel_x, mel_y) + + def __getitem__(self, index): + mel_id, spk = self.train_info[index] + mel_x, mel_y = self.get_vc_data(mel_id, spk) + return {"x": mel_x, "y": mel_y} + + def __len__(self): + return len(self.train_info) + + def get_test_dataset(self): + pairs = [] + for i in range(len(self.test_info)): + mel_id, spk = self.test_info[i] + mel_x, mel_y = self.get_vc_data(mel_id, spk) + pairs.append((mel_x, mel_y)) + return [{"x": pair[0], "y": pair[1]} for pair in pairs] + +class VCDecDataset(torch.utils.data.Dataset): + def __init__(self, data_dir, val_file, exc_file): + self.mel_dir = os.path.join(data_dir, 'mels') + #self.mel_dir = self.mel_dir.replace('\\','/') + #self.mel_dir="./data/mels" + self.emb_dir = os.path.join(data_dir, 'embeds') + self.test_speakers = get_test_speakers() + self.speakers = [spk for spk in os.listdir(self.mel_dir) + if spk not in self.test_speakers] + self.speakers = [spk for spk in self.speakers + if len(os.listdir(os.path.join(self.mel_dir, spk))) >= 10] + random.seed(random_seed) + random.shuffle(self.speakers) + with open(exc_file) as f: + exceptions = f.readlines() + self.exceptions = [e.strip() + '_mel.npy' for e in exceptions] + with open(val_file) as f: + valid_ids = f.readlines() + self.valid_ids = set([v.strip() + '_mel.npy' for v in valid_ids]) + self.exceptions += self.valid_ids + + self.valid_info = [(v[:-8], v.split('_')[0]) for v in self.valid_ids] + self.train_info = [] + for spk in self.speakers: + mel_ids = os.listdir(os.path.join(self.mel_dir, spk)) + mel_ids = [m for m in mel_ids if m not in self.exceptions] + self.train_info += [(i[:-8], spk) for i in mel_ids] + print("Total number of validation wavs is %d." % len(self.valid_info)) + print("Total number of training wavs is %d." % len(self.train_info)) + print("Total number of training speakers is %d." % len(self.speakers)) + random.seed(random_seed) + random.shuffle(self.train_info) + def get_vc_data(self, audio_info): + audio_id, spk = audio_info + mels = self.get_mels(audio_id, spk) + embed = self.get_embed(audio_id, spk) + return (mels, embed) + + def get_mels(self, audio_id, spk): + mel_path = os.path.join(self.mel_dir, spk, audio_id + '_mel.npy') + mels = np.load(mel_path) + mels = torch.from_numpy(mels).float() + return mels + + def get_embed(self, audio_id, spk): + embed_path = os.path.join(self.emb_dir, spk, audio_id + '_embed.npy') + embed = np.load(embed_path) + embed = torch.from_numpy(embed).float() + return embed + + def __getitem__(self, index): + mels, embed = self.get_vc_data(self.train_info[index]) + item = {'mel': mels, 'c': embed} + return item + + def __len__(self): + return len(self.train_info) + + def get_valid_dataset(self): + pairs = [] + for i in range(len(self.valid_info)): + mels, embed = self.get_vc_data(self.valid_info[i]) + pairs.append((mels, embed)) + return pairs + +# 用于训练 "average voice" 编码器的 VCTK 数据集。 +class VCTKEncDataset(Dataset): + def __init__(self, config, datasplit): + super().__init__(config, datasplit) + data_dir=config['data_dir'] + exc_file=config['exc_file'] + avg_type=config['avg_type'] + self.mel_x_dir = os.path.join(data_dir, 'mels') + self.mel_y_dir = os.path.join(data_dir, 'mels_%s' % avg_type) + + self.unseen_speakers = get_vctk_unseen_speakers() + self.unseen_sentences = get_vctk_unseen_sentences() + self.speakers = [spk for spk in os.listdir(self.mel_x_dir) + if spk not in self.unseen_speakers] + with open(exc_file) as f: + exceptions = f.readlines() + self.exceptions = [e.strip() + '_mel.npy' for e in exceptions] + self.test_info = [] + self.train_info = [] + for spk in self.speakers: + mel_ids = os.listdir(os.path.join(self.mel_x_dir, spk)) + mel_ids = [m for m in mel_ids if m.split('_')[1] not in self.unseen_sentences] + mel_ids = [m[:-8] for m in mel_ids if m not in self.exceptions] + mel_ids = exclude_spn(data_dir, spk, mel_ids) + self.train_info += [(m, spk) for m in mel_ids] + for spk in self.unseen_speakers: + mel_ids = os.listdir(os.path.join(self.mel_x_dir, spk)) + mel_ids = [m for m in mel_ids if m.split('_')[1] not in self.unseen_sentences] + mel_ids = [m[:-8] for m in mel_ids if m not in self.exceptions] + self.test_info += [(m, spk) for m in mel_ids] + print("Total number of test wavs is %d." % len(self.test_info)) + print("Total number of training wavs is %d." % len(self.train_info)) + random.seed(random_seed) + random.shuffle(self.train_info) + + def get_vc_data(self, mel_id, spk): + mel_x_path = os.path.join(self.mel_x_dir, spk, mel_id + '_mel.npy') + mel_y_path = os.path.join(self.mel_y_dir, spk, mel_id + '_avgmel.npy') + mel_x = np.load(mel_x_path) + mel_y = np.load(mel_y_path) + mel_x = torch.from_numpy(mel_x).float() + mel_y = torch.from_numpy(mel_y).float() + return (mel_x, mel_y) + + def __getitem__(self, index): + mel_id, spk = self.train_info[index] + mel_x, mel_y = self.get_vc_data(mel_id, spk) + return {"x": mel_x, "y": mel_y} + + def __len__(self): + return len(self.train_info) + + def get_test_dataset(self): + pairs = [] + for i in range(len(self.test_info)): + mel_id, spk = self.test_info[i] + mel_x, mel_y = self.get_vc_data(mel_id, spk) + pairs.append((mel_x, mel_y)) + return [{"x": pair[0], "y": pair[1]} for pair in pairs] + +# 用于训练 speaker-conditional diffusion-based 解码器的 LibriTTS 数据集。(train) +class diffvcDataset(Dataset): + def __init__(self, config, datasplit): + super().__init__(config, datasplit) + data_dir=config['data_dir'] + exc_file=config['exc_file'] + val_file=config['val_file'] + self.mel_dir = os.path.join(data_dir, 'mels') + self.emb_dir = os.path.join(data_dir, 'embeds') + self.test_speakers = get_test_speakers() + self.speakers = [spk for spk in os.listdir(self.mel_dir) + if spk not in self.test_speakers] + self.speakers = [spk for spk in self.speakers + if len(os.listdir(os.path.join(self.mel_dir, spk))) >= 10] + random.seed(random_seed) + random.shuffle(self.speakers) + with open(exc_file) as f: + exceptions = f.readlines() + self.exceptions = [e.strip() + '_mel.npy' for e in exceptions] + with open(val_file) as f: + valid_ids = f.readlines() + self.valid_ids = set([v.strip() + '_mel.npy' for v in valid_ids]) + self.exceptions += self.valid_ids + + self.valid_info = [(v[:-8], v.split('_')[0]) for v in self.valid_ids] + self.train_info = [] + for spk in self.speakers: + mel_ids = os.listdir(os.path.join(self.mel_dir, spk)) + mel_ids = [m for m in mel_ids if m not in self.exceptions] + self.train_info += [(i[:-8], spk) for i in mel_ids] + print("Total number of validation wavs is %d." % len(self.valid_info)) + print("Total number of training wavs is %d." % len(self.train_info)) + print("Total number of training speakers is %d." % len(self.speakers)) + random.seed(random_seed) + random.shuffle(self.train_info) + + def get_vc_data(self, audio_info): + audio_id, spk = audio_info + mels = self.get_mels(audio_id, spk) + embed = self.get_embed(audio_id, spk) + return (mels, embed) + + def get_mels(self, audio_id, spk): + mel_path = os.path.join(self.mel_dir, spk, audio_id + '_mel.npy') + mels = np.load(mel_path) + mels = torch.from_numpy(mels).float() + return mels + + def get_embed(self, audio_id, spk): + embed_path = os.path.join(self.emb_dir, spk, audio_id + '_embed.npy') + embed = np.load(embed_path) + embed = torch.from_numpy(embed).float() + return embed + + def __getitem__(self, index): + mels, embed = self.get_vc_data(self.train_info[index]) + return {'mel': mels, 'c': embed} + + def __len__(self): + return len(self.train_info) + + def get_valid_dataset(self): + pairs = [] + for i in range(len(self.valid_info)): + mels, embed = self.get_vc_data(self.valid_info[i]) + pairs.append((mels, embed)) + return [{"mel": pair[0], "c": pair[1]} for pair in pairs] + + + + +# 用于训练 speaker-conditional diffusion-based 解码器的 VCTK 数据集。 +class VCTKDecDataset(Dataset): + def __init__(self,config, datasplit, data_dir): + super().__init__(config, datasplit) + self.mel_dir = os.path.join(data_dir, 'mels') + self.emb_dir = os.path.join(data_dir, 'embeds') + self.unseen_speakers = get_vctk_unseen_speakers() + self.unseen_sentences = get_vctk_unseen_sentences() + self.speakers = [spk for spk in os.listdir(self.mel_dir) + if spk not in self.unseen_speakers] + random.seed(random_seed) + random.shuffle(self.speakers) + self.train_info = [] + for spk in self.speakers: + mel_ids = os.listdir(os.path.join(self.mel_dir, spk)) + mel_ids = [m for m in mel_ids if m.split('_')[1] not in self.unseen_sentences] + self.train_info += [(i[:-8], spk) for i in mel_ids] + self.valid_info = [] + for spk in self.unseen_speakers: + mel_ids = os.listdir(os.path.join(self.mel_dir, spk)) + mel_ids = [m for m in mel_ids if m.split('_')[1] not in self.unseen_sentences] + self.valid_info += [(i[:-8], spk) for i in mel_ids] + print("Total number of validation wavs is %d." % len(self.valid_info)) + print("Total number of training wavs is %d." % len(self.train_info)) + print("Total number of training speakers is %d." % len(self.speakers)) + random.seed(random_seed) + random.shuffle(self.train_info) + + def get_vc_data(self, audio_info): + audio_id, spk = audio_info + mels = self.get_mels(audio_id, spk) + embed = self.get_embed(audio_id, spk) + return (mels, embed) + + def get_mels(self, audio_id, spk): + mel_path = os.path.join(self.mel_dir, spk, audio_id + '_mel.npy') + mels = np.load(mel_path) + mels = torch.from_numpy(mels).float() + return mels + + def get_embed(self, audio_id, spk): + embed_path = os.path.join(self.emb_dir, spk, audio_id + '_embed.npy') + embed = np.load(embed_path) + embed = torch.from_numpy(embed).float() + return embed + + def __getitem__(self, index): + mels, embed = self.get_vc_data(self.train_info[index]) + return {'mel': mels, 'c': embed} + + def __len__(self): + return len(self.train_info) + + def get_valid_dataset(self): + pairs = [] + for i in range(len(self.valid_info)): + mels, embed = self.get_vc_data(self.valid_info[i]) + pairs.append((mels, embed)) + return [{"mel": pair[0], "c": pair[1]} for pair in pairs] + +class VCDecBatchCollate(object): + def __call__(self, batch): + B = len(batch) + mels1 = torch.zeros((B, n_mels, train_frames), dtype=torch.float32) + mels2 = torch.zeros((B, n_mels, train_frames), dtype=torch.float32) + max_starts = [max(item['mel'].shape[-1] - train_frames, 0) + for item in batch] + starts1 = [random.choice(range(m)) if m > 0 else 0 for m in max_starts] + starts2 = [random.choice(range(m)) if m > 0 else 0 for m in max_starts] + mel_lengths = [] + for i, item in enumerate(batch): + mel = item['mel'] + if mel.shape[-1] < train_frames: + mel_length = mel.shape[-1] + else: + mel_length = train_frames + mels1[i, :, :mel_length] = mel[:, starts1[i]:starts1[i] + mel_length] + mels2[i, :, :mel_length] = mel[:, starts2[i]:starts2[i] + mel_length] + mel_lengths.append(mel_length) + mel_lengths = torch.LongTensor(mel_lengths) + embed = torch.stack([item['c'] for item in batch], 0) + return {'mel1': mels1, 'mel2': mels2, 'mel_lengths': mel_lengths, 'c': embed} + + diff --git a/talkingface/model/.DS_Store b/talkingface/model/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..76fc83f66620953933134b954624a815f6d63435 GIT binary patch literal 6148 zcmeHKO^XvT7=F{OI%5%v_|b#HfY-9tc3BiJwXO%RR`j4!XFA>Pu(L_&Ot&nh(6jyr z|AJS4iT}luK5vr3c3NRsq^RT_k|)XJeUdzxBoiVMok{$Ns7gd00%Q3)q7BCHIjmX9 zmaw1D90}#r!?R1Hwc4)0Dc}_N+Z5onThVJaAxY=g@7c>#^`lfpsKMLAGlbor+K?i; zNg+j)Va1P;>g$!CVcie#^yx_N0Y#3l1&+HLIC7Mwbd0`88q%TW9&BP0_X}(_)SXZa zPlXZ1=vP9g=#4~fBkSr2#pgA?gigRY1jl9geiZn8j4Pxwu#KS`?~w(4O2dN39rT@R zAENyTz8P}y#YUs&>&vF$zpro6C{41w z(fBS(JLM}^uX&zV@ji5q)U=z2`6TOv<9D2UtyCH<(_!=`8BBZCThEou!$c0o+98Pt zsPg`8B4ah}sELd-?b|gkJm2?w)!J;<+<#mTTCL{19?YIJ8}(qXwLhQx-t7kupT6oH zrzcW qkv b heads c (h w)', + heads = self.heads, qkv=3) + k = k.softmax(dim=-1) + context = torch.einsum('bhdn,bhen->bhde', k, v) + out = torch.einsum('bhde,bhdn->bhen', context, q) + out = rearrange(out, 'b heads c (h w) -> b (heads c) h w', + heads=self.heads, h=h, w=w) + return self.to_out(out) + + +class Residual(BaseModule): + def __init__(self, fn): + super(Residual, self).__init__() + self.fn = fn + + def forward(self, x, *args, **kwargs): + output = self.fn(x, *args, **kwargs) + x + return output + + +class SinusoidalPosEmb(BaseModule): + def __init__(self, dim): + super(SinusoidalPosEmb, self).__init__() + self.dim = dim + + def forward(self, x): + device = x.device + half_dim = self.dim // 2 + emb = math.log(10000) / (half_dim - 1) + emb = torch.exp(torch.arange(half_dim, device=device).float() * -emb) + emb = 1000.0 * x.unsqueeze(1) * emb.unsqueeze(0) + emb = torch.cat((emb.sin(), emb.cos()), dim=-1) + return emb + + +class RefBlock(BaseModule): + def __init__(self, out_dim, time_emb_dim): + super(RefBlock, self).__init__() + base_dim = out_dim // 4 + self.mlp1 = torch.nn.Sequential(Mish(), torch.nn.Linear(time_emb_dim, + base_dim)) + self.mlp2 = torch.nn.Sequential(Mish(), torch.nn.Linear(time_emb_dim, + 2 * base_dim)) + self.block11 = torch.nn.Sequential(torch.nn.Conv2d(1, 2 * base_dim, + 3, 1, 1), torch.nn.InstanceNorm2d(2 * base_dim, affine=True), + torch.nn.GLU(dim=1)) + self.block12 = torch.nn.Sequential(torch.nn.Conv2d(base_dim, 2 * base_dim, + 3, 1, 1), torch.nn.InstanceNorm2d(2 * base_dim, affine=True), + torch.nn.GLU(dim=1)) + self.block21 = torch.nn.Sequential(torch.nn.Conv2d(base_dim, 4 * base_dim, + 3, 1, 1), torch.nn.InstanceNorm2d(4 * base_dim, affine=True), + torch.nn.GLU(dim=1)) + self.block22 = torch.nn.Sequential(torch.nn.Conv2d(2 * base_dim, 4 * base_dim, + 3, 1, 1), torch.nn.InstanceNorm2d(4 * base_dim, affine=True), + torch.nn.GLU(dim=1)) + self.block31 = torch.nn.Sequential(torch.nn.Conv2d(2 * base_dim, 8 * base_dim, + 3, 1, 1), torch.nn.InstanceNorm2d(8 * base_dim, affine=True), + torch.nn.GLU(dim=1)) + self.block32 = torch.nn.Sequential(torch.nn.Conv2d(4 * base_dim, 8 * base_dim, + 3, 1, 1), torch.nn.InstanceNorm2d(8 * base_dim, affine=True), + torch.nn.GLU(dim=1)) + self.final_conv = torch.nn.Conv2d(4 * base_dim, out_dim, 1) + + def forward(self, x, mask, time_emb): + y = self.block11(x * mask) + y = self.block12(y * mask) + y += self.mlp1(time_emb).unsqueeze(-1).unsqueeze(-1) + y = self.block21(y * mask) + y = self.block22(y * mask) + y += self.mlp2(time_emb).unsqueeze(-1).unsqueeze(-1) + y = self.block31(y * mask) + y = self.block32(y * mask) + y = self.final_conv(y * mask) + return (y * mask).sum((2, 3)) / (mask.sum((2, 3)) * x.shape[2]) + +#diffusion +class GradLogPEstimator(BaseModule): + def __init__(self, dim_base, dim_cond, use_ref_t, dim_mults=(1, 2, 4)): + super(GradLogPEstimator, self).__init__() + self.use_ref_t = use_ref_t + dims = [2 + dim_cond, *map(lambda m: dim_base * m, dim_mults)] + in_out = list(zip(dims[:-1], dims[1:])) + + self.time_pos_emb = SinusoidalPosEmb(dim_base) + self.mlp = torch.nn.Sequential(torch.nn.Linear(dim_base, dim_base * 4), + Mish(), torch.nn.Linear(dim_base * 4, dim_base)) + + cond_total = dim_base + 256 + if use_ref_t: + self.ref_block = RefBlock(out_dim=dim_cond, time_emb_dim=dim_base) + cond_total += dim_cond + self.cond_block = torch.nn.Sequential(torch.nn.Linear(cond_total, 4 * dim_cond), + Mish(), torch.nn.Linear(4 * dim_cond, dim_cond)) + + self.downs = torch.nn.ModuleList([]) + self.ups = torch.nn.ModuleList([]) + num_resolutions = len(in_out) + + for ind, (dim_in, dim_out) in enumerate(in_out): + is_last = ind >= (num_resolutions - 1) + self.downs.append(torch.nn.ModuleList([ + ResnetBlock(dim_in, dim_out,time_emb_dim=dim_base), + ResnetBlock(dim_out, dim_out, time_emb_dim=dim_base), + Residual(Rezero(LinearAttention(dim_out))), + Downsample(dim_out) if not is_last else torch.nn.Identity()])) + + mid_dim = dims[-1] + self.mid_block1 = ResnetBlock(mid_dim, mid_dim, time_emb_dim=dim_base) + self.mid_attn = Residual(Rezero(LinearAttention(mid_dim))) + self.mid_block2 = ResnetBlock(mid_dim, mid_dim, time_emb_dim=dim_base) + + for ind, (dim_in, dim_out) in enumerate(reversed(in_out[1:])): + self.ups.append(torch.nn.ModuleList([ + ResnetBlock(dim_out * 2, dim_in, time_emb_dim=dim_base), + ResnetBlock(dim_in, dim_in, time_emb_dim=dim_base), + Residual(Rezero(LinearAttention(dim_in))), + Upsample(dim_in)])) + self.final_block = Block(dim_base, dim_base) + self.final_conv = torch.nn.Conv2d(dim_base, 1, 1) + + def forward(self, x, x_mask, mean, ref, ref_mask, c, t): + condition = self.time_pos_emb(t) + t = self.mlp(condition) + + x = torch.stack([mean, x], 1) + x_mask = x_mask.unsqueeze(1) + ref_mask = ref_mask.unsqueeze(1) + + if self.use_ref_t: + condition = torch.cat([condition, self.ref_block(ref, ref_mask, t)], 1) + condition = torch.cat([condition, c], 1) + + condition = self.cond_block(condition).unsqueeze(-1).unsqueeze(-1) + condition = torch.cat(x.shape[2]*[condition], 2) + condition = torch.cat(x.shape[3]*[condition], 3) + x = torch.cat([x, condition], 1) + + hiddens = [] + masks = [x_mask] + for resnet1, resnet2, attn, downsample in self.downs: + mask_down = masks[-1] + x = resnet1(x, mask_down, t) + x = resnet2(x, mask_down, t) + x = attn(x) + hiddens.append(x) + x = downsample(x * mask_down) + masks.append(mask_down[:, :, :, ::2]) + + masks = masks[:-1] + mask_mid = masks[-1] + x = self.mid_block1(x, mask_mid, t) + x = self.mid_attn(x) + x = self.mid_block2(x, mask_mid, t) + + for resnet1, resnet2, attn, upsample in self.ups: + mask_up = masks.pop() + x = torch.cat((x, hiddens.pop()), dim=1) + x = resnet1(x, mask_up, t) + x = resnet2(x, mask_up, t) + x = attn(x) + x = upsample(x * mask_up) + + x = self.final_block(x, x_mask) + output = self.final_conv(x * x_mask) + + return (output * x_mask).squeeze(1) + + +class Diffusion(BaseModule): + def __init__(self, n_feats, dim_unet, dim_spk, use_ref_t, beta_min, beta_max): + super(Diffusion, self).__init__() + self.estimator = GradLogPEstimator(dim_unet, dim_spk, use_ref_t) + self.n_feats = n_feats + self.dim_unet = dim_unet + self.dim_spk = dim_spk + self.use_ref_t = use_ref_t + self.beta_min = beta_min + self.beta_max = beta_max + + def get_beta(self, t): + beta = self.beta_min + (self.beta_max - self.beta_min) * t + return beta + + def get_gamma(self, s, t, p=1.0, use_torch=False): + beta_integral = self.beta_min + 0.5*(self.beta_max - self.beta_min)*(t + s) + beta_integral *= (t - s) + if use_torch: + gamma = torch.exp(-0.5*p*beta_integral).unsqueeze(-1).unsqueeze(-1) + else: + gamma = math.exp(-0.5*p*beta_integral) + return gamma + + def get_mu(self, s, t): + a = self.get_gamma(s, t) + b = 1.0 - self.get_gamma(0, s, p=2.0) + c = 1.0 - self.get_gamma(0, t, p=2.0) + return a * b / c + + def get_nu(self, s, t): + a = self.get_gamma(0, s) + b = 1.0 - self.get_gamma(s, t, p=2.0) + c = 1.0 - self.get_gamma(0, t, p=2.0) + return a * b / c + + def get_sigma(self, s, t): + a = 1.0 - self.get_gamma(0, s, p=2.0) + b = 1.0 - self.get_gamma(s, t, p=2.0) + c = 1.0 - self.get_gamma(0, t, p=2.0) + return math.sqrt(a * b / c) + + def compute_diffused_mean(self, x0, mask, mean, t, use_torch=False): + x0_weight = self.get_gamma(0, t, use_torch=use_torch) + mean_weight = 1.0 - x0_weight + xt_mean = x0 * x0_weight + mean * mean_weight + return xt_mean * mask + + def forward_diffusion(self, x0, mask, mean, t): + xt_mean = self.compute_diffused_mean(x0, mask, mean, t, use_torch=True) + variance = 1.0 - self.get_gamma(0, t, p=2.0, use_torch=True) + z = torch.randn(x0.shape, dtype=x0.dtype, device=x0.device, requires_grad=False) + xt = xt_mean + z * torch.sqrt(variance) + return xt * mask, z * mask + + @torch.no_grad() + def reverse_diffusion(self, z, mask, mean, ref, ref_mask, mean_ref, c, + n_timesteps, mode): + h = 1.0 / n_timesteps + xt = z * mask + for i in range(n_timesteps): + t = 1.0 - i*h + time = t * torch.ones(z.shape[0], dtype=z.dtype, device=z.device) + beta_t = self.get_beta(t) + xt_ref = [self.compute_diffused_mean(ref, ref_mask, mean_ref, t)] +# for j in range(15): +# xt_ref += [self.compute_diffused_mean(ref, ref_mask, mean_ref, (j+0.5)/15.0)] + xt_ref = torch.stack(xt_ref, 1) + if mode == 'pf': + dxt = 0.5 * (mean - xt - self.estimator(xt, mask, mean, xt_ref, ref_mask, c, time)) * (beta_t * h) + else: + if mode == 'ml': + kappa = self.get_gamma(0, t - h) * (1.0 - self.get_gamma(t - h, t, p=2.0)) + kappa /= (self.get_gamma(0, t) * beta_t * h) + kappa -= 1.0 + omega = self.get_nu(t - h, t) / self.get_gamma(0, t) + omega += self.get_mu(t - h, t) + omega -= (0.5 * beta_t * h + 1.0) + sigma = self.get_sigma(t - h, t) + else: + kappa = 0.0 + omega = 0.0 + sigma = math.sqrt(beta_t * h) + dxt = (mean - xt) * (0.5 * beta_t * h + omega) + dxt -= self.estimator(xt, mask, mean, xt_ref, ref_mask, c, time) * (1.0 + kappa) * (beta_t * h) + dxt += torch.randn_like(z, device=z.device) * sigma + xt = (xt - dxt) * mask + return xt + + @torch.no_grad() + def forward(self, z, mask, mean, ref, ref_mask, mean_ref, c, + n_timesteps, mode): + if mode not in ['pf', 'em', 'ml']: + print('Inference mode must be one of [pf, em, ml]!') + return z + return self.reverse_diffusion(z, mask, mean, ref, ref_mask, mean_ref, c, + n_timesteps, mode) + + def loss_t(self, x0, mask, mean, x_ref, mean_ref, c, t): + xt, z = self.forward_diffusion(x0, mask, mean, t) + xt_ref = [self.compute_diffused_mean(x_ref, mask, mean_ref, t, use_torch=True)] +# for j in range(15): +# xt_ref += [self.compute_diffused_mean(x_ref, mask, mean_ref, (j+0.5)/15.0)] + xt_ref = torch.stack(xt_ref, 1) + z_estimation = self.estimator(xt, mask, mean, xt_ref, mask, c, t) + z_estimation *= torch.sqrt(1.0 - self.get_gamma(0, t, p=2.0, use_torch=True)) + loss = torch.sum((z_estimation + z)**2) / (torch.sum(mask)*self.n_feats) + return loss + + def compute_loss(self, x0, mask, mean, x_ref, mean_ref, c, offset=1e-5): + b = x0.shape[0] + t = torch.rand(b, dtype=x0.dtype, device=x0.device, requires_grad=False) + t = torch.clamp(t, offset, 1.0 - offset) + return self.loss_t(x0, mask, mean, x_ref, mean_ref, c, t) +#encoder +class LayerNorm(BaseModule): + def __init__(self, channels, eps=1e-4): + super(LayerNorm, self).__init__() + self.channels = channels + self.eps = eps + + self.gamma = torch.nn.Parameter(torch.ones(channels)) + self.beta = torch.nn.Parameter(torch.zeros(channels)) + + def forward(self, x): + n_dims = len(x.shape) + mean = torch.mean(x, 1, keepdim=True) + variance = torch.mean((x - mean)**2, 1, keepdim=True) + + x = (x - mean) * torch.rsqrt(variance + self.eps) + + shape = [1, -1] + [1] * (n_dims - 2) + x = x * self.gamma.view(*shape) + self.beta.view(*shape) + return x + + +class ConvReluNorm(BaseModule): + def __init__(self, in_channels, hidden_channels, out_channels, kernel_size, + n_layers, p_dropout): + super(ConvReluNorm, self).__init__() + self.in_channels = in_channels + self.hidden_channels = hidden_channels + self.out_channels = out_channels + self.kernel_size = kernel_size + self.n_layers = n_layers + self.p_dropout = p_dropout + + self.conv_layers = torch.nn.ModuleList() + self.norm_layers = torch.nn.ModuleList() + self.conv_layers.append(torch.nn.Conv1d(in_channels, hidden_channels, + kernel_size, padding=kernel_size//2)) + self.norm_layers.append(LayerNorm(hidden_channels)) + self.relu_drop = torch.nn.Sequential(torch.nn.ReLU(), torch.nn.Dropout(p_dropout)) + for _ in range(n_layers - 1): + self.conv_layers.append(torch.nn.Conv1d(hidden_channels, hidden_channels, + kernel_size, padding=kernel_size//2)) + self.norm_layers.append(LayerNorm(hidden_channels)) + self.proj = torch.nn.Conv1d(hidden_channels, out_channels, 1) + self.proj.weight.data.zero_() + self.proj.bias.data.zero_() + + def forward(self, x, x_mask): + x_org = x + for i in range(self.n_layers): + x = self.conv_layers[i](x * x_mask) + x = self.norm_layers[i](x) + x = self.relu_drop(x) + x = x_org + self.proj(x) + return x * x_mask + + +class MultiHeadAttention(BaseModule): + def __init__(self, channels, out_channels, n_heads, window_size=None, + heads_share=True, p_dropout=0.0, proximal_bias=False, + proximal_init=False): + super(MultiHeadAttention, self).__init__() + assert channels % n_heads == 0 + + self.channels = channels + self.out_channels = out_channels + self.n_heads = n_heads + self.window_size = window_size + self.heads_share = heads_share + self.proximal_bias = proximal_bias + self.p_dropout = p_dropout + self.attn = None + + self.k_channels = channels // n_heads + self.conv_q = torch.nn.Conv1d(channels, channels, 1) + self.conv_k = torch.nn.Conv1d(channels, channels, 1) + self.conv_v = torch.nn.Conv1d(channels, channels, 1) + if window_size is not None: + n_heads_rel = 1 if heads_share else n_heads + rel_stddev = self.k_channels**-0.5 + self.emb_rel_k = torch.nn.Parameter(torch.randn(n_heads_rel, + window_size * 2 + 1, self.k_channels) * rel_stddev) + self.emb_rel_v = torch.nn.Parameter(torch.randn(n_heads_rel, + window_size * 2 + 1, self.k_channels) * rel_stddev) + self.conv_o = torch.nn.Conv1d(channels, out_channels, 1) + self.drop = torch.nn.Dropout(p_dropout) + + torch.nn.init.xavier_uniform_(self.conv_q.weight) + torch.nn.init.xavier_uniform_(self.conv_k.weight) + if proximal_init: + self.conv_k.weight.data.copy_(self.conv_q.weight.data) + self.conv_k.bias.data.copy_(self.conv_q.bias.data) + torch.nn.init.xavier_uniform_(self.conv_v.weight) + + def forward(self, x, c, attn_mask=None): + q = self.conv_q(x) + k = self.conv_k(c) + v = self.conv_v(c) + + x, self.attn = self.attention(q, k, v, mask=attn_mask) + + x = self.conv_o(x) + return x + + def attention(self, query, key, value, mask=None): + b, d, t_s, t_t = (*key.size(), query.size(2)) + query = query.view(b, self.n_heads, self.k_channels, t_t).transpose(2, 3) + key = key.view(b, self.n_heads, self.k_channels, t_s).transpose(2, 3) + value = value.view(b, self.n_heads, self.k_channels, t_s).transpose(2, 3) + + scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(self.k_channels) + if self.window_size is not None: + assert t_s == t_t, "Relative attention is only available for self-attention." + key_relative_embeddings = self._get_relative_embeddings(self.emb_rel_k, t_s) + rel_logits = self._matmul_with_relative_keys(query, key_relative_embeddings) + rel_logits = self._relative_position_to_absolute_position(rel_logits) + scores_local = rel_logits / math.sqrt(self.k_channels) + scores = scores + scores_local + if self.proximal_bias: + assert t_s == t_t, "Proximal bias is only available for self-attention." + scores = scores + self._attention_bias_proximal(t_s).to(device=scores.device, + dtype=scores.dtype) + if mask is not None: + scores = scores.masked_fill(mask == 0, -1e4) + p_attn = torch.nn.functional.softmax(scores, dim=-1) + p_attn = self.drop(p_attn) + output = torch.matmul(p_attn, value) + if self.window_size is not None: + relative_weights = self._absolute_position_to_relative_position(p_attn) + value_relative_embeddings = self._get_relative_embeddings(self.emb_rel_v, t_s) + output = output + self._matmul_with_relative_values(relative_weights, + value_relative_embeddings) + output = output.transpose(2, 3).contiguous().view(b, d, t_t) + return output, p_attn + + def _matmul_with_relative_values(self, x, y): + ret = torch.matmul(x, y.unsqueeze(0)) + return ret + + def _matmul_with_relative_keys(self, x, y): + ret = torch.matmul(x, y.unsqueeze(0).transpose(-2, -1)) + return ret + + def _get_relative_embeddings(self, relative_embeddings, length): + pad_length = max(length - (self.window_size + 1), 0) + slice_start_position = max((self.window_size + 1) - length, 0) + slice_end_position = slice_start_position + 2 * length - 1 + if pad_length > 0: + padded_relative_embeddings = torch.nn.functional.pad( + relative_embeddings, convert_pad_shape([[0, 0], + [pad_length, pad_length], [0, 0]])) + else: + padded_relative_embeddings = relative_embeddings + used_relative_embeddings = padded_relative_embeddings[:, + slice_start_position:slice_end_position] + return used_relative_embeddings + + def _relative_position_to_absolute_position(self, x): + batch, heads, length, _ = x.size() + x = torch.nn.functional.pad(x, convert_pad_shape([[0,0],[0,0],[0,0],[0,1]])) + x_flat = x.view([batch, heads, length * 2 * length]) + x_flat = torch.nn.functional.pad(x_flat, convert_pad_shape([[0,0],[0,0],[0,length-1]])) + x_final = x_flat.view([batch, heads, length+1, 2*length-1])[:, :, :length, length-1:] + return x_final + + def _absolute_position_to_relative_position(self, x): + batch, heads, length, _ = x.size() + x = torch.nn.functional.pad(x, convert_pad_shape([[0, 0], [0, 0], [0, 0], [0, length-1]])) + x_flat = x.view([batch, heads, length**2 + length*(length - 1)]) + x_flat = torch.nn.functional.pad(x_flat, convert_pad_shape([[0, 0], [0, 0], [length, 0]])) + x_final = x_flat.view([batch, heads, length, 2*length])[:,:,:,1:] + return x_final + + def _attention_bias_proximal(self, length): + r = torch.arange(length, dtype=torch.float32) + diff = torch.unsqueeze(r, 0) - torch.unsqueeze(r, 1) + return torch.unsqueeze(torch.unsqueeze(-torch.log1p(torch.abs(diff)), 0), 0) + + +class FFN(BaseModule): + def __init__(self, in_channels, out_channels, filter_channels, kernel_size, + p_dropout=0.0): + super(FFN, self).__init__() + self.in_channels = in_channels + self.out_channels = out_channels + self.filter_channels = filter_channels + self.kernel_size = kernel_size + self.p_dropout = p_dropout + + self.conv_1 = torch.nn.Conv1d(in_channels, filter_channels, kernel_size, + padding=kernel_size//2) + self.conv_2 = torch.nn.Conv1d(filter_channels, out_channels, kernel_size, + padding=kernel_size//2) + self.drop = torch.nn.Dropout(p_dropout) + + def forward(self, x, x_mask): + x = self.conv_1(x * x_mask) + x = torch.relu(x) + x = self.drop(x) + x = self.conv_2(x * x_mask) + return x * x_mask + + +class Encoder(BaseModule): + def __init__(self, hidden_channels, filter_channels, n_heads, n_layers, + kernel_size=1, p_dropout=0.0, window_size=None, **kwargs): + super(Encoder, self).__init__() + self.hidden_channels = hidden_channels + self.filter_channels = filter_channels + self.n_heads = n_heads + self.n_layers = n_layers + self.kernel_size = kernel_size + self.p_dropout = p_dropout + self.window_size = window_size + + self.drop = torch.nn.Dropout(p_dropout) + self.attn_layers = torch.nn.ModuleList() + self.norm_layers_1 = torch.nn.ModuleList() + self.ffn_layers = torch.nn.ModuleList() + self.norm_layers_2 = torch.nn.ModuleList() + for _ in range(self.n_layers): + self.attn_layers.append(MultiHeadAttention(hidden_channels, hidden_channels, + n_heads, window_size=window_size, p_dropout=p_dropout)) + self.norm_layers_1.append(LayerNorm(hidden_channels)) + self.ffn_layers.append(FFN(hidden_channels, hidden_channels, + filter_channels, kernel_size, p_dropout=p_dropout)) + self.norm_layers_2.append(LayerNorm(hidden_channels)) + + def forward(self, x, x_mask): + attn_mask = x_mask.unsqueeze(2) * x_mask.unsqueeze(-1) + for i in range(self.n_layers): + x = x * x_mask + y = self.attn_layers[i](x, x, attn_mask) + y = self.drop(y) + x = self.norm_layers_1[i](x + y) + y = self.ffn_layers[i](x, x_mask) + y = self.drop(y) + x = self.norm_layers_2[i](x + y) + x = x * x_mask + return x + + +class MelEncoder(BaseModule): + def __init__(self, n_feats, channels, filters, heads, layers, kernel, + dropout, window_size=None): + super(MelEncoder, self).__init__() + self.n_feats = n_feats + self.channels = channels + self.filters = filters + self.heads = heads + self.layers = layers + self.kernel = kernel + self.dropout = dropout + self.window_size = window_size + print(self.channels) + print(self.n_feats) + self.init_proj = torch.nn.Conv1d(n_feats, channels, 1) + self.prenet = ConvReluNorm(channels, channels, channels, + kernel_size=5, n_layers=3, p_dropout=0.5) + + self.encoder = Encoder(channels, filters, heads, layers, kernel, + dropout, window_size=window_size) + + self.term_proj = torch.nn.Conv1d(channels, n_feats, 1) + + def forward(self, x, x_mask): + x = self.init_proj(x * x_mask) + x = self.prenet(x, x_mask) + x = self.encoder(x, x_mask) + x = self.term_proj(x * x_mask) + return x + +#layer +class Conv2d(nn.Module): + def __init__(self, cin, cout, kernel_size, stride, padding, residual=False, *args, **kwargs): + super().__init__(*args, **kwargs) + self.conv_block = nn.Sequential( + nn.Conv2d(cin, cout, kernel_size, stride, padding), + nn.BatchNorm2d(cout) + ) + self.act = nn.ReLU() + self.residual = residual + + def forward(self, x): + out = self.conv_block(x) + if self.residual: + out += x + return self.act(out) + +class nonorm_Conv2d(nn.Module): + def __init__(self, cin, cout, kernel_size, stride, padding, residual=False, *args, **kwargs): + super().__init__(*args, **kwargs) + self.conv_block = nn.Sequential( + nn.Conv2d(cin, cout, kernel_size, stride, padding), + ) + self.act = nn.LeakyReLU(0.01, inplace=True) + + def forward(self, x): + out = self.conv_block(x) + return self.act(out) + +class Conv2dTranspose(nn.Module): + def __init__(self, cin, cout, kernel_size, stride, padding, output_padding=0, *args, **kwargs): + super().__init__(*args, **kwargs) + self.conv_block = nn.Sequential( + nn.ConvTranspose2d(cin, cout, kernel_size, stride, padding, output_padding), + nn.BatchNorm2d(cout) + ) + self.act = nn.ReLU() + + def forward(self, x): + out = self.conv_block(x) + return self.act(out) + +#postnet +class Block1(BaseModule): + def __init__(self, dim, groups=8): + super(Block1, self).__init__() + self.block = torch.nn.Sequential(torch.nn.Conv2d(dim, dim, 7, + padding=3), torch.nn.GroupNorm(groups, dim), Mish()) + + def forward(self, x, mask): + output = self.block(x * mask) + return output * mask + + +class ResnetBlock1(BaseModule): + def __init__(self, dim, groups=8): + super(ResnetBlock1, self).__init__() + self.block1 = Block1(dim) + self.block2 = Block1(dim) + self.res = torch.nn.Conv2d(dim, dim, 1) + + def forward(self, x, mask): + h = self.block1(x, mask) + h = self.block2(h, mask) + output = self.res(x * mask) + h + return output + + +class PostNet(BaseModule): + def __init__(self, dim, groups=8): + super(PostNet, self).__init__() + self.init_conv = torch.nn.Conv2d(1, dim, 1) + self.res_block = ResnetBlock1(dim, groups=groups) + self.final_conv = torch.nn.Conv2d(dim, 1, 1) + + def forward(self, x, mask): + x = x.unsqueeze(1) + mask = mask.unsqueeze(1) + x = self.init_conv(x * mask) + x = self.res_block(x, mask) + output = self.final_conv(x * mask) + return output.squeeze(1) + +#utils +class PseudoInversion(BaseModule): + def __init__(self, n_mels, sampling_rate, n_fft): + super(PseudoInversion, self).__init__() + self.n_mels = n_mels + self.sampling_rate = sampling_rate + self.n_fft = n_fft + mel_basis = librosa_mel_fn(sampling_rate, n_fft, n_mels, 0, 8000) + mel_basis_inverse = np.linalg.pinv(mel_basis) + mel_basis_inverse = torch.from_numpy(mel_basis_inverse).float() + self.register_buffer("mel_basis_inverse", mel_basis_inverse) + + def forward(self, log_mel_spectrogram): + mel_spectrogram = torch.exp(log_mel_spectrogram) + stftm = torch.matmul(self.mel_basis_inverse, mel_spectrogram) + return stftm + + +class InitialReconstruction(BaseModule): + def __init__(self, n_fft, hop_size): + super(InitialReconstruction, self).__init__() + self.n_fft = n_fft + self.hop_size = hop_size + window = torch.hann_window(n_fft).float() + self.register_buffer("window", window) + + def forward(self, stftm): + real_part = torch.ones_like(stftm, device=stftm.device) + imag_part = torch.zeros_like(stftm, device=stftm.device) + #stft = torch.stack([real_part, imag_part], -1)*stftm.unsqueeze(-1) + + stft_complex = torch.complex(real_part * stftm, imag_part * stftm) + #istft = torchaudio.functional.istft(stft, n_fft=self.n_fft, + #hop_length=self.hop_size, win_length=self.n_fft, + # window=self.window, center=True) + print("0stft.shape:", stft_complex.shape) + istft = torch.istft(stft_complex, n_fft=self.n_fft, hop_length=self.hop_size, win_length=self.n_fft, + window=self.window,center=True ) + return istft.unsqueeze(1) + + +# Fast Griffin-Lim algorithm as a PyTorch module +class FastGL(BaseModule): + def __init__(self, n_mels, sampling_rate, n_fft, hop_size, momentum=0.99): + super(FastGL, self).__init__() + self.n_mels = n_mels + self.sampling_rate = sampling_rate + self.n_fft = n_fft + self.hop_size = hop_size + self.momentum = momentum + self.pi = PseudoInversion(n_mels, sampling_rate, n_fft) + self.ir = InitialReconstruction(n_fft, hop_size) + window = torch.hann_window(n_fft).float() + self.register_buffer("window", window) + + @torch.no_grad() + def forward(self, s, n_iters=32): + c = self.pi(s) + x = self.ir(c) + x = x.squeeze(1) + c = c.unsqueeze(-1) + prev_angles = torch.zeros_like(c, device=c.device) + for _ in range(n_iters): + s = torch.stft(x, n_fft=self.n_fft, hop_length=self.hop_size, + win_length=self.n_fft, window=self.window, + center=True, return_complex=True) + #real_part, imag_part = s.unbind(-1) + real_part = s.real + imag_part = s.imag + stftm = torch.sqrt(torch.clamp(real_part**2 + imag_part**2, min=1e-8)) + print(s.shape) + print(stftm.shape) + stftm_complex = torch.complex(stftm, torch.zeros_like(stftm)) + #angles = s / stftm.unsqueeze(-1) + angles = s / stftm_complex + angles=angles.unsqueeze(-1) + print("1s.shape:", s.shape) + print("c.shape:", c.shape) + print("prev_angles.shape:", prev_angles.shape) + s = c * (angles + self.momentum * (angles - prev_angles)) + # real_part = torch.ones_like(s, device=stftm.device) + # imag_part = torch.zeros_like(s, device=stftm.device) + # s_complex = torch.complex(real_part * s, imag_part * s) + #x = torchaudio.functional.istft(s, n_fft=self.n_fft, hop_length=self.hop_size, + #win_length=self.n_fft, window=self.window, + #center=True) + # print("2s.shape:", s_complex.shape) + s = s.squeeze(-1) + print("3s.shape:", s.shape) + x = torch.istft(s, n_fft=self.n_fft, hop_length=self.hop_size, + win_length=self.n_fft, window=self.window, + center=True) + prev_angles = angles + return x.unsqueeze(1) + +#vc +class FwdDiffusion(BaseModule): + def __init__(self, n_feats, channels, filters, heads, layers, kernel, + dropout, window_size, dim): + super(FwdDiffusion, self).__init__() + self.n_feats = n_feats + self.channels = channels + self.filters = filters + self.heads = heads + self.layers = layers + self.kernel = kernel + self.dropout = dropout + self.window_size = window_size + self.dim = dim + print(self.channels) + self.encoder = MelEncoder(n_feats, channels, filters, heads, layers, + kernel, dropout, window_size) + self.postnet = PostNet(dim) + + def nparams(self): + num_params = 0 + for name, param in self.named_parameters(): + if param.requires_grad: + num_params += np.prod(param.detach().cpu().numpy().shape) + return num_params + + + def relocate_input(self, x: list): + device = next(self.parameters()).device + for i in range(len(x)): + if isinstance(x[i], torch.Tensor) and x[i].device != device: + x[i] = x[i].to(device) + return x + + @torch.no_grad() + def forward(self, x, mask): + x, mask = self.relocate_input([x, mask]) + z = self.encoder(x, mask) + z_output = self.postnet(z, mask) + return z_output + + def compute_loss(self, x, y, mask): + x, y, mask = self.relocate_input([x, y, mask]) + z = self.encoder(x, mask) + z_output = self.postnet(z, mask) + loss = mse_loss(z_output, y, mask, self.n_feats) + return loss + + +class diffvc(AbstractTalkingFace): + def __init__(self, config): + super(diffvc, self).__init__() + + self.n_feats=config["n_feats"] + self.channels= config["channels"] + self.filters=config["filters"] + self.heads=config["heads"] + self.layers=config["layers"] + self.kernel=config["kernel"] + self.dropout=config["dropout"] + self.window_size=config["window_size"] + self.enc_dim=config["enc_dim"] + self.spk_dim=config["spk_dim"] + self.use_ref_t=config["use_ref_t"] + self.dec_dim=config["dec_dim"] + self.beta_min=config["beta_min"] + self.beta_max=config["beta_max"] + print(config["n_feats"]) + self.encoder = FwdDiffusion(config["n_feats"], config["channels"], config["filters"], + config["heads"],config["layers"],config["kernel"], + config["dropout"],config["window_size"],config["enc_dim"]) + self.decoder = Diffusion(config["n_feats"], config["dec_dim"],config["spk_dim"],config["use_ref_t"], + config["beta_min"],config["beta_max"]) + + def nparams(self): + num_params = 0 + for name, param in self.named_parameters(): + if param.requires_grad: + num_params += np.prod(param.detach().cpu().numpy().shape) + return num_params + + + def relocate_input(self, x: list): + device = next(self.parameters()).device + for i in range(len(x)): + if isinstance(x[i], torch.Tensor) and x[i].device != device: + x[i] = x[i].to(device) + return x + + def load_encoder(self, enc_path): + enc_dict = torch.load(enc_path, map_location=lambda loc, storage: loc) + self.encoder.load_state_dict(enc_dict, strict=False) + + @torch.no_grad() + def forward(self, x, x_lengths, x_ref, x_ref_lengths, c, n_timesteps, + mode='ml'): + """ + Generates mel-spectrogram from source mel-spectrogram conditioned on + target speaker embedding. Returns: + 1. 'average voice' encoder outputs + 2. decoder outputs + + Args: + x (torch.Tensor): batch of source mel-spectrograms. + x_lengths (torch.Tensor): numbers of frames in source mel-spectrograms. + x_ref (torch.Tensor): batch of reference mel-spectrograms. + x_ref_lengths (torch.Tensor): numbers of frames in reference mel-spectrograms. + c (torch.Tensor): batch of reference speaker embeddings + n_timesteps (int): number of steps to use for reverse diffusion in decoder. + mode (string, optional): sampling method. Can be one of: + 'pf' - probability flow sampling (Euler scheme for ODE) + 'em' - Euler-Maruyama SDE solver + 'ml' - Maximum Likelihood SDE solver + """ + x, x_lengths = self.relocate_input([x, x_lengths]) + x_ref, x_ref_lengths, c = self.relocate_input([x_ref, x_ref_lengths, c]) + x_mask = sequence_mask(x_lengths).unsqueeze(1).to(x.dtype) + x_ref_mask = sequence_mask(x_ref_lengths).unsqueeze(1).to(x_ref.dtype) + mean = self.encoder(x, x_mask) + mean_x = self.decoder.compute_diffused_mean(x, x_mask, mean, 1.0) + mean_ref = self.encoder(x_ref, x_ref_mask) + + b = x.shape[0] + max_length = int(x_lengths.max()) + max_length_new = fix_len_compatibility(max_length) + x_mask_new = sequence_mask(x_lengths, max_length_new).unsqueeze(1).to(x.dtype) + mean_new = torch.zeros((b, self.n_feats, max_length_new), dtype=x.dtype, + device=x.device) + mean_x_new = torch.zeros((b, self.n_feats, max_length_new), dtype=x.dtype, + device=x.device) + for i in range(b): + mean_new[i, :, :x_lengths[i]] = mean[i, :, :x_lengths[i]] + mean_x_new[i, :, :x_lengths[i]] = mean_x[i, :, :x_lengths[i]] + + z = mean_x_new + z += torch.randn_like(mean_x_new, device=mean_x_new.device) + + y = self.decoder(z, x_mask_new, mean_new, x_ref, x_ref_mask, mean_ref, c, + n_timesteps, mode) + return mean_x, y[:, :, :max_length] + + def compute_loss(self, x, x_lengths, x_ref, c): + """ + Computes diffusion (score matching) loss. + + Args: + x (torch.Tensor): batch of source mel-spectrograms. + x_lengths (torch.Tensor): numbers of frames in source mel-spectrograms. + x_ref (torch.Tensor): batch of reference mel-spectrograms. + c (torch.Tensor): batch of reference speaker embeddings + """ + x, x_lengths, x_ref, c = self.relocate_input([x, x_lengths, x_ref, c]) + x_mask = sequence_mask(x_lengths).unsqueeze(1).to(x.dtype) + mean = self.encoder(x, x_mask).detach() + mean_ref = self.encoder(x_ref, x_mask).detach() + diff_loss = self.decoder.compute_loss(x, x_mask, mean, x_ref, mean_ref, c) + return diff_loss + + + def calculate_loss(self, interaction): + x, x_lengths, x_ref, c = interaction + return {"loss": self.compute_loss(x, x_lengths, x_ref, c)} + + def predict(self, interaction): + x, x_lengths, x_ref, x_ref_lengths, c, n_timesteps, mode = interaction + return self.forward(x, x_lengths, x_ref, x_ref_lengths, c, n_timesteps, mode) + + def generate_batch(self): + # Implement this method based on your requirements + pass + + + + diff --git a/talkingface/model/voice_conversion/hifi-gan.py b/talkingface/model/voice_conversion/hifi-gan.py new file mode 100644 index 00000000..06fa6efa --- /dev/null +++ b/talkingface/model/voice_conversion/hifi-gan.py @@ -0,0 +1,340 @@ +""" from https://github.com/jik876/hifi-gan """ +from abstract_talkingface import AbstractTalkingFace +import torch +import torch.nn.functional as F +import torch.nn as nn +from torch.nn import Conv1d, ConvTranspose1d, AvgPool1d, Conv2d +from torch.nn.utils import weight_norm, remove_weight_norm, spectral_norm +from xutils import init_weights, get_padding +import glob +import os +import matplotlib +matplotlib.use("Agg") +import matplotlib.pylab as plt + +LRELU_SLOPE = 0.1 + +class HiFiGANUtils: + @staticmethod + def plot_spectrogram(spectrogram): + fig, ax = plt.subplots(figsize=(10, 2)) + im = ax.imshow(spectrogram, aspect="auto", origin="lower", + interpolation='none') + plt.colorbar(im, ax=ax) + + fig.canvas.draw() + plt.close() + + return fig + + @staticmethod + 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) + + @staticmethod + def apply_weight_norm(m): + classname = m.__class__.__name__ + if classname.find("Conv") != -1: + weight_norm(m) + + @staticmethod + def get_padding(kernel_size, dilation=1): + return int((kernel_size * dilation - dilation) / 2) + + @staticmethod + def load_checkpoint(filepath, device): + assert os.path.isfile(filepath) + print("Loading '{}'".format(filepath)) + checkpoint_dict = torch.load(filepath, map_location=device) + print("Complete.") + return checkpoint_dict + + @staticmethod + def save_checkpoint(filepath, obj): + print("Saving checkpoint to {}".format(filepath)) + torch.save(obj, filepath) + print("Complete.") + + @staticmethod + def scan_checkpoint(cp_dir, prefix): + pattern = os.path.join(cp_dir, prefix + '????????') + cp_list = glob.glob(pattern) + if len(cp_list) == 0: + return None + return sorted(cp_list)[-1] + +class ResBlock1(torch.nn.Module): + def __init__(self, h, channels, kernel_size=3, dilation=(1, 3, 5)): + super(ResBlock1, self).__init__() + self.h = h + self.convs1 = nn.ModuleList([ + weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=dilation[0], + padding=HiFiGANUtils.get_padding(kernel_size, dilation[0]))), + weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=dilation[1], + padding=HiFiGANUtils.get_padding(kernel_size, dilation[1]))), + weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=dilation[2], + padding=HiFiGANUtils.get_padding(kernel_size, dilation[2]))) + ]) + self.convs1.apply(HiFiGANUtils.init_weights) + + self.convs2 = nn.ModuleList([ + weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=1, + padding=HiFiGANUtils.get_padding(kernel_size, 1))), + weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=1, + padding=HiFiGANUtils.get_padding(kernel_size, 1))), + weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=1, + padding=HiFiGANUtils.get_padding(kernel_size, 1))) + ]) + self.convs2.apply(HiFiGANUtils.init_weights) + + def forward(self, x): + for c1, c2 in zip(self.convs1, self.convs2): + xt = F.leaky_relu(x, LRELU_SLOPE) + xt = c1(xt) + xt = F.leaky_relu(xt, LRELU_SLOPE) + xt = c2(xt) + x = xt + x + return x + + def remove_weight_norm(self): + for l in self.convs1: + remove_weight_norm(l) + for l in self.convs2: + remove_weight_norm(l) + + +class ResBlock2(torch.nn.Module): + def __init__(self, h, channels, kernel_size=3, dilation=(1, 3)): + super(ResBlock2, self).__init__() + self.h = h + self.convs = nn.ModuleList([ + weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=dilation[0], + padding=HiFiGANUtils.get_padding(kernel_size, dilation[0]))), + weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=dilation[1], + padding=HiFiGANUtils.get_padding(kernel_size, dilation[1]))) + ]) + self.convs.apply(HiFiGANUtils.init_weights) + + def forward(self, x): + for c in self.convs: + xt = F.leaky_relu(x, LRELU_SLOPE) + xt = c(xt) + x = xt + x + return x + + def remove_weight_norm(self): + for l in self.convs: + remove_weight_norm(l) + + +class Generator(torch.nn.Module): + def __init__(self, h): + super(Generator, self).__init__() + self.h = h + self.num_kernels = len(h.resblock_kernel_sizes) + self.num_upsamples = len(h.upsample_rates) + self.conv_pre = weight_norm(Conv1d(80, h.upsample_initial_channel, 7, 1, padding=3)) + resblock = ResBlock1 if h.resblock == '1' else ResBlock2 + + self.ups = nn.ModuleList() + for i, (u, k) in enumerate(zip(h.upsample_rates, h.upsample_kernel_sizes)): + self.ups.append(weight_norm( + ConvTranspose1d(h.upsample_initial_channel//(2**i), h.upsample_initial_channel//(2**(i+1)), + k, u, padding=(k-u)//2))) + + self.resblocks = nn.ModuleList() + for i in range(len(self.ups)): + ch = h.upsample_initial_channel//(2**(i+1)) + for j, (k, d) in enumerate(zip(h.resblock_kernel_sizes, h.resblock_dilation_sizes)): + self.resblocks.append(resblock(h, ch, k, d)) + + self.conv_post = weight_norm(Conv1d(ch, 1, 7, 1, padding=3)) + self.ups.apply(HiFiGANUtils.init_weights) + self.conv_post.apply(HiFiGANUtils.init_weights) + + def forward(self, x): + x = self.conv_pre(x) + for i in range(self.num_upsamples): + x = F.leaky_relu(x, LRELU_SLOPE) + x = self.ups[i](x) + xs = None + for j in range(self.num_kernels): + if xs is None: + xs = self.resblocks[i*self.num_kernels+j](x) + else: + xs += self.resblocks[i*self.num_kernels+j](x) + x = xs / self.num_kernels + x = F.leaky_relu(x) + x = self.conv_post(x) + x = torch.tanh(x) + + return x + + def remove_weight_norm(self): + print('Removing weight norm...') + for l in self.ups: + remove_weight_norm(l) + for l in self.resblocks: + l.remove_weight_norm() + remove_weight_norm(self.conv_pre) + remove_weight_norm(self.conv_post) + + +class DiscriminatorP(torch.nn.Module): + def __init__(self, period, kernel_size=5, stride=3, use_spectral_norm=False): + super(DiscriminatorP, self).__init__() + self.period = period + norm_f = weight_norm if use_spectral_norm == False else spectral_norm + self.convs = nn.ModuleList([ + norm_f(Conv2d(1, 32, (kernel_size, 1), (stride, 1), padding=(HiFiGANUtils.get_padding(5, 1), 0))), + norm_f(Conv2d(32, 128, (kernel_size, 1), (stride, 1), padding=(HiFiGANUtils.get_padding(5, 1), 0))), + norm_f(Conv2d(128, 512, (kernel_size, 1), (stride, 1), padding=(HiFiGANUtils.get_padding(5, 1), 0))), + norm_f(Conv2d(512, 1024, (kernel_size, 1), (stride, 1), padding=(HiFiGANUtils.get_padding(5, 1), 0))), + norm_f(Conv2d(1024, 1024, (kernel_size, 1), 1, padding=(2, 0))), + ]) + self.conv_post = norm_f(Conv2d(1024, 1, (3, 1), 1, padding=(1, 0))) + + def forward(self, x): + fmap = [] + + # 1d to 2d + b, c, t = x.shape + if t % self.period != 0: # pad first + n_pad = self.period - (t % self.period) + x = F.pad(x, (0, n_pad), "reflect") + t = t + n_pad + x = x.view(b, c, t // self.period, self.period) + + for l in self.convs: + x = l(x) + x = F.leaky_relu(x, LRELU_SLOPE) + fmap.append(x) + x = self.conv_post(x) + fmap.append(x) + x = torch.flatten(x, 1, -1) + + return x, fmap + + +class MultiPeriodDiscriminator(torch.nn.Module): + def __init__(self): + super(MultiPeriodDiscriminator, self).__init__() + self.discriminators = nn.ModuleList([ + DiscriminatorP(2), + DiscriminatorP(3), + DiscriminatorP(5), + DiscriminatorP(7), + DiscriminatorP(11), + ]) + + def forward(self, y, y_hat): + y_d_rs = [] + y_d_gs = [] + fmap_rs = [] + fmap_gs = [] + for i, d in enumerate(self.discriminators): + y_d_r, fmap_r = d(y) + y_d_g, fmap_g = d(y_hat) + y_d_rs.append(y_d_r) + fmap_rs.append(fmap_r) + y_d_gs.append(y_d_g) + fmap_gs.append(fmap_g) + + return y_d_rs, y_d_gs, fmap_rs, fmap_gs + + +class DiscriminatorS(torch.nn.Module): + def __init__(self, use_spectral_norm=False): + super(DiscriminatorS, self).__init__() + norm_f = weight_norm if use_spectral_norm == False else spectral_norm + self.convs = nn.ModuleList([ + norm_f(Conv1d(1, 128, 15, 1, padding=7)), + norm_f(Conv1d(128, 128, 41, 2, groups=4, padding=20)), + norm_f(Conv1d(128, 256, 41, 2, groups=16, padding=20)), + norm_f(Conv1d(256, 512, 41, 4, groups=16, padding=20)), + norm_f(Conv1d(512, 1024, 41, 4, groups=16, padding=20)), + norm_f(Conv1d(1024, 1024, 41, 1, groups=16, padding=20)), + norm_f(Conv1d(1024, 1024, 5, 1, padding=2)), + ]) + self.conv_post = norm_f(Conv1d(1024, 1, 3, 1, padding=1)) + + def forward(self, x): + fmap = [] + for l in self.convs: + x = l(x) + x = F.leaky_relu(x, LRELU_SLOPE) + fmap.append(x) + x = self.conv_post(x) + fmap.append(x) + x = torch.flatten(x, 1, -1) + + return x, fmap + + +class MultiScaleDiscriminator(torch.nn.Module): + def __init__(self): + super(MultiScaleDiscriminator, self).__init__() + self.discriminators = nn.ModuleList([ + DiscriminatorS(use_spectral_norm=True), + DiscriminatorS(), + DiscriminatorS(), + ]) + self.meanpools = nn.ModuleList([ + AvgPool1d(4, 2, padding=2), + AvgPool1d(4, 2, padding=2) + ]) + + def forward(self, y, y_hat): + y_d_rs = [] + y_d_gs = [] + fmap_rs = [] + fmap_gs = [] + for i, d in enumerate(self.discriminators): + if i != 0: + y = self.meanpools[i-1](y) + y_hat = self.meanpools[i-1](y_hat) + y_d_r, fmap_r = d(y) + y_d_g, fmap_g = d(y_hat) + y_d_rs.append(y_d_r) + fmap_rs.append(fmap_r) + y_d_gs.append(y_d_g) + fmap_gs.append(fmap_g) + + return y_d_rs, y_d_gs, fmap_rs, fmap_gs + + +def feature_loss(fmap_r, fmap_g): + loss = 0 + for dr, dg in zip(fmap_r, fmap_g): + for rl, gl in zip(dr, dg): + loss += torch.mean(torch.abs(rl - gl)) + + return loss*2 + + +def discriminator_loss(disc_real_outputs, disc_generated_outputs): + loss = 0 + r_losses = [] + g_losses = [] + for dr, dg in zip(disc_real_outputs, disc_generated_outputs): + r_loss = torch.mean((1-dr)**2) + g_loss = torch.mean(dg**2) + loss += (r_loss + g_loss) + r_losses.append(r_loss.item()) + g_losses.append(g_loss.item()) + + return loss, r_losses, g_losses + + +def generator_loss(disc_outputs): + loss = 0 + gen_losses = [] + for dg in disc_outputs: + l = torch.mean((1-dg)**2) + gen_losses.append(l) + loss += l + + return loss, gen_losses + diff --git a/talkingface/properties/dataset/diffvc_dataset.yaml b/talkingface/properties/dataset/diffvc_dataset.yaml new file mode 100644 index 00000000..8dc20796 --- /dev/null +++ b/talkingface/properties/dataset/diffvc_dataset.yaml @@ -0,0 +1,19 @@ +--- +data_dir: 'dataset/diffvc_data/' +val_file: "dataset/diffvc_data/filelist/valid.txt" # 注意:修复了注释位置dataset/diffvc_data/filelist/valid.txt +exc_file: "dataset/diffvc_data/filelist/exceptions_libritts.txt" # 注意:修复了注释位置 +log_dir: 'logs_dec' +enc_dir: 'logs_enc' +epochs: 10 +batch_size: 2 +learning_rate: 1e-4 +save_every: 1 + +# Train +checkpoint_sub_dir: "/diffvc" # 和overall.yaml里checkpoint_dir拼起来作为最终目录 + +temp_sub_dir: "/diffvc" # 和overall.yaml里temp_dir拼起来作为最终目录 + +train_filelist: 'dataset/diff_data/filelist/valid.txt' # 当前数据集的数据划分文件 train +test_filelist: 'dataset/diff_data/filelist/valid.txt' # 当前数据集的数据划分文件 test +val_filelist: 'dataset/diff_data/filelist/valid.txt' # 当前数据集的数据划分文件 val \ No newline at end of file diff --git a/talkingface/properties/dataset/diffvc_encoder_dataset.yaml b/talkingface/properties/dataset/diffvc_encoder_dataset.yaml new file mode 100644 index 00000000..25a430ae --- /dev/null +++ b/talkingface/properties/dataset/diffvc_encoder_dataset.yaml @@ -0,0 +1,32 @@ +""" from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ + +## Mel-filterbank +mel_window_length = 25 # In milliseconds +mel_window_step = 10 # In milliseconds +mel_n_channels = 40 + + +## Audio +sampling_rate = 16000 +# Number of spectrogram frames in a partial utterance +partials_n_frames = 160 # 1600 ms +# Number of spectrogram frames at inference +inference_n_frames = 80 # 800 ms + + +## Voice Activation Detection +# Window size of the VAD. Must be either 10, 20 or 30 milliseconds. +# This sets the granularity of the VAD. Should not need to be changed. +vad_window_length = 30 # In milliseconds +# Number of frames to average together when performing the moving average smoothing. +# The larger this value, the larger the VAD variations must be to not get smoothed out. +vad_moving_average_width = 8 +# Maximum number of consecutive silent frames a segment can have. +vad_max_silence_length = 6 + + +## Audio volume normalization +audio_norm_target_dBFS = -30 + + + diff --git a/talkingface/properties/dataset/params_data.py b/talkingface/properties/dataset/params_data.py new file mode 100644 index 00000000..62d04121 --- /dev/null +++ b/talkingface/properties/dataset/params_data.py @@ -0,0 +1,30 @@ +""" from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ + +## Mel-filterbank +mel_window_length = 25 # In milliseconds +mel_window_step = 10 # In milliseconds +mel_n_channels = 40 + + +## Audio +sampling_rate = 16000 +# Number of spectrogram frames in a partial utterance +partials_n_frames = 160 # 1600 ms +# Number of spectrogram frames at inference +inference_n_frames = 80 # 800 ms + + +## Voice Activation Detection +# Window size of the VAD. Must be either 10, 20 or 30 milliseconds. +# This sets the granularity of the VAD. Should not need to be changed. +vad_window_length = 30 # In milliseconds +# Number of frames to average together when performing the moving average smoothing. +# The larger this value, the larger the VAD variations must be to not get smoothed out. +vad_moving_average_width = 8 +# Maximum number of consecutive silent frames a segment can have. +vad_max_silence_length = 6 + + +## Audio volume normalization +audio_norm_target_dBFS = -30 + diff --git a/talkingface/properties/model/diffvc.yaml b/talkingface/properties/model/diffvc.yaml new file mode 100644 index 00000000..bf473874 --- /dev/null +++ b/talkingface/properties/model/diffvc.yaml @@ -0,0 +1,39 @@ +--- +n_feats: 80 +sampling_rate: 22050 +n_fft: 1024 +hop_size: 256 +channels: 192 +filters: 768 +layers: 6 +kernel: 3 +dropout: 0.1 +heads: 2 +window_size: 4 +enc_dim: 128 +dec_dim: 256 +spk_dim: 128 +use_ref_t: True +beta_min: 0.05 +beta_max: 20.0 +random_seed: 37 +test_size: 1 +train_frame: 128 +data_dir: 'dataset/diffvc_data/' +val_file: "dataset/diffvc_data/filelist/valid.txt" # 注意:修复了注释位置dataset/diffvc_data/filelist/valid.txt +exc_file: "dataset/diffvc_data/filelist/exceptions_libritts.txt" # 注意:修复了注释位置 +log_dir: 'logs_dec' +enc_dir: 'logs_enc' +epochs: 10 +batch_size: 32 +learning_rate: 1e-4 +save_every: 1 + +# Train +checkpoint_sub_dir: "/diffvc" # 和overall.yaml里checkpoint_dir拼起来作为最终目录 + +temp_sub_dir: "/diffvc" # 和overall.yaml里temp_dir拼起来作为最终目录 + +train_filelist: 'dataset/diff_data/filelist/valid.txt' # 当前数据集的数据划分文件 train +test_filelist: '' # 当前数据集的数据划分文件 test +val_filelist: '' # 当前数据集的数据划分文件 val \ No newline at end of file diff --git a/talkingface/properties/model/diffvc_encoder.yaml b/talkingface/properties/model/diffvc_encoder.yaml new file mode 100644 index 00000000..be1c069a --- /dev/null +++ b/talkingface/properties/model/diffvc_encoder.yaml @@ -0,0 +1,14 @@ +""" from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ + +## Model parameters +model_hidden_size = 256 +model_embedding_size = 256 +model_num_layers = 3 + + +## Training parameters +learning_rate_init = 1e-4 +speakers_per_batch = 64 +utterances_per_speaker = 10 + + diff --git a/talkingface/properties/overall.yaml b/talkingface/properties/overall.yaml index 81ac51ae..dab84676 100644 --- a/talkingface/properties/overall.yaml +++ b/talkingface/properties/overall.yaml @@ -1,13 +1,13 @@ # Enviroment Settings gpu_id: '3, 4, 5' # (str) The id of GPU device(s). worker: 0 # (int) The number of workers processing the data. -use_gpu: True # (bool) Whether or not to use GPU. +use_gpu: False # (bool) Whether or not to use GPU. seed: 2023 # (int) Random seed. checkpoint_dir: 'saved' # (str) The path to save checkpoint file. show_progress: True # (bool) Whether or not to show the progress bar of every epoch. log_wandb: False # (bool) Whether or not to use Weights & Biases(W&B). shuffle: True # (bool) Whether or not to shuffle the training data before each epoch. -device: 'cuda' +device: 'cpu' reproducibility: True # (bool) Whether or not to make results reproducible. # Training Settings diff --git a/talkingface/quick_start/quick_start.py b/talkingface/quick_start/quick_start.py index 3ff2e889..185bb7d1 100644 --- a/talkingface/quick_start/quick_start.py +++ b/talkingface/quick_start/quick_start.py @@ -77,13 +77,12 @@ def run_talkingface( train_dataset, val_dataset = create_dataset(config) train_data_loader = data_utils.DataLoader( - train_dataset, batch_size=config["batch_size"], shuffle=True - ) + train_dataset, batch_size=config["batch_size"], shuffle=False) val_data_loader = data_utils.DataLoader( val_dataset, batch_size=config["batch_size"], shuffle=False ) - # load model + #load model model = get_model(config["model"])(config).to(config["device"]) logger.info(model) diff --git a/talkingface/trainer/diffvc_trainer.py b/talkingface/trainer/diffvc_trainer.py new file mode 100644 index 00000000..e69de29b diff --git a/talkingface/trainer/trainer.py b/talkingface/trainer/trainer.py index 2c34717b..e7eb88df 100644 --- a/talkingface/trainer/trainer.py +++ b/talkingface/trainer/trainer.py @@ -2,7 +2,7 @@ from logging import getLogger from time import time -import dlib, json, subprocess +#import dlib, json, subprocess import torch.nn.functional as F import glob import numpy as np @@ -13,6 +13,33 @@ import torch.cuda.amp as amp from torch import nn from pathlib import Path +#train talkingface.data.dataset.data_objects talkingface.utils.voice_conversion_talkingface +from talkingface.data.dataset.data_objects import SpeakerVerificationDataLoader, SpeakerVerificationDataset +from talkingface.utils.voice_conversion_talkingface.params_model import * +from talkingface.model.voice_conversion.diffvc import SpeakerEncoder +from talkingface.utils.voice_conversion_talkingface.DiffVC_speaker_encoder_utils import Profiler as Profiler +from pathlib import Path + +from torch.utils.data import DataLoader + +from talkingface.data.dataset.diffvc_dataset import diffvcDataset, VCDecBatchCollate,VCDecDataset +from talkingface.model.voice_conversion.diffvc import diffvc +from talkingface.model.voice_conversion.diffvc import FastGL +from talkingface.utils.utils import save_plot, save_audio + +#visualization +from talkingface.data.dataset.data_objects.speaker_verification_dataset import SpeakerVerificationDataset +from datetime import datetime +from time import perf_counter as timer +import matplotlib.pyplot as plt +import numpy as np +import params + +from talkingface.model.voice_conversion.diffvc import diffvc +# import webbrowser +#import visdom +#import umap +#import talkingface.properties.model.diffvctrain_params as params from talkingface.utils import( ensure_dir, @@ -25,9 +52,296 @@ get_gpu_usage, WandbLogger ) -from talkingface.data.dataprocess.wav2lip_process import Wav2LipAudio +#from talkingface.data.dataprocess.wav2lip_process import Wav2LipAudio from talkingface.evaluator import Evaluator +class diffVC_encoder_train: + def sync(device: torch.device): + # FIXME + return + # For correct profiling (cuda operations are async) + if device.type == "cuda": + torch.cuda.synchronize(device) + + def train(run_id: str, clean_data_root: Path, models_dir: Path, umap_every: int, save_every: int, + backup_every: int, vis_every: int, force_restart: bool, visdom_server: str, + no_visdom: bool): + # Create a dataset and a dataloader + dataset = SpeakerVerificationDataset(clean_data_root) + loader = SpeakerVerificationDataLoader( + dataset, + diffVC_encoder_train.speakers_per_batch, + diffVC_encoder_train.utterances_per_speaker, + num_workers=8, + ) + + # Setup the device on which to run the forward pass and the loss. These can be different, + # because the forward pass is faster on the GPU whereas the loss is often (depending on your + # hyperparameters) faster on the CPU. + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + # FIXME: currently, the gradient is None if loss_device is cuda + loss_device = torch.device("cpu") + + # Create the model and the optimizer + model = SpeakerEncoder(device, loss_device) + optimizer = torch.optim.Adam(model.parameters(), lr=diffVC_encoder_train.learning_rate_init) + init_step = 1 + + # Configure file path for the model + state_fpath = models_dir.joinpath(run_id + ".pt") + backup_dir = models_dir.joinpath(run_id + "_backups") + + # Load any existing model + if not force_restart: + if state_fpath.exists(): + print("Found existing model \"%s\", loading it and resuming training." % run_id) + checkpoint = torch.load(state_fpath) + init_step = checkpoint["step"] + model.load_state_dict(checkpoint["model_state"]) + optimizer.load_state_dict(checkpoint["optimizer_state"]) + optimizer.param_groups[0]["lr"] = diffVC_encoder_train.learning_rate_init + else: + print("No model \"%s\" found, starting training from scratch." % run_id) + else: + print("Starting the training from scratch.") + model.train() + + # Initialize the visualization environment + vis = Visualizations(run_id, vis_every, server=visdom_server, disabled=no_visdom) + vis.log_dataset(dataset) + vis.log_params() + device_name = str(torch.cuda.get_device_name(0) if torch.cuda.is_available() else "CPU") + vis.log_implementation({"Device": device_name}) + + # Training loop + profiler = Profiler(summarize_every=10, disabled=False) + for step, speaker_batch in enumerate(loader, init_step): + profiler.tick("Blocking, waiting for batch (threaded)") + + # Forward pass + inputs = torch.from_numpy(speaker_batch.data).to(device) + diffVC_encoder_train.sync(device) + profiler.tick("Data to %s" % device) + embeds = model(inputs) + diffVC_encoder_train.sync(device) + profiler.tick("Forward pass") + embeds_loss = embeds.view((diffVC_encoder_train.speakers_per_batch, diffVC_encoder_train.utterances_per_speaker, -1)).to(loss_device) + loss, eer = model.loss(embeds_loss) + diffVC_encoder_train.sync(loss_device) + profiler.tick("Loss") + + # Backward pass + model.zero_grad() + loss.backward() + profiler.tick("Backward pass") + model.do_gradient_ops() + optimizer.step() + profiler.tick("Parameter update") + + # Update visualizations + # learning_rate = optimizer.param_groups[0]["lr"] + vis.update(loss.item(), eer, step) + + # Draw projections and save them to the backup folder + if umap_every != 0 and step % umap_every == 0: + print("Drawing and saving projections (step %d)" % step) + backup_dir.mkdir(exist_ok=True) + projection_fpath = backup_dir.joinpath("%s_umap_%06d.png" % (run_id, step)) + embeds = embeds.detach().cpu().numpy() + vis.draw_projections(embeds, diffVC_encoder_train.utterances_per_speaker, step, projection_fpath) + vis.save() + + # Overwrite the latest version of the model + if save_every != 0 and step % save_every == 0: + print("Saving the model (step %d)" % step) + torch.save({ + "step": step + 1, + "model_state": model.state_dict(), + "optimizer_state": optimizer.state_dict(), + }, state_fpath) + + # Make a backup + if backup_every != 0 and step % backup_every == 0: + print("Making a backup (step %d)" % step) + backup_dir.mkdir(exist_ok=True) + backup_fpath = backup_dir.joinpath("%s_bak_%06d.pt" % (run_id, step)) + torch.save({ + "step": step + 1, + "model_state": model.state_dict(), + "optimizer_state": optimizer.state_dict(), + }, backup_fpath) + + profiler.tick("Extras (visualizations, saving)") + + +class Visualizations: + colormap = np.array([ + [76, 255, 0], + [0, 127, 70], + [255, 0, 0], + [255, 217, 38], + [0, 135, 255], + [165, 0, 165], + [255, 167, 255], + [0, 255, 255], + [255, 96, 38], + [142, 76, 0], + [33, 0, 127], + [0, 0, 0], + [183, 183, 183], + ], dtype=np.float) / 255 + + def __init__(self, env_name=None, update_every=10, server="http://localhost", disabled=False): + # Tracking data + self.last_update_timestamp = timer() + self.update_every = update_every + self.step_times = [] + self.losses = [] + self.eers = [] + print("Updating the visualizations every %d steps." % update_every) + + # If visdom is disabled TODO: use a better paradigm for that + self.disabled = disabled + if self.disabled: + return + + # Set the environment name + now = str(datetime.now().strftime("%d-%m %Hh%M")) + if env_name is None: + self.env_name = now + else: + self.env_name = "%s (%s)" % (env_name, now) + + # Connect to visdom and open the corresponding window in the browser + try: + self.vis = visdom.Visdom(server, env=self.env_name, raise_exceptions=True) + except ConnectionError: + raise Exception("No visdom server detected. Run the command \"visdom\" in your CLI to " + "start it.") + # webbrowser.open("http://localhost:8097/env/" + self.env_name) + + # Create the windows + self.loss_win = None + self.eer_win = None + # self.lr_win = None + self.implementation_win = None + self.projection_win = None + self.implementation_string = "" + + def log_params(self): + if self.disabled: + return + from talkingface.utils.voice_conversion_talkingface import params_data + from talkingface.utils.voice_conversion_talkingface import params_model + param_string = "Model parameters:
" + for param_name in (p for p in dir(params_model) if not p.startswith("__")): + value = getattr(params_model, param_name) + param_string += "\t%s: %s
" % (param_name, value) + param_string += "Data parameters:
" + for param_name in (p for p in dir(params_data) if not p.startswith("__")): + value = getattr(params_data, param_name) + param_string += "\t%s: %s
" % (param_name, value) + self.vis.text(param_string, opts={"title": "Parameters"}) + + def log_dataset(self, dataset: SpeakerVerificationDataset): + if self.disabled: + return + dataset_string = "" + dataset_string += "Speakers: %s\n" % len(dataset.speakers) + dataset_string += "\n" + dataset.get_logs() + dataset_string = dataset_string.replace("\n", "
") + self.vis.text(dataset_string, opts={"title": "Dataset"}) + + def log_implementation(self, params): + if self.disabled: + return + implementation_string = "" + for param, value in params.items(): + implementation_string += "%s: %s\n" % (param, value) + implementation_string = implementation_string.replace("\n", "
") + self.implementation_string = implementation_string + self.implementation_win = self.vis.text( + implementation_string, + opts={"title": "Training implementation"} + ) + + def update(self, loss, eer, step): + # Update the tracking data + now = timer() + self.step_times.append(1000 * (now - self.last_update_timestamp)) + self.last_update_timestamp = now + self.losses.append(loss) + self.eers.append(eer) + print(".", end="") + + # Update the plots every steps + if step % self.update_every != 0: + return + time_string = "Step time: mean: %5dms std: %5dms" % \ + (int(np.mean(self.step_times)), int(np.std(self.step_times))) + print("\nStep %6d Loss: %.4f EER: %.4f %s" % + (step, np.mean(self.losses), np.mean(self.eers), time_string)) + if not self.disabled: + self.loss_win = self.vis.line( + [np.mean(self.losses)], + [step], + win=self.loss_win, + update="append" if self.loss_win else None, + opts=dict( + legend=["Avg. loss"], + xlabel="Step", + ylabel="Loss", + title="Loss", + ) + ) + self.eer_win = self.vis.line( + [np.mean(self.eers)], + [step], + win=self.eer_win, + update="append" if self.eer_win else None, + opts=dict( + legend=["Avg. EER"], + xlabel="Step", + ylabel="EER", + title="Equal error rate" + ) + ) + if self.implementation_win is not None: + self.vis.text( + self.implementation_string + ("%s" % time_string), + win=self.implementation_win, + opts={"title": "Training implementation"}, + ) + + # Reset the tracking + self.losses.clear() + self.eers.clear() + self.step_times.clear() + + def draw_projections(self, embeds, utterances_per_speaker, step, out_fpath=None, + max_speakers=10): + max_speakers = min(max_speakers, len(Visualizations.colormap)) + embeds = embeds[:max_speakers * utterances_per_speaker] + + n_speakers = len(embeds) // utterances_per_speaker + ground_truth = np.repeat(np.arange(n_speakers), utterances_per_speaker) + colors = [Visualizations.colormap[i] for i in ground_truth] + + reducer = umap.UMAP() + projected = reducer.fit_transform(embeds) + plt.scatter(projected[:, 0], projected[:, 1], c=colors) + plt.gca().set_aspect("equal", "datalim") + plt.title("UMAP projection (step %d)" % step) + if not self.disabled: + self.projection_win = self.vis.matplot(plt, win=self.projection_win) + if out_fpath is not None: + plt.savefig(out_fpath) + plt.clf() + + def save(self): + if not self.disabled: + self.vis.save([self.env_name]) + class AbstractTrainer(object): r"""Trainer Class is used to manage the training and evaluation processes of recommender system models. @@ -446,6 +760,133 @@ def evaluate(self, load_best_model=True, model_file=None): eval_result = self.evaluator.evaluate(datadict) self.logger.info(eval_result) +class diffvcTrainer(Trainer): + def __init__(self, config, model): + super(diffvcTrainer, self).__init__(config, model) + self.optimizer = config["optimizer"] + self.train_loader = config["train_loader"] + self.epochs = config["epochs"] + self.batch_size = config["batch_size"] + self.learning_rate = config["learning_rate"] + self.save_every = config["save_every"] + self.log_dir = config["log_dir"] + torch.manual_seed(config["random_seed"]) + np.random.seed(config["random_seed"]) + n_mels = params.n_mels + sampling_rate = params.sampling_rate + n_fft = params.n_fft + hop_size = params.hop_size + + random_seed = params.seed + test_size = params.test_size + + #data_dir = r'C:\\Users\\liberty\Desktop\diffvc-yuanma\data\\' + #data_dir = r'..\dataset\diffvc_data\\' + data_dir=params.data_dir + val_file=params.val_file + exc_file=params.exc_file + #val_file = "../../dataset/diffvc_data/filelists/valid.txt" + #val_file = r'C:\Users\liberty\Desktop\diffvc-yuanma\\filelists\\valid.txt' + #exc_file = "../../dataset/diffvc_data/filelists//exceptions_libritts.txt" + #exc_file = r'C:\\Users\\liberty\Desktop\diffvc-yuanma\\filelistsexceptions_libritts.txt' + + log_dir = 'logs_dec' + enc_dir = 'logs_enc' + epochs = 10 + batch_size = 32 + learning_rate = 1e-5 + save_every = 1 + + + + + torch.manual_seed(random_seed) + np.random.seed(random_seed) + + os.makedirs(log_dir, exist_ok=True) + + print('Initializing data loaders...') + train_set = VCDecDataset(data_dir, val_file, exc_file) + collate_fn = VCDecBatchCollate() + train_loader = DataLoader(train_set, batch_size=batch_size, + collate_fn=collate_fn, num_workers=4, drop_last=True) + + print('Initializing and loading models...') + #fgl = FastGL(n_mels, sampling_rate, n_fft, hop_size).cuda() + fgl = FastGL(n_mels, sampling_rate, n_fft, hop_size) + + #.cuda() + model.load_encoder(os.path.join(enc_dir, 'enc.pt')) + + print('Encoder:') + print(model.encoder) + # print('Number of parameters = %.2fm\n' % (model.encoder.nparams/1e6)) + print('Decoder:') + print(model.decoder) + # print('Number of parameters = %.2fm\n' % (model.decoder.nparams/1e6)) + + print('Initializing optimizers...') + optimizer = torch.optim.Adam(params=model.decoder.parameters(), lr=learning_rate) + + print('Start training.') + torch.backends.cudnn.benchmark = True + iteration = 0 + for epoch in range(1, epochs + 1): + print(f'Epoch: {epoch} [iteration: {iteration}]') + model.train() + losses = [] + for batch in tqdm(train_loader, total=len(train_set)//batch_size): + mel, mel_ref = batch['mel1'].cuda(), batch['mel2'] + #.cuda() + c, mel_lengths = batch['c'].cuda(), batch['mel_lengths'] + #.cuda() + model.zero_grad() + loss = model.compute_loss(mel, mel_lengths, mel_ref, c) + loss.backward() + torch.nn.utils.clip_grad_norm_(model.decoder.parameters(), max_norm=1) + optimizer.step() + losses.append(loss.item()) + iteration += 1 + + losses = np.asarray(losses) + msg = 'Epoch %d: loss = %.4f\n' % (epoch, np.mean(losses)) + print(msg) + with open(f'{log_dir}/train_dec.log', 'a') as f: + f.write(msg) + losses = [] + + if epoch % save_every > 0: + continue + + model.eval() + print('Inference...\n') + with torch.no_grad(): + mels = train_set.get_valid_dataset() + for i, (mel, c) in enumerate(mels): + if i >= test_size: + break + mel = mel.unsqueeze(0).float() + #.cuda() + c = c.unsqueeze(0).float() + #.cuda() + mel_lengths = torch.LongTensor([mel.shape[-1]]) + #.cuda() + mel_avg, mel_rec = model(mel, mel_lengths, mel, mel_lengths, c, + n_timesteps=100) + if epoch == save_every: + save_plot(mel.squeeze().cpu(), f'{log_dir}/original_{i}.png') + audio = fgl(mel) + save_audio(f'{log_dir}/original_{i}.wav', sampling_rate, audio) + save_plot(mel_avg.squeeze().cpu(), f'{log_dir}/average_{i}.png') + audio = fgl(mel_avg) + save_audio(f'{log_dir}/average_{i}.wav', sampling_rate, audio) + save_plot(mel_rec.squeeze().cpu(), f'{log_dir}/reconstructed_{i}.png') + audio = fgl(mel_rec) + save_audio(f'{log_dir}/reconstructed_{i}.wav', sampling_rate, audio) + + print('Saving model...\n') + ckpt = model.state_dict() + torch.save(ckpt, f=f"{log_dir}/vc_{epoch}.pt") class Wav2LipTrainer(Trainer): diff --git a/talkingface/utils/.DS_Store b/talkingface/utils/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..8a9ddfeda015cb4d79ea0975499d739ec81c864e GIT binary patch literal 8196 zcmeHM%Wl&^6ur|Ha8xKk0+9tsme@w6X`s9|A%q26f?&bJ#ED6*I-V$Y8dXuHY=M7Z z%>s!f`~bhezpy}X?##4GowPuSDjsvAnLDp@$3Ayv?CTJbsPWP=ee(YZEhK`4E&c2 z@cG~(v91_B(pnuI$W#aby?|yVC?gI~7}w~E(Ic%D6gGAAAPiKP5JPA<`duClT`_v3 zwT6?>a1v%_VImZvW(Uqy!%0-Mwz*}%GBC*i$L@mB2scs`J ze^Y`Iw>79oZR%5p0$@4vk6~3W{0i0|BwfSS2frJINxHJ~SS-hGC)GypG1neQ8TmWqhW{`O244Q`9T}&MFb;YO5VisgdGaWX zTXIm9{kWAVY+ZLaxm?c6pC1mduC5f_Yo+a?JG{ZgE9JHA?VNM&(&bzCy^W|7%a^zv zh%hH8oznH){1r+E(z99b#gUBLYy>F>cS|gQHYn^(BY%!ZjxQMpi-oC;i3KhS0dH$u2?*&z5M1Dd#s}Zv*uhS7Mx-aU?IEnb;k3^2Z~0a$z`JUAta%%jMMr zyv$yN?CWLT5QpY9YM1Iw*%|-1cW7H3S*w{1B%ja0zMS70J!sA9-D+n?bjzg<(9D4T;LzG<%WlY8Bk=AH|9Q}uYpL~b3&wu3~ JoBf*%d6$d1QDo-`@#`a7cKi76lL}D<_4~e=&6hIl-W>sQlDg{&Je-d!((Xgml1eG`;=mp zOS9J3W52D%hLlr*y`i8JSV@}H>eI!IKWAjKYV6+Udp0g*RrmWpL~E~oVgF(f1fAeb z@?Ou9I<2SGAf3G8(sQlL?95NI7qXa*x>ufPRi{!F6O$nG0wJ$oN|o!`Ku=X(ncT!S z1YsDCy4U9Od$;=s(SyUqK{S8J?%g~07mF~sdh^z!i(lH%jf`;@A*h7B4M(@wHjDV(QBF^cd z#qYA*1BzaDjRE&J4LZ9vFh3s8fM?*Z8Q}dPf-=Sq8;fS^fH7A9U>ns&;Pb}<=R^); zhmA$FK!i;N+En4L7{aEbK5}uf!^WabC*dw1!u2fN4MmvV@%+eylZY*P>lyG2tTM1? zi(NkdkIujUuO|7DXTUSCQw)gqFc}UoBzLxM435v*2>KSv!f}m7lLF(8W8?5qd>v{8 ZZ4o=b*kNN4H4y(HU}*5hGw@Ft_zlu3o6rCN literal 0 HcmV?d00001 diff --git a/talkingface/utils/face_detection/detection/.DS_Store b/talkingface/utils/face_detection/detection/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..01c190d663e08b017366fcd6bd33dabedc7d4a80 GIT binary patch literal 6148 zcmeHK%}N6?5T3Nv?oxyv6uk%wUZvK5#LHUs;MEm9sMM}ob#dL4?$#ouuvdK*eWP8K;dfZnjlI7&I~D>?Di>*{#V=93(2&(+!qw+x7BnueZKZU34~=`-@I* zi_Epvjeg&@W)_yV59_zlT`V7o0o?Vt?UhU!9KaJAX3FV#^+&~0cPOuGC=2pgG%Tc%rvT_0}J&8KrEnJ3EK3tNI8x{*I=d*TTp~f zMbxRnJTZh$N55m@T!WcLoesi0K7<)rm=}srqhtS$3J2k8uQ) S(}*4v{t-|#aKQ}xCHf1f9P(@qe& zs5fKG3~AqX+Ic&DZMGDEYtDMdKpjAhMX<8LYK2L^l#*4{vrcrLV}b_m@N_<$bW5Yn z@fQ`4wW}e+3_T36uzsE4q{BXL4EIKCynFLg*hhSn8Aea0^fYegzEQ@|831xgC= z%oeL{2DI7~Fa=D3xdQTiNLU0@#Uh~pbg=SQ0AiJHZ+w=!gv2C@sbUe3Jv0|Wi6PYK z6~l#a`V$?ODi#4lIGkQSoW9xV4aNDllRxpp;nILsn*yf5vI1+NS(o$w?Cbmga*(x5 z0aM^#Dd4KDi&m3U(zCTRIXP<+mMa!98CL|Hhw#B4#T+X~@g9pe`V)B|riw*CdT91X Nz{_BjDe$Wbd;n;fgk}H$ literal 0 HcmV?d00001 diff --git a/talkingface/utils/voice_conversion_talkingface/DiffVC_hifi-gan_xutils.py b/talkingface/utils/voice_conversion_talkingface/DiffVC_hifi-gan_xutils.py new file mode 100644 index 00000000..e2d88d5c --- /dev/null +++ b/talkingface/utils/voice_conversion_talkingface/DiffVC_hifi-gan_xutils.py @@ -0,0 +1,60 @@ +""" from https://github.com/jik876/hifi-gan """ + +import glob +import os +import matplotlib +import torch +from torch.nn.utils import weight_norm +matplotlib.use("Agg") +import matplotlib.pylab as plt + + +def plot_spectrogram(spectrogram): + fig, ax = plt.subplots(figsize=(10, 2)) + im = ax.imshow(spectrogram, aspect="auto", origin="lower", + interpolation='none') + plt.colorbar(im, ax=ax) + + fig.canvas.draw() + plt.close() + + return fig + + +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 apply_weight_norm(m): + classname = m.__class__.__name__ + if classname.find("Conv") != -1: + weight_norm(m) + + +def get_padding(kernel_size, dilation=1): + return int((kernel_size*dilation - dilation)/2) + + +def load_checkpoint(filepath, device): + assert os.path.isfile(filepath) + print("Loading '{}'".format(filepath)) + checkpoint_dict = torch.load(filepath, map_location=device) + print("Complete.") + return checkpoint_dict + + +def save_checkpoint(filepath, obj): + print("Saving checkpoint to {}".format(filepath)) + torch.save(obj, filepath) + print("Complete.") + + +def scan_checkpoint(cp_dir, prefix): + pattern = os.path.join(cp_dir, prefix + '????????') + cp_list = glob.glob(pattern) + if len(cp_list) == 0: + return None + return sorted(cp_list)[-1] + diff --git a/talkingface/utils/voice_conversion_talkingface/DiffVC_model_utils.py b/talkingface/utils/voice_conversion_talkingface/DiffVC_model_utils.py new file mode 100644 index 00000000..79be82b5 --- /dev/null +++ b/talkingface/utils/voice_conversion_talkingface/DiffVC_model_utils.py @@ -0,0 +1,110 @@ +# Copyright (C) 2022. Huawei Technologies Co., Ltd. All rights reserved. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the MIT License. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MIT License for more details. + +import torch +import torchaudio +import numpy as np +from librosa.filters import mel as librosa_mel_fn + +from model.base import BaseModule + + +def mse_loss(x, y, mask, n_feats): + loss = torch.sum(((x - y)**2) * mask) + return loss / (torch.sum(mask) * n_feats) + + +def sequence_mask(length, max_length=None): + if max_length is None: + max_length = length.max() + x = torch.arange(int(max_length), dtype=length.dtype, device=length.device) + return x.unsqueeze(0) < length.unsqueeze(1) + + +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 fix_len_compatibility(length, num_downsamplings_in_unet=2): + while True: + if length % (2**num_downsamplings_in_unet) == 0: + return length + length += 1 + + +class PseudoInversion(BaseModule): + def __init__(self, n_mels, sampling_rate, n_fft): + super(PseudoInversion, self).__init__() + self.n_mels = n_mels + self.sampling_rate = sampling_rate + self.n_fft = n_fft + mel_basis = librosa_mel_fn(sampling_rate, n_fft, n_mels, 0, 8000) + mel_basis_inverse = np.linalg.pinv(mel_basis) + mel_basis_inverse = torch.from_numpy(mel_basis_inverse).float() + self.register_buffer("mel_basis_inverse", mel_basis_inverse) + + def forward(self, log_mel_spectrogram): + mel_spectrogram = torch.exp(log_mel_spectrogram) + stftm = torch.matmul(self.mel_basis_inverse, mel_spectrogram) + return stftm + + +class InitialReconstruction(BaseModule): + def __init__(self, n_fft, hop_size): + super(InitialReconstruction, self).__init__() + self.n_fft = n_fft + self.hop_size = hop_size + window = torch.hann_window(n_fft).float() + self.register_buffer("window", window) + + def forward(self, stftm): + real_part = torch.ones_like(stftm, device=stftm.device) + imag_part = torch.zeros_like(stftm, device=stftm.device) + stft = torch.stack([real_part, imag_part], -1)*stftm.unsqueeze(-1) + istft = torchaudio.functional.istft(stft, n_fft=self.n_fft, + hop_length=self.hop_size, win_length=self.n_fft, + window=self.window, center=True) + return istft.unsqueeze(1) + + +# Fast Griffin-Lim algorithm as a PyTorch module +class FastGL(BaseModule): + def __init__(self, n_mels, sampling_rate, n_fft, hop_size, momentum=0.99): + super(FastGL, self).__init__() + self.n_mels = n_mels + self.sampling_rate = sampling_rate + self.n_fft = n_fft + self.hop_size = hop_size + self.momentum = momentum + self.pi = PseudoInversion(n_mels, sampling_rate, n_fft) + self.ir = InitialReconstruction(n_fft, hop_size) + window = torch.hann_window(n_fft).float() + self.register_buffer("window", window) + + @torch.no_grad() + def forward(self, s, n_iters=32): + c = self.pi(s) + x = self.ir(c) + x = x.squeeze(1) + c = c.unsqueeze(-1) + prev_angles = torch.zeros_like(c, device=c.device) + for _ in range(n_iters): + s = torch.stft(x, n_fft=self.n_fft, hop_length=self.hop_size, + win_length=self.n_fft, window=self.window, + center=True) + real_part, imag_part = s.unbind(-1) + stftm = torch.sqrt(torch.clamp(real_part**2 + imag_part**2, min=1e-8)) + angles = s / stftm.unsqueeze(-1) + s = c * (angles + self.momentum * (angles - prev_angles)) + x = torchaudio.functional.istft(s, n_fft=self.n_fft, hop_length=self.hop_size, + win_length=self.n_fft, window=self.window, + center=True) + prev_angles = angles + return x.unsqueeze(1) diff --git a/talkingface/utils/voice_conversion_talkingface/DiffVC_speaker_encoder_utils.py b/talkingface/utils/voice_conversion_talkingface/DiffVC_speaker_encoder_utils.py new file mode 100644 index 00000000..9eacd8e2 --- /dev/null +++ b/talkingface/utils/voice_conversion_talkingface/DiffVC_speaker_encoder_utils.py @@ -0,0 +1,303 @@ +from pathlib import Path +import numpy as np +import argparse + +import numpy as np +import math +from scipy.special import expn +from collections import namedtuple + +from time import perf_counter as timer +from collections import OrderedDict +import numpy as np + +_type_priorities = [ # In decreasing order + Path, + str, + int, + float, + bool, +] + +class argutils: + + def _priority(o): + p = next((i for i, t in enumerate(_type_priorities) if type(o) is t), None) + if p is not None: + return p + p = next((i for i, t in enumerate(_type_priorities) if isinstance(o, t)), None) + if p is not None: + return p + return len(_type_priorities) + + def print_args(args: argparse.Namespace, parser=None): + args = vars(args) + if parser is None: + priorities = list(map(argutils._priority, args.values())) + else: + all_params = [a.dest for g in parser._action_groups for a in g._group_actions] + priority = lambda p: all_params.index(p) if p in all_params else len(all_params) + priorities = list(map(priority, args.keys())) + + pad = max(map(len, args.keys())) + 3 + indices = np.lexsort((list(args.keys()), priorities)) + items = list(args.items()) + + print("Arguments:") + for i in indices: + param, value = items[i] + print(" {0}:{1}{2}".format(param, ' ' * (pad - len(param)), value)) + print("") + +class logmmse: + NoiseProfile = namedtuple("NoiseProfile", "sampling_rate window_size len1 len2 win n_fft noise_mu2") + def profile_noise(noise, sampling_rate, window_size=0): + """ + Creates a profile of the noise in a given waveform. + + :param noise: a waveform containing noise ONLY, as a numpy array of floats or ints. + :param sampling_rate: the sampling rate of the audio + :param window_size: the size of the window the logmmse algorithm operates on. A default value + will be picked if left as 0. + :return: a NoiseProfile object + """ + noise, dtype = logmmse.to_float(noise) + noise += np.finfo(np.float64).eps + + if window_size == 0: + window_size = int(math.floor(0.02 * sampling_rate)) + + if window_size % 2 == 1: + window_size = window_size + 1 + + perc = 50 + len1 = int(math.floor(window_size * perc / 100)) + len2 = int(window_size - len1) + + win = np.hanning(window_size) + win = win * len2 / np.sum(win) + n_fft = 2 * window_size + + noise_mean = np.zeros(n_fft) + n_frames = len(noise) // window_size + for j in range(0, window_size * n_frames, window_size): + noise_mean += np.absolute(np.fft.fft(win * noise[j:j + window_size], n_fft, axis=0)) + noise_mu2 = (noise_mean / n_frames) ** 2 + + return logmmse.NoiseProfile(sampling_rate, window_size, len1, len2, win, n_fft, noise_mu2) + + def denoise(wav, noise_profile: NoiseProfile, eta=0.15): + """ + Cleans the noise from a speech waveform given a noise profile. The waveform must have the + same sampling rate as the one used to create the noise profile. + + :param wav: a speech waveform as a numpy array of floats or ints. + :param noise_profile: a NoiseProfile object that was created from a similar (or a segment of + the same) waveform. + :param eta: voice threshold for noise update. While the voice activation detection value is + below this threshold, the noise profile will be continuously updated throughout the audio. + Set to 0 to disable updating the noise profile. + :return: the clean wav as a numpy array of floats or ints of the same length. + """ + wav, dtype = logmmse.to_float(wav) + wav += np.finfo(np.float64).eps + p = noise_profile + + nframes = int(math.floor(len(wav) / p.len2) - math.floor(p.window_size / p.len2)) + x_final = np.zeros(nframes * p.len2) + + aa = 0.98 + mu = 0.98 + ksi_min = 10 ** (-25 / 10) + + x_old = np.zeros(p.len1) + xk_prev = np.zeros(p.len1) + noise_mu2 = p.noise_mu2 + for k in range(0, nframes * p.len2, p.len2): + insign = p.win * wav[k:k + p.window_size] + + spec = np.fft.fft(insign, p.n_fft, axis=0) + sig = np.absolute(spec) + sig2 = sig ** 2 + + gammak = np.minimum(sig2 / noise_mu2, 40) + + if xk_prev.all() == 0: + ksi = aa + (1 - aa) * np.maximum(gammak - 1, 0) + else: + ksi = aa * xk_prev / noise_mu2 + (1 - aa) * np.maximum(gammak - 1, 0) + ksi = np.maximum(ksi_min, ksi) + + log_sigma_k = gammak * ksi / (1 + ksi) - np.log(1 + ksi) + vad_decision = np.sum(log_sigma_k) / p.window_size + if vad_decision < eta: + noise_mu2 = mu * noise_mu2 + (1 - mu) * sig2 + + a = ksi / (1 + ksi) + vk = a * gammak + ei_vk = 0.5 * expn(1, np.maximum(vk, 1e-8)) + hw = a * np.exp(ei_vk) + sig = sig * hw + xk_prev = sig ** 2 + xi_w = np.fft.ifft(hw * spec, p.n_fft, axis=0) + xi_w = np.real(xi_w) + + x_final[k:k + p.len2] = x_old + xi_w[0:p.len1] + x_old = xi_w[p.len1:p.window_size] + + output = logmmse.from_float(x_final, dtype) + output = np.pad(output, (0, len(wav) - len(output)), mode="constant") + return output + + ## Alternative VAD algorithm to webrctvad. It has the advantage of not requiring to install that + ## darn package and it also works for any sampling rate. Maybe I'll eventually use it instead of + ## webrctvad + # def vad(wav, sampling_rate, eta=0.15, window_size=0): + # """ + # TODO: fix doc + # Creates a profile of the noise in a given waveform. + # + # :param wav: a waveform containing noise ONLY, as a numpy array of floats or ints. + # :param sampling_rate: the sampling rate of the audio + # :param window_size: the size of the window the logmmse algorithm operates on. A default value + # will be picked if left as 0. + # :param eta: voice threshold for noise update. While the voice activation detection value is + # below this threshold, the noise profile will be continuously updated throughout the audio. + # Set to 0 to disable updating the noise profile. + # """ + # wav, dtype = to_float(wav) + # wav += np.finfo(np.float64).eps + # + # if window_size == 0: + # window_size = int(math.floor(0.02 * sampling_rate)) + # + # if window_size % 2 == 1: + # window_size = window_size + 1 + # + # perc = 50 + # len1 = int(math.floor(window_size * perc / 100)) + # len2 = int(window_size - len1) + # + # win = np.hanning(window_size) + # win = win * len2 / np.sum(win) + # n_fft = 2 * window_size + # + # wav_mean = np.zeros(n_fft) + # n_frames = len(wav) // window_size + # for j in range(0, window_size * n_frames, window_size): + # wav_mean += np.absolute(np.fft.fft(win * wav[j:j + window_size], n_fft, axis=0)) + # noise_mu2 = (wav_mean / n_frames) ** 2 + # + # wav, dtype = to_float(wav) + # wav += np.finfo(np.float64).eps + # + # nframes = int(math.floor(len(wav) / len2) - math.floor(window_size / len2)) + # vad = np.zeros(nframes * len2, dtype=np.bool) + # + # aa = 0.98 + # mu = 0.98 + # ksi_min = 10 ** (-25 / 10) + # + # xk_prev = np.zeros(len1) + # noise_mu2 = noise_mu2 + # for k in range(0, nframes * len2, len2): + # insign = win * wav[k:k + window_size] + # + # spec = np.fft.fft(insign, n_fft, axis=0) + # sig = np.absolute(spec) + # sig2 = sig ** 2 + # + # gammak = np.minimum(sig2 / noise_mu2, 40) + # + # if xk_prev.all() == 0: + # ksi = aa + (1 - aa) * np.maximum(gammak - 1, 0) + # else: + # ksi = aa * xk_prev / noise_mu2 + (1 - aa) * np.maximum(gammak - 1, 0) + # ksi = np.maximum(ksi_min, ksi) + # + # log_sigma_k = gammak * ksi / (1 + ksi) - np.log(1 + ksi) + # vad_decision = np.sum(log_sigma_k) / window_size + # if vad_decision < eta: + # noise_mu2 = mu * noise_mu2 + (1 - mu) * sig2 + # print(vad_decision) + # + # a = ksi / (1 + ksi) + # vk = a * gammak + # ei_vk = 0.5 * expn(1, np.maximum(vk, 1e-8)) + # hw = a * np.exp(ei_vk) + # sig = sig * hw + # xk_prev = sig ** 2 + # + # vad[k:k + len2] = vad_decision >= eta + # + # vad = np.pad(vad, (0, len(wav) - len(vad)), mode="constant") + # return vad + + def to_float(_input): + if _input.dtype == np.float64: + return _input, _input.dtype + elif _input.dtype == np.float32: + return _input.astype(np.float64), _input.dtype + elif _input.dtype == np.uint8: + return (_input - 128) / 128., _input.dtype + elif _input.dtype == np.int16: + return _input / 32768., _input.dtype + elif _input.dtype == np.int32: + return _input / 2147483648., _input.dtype + raise ValueError('Unsupported wave file format') + + def from_float(_input, dtype): + if dtype == np.float64: + return _input, np.float64 + elif dtype == np.float32: + return _input.astype(np.float32) + elif dtype == np.uint8: + return ((_input * 128) + 128).astype(np.uint8) + elif dtype == np.int16: + return (_input * 32768).astype(np.int16) + elif dtype == np.int32: + print(_input) + return (_input * 2147483648).astype(np.int32) + raise ValueError('Unsupported wave file format') + + +class Profiler: + def __init__(self, summarize_every=5, disabled=False): + self.last_tick = timer() + self.logs = OrderedDict() + self.summarize_every = summarize_every + self.disabled = disabled + + def tick(self, name): + if self.disabled: + return + + # Log the time needed to execute that function + if not name in self.logs: + self.logs[name] = [] + if len(self.logs[name]) >= self.summarize_every: + self.summarize() + self.purge_logs() + self.logs[name].append(timer() - self.last_tick) + + self.reset_timer() + + def purge_logs(self): + for name in self.logs: + self.logs[name].clear() + + def reset_timer(self): + self.last_tick = timer() + + def summarize(self): + n = max(map(len, self.logs.values())) + assert n == self.summarize_every + print("\nAverage execution time over %d steps:" % n) + + name_msgs = ["%s (%d/%d):" % (name, len(deltas), n) for name, deltas in self.logs.items()] + pad = max(map(len, name_msgs)) + for name_msg, deltas in zip(name_msgs, self.logs.values()): + print(" %s mean: %4.0fms std: %4.0fms" % + (name_msg.ljust(pad), np.mean(deltas) * 1000, np.std(deltas) * 1000)) + print("", flush=True) + diff --git a/talkingface/utils/voice_conversion_talkingface/DiffVC_utils.py b/talkingface/utils/voice_conversion_talkingface/DiffVC_utils.py new file mode 100644 index 00000000..45e34eeb --- /dev/null +++ b/talkingface/utils/voice_conversion_talkingface/DiffVC_utils.py @@ -0,0 +1,27 @@ +# Copyright (C) 2022. Huawei Technologies Co., Ltd. All rights reserved. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the MIT License. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MIT License for more details. + +import numpy as np +import matplotlib.pyplot as plt +from scipy.io import wavfile + + +def save_plot(tensor, savepath): + plt.style.use('default') + fig, ax = plt.subplots(figsize=(12, 3)) + im = ax.imshow(tensor, aspect="auto", origin="lower", interpolation='none') + plt.colorbar(im, ax=ax) + plt.tight_layout() + fig.canvas.draw() + plt.savefig(savepath) + plt.close() + + +def save_audio(file_path, sampling_rate, audio): + audio = np.clip(audio.detach().cpu().squeeze().numpy(), -0.999, 0.999) + wavfile.write(file_path, sampling_rate, (audio * 32767).astype("int16")) diff --git a/talkingface/utils/voice_conversion_talkingface/audio.py b/talkingface/utils/voice_conversion_talkingface/audio.py new file mode 100644 index 00000000..b8b55311 --- /dev/null +++ b/talkingface/utils/voice_conversion_talkingface/audio.py @@ -0,0 +1,157 @@ +""" from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ + +from scipy.ndimage.morphology import binary_dilation +from encoder.params_data import * +from pathlib import Path +from typing import Optional, Union +import numpy as np +import webrtcvad +import librosa +import struct + +import torch +from torchaudio.transforms import Resample +from librosa.filters import mel as librosa_mel_fn + + +int16_max = (2 ** 15) - 1 + + +def preprocess_wav(fpath_or_wav: Union[str, Path, np.ndarray], + source_sr: Optional[int] = None): + """ + Applies the preprocessing operations used in training the Speaker Encoder to a waveform + either on disk or in memory. The waveform will be resampled to match the data hyperparameters. + + :param fpath_or_wav: either a filepath to an audio file (many extensions are supported, not + just .wav), either the waveform as a numpy array of floats. + :param source_sr: if passing an audio waveform, the sampling rate of the waveform before + preprocessing. After preprocessing, the waveform's sampling rate will match the data + hyperparameters. If passing a filepath, the sampling rate will be automatically detected and + this argument will be ignored. + """ + # Load the wav from disk if needed + if isinstance(fpath_or_wav, str) or isinstance(fpath_or_wav, Path): + wav, source_sr = librosa.load(fpath_or_wav, sr=None) + else: + wav = fpath_or_wav + + # Resample the wav if needed + if source_sr is not None and source_sr != sampling_rate: + wav = librosa.resample(wav, source_sr, sampling_rate) + + # Apply the preprocessing: normalize volume and shorten long silences + wav = normalize_volume(wav, audio_norm_target_dBFS, increase_only=True) + wav = trim_long_silences(wav) + + return wav + + +def preprocess_wav_batch(wavs, source_sr=22050): + # This torch version is designed to cope with a batch of same lengths wavs + if sampling_rate != source_sr: + resample = Resample(source_sr, sampling_rate) + wavs = resample(wavs) + wavs_preprocessed = normalize_volume_batch(wavs, audio_norm_target_dBFS, + increase_only=True) + # Trimming silence is not implemented in this version yet! + return wavs_preprocessed + + +def wav_to_mel_spectrogram(wav): + """ + Derives a mel spectrogram ready to be used by the encoder from a preprocessed audio waveform. + Note: this not a log-mel spectrogram. + """ + frames = librosa.feature.melspectrogram( + wav, + sampling_rate, + n_fft=int(sampling_rate * mel_window_length / 1000), + hop_length=int(sampling_rate * mel_window_step / 1000), + n_mels=mel_n_channels + ) + return frames.astype(np.float32).T + + +def wav_to_mel_spectrogram_batch(wavs): + # This torch version is designed to cope with a batch of same lengths wavs + n_fft = int(sampling_rate * mel_window_length / 1000) + hop_length = int(sampling_rate * mel_window_step / 1000) + win_length = int(sampling_rate * mel_window_length / 1000) + window = torch.hann_window(n_fft).to(wavs) + mel_basis = torch.from_numpy(librosa_mel_fn(sampling_rate, n_fft, + mel_n_channels)).to(wavs) + s = torch.stft(wavs, n_fft=n_fft, hop_length=hop_length, + win_length=win_length, window=window, center=True) + real_part, imag_part = s.unbind(-1) + stftm = real_part**2 + imag_part**2 + mels = torch.matmul(mel_basis, stftm) + return torch.transpose(mels, 1, 2) + + +def normalize_volume(wav, target_dBFS, increase_only=False, decrease_only=False): + if increase_only and decrease_only: + raise ValueError("Both increase only and decrease only are set") + dBFS_change = target_dBFS - 10 * np.log10(np.mean(wav ** 2)) + if (dBFS_change < 0 and increase_only) or (dBFS_change > 0 and decrease_only): + return wav + return wav * (10 ** (dBFS_change / 20)) + + +def normalize_volume_batch(wavs, target_dBFS, increase_only=False, decrease_only=False): + # This torch version is designed to cope with a batch of same lengths wavs + if increase_only and decrease_only: + raise ValueError("Both increase only and decrease only are set") + dBFS_change = target_dBFS - 10 * torch.log10(torch.mean(wavs ** 2, axis=-1)) + scales = torch.ones(wavs.shape[0], device=wavs.device, dtype=wavs.dtype) + if increase_only: + mask = (dBFS_change > 0).to(scales) + elif decrease_only: + mask = (dBFS_change < 0).to(scales) + else: + mask = torch.zeros_like(scales) + scales = scales + mask * (10 ** (dBFS_change / 20) - 1.0) + return wavs * scales.unsqueeze(-1) + + +def trim_long_silences(wav): + """ + Ensures that segments without voice in the waveform remain no longer than a + threshold determined by the VAD parameters in params.py. + + :param wav: the raw waveform as a numpy array of floats + :return: the same waveform with silences trimmed away (length <= original wav length) + """ + # Compute the voice detection window size + samples_per_window = (vad_window_length * sampling_rate) // 1000 + + # Trim the end of the audio to have a multiple of the window size + wav = wav[:len(wav) - (len(wav) % samples_per_window)] + + # Convert the float waveform to 16-bit mono PCM + pcm_wave = struct.pack("%dh" % len(wav), *(np.round(wav * int16_max)).astype(np.int16)) + + # Perform voice activation detection + voice_flags = [] + vad = webrtcvad.Vad(mode=3) + for window_start in range(0, len(wav), samples_per_window): + window_end = window_start + samples_per_window + voice_flags.append(vad.is_speech(pcm_wave[window_start * 2:window_end * 2], + sample_rate=sampling_rate)) + voice_flags = np.array(voice_flags) + + # Smooth the voice detection with a moving average + def moving_average(array, width): + array_padded = np.concatenate((np.zeros((width - 1) // 2), array, np.zeros(width // 2))) + ret = np.cumsum(array_padded, dtype=float) + ret[width:] = ret[width:] - ret[:-width] + return ret[width - 1:] / width + + audio_mask = moving_average(voice_flags, vad_moving_average_width) + audio_mask = np.round(audio_mask).astype(np.bool) + + # Dilate the voiced regions + audio_mask = binary_dilation(audio_mask, np.ones(vad_max_silence_length + 1)) + audio_mask = np.repeat(audio_mask, samples_per_window) + + return wav[audio_mask == True] diff --git a/talkingface/utils/voice_conversion_talkingface/inference.py b/talkingface/utils/voice_conversion_talkingface/inference.py new file mode 100644 index 00000000..0fd2c8f1 --- /dev/null +++ b/talkingface/utils/voice_conversion_talkingface/inference.py @@ -0,0 +1,209 @@ +""" from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ + +from encoder.params_data import * +from encoder.model import SpeakerEncoder +from encoder.audio import preprocess_wav, preprocess_wav_batch +from matplotlib import cm +from encoder import audio +from pathlib import Path +import matplotlib.pyplot as plt +import numpy as np +import torch + +_model = None # type: SpeakerEncoder +_device = None # type: torch.device + + +def load_model(weights_fpath: Path, device="cpu"): + """ + Loads the model in memory. If this function is not explicitely called, it will be run on the + first call to embed_frames() with the default weights file. + + :param weights_fpath: the path to saved model weights. + :param device: either a torch device or the name of a torch device (e.g. "cpu", "cuda"). The + model will be loaded and will run on this device. Outputs will however always be on the cpu. + If None, will default to your GPU if it"s available, otherwise your CPU. + """ + # TODO: I think the slow loading of the encoder might have something to do with the device it + # was saved on. Worth investigating. + global _model, _device + if device is None: + _device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + elif isinstance(device, str): + _device = torch.device(device) + _model = SpeakerEncoder(_device, torch.device("cpu")) + checkpoint = torch.load(weights_fpath, map_location="cpu") + _model.load_state_dict(checkpoint["model_state"]) + _model.eval() + print("Loaded encoder \"%s\" trained to step %d" % (weights_fpath.name, checkpoint["step"])) + + +def is_loaded(): + return _model is not None + + +def embed_frames_batch(frames, use_torch=False): + if _model is None: + raise Exception("Model was not loaded. Call load_model() before inference.") + + if not use_torch: + frames = torch.from_numpy(frames) + frames = frames.to(_device) + + embeds = _model.forward(frames) + if not use_torch: + embeds = embeds.detach().cpu().numpy() + return embeds + + +def compute_partial_slices(n_samples, partial_utterance_n_frames=partials_n_frames, + min_pad_coverage=0.75, overlap=0.5): + """ + Computes where to split an utterance waveform and its corresponding mel spectrogram to obtain + partial utterances of each. Both the waveform and the mel + spectrogram slices are returned, so as to make each partial utterance waveform correspond to + its spectrogram. This function assumes that the mel spectrogram parameters used are those + defined in params_data.py. + + The returned ranges may be indexing further than the length of the waveform. It is + recommended that you pad the waveform with zeros up to wave_slices[-1].stop. + + :param n_samples: the number of samples in the waveform + :param partial_utterance_n_frames: the number of mel spectrogram frames in each partial + utterance + :param min_pad_coverage: when reaching the last partial utterance, it may or may not have + enough frames. If at least of are present, + then the last partial utterance will be considered, as if we padded the audio. Otherwise, + it will be discarded, as if we trimmed the audio. If there aren't enough frames for 1 partial + utterance, this parameter is ignored so that the function always returns at least 1 slice. + :param overlap: by how much the partial utterance should overlap. If set to 0, the partial + utterances are entirely disjoint. + :return: the waveform slices and mel spectrogram slices as lists of array slices. Index + respectively the waveform and the mel spectrogram with these slices to obtain the partial + utterances. + """ + assert 0 <= overlap < 1 + assert 0 < min_pad_coverage <= 1 + + samples_per_frame = int((sampling_rate * mel_window_step / 1000)) + n_frames = int(np.ceil((n_samples + 1) / samples_per_frame)) + frame_step = max(int(np.round(partial_utterance_n_frames * (1 - overlap))), 1) + + # Compute the slices + wav_slices, mel_slices = [], [] + steps = max(1, n_frames - partial_utterance_n_frames + frame_step + 1) + for i in range(0, steps, frame_step): + mel_range = np.array([i, i + partial_utterance_n_frames]) + wav_range = mel_range * samples_per_frame + mel_slices.append(slice(*mel_range)) + wav_slices.append(slice(*wav_range)) + + # Evaluate whether extra padding is warranted or not + last_wav_range = wav_slices[-1] + coverage = (n_samples - last_wav_range.start) / (last_wav_range.stop - last_wav_range.start) + if coverage < min_pad_coverage and len(mel_slices) > 1: + mel_slices = mel_slices[:-1] + wav_slices = wav_slices[:-1] + + return wav_slices, mel_slices + + +def embed_utterance(wav, using_partials=True, return_partials=False, **kwargs): + """ + Computes an embedding for a single utterance. + + # TODO: handle multiple wavs to benefit from batching on GPU + :param wav: a preprocessed (see audio.py) utterance waveform as a numpy array of float32 + :param using_partials: if True, then the utterance is split in partial utterances of + frames and the utterance embedding is computed from their + normalized average. If False, the utterance is instead computed from feeding the entire + spectogram to the network. + :param return_partials: if True, the partial embeddings will also be returned along with the + wav slices that correspond to the partial embeddings. + :param kwargs: additional arguments to compute_partial_splits() + :return: the embedding as a numpy array of float32 of shape (model_embedding_size,). If + is True, the partial utterances as a numpy array of float32 of shape + (n_partials, model_embedding_size) and the wav partials as a list of slices will also be + returned. If is simultaneously set to False, both these values will be None + instead. + """ + # Process the entire utterance if not using partials + if not using_partials: + frames = audio.wav_to_mel_spectrogram(wav) + embed = embed_frames_batch(frames[None, ...])[0] + if return_partials: + return embed, None, None + return embed + + # Compute where to split the utterance into partials and pad if necessary + wave_slices, mel_slices = compute_partial_slices(len(wav), **kwargs) + max_wave_length = wave_slices[-1].stop + if max_wave_length >= len(wav): + wav = np.pad(wav, (0, max_wave_length - len(wav)), "constant") + + # Split the utterance into partials + frames = audio.wav_to_mel_spectrogram(wav) + frames_batch = np.array([frames[s] for s in mel_slices]) + partial_embeds = embed_frames_batch(frames_batch) + + # Compute the utterance embedding from the partial embeddings + raw_embed = np.mean(partial_embeds, axis=0) + embed = raw_embed / np.linalg.norm(raw_embed, 2) + + if return_partials: + return embed, partial_embeds, wave_slices + return embed + + +def embed_utterance_batch(wavs, using_partials=True, return_partials=False, **kwargs): + # This torch version is designed to cope with a batch of same lengths wavs + if not using_partials: + print(wavs.shape) + frames = audio.wav_to_mel_spectrogram_batch(wavs) + embeds = embed_frames_batch(frames) + if return_partials: + return embeds, None, None + return embeds + + wave_slices, mel_slices = compute_partial_slices(wavs.shape[-1], **kwargs) + max_wave_length = wave_slices[-1].stop + if max_wave_length >= wavs.shape[-1]: + wavs = torch.cat([wavs, torch.ones((wavs.shape[0], max_wave_length - wavs.shape[-1]), + dtype=wavs.dtype, device=wavs.device)], 1) + + frames = audio.wav_to_mel_spectrogram_batch(wavs) + frames_batch = [] + for i in range(len(frames)): + frames_batch += [frames[i][s] for s in mel_slices] + frames_batch = torch.stack(frames_batch, 0) + partial_embeds = embed_frames_batch(frames_batch, use_torch=True) + partial_embeds = partial_embeds.view(wavs.shape[0], len(mel_slices), -1) + + raw_embeds = torch.mean(partial_embeds, axis=1, keepdims=False) + embeds = raw_embeds / torch.linalg.norm(raw_embeds, axis=-1, keepdims=True) + + if return_partials: + return embeds, partial_embeds, wave_slices + return embeds + + +def embed_speaker(wavs, **kwargs): + raise NotImplemented() + + +def plot_embedding_as_heatmap(embed, ax=None, title="", shape=None, color_range=(0, 0.30)): + if ax is None: + ax = plt.gca() + + if shape is None: + height = int(np.sqrt(len(embed))) + shape = (height, -1) + embed = embed.reshape(shape) + + cmap = cm.get_cmap() + mappable = ax.imshow(embed, cmap=cmap) + cbar = plt.colorbar(mappable, ax=ax, fraction=0.046, pad=0.04) + cbar.set_clim(*color_range) + + ax.set_xticks([]), ax.set_yticks([]) + ax.set_title(title) diff --git a/talkingface/utils/voice_conversion_talkingface/params_data.py b/talkingface/utils/voice_conversion_talkingface/params_data.py new file mode 100644 index 00000000..62d04121 --- /dev/null +++ b/talkingface/utils/voice_conversion_talkingface/params_data.py @@ -0,0 +1,30 @@ +""" from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ + +## Mel-filterbank +mel_window_length = 25 # In milliseconds +mel_window_step = 10 # In milliseconds +mel_n_channels = 40 + + +## Audio +sampling_rate = 16000 +# Number of spectrogram frames in a partial utterance +partials_n_frames = 160 # 1600 ms +# Number of spectrogram frames at inference +inference_n_frames = 80 # 800 ms + + +## Voice Activation Detection +# Window size of the VAD. Must be either 10, 20 or 30 milliseconds. +# This sets the granularity of the VAD. Should not need to be changed. +vad_window_length = 30 # In milliseconds +# Number of frames to average together when performing the moving average smoothing. +# The larger this value, the larger the VAD variations must be to not get smoothed out. +vad_moving_average_width = 8 +# Maximum number of consecutive silent frames a segment can have. +vad_max_silence_length = 6 + + +## Audio volume normalization +audio_norm_target_dBFS = -30 + diff --git a/talkingface/utils/voice_conversion_talkingface/params_model.py b/talkingface/utils/voice_conversion_talkingface/params_model.py new file mode 100644 index 00000000..9c535205 --- /dev/null +++ b/talkingface/utils/voice_conversion_talkingface/params_model.py @@ -0,0 +1,12 @@ +""" from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ + +## Model parameters +model_hidden_size = 256 +model_embedding_size = 256 +model_num_layers = 3 + + +## Training parameters +learning_rate_init = 1e-4 +speakers_per_batch = 64 +utterances_per_speaker = 10 From cb09911631bba50b0bfbc4c4dabd39141cee3d0b Mon Sep 17 00:00:00 2001 From: yaya <3181871180@qq.com> Date: Wed, 24 Jan 2024 23:03:11 +0800 Subject: [PATCH 3/9] add readme --- Diffvc-Readme.md | 252 ++ README-group part.txt | 0 diffvc_requirements.txt | 12 + encoder/__init__.py | 0 encoder/audio.py | 117 - encoder/config.py | 45 - encoder/data_objects/__init__.py | 2 - encoder/data_objects/random_cycler.py | 37 - encoder/data_objects/speaker.py | 40 - encoder/data_objects/speaker_batch.py | 13 - .../speaker_verification_dataset.py | 56 - encoder/data_objects/utterance.py | 26 - encoder/inference.py | 178 -- encoder/model.py | 135 - encoder/params_data.py | 29 - encoder/params_model.py | 11 - encoder/preprocess.py | 184 -- encoder/train.py | 125 - encoder/visualizations.py | 179 -- .../data/dataprocess/get_avg_mels.ipynb | 2450 +++++++++++++++++ talkingface/data/dataprocess/inference.ipynb | 356 +++ .../data/dataset/data_objects/__init__.py | 4 +- .../data/dataset/data_objects/speaker.py | 4 +- .../dataset/data_objects/speaker_batch.py | 2 +- .../speaker_verification_dataset.py | 8 +- talkingface/model/voice_conversion/diffvc.py | 4 - .../properties/model/params.py | 0 talkingface/quick_start/quick_start.py | 3 +- talkingface/trainer/diffvc_trainer.py | 0 talkingface/trainer/trainer.py | 1057 ++++--- 30 files changed, 3608 insertions(+), 1721 deletions(-) create mode 100644 Diffvc-Readme.md delete mode 100644 README-group part.txt create mode 100644 diffvc_requirements.txt delete mode 100644 encoder/__init__.py delete mode 100644 encoder/audio.py delete mode 100644 encoder/config.py delete mode 100644 encoder/data_objects/__init__.py delete mode 100644 encoder/data_objects/random_cycler.py delete mode 100644 encoder/data_objects/speaker.py delete mode 100644 encoder/data_objects/speaker_batch.py delete mode 100644 encoder/data_objects/speaker_verification_dataset.py delete mode 100644 encoder/data_objects/utterance.py delete mode 100644 encoder/inference.py delete mode 100644 encoder/model.py delete mode 100644 encoder/params_data.py delete mode 100644 encoder/params_model.py delete mode 100644 encoder/preprocess.py delete mode 100644 encoder/train.py delete mode 100644 encoder/visualizations.py create mode 100644 talkingface/data/dataprocess/get_avg_mels.ipynb create mode 100644 talkingface/data/dataprocess/inference.ipynb rename params.py => talkingface/properties/model/params.py (100%) delete mode 100644 talkingface/trainer/diffvc_trainer.py diff --git a/Diffvc-Readme.md b/Diffvc-Readme.md new file mode 100644 index 00000000..7fe8176c --- /dev/null +++ b/Diffvc-Readme.md @@ -0,0 +1,252 @@ +# Diffvc-Readme + +## 成员分工及工作量描述 + +吕春吉(贡献度:50%):组长,负责阅读论文源码,理清架构,进行了model主模型的嵌入、dataset数据处理部分的嵌入、trainer部分的嵌入、推理部分的嵌入、模型的训练和调整、参数的调整等工作 +蔡昕怡(贡献度:25%):负责数据集的查找、数据集的处理、模型的训练和调整、hifi_gan模块的嵌入 +唐欣欣(贡献度:10%):负责encoder训练部分的添加 +马翊程(贡献度:10%):参与数据处理部分的嵌入 +孙嘉成(贡献度:5%):负责数据集的查找 + +## 完成功能 + +可以进行diffvc模型的相关训练,可将数据集处理为所需要的形式(进行相关的特征提取和生成),在命令行输入指定命令可进行模型的训练并返回正确的训练结果。 + +## 训练截图 + +![image-20240124225137897](C:\Users\liberty\AppData\Roaming\Typora\typora-user-images\image-20240124225137897.png) + +![image-20240124225205428](C:\Users\liberty\AppData\Roaming\Typora\typora-user-images\image-20240124225205428.png) + +![image-20240124225219412](C:\Users\liberty\AppData\Roaming\Typora\typora-user-images\image-20240124225219412.png) + +![image-20240124225229981](C:\Users\liberty\AppData\Roaming\Typora\typora-user-images\image-20240124225229981.png) + + + +## 所使用的的依赖 + +参见 diffvc_requierments.txt文件 + +## 训练过程 + +1.下载我们所需要的预训练模型hifi-gan声码器。 + +取自官方的hifi-gan存储库:https://drive.google.com/file/d/10khlrM645pTbQ4rc2aNEYPba8RFDBkW-/view?usp=sharing + +放在checkpts/vocoder/下 + +2.下载在LibriTTS和VCTK上训练好的模型: + +LibriTTS:https://drive.google.com/file/d/18Xbme0CTVo58p2vOHoTQm8PBGW7oEjAy/view?usp=sharing + +VCTK:https://drive.google.com/file/d/12s9RPmwp9suleMkBCVetD8pub7wsDAy4/view?usp=sharing + +放在checkpts/vc/下 + +3.进行环境配置,参考diffvc_requirements.txt文件。 + +4.数据集获取和数据处理部分: + +数据集链接:https://www.openslr.org/60/ + +数据处理部分: + +①首先建立一个数据集文件夹data,包含“wavs”、“mels”和“embeds”三个文件夹,并将原数据集上的wav文件按照原文件夹放入wav文件中,在filelist中加入训练数据名称 + +(以下涉及到的代码块在talkingface/data/dataprocess/下 + +②运行inference文件的第一部分,配置相应的环境(注:librosa版本为0.9.2) + +| | | +| ---- | ------------------------------------------------------------ | +| | ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps1.jpg) | + +③对inference文件的第二个代码块中的get_mel函数和get_embed函数进行阅读,编写相关代码分别对原wav文件运行这两个函数。对原始数据运行get_mel函数生成mel文件,运行get_embed函数生成embed文件,分别保存在两个文件夹中。(注意,这里需要调用spk_encoder的预训练模型,下载之后引用其路径即可) + +| | | +| ---- | ------------------------------------------------------------ | +| | ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps2.jpg) | + +④在原代码中补充引用预训练模型、提取wav文件并执行两个函数,并分别保存到相应的两个文件夹中且正确命名的代码: + +| | | +| ---- | ------------------------------------------------------------ | +| | ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps3.jpg) | + +⑤运行该代码块,即可得到处理后的embed和mel文件。 + +5.创建logs_enc文件夹,并下载训练好的编码器放在该文件夹下 + +编码器下载地址:https://drive.google.com/file/d/1JdoC5hh7k6Nz_oTcumH0nXNEib-GDbSq/view?usp=sharing + +6.新建log_dec文件夹 + +7.进行训练 + +## + +## 框架整体介绍 + +### checkpoints + +主要保存的是训练和评估模型所需要的额外的预训练模型,在对应文件夹的[README](https://github.com/Academic-Hammer/talkingface-toolkit/blob/main/checkpoints/README.md)有更详细的介绍 + +### datset + +存放数据集以及数据集预处理之后的数据,详细内容见dataset里的[README](https://github.com/Academic-Hammer/talkingface-toolkit/blob/main/dataset/README.md) + +### saved + +存放训练过程中保存的模型checkpoint, 训练过程中保存模型时自动创建 + +### talkingface + +主要功能模块,包括所有核心代码 + +#### config + +根据模型和数据集名称自动生成所有模型、数据集、训练、评估等相关的配置信息 + +``` +config/ + +├── configurator.py + +``` + +#### data + +- dataprocess:该模型主要涉及到inference.ipynb和get_avg_mels.ipynb文件,用于生成数据集中的mels和embed文件夹和文件夹中的内容。 +- dataset:数据处理部分,主要为diffvc_dataset,其他文件为训练encoder部分所需的数据处理,可不关注。 + +``` +data/ + +├── dataprocess + +| ├── wav2lip_process.py + +| ├── inference.ipynb + +| ├── get_avg_mels.ipynb + +├── dataset + +| ├── wav2lip_dataset.py + +| ├── diffvc_dataset.py +``` + +#### evaluate + +主要涉及模型评估的代码 +LSE metric 需要的数据是生成的视频列表 +SSIM metric 需要的数据是生成的视频和真实的视频列表 + +#### model + +实现的模型的网络和对应的方法 + +diffvc存储在voice_conversion文件夹下的diffvc.py中,hifi-gan为训练encoder所需模块,可不关注。 + +``` +model/ + +├── audio_driven_talkingface + +| ├── wav2lip.py + +├── image_driven_talkingface + +| ├── xxxx.py + +├── nerf_based_talkingface + +| ├── xxxx.py + +├── voice_conversion + +| ├── diffvc.py + +├── abstract_talkingface.py + +``` + +#### properties + +保存默认配置文件,包括diffvc.yaml,diffvc_dataset.yaml,diffvc_encoder.yaml和diffvc_encoder_dataset.yaml,其中diffvc_encoder.yaml和diffvc_encoder_dataset.yaml为训练encoder所需要的配置文件,可不关注。 + +``` +properties/ + +├── dataset + +| ├── diffvc_dataset.yaml + +| ├── diffvc_encoder_dataset.yaml + +├── model + +| ├── diffvc.yaml + +| ├── diffvc_encoder.yaml + +├── overall.yaml + +``` + +#### quick_start + +通用的启动文件,根据传入参数自动配置数据集和模型,然后训练和评估 + +``` +quick_start/ + +├── quick_start.py + +``` + +#### trainer + +训练、评估函数的主类。 + +``` +trainer/ + +├── trainer.py + +``` + +#### utils + +公用的工具类。 + +## 使用方法 + +### 环境要求 + +参见 diffvc_requierments.txt,可运行以下代码配置。 + +``` +pip install -r diffvc_requierments.txt +``` + + + +### 训练和评估 + +```bash +python run_talkingface.py --model=diffvc -–dataset=diffvc_data +``` + + + +## 论文及源代码仓库链接: + +论文:https://arxiv.org/abs/2109.13821 + +源代码仓库:https://github.com/huawei-noah/Speech-Backbones/tree/main/DiffVC + + + diff --git a/README-group part.txt b/README-group part.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/diffvc_requirements.txt b/diffvc_requirements.txt new file mode 100644 index 00000000..6e9219d6 --- /dev/null +++ b/diffvc_requirements.txt @@ -0,0 +1,12 @@ +torchaudio==0.5.1 +torch==1.7.1 +einops==0.3.0 +librosa==0.6.0 +webrtcvad==2.0.10 +numpy==1.19.0 +scipy==1.5.1 +matplotlib==3.3.3 +tb-nightly +future +tqdm +tgt \ No newline at end of file diff --git a/encoder/__init__.py b/encoder/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/encoder/audio.py b/encoder/audio.py deleted file mode 100644 index 799aa835..00000000 --- a/encoder/audio.py +++ /dev/null @@ -1,117 +0,0 @@ -from scipy.ndimage.morphology import binary_dilation -from encoder.params_data import * -from pathlib import Path -from typing import Optional, Union -from warnings import warn -import numpy as np -import librosa -import struct - -try: - import webrtcvad -except: - warn("Unable to import 'webrtcvad'. This package enables noise removal and is recommended.") - webrtcvad=None - -int16_max = (2 ** 15) - 1 - - -def preprocess_wav(fpath_or_wav: Union[str, Path, np.ndarray], - source_sr: Optional[int] = None, - normalize: Optional[bool] = True, - trim_silence: Optional[bool] = True): - """ - Applies the preprocessing operations used in training the Speaker Encoder to a waveform - either on disk or in memory. The waveform will be resampled to match the data hyperparameters. - - :param fpath_or_wav: either a filepath to an audio file (many extensions are supported, not - just .wav), either the waveform as a numpy array of floats. - :param source_sr: if passing an audio waveform, the sampling rate of the waveform before - preprocessing. After preprocessing, the waveform's sampling rate will match the data - hyperparameters. If passing a filepath, the sampling rate will be automatically detected and - this argument will be ignored. - """ - # Load the wav from disk if needed - if isinstance(fpath_or_wav, str) or isinstance(fpath_or_wav, Path): - wav, source_sr = librosa.load(str(fpath_or_wav), sr=None) - else: - wav = fpath_or_wav - - # Resample the wav if needed - if source_sr is not None and source_sr != sampling_rate: - wav = librosa.resample(wav, source_sr, sampling_rate) - - # Apply the preprocessing: normalize volume and shorten long silences - if normalize: - wav = normalize_volume(wav, audio_norm_target_dBFS, increase_only=True) - if webrtcvad and trim_silence: - wav = trim_long_silences(wav) - - return wav - - -def wav_to_mel_spectrogram(wav): - """ - Derives a mel spectrogram ready to be used by the encoder from a preprocessed audio waveform. - Note: this not a log-mel spectrogram. - """ - frames = librosa.feature.melspectrogram( - wav, - sampling_rate, - n_fft=int(sampling_rate * mel_window_length / 1000), - hop_length=int(sampling_rate * mel_window_step / 1000), - n_mels=mel_n_channels - ) - return frames.astype(np.float32).T - - -def trim_long_silences(wav): - """ - Ensures that segments without voice in the waveform remain no longer than a - threshold determined by the VAD parameters in params.py. - - :param wav: the raw waveform as a numpy array of floats - :return: the same waveform with silences trimmed away (length <= original wav length) - """ - # Compute the voice detection window size - samples_per_window = (vad_window_length * sampling_rate) // 1000 - - # Trim the end of the audio to have a multiple of the window size - wav = wav[:len(wav) - (len(wav) % samples_per_window)] - - # Convert the float waveform to 16-bit mono PCM - pcm_wave = struct.pack("%dh" % len(wav), *(np.round(wav * int16_max)).astype(np.int16)) - - # Perform voice activation detection - voice_flags = [] - vad = webrtcvad.Vad(mode=3) - for window_start in range(0, len(wav), samples_per_window): - window_end = window_start + samples_per_window - voice_flags.append(vad.is_speech(pcm_wave[window_start * 2:window_end * 2], - sample_rate=sampling_rate)) - voice_flags = np.array(voice_flags) - - # Smooth the voice detection with a moving average - def moving_average(array, width): - array_padded = np.concatenate((np.zeros((width - 1) // 2), array, np.zeros(width // 2))) - ret = np.cumsum(array_padded, dtype=float) - ret[width:] = ret[width:] - ret[:-width] - return ret[width - 1:] / width - - audio_mask = moving_average(voice_flags, vad_moving_average_width) - audio_mask = np.round(audio_mask).astype(np.bool) - - # Dilate the voiced regions - audio_mask = binary_dilation(audio_mask, np.ones(vad_max_silence_length + 1)) - audio_mask = np.repeat(audio_mask, samples_per_window) - - return wav[audio_mask == True] - - -def normalize_volume(wav, target_dBFS, increase_only=False, decrease_only=False): - if increase_only and decrease_only: - raise ValueError("Both increase only and decrease only are set") - dBFS_change = target_dBFS - 10 * np.log10(np.mean(wav ** 2)) - if (dBFS_change < 0 and increase_only) or (dBFS_change > 0 and decrease_only): - return wav - return wav * (10 ** (dBFS_change / 20)) diff --git a/encoder/config.py b/encoder/config.py deleted file mode 100644 index 1c21312f..00000000 --- a/encoder/config.py +++ /dev/null @@ -1,45 +0,0 @@ -librispeech_datasets = { - "train": { - "clean": ["LibriSpeech/train-clean-100", "LibriSpeech/train-clean-360"], - "other": ["LibriSpeech/train-other-500"] - }, - "test": { - "clean": ["LibriSpeech/test-clean"], - "other": ["LibriSpeech/test-other"] - }, - "dev": { - "clean": ["LibriSpeech/dev-clean"], - "other": ["LibriSpeech/dev-other"] - }, -} -libritts_datasets = { - "train": { - "clean": ["LibriTTS/train-clean-100", "LibriTTS/train-clean-360"], - "other": ["LibriTTS/train-other-500"] - }, - "test": { - "clean": ["LibriTTS/test-clean"], - "other": ["LibriTTS/test-other"] - }, - "dev": { - "clean": ["LibriTTS/dev-clean"], - "other": ["LibriTTS/dev-other"] - }, -} -voxceleb_datasets = { - "voxceleb1" : { - "train": ["VoxCeleb1/wav"], - "test": ["VoxCeleb1/test_wav"] - }, - "voxceleb2" : { - "train": ["VoxCeleb2/dev/aac"], - "test": ["VoxCeleb2/test_wav"] - } -} - -other_datasets = [ - "LJSpeech-1.1", - "VCTK-Corpus/wav48", -] - -anglophone_nationalites = ["australia", "canada", "ireland", "uk", "usa"] diff --git a/encoder/data_objects/__init__.py b/encoder/data_objects/__init__.py deleted file mode 100644 index ef04ade6..00000000 --- a/encoder/data_objects/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from encoder.data_objects.speaker_verification_dataset import SpeakerVerificationDataset -from encoder.data_objects.speaker_verification_dataset import SpeakerVerificationDataLoader diff --git a/encoder/data_objects/random_cycler.py b/encoder/data_objects/random_cycler.py deleted file mode 100644 index c405db6b..00000000 --- a/encoder/data_objects/random_cycler.py +++ /dev/null @@ -1,37 +0,0 @@ -import random - -class RandomCycler: - """ - Creates an internal copy of a sequence and allows access to its items in a constrained random - order. For a source sequence of n items and one or several consecutive queries of a total - of m items, the following guarantees hold (one implies the other): - - Each item will be returned between m // n and ((m - 1) // n) + 1 times. - - Between two appearances of the same item, there may be at most 2 * (n - 1) other items. - """ - - def __init__(self, source): - if len(source) == 0: - raise Exception("Can't create RandomCycler from an empty collection") - self.all_items = list(source) - self.next_items = [] - - def sample(self, count: int): - shuffle = lambda l: random.sample(l, len(l)) - - out = [] - while count > 0: - if count >= len(self.all_items): - out.extend(shuffle(list(self.all_items))) - count -= len(self.all_items) - continue - n = min(count, len(self.next_items)) - out.extend(self.next_items[:n]) - count -= n - self.next_items = self.next_items[n:] - if len(self.next_items) == 0: - self.next_items = shuffle(list(self.all_items)) - return out - - def __next__(self): - return self.sample(1)[0] - diff --git a/encoder/data_objects/speaker.py b/encoder/data_objects/speaker.py deleted file mode 100644 index 494e882f..00000000 --- a/encoder/data_objects/speaker.py +++ /dev/null @@ -1,40 +0,0 @@ -from encoder.data_objects.random_cycler import RandomCycler -from encoder.data_objects.utterance import Utterance -from pathlib import Path - -# Contains the set of utterances of a single speaker -class Speaker: - def __init__(self, root: Path): - self.root = root - self.name = root.name - self.utterances = None - self.utterance_cycler = None - - def _load_utterances(self): - with self.root.joinpath("_sources.txt").open("r") as sources_file: - sources = [l.split(",") for l in sources_file] - sources = {frames_fname: wave_fpath for frames_fname, wave_fpath in sources} - self.utterances = [Utterance(self.root.joinpath(f), w) for f, w in sources.items()] - self.utterance_cycler = RandomCycler(self.utterances) - - def random_partial(self, count, n_frames): - """ - Samples a batch of unique partial utterances from the disk in a way that all - utterances come up at least once every two cycles and in a random order every time. - - :param count: The number of partial utterances to sample from the set of utterances from - that speaker. Utterances are guaranteed not to be repeated if is not larger than - the number of utterances available. - :param n_frames: The number of frames in the partial utterance. - :return: A list of tuples (utterance, frames, range) where utterance is an Utterance, - frames are the frames of the partial utterances and range is the range of the partial - utterance with regard to the complete utterance. - """ - if self.utterances is None: - self._load_utterances() - - utterances = self.utterance_cycler.sample(count) - - a = [(u,) + u.random_partial(n_frames) for u in utterances] - - return a diff --git a/encoder/data_objects/speaker_batch.py b/encoder/data_objects/speaker_batch.py deleted file mode 100644 index e219b738..00000000 --- a/encoder/data_objects/speaker_batch.py +++ /dev/null @@ -1,13 +0,0 @@ -import numpy as np -from typing import List -from encoder.data_objects.speaker import Speaker - - -class SpeakerBatch: - def __init__(self, speakers: List[Speaker], utterances_per_speaker: int, n_frames: int): - self.speakers = speakers - self.partials = {s: s.random_partial(utterances_per_speaker, n_frames) for s in speakers} - - # Array of shape (n_speakers * n_utterances, n_frames, mel_n), e.g. for 3 speakers with - # 4 utterances each of 160 frames of 40 mel coefficients: (12, 160, 40) - self.data = np.array([frames for s in speakers for _, frames, _ in self.partials[s]]) diff --git a/encoder/data_objects/speaker_verification_dataset.py b/encoder/data_objects/speaker_verification_dataset.py deleted file mode 100644 index 77a6e05e..00000000 --- a/encoder/data_objects/speaker_verification_dataset.py +++ /dev/null @@ -1,56 +0,0 @@ -from encoder.data_objects.random_cycler import RandomCycler -from encoder.data_objects.speaker_batch import SpeakerBatch -from encoder.data_objects.speaker import Speaker -from encoder.params_data import partials_n_frames -from torch.utils.data import Dataset, DataLoader -from pathlib import Path - -# TODO: improve with a pool of speakers for data efficiency - -class SpeakerVerificationDataset(Dataset): - def __init__(self, datasets_root: Path): - self.root = datasets_root - speaker_dirs = [f for f in self.root.glob("*") if f.is_dir()] - if len(speaker_dirs) == 0: - raise Exception("No speakers found. Make sure you are pointing to the directory " - "containing all preprocessed speaker directories.") - self.speakers = [Speaker(speaker_dir) for speaker_dir in speaker_dirs] - self.speaker_cycler = RandomCycler(self.speakers) - - def __len__(self): - return int(1e10) - - def __getitem__(self, index): - return next(self.speaker_cycler) - - def get_logs(self): - log_string = "" - for log_fpath in self.root.glob("*.txt"): - with log_fpath.open("r") as log_file: - log_string += "".join(log_file.readlines()) - return log_string - - -class SpeakerVerificationDataLoader(DataLoader): - def __init__(self, dataset, speakers_per_batch, utterances_per_speaker, sampler=None, - batch_sampler=None, num_workers=0, pin_memory=False, timeout=0, - worker_init_fn=None): - self.utterances_per_speaker = utterances_per_speaker - - super().__init__( - dataset=dataset, - batch_size=speakers_per_batch, - shuffle=False, - sampler=sampler, - batch_sampler=batch_sampler, - num_workers=num_workers, - collate_fn=self.collate, - pin_memory=pin_memory, - drop_last=False, - timeout=timeout, - worker_init_fn=worker_init_fn - ) - - def collate(self, speakers): - return SpeakerBatch(speakers, self.utterances_per_speaker, partials_n_frames) - \ No newline at end of file diff --git a/encoder/data_objects/utterance.py b/encoder/data_objects/utterance.py deleted file mode 100644 index 0768c342..00000000 --- a/encoder/data_objects/utterance.py +++ /dev/null @@ -1,26 +0,0 @@ -import numpy as np - - -class Utterance: - def __init__(self, frames_fpath, wave_fpath): - self.frames_fpath = frames_fpath - self.wave_fpath = wave_fpath - - def get_frames(self): - return np.load(self.frames_fpath) - - def random_partial(self, n_frames): - """ - Crops the frames into a partial utterance of n_frames - - :param n_frames: The number of frames of the partial utterance - :return: the partial utterance frames and a tuple indicating the start and end of the - partial utterance in the complete utterance. - """ - frames = self.get_frames() - if frames.shape[0] == n_frames: - start = 0 - else: - start = np.random.randint(0, frames.shape[0] - n_frames) - end = start + n_frames - return frames[start:end], (start, end) \ No newline at end of file diff --git a/encoder/inference.py b/encoder/inference.py deleted file mode 100644 index 43862e43..00000000 --- a/encoder/inference.py +++ /dev/null @@ -1,178 +0,0 @@ -from encoder.params_data import * -from encoder.model import SpeakerEncoder -from encoder.audio import preprocess_wav # We want to expose this function from here -from matplotlib import cm -from encoder import audio -from pathlib import Path -import numpy as np -import torch - -_model = None # type: SpeakerEncoder -_device = None # type: torch.device - - -def load_model(weights_fpath: Path, device=None): - """ - Loads the model in memory. If this function is not explicitely called, it will be run on the - first call to embed_frames() with the default weights file. - - :param weights_fpath: the path to saved model weights. - :param device: either a torch device or the name of a torch device (e.g. "cpu", "cuda"). The - model will be loaded and will run on this device. Outputs will however always be on the cpu. - If None, will default to your GPU if it"s available, otherwise your CPU. - """ - # TODO: I think the slow loading of the encoder might have something to do with the device it - # was saved on. Worth investigating. - global _model, _device - if device is None: - _device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - elif isinstance(device, str): - _device = torch.device(device) - _model = SpeakerEncoder(_device, torch.device("cpu")) - checkpoint = torch.load(weights_fpath, _device) - _model.load_state_dict(checkpoint["model_state"]) - _model.eval() - print("Loaded encoder \"%s\" trained to step %d" % (weights_fpath.name, checkpoint["step"])) - - -def is_loaded(): - return _model is not None - - -def embed_frames_batch(frames_batch): - """ - Computes embeddings for a batch of mel spectrogram. - - :param frames_batch: a batch mel of spectrogram as a numpy array of float32 of shape - (batch_size, n_frames, n_channels) - :return: the embeddings as a numpy array of float32 of shape (batch_size, model_embedding_size) - """ - if _model is None: - raise Exception("Model was not loaded. Call load_model() before inference.") - - frames = torch.from_numpy(frames_batch).to(_device) - embed = _model.forward(frames).detach().cpu().numpy() - return embed - - -def compute_partial_slices(n_samples, partial_utterance_n_frames=partials_n_frames, - min_pad_coverage=0.75, overlap=0.5): - """ - Computes where to split an utterance waveform and its corresponding mel spectrogram to obtain - partial utterances of each. Both the waveform and the mel - spectrogram slices are returned, so as to make each partial utterance waveform correspond to - its spectrogram. This function assumes that the mel spectrogram parameters used are those - defined in params_data.py. - - The returned ranges may be indexing further than the length of the waveform. It is - recommended that you pad the waveform with zeros up to wave_slices[-1].stop. - - :param n_samples: the number of samples in the waveform - :param partial_utterance_n_frames: the number of mel spectrogram frames in each partial - utterance - :param min_pad_coverage: when reaching the last partial utterance, it may or may not have - enough frames. If at least of are present, - then the last partial utterance will be considered, as if we padded the audio. Otherwise, - it will be discarded, as if we trimmed the audio. If there aren't enough frames for 1 partial - utterance, this parameter is ignored so that the function always returns at least 1 slice. - :param overlap: by how much the partial utterance should overlap. If set to 0, the partial - utterances are entirely disjoint. - :return: the waveform slices and mel spectrogram slices as lists of array slices. Index - respectively the waveform and the mel spectrogram with these slices to obtain the partial - utterances. - """ - assert 0 <= overlap < 1 - assert 0 < min_pad_coverage <= 1 - - samples_per_frame = int((sampling_rate * mel_window_step / 1000)) - n_frames = int(np.ceil((n_samples + 1) / samples_per_frame)) - frame_step = max(int(np.round(partial_utterance_n_frames * (1 - overlap))), 1) - - # Compute the slices - wav_slices, mel_slices = [], [] - steps = max(1, n_frames - partial_utterance_n_frames + frame_step + 1) - for i in range(0, steps, frame_step): - mel_range = np.array([i, i + partial_utterance_n_frames]) - wav_range = mel_range * samples_per_frame - mel_slices.append(slice(*mel_range)) - wav_slices.append(slice(*wav_range)) - - # Evaluate whether extra padding is warranted or not - last_wav_range = wav_slices[-1] - coverage = (n_samples - last_wav_range.start) / (last_wav_range.stop - last_wav_range.start) - if coverage < min_pad_coverage and len(mel_slices) > 1: - mel_slices = mel_slices[:-1] - wav_slices = wav_slices[:-1] - - return wav_slices, mel_slices - - -def embed_utterance(wav, using_partials=True, return_partials=False, **kwargs): - """ - Computes an embedding for a single utterance. - - # TODO: handle multiple wavs to benefit from batching on GPU - :param wav: a preprocessed (see audio.py) utterance waveform as a numpy array of float32 - :param using_partials: if True, then the utterance is split in partial utterances of - frames and the utterance embedding is computed from their - normalized average. If False, the utterance is instead computed from feeding the entire - spectogram to the network. - :param return_partials: if True, the partial embeddings will also be returned along with the - wav slices that correspond to the partial embeddings. - :param kwargs: additional arguments to compute_partial_splits() - :return: the embedding as a numpy array of float32 of shape (model_embedding_size,). If - is True, the partial utterances as a numpy array of float32 of shape - (n_partials, model_embedding_size) and the wav partials as a list of slices will also be - returned. If is simultaneously set to False, both these values will be None - instead. - """ - # Process the entire utterance if not using partials - if not using_partials: - frames = audio.wav_to_mel_spectrogram(wav) - embed = embed_frames_batch(frames[None, ...])[0] - if return_partials: - return embed, None, None - return embed - - # Compute where to split the utterance into partials and pad if necessary - wave_slices, mel_slices = compute_partial_slices(len(wav), **kwargs) - max_wave_length = wave_slices[-1].stop - if max_wave_length >= len(wav): - wav = np.pad(wav, (0, max_wave_length - len(wav)), "constant") - - # Split the utterance into partials - frames = audio.wav_to_mel_spectrogram(wav) - frames_batch = np.array([frames[s] for s in mel_slices]) - partial_embeds = embed_frames_batch(frames_batch) - - # Compute the utterance embedding from the partial embeddings - raw_embed = np.mean(partial_embeds, axis=0) - embed = raw_embed / np.linalg.norm(raw_embed, 2) - - if return_partials: - return embed, partial_embeds, wave_slices - return embed - - -def embed_speaker(wavs, **kwargs): - raise NotImplemented() - - -def plot_embedding_as_heatmap(embed, ax=None, title="", shape=None, color_range=(0, 0.30)): - import matplotlib.pyplot as plt - if ax is None: - ax = plt.gca() - - if shape is None: - height = int(np.sqrt(len(embed))) - shape = (height, -1) - embed = embed.reshape(shape) - - cmap = cm.get_cmap() - mappable = ax.imshow(embed, cmap=cmap) - cbar = plt.colorbar(mappable, ax=ax, fraction=0.046, pad=0.04) - sm = cm.ScalarMappable(cmap=cmap) - sm.set_clim(*color_range) - - ax.set_xticks([]), ax.set_yticks([]) - ax.set_title(title) diff --git a/encoder/model.py b/encoder/model.py deleted file mode 100644 index e050d320..00000000 --- a/encoder/model.py +++ /dev/null @@ -1,135 +0,0 @@ -from encoder.params_model import * -from encoder.params_data import * -from scipy.interpolate import interp1d -from sklearn.metrics import roc_curve -from torch.nn.utils import clip_grad_norm_ -from scipy.optimize import brentq -from torch import nn -import numpy as np -import torch - - -class SpeakerEncoder(nn.Module): - def __init__(self, device, loss_device): - super().__init__() - self.loss_device = loss_device - - # Network defition - self.lstm = nn.LSTM(input_size=mel_n_channels, - hidden_size=model_hidden_size, - num_layers=model_num_layers, - batch_first=True).to(device) - self.linear = nn.Linear(in_features=model_hidden_size, - out_features=model_embedding_size).to(device) - self.relu = torch.nn.ReLU().to(device) - - # Cosine similarity scaling (with fixed initial parameter values) - self.similarity_weight = nn.Parameter(torch.tensor([10.])).to(loss_device) - self.similarity_bias = nn.Parameter(torch.tensor([-5.])).to(loss_device) - - # Loss - self.loss_fn = nn.CrossEntropyLoss().to(loss_device) - - def do_gradient_ops(self): - # Gradient scale - self.similarity_weight.grad *= 0.01 - self.similarity_bias.grad *= 0.01 - - # Gradient clipping - clip_grad_norm_(self.parameters(), 3, norm_type=2) - - def forward(self, utterances, hidden_init=None): - """ - Computes the embeddings of a batch of utterance spectrograms. - - :param utterances: batch of mel-scale filterbanks of same duration as a tensor of shape - (batch_size, n_frames, n_channels) - :param hidden_init: initial hidden state of the LSTM as a tensor of shape (num_layers, - batch_size, hidden_size). Will default to a tensor of zeros if None. - :return: the embeddings as a tensor of shape (batch_size, embedding_size) - """ - # Pass the input through the LSTM layers and retrieve all outputs, the final hidden state - # and the final cell state. - out, (hidden, cell) = self.lstm(utterances, hidden_init) - - # We take only the hidden state of the last layer - embeds_raw = self.relu(self.linear(hidden[-1])) - - # L2-normalize it - embeds = embeds_raw / (torch.norm(embeds_raw, dim=1, keepdim=True) + 1e-5) - - return embeds - - def similarity_matrix(self, embeds): - """ - Computes the similarity matrix according the section 2.1 of GE2E. - - :param embeds: the embeddings as a tensor of shape (speakers_per_batch, - utterances_per_speaker, embedding_size) - :return: the similarity matrix as a tensor of shape (speakers_per_batch, - utterances_per_speaker, speakers_per_batch) - """ - speakers_per_batch, utterances_per_speaker = embeds.shape[:2] - - # Inclusive centroids (1 per speaker). Cloning is needed for reverse differentiation - centroids_incl = torch.mean(embeds, dim=1, keepdim=True) - centroids_incl = centroids_incl.clone() / (torch.norm(centroids_incl, dim=2, keepdim=True) + 1e-5) - - # Exclusive centroids (1 per utterance) - centroids_excl = (torch.sum(embeds, dim=1, keepdim=True) - embeds) - centroids_excl /= (utterances_per_speaker - 1) - centroids_excl = centroids_excl.clone() / (torch.norm(centroids_excl, dim=2, keepdim=True) + 1e-5) - - # Similarity matrix. The cosine similarity of already 2-normed vectors is simply the dot - # product of these vectors (which is just an element-wise multiplication reduced by a sum). - # We vectorize the computation for efficiency. - sim_matrix = torch.zeros(speakers_per_batch, utterances_per_speaker, - speakers_per_batch).to(self.loss_device) - mask_matrix = 1 - np.eye(speakers_per_batch, dtype=np.int) - for j in range(speakers_per_batch): - mask = np.where(mask_matrix[j])[0] - sim_matrix[mask, :, j] = (embeds[mask] * centroids_incl[j]).sum(dim=2) - sim_matrix[j, :, j] = (embeds[j] * centroids_excl[j]).sum(dim=1) - - ## Even more vectorized version (slower maybe because of transpose) - # sim_matrix2 = torch.zeros(speakers_per_batch, speakers_per_batch, utterances_per_speaker - # ).to(self.loss_device) - # eye = np.eye(speakers_per_batch, dtype=np.int) - # mask = np.where(1 - eye) - # sim_matrix2[mask] = (embeds[mask[0]] * centroids_incl[mask[1]]).sum(dim=2) - # mask = np.where(eye) - # sim_matrix2[mask] = (embeds * centroids_excl).sum(dim=2) - # sim_matrix2 = sim_matrix2.transpose(1, 2) - - sim_matrix = sim_matrix * self.similarity_weight + self.similarity_bias - return sim_matrix - - def loss(self, embeds): - """ - Computes the softmax loss according the section 2.1 of GE2E. - - :param embeds: the embeddings as a tensor of shape (speakers_per_batch, - utterances_per_speaker, embedding_size) - :return: the loss and the EER for this batch of embeddings. - """ - speakers_per_batch, utterances_per_speaker = embeds.shape[:2] - - # Loss - sim_matrix = self.similarity_matrix(embeds) - sim_matrix = sim_matrix.reshape((speakers_per_batch * utterances_per_speaker, - speakers_per_batch)) - ground_truth = np.repeat(np.arange(speakers_per_batch), utterances_per_speaker) - target = torch.from_numpy(ground_truth).long().to(self.loss_device) - loss = self.loss_fn(sim_matrix, target) - - # EER (not backpropagated) - with torch.no_grad(): - inv_argmax = lambda i: np.eye(1, speakers_per_batch, i, dtype=np.int)[0] - labels = np.array([inv_argmax(i) for i in ground_truth]) - preds = sim_matrix.detach().cpu().numpy() - - # Snippet from https://yangcha.github.io/EER-ROC/ - fpr, tpr, thresholds = roc_curve(labels.flatten(), preds.flatten()) - eer = brentq(lambda x: 1. - x - interp1d(fpr, tpr)(x), 0., 1.) - - return loss, eer diff --git a/encoder/params_data.py b/encoder/params_data.py deleted file mode 100644 index bdb1716e..00000000 --- a/encoder/params_data.py +++ /dev/null @@ -1,29 +0,0 @@ - -## Mel-filterbank -mel_window_length = 25 # In milliseconds -mel_window_step = 10 # In milliseconds -mel_n_channels = 40 - - -## Audio -sampling_rate = 16000 -# Number of spectrogram frames in a partial utterance -partials_n_frames = 160 # 1600 ms -# Number of spectrogram frames at inference -inference_n_frames = 80 # 800 ms - - -## Voice Activation Detection -# Window size of the VAD. Must be either 10, 20 or 30 milliseconds. -# This sets the granularity of the VAD. Should not need to be changed. -vad_window_length = 30 # In milliseconds -# Number of frames to average together when performing the moving average smoothing. -# The larger this value, the larger the VAD variations must be to not get smoothed out. -vad_moving_average_width = 8 -# Maximum number of consecutive silent frames a segment can have. -vad_max_silence_length = 6 - - -## Audio volume normalization -audio_norm_target_dBFS = -30 - diff --git a/encoder/params_model.py b/encoder/params_model.py deleted file mode 100644 index 3e356472..00000000 --- a/encoder/params_model.py +++ /dev/null @@ -1,11 +0,0 @@ - -## Model parameters -model_hidden_size = 256 -model_embedding_size = 256 -model_num_layers = 3 - - -## Training parameters -learning_rate_init = 1e-4 -speakers_per_batch = 64 -utterances_per_speaker = 10 diff --git a/encoder/preprocess.py b/encoder/preprocess.py deleted file mode 100644 index d2dfc5ec..00000000 --- a/encoder/preprocess.py +++ /dev/null @@ -1,184 +0,0 @@ -from datetime import datetime -from functools import partial -from multiprocessing import Pool -from pathlib import Path - -import numpy as np -from tqdm import tqdm - -from encoder import audio -from encoder.config import librispeech_datasets, anglophone_nationalites -from encoder.params_data import * - - -_AUDIO_EXTENSIONS = ("wav", "flac", "m4a", "mp3") - -class DatasetLog: - """ - Registers metadata about the dataset in a text file. - """ - def __init__(self, root, name): - self.text_file = open(Path(root, "Log_%s.txt" % name.replace("/", "_")), "w") - self.sample_data = dict() - - start_time = str(datetime.now().strftime("%A %d %B %Y at %H:%M")) - self.write_line("Creating dataset %s on %s" % (name, start_time)) - self.write_line("-----") - self._log_params() - - def _log_params(self): - from encoder import params_data - self.write_line("Parameter values:") - for param_name in (p for p in dir(params_data) if not p.startswith("__")): - value = getattr(params_data, param_name) - self.write_line("\t%s: %s" % (param_name, value)) - self.write_line("-----") - - def write_line(self, line): - self.text_file.write("%s\n" % line) - - def add_sample(self, **kwargs): - for param_name, value in kwargs.items(): - if not param_name in self.sample_data: - self.sample_data[param_name] = [] - self.sample_data[param_name].append(value) - - def finalize(self): - self.write_line("Statistics:") - for param_name, values in self.sample_data.items(): - self.write_line("\t%s:" % param_name) - self.write_line("\t\tmin %.3f, max %.3f" % (np.min(values), np.max(values))) - self.write_line("\t\tmean %.3f, median %.3f" % (np.mean(values), np.median(values))) - self.write_line("-----") - end_time = str(datetime.now().strftime("%A %d %B %Y at %H:%M")) - self.write_line("Finished on %s" % end_time) - self.text_file.close() - - -def _init_preprocess_dataset(dataset_name, datasets_root, out_dir) -> (Path, DatasetLog): - dataset_root = datasets_root.joinpath(dataset_name) - if not dataset_root.exists(): - print("Couldn\'t find %s, skipping this dataset." % dataset_root) - return None, None - return dataset_root, DatasetLog(out_dir, dataset_name) - - -def _preprocess_speaker(speaker_dir: Path, datasets_root: Path, out_dir: Path, skip_existing: bool): - # Give a name to the speaker that includes its dataset - speaker_name = "_".join(speaker_dir.relative_to(datasets_root).parts) - - # Create an output directory with that name, as well as a txt file containing a - # reference to each source file. - speaker_out_dir = out_dir.joinpath(speaker_name) - speaker_out_dir.mkdir(exist_ok=True) - sources_fpath = speaker_out_dir.joinpath("_sources.txt") - - # There's a possibility that the preprocessing was interrupted earlier, check if - # there already is a sources file. - if sources_fpath.exists(): - try: - with sources_fpath.open("r") as sources_file: - existing_fnames = {line.split(",")[0] for line in sources_file} - except: - existing_fnames = {} - else: - existing_fnames = {} - - # Gather all audio files for that speaker recursively - sources_file = sources_fpath.open("a" if skip_existing else "w") - audio_durs = [] - for extension in _AUDIO_EXTENSIONS: - for in_fpath in speaker_dir.glob("**/*.%s" % extension): - # Check if the target output file already exists - out_fname = "_".join(in_fpath.relative_to(speaker_dir).parts) - out_fname = out_fname.replace(".%s" % extension, ".npy") - if skip_existing and out_fname in existing_fnames: - continue - - # Load and preprocess the waveform - wav = audio.preprocess_wav(in_fpath) - if len(wav) == 0: - continue - - # Create the mel spectrogram, discard those that are too short - frames = audio.wav_to_mel_spectrogram(wav) - if len(frames) < partials_n_frames: - continue - - out_fpath = speaker_out_dir.joinpath(out_fname) - np.save(out_fpath, frames) - sources_file.write("%s,%s\n" % (out_fname, in_fpath)) - audio_durs.append(len(wav) / sampling_rate) - - sources_file.close() - - return audio_durs - - -def _preprocess_speaker_dirs(speaker_dirs, dataset_name, datasets_root, out_dir, skip_existing, logger): - print("%s: Preprocessing data for %d speakers." % (dataset_name, len(speaker_dirs))) - - # Process the utterances for each speaker - work_fn = partial(_preprocess_speaker, datasets_root=datasets_root, out_dir=out_dir, skip_existing=skip_existing) - with Pool(4) as pool: - tasks = pool.imap(work_fn, speaker_dirs) - for sample_durs in tqdm(tasks, dataset_name, len(speaker_dirs), unit="speakers"): - for sample_dur in sample_durs: - logger.add_sample(duration=sample_dur) - - logger.finalize() - print("Done preprocessing %s.\n" % dataset_name) - - -def preprocess_librispeech(datasets_root: Path, out_dir: Path, skip_existing=False): - for dataset_name in librispeech_datasets["train"]["other"]: - # Initialize the preprocessing - dataset_root, logger = _init_preprocess_dataset(dataset_name, datasets_root, out_dir) - if not dataset_root: - return - - # Preprocess all speakers - speaker_dirs = list(dataset_root.glob("*")) - _preprocess_speaker_dirs(speaker_dirs, dataset_name, datasets_root, out_dir, skip_existing, logger) - - -def preprocess_voxceleb1(datasets_root: Path, out_dir: Path, skip_existing=False): - # Initialize the preprocessing - dataset_name = "VoxCeleb1" - dataset_root, logger = _init_preprocess_dataset(dataset_name, datasets_root, out_dir) - if not dataset_root: - return - - # Get the contents of the meta file - with dataset_root.joinpath("vox1_meta.csv").open("r") as metafile: - metadata = [line.split("\t") for line in metafile][1:] - - # Select the ID and the nationality, filter out non-anglophone speakers - nationalities = {line[0]: line[3] for line in metadata} - keep_speaker_ids = [speaker_id for speaker_id, nationality in nationalities.items() if - nationality.lower() in anglophone_nationalites] - print("VoxCeleb1: using samples from %d (presumed anglophone) speakers out of %d." % - (len(keep_speaker_ids), len(nationalities))) - - # Get the speaker directories for anglophone speakers only - speaker_dirs = dataset_root.joinpath("wav").glob("*") - speaker_dirs = [speaker_dir for speaker_dir in speaker_dirs if - speaker_dir.name in keep_speaker_ids] - print("VoxCeleb1: found %d anglophone speakers on the disk, %d missing (this is normal)." % - (len(speaker_dirs), len(keep_speaker_ids) - len(speaker_dirs))) - - # Preprocess all speakers - _preprocess_speaker_dirs(speaker_dirs, dataset_name, datasets_root, out_dir, skip_existing, logger) - - -def preprocess_voxceleb2(datasets_root: Path, out_dir: Path, skip_existing=False): - # Initialize the preprocessing - dataset_name = "VoxCeleb2" - dataset_root, logger = _init_preprocess_dataset(dataset_name, datasets_root, out_dir) - if not dataset_root: - return - - # Get the speaker directories - # Preprocess all speakers - speaker_dirs = list(dataset_root.joinpath("dev", "aac").glob("*")) - _preprocess_speaker_dirs(speaker_dirs, dataset_name, datasets_root, out_dir, skip_existing, logger) diff --git a/encoder/train.py b/encoder/train.py deleted file mode 100644 index 2bed4eb2..00000000 --- a/encoder/train.py +++ /dev/null @@ -1,125 +0,0 @@ -from pathlib import Path - -import torch - -from encoder.data_objects import SpeakerVerificationDataLoader, SpeakerVerificationDataset -from encoder.model import SpeakerEncoder -from encoder.params_model import * -from encoder.visualizations import Visualizations -from utils.profiler import Profiler - - -def sync(device: torch.device): - # For correct profiling (cuda operations are async) - if device.type == "cuda": - torch.cuda.synchronize(device) - - -def train(run_id: str, clean_data_root: Path, models_dir: Path, umap_every: int, save_every: int, - backup_every: int, vis_every: int, force_restart: bool, visdom_server: str, - no_visdom: bool): - # Create a dataset and a dataloader - dataset = SpeakerVerificationDataset(clean_data_root) - loader = SpeakerVerificationDataLoader( - dataset, - speakers_per_batch, - utterances_per_speaker, - num_workers=4, - ) - - # Setup the device on which to run the forward pass and the loss. These can be different, - # because the forward pass is faster on the GPU whereas the loss is often (depending on your - # hyperparameters) faster on the CPU. - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - # FIXME: currently, the gradient is None if loss_device is cuda - loss_device = torch.device("cpu") - - # Create the model and the optimizer - model = SpeakerEncoder(device, loss_device) - optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate_init) - init_step = 1 - - # Configure file path for the model - model_dir = models_dir / run_id - model_dir.mkdir(exist_ok=True, parents=True) - state_fpath = model_dir / "encoder.pt" - - # Load any existing model - if not force_restart: - if state_fpath.exists(): - print("Found existing model \"%s\", loading it and resuming training." % run_id) - checkpoint = torch.load(state_fpath) - init_step = checkpoint["step"] - model.load_state_dict(checkpoint["model_state"]) - optimizer.load_state_dict(checkpoint["optimizer_state"]) - optimizer.param_groups[0]["lr"] = learning_rate_init - else: - print("No model \"%s\" found, starting training from scratch." % run_id) - else: - print("Starting the training from scratch.") - model.train() - - # Initialize the visualization environment - vis = Visualizations(run_id, vis_every, server=visdom_server, disabled=no_visdom) - vis.log_dataset(dataset) - vis.log_params() - device_name = str(torch.cuda.get_device_name(0) if torch.cuda.is_available() else "CPU") - vis.log_implementation({"Device": device_name}) - - # Training loop - profiler = Profiler(summarize_every=10, disabled=False) - for step, speaker_batch in enumerate(loader, init_step): - profiler.tick("Blocking, waiting for batch (threaded)") - - # Forward pass - inputs = torch.from_numpy(speaker_batch.data).to(device) - sync(device) - profiler.tick("Data to %s" % device) - embeds = model(inputs) - sync(device) - profiler.tick("Forward pass") - embeds_loss = embeds.view((speakers_per_batch, utterances_per_speaker, -1)).to(loss_device) - loss, eer = model.loss(embeds_loss) - sync(loss_device) - profiler.tick("Loss") - - # Backward pass - model.zero_grad() - loss.backward() - profiler.tick("Backward pass") - model.do_gradient_ops() - optimizer.step() - profiler.tick("Parameter update") - - # Update visualizations - # learning_rate = optimizer.param_groups[0]["lr"] - vis.update(loss.item(), eer, step) - - # Draw projections and save them to the backup folder - if umap_every != 0 and step % umap_every == 0: - print("Drawing and saving projections (step %d)" % step) - projection_fpath = model_dir / f"umap_{step:06d}.png" - embeds = embeds.detach().cpu().numpy() - vis.draw_projections(embeds, utterances_per_speaker, step, projection_fpath) - vis.save() - - # Overwrite the latest version of the model - if save_every != 0 and step % save_every == 0: - print("Saving the model (step %d)" % step) - torch.save({ - "step": step + 1, - "model_state": model.state_dict(), - "optimizer_state": optimizer.state_dict(), - }, state_fpath) - - # Make a backup - if backup_every != 0 and step % backup_every == 0: - print("Making a backup (step %d)" % step) - backup_fpath = model_dir / f"encoder_{step:06d}.bak" - torch.save({ - "step": step + 1, - "model_state": model.state_dict(), - "optimizer_state": optimizer.state_dict(), - }, backup_fpath) - - profiler.tick("Extras (visualizations, saving)") diff --git a/encoder/visualizations.py b/encoder/visualizations.py deleted file mode 100644 index d103944f..00000000 --- a/encoder/visualizations.py +++ /dev/null @@ -1,179 +0,0 @@ -from datetime import datetime -from time import perf_counter as timer - -import numpy as np -import umap -import visdom - -from encoder.data_objects.speaker_verification_dataset import SpeakerVerificationDataset - - -colormap = np.array([ - [76, 255, 0], - [0, 127, 70], - [255, 0, 0], - [255, 217, 38], - [0, 135, 255], - [165, 0, 165], - [255, 167, 255], - [0, 255, 255], - [255, 96, 38], - [142, 76, 0], - [33, 0, 127], - [0, 0, 0], - [183, 183, 183], -], dtype=np.float) / 255 - - -class Visualizations: - def __init__(self, env_name=None, update_every=10, server="http://localhost", disabled=False): - # Tracking data - self.last_update_timestamp = timer() - self.update_every = update_every - self.step_times = [] - self.losses = [] - self.eers = [] - print("Updating the visualizations every %d steps." % update_every) - - # If visdom is disabled TODO: use a better paradigm for that - self.disabled = disabled - if self.disabled: - return - - # Set the environment name - now = str(datetime.now().strftime("%d-%m %Hh%M")) - if env_name is None: - self.env_name = now - else: - self.env_name = "%s (%s)" % (env_name, now) - - # Connect to visdom and open the corresponding window in the browser - try: - self.vis = visdom.Visdom(server, env=self.env_name, raise_exceptions=True) - except ConnectionError: - raise Exception("No visdom server detected. Run the command \"visdom\" in your CLI to " - "start it.") - # webbrowser.open("http://localhost:8097/env/" + self.env_name) - - # Create the windows - self.loss_win = None - self.eer_win = None - # self.lr_win = None - self.implementation_win = None - self.projection_win = None - self.implementation_string = "" - - def log_params(self): - if self.disabled: - return - from encoder import params_data - from encoder import params_model - param_string = "Model parameters:
" - for param_name in (p for p in dir(params_model) if not p.startswith("__")): - value = getattr(params_model, param_name) - param_string += "\t%s: %s
" % (param_name, value) - param_string += "Data parameters:
" - for param_name in (p for p in dir(params_data) if not p.startswith("__")): - value = getattr(params_data, param_name) - param_string += "\t%s: %s
" % (param_name, value) - self.vis.text(param_string, opts={"title": "Parameters"}) - - def log_dataset(self, dataset: SpeakerVerificationDataset): - if self.disabled: - return - dataset_string = "" - dataset_string += "Speakers: %s\n" % len(dataset.speakers) - dataset_string += "\n" + dataset.get_logs() - dataset_string = dataset_string.replace("\n", "
") - self.vis.text(dataset_string, opts={"title": "Dataset"}) - - def log_implementation(self, params): - if self.disabled: - return - implementation_string = "" - for param, value in params.items(): - implementation_string += "%s: %s\n" % (param, value) - implementation_string = implementation_string.replace("\n", "
") - self.implementation_string = implementation_string - self.implementation_win = self.vis.text( - implementation_string, - opts={"title": "Training implementation"} - ) - - def update(self, loss, eer, step): - # Update the tracking data - now = timer() - self.step_times.append(1000 * (now - self.last_update_timestamp)) - self.last_update_timestamp = now - self.losses.append(loss) - self.eers.append(eer) - print(".", end="") - - # Update the plots every steps - if step % self.update_every != 0: - return - time_string = "Step time: mean: %5dms std: %5dms" % \ - (int(np.mean(self.step_times)), int(np.std(self.step_times))) - print("\nStep %6d Loss: %.4f EER: %.4f %s" % - (step, np.mean(self.losses), np.mean(self.eers), time_string)) - if not self.disabled: - self.loss_win = self.vis.line( - [np.mean(self.losses)], - [step], - win=self.loss_win, - update="append" if self.loss_win else None, - opts=dict( - legend=["Avg. loss"], - xlabel="Step", - ylabel="Loss", - title="Loss", - ) - ) - self.eer_win = self.vis.line( - [np.mean(self.eers)], - [step], - win=self.eer_win, - update="append" if self.eer_win else None, - opts=dict( - legend=["Avg. EER"], - xlabel="Step", - ylabel="EER", - title="Equal error rate" - ) - ) - if self.implementation_win is not None: - self.vis.text( - self.implementation_string + ("%s" % time_string), - win=self.implementation_win, - opts={"title": "Training implementation"}, - ) - - # Reset the tracking - self.losses.clear() - self.eers.clear() - self.step_times.clear() - - def draw_projections(self, embeds, utterances_per_speaker, step, out_fpath=None, max_speakers=10): - import matplotlib.pyplot as plt - - max_speakers = min(max_speakers, len(colormap)) - embeds = embeds[:max_speakers * utterances_per_speaker] - - n_speakers = len(embeds) // utterances_per_speaker - ground_truth = np.repeat(np.arange(n_speakers), utterances_per_speaker) - colors = [colormap[i] for i in ground_truth] - - reducer = umap.UMAP() - projected = reducer.fit_transform(embeds) - plt.scatter(projected[:, 0], projected[:, 1], c=colors) - plt.gca().set_aspect("equal", "datalim") - plt.title("UMAP projection (step %d)" % step) - if not self.disabled: - self.projection_win = self.vis.matplot(plt, win=self.projection_win) - if out_fpath is not None: - plt.savefig(out_fpath) - plt.clf() - - def save(self): - if not self.disabled: - self.vis.save([self.env_name]) diff --git a/talkingface/data/dataprocess/get_avg_mels.ipynb b/talkingface/data/dataprocess/get_avg_mels.ipynb new file mode 100644 index 00000000..a5ab8c59 --- /dev/null +++ b/talkingface/data/dataprocess/get_avg_mels.ipynb @@ -0,0 +1,2450 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import os\n", + "import numpy as np\n", + "import tgt\n", + "from scipy.stats import mode" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "phoneme_list = ['AA0', 'AA1', 'AA2', 'AE0', 'AE1', 'AE2', \n", + " 'AH0', 'AH1', 'AH2', 'AO0', 'AO1', 'AO2', 'AW0', \n", + " 'AW1', 'AW2', 'AY0', 'AY1', 'AY2', 'B', 'CH', 'D', 'DH', \n", + " 'EH0', 'EH1', 'EH2', 'ER0', 'ER1', 'ER2', \n", + " 'EY0', 'EY1', 'EY2', 'F', 'G', 'HH', 'IH0', 'IH1', 'IH2', \n", + " 'IY0', 'IY1', 'IY2', 'JH', 'K', 'L', 'M', 'N', 'NG', \n", + " 'OW0', 'OW1', 'OW2', 'OY0', 'OY1', 'OY2', 'P', \n", + " 'R', 'S', 'SH', 'T', 'TH', 'UH0', 'UH1', 'UH2', \n", + " 'UW0', 'UW1', 'UW2', 'V', 'W', 'Y', 'Z', 'ZH', 'sil', 'sp', 'spn']\n", + "phoneme_dict = dict()\n", + "for j, p in enumerate(phoneme_list):\n", + " phoneme_dict[p] = j" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Speaker 1: 8057\n", + "Speaker 2: 4014\n", + "Speaker 3: 6415\n", + "Speaker 4: 5126\n", + "Speaker 5: 3723\n", + "Speaker 6: 587\n", + "Speaker 7: 8534\n", + "Speaker 8: 5322\n", + "Speaker 9: 2238\n", + "Speaker 10: 1401\n", + "Speaker 11: 4427\n", + "Speaker 12: 1705\n", + "Speaker 13: 561\n", + "Speaker 14: 2992\n", + "Speaker 15: 8776\n", + "Speaker 16: 54\n", + "Speaker 17: 806\n", + "Speaker 18: 1970\n", + "Speaker 19: 302\n", + "Speaker 20: 6272\n", + "Speaker 21: 1289\n", + "Speaker 22: 3807\n", + "Speaker 23: 6075\n", + "Speaker 24: 329\n", + "Speaker 25: 3483\n", + "Speaker 26: 1914\n", + "Speaker 27: 6499\n", + "Speaker 28: 7117\n", + "Speaker 29: 5703\n", + "Speaker 30: 3032\n", + "Speaker 31: 3001\n", + "Speaker 32: 5304\n", + "Speaker 33: 5012\n", + "Speaker 34: 8786\n", + "Speaker 35: 3187\n", + "Speaker 36: 5935\n", + "Speaker 37: 1088\n", + "Speaker 38: 783\n", + "Speaker 39: 5186\n", + "Speaker 40: 7994\n", + "Speaker 41: 6078\n", + "Speaker 42: 3168\n", + "Speaker 43: 6550\n", + "Speaker 44: 6701\n", + "Speaker 45: 4926\n", + "Speaker 46: 1355\n", + "Speaker 47: 1337\n", + "Speaker 48: 2582\n", + "Speaker 49: 8119\n", + "Speaker 50: 5767\n", + "Speaker 51: 1112\n", + "Speaker 52: 6054\n", + "Speaker 53: 5583\n", + "Speaker 54: 6120\n", + "Speaker 55: 4290\n", + "Speaker 56: 3440\n", + "Speaker 57: 2230\n", + "Speaker 58: 5802\n", + "Speaker 59: 3448\n", + "Speaker 60: 730\n", + "Speaker 61: 7011\n", + "Speaker 62: 40\n", + "Speaker 63: 1845\n", + "Speaker 64: 7816\n", + "Speaker 65: 4010\n", + "Speaker 66: 2823\n", + "Speaker 67: 511\n", + "Speaker 68: 229\n", + "Speaker 69: 5319\n", + "Speaker 70: 4830\n", + "Speaker 71: 8494\n", + "Speaker 72: 1509\n", + "Speaker 73: 7285\n", + "Speaker 74: 1226\n", + "Speaker 75: 2638\n", + "Speaker 76: 2920\n", + "Speaker 77: 7367\n", + "Speaker 78: 2598\n", + "Speaker 79: 3686\n", + "Speaker 80: 412\n", + "Speaker 81: 5538\n", + "Speaker 82: 663\n", + "Speaker 83: 6683\n", + "Speaker 84: 1271\n", + "Speaker 85: 5514\n", + "Speaker 86: 8699\n", + "Speaker 87: 7264\n", + "Speaker 88: 816\n", + "Speaker 89: 5092\n", + "Speaker 90: 7752\n", + "Speaker 91: 3008\n", + "Speaker 92: 5688\n", + "Speaker 93: 3513\n", + "Speaker 94: 1224\n", + "Speaker 95: 8312\n", + "Speaker 96: 8791\n", + "Speaker 97: 5104\n", + "Speaker 98: 5266\n", + "Speaker 99: 1392\n", + "Speaker 100: 3549\n", + "Speaker 101: 7945\n", + "Speaker 102: 272\n", + "Speaker 103: 5940\n", + "Speaker 104: 6437\n", + "Speaker 105: 2531\n", + "Speaker 106: 6509\n", + "Speaker 107: 4064\n", + "Speaker 108: 2167\n", + "Speaker 109: 3630\n", + "Speaker 110: 4018\n", + "Speaker 111: 8770\n", + "Speaker 112: 8163\n", + "Speaker 113: 5809\n", + "Speaker 114: 510\n", + "Speaker 115: 5007\n", + "Speaker 116: 4967\n", + "Speaker 117: 8396\n", + "Speaker 118: 359\n", + "Speaker 119: 5622\n", + "Speaker 120: 3521\n", + "Speaker 121: 3923\n", + "Speaker 122: 1382\n", + "Speaker 123: 1012\n", + "Speaker 124: 7939\n", + "Speaker 125: 4839\n", + "Speaker 126: 1175\n", + "Speaker 127: 2836\n", + "Speaker 128: 4853\n", + "Speaker 129: 639\n", + "Speaker 130: 4236\n", + "Speaker 131: 2654\n", + "Speaker 132: 3866\n", + "Speaker 133: 335\n", + "Speaker 134: 3551\n", + "Speaker 135: 1046\n", + "Speaker 136: 6147\n", + "Speaker 137: 157\n", + "Speaker 138: 3094\n", + "Speaker 139: 2427\n", + "Speaker 140: 8195\n", + "Speaker 141: 4238\n", + "Speaker 142: 4854\n", + "Speaker 143: 7832\n", + "Speaker 144: 1748\n", + "Speaker 145: 4586\n", + "Speaker 146: 7484\n", + "Speaker 147: 1825\n", + "Speaker 148: 669\n", + "Speaker 149: 512\n", + "Speaker 150: 4433\n", + "Speaker 151: 3374\n", + "Speaker 152: 6064\n", + "Speaker 153: 2201\n", + "Speaker 154: 6519\n", + "Speaker 155: 323\n", + "Speaker 156: 7515\n", + "Speaker 157: 1316\n", + "Speaker 158: 3717\n", + "Speaker 159: 4362\n", + "Speaker 160: 89\n", + "Speaker 161: 5810\n", + "Speaker 162: 8050\n", + "Speaker 163: 1025\n", + "Speaker 164: 7991\n", + "Speaker 165: 4495\n", + "Speaker 166: 3003\n", + "Speaker 167: 1001\n", + "Speaker 168: 4243\n", + "Speaker 169: 7069\n", + "Speaker 170: 593\n", + "Speaker 171: 1913\n", + "Speaker 172: 1058\n", + "Speaker 173: 4363\n", + "Speaker 174: 2056\n", + "Speaker 175: 4535\n", + "Speaker 176: 4138\n", + "Speaker 177: 2751\n", + "Speaker 178: 6367\n", + "Speaker 179: 6904\n", + "Speaker 180: 8677\n", + "Speaker 181: 5123\n", + "Speaker 182: 7520\n", + "Speaker 183: 6019\n", + "Speaker 184: 6294\n", + "Speaker 185: 1811\n", + "Speaker 186: 4226\n", + "Speaker 187: 6206\n", + "Speaker 188: 5062\n", + "Speaker 189: 16\n", + "Speaker 190: 6877\n", + "Speaker 191: 163\n", + "Speaker 192: 3114\n", + "Speaker 193: 7956\n", + "Speaker 194: 5002\n", + "Speaker 195: 957\n", + "Speaker 196: 8635\n", + "Speaker 197: 3977\n", + "Speaker 198: 3389\n", + "Speaker 199: 1639\n", + "Speaker 200: 1552\n", + "Speaker 201: 925\n", + "Speaker 202: 6115\n", + "Speaker 203: 2162\n", + "Speaker 204: 8051\n", + "Speaker 205: 8098\n", + "Speaker 206: 2581\n", + "Speaker 207: 4381\n", + "Speaker 208: 125\n", + "Speaker 209: 3046\n", + "Speaker 210: 6544\n", + "Speaker 211: 7594\n", + "Speaker 212: 2136\n", + "Speaker 213: 250\n", + "Speaker 214: 9023\n", + "Speaker 215: 4438\n", + "Speaker 216: 954\n", + "Speaker 217: 6189\n", + "Speaker 218: 7569\n", + "Speaker 219: 5389\n", + "Speaker 220: 6924\n", + "Speaker 221: 226\n", + "Speaker 222: 118\n", + "Speaker 223: 476\n", + "Speaker 224: 1556\n", + "Speaker 225: 4267\n", + "Speaker 226: 7316\n", + "Speaker 227: 409\n", + "Speaker 228: 7398\n", + "Speaker 229: 2182\n", + "Speaker 230: 8193\n", + "Speaker 231: 6782\n", + "Speaker 232: 3979\n", + "Speaker 233: 8879\n", + "Speaker 234: 90\n", + "Speaker 235: 850\n", + "Speaker 236: 4945\n", + "Speaker 237: 7833\n", + "Speaker 238: 583\n", + "Speaker 239: 6000\n", + "Speaker 240: 7030\n", + "Speaker 241: 1961\n", + "Speaker 242: 288\n", + "Speaker 243: 8190\n", + "Speaker 244: 7090\n", + "Speaker 245: 1867\n", + "Speaker 246: 2137\n", + "Speaker 247: 2110\n", + "Speaker 248: 233\n", + "Speaker 249: 5063\n", + "Speaker 250: 1776\n", + "Speaker 251: 7312\n", + "Speaker 252: 724\n", + "Speaker 253: 6673\n", + "Speaker 254: 1851\n", + "Speaker 255: 231\n", + "Speaker 256: 3082\n", + "Speaker 257: 8772\n", + "Speaker 258: 8629\n", + "Speaker 259: 32\n", + "Speaker 260: 4116\n", + "Speaker 261: 487\n", + "Speaker 262: 1649\n", + "Speaker 263: 1731\n", + "Speaker 264: 7909\n", + "Speaker 265: 3879\n", + "Speaker 266: 1903\n", + "Speaker 267: 8123\n", + "Speaker 268: 3699\n", + "Speaker 269: 1348\n", + "Speaker 270: 8118\n", + "Speaker 271: 2882\n", + "Speaker 272: 3083\n", + "Speaker 273: 7085\n", + "Speaker 274: 5154\n", + "Speaker 275: 1724\n", + "Speaker 276: 1446\n", + "Speaker 277: 1740\n", + "Speaker 278: 6080\n", + "Speaker 279: 6880\n", + "Speaker 280: 7384\n", + "Speaker 281: 1334\n", + "Speaker 282: 7540\n", + "Speaker 283: 5731\n", + "Speaker 284: 7517\n", + "Speaker 285: 4145\n", + "Speaker 286: 3852\n", + "Speaker 287: 4846\n", + "Speaker 288: 2812\n", + "Speaker 289: 1624\n", + "Speaker 290: 764\n", + "Speaker 291: 3482\n", + "Speaker 292: 2688\n", + "Speaker 293: 1422\n", + "Speaker 294: 1874\n", + "Speaker 295: 3072\n", + "Speaker 296: 7982\n", + "Speaker 297: 345\n", + "Speaker 298: 5115\n", + "Speaker 299: 7278\n", + "Speaker 300: 7730\n", + "Speaker 301: 5975\n", + "Speaker 302: 5985\n", + "Speaker 303: 1283\n", + "Speaker 304: 6492\n", + "Speaker 305: 5192\n", + "Speaker 306: 5463\n", + "Speaker 307: 8063\n", + "Speaker 308: 3025\n", + "Speaker 309: 5604\n", + "Speaker 310: 984\n", + "Speaker 311: 1241\n", + "Speaker 312: 1417\n", + "Speaker 313: 4731\n", + "Speaker 314: 8080\n", + "Speaker 315: 5672\n", + "Speaker 316: 3330\n", + "Speaker 317: 7286\n", + "Speaker 318: 1052\n", + "Speaker 319: 1502\n", + "Speaker 320: 4098\n", + "Speaker 321: 6925\n", + "Speaker 322: 4592\n", + "Speaker 323: 596\n", + "Speaker 324: 1448\n", + "Speaker 325: 6188\n", + "Speaker 326: 1841\n", + "Speaker 327: 4490\n", + "Speaker 328: 5157\n", + "Speaker 329: 3967\n", + "Speaker 330: 3994\n", + "Speaker 331: 1222\n", + "Speaker 332: 3228\n", + "Speaker 333: 254\n", + "Speaker 334: 60\n", + "Speaker 335: 3361\n", + "Speaker 336: 454\n", + "Speaker 337: 7837\n", + "Speaker 338: 8465\n", + "Speaker 339: 1093\n", + "Speaker 340: 2787\n", + "Speaker 341: 8097\n", + "Speaker 342: 7297\n", + "Speaker 343: 711\n", + "Speaker 344: 598\n", + "Speaker 345: 6330\n", + "Speaker 346: 8011\n", + "Speaker 347: 1322\n", + "Speaker 348: 6494\n", + "Speaker 349: 4425\n", + "Speaker 350: 374\n", + "Speaker 351: 1737\n", + "Speaker 352: 216\n", + "Speaker 353: 7061\n", + "Speaker 354: 2391\n", + "Speaker 355: 4051\n", + "Speaker 356: 2272\n", + "Speaker 357: 6497\n", + "Speaker 358: 2499\n", + "Speaker 359: 8108\n", + "Speaker 360: 7794\n", + "Speaker 361: 1349\n", + "Speaker 362: 6446\n", + "Speaker 363: 3118\n", + "Speaker 364: 8468\n", + "Speaker 365: 7051\n", + "Speaker 366: 2741\n", + "Speaker 367: 8705\n", + "Speaker 368: 2039\n", + "Speaker 369: 7802\n", + "Speaker 370: 5750\n", + "Speaker 371: 2285\n", + "Speaker 372: 4898\n", + "Speaker 373: 5606\n", + "Speaker 374: 5570\n", + "Speaker 375: 1898\n", + "Speaker 376: 2827\n", + "Speaker 377: 4434\n", + "Speaker 378: 3294\n", + "Speaker 379: 7825\n", + "Speaker 380: 2113\n", + "Speaker 381: 8266\n", + "Speaker 382: 322\n", + "Speaker 383: 6927\n", + "Speaker 384: 5652\n", + "Speaker 385: 6269\n", + "Speaker 386: 887\n", + "Speaker 387: 5133\n", + "Speaker 388: 4807\n", + "Speaker 389: 4733\n", + "Speaker 390: 7067\n", + "Speaker 391: 6531\n", + "Speaker 392: 8008\n", + "Speaker 393: 2512\n", + "Speaker 394: 1987\n", + "Speaker 395: 1195\n", + "Speaker 396: 7335\n", + "Speaker 397: 8758\n", + "Speaker 398: 2691\n", + "Speaker 399: 1061\n", + "Speaker 400: 5519\n", + "Speaker 401: 4680\n", + "Speaker 402: 3546\n", + "Speaker 403: 8419\n", + "Speaker 404: 1958\n", + "Speaker 405: 3289\n", + "Speaker 406: 5918\n", + "Speaker 407: 8887\n", + "Speaker 408: 8575\n", + "Speaker 409: 210\n", + "Speaker 410: 6965\n", + "Speaker 411: 258\n", + "Speaker 412: 2010\n", + "Speaker 413: 1066\n", + "Speaker 414: 7926\n", + "Speaker 415: 2401\n", + "Speaker 416: 3258\n", + "Speaker 417: 8742\n", + "Speaker 418: 1050\n", + "Speaker 419: 548\n", + "Speaker 420: 1027\n", + "Speaker 421: 8643\n", + "Speaker 422: 5513\n", + "Speaker 423: 3914\n", + "Speaker 424: 1053\n", + "Speaker 425: 7511\n", + "Speaker 426: 5290\n", + "Speaker 427: 4013\n", + "Speaker 428: 543\n", + "Speaker 429: 369\n", + "Speaker 430: 2404\n", + "Speaker 431: 4629\n", + "Speaker 432: 481\n", + "Speaker 433: 625\n", + "Speaker 434: 1472\n", + "Speaker 435: 7145\n", + "Speaker 436: 426\n", + "Speaker 437: 7647\n", + "Speaker 438: 2397\n", + "Speaker 439: 8464\n", + "Speaker 440: 6686\n", + "Speaker 441: 8222\n", + "Speaker 442: 2002\n", + "Speaker 443: 7437\n", + "Speaker 444: 1547\n", + "Speaker 445: 7733\n", + "Speaker 446: 8527\n", + "Speaker 447: 8194\n", + "Speaker 448: 911\n", + "Speaker 449: 3357\n", + "Speaker 450: 227\n", + "Speaker 451: 781\n", + "Speaker 452: 122\n", + "Speaker 453: 5635\n", + "Speaker 454: 362\n", + "Speaker 455: 353\n", + "Speaker 456: 7962\n", + "Speaker 457: 2240\n", + "Speaker 458: 8605\n", + "Speaker 459: 4044\n", + "Speaker 460: 7959\n", + "Speaker 461: 3703\n", + "Speaker 462: 311\n", + "Speaker 463: 2289\n", + "Speaker 464: 7704\n", + "Speaker 465: 4800\n", + "Speaker 466: 2411\n", + "Speaker 467: 5724\n", + "Speaker 468: 2517\n", + "Speaker 469: 606\n", + "Speaker 470: 1789\n", + "Speaker 471: 5206\n", + "Speaker 472: 6139\n", + "Speaker 473: 7783\n", + "Speaker 474: 1806\n", + "Speaker 475: 1028\n", + "Speaker 476: 4860\n", + "Speaker 477: 4859\n", + "Speaker 478: 6006\n", + "Speaker 479: 6157\n", + "Speaker 480: 2960\n", + "Speaker 481: 2092\n", + "Speaker 482: 8687\n", + "Speaker 483: 968\n", + "Speaker 484: 6060\n", + "Speaker 485: 8142\n", + "Speaker 486: 1943\n", + "Speaker 487: 6352\n", + "Speaker 488: 79\n", + "Speaker 489: 459\n", + "Speaker 490: 2364\n", + "Speaker 491: 3259\n", + "Speaker 492: 6288\n", + "Speaker 493: 3235\n", + "Speaker 494: 2269\n", + "Speaker 495: 1513\n", + "Speaker 496: 6341\n", + "Speaker 497: 1769\n", + "Speaker 498: 8474\n", + "Speaker 499: 534\n", + "Speaker 500: 1849\n", + "Speaker 501: 7294\n", + "Speaker 502: 5401\n", + "Speaker 503: 6865\n", + "Speaker 504: 8591\n", + "Speaker 505: 1859\n", + "Speaker 506: 56\n", + "Speaker 507: 101\n", + "Speaker 508: 4813\n", + "Speaker 509: 458\n", + "Speaker 510: 1498\n", + "Speaker 511: 1121\n", + "Speaker 512: 8404\n", + "Speaker 513: 1323\n", + "Speaker 514: 4734\n", + "Speaker 515: 6385\n", + "Speaker 516: 7874\n", + "Speaker 517: 1734\n", + "Speaker 518: 7981\n", + "Speaker 519: 5239\n", + "Speaker 520: 6160\n", + "Speaker 521: 201\n", + "Speaker 522: 7754\n", + "Speaker 523: 2204\n", + "Speaker 524: 986\n", + "Speaker 525: 2785\n", + "Speaker 526: 1018\n", + "Speaker 527: 4054\n", + "Speaker 528: 3307\n", + "Speaker 529: 5339\n", + "Speaker 530: 298\n", + "Speaker 531: 1296\n", + "Speaker 532: 4195\n", + "Speaker 533: 1054\n", + "Speaker 534: 2074\n", + "Speaker 535: 815\n", + "Speaker 536: 7665\n", + "Speaker 537: 594\n", + "Speaker 538: 119\n", + "Speaker 539: 8545\n", + "Speaker 540: 5261\n", + "Speaker 541: 2696\n", + "Speaker 542: 1944\n", + "Speaker 543: 7949\n", + "Speaker 544: 8152\n", + "Speaker 545: 820\n", + "Speaker 546: 7095\n", + "Speaker 547: 2159\n", + "Speaker 548: 4289\n", + "Speaker 549: 6014\n", + "Speaker 550: 7460\n", + "Speaker 551: 150\n", + "Speaker 552: 2893\n", + "Speaker 553: 2769\n", + "Speaker 554: 8479\n", + "Speaker 555: 8747\n", + "Speaker 556: 5789\n", + "Speaker 557: 6082\n", + "Speaker 558: 1641\n", + "Speaker 559: 3825\n", + "Speaker 560: 6119\n", + "Speaker 561: 7867\n", + "Speaker 562: 318\n", + "Speaker 563: 39\n", + "Speaker 564: 4837\n", + "Speaker 565: 7868\n", + "Speaker 566: 7498\n", + "Speaker 567: 2053\n", + "Speaker 568: 14\n", + "Speaker 569: 8820\n", + "Speaker 570: 7313\n", + "Speaker 571: 7383\n", + "Speaker 572: 1553\n", + "Speaker 573: 5727\n", + "Speaker 574: 2494\n", + "Speaker 575: 1079\n", + "Speaker 576: 2592\n", + "Speaker 577: 1678\n", + "Speaker 578: 2589\n", + "Speaker 579: 696\n", + "Speaker 580: 3224\n", + "Speaker 581: 4719\n", + "Speaker 582: 3972\n", + "Speaker 583: 731\n", + "Speaker 584: 1290\n", + "Speaker 585: 2319\n", + "Speaker 586: 1933\n", + "Speaker 587: 7126\n", + "Speaker 588: 803\n", + "Speaker 589: 4057\n", + "Speaker 590: 2628\n", + "Speaker 591: 2436\n", + "Speaker 592: 6788\n", + "Speaker 593: 3379\n", + "Speaker 594: 948\n", + "Speaker 595: 5712\n", + "Speaker 596: 5242\n", + "Speaker 597: 7140\n", + "Speaker 598: 7078\n", + "Speaker 599: 5655\n", + "Speaker 600: 4957\n", + "Speaker 601: 1607\n", + "Speaker 602: 78\n", + "Speaker 603: 2652\n", + "Speaker 604: 5984\n", + "Speaker 605: 953\n", + "Speaker 606: 7169\n", + "Speaker 607: 500\n", + "Speaker 608: 5093\n", + "Speaker 609: 8300\n", + "Speaker 610: 3171\n", + "Speaker 611: 8498\n", + "Speaker 612: 5561\n", + "Speaker 613: 7538\n", + "Speaker 614: 7789\n", + "Speaker 615: 451\n", + "Speaker 616: 4111\n", + "Speaker 617: 3180\n", + "Speaker 618: 6378\n", + "Speaker 619: 2989\n", + "Speaker 620: 6574\n", + "Speaker 621: 4406\n", + "Speaker 622: 208\n", + "Speaker 623: 248\n", + "Speaker 624: 274\n", + "Speaker 625: 3157\n", + "Speaker 626: 6836\n", + "Speaker 627: 5333\n", + "Speaker 628: 7445\n", + "Speaker 629: 5456\n", + "Speaker 630: 3654\n", + "Speaker 631: 5246\n", + "Speaker 632: 6099\n", + "Speaker 633: 4297\n", + "Speaker 634: 7688\n", + "Speaker 635: 839\n", + "Speaker 636: 1571\n", + "Speaker 637: 1390\n", + "Speaker 638: 2764\n", + "Speaker 639: 7000\n", + "Speaker 640: 2294\n", + "Speaker 641: 289\n", + "Speaker 642: 3922\n", + "Speaker 643: 8797\n", + "Speaker 644: 6518\n", + "Speaker 645: 8630\n", + "Speaker 646: 2929\n", + "Speaker 647: 7967\n", + "Speaker 648: 6286\n", + "Speaker 649: 6620\n", + "Speaker 650: 1379\n", + "Speaker 651: 405\n", + "Speaker 652: 8329\n", + "Speaker 653: 8838\n", + "Speaker 654: 3493\n", + "Speaker 655: 6167\n", + "Speaker 656: 7241\n", + "Speaker 657: 6308\n", + "Speaker 658: 4039\n", + "Speaker 659: 3982\n", + "Speaker 660: 770\n", + "Speaker 661: 8324\n", + "Speaker 662: 7777\n", + "Speaker 663: 2299\n", + "Speaker 664: 3446\n", + "Speaker 665: 2004\n", + "Speaker 666: 6359\n", + "Speaker 667: 6818\n", + "Speaker 668: 203\n", + "Speaker 669: 8609\n", + "Speaker 670: 1777\n", + "Speaker 671: 2007\n", + "Speaker 672: 3185\n", + "Speaker 673: 9022\n", + "Speaker 674: 3328\n", + "Speaker 675: 6763\n", + "Speaker 676: 6918\n", + "Speaker 677: 637\n", + "Speaker 678: 5054\n", + "Speaker 679: 4358\n", + "Speaker 680: 2758\n", + "Speaker 681: 3105\n", + "Speaker 682: 5400\n", + "Speaker 683: 5337\n", + "Speaker 684: 2254\n", + "Speaker 685: 3242\n", + "Speaker 686: 8176\n", + "Speaker 687: 19\n", + "Speaker 688: 4481\n", + "Speaker 689: 4899\n", + "Speaker 690: 1460\n", + "Speaker 691: 7657\n", + "Speaker 692: 4640\n", + "Speaker 693: 6696\n", + "Speaker 694: 7402\n", + "Speaker 695: 920\n", + "Speaker 696: 1311\n", + "Speaker 697: 83\n", + "Speaker 698: 225\n", + "Speaker 699: 224\n", + "Speaker 700: 6426\n", + "Speaker 701: 7910\n", + "Speaker 702: 8848\n", + "Speaker 703: 7247\n", + "Speaker 704: 408\n", + "Speaker 705: 8825\n", + "Speaker 706: 2481\n", + "Speaker 707: 7938\n", + "Speaker 708: 307\n", + "Speaker 709: 475\n", + "Speaker 710: 4598\n", + "Speaker 711: 3119\n", + "Speaker 712: 1081\n", + "Speaker 713: 6690\n", + "Speaker 714: 2012\n", + "Speaker 715: 1594\n", + "Speaker 716: 192\n", + "Speaker 717: 115\n", + "Speaker 718: 8088\n", + "Speaker 719: 81\n", + "Speaker 720: 8095\n", + "Speaker 721: 1779\n", + "Speaker 722: 5684\n", + "Speaker 723: 6510\n", + "Speaker 724: 6032\n", + "Speaker 725: 3728\n", + "Speaker 726: 1116\n", + "Speaker 727: 8138\n", + "Speaker 728: 5808\n", + "Speaker 729: 4397\n", + "Speaker 730: 2368\n", + "Speaker 731: 5968\n", + "Speaker 732: 5588\n", + "Speaker 733: 7148\n", + "Speaker 734: 6981\n", + "Speaker 735: 6395\n", + "Speaker 736: 1313\n", + "Speaker 737: 200\n", + "Speaker 738: 7525\n", + "Speaker 739: 8075\n", + "Speaker 740: 1212\n", + "Speaker 741: 3835\n", + "Speaker 742: 1100\n", + "Speaker 743: 7314\n", + "Speaker 744: 8459\n", + "Speaker 745: 7342\n", + "Speaker 746: 5147\n", + "Speaker 747: 6181\n", + "Speaker 748: 6037\n", + "Speaker 749: 2473\n", + "Speaker 750: 2971\n", + "Speaker 751: 2156\n", + "Speaker 752: 7705\n", + "Speaker 753: 6937\n", + "Speaker 754: 22\n", + "Speaker 755: 666\n", + "Speaker 756: 3945\n", + "Speaker 757: 1335\n", + "Speaker 758: 5618\n", + "Speaker 759: 559\n", + "Speaker 760: 340\n", + "Speaker 761: 7828\n", + "Speaker 762: 7229\n", + "Speaker 763: 1165\n", + "Speaker 764: 8225\n", + "Speaker 765: 2843\n", + "Speaker 766: 1536\n", + "Speaker 767: 8573\n", + "Speaker 768: 3927\n", + "Speaker 769: 8875\n", + "Speaker 770: 636\n", + "Speaker 771: 597\n", + "Speaker 772: 4356\n", + "Speaker 773: 176\n", + "Speaker 774: 434\n", + "Speaker 775: 1343\n", + "Speaker 776: 332\n", + "Speaker 777: 4260\n", + "Speaker 778: 2775\n", + "Speaker 779: 17\n", + "Speaker 780: 3905\n", + "Speaker 781: 7717\n", + "Speaker 782: 198\n", + "Speaker 783: 6529\n", + "Speaker 784: 8580\n", + "Speaker 785: 1885\n", + "Speaker 786: 7932\n", + "Speaker 787: 5778\n", + "Speaker 788: 7518\n", + "Speaker 789: 4519\n", + "Speaker 790: 3792\n", + "Speaker 791: 5029\n", + "Speaker 792: 3857\n", + "Speaker 793: 949\n", + "Speaker 794: 8421\n", + "Speaker 795: 1455\n", + "Speaker 796: 5717\n", + "Speaker 797: 3781\n", + "Speaker 798: 7134\n", + "Speaker 799: 7732\n", + "Speaker 800: 576\n", + "Speaker 801: 8226\n", + "Speaker 802: 7226\n", + "Speaker 803: 1098\n", + "Speaker 804: 7780\n", + "Speaker 805: 5723\n", + "Speaker 806: 7553\n", + "Speaker 807: 5740\n", + "Speaker 808: 1992\n", + "Speaker 809: 4441\n", + "Speaker 810: 28\n", + "Speaker 811: 7302\n", + "Speaker 812: 4214\n", + "Speaker 813: 7957\n", + "Speaker 814: 1363\n", + "Speaker 815: 6098\n", + "Speaker 816: 4848\n", + "Speaker 817: 1365\n", + "Speaker 818: 2093\n", + "Speaker 819: 1265\n", + "Speaker 820: 1578\n", + "Speaker 821: 4278\n", + "Speaker 822: 3816\n", + "Speaker 823: 1752\n", + "Speaker 824: 7495\n", + "Speaker 825: 1183\n", + "Speaker 826: 1645\n", + "Speaker 827: 698\n", + "Speaker 828: 2060\n", + "Speaker 829: 7318\n", + "Speaker 830: 112\n", + "Speaker 831: 4088\n", + "Speaker 832: 7859\n", + "Speaker 833: 7447\n", + "Speaker 834: 3009\n", + "Speaker 835: 246\n", + "Speaker 836: 1754\n", + "Speaker 837: 480\n", + "Speaker 838: 403\n", + "Speaker 839: 3368\n", + "Speaker 840: 446\n", + "Speaker 841: 2774\n", + "Speaker 842: 2498\n", + "Speaker 843: 3540\n", + "Speaker 844: 6300\n", + "Speaker 845: 6458\n", + "Speaker 846: 8401\n", + "Speaker 847: 2577\n", + "Speaker 848: 7635\n", + "Speaker 849: 1040\n", + "Speaker 850: 6215\n", + "Speaker 851: 6727\n", + "Speaker 852: 6406\n", + "Speaker 853: 2853\n", + "Speaker 854: 6317\n", + "Speaker 855: 7113\n", + "Speaker 856: 8425\n", + "Speaker 857: 6694\n", + "Speaker 858: 882\n", + "Speaker 859: 100\n", + "Speaker 860: 6454\n", + "Speaker 861: 3584\n", + "Speaker 862: 2817\n", + "Speaker 863: 667\n", + "Speaker 864: 2229\n", + "Speaker 865: 3851\n", + "Speaker 866: 4133\n", + "Speaker 867: 5656\n", + "Speaker 868: 278\n", + "Speaker 869: 1535\n", + "Speaker 870: 1259\n", + "Speaker 871: 7128\n", + "Speaker 872: 296\n", + "Speaker 873: 2061\n", + "Speaker 874: 5393\n", + "Speaker 875: 3221\n", + "Speaker 876: 30\n", + "Speaker 877: 188\n", + "Speaker 878: 1445\n", + "Speaker 879: 2393\n", + "Speaker 880: 6956\n", + "Speaker 881: 5163\n", + "Speaker 882: 549\n", + "Speaker 883: 2518\n", + "Speaker 884: 829\n", + "Speaker 885: 6567\n", + "Speaker 886: 8592\n", + "Speaker 887: 303\n", + "Speaker 888: 240\n", + "Speaker 889: 3380\n", + "Speaker 890: 7481\n", + "Speaker 891: 5883\n", + "Speaker 892: 3214\n", + "Speaker 893: 8855\n", + "Speaker 894: 3947\n", + "Speaker 895: 398\n", + "Speaker 896: 55\n", + "Speaker 897: 8722\n", + "Speaker 898: 8713\n", + "Speaker 899: 5868\n", + "Speaker 900: 979\n", + "Speaker 901: 209\n", + "Speaker 902: 2673\n", + "Speaker 903: 3340\n", + "Speaker 904: 126\n", + "Speaker 905: 612\n", + "Speaker 906: 580\n", + "Speaker 907: 1182\n", + "Speaker 908: 664\n", + "Speaker 909: 1246\n", + "Speaker 910: 5678\n", + "Speaker 911: 1487\n", + "Speaker 912: 9026\n", + "Speaker 913: 2196\n", + "Speaker 914: 6235\n", + "Speaker 915: 2952\n", + "Speaker 916: 3733\n", + "Speaker 917: 2790\n", + "Speaker 918: 6339\n", + "Speaker 919: 5489\n", + "Speaker 920: 7505\n", + "Speaker 921: 7190\n", + "Speaker 922: 7059\n", + "Speaker 923: 175\n", + "Speaker 924: 5390\n", + "Speaker 925: 6373\n", + "Speaker 926: 6895\n", + "Speaker 927: 4148\n", + "Speaker 928: 93\n", + "Speaker 929: 339\n", + "Speaker 930: 8113\n", + "Speaker 931: 7478\n", + "Speaker 932: 439\n", + "Speaker 933: 6209\n", + "Speaker 934: 6553\n", + "Speaker 935: 5660\n", + "Speaker 936: 716\n", + "Speaker 937: 6643\n", + "Speaker 938: 4788\n", + "Speaker 939: 114\n", + "Speaker 940: 492\n", + "Speaker 941: 5909\n", + "Speaker 942: 1482\n", + "Speaker 943: 38\n", + "Speaker 944: 5448\n", + "Speaker 945: 98\n", + "Speaker 946: 159\n", + "Speaker 947: 718\n", + "Speaker 948: 922\n", + "Speaker 949: 7258\n", + "Speaker 950: 6388\n", + "Speaker 951: 7178\n", + "Speaker 952: 7558\n", + "Speaker 953: 899\n", + "Speaker 954: 373\n", + "Speaker 955: 87\n", + "Speaker 956: 3526\n", + "Speaker 957: 3864\n", + "Speaker 958: 3370\n", + "Speaker 959: 1826\n", + "Speaker 960: 7739\n", + "Speaker 961: 6575\n", + "Speaker 962: 501\n", + "Speaker 963: 909\n", + "Speaker 964: 3112\n", + "Speaker 965: 7240\n", + "Speaker 966: 699\n", + "Speaker 967: 4595\n", + "Speaker 968: 5746\n", + "Speaker 969: 4856\n", + "Speaker 970: 1629\n", + "Speaker 971: 707\n", + "Speaker 972: 589\n", + "Speaker 973: 1638\n", + "Speaker 974: 830\n", + "Speaker 975: 3989\n", + "Speaker 976: 8066\n", + "Speaker 977: 7416\n", + "Speaker 978: 70\n", + "Speaker 979: 6993\n", + "Speaker 980: 3790\n", + "Speaker 981: 3490\n", + "Speaker 982: 8684\n", + "Speaker 983: 166\n", + "Speaker 984: 6505\n", + "Speaker 985: 2911\n", + "Speaker 986: 2127\n", + "Speaker 987: 2146\n", + "Speaker 988: 3664\n", + "Speaker 989: 7995\n", + "Speaker 990: 8725\n", + "Speaker 991: 4340\n", + "Speaker 992: 8006\n", + "Speaker 993: 4973\n", + "Speaker 994: 2910\n", + "Speaker 995: 497\n", + "Speaker 996: 5876\n", + "Speaker 997: 6233\n", + "Speaker 998: 3537\n", + "Speaker 999: 1413\n", + "Speaker 1000: 5189\n", + "Speaker 1001: 204\n", + "Speaker 1002: 836\n", + "Speaker 1003: 2618\n", + "Speaker 1004: 7276\n", + "Speaker 1005: 1264\n", + "Speaker 1006: 2045\n", + "Speaker 1007: 3215\n", + "Speaker 1008: 6555\n", + "Speaker 1009: 196\n", + "Speaker 1010: 6848\n", + "Speaker 1011: 1160\n", + "Speaker 1012: 8771\n", + "Speaker 1013: 4744\n", + "Speaker 1014: 6637\n", + "Speaker 1015: 1463\n", + "Speaker 1016: 3615\n", + "Speaker 1017: 5776\n", + "Speaker 1018: 26\n", + "Speaker 1019: 7339\n", + "Speaker 1020: 249\n", + "Speaker 1021: 1034\n", + "Speaker 1022: 1743\n", + "Speaker 1023: 207\n", + "Speaker 1024: 831\n", + "Speaker 1025: 4335\n", + "Speaker 1026: 7720\n", + "Speaker 1027: 2194\n", + "Speaker 1028: 688\n", + "Speaker 1029: 8619\n", + "Speaker 1030: 8718\n", + "Speaker 1031: 581\n", + "Speaker 1032: 835\n", + "Speaker 1033: 7881\n", + "Speaker 1034: 3607\n", + "Speaker 1035: 7933\n", + "Speaker 1036: 708\n", + "Speaker 1037: 7188\n", + "Speaker 1038: 4246\n", + "Speaker 1039: 1926\n", + "Speaker 1040: 7766\n", + "Speaker 1041: 6538\n", + "Speaker 1042: 2149\n", + "Speaker 1043: 7434\n", + "Speaker 1044: 3230\n", + "Speaker 1045: 3983\n", + "Speaker 1046: 4152\n", + "Speaker 1047: 1336\n", + "Speaker 1048: 2388\n", + "Speaker 1049: 5139\n", + "Speaker 1050: 1473\n", + "Speaker 1051: 868\n", + "Speaker 1052: 2709\n", + "Speaker 1053: 2674\n", + "Speaker 1054: 2570\n", + "Speaker 1055: 211\n", + "Speaker 1056: 4137\n", + "Speaker 1057: 472\n", + "Speaker 1058: 5022\n", + "Speaker 1059: 1263\n", + "Speaker 1060: 1801\n", + "Speaker 1061: 1963\n", + "Speaker 1062: 5386\n", + "Speaker 1063: 3274\n", + "Speaker 1064: 3070\n", + "Speaker 1065: 3436\n", + "Speaker 1066: 8347\n", + "Speaker 1067: 7245\n", + "Speaker 1068: 3240\n", + "Speaker 1069: 7555\n", + "Speaker 1070: 6081\n", + "Speaker 1071: 5914\n", + "Speaker 1072: 1827\n", + "Speaker 1073: 8238\n", + "Speaker 1074: 2256\n", + "Speaker 1075: 7139\n", + "Speaker 1076: 1668\n", + "Speaker 1077: 4108\n", + "Speaker 1078: 7809\n", + "Speaker 1079: 2384\n", + "Speaker 1080: 4806\n", + "Speaker 1081: 3830\n", + "Speaker 1082: 3889\n", + "Speaker 1083: 217\n", + "Speaker 1084: 3645\n", + "Speaker 1085: 205\n", + "Speaker 1086: 6476\n", + "Speaker 1087: 4590\n", + "Speaker 1088: 6563\n", + "Speaker 1089: 2416\n", + "Speaker 1090: 8183\n", + "Speaker 1091: 8975\n", + "Speaker 1092: 4257\n", + "Speaker 1093: 1425\n", + "Speaker 1094: 8014\n", + "Speaker 1095: 5190\n", + "Speaker 1096: 4160\n", + "Speaker 1097: 1069\n", + "Speaker 1098: 1923\n", + "Speaker 1099: 4110\n", + "Speaker 1100: 1235\n", + "Speaker 1101: 5049\n", + "Speaker 1102: 479\n", + "Speaker 1103: 2573\n", + "Speaker 1104: 4331\n", + "Speaker 1105: 6828\n", + "Speaker 1106: 380\n", + "Speaker 1107: 7395\n", + "Speaker 1108: 3638\n", + "Speaker 1109: 6104\n", + "Speaker 1110: 4681\n", + "Speaker 1111: 8824\n", + "Speaker 1112: 2085\n", + "Speaker 1113: 6038\n", + "Speaker 1114: 7475\n", + "Speaker 1115: 8490\n", + "Speaker 1116: 3486\n", + "Speaker 1117: 6258\n", + "Speaker 1118: 2999\n", + "Speaker 1119: 8228\n", + "Speaker 1120: 1387\n", + "Speaker 1121: 8028\n", + "Speaker 1122: 1060\n", + "Speaker 1123: 3869\n", + "Speaker 1124: 8410\n", + "Speaker 1125: 2532\n", + "Speaker 1126: 2562\n", + "Speaker 1127: 1383\n", + "Speaker 1128: 7120\n", + "Speaker 1129: 1974\n", + "Speaker 1130: 1603\n", + "Speaker 1131: 8388\n", + "Speaker 1132: 8506\n", + "Speaker 1133: 337\n", + "Speaker 1134: 4222\n", + "Speaker 1135: 6371\n", + "Speaker 1136: 5039\n", + "Speaker 1137: 5867\n", + "Speaker 1138: 3876\n", + "Speaker 1139: 5293\n", + "Speaker 1140: 103\n", + "Speaker 1141: 64\n", + "Speaker 1142: 7800\n", + "Speaker 1143: 1447\n", + "Speaker 1144: 154\n", + "Speaker 1145: 460\n", + "Speaker 1146: 2514\n", + "Speaker 1147: 2816\n", + "Speaker 1148: 27\n", + "Speaker 1149: 242\n", + "Speaker 1150: 3738\n" + ] + } + ], + "source": [ + "data_dir = 'LibriTTS'\n", + "mels_mode_dict = dict()\n", + "lens_dict = dict()\n", + "for p in phoneme_list:\n", + " mels_mode_dict[p] = []\n", + " lens_dict[p] = []\n", + "speakers = os.listdir(os.path.join(data_dir, 'mels'))\n", + "for s, speaker in enumerate(speakers):\n", + " print('Speaker %d: %s' % (s + 1, speaker))\n", + " textgrids = os.listdir(os.path.join(data_dir, 'textgrids', speaker))\n", + " for textgrid in textgrids:\n", + " t = tgt.io.read_textgrid(os.path.join(data_dir, 'textgrids', speaker, textgrid))\n", + " m = np.load(os.path.join(data_dir, 'mels', speaker, textgrid.replace('.TextGrid', '_mel.npy')))\n", + " t = t.get_tier_by_name('phones')\n", + " for i in range(len(t)):\n", + " phoneme = t[i].text\n", + " start_frame = int(t[i].start_time * 22050.0) // 256\n", + " end_frame = int(t[i].end_time * 22050.0) // 256 + 1\n", + " mels_mode_dict[phoneme] += [np.round(np.median(m[:, start_frame:end_frame], 1), 1)]\n", + " lens_dict[phoneme] += [end_frame - start_frame]\n", + "\n", + "mels_mode = dict()\n", + "lens = dict()\n", + "for p in phoneme_list:\n", + " mels_mode[p] = mode(np.asarray(mels_mode_dict[p]), 0).mode[0]\n", + " lens[p] = np.mean(np.asarray(lens_dict[p]))\n", + "del mels_mode_dict\n", + "del lens_dict" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Speaker 1: 8057\n", + "Speaker 2: 4014\n", + "Speaker 3: 6415\n", + "Speaker 4: 5126\n", + "Speaker 5: 3723\n", + "Speaker 6: 587\n", + "Speaker 7: 8534\n", + "Speaker 8: 5322\n", + "Speaker 9: 2238\n", + "Speaker 10: 1401\n", + "Speaker 11: 4427\n", + "Speaker 12: 1705\n", + "Speaker 13: 561\n", + "Speaker 14: 2992\n", + "Speaker 15: 8776\n", + "Speaker 16: 54\n", + "Speaker 17: 806\n", + "Speaker 18: 1970\n", + "Speaker 19: 302\n", + "Speaker 20: 6272\n", + "Speaker 21: 1289\n", + "Speaker 22: 3807\n", + "Speaker 23: 6075\n", + "Speaker 24: 329\n", + "Speaker 25: 3483\n", + "Speaker 26: 1914\n", + "Speaker 27: 6499\n", + "Speaker 28: 7117\n", + "Speaker 29: 5703\n", + "Speaker 30: 3032\n", + "Speaker 31: 3001\n", + "Speaker 32: 5304\n", + "Speaker 33: 5012\n", + "Speaker 34: 8786\n", + "Speaker 35: 3187\n", + "Speaker 36: 5935\n", + "Speaker 37: 1088\n", + "Speaker 38: 783\n", + "Speaker 39: 5186\n", + "Speaker 40: 7994\n", + "Speaker 41: 6078\n", + "Speaker 42: 3168\n", + "Speaker 43: 6550\n", + "Speaker 44: 6701\n", + "Speaker 45: 4926\n", + "Speaker 46: 1355\n", + "Speaker 47: 1337\n", + "Speaker 48: 2582\n", + "Speaker 49: 8119\n", + "Speaker 50: 5767\n", + "Speaker 51: 1112\n", + "Speaker 52: 6054\n", + "Speaker 53: 5583\n", + "Speaker 54: 6120\n", + "Speaker 55: 4290\n", + "Speaker 56: 3440\n", + "Speaker 57: 2230\n", + "Speaker 58: 5802\n", + "Speaker 59: 3448\n", + "Speaker 60: 730\n", + "Speaker 61: 7011\n", + "Speaker 62: 40\n", + "Speaker 63: 1845\n", + "Speaker 64: 7816\n", + "Speaker 65: 4010\n", + "Speaker 66: 2823\n", + "Speaker 67: 511\n", + "Speaker 68: 229\n", + "Speaker 69: 5319\n", + "Speaker 70: 4830\n", + "Speaker 71: 8494\n", + "Speaker 72: 1509\n", + "Speaker 73: 7285\n", + "Speaker 74: 1226\n", + "Speaker 75: 2638\n", + "Speaker 76: 2920\n", + "Speaker 77: 7367\n", + "Speaker 78: 2598\n", + "Speaker 79: 3686\n", + "Speaker 80: 412\n", + "Speaker 81: 5538\n", + "Speaker 82: 663\n", + "Speaker 83: 6683\n", + "Speaker 84: 1271\n", + "Speaker 85: 5514\n", + "Speaker 86: 8699\n", + "Speaker 87: 7264\n", + "Speaker 88: 816\n", + "Speaker 89: 5092\n", + "Speaker 90: 7752\n", + "Speaker 91: 3008\n", + "Speaker 92: 5688\n", + "Speaker 93: 3513\n", + "Speaker 94: 1224\n", + "Speaker 95: 8312\n", + "Speaker 96: 8791\n", + "Speaker 97: 5104\n", + "Speaker 98: 5266\n", + "Speaker 99: 1392\n", + "Speaker 100: 3549\n", + "Speaker 101: 7945\n", + "Speaker 102: 272\n", + "Speaker 103: 5940\n", + "Speaker 104: 6437\n", + "Speaker 105: 2531\n", + "Speaker 106: 6509\n", + "Speaker 107: 4064\n", + "Speaker 108: 2167\n", + "Speaker 109: 3630\n", + "Speaker 110: 4018\n", + "Speaker 111: 8770\n", + "Speaker 112: 8163\n", + "Speaker 113: 5809\n", + "Speaker 114: 510\n", + "Speaker 115: 5007\n", + "Speaker 116: 4967\n", + "Speaker 117: 8396\n", + "Speaker 118: 359\n", + "Speaker 119: 5622\n", + "Speaker 120: 3521\n", + "Speaker 121: 3923\n", + "Speaker 122: 1382\n", + "Speaker 123: 1012\n", + "Speaker 124: 7939\n", + "Speaker 125: 4839\n", + "Speaker 126: 1175\n", + "Speaker 127: 2836\n", + "Speaker 128: 4853\n", + "Speaker 129: 639\n", + "Speaker 130: 4236\n", + "Speaker 131: 2654\n", + "Speaker 132: 3866\n", + "Speaker 133: 335\n", + "Speaker 134: 3551\n", + "Speaker 135: 1046\n", + "Speaker 136: 6147\n", + "Speaker 137: 157\n", + "Speaker 138: 3094\n", + "Speaker 139: 2427\n", + "Speaker 140: 8195\n", + "Speaker 141: 4238\n", + "Speaker 142: 4854\n", + "Speaker 143: 7832\n", + "Speaker 144: 1748\n", + "Speaker 145: 4586\n", + "Speaker 146: 7484\n", + "Speaker 147: 1825\n", + "Speaker 148: 669\n", + "Speaker 149: 512\n", + "Speaker 150: 4433\n", + "Speaker 151: 3374\n", + "Speaker 152: 6064\n", + "Speaker 153: 2201\n", + "Speaker 154: 6519\n", + "Speaker 155: 323\n", + "Speaker 156: 7515\n", + "Speaker 157: 1316\n", + "Speaker 158: 3717\n", + "Speaker 159: 4362\n", + "Speaker 160: 89\n", + "Speaker 161: 5810\n", + "Speaker 162: 8050\n", + "Speaker 163: 1025\n", + "Speaker 164: 7991\n", + "Speaker 165: 4495\n", + "Speaker 166: 3003\n", + "Speaker 167: 1001\n", + "Speaker 168: 4243\n", + "Speaker 169: 7069\n", + "Speaker 170: 593\n", + "Speaker 171: 1913\n", + "Speaker 172: 1058\n", + "Speaker 173: 4363\n", + "Speaker 174: 2056\n", + "Speaker 175: 4535\n", + "Speaker 176: 4138\n", + "Speaker 177: 2751\n", + "Speaker 178: 6367\n", + "Speaker 179: 6904\n", + "Speaker 180: 8677\n", + "Speaker 181: 5123\n", + "Speaker 182: 7520\n", + "Speaker 183: 6019\n", + "Speaker 184: 6294\n", + "Speaker 185: 1811\n", + "Speaker 186: 4226\n", + "Speaker 187: 6206\n", + "Speaker 188: 5062\n", + "Speaker 189: 16\n", + "Speaker 190: 6877\n", + "Speaker 191: 163\n", + "Speaker 192: 3114\n", + "Speaker 193: 7956\n", + "Speaker 194: 5002\n", + "Speaker 195: 957\n", + "Speaker 196: 8635\n", + "Speaker 197: 3977\n", + "Speaker 198: 3389\n", + "Speaker 199: 1639\n", + "Speaker 200: 1552\n", + "Speaker 201: 925\n", + "Speaker 202: 6115\n", + "Speaker 203: 2162\n", + "Speaker 204: 8051\n", + "Speaker 205: 8098\n", + "Speaker 206: 2581\n", + "Speaker 207: 4381\n", + "Speaker 208: 125\n", + "Speaker 209: 3046\n", + "Speaker 210: 6544\n", + "Speaker 211: 7594\n", + "Speaker 212: 2136\n", + "Speaker 213: 250\n", + "Speaker 214: 9023\n", + "Speaker 215: 4438\n", + "Speaker 216: 954\n", + "Speaker 217: 6189\n", + "Speaker 218: 7569\n", + "Speaker 219: 5389\n", + "Speaker 220: 6924\n", + "Speaker 221: 226\n", + "Speaker 222: 118\n", + "Speaker 223: 476\n", + "Speaker 224: 1556\n", + "Speaker 225: 4267\n", + "Speaker 226: 7316\n", + "Speaker 227: 409\n", + "Speaker 228: 7398\n", + "Speaker 229: 2182\n", + "Speaker 230: 8193\n", + "Speaker 231: 6782\n", + "Speaker 232: 3979\n", + "Speaker 233: 8879\n", + "Speaker 234: 90\n", + "Speaker 235: 850\n", + "Speaker 236: 4945\n", + "Speaker 237: 7833\n", + "Speaker 238: 583\n", + "Speaker 239: 6000\n", + "Speaker 240: 7030\n", + "Speaker 241: 1961\n", + "Speaker 242: 288\n", + "Speaker 243: 8190\n", + "Speaker 244: 7090\n", + "Speaker 245: 1867\n", + "Speaker 246: 2137\n", + "Speaker 247: 2110\n", + "Speaker 248: 233\n", + "Speaker 249: 5063\n", + "Speaker 250: 1776\n", + "Speaker 251: 7312\n", + "Speaker 252: 724\n", + "Speaker 253: 6673\n", + "Speaker 254: 1851\n", + "Speaker 255: 231\n", + "Speaker 256: 3082\n", + "Speaker 257: 8772\n", + "Speaker 258: 8629\n", + "Speaker 259: 32\n", + "Speaker 260: 4116\n", + "Speaker 261: 487\n", + "Speaker 262: 1649\n", + "Speaker 263: 1731\n", + "Speaker 264: 7909\n", + "Speaker 265: 3879\n", + "Speaker 266: 1903\n", + "Speaker 267: 8123\n", + "Speaker 268: 3699\n", + "Speaker 269: 1348\n", + "Speaker 270: 8118\n", + "Speaker 271: 2882\n", + "Speaker 272: 3083\n", + "Speaker 273: 7085\n", + "Speaker 274: 5154\n", + "Speaker 275: 1724\n", + "Speaker 276: 1446\n", + "Speaker 277: 1740\n", + "Speaker 278: 6080\n", + "Speaker 279: 6880\n", + "Speaker 280: 7384\n", + "Speaker 281: 1334\n", + "Speaker 282: 7540\n", + "Speaker 283: 5731\n", + "Speaker 284: 7517\n", + "Speaker 285: 4145\n", + "Speaker 286: 3852\n", + "Speaker 287: 4846\n", + "Speaker 288: 2812\n", + "Speaker 289: 1624\n", + "Speaker 290: 764\n", + "Speaker 291: 3482\n", + "Speaker 292: 2688\n", + "Speaker 293: 1422\n", + "Speaker 294: 1874\n", + "Speaker 295: 3072\n", + "Speaker 296: 7982\n", + "Speaker 297: 345\n", + "Speaker 298: 5115\n", + "Speaker 299: 7278\n", + "Speaker 300: 7730\n", + "Speaker 301: 5975\n", + "Speaker 302: 5985\n", + "Speaker 303: 1283\n", + "Speaker 304: 6492\n", + "Speaker 305: 5192\n", + "Speaker 306: 5463\n", + "Speaker 307: 8063\n", + "Speaker 308: 3025\n", + "Speaker 309: 5604\n", + "Speaker 310: 984\n", + "Speaker 311: 1241\n", + "Speaker 312: 1417\n", + "Speaker 313: 4731\n", + "Speaker 314: 8080\n", + "Speaker 315: 5672\n", + "Speaker 316: 3330\n", + "Speaker 317: 7286\n", + "Speaker 318: 1052\n", + "Speaker 319: 1502\n", + "Speaker 320: 4098\n", + "Speaker 321: 6925\n", + "Speaker 322: 4592\n", + "Speaker 323: 596\n", + "Speaker 324: 1448\n", + "Speaker 325: 6188\n", + "Speaker 326: 1841\n", + "Speaker 327: 4490\n", + "Speaker 328: 5157\n", + "Speaker 329: 3967\n", + "Speaker 330: 3994\n", + "Speaker 331: 1222\n", + "Speaker 332: 3228\n", + "Speaker 333: 254\n", + "Speaker 334: 60\n", + "Speaker 335: 3361\n", + "Speaker 336: 454\n", + "Speaker 337: 7837\n", + "Speaker 338: 8465\n", + "Speaker 339: 1093\n", + "Speaker 340: 2787\n", + "Speaker 341: 8097\n", + "Speaker 342: 7297\n", + "Speaker 343: 711\n", + "Speaker 344: 598\n", + "Speaker 345: 6330\n", + "Speaker 346: 8011\n", + "Speaker 347: 1322\n", + "Speaker 348: 6494\n", + "Speaker 349: 4425\n", + "Speaker 350: 374\n", + "Speaker 351: 1737\n", + "Speaker 352: 216\n", + "Speaker 353: 7061\n", + "Speaker 354: 2391\n", + "Speaker 355: 4051\n", + "Speaker 356: 2272\n", + "Speaker 357: 6497\n", + "Speaker 358: 2499\n", + "Speaker 359: 8108\n", + "Speaker 360: 7794\n", + "Speaker 361: 1349\n", + "Speaker 362: 6446\n", + "Speaker 363: 3118\n", + "Speaker 364: 8468\n", + "Speaker 365: 7051\n", + "Speaker 366: 2741\n", + "Speaker 367: 8705\n", + "Speaker 368: 2039\n", + "Speaker 369: 7802\n", + "Speaker 370: 5750\n", + "Speaker 371: 2285\n", + "Speaker 372: 4898\n", + "Speaker 373: 5606\n", + "Speaker 374: 5570\n", + "Speaker 375: 1898\n", + "Speaker 376: 2827\n", + "Speaker 377: 4434\n", + "Speaker 378: 3294\n", + "Speaker 379: 7825\n", + "Speaker 380: 2113\n", + "Speaker 381: 8266\n", + "Speaker 382: 322\n", + "Speaker 383: 6927\n", + "Speaker 384: 5652\n", + "Speaker 385: 6269\n", + "Speaker 386: 887\n", + "Speaker 387: 5133\n", + "Speaker 388: 4807\n", + "Speaker 389: 4733\n", + "Speaker 390: 7067\n", + "Speaker 391: 6531\n", + "Speaker 392: 8008\n", + "Speaker 393: 2512\n", + "Speaker 394: 1987\n", + "Speaker 395: 1195\n", + "Speaker 396: 7335\n", + "Speaker 397: 8758\n", + "Speaker 398: 2691\n", + "Speaker 399: 1061\n", + "Speaker 400: 5519\n", + "Speaker 401: 4680\n", + "Speaker 402: 3546\n", + "Speaker 403: 8419\n", + "Speaker 404: 1958\n", + "Speaker 405: 3289\n", + "Speaker 406: 5918\n", + "Speaker 407: 8887\n", + "Speaker 408: 8575\n", + "Speaker 409: 210\n", + "Speaker 410: 6965\n", + "Speaker 411: 258\n", + "Speaker 412: 2010\n", + "Speaker 413: 1066\n", + "Speaker 414: 7926\n", + "Speaker 415: 2401\n", + "Speaker 416: 3258\n", + "Speaker 417: 8742\n", + "Speaker 418: 1050\n", + "Speaker 419: 548\n", + "Speaker 420: 1027\n", + "Speaker 421: 8643\n", + "Speaker 422: 5513\n", + "Speaker 423: 3914\n", + "Speaker 424: 1053\n", + "Speaker 425: 7511\n", + "Speaker 426: 5290\n", + "Speaker 427: 4013\n", + "Speaker 428: 543\n", + "Speaker 429: 369\n", + "Speaker 430: 2404\n", + "Speaker 431: 4629\n", + "Speaker 432: 481\n", + "Speaker 433: 625\n", + "Speaker 434: 1472\n", + "Speaker 435: 7145\n", + "Speaker 436: 426\n", + "Speaker 437: 7647\n", + "Speaker 438: 2397\n", + "Speaker 439: 8464\n", + "Speaker 440: 6686\n", + "Speaker 441: 8222\n", + "Speaker 442: 2002\n", + "Speaker 443: 7437\n", + "Speaker 444: 1547\n", + "Speaker 445: 7733\n", + "Speaker 446: 8527\n", + "Speaker 447: 8194\n", + "Speaker 448: 911\n", + "Speaker 449: 3357\n", + "Speaker 450: 227\n", + "Speaker 451: 781\n", + "Speaker 452: 122\n", + "Speaker 453: 5635\n", + "Speaker 454: 362\n", + "Speaker 455: 353\n", + "Speaker 456: 7962\n", + "Speaker 457: 2240\n", + "Speaker 458: 8605\n", + "Speaker 459: 4044\n", + "Speaker 460: 7959\n", + "Speaker 461: 3703\n", + "Speaker 462: 311\n", + "Speaker 463: 2289\n", + "Speaker 464: 7704\n", + "Speaker 465: 4800\n", + "Speaker 466: 2411\n", + "Speaker 467: 5724\n", + "Speaker 468: 2517\n", + "Speaker 469: 606\n", + "Speaker 470: 1789\n", + "Speaker 471: 5206\n", + "Speaker 472: 6139\n", + "Speaker 473: 7783\n", + "Speaker 474: 1806\n", + "Speaker 475: 1028\n", + "Speaker 476: 4860\n", + "Speaker 477: 4859\n", + "Speaker 478: 6006\n", + "Speaker 479: 6157\n", + "Speaker 480: 2960\n", + "Speaker 481: 2092\n", + "Speaker 482: 8687\n", + "Speaker 483: 968\n", + "Speaker 484: 6060\n", + "Speaker 485: 8142\n", + "Speaker 486: 1943\n", + "Speaker 487: 6352\n", + "Speaker 488: 79\n", + "Speaker 489: 459\n", + "Speaker 490: 2364\n", + "Speaker 491: 3259\n", + "Speaker 492: 6288\n", + "Speaker 493: 3235\n", + "Speaker 494: 2269\n", + "Speaker 495: 1513\n", + "Speaker 496: 6341\n", + "Speaker 497: 1769\n", + "Speaker 498: 8474\n", + "Speaker 499: 534\n", + "Speaker 500: 1849\n", + "Speaker 501: 7294\n", + "Speaker 502: 5401\n", + "Speaker 503: 6865\n", + "Speaker 504: 8591\n", + "Speaker 505: 1859\n", + "Speaker 506: 56\n", + "Speaker 507: 101\n", + "Speaker 508: 4813\n", + "Speaker 509: 458\n", + "Speaker 510: 1498\n", + "Speaker 511: 1121\n", + "Speaker 512: 8404\n", + "Speaker 513: 1323\n", + "Speaker 514: 4734\n", + "Speaker 515: 6385\n", + "Speaker 516: 7874\n", + "Speaker 517: 1734\n", + "Speaker 518: 7981\n", + "Speaker 519: 5239\n", + "Speaker 520: 6160\n", + "Speaker 521: 201\n", + "Speaker 522: 7754\n", + "Speaker 523: 2204\n", + "Speaker 524: 986\n", + "Speaker 525: 2785\n", + "Speaker 526: 1018\n", + "Speaker 527: 4054\n", + "Speaker 528: 3307\n", + "Speaker 529: 5339\n", + "Speaker 530: 298\n", + "Speaker 531: 1296\n", + "Speaker 532: 4195\n", + "Speaker 533: 1054\n", + "Speaker 534: 2074\n", + "Speaker 535: 815\n", + "Speaker 536: 7665\n", + "Speaker 537: 594\n", + "Speaker 538: 119\n", + "Speaker 539: 8545\n", + "Speaker 540: 5261\n", + "Speaker 541: 2696\n", + "Speaker 542: 1944\n", + "Speaker 543: 7949\n", + "Speaker 544: 8152\n", + "Speaker 545: 820\n", + "Speaker 546: 7095\n", + "Speaker 547: 2159\n", + "Speaker 548: 4289\n", + "Speaker 549: 6014\n", + "Speaker 550: 7460\n", + "Speaker 551: 150\n", + "Speaker 552: 2893\n", + "Speaker 553: 2769\n", + "Speaker 554: 8479\n", + "Speaker 555: 8747\n", + "Speaker 556: 5789\n", + "Speaker 557: 6082\n", + "Speaker 558: 1641\n", + "Speaker 559: 3825\n", + "Speaker 560: 6119\n", + "Speaker 561: 7867\n", + "Speaker 562: 318\n", + "Speaker 563: 39\n", + "Speaker 564: 4837\n", + "Speaker 565: 7868\n", + "Speaker 566: 7498\n", + "Speaker 567: 2053\n", + "Speaker 568: 14\n", + "Speaker 569: 8820\n", + "Speaker 570: 7313\n", + "Speaker 571: 7383\n", + "Speaker 572: 1553\n", + "Speaker 573: 5727\n", + "Speaker 574: 2494\n", + "Speaker 575: 1079\n", + "Speaker 576: 2592\n", + "Speaker 577: 1678\n", + "Speaker 578: 2589\n", + "Speaker 579: 696\n", + "Speaker 580: 3224\n", + "Speaker 581: 4719\n", + "Speaker 582: 3972\n", + "Speaker 583: 731\n", + "Speaker 584: 1290\n", + "Speaker 585: 2319\n", + "Speaker 586: 1933\n", + "Speaker 587: 7126\n", + "Speaker 588: 803\n", + "Speaker 589: 4057\n", + "Speaker 590: 2628\n", + "Speaker 591: 2436\n", + "Speaker 592: 6788\n", + "Speaker 593: 3379\n", + "Speaker 594: 948\n", + "Speaker 595: 5712\n", + "Speaker 596: 5242\n", + "Speaker 597: 7140\n", + "Speaker 598: 7078\n", + "Speaker 599: 5655\n", + "Speaker 600: 4957\n", + "Speaker 601: 1607\n", + "Speaker 602: 78\n", + "Speaker 603: 2652\n", + "Speaker 604: 5984\n", + "Speaker 605: 953\n", + "Speaker 606: 7169\n", + "Speaker 607: 500\n", + "Speaker 608: 5093\n", + "Speaker 609: 8300\n", + "Speaker 610: 3171\n", + "Speaker 611: 8498\n", + "Speaker 612: 5561\n", + "Speaker 613: 7538\n", + "Speaker 614: 7789\n", + "Speaker 615: 451\n", + "Speaker 616: 4111\n", + "Speaker 617: 3180\n", + "Speaker 618: 6378\n", + "Speaker 619: 2989\n", + "Speaker 620: 6574\n", + "Speaker 621: 4406\n", + "Speaker 622: 208\n", + "Speaker 623: 248\n", + "Speaker 624: 274\n", + "Speaker 625: 3157\n", + "Speaker 626: 6836\n", + "Speaker 627: 5333\n", + "Speaker 628: 7445\n", + "Speaker 629: 5456\n", + "Speaker 630: 3654\n", + "Speaker 631: 5246\n", + "Speaker 632: 6099\n", + "Speaker 633: 4297\n", + "Speaker 634: 7688\n", + "Speaker 635: 839\n", + "Speaker 636: 1571\n", + "Speaker 637: 1390\n", + "Speaker 638: 2764\n", + "Speaker 639: 7000\n", + "Speaker 640: 2294\n", + "Speaker 641: 289\n", + "Speaker 642: 3922\n", + "Speaker 643: 8797\n", + "Speaker 644: 6518\n", + "Speaker 645: 8630\n", + "Speaker 646: 2929\n", + "Speaker 647: 7967\n", + "Speaker 648: 6286\n", + "Speaker 649: 6620\n", + "Speaker 650: 1379\n", + "Speaker 651: 405\n", + "Speaker 652: 8329\n", + "Speaker 653: 8838\n", + "Speaker 654: 3493\n", + "Speaker 655: 6167\n", + "Speaker 656: 7241\n", + "Speaker 657: 6308\n", + "Speaker 658: 4039\n", + "Speaker 659: 3982\n", + "Speaker 660: 770\n", + "Speaker 661: 8324\n", + "Speaker 662: 7777\n", + "Speaker 663: 2299\n", + "Speaker 664: 3446\n", + "Speaker 665: 2004\n", + "Speaker 666: 6359\n", + "Speaker 667: 6818\n", + "Speaker 668: 203\n", + "Speaker 669: 8609\n", + "Speaker 670: 1777\n", + "Speaker 671: 2007\n", + "Speaker 672: 3185\n", + "Speaker 673: 9022\n", + "Speaker 674: 3328\n", + "Speaker 675: 6763\n", + "Speaker 676: 6918\n", + "Speaker 677: 637\n", + "Speaker 678: 5054\n", + "Speaker 679: 4358\n", + "Speaker 680: 2758\n", + "Speaker 681: 3105\n", + "Speaker 682: 5400\n", + "Speaker 683: 5337\n", + "Speaker 684: 2254\n", + "Speaker 685: 3242\n", + "Speaker 686: 8176\n", + "Speaker 687: 19\n", + "Speaker 688: 4481\n", + "Speaker 689: 4899\n", + "Speaker 690: 1460\n", + "Speaker 691: 7657\n", + "Speaker 692: 4640\n", + "Speaker 693: 6696\n", + "Speaker 694: 7402\n", + "Speaker 695: 920\n", + "Speaker 696: 1311\n", + "Speaker 697: 83\n", + "Speaker 698: 225\n", + "Speaker 699: 224\n", + "Speaker 700: 6426\n", + "Speaker 701: 7910\n", + "Speaker 702: 8848\n", + "Speaker 703: 7247\n", + "Speaker 704: 408\n", + "Speaker 705: 8825\n", + "Speaker 706: 2481\n", + "Speaker 707: 7938\n", + "Speaker 708: 307\n", + "Speaker 709: 475\n", + "Speaker 710: 4598\n", + "Speaker 711: 3119\n", + "Speaker 712: 1081\n", + "Speaker 713: 6690\n", + "Speaker 714: 2012\n", + "Speaker 715: 1594\n", + "Speaker 716: 192\n", + "Speaker 717: 115\n", + "Speaker 718: 8088\n", + "Speaker 719: 81\n", + "Speaker 720: 8095\n", + "Speaker 721: 1779\n", + "Speaker 722: 5684\n", + "Speaker 723: 6510\n", + "Speaker 724: 6032\n", + "Speaker 725: 3728\n", + "Speaker 726: 1116\n", + "Speaker 727: 8138\n", + "Speaker 728: 5808\n", + "Speaker 729: 4397\n", + "Speaker 730: 2368\n", + "Speaker 731: 5968\n", + "Speaker 732: 5588\n", + "Speaker 733: 7148\n", + "Speaker 734: 6981\n", + "Speaker 735: 6395\n", + "Speaker 736: 1313\n", + "Speaker 737: 200\n", + "Speaker 738: 7525\n", + "Speaker 739: 8075\n", + "Speaker 740: 1212\n", + "Speaker 741: 3835\n", + "Speaker 742: 1100\n", + "Speaker 743: 7314\n", + "Speaker 744: 8459\n", + "Speaker 745: 7342\n", + "Speaker 746: 5147\n", + "Speaker 747: 6181\n", + "Speaker 748: 6037\n", + "Speaker 749: 2473\n", + "Speaker 750: 2971\n", + "Speaker 751: 2156\n", + "Speaker 752: 7705\n", + "Speaker 753: 6937\n", + "Speaker 754: 22\n", + "Speaker 755: 666\n", + "Speaker 756: 3945\n", + "Speaker 757: 1335\n", + "Speaker 758: 5618\n", + "Speaker 759: 559\n", + "Speaker 760: 340\n", + "Speaker 761: 7828\n", + "Speaker 762: 7229\n", + "Speaker 763: 1165\n", + "Speaker 764: 8225\n", + "Speaker 765: 2843\n", + "Speaker 766: 1536\n", + "Speaker 767: 8573\n", + "Speaker 768: 3927\n", + "Speaker 769: 8875\n", + "Speaker 770: 636\n", + "Speaker 771: 597\n", + "Speaker 772: 4356\n", + "Speaker 773: 176\n", + "Speaker 774: 434\n", + "Speaker 775: 1343\n", + "Speaker 776: 332\n", + "Speaker 777: 4260\n", + "Speaker 778: 2775\n", + "Speaker 779: 17\n", + "Speaker 780: 3905\n", + "Speaker 781: 7717\n", + "Speaker 782: 198\n", + "Speaker 783: 6529\n", + "Speaker 784: 8580\n", + "Speaker 785: 1885\n", + "Speaker 786: 7932\n", + "Speaker 787: 5778\n", + "Speaker 788: 7518\n", + "Speaker 789: 4519\n", + "Speaker 790: 3792\n", + "Speaker 791: 5029\n", + "Speaker 792: 3857\n", + "Speaker 793: 949\n", + "Speaker 794: 8421\n", + "Speaker 795: 1455\n", + "Speaker 796: 5717\n", + "Speaker 797: 3781\n", + "Speaker 798: 7134\n", + "Speaker 799: 7732\n", + "Speaker 800: 576\n", + "Speaker 801: 8226\n", + "Speaker 802: 7226\n", + "Speaker 803: 1098\n", + "Speaker 804: 7780\n", + "Speaker 805: 5723\n", + "Speaker 806: 7553\n", + "Speaker 807: 5740\n", + "Speaker 808: 1992\n", + "Speaker 809: 4441\n", + "Speaker 810: 28\n", + "Speaker 811: 7302\n", + "Speaker 812: 4214\n", + "Speaker 813: 7957\n", + "Speaker 814: 1363\n", + "Speaker 815: 6098\n", + "Speaker 816: 4848\n", + "Speaker 817: 1365\n", + "Speaker 818: 2093\n", + "Speaker 819: 1265\n", + "Speaker 820: 1578\n", + "Speaker 821: 4278\n", + "Speaker 822: 3816\n", + "Speaker 823: 1752\n", + "Speaker 824: 7495\n", + "Speaker 825: 1183\n", + "Speaker 826: 1645\n", + "Speaker 827: 698\n", + "Speaker 828: 2060\n", + "Speaker 829: 7318\n", + "Speaker 830: 112\n", + "Speaker 831: 4088\n", + "Speaker 832: 7859\n", + "Speaker 833: 7447\n", + "Speaker 834: 3009\n", + "Speaker 835: 246\n", + "Speaker 836: 1754\n", + "Speaker 837: 480\n", + "Speaker 838: 403\n", + "Speaker 839: 3368\n", + "Speaker 840: 446\n", + "Speaker 841: 2774\n", + "Speaker 842: 2498\n", + "Speaker 843: 3540\n", + "Speaker 844: 6300\n", + "Speaker 845: 6458\n", + "Speaker 846: 8401\n", + "Speaker 847: 2577\n", + "Speaker 848: 7635\n", + "Speaker 849: 1040\n", + "Speaker 850: 6215\n", + "Speaker 851: 6727\n", + "Speaker 852: 6406\n", + "Speaker 853: 2853\n", + "Speaker 854: 6317\n", + "Speaker 855: 7113\n", + "Speaker 856: 8425\n", + "Speaker 857: 6694\n", + "Speaker 858: 882\n", + "Speaker 859: 100\n", + "Speaker 860: 6454\n", + "Speaker 861: 3584\n", + "Speaker 862: 2817\n", + "Speaker 863: 667\n", + "Speaker 864: 2229\n", + "Speaker 865: 3851\n", + "Speaker 866: 4133\n", + "Speaker 867: 5656\n", + "Speaker 868: 278\n", + "Speaker 869: 1535\n", + "Speaker 870: 1259\n", + "Speaker 871: 7128\n", + "Speaker 872: 296\n", + "Speaker 873: 2061\n", + "Speaker 874: 5393\n", + "Speaker 875: 3221\n", + "Speaker 876: 30\n", + "Speaker 877: 188\n", + "Speaker 878: 1445\n", + "Speaker 879: 2393\n", + "Speaker 880: 6956\n", + "Speaker 881: 5163\n", + "Speaker 882: 549\n", + "Speaker 883: 2518\n", + "Speaker 884: 829\n", + "Speaker 885: 6567\n", + "Speaker 886: 8592\n", + "Speaker 887: 303\n", + "Speaker 888: 240\n", + "Speaker 889: 3380\n", + "Speaker 890: 7481\n", + "Speaker 891: 5883\n", + "Speaker 892: 3214\n", + "Speaker 893: 8855\n", + "Speaker 894: 3947\n", + "Speaker 895: 398\n", + "Speaker 896: 55\n", + "Speaker 897: 8722\n", + "Speaker 898: 8713\n", + "Speaker 899: 5868\n", + "Speaker 900: 979\n", + "Speaker 901: 209\n", + "Speaker 902: 2673\n", + "Speaker 903: 3340\n", + "Speaker 904: 126\n", + "Speaker 905: 612\n", + "Speaker 906: 580\n", + "Speaker 907: 1182\n", + "Speaker 908: 664\n", + "Speaker 909: 1246\n", + "Speaker 910: 5678\n", + "Speaker 911: 1487\n", + "Speaker 912: 9026\n", + "Speaker 913: 2196\n", + "Speaker 914: 6235\n", + "Speaker 915: 2952\n", + "Speaker 916: 3733\n", + "Speaker 917: 2790\n", + "Speaker 918: 6339\n", + "Speaker 919: 5489\n", + "Speaker 920: 7505\n", + "Speaker 921: 7190\n", + "Speaker 922: 7059\n", + "Speaker 923: 175\n", + "Speaker 924: 5390\n", + "Speaker 925: 6373\n", + "Speaker 926: 6895\n", + "Speaker 927: 4148\n", + "Speaker 928: 93\n", + "Speaker 929: 339\n", + "Speaker 930: 8113\n", + "Speaker 931: 7478\n", + "Speaker 932: 439\n", + "Speaker 933: 6209\n", + "Speaker 934: 6553\n", + "Speaker 935: 5660\n", + "Speaker 936: 716\n", + "Speaker 937: 6643\n", + "Speaker 938: 4788\n", + "Speaker 939: 114\n", + "Speaker 940: 492\n", + "Speaker 941: 5909\n", + "Speaker 942: 1482\n", + "Speaker 943: 38\n", + "Speaker 944: 5448\n", + "Speaker 945: 98\n", + "Speaker 946: 159\n", + "Speaker 947: 718\n", + "Speaker 948: 922\n", + "Speaker 949: 7258\n", + "Speaker 950: 6388\n", + "Speaker 951: 7178\n", + "Speaker 952: 7558\n", + "Speaker 953: 899\n", + "Speaker 954: 373\n", + "Speaker 955: 87\n", + "Speaker 956: 3526\n", + "Speaker 957: 3864\n", + "Speaker 958: 3370\n", + "Speaker 959: 1826\n", + "Speaker 960: 7739\n", + "Speaker 961: 6575\n", + "Speaker 962: 501\n", + "Speaker 963: 909\n", + "Speaker 964: 3112\n", + "Speaker 965: 7240\n", + "Speaker 966: 699\n", + "Speaker 967: 4595\n", + "Speaker 968: 5746\n", + "Speaker 969: 4856\n", + "Speaker 970: 1629\n", + "Speaker 971: 707\n", + "Speaker 972: 589\n", + "Speaker 973: 1638\n", + "Speaker 974: 830\n", + "Speaker 975: 3989\n", + "Speaker 976: 8066\n", + "Speaker 977: 7416\n", + "Speaker 978: 70\n", + "Speaker 979: 6993\n", + "Speaker 980: 3790\n", + "Speaker 981: 3490\n", + "Speaker 982: 8684\n", + "Speaker 983: 166\n", + "Speaker 984: 6505\n", + "Speaker 985: 2911\n", + "Speaker 986: 2127\n", + "Speaker 987: 2146\n", + "Speaker 988: 3664\n", + "Speaker 989: 7995\n", + "Speaker 990: 8725\n", + "Speaker 991: 4340\n", + "Speaker 992: 8006\n", + "Speaker 993: 4973\n", + "Speaker 994: 2910\n", + "Speaker 995: 497\n", + "Speaker 996: 5876\n", + "Speaker 997: 6233\n", + "Speaker 998: 3537\n", + "Speaker 999: 1413\n", + "Speaker 1000: 5189\n", + "Speaker 1001: 204\n", + "Speaker 1002: 836\n", + "Speaker 1003: 2618\n", + "Speaker 1004: 7276\n", + "Speaker 1005: 1264\n", + "Speaker 1006: 2045\n", + "Speaker 1007: 3215\n", + "Speaker 1008: 6555\n", + "Speaker 1009: 196\n", + "Speaker 1010: 6848\n", + "Speaker 1011: 1160\n", + "Speaker 1012: 8771\n", + "Speaker 1013: 4744\n", + "Speaker 1014: 6637\n", + "Speaker 1015: 1463\n", + "Speaker 1016: 3615\n", + "Speaker 1017: 5776\n", + "Speaker 1018: 26\n", + "Speaker 1019: 7339\n", + "Speaker 1020: 249\n", + "Speaker 1021: 1034\n", + "Speaker 1022: 1743\n", + "Speaker 1023: 207\n", + "Speaker 1024: 831\n", + "Speaker 1025: 4335\n", + "Speaker 1026: 7720\n", + "Speaker 1027: 2194\n", + "Speaker 1028: 688\n", + "Speaker 1029: 8619\n", + "Speaker 1030: 8718\n", + "Speaker 1031: 581\n", + "Speaker 1032: 835\n", + "Speaker 1033: 7881\n", + "Speaker 1034: 3607\n", + "Speaker 1035: 7933\n", + "Speaker 1036: 708\n", + "Speaker 1037: 7188\n", + "Speaker 1038: 4246\n", + "Speaker 1039: 1926\n", + "Speaker 1040: 7766\n", + "Speaker 1041: 6538\n", + "Speaker 1042: 2149\n", + "Speaker 1043: 7434\n", + "Speaker 1044: 3230\n", + "Speaker 1045: 3983\n", + "Speaker 1046: 4152\n", + "Speaker 1047: 1336\n", + "Speaker 1048: 2388\n", + "Speaker 1049: 5139\n", + "Speaker 1050: 1473\n", + "Speaker 1051: 868\n", + "Speaker 1052: 2709\n", + "Speaker 1053: 2674\n", + "Speaker 1054: 2570\n", + "Speaker 1055: 211\n", + "Speaker 1056: 4137\n", + "Speaker 1057: 472\n", + "Speaker 1058: 5022\n", + "Speaker 1059: 1263\n", + "Speaker 1060: 1801\n", + "Speaker 1061: 1963\n", + "Speaker 1062: 5386\n", + "Speaker 1063: 3274\n", + "Speaker 1064: 3070\n", + "Speaker 1065: 3436\n", + "Speaker 1066: 8347\n", + "Speaker 1067: 7245\n", + "Speaker 1068: 3240\n", + "Speaker 1069: 7555\n", + "Speaker 1070: 6081\n", + "Speaker 1071: 5914\n", + "Speaker 1072: 1827\n", + "Speaker 1073: 8238\n", + "Speaker 1074: 2256\n", + "Speaker 1075: 7139\n", + "Speaker 1076: 1668\n", + "Speaker 1077: 4108\n", + "Speaker 1078: 7809\n", + "Speaker 1079: 2384\n", + "Speaker 1080: 4806\n", + "Speaker 1081: 3830\n", + "Speaker 1082: 3889\n", + "Speaker 1083: 217\n", + "Speaker 1084: 3645\n", + "Speaker 1085: 205\n", + "Speaker 1086: 6476\n", + "Speaker 1087: 4590\n", + "Speaker 1088: 6563\n", + "Speaker 1089: 2416\n", + "Speaker 1090: 8183\n", + "Speaker 1091: 8975\n", + "Speaker 1092: 4257\n", + "Speaker 1093: 1425\n", + "Speaker 1094: 8014\n", + "Speaker 1095: 5190\n", + "Speaker 1096: 4160\n", + "Speaker 1097: 1069\n", + "Speaker 1098: 1923\n", + "Speaker 1099: 4110\n", + "Speaker 1100: 1235\n", + "Speaker 1101: 5049\n", + "Speaker 1102: 479\n", + "Speaker 1103: 2573\n", + "Speaker 1104: 4331\n", + "Speaker 1105: 6828\n", + "Speaker 1106: 380\n", + "Speaker 1107: 7395\n", + "Speaker 1108: 3638\n", + "Speaker 1109: 6104\n", + "Speaker 1110: 4681\n", + "Speaker 1111: 8824\n", + "Speaker 1112: 2085\n", + "Speaker 1113: 6038\n", + "Speaker 1114: 7475\n", + "Speaker 1115: 8490\n", + "Speaker 1116: 3486\n", + "Speaker 1117: 6258\n", + "Speaker 1118: 2999\n", + "Speaker 1119: 8228\n", + "Speaker 1120: 1387\n", + "Speaker 1121: 8028\n", + "Speaker 1122: 1060\n", + "Speaker 1123: 3869\n", + "Speaker 1124: 8410\n", + "Speaker 1125: 2532\n", + "Speaker 1126: 2562\n", + "Speaker 1127: 1383\n", + "Speaker 1128: 7120\n", + "Speaker 1129: 1974\n", + "Speaker 1130: 1603\n", + "Speaker 1131: 8388\n", + "Speaker 1132: 8506\n", + "Speaker 1133: 337\n", + "Speaker 1134: 4222\n", + "Speaker 1135: 6371\n", + "Speaker 1136: 5039\n", + "Speaker 1137: 5867\n", + "Speaker 1138: 3876\n", + "Speaker 1139: 5293\n", + "Speaker 1140: 103\n", + "Speaker 1141: 64\n", + "Speaker 1142: 7800\n", + "Speaker 1143: 1447\n", + "Speaker 1144: 154\n", + "Speaker 1145: 460\n", + "Speaker 1146: 2514\n", + "Speaker 1147: 2816\n", + "Speaker 1148: 27\n", + "Speaker 1149: 242\n", + "Speaker 1150: 3738\n" + ] + } + ], + "source": [ + "for s, speaker in enumerate(speakers):\n", + " print('Speaker %d: %s' % (s + 1, speaker))\n", + " os.mkdir(os.path.join(data_dir, 'mels_mode', speaker))\n", + " textgrids = os.listdir(os.path.join(data_dir, 'textgrids', speaker))\n", + " for textgrid in textgrids:\n", + " t = tgt.io.read_textgrid(os.path.join(data_dir, 'textgrids', speaker, textgrid))\n", + " m = np.load(os.path.join(data_dir, 'mels', speaker, textgrid.replace('.TextGrid', '_mel.npy')))\n", + " m_mode = np.copy(m)\n", + " t = t.get_tier_by_name('phones')\n", + " for i in range(len(t)):\n", + " phoneme = t[i].text\n", + " start_frame = int(t[i].start_time * 22050.0) // 256\n", + " end_frame = int(t[i].end_time * 22050.0) // 256 + 1\n", + " m_mode[:, start_frame:end_frame] = np.repeat(np.expand_dims(mels_mode[phoneme], 1), end_frame - start_frame, 1)\n", + " np.save(os.path.join(data_dir, 'mels_mode', speaker, textgrid.replace('.TextGrid', '_avgmel.npy')), m_mode)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python [default]", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/talkingface/data/dataprocess/inference.ipynb b/talkingface/data/dataprocess/inference.ipynb new file mode 100644 index 00000000..284de0c9 --- /dev/null +++ b/talkingface/data/dataprocess/inference.ipynb @@ -0,0 +1,356 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import argparse\n", + "import json\n", + "import os\n", + "import numpy as np\n", + "import IPython.display as ipd\n", + "from tqdm import tqdm\n", + "from scipy.io.wavfile import write\n", + "\n", + "import torch\n", + "use_gpu = torch.cuda.is_available()\n", + "\n", + "import librosa\n", + "from librosa.core import load\n", + "from librosa.filters import mel as librosa_mel_fn\n", + "#mel_basis = librosa_mel_fn(22050, 1024, 80, 0, 8000)\n", + "mel_basis = librosa_mel_fn(sr=22050, n_fft=1024, n_mels=80, fmin=0, fmax=8000)\n", + "\n", + "import params\n", + "from model import DiffVC\n", + "\n", + "import sys\n", + "sys.path.append('hifi-gan/')\n", + "from env import AttrDict\n", + "from models import Generator as HiFiGAN\n", + "\n", + "sys.path.append('speaker_encoder/')\n", + "from encoder import inference as spk_encoder\n", + "from pathlib import Path" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def get_mel(wav_path):\n", + " wav, _ = load(wav_path, sr=22050)\n", + " wav = wav[:(wav.shape[0] // 256)*256]\n", + " wav = np.pad(wav, 384, mode='reflect')\n", + " stft = librosa.core.stft(wav, n_fft=1024, hop_length=256, win_length=1024, window='hann', center=False)\n", + " stftm = np.sqrt(np.real(stft) ** 2 + np.imag(stft) ** 2 + (1e-9))\n", + " mel_spectrogram = np.matmul(mel_basis, stftm)\n", + " log_mel_spectrogram = np.log(np.clip(mel_spectrogram, a_min=1e-5, a_max=None))\n", + " return log_mel_spectrogram\n", + "\n", + "def get_embed(wav_path):\n", + " wav_preprocessed = spk_encoder.preprocess_wav(wav_path)\n", + " embed = spk_encoder.embed_utterance(wav_preprocessed)\n", + " return embed\n", + "\n", + "def noise_median_smoothing(x, w=5):\n", + " y = np.copy(x)\n", + " x = np.pad(x, w, \"edge\")\n", + " for i in range(y.shape[0]):\n", + " med = np.median(x[i:i+2*w+1])\n", + " y[i] = min(x[i+w+1], med)\n", + " return y\n", + "\n", + "def mel_spectral_subtraction(mel_synth, mel_source, spectral_floor=0.02, silence_window=5, smoothing_window=5):\n", + " mel_len = mel_source.shape[-1]\n", + " energy_min = 100000.0\n", + " i_min = 0\n", + " for i in range(mel_len - silence_window):\n", + " energy_cur = np.sum(np.exp(2.0 * mel_source[:, i:i+silence_window]))\n", + " if energy_cur < energy_min:\n", + " i_min = i\n", + " energy_min = energy_cur\n", + " estimated_noise_energy = np.min(np.exp(2.0 * mel_synth[:, i_min:i_min+silence_window]), axis=-1)\n", + " if smoothing_window is not None:\n", + " estimated_noise_energy = noise_median_smoothing(estimated_noise_energy, smoothing_window)\n", + " mel_denoised = np.copy(mel_synth)\n", + " for i in range(mel_len):\n", + " signal_subtract_noise = np.exp(2.0 * mel_synth[:, i]) - estimated_noise_energy\n", + " estimated_signal_energy = np.maximum(signal_subtract_noise, spectral_floor * estimated_noise_energy)\n", + " mel_denoised[:, i] = np.log(np.sqrt(estimated_signal_energy))\n", + " return mel_denoised" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of parameters: 126259128\n" + ] + } + ], + "source": [ + "# loading voice conversion model\n", + "vc_path = 'checkpts/vc/vc_libritts_wodyn.pt' # path to voice conversion model\n", + "\n", + "generator = DiffVC(params.n_mels, params.channels, params.filters, params.heads, \n", + " params.layers, params.kernel, params.dropout, params.window_size, \n", + " params.enc_dim, params.spk_dim, params.use_ref_t, params.dec_dim, \n", + " params.beta_min, params.beta_max)\n", + "if use_gpu:\n", + " generator = generator.cuda()\n", + " generator.load_state_dict(torch.load(vc_path))\n", + "else:\n", + " generator.load_state_dict(torch.load(vc_path, map_location='cpu'))\n", + "generator.eval()\n", + "\n", + "print(f'Number of parameters: {generator.nparams}')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\liberty\\AppData\\Local\\Programs\\Python\\Python310\\lib\\site-packages\\torch\\nn\\utils\\weight_norm.py:30: UserWarning: torch.nn.utils.weight_norm is deprecated in favor of torch.nn.utils.parametrizations.weight_norm.\n", + " warnings.warn(\"torch.nn.utils.weight_norm is deprecated in favor of torch.nn.utils.parametrizations.weight_norm.\")\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Removing weight norm...\n" + ] + } + ], + "source": [ + "# loading HiFi-GAN vocoder\n", + "hfg_path = 'checkpts/vocoder/' # HiFi-GAN path\n", + "\n", + "with open(hfg_path + 'config.json') as f:\n", + " h = AttrDict(json.load(f))\n", + "\n", + "if use_gpu:\n", + " hifigan_universal = HiFiGAN(h).cuda()\n", + " hifigan_universal.load_state_dict(torch.load(hfg_path + 'generator')['generator'])\n", + "else:\n", + " hifigan_universal = HiFiGAN(h)\n", + " hifigan_universal.load_state_dict(torch.load(hfg_path + 'generator', map_location='cpu')['generator'])\n", + "\n", + "_ = hifigan_universal.eval()\n", + "hifigan_universal.remove_weight_norm()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loaded encoder \"pretrained.pt\" trained to step 1564501\n" + ] + } + ], + "source": [ + "# loading speaker encoder\n", + "enc_model_fpath = Path('checkpts/spk_encoder/pretrained.pt') # speaker encoder path\n", + "if use_gpu:\n", + " spk_encoder.load_model(enc_model_fpath, device=\"cuda\")\n", + "else:\n", + " spk_encoder.load_model(enc_model_fpath, device=\"cpu\")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\liberty\\Desktop\\divff\\Speech-Backbones\\DiffVC\\speaker_encoder\\encoder\\audio.py:41: FutureWarning: Pass orig_sr=24000, target_sr=16000 as keyword args. From version 0.10 passing these as positional arguments will result in an error\n", + " wav = librosa.resample(wav, source_sr, sampling_rate)\n", + "c:\\Users\\liberty\\Desktop\\divff\\Speech-Backbones\\DiffVC\\speaker_encoder\\encoder\\audio.py:75: FutureWarning: Pass y=[-5.1470578e-04 -4.9517461e-04 7.9890393e-05 ... 7.1139593e-04\n", + " 4.4408118e-04 4.9962930e-04] as keyword args. From version 0.10 passing these as positional arguments will result in an error\n", + " frames = librosa.feature.melspectrogram(\n" + ] + } + ], + "source": [ + "# loading source and reference wavs, calculating mel-spectrograms and speaker embeddings\n", + "src_path = 'example/8534_216567_000015_000010.wav' # path to source utterance\n", + "tgt_path = 'example/6415_111615_000012_000005.wav' # path to reference utterance\n", + "\n", + "mel_source = torch.from_numpy(get_mel(src_path)).float().unsqueeze(0)\n", + "if use_gpu:\n", + " mel_source = mel_source.cuda()\n", + "mel_source_lengths = torch.LongTensor([mel_source.shape[-1]])\n", + "if use_gpu:\n", + " mel_source_lengths = mel_source_lengths.cuda()\n", + "\n", + "mel_target = torch.from_numpy(get_mel(tgt_path)).float().unsqueeze(0)\n", + "if use_gpu:\n", + " mel_target = mel_target.cuda()\n", + "mel_target_lengths = torch.LongTensor([mel_target.shape[-1]])\n", + "if use_gpu:\n", + " mel_target_lengths = mel_target_lengths.cuda()\n", + "\n", + "embed_target = torch.from_numpy(get_embed(tgt_path)).float().unsqueeze(0)\n", + "if use_gpu:\n", + " embed_target = embed_target.cuda()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "# performing voice conversion\n", + "mel_encoded, mel_ = generator.forward(mel_source, mel_source_lengths, mel_target, mel_target_lengths, embed_target, \n", + " n_timesteps=30, mode='ml')\n", + "mel_synth_np = mel_.cpu().detach().squeeze().numpy()\n", + "mel_source_np = mel_.cpu().detach().squeeze().numpy()\n", + "mel = torch.from_numpy(mel_spectral_subtraction(mel_synth_np, mel_source_np, smoothing_window=1)).float().unsqueeze(0)\n", + "if use_gpu:\n", + " mel = mel.cuda()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# source utterance (vocoded)\n", + "with torch.no_grad():\n", + " audio = hifigan_universal.forward(mel_source).cpu().squeeze().clamp(-1, 1)\n", + "ipd.display(ipd.Audio(audio, rate=22050))" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# reference utterance (vocoded)\n", + "with torch.no_grad():\n", + " audio = hifigan_universal.forward(mel_target).cpu().squeeze().clamp(-1, 1)\n", + "ipd.display(ipd.Audio(audio, rate=22050))" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# converted speech\n", + "with torch.no_grad():\n", + " audio = hifigan_universal.forward(mel).cpu().squeeze().clamp(-1, 1)\n", + "ipd.display(ipd.Audio(audio, rate=22050))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/talkingface/data/dataset/data_objects/__init__.py b/talkingface/data/dataset/data_objects/__init__.py index ec1999f1..12ae8452 100644 --- a/talkingface/data/dataset/data_objects/__init__.py +++ b/talkingface/data/dataset/data_objects/__init__.py @@ -1,4 +1,4 @@ """ from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ -from encoder.data_objects.speaker_verification_dataset import SpeakerVerificationDataset -from encoder.data_objects.speaker_verification_dataset import SpeakerVerificationDataLoader +from talkingface.data.dataset.data_objects.speaker_verification_dataset import SpeakerVerificationDataset +from talkingface.data.dataset.data_objects.speaker_verification_dataset import SpeakerVerificationDataLoader diff --git a/talkingface/data/dataset/data_objects/speaker.py b/talkingface/data/dataset/data_objects/speaker.py index 53d5bc4e..1f7dada6 100644 --- a/talkingface/data/dataset/data_objects/speaker.py +++ b/talkingface/data/dataset/data_objects/speaker.py @@ -1,7 +1,7 @@ """ from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ -from encoder.data_objects.random_cycler import RandomCycler -from encoder.data_objects.utterance import Utterance +from talkingface.data.dataset.data_objects.random_cycler import RandomCycler +from talkingface.data.dataset.data_objects.utterance import Utterance from pathlib import Path # Contains the set of utterances of a single speaker diff --git a/talkingface/data/dataset/data_objects/speaker_batch.py b/talkingface/data/dataset/data_objects/speaker_batch.py index d6480f1c..afad39b5 100644 --- a/talkingface/data/dataset/data_objects/speaker_batch.py +++ b/talkingface/data/dataset/data_objects/speaker_batch.py @@ -2,7 +2,7 @@ import numpy as np from typing import List -from encoder.data_objects.speaker import Speaker +from talkingface.data.dataset.data_objects.speaker import Speaker class SpeakerBatch: def __init__(self, speakers: List[Speaker], utterances_per_speaker: int, n_frames: int): diff --git a/talkingface/data/dataset/data_objects/speaker_verification_dataset.py b/talkingface/data/dataset/data_objects/speaker_verification_dataset.py index 9f79fd7f..a52ebbc2 100644 --- a/talkingface/data/dataset/data_objects/speaker_verification_dataset.py +++ b/talkingface/data/dataset/data_objects/speaker_verification_dataset.py @@ -1,9 +1,9 @@ """ from https://github.com/CorentinJ/Real-Time-Voice-Cloning """ -from encoder.data_objects.random_cycler import RandomCycler -from encoder.data_objects.speaker_batch import SpeakerBatch -from encoder.data_objects.speaker import Speaker -from encoder.params_data import partials_n_frames +from talkingface.data.dataset.data_objects.random_cycler import RandomCycler +from talkingface.data.dataset.data_objects.speaker_batch import SpeakerBatch +from talkingface.data.dataset.data_objects.speaker import Speaker +from talkingface.properties.dataset.params_data import partials_n_frames from torch.utils.data import Dataset, DataLoader from pathlib import Path diff --git a/talkingface/model/voice_conversion/diffvc.py b/talkingface/model/voice_conversion/diffvc.py index 8e941432..fbe0c590 100644 --- a/talkingface/model/voice_conversion/diffvc.py +++ b/talkingface/model/voice_conversion/diffvc.py @@ -919,7 +919,6 @@ def forward(self, stftm): #istft = torchaudio.functional.istft(stft, n_fft=self.n_fft, #hop_length=self.hop_size, win_length=self.n_fft, # window=self.window, center=True) - print("0stft.shape:", stft_complex.shape) istft = torch.istft(stft_complex, n_fft=self.n_fft, hop_length=self.hop_size, win_length=self.n_fft, window=self.window,center=True ) return istft.unsqueeze(1) @@ -960,8 +959,6 @@ def forward(self, s, n_iters=32): #angles = s / stftm.unsqueeze(-1) angles = s / stftm_complex angles=angles.unsqueeze(-1) - print("1s.shape:", s.shape) - print("c.shape:", c.shape) print("prev_angles.shape:", prev_angles.shape) s = c * (angles + self.momentum * (angles - prev_angles)) # real_part = torch.ones_like(s, device=stftm.device) @@ -972,7 +969,6 @@ def forward(self, s, n_iters=32): #center=True) # print("2s.shape:", s_complex.shape) s = s.squeeze(-1) - print("3s.shape:", s.shape) x = torch.istft(s, n_fft=self.n_fft, hop_length=self.hop_size, win_length=self.n_fft, window=self.window, center=True) diff --git a/params.py b/talkingface/properties/model/params.py similarity index 100% rename from params.py rename to talkingface/properties/model/params.py diff --git a/talkingface/quick_start/quick_start.py b/talkingface/quick_start/quick_start.py index 185bb7d1..0587213f 100644 --- a/talkingface/quick_start/quick_start.py +++ b/talkingface/quick_start/quick_start.py @@ -77,7 +77,8 @@ def run_talkingface( train_dataset, val_dataset = create_dataset(config) train_data_loader = data_utils.DataLoader( - train_dataset, batch_size=config["batch_size"], shuffle=False) + train_dataset, batch_size=config["batch_size"], shuffle=True + ) val_data_loader = data_utils.DataLoader( val_dataset, batch_size=config["batch_size"], shuffle=False ) diff --git a/talkingface/trainer/diffvc_trainer.py b/talkingface/trainer/diffvc_trainer.py deleted file mode 100644 index e69de29b..00000000 diff --git a/talkingface/trainer/trainer.py b/talkingface/trainer/trainer.py index e7eb88df..a715f2cc 100644 --- a/talkingface/trainer/trainer.py +++ b/talkingface/trainer/trainer.py @@ -33,7 +33,7 @@ from time import perf_counter as timer import matplotlib.pyplot as plt import numpy as np -import params +import talkingface.properties.model.params as params from talkingface.model.voice_conversion.diffvc import diffvc # import webbrowser @@ -55,565 +55,280 @@ #from talkingface.data.dataprocess.wav2lip_process import Wav2LipAudio from talkingface.evaluator import Evaluator -class diffVC_encoder_train: - def sync(device: torch.device): - # FIXME - return - # For correct profiling (cuda operations are async) - if device.type == "cuda": - torch.cuda.synchronize(device) - def train(run_id: str, clean_data_root: Path, models_dir: Path, umap_every: int, save_every: int, - backup_every: int, vis_every: int, force_restart: bool, visdom_server: str, - no_visdom: bool): - # Create a dataset and a dataloader - dataset = SpeakerVerificationDataset(clean_data_root) - loader = SpeakerVerificationDataLoader( - dataset, - diffVC_encoder_train.speakers_per_batch, - diffVC_encoder_train.utterances_per_speaker, - num_workers=8, - ) - # Setup the device on which to run the forward pass and the loss. These can be different, - # because the forward pass is faster on the GPU whereas the loss is often (depending on your - # hyperparameters) faster on the CPU. - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - # FIXME: currently, the gradient is None if loss_device is cuda - loss_device = torch.device("cpu") - # Create the model and the optimizer - model = SpeakerEncoder(device, loss_device) - optimizer = torch.optim.Adam(model.parameters(), lr=diffVC_encoder_train.learning_rate_init) - init_step = 1 +class AbstractTrainer(object): + r"""Trainer Class is used to manage the training and evaluation processes of recommender system models. + AbstractTrainer is an abstract class in which the fit() and evaluate() method should be implemented according + to different training and evaluation strategies. + """ - # Configure file path for the model - state_fpath = models_dir.joinpath(run_id + ".pt") - backup_dir = models_dir.joinpath(run_id + "_backups") + def __init__(self, config, model): + self.config = config + self.model = model + + def fit(self, train_data): + r"""Train the model based on the train data.""" + raise NotImplementedError("Method [next] should be implemented.") - # Load any existing model - if not force_restart: - if state_fpath.exists(): - print("Found existing model \"%s\", loading it and resuming training." % run_id) - checkpoint = torch.load(state_fpath) - init_step = checkpoint["step"] - model.load_state_dict(checkpoint["model_state"]) - optimizer.load_state_dict(checkpoint["optimizer_state"]) - optimizer.param_groups[0]["lr"] = diffVC_encoder_train.learning_rate_init - else: - print("No model \"%s\" found, starting training from scratch." % run_id) - else: - print("Starting the training from scratch.") - model.train() + def evaluate(self, eval_data): + r"""Evaluate the model based on the eval data.""" - # Initialize the visualization environment - vis = Visualizations(run_id, vis_every, server=visdom_server, disabled=no_visdom) - vis.log_dataset(dataset) - vis.log_params() - device_name = str(torch.cuda.get_device_name(0) if torch.cuda.is_available() else "CPU") - vis.log_implementation({"Device": device_name}) + raise NotImplementedError("Method [next] should be implemented.") + - # Training loop - profiler = Profiler(summarize_every=10, disabled=False) - for step, speaker_batch in enumerate(loader, init_step): - profiler.tick("Blocking, waiting for batch (threaded)") +class Trainer(AbstractTrainer): + r"""The basic Trainer for basic training and evaluation strategies in talkingface systems. This class defines common + functions for training and evaluation processes of most recommender system models, including fit(), evaluate(), + resume_checkpoint() and some other features helpful for model training and evaluation. - # Forward pass - inputs = torch.from_numpy(speaker_batch.data).to(device) - diffVC_encoder_train.sync(device) - profiler.tick("Data to %s" % device) - embeds = model(inputs) - diffVC_encoder_train.sync(device) - profiler.tick("Forward pass") - embeds_loss = embeds.view((diffVC_encoder_train.speakers_per_batch, diffVC_encoder_train.utterances_per_speaker, -1)).to(loss_device) - loss, eer = model.loss(embeds_loss) - diffVC_encoder_train.sync(loss_device) - profiler.tick("Loss") + Generally speaking, this class can serve most recommender system models, If the training process of the model is to + simply optimize a single loss without involving any complex training strategies. - # Backward pass - model.zero_grad() - loss.backward() - profiler.tick("Backward pass") - model.do_gradient_ops() - optimizer.step() - profiler.tick("Parameter update") + Initializing the Trainer needs two parameters: `config` and `model`. `config` records the parameters information + for controlling training and evaluation, such as `learning_rate`, `epochs`, `eval_step` and so on. + `model` is the instantiated object of a Model Class. - # Update visualizations - # learning_rate = optimizer.param_groups[0]["lr"] - vis.update(loss.item(), eer, step) + """ + def __init__(self, config, model): + super(Trainer, self).__init__(config, model) + self.logger = getLogger() + self.tensorboard = get_tensorboard(self.logger) + self.wandblogger = WandbLogger(config) + # self.enable_amp = config["enable_amp"] + # self.enable_scaler = torch.cuda.is_available() and config["enable_scaler"] - # Draw projections and save them to the backup folder - if umap_every != 0 and step % umap_every == 0: - print("Drawing and saving projections (step %d)" % step) - backup_dir.mkdir(exist_ok=True) - projection_fpath = backup_dir.joinpath("%s_umap_%06d.png" % (run_id, step)) - embeds = embeds.detach().cpu().numpy() - vis.draw_projections(embeds, diffVC_encoder_train.utterances_per_speaker, step, projection_fpath) - vis.save() + # config for train + self.learner = config["learner"] + self.learning_rate = config["learning_rate"] + self.epochs = config["epochs"] + self.eval_step = min(config["eval_step"], self.epochs) + self.stopping_step = config["stopping_step"] + self.test_batch_size = config["eval_batch_size"] + self.gpu_available = torch.cuda.is_available() and config["use_gpu"] + self.device = config["device"] + self.checkpoint_dir = config["checkpoint_dir"] + ensure_dir(self.checkpoint_dir) + saved_model_file = "{}-{}.pth".format(self.config["model"], get_local_time()) + self.saved_model_file = os.path.join(self.checkpoint_dir, saved_model_file) + self.weight_decay = config["weight_decay"] + self.start_epoch = 0 + self.cur_step = 0 + self.train_loss_dict = dict() + self.optimizer = self._build_optimizer() + self.evaluator = Evaluator(config) - # Overwrite the latest version of the model - if save_every != 0 and step % save_every == 0: - print("Saving the model (step %d)" % step) - torch.save({ - "step": step + 1, - "model_state": model.state_dict(), - "optimizer_state": optimizer.state_dict(), - }, state_fpath) + self.valid_metric_bigger = config["valid_metric_bigger"] + self.best_valid_score = -np.inf if self.valid_metric_bigger else np.inf + self.best_valid_result = None - # Make a backup - if backup_every != 0 and step % backup_every == 0: - print("Making a backup (step %d)" % step) - backup_dir.mkdir(exist_ok=True) - backup_fpath = backup_dir.joinpath("%s_bak_%06d.pt" % (run_id, step)) - torch.save({ - "step": step + 1, - "model_state": model.state_dict(), - "optimizer_state": optimizer.state_dict(), - }, backup_fpath) + def _build_optimizer(self, **kwargs): + params = kwargs.pop("params", self.model.parameters()) + learner = kwargs.pop("learner", self.learner) + learning_rate = kwargs.pop("learning_rate", self.learning_rate) + weight_decay = kwargs.pop("weight_decay", self.weight_decay) + if (self.config["reg_weight"] and weight_decay and weight_decay * self.config["reg_weight"] > 0): + self.logger.warning( + "The parameters [weight_decay] and [reg_weight] are specified simultaneously, " + "which may lead to double regularization." + ) - profiler.tick("Extras (visualizations, saving)") + if learner.lower() == "adam": + optimizer = optim.Adam(params, lr=learning_rate, weight_decay=weight_decay) + elif learner.lower() == "adamw": + optimizer = optim.AdamW(params, lr=learning_rate, weight_decay=weight_decay) + elif learner.lower() == "sgd": + optimizer = optim.SGD(params, lr=learning_rate, weight_decay=weight_decay) + elif learner.lower() == "adagrad": + optimizer = optim.Adagrad( + params, lr=learning_rate, weight_decay=weight_decay + ) + elif learner.lower() == "rmsprop": + optimizer = optim.RMSprop( + params, lr=learning_rate, weight_decay=weight_decay + ) + elif learner.lower() == "sparse_adam": + optimizer = optim.SparseAdam(params, lr=learning_rate) + if weight_decay > 0: + self.logger.warning( + "Sparse Adam cannot argument received argument [{weight_decay}]" + ) + else: + self.logger.warning( + "Received unrecognized optimizer, set default Adam optimizer" + ) + optimizer = optim.Adam(params, lr=learning_rate) + return optimizer + def _train_epoch(self, train_data, epoch_idx, loss_func=None, show_progress=False): + r"""Train the model in an epoch -class Visualizations: - colormap = np.array([ - [76, 255, 0], - [0, 127, 70], - [255, 0, 0], - [255, 217, 38], - [0, 135, 255], - [165, 0, 165], - [255, 167, 255], - [0, 255, 255], - [255, 96, 38], - [142, 76, 0], - [33, 0, 127], - [0, 0, 0], - [183, 183, 183], - ], dtype=np.float) / 255 + Args: + train_data (DataLoader): The train data. + epoch_idx (int): The current epoch id. + loss_func (function): The loss function of :attr:`model`. If it is ``None``, the loss function will be + :attr:`self.model.calculate_loss`. Defaults to ``None``. + show_progress (bool): Show the progress of training epoch. Defaults to ``False``. - def __init__(self, env_name=None, update_every=10, server="http://localhost", disabled=False): - # Tracking data - self.last_update_timestamp = timer() - self.update_every = update_every - self.step_times = [] - self.losses = [] - self.eers = [] - print("Updating the visualizations every %d steps." % update_every) + Returns: + the averaged loss of this epoch + """ + self.model.train() + loss_func = loss_func or self.model.calculate_loss + total_loss_dict = {} + step = 0 + iter_data = ( + tqdm( + train_data, + total=len(train_data), + ncols=None, + ) + if show_progress + else train_data + ) - # If visdom is disabled TODO: use a better paradigm for that - self.disabled = disabled - if self.disabled: - return + for batch_idx, interaction in enumerate(iter_data): + self.optimizer.zero_grad() + step += 1 + losses_dict = loss_func(interaction) + loss = losses_dict["loss"] - # Set the environment name - now = str(datetime.now().strftime("%d-%m %Hh%M")) - if env_name is None: - self.env_name = now - else: - self.env_name = "%s (%s)" % (env_name, now) - - # Connect to visdom and open the corresponding window in the browser - try: - self.vis = visdom.Visdom(server, env=self.env_name, raise_exceptions=True) - except ConnectionError: - raise Exception("No visdom server detected. Run the command \"visdom\" in your CLI to " - "start it.") - # webbrowser.open("http://localhost:8097/env/" + self.env_name) + for key, value in losses_dict.items(): + if key in total_loss_dict: + if not torch.is_tensor(value): + total_loss_dict[key] += value + # 如果键已经在总和字典中,累加当前值 + else: + losses_dict[key] = value.item() + total_loss_dict[key] += value.item() + else: + if not torch.is_tensor(value): + total_loss_dict[key] = value + # 否则,将当前值添加到字典中 + else: + losses_dict[key] = value.item() + total_loss_dict[key] = value.item() + iter_data.set_description(set_color(f"train {epoch_idx} {losses_dict}", "pink")) - # Create the windows - self.loss_win = None - self.eer_win = None - # self.lr_win = None - self.implementation_win = None - self.projection_win = None - self.implementation_string = "" + self._check_nan(loss) + loss.backward() + self.optimizer.step() + average_loss_dict = {} + for key, value in total_loss_dict.items(): + average_loss_dict[key] = value/step - def log_params(self): - if self.disabled: - return - from talkingface.utils.voice_conversion_talkingface import params_data - from talkingface.utils.voice_conversion_talkingface import params_model - param_string = "Model parameters:
" - for param_name in (p for p in dir(params_model) if not p.startswith("__")): - value = getattr(params_model, param_name) - param_string += "\t%s: %s
" % (param_name, value) - param_string += "Data parameters:
" - for param_name in (p for p in dir(params_data) if not p.startswith("__")): - value = getattr(params_data, param_name) - param_string += "\t%s: %s
" % (param_name, value) - self.vis.text(param_string, opts={"title": "Parameters"}) + return average_loss_dict - def log_dataset(self, dataset: SpeakerVerificationDataset): - if self.disabled: - return - dataset_string = "" - dataset_string += "Speakers: %s\n" % len(dataset.speakers) - dataset_string += "\n" + dataset.get_logs() - dataset_string = dataset_string.replace("\n", "
") - self.vis.text(dataset_string, opts={"title": "Dataset"}) + - def log_implementation(self, params): - if self.disabled: - return - implementation_string = "" - for param, value in params.items(): - implementation_string += "%s: %s\n" % (param, value) - implementation_string = implementation_string.replace("\n", "
") - self.implementation_string = implementation_string - self.implementation_win = self.vis.text( - implementation_string, - opts={"title": "Training implementation"} - ) + def _valid_epoch(self, valid_data, show_progress=False): + r"""Valid the model with valid data. Different from the evaluate, this is use for training. - def update(self, loss, eer, step): - # Update the tracking data - now = timer() - self.step_times.append(1000 * (now - self.last_update_timestamp)) - self.last_update_timestamp = now - self.losses.append(loss) - self.eers.append(eer) - print(".", end="") + Args: + valid_data (DataLoader): the valid data. + show_progress (bool): Show the progress of evaluate epoch. Defaults to ``False``. - # Update the plots every steps - if step % self.update_every != 0: - return - time_string = "Step time: mean: %5dms std: %5dms" % \ - (int(np.mean(self.step_times)), int(np.std(self.step_times))) - print("\nStep %6d Loss: %.4f EER: %.4f %s" % - (step, np.mean(self.losses), np.mean(self.eers), time_string)) - if not self.disabled: - self.loss_win = self.vis.line( - [np.mean(self.losses)], - [step], - win=self.loss_win, - update="append" if self.loss_win else None, - opts=dict( - legend=["Avg. loss"], - xlabel="Step", - ylabel="Loss", - title="Loss", - ) - ) - self.eer_win = self.vis.line( - [np.mean(self.eers)], - [step], - win=self.eer_win, - update="append" if self.eer_win else None, - opts=dict( - legend=["Avg. EER"], - xlabel="Step", - ylabel="EER", - title="Equal error rate" - ) + Returns: + loss + """ + print('Valid for {} steps'.format(self.eval_steps)) + self.model.eval() + total_loss_dict = {} + iter_data = ( + tqdm(valid_data, + total=len(valid_data), + ncols=None, ) - if self.implementation_win is not None: - self.vis.text( - self.implementation_string + ("%s" % time_string), - win=self.implementation_win, - opts={"title": "Training implementation"}, - ) + if show_progress + else valid_data + ) + step = 0 + for batch_idx, batched_data in enumerate(iter_data): + step += 1 + batched_data.to(self.device) + losses_dict = self.model.calculate_loss(batched_data, valid=True) + for key, value in losses_dict.items(): + if key in total_loss_dict: + if not torch.is_tensor(value): + total_loss_dict[key] += value + # 如果键已经在总和字典中,累加当前值 + else: + losses_dict[key] = value.item() + total_loss_dict[key] += value.item() + else: + if not torch.is_tensor(value): + total_loss_dict[key] = value + # 否则,将当前值添加到字典中 + else: + losses_dict[key] = value.item() + total_loss_dict[key] = value.item() + iter_data.set_description(set_color(f"Valid {losses_dict}", "pink")) + average_loss_dict = {} + for key, value in total_loss_dict.items(): + average_loss_dict[key] = value/step - # Reset the tracking - self.losses.clear() - self.eers.clear() - self.step_times.clear() + return average_loss_dict + - def draw_projections(self, embeds, utterances_per_speaker, step, out_fpath=None, - max_speakers=10): - max_speakers = min(max_speakers, len(Visualizations.colormap)) - embeds = embeds[:max_speakers * utterances_per_speaker] - n_speakers = len(embeds) // utterances_per_speaker - ground_truth = np.repeat(np.arange(n_speakers), utterances_per_speaker) - colors = [Visualizations.colormap[i] for i in ground_truth] + + def _save_checkpoint(self, epoch, verbose=True, **kwargs): + r"""Store the model parameters information and training information. - reducer = umap.UMAP() - projected = reducer.fit_transform(embeds) - plt.scatter(projected[:, 0], projected[:, 1], c=colors) - plt.gca().set_aspect("equal", "datalim") - plt.title("UMAP projection (step %d)" % step) - if not self.disabled: - self.projection_win = self.vis.matplot(plt, win=self.projection_win) - if out_fpath is not None: - plt.savefig(out_fpath) - plt.clf() + Args: + epoch (int): the current epoch id - def save(self): - if not self.disabled: - self.vis.save([self.env_name]) + """ + saved_model_file = kwargs.pop("saved_model_file", self.saved_model_file) + state = { + "config": self.config, + "epoch": epoch, + "cur_step": self.cur_step, + "best_valid_score": self.best_valid_score, + "state_dict": self.model.state_dict(), + "other_parameter": self.model.other_parameter(), + "optimizer": self.optimizer.state_dict(), + } + torch.save(state, saved_model_file, pickle_protocol=4) + if verbose: + self.logger.info( + set_color("Saving current", "blue") + f": {saved_model_file}" + ) + def resume_checkpoint(self, resume_file): + r"""Load the model parameters information and training information. -class AbstractTrainer(object): - r"""Trainer Class is used to manage the training and evaluation processes of recommender system models. - AbstractTrainer is an abstract class in which the fit() and evaluate() method should be implemented according - to different training and evaluation strategies. - """ + Args: + resume_file (file): the checkpoint file - def __init__(self, config, model): - self.config = config - self.model = model - - def fit(self, train_data): - r"""Train the model based on the train data.""" - raise NotImplementedError("Method [next] should be implemented.") + """ + resume_file = str(resume_file) + self.saved_model_file = resume_file + checkpoint = torch.load(resume_file, map_location=self.device) + self.start_epoch = checkpoint["epoch"] + 1 + self.cur_step = checkpoint["cur_step"] + # self.best_valid_score = checkpoint["best_valid_score"] - def evaluate(self, eval_data): - r"""Evaluate the model based on the eval data.""" + # load architecture params from checkpoint + if checkpoint["config"]["model"].lower() != self.config["model"].lower(): + self.logger.warning( + "Architecture configuration given in config file is different from that of checkpoint. " + "This may yield an exception while state_dict is being loaded." + ) + self.model.load_state_dict(checkpoint["state_dict"]) + self.model.load_other_parameter(checkpoint.get("other_parameter")) - raise NotImplementedError("Method [next] should be implemented.") - + # load optimizer state from checkpoint only when optimizer type is not changed + self.optimizer.load_state_dict(checkpoint["optimizer"]) + message_output = "Checkpoint loaded. Resume training from epoch {}".format( + self.start_epoch + ) + self.logger.info(message_output) -class Trainer(AbstractTrainer): - r"""The basic Trainer for basic training and evaluation strategies in talkingface systems. This class defines common - functions for training and evaluation processes of most recommender system models, including fit(), evaluate(), - resume_checkpoint() and some other features helpful for model training and evaluation. - - Generally speaking, this class can serve most recommender system models, If the training process of the model is to - simply optimize a single loss without involving any complex training strategies. - - Initializing the Trainer needs two parameters: `config` and `model`. `config` records the parameters information - for controlling training and evaluation, such as `learning_rate`, `epochs`, `eval_step` and so on. - `model` is the instantiated object of a Model Class. - - """ - def __init__(self, config, model): - super(Trainer, self).__init__(config, model) - self.logger = getLogger() - self.tensorboard = get_tensorboard(self.logger) - self.wandblogger = WandbLogger(config) - # self.enable_amp = config["enable_amp"] - # self.enable_scaler = torch.cuda.is_available() and config["enable_scaler"] - - # config for train - self.learner = config["learner"] - self.learning_rate = config["learning_rate"] - self.epochs = config["epochs"] - self.eval_step = min(config["eval_step"], self.epochs) - self.stopping_step = config["stopping_step"] - self.test_batch_size = config["eval_batch_size"] - self.gpu_available = torch.cuda.is_available() and config["use_gpu"] - self.device = config["device"] - self.checkpoint_dir = config["checkpoint_dir"] - ensure_dir(self.checkpoint_dir) - saved_model_file = "{}-{}.pth".format(self.config["model"], get_local_time()) - self.saved_model_file = os.path.join(self.checkpoint_dir, saved_model_file) - self.weight_decay = config["weight_decay"] - self.start_epoch = 0 - self.cur_step = 0 - self.train_loss_dict = dict() - self.optimizer = self._build_optimizer() - self.evaluator = Evaluator(config) - - self.valid_metric_bigger = config["valid_metric_bigger"] - self.best_valid_score = -np.inf if self.valid_metric_bigger else np.inf - self.best_valid_result = None - - def _build_optimizer(self, **kwargs): - params = kwargs.pop("params", self.model.parameters()) - learner = kwargs.pop("learner", self.learner) - learning_rate = kwargs.pop("learning_rate", self.learning_rate) - weight_decay = kwargs.pop("weight_decay", self.weight_decay) - if (self.config["reg_weight"] and weight_decay and weight_decay * self.config["reg_weight"] > 0): - self.logger.warning( - "The parameters [weight_decay] and [reg_weight] are specified simultaneously, " - "which may lead to double regularization." - ) - - if learner.lower() == "adam": - optimizer = optim.Adam(params, lr=learning_rate, weight_decay=weight_decay) - elif learner.lower() == "adamw": - optimizer = optim.AdamW(params, lr=learning_rate, weight_decay=weight_decay) - elif learner.lower() == "sgd": - optimizer = optim.SGD(params, lr=learning_rate, weight_decay=weight_decay) - elif learner.lower() == "adagrad": - optimizer = optim.Adagrad( - params, lr=learning_rate, weight_decay=weight_decay - ) - elif learner.lower() == "rmsprop": - optimizer = optim.RMSprop( - params, lr=learning_rate, weight_decay=weight_decay - ) - elif learner.lower() == "sparse_adam": - optimizer = optim.SparseAdam(params, lr=learning_rate) - if weight_decay > 0: - self.logger.warning( - "Sparse Adam cannot argument received argument [{weight_decay}]" - ) - else: - self.logger.warning( - "Received unrecognized optimizer, set default Adam optimizer" - ) - optimizer = optim.Adam(params, lr=learning_rate) - return optimizer - - def _train_epoch(self, train_data, epoch_idx, loss_func=None, show_progress=False): - r"""Train the model in an epoch - - Args: - train_data (DataLoader): The train data. - epoch_idx (int): The current epoch id. - loss_func (function): The loss function of :attr:`model`. If it is ``None``, the loss function will be - :attr:`self.model.calculate_loss`. Defaults to ``None``. - show_progress (bool): Show the progress of training epoch. Defaults to ``False``. - - Returns: - the averaged loss of this epoch - """ - self.model.train() - loss_func = loss_func or self.model.calculate_loss - total_loss_dict = {} - step = 0 - iter_data = ( - tqdm( - train_data, - total=len(train_data), - ncols=None, - ) - if show_progress - else train_data - ) - - for batch_idx, interaction in enumerate(iter_data): - self.optimizer.zero_grad() - step += 1 - losses_dict = loss_func(interaction) - loss = losses_dict["loss"] - - for key, value in losses_dict.items(): - if key in total_loss_dict: - if not torch.is_tensor(value): - total_loss_dict[key] += value - # 如果键已经在总和字典中,累加当前值 - else: - losses_dict[key] = value.item() - total_loss_dict[key] += value.item() - else: - if not torch.is_tensor(value): - total_loss_dict[key] = value - # 否则,将当前值添加到字典中 - else: - losses_dict[key] = value.item() - total_loss_dict[key] = value.item() - iter_data.set_description(set_color(f"train {epoch_idx} {losses_dict}", "pink")) - - self._check_nan(loss) - loss.backward() - self.optimizer.step() - average_loss_dict = {} - for key, value in total_loss_dict.items(): - average_loss_dict[key] = value/step - - return average_loss_dict - - - - def _valid_epoch(self, valid_data, show_progress=False): - r"""Valid the model with valid data. Different from the evaluate, this is use for training. - - Args: - valid_data (DataLoader): the valid data. - show_progress (bool): Show the progress of evaluate epoch. Defaults to ``False``. - - Returns: - loss - """ - print('Valid for {} steps'.format(self.eval_steps)) - self.model.eval() - total_loss_dict = {} - iter_data = ( - tqdm(valid_data, - total=len(valid_data), - ncols=None, - ) - if show_progress - else valid_data - ) - step = 0 - for batch_idx, batched_data in enumerate(iter_data): - step += 1 - batched_data.to(self.device) - losses_dict = self.model.calculate_loss(batched_data, valid=True) - for key, value in losses_dict.items(): - if key in total_loss_dict: - if not torch.is_tensor(value): - total_loss_dict[key] += value - # 如果键已经在总和字典中,累加当前值 - else: - losses_dict[key] = value.item() - total_loss_dict[key] += value.item() - else: - if not torch.is_tensor(value): - total_loss_dict[key] = value - # 否则,将当前值添加到字典中 - else: - losses_dict[key] = value.item() - total_loss_dict[key] = value.item() - iter_data.set_description(set_color(f"Valid {losses_dict}", "pink")) - average_loss_dict = {} - for key, value in total_loss_dict.items(): - average_loss_dict[key] = value/step - - return average_loss_dict - - - - - def _save_checkpoint(self, epoch, verbose=True, **kwargs): - r"""Store the model parameters information and training information. - - Args: - epoch (int): the current epoch id - - """ - saved_model_file = kwargs.pop("saved_model_file", self.saved_model_file) - state = { - "config": self.config, - "epoch": epoch, - "cur_step": self.cur_step, - "best_valid_score": self.best_valid_score, - "state_dict": self.model.state_dict(), - "other_parameter": self.model.other_parameter(), - "optimizer": self.optimizer.state_dict(), - } - torch.save(state, saved_model_file, pickle_protocol=4) - if verbose: - self.logger.info( - set_color("Saving current", "blue") + f": {saved_model_file}" - ) - - def resume_checkpoint(self, resume_file): - r"""Load the model parameters information and training information. - - Args: - resume_file (file): the checkpoint file - - """ - resume_file = str(resume_file) - self.saved_model_file = resume_file - checkpoint = torch.load(resume_file, map_location=self.device) - self.start_epoch = checkpoint["epoch"] + 1 - self.cur_step = checkpoint["cur_step"] - # self.best_valid_score = checkpoint["best_valid_score"] - - # load architecture params from checkpoint - if checkpoint["config"]["model"].lower() != self.config["model"].lower(): - self.logger.warning( - "Architecture configuration given in config file is different from that of checkpoint. " - "This may yield an exception while state_dict is being loaded." - ) - self.model.load_state_dict(checkpoint["state_dict"]) - self.model.load_other_parameter(checkpoint.get("other_parameter")) - - # load optimizer state from checkpoint only when optimizer type is not changed - self.optimizer.load_state_dict(checkpoint["optimizer"]) - message_output = "Checkpoint loaded. Resume training from epoch {}".format( - self.start_epoch - ) - self.logger.info(message_output) - - def _check_nan(self, loss): - if torch.isnan(loss): - raise ValueError("Training loss is nan") + def _check_nan(self, loss): + if torch.isnan(loss): + raise ValueError("Training loss is nan") def _generate_train_loss_output(self, epoch_idx, s_time, e_time, losses): des = self.config["loss_decimal_place"] or 4 @@ -780,21 +495,15 @@ def __init__(self, config, model): random_seed = params.seed test_size = params.test_size - #data_dir = r'C:\\Users\\liberty\Desktop\diffvc-yuanma\data\\' - #data_dir = r'..\dataset\diffvc_data\\' data_dir=params.data_dir val_file=params.val_file exc_file=params.exc_file - #val_file = "../../dataset/diffvc_data/filelists/valid.txt" - #val_file = r'C:\Users\liberty\Desktop\diffvc-yuanma\\filelists\\valid.txt' - #exc_file = "../../dataset/diffvc_data/filelists//exceptions_libritts.txt" - #exc_file = r'C:\\Users\\liberty\Desktop\diffvc-yuanma\\filelistsexceptions_libritts.txt' log_dir = 'logs_dec' enc_dir = 'logs_enc' epochs = 10 batch_size = 32 - learning_rate = 1e-5 + learning_rate = 1e-4 save_every = 1 @@ -889,6 +598,294 @@ def __init__(self, config, model): torch.save(ckpt, f=f"{log_dir}/vc_{epoch}.pt") +class diffVC_encoder_train: + def sync(device: torch.device): + # FIXME + return + # For correct profiling (cuda operations are async) + if device.type == "cuda": + torch.cuda.synchronize(device) + + def train(run_id: str, clean_data_root: Path, models_dir: Path, umap_every: int, save_every: int, + backup_every: int, vis_every: int, force_restart: bool, visdom_server: str, + no_visdom: bool): + # Create a dataset and a dataloader + dataset = SpeakerVerificationDataset(clean_data_root) + loader = SpeakerVerificationDataLoader( + dataset, + diffVC_encoder_train.speakers_per_batch, + diffVC_encoder_train.utterances_per_speaker, + num_workers=8, + ) + + # Setup the device on which to run the forward pass and the loss. These can be different, + # because the forward pass is faster on the GPU whereas the loss is often (depending on your + # hyperparameters) faster on the CPU. + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + # FIXME: currently, the gradient is None if loss_device is cuda + loss_device = torch.device("cpu") + + # Create the model and the optimizer + model = SpeakerEncoder(device, loss_device) + optimizer = torch.optim.Adam(model.parameters(), lr=diffVC_encoder_train.learning_rate_init) + init_step = 1 + + # Configure file path for the model + state_fpath = models_dir.joinpath(run_id + ".pt") + backup_dir = models_dir.joinpath(run_id + "_backups") + + # Load any existing model + if not force_restart: + if state_fpath.exists(): + print("Found existing model \"%s\", loading it and resuming training." % run_id) + checkpoint = torch.load(state_fpath) + init_step = checkpoint["step"] + model.load_state_dict(checkpoint["model_state"]) + optimizer.load_state_dict(checkpoint["optimizer_state"]) + optimizer.param_groups[0]["lr"] = diffVC_encoder_train.learning_rate_init + else: + print("No model \"%s\" found, starting training from scratch." % run_id) + else: + print("Starting the training from scratch.") + model.train() + + # Initialize the visualization environment + vis = Visualizations(run_id, vis_every, server=visdom_server, disabled=no_visdom) + vis.log_dataset(dataset) + vis.log_params() + device_name = str(torch.cuda.get_device_name(0) if torch.cuda.is_available() else "CPU") + vis.log_implementation({"Device": device_name}) + + # Training loop + profiler = Profiler(summarize_every=10, disabled=False) + for step, speaker_batch in enumerate(loader, init_step): + profiler.tick("Blocking, waiting for batch (threaded)") + + # Forward pass + inputs = torch.from_numpy(speaker_batch.data).to(device) + diffVC_encoder_train.sync(device) + profiler.tick("Data to %s" % device) + embeds = model(inputs) + diffVC_encoder_train.sync(device) + profiler.tick("Forward pass") + embeds_loss = embeds.view((diffVC_encoder_train.speakers_per_batch, diffVC_encoder_train.utterances_per_speaker, -1)).to(loss_device) + loss, eer = model.loss(embeds_loss) + diffVC_encoder_train.sync(loss_device) + profiler.tick("Loss") + + # Backward pass + model.zero_grad() + loss.backward() + profiler.tick("Backward pass") + model.do_gradient_ops() + optimizer.step() + profiler.tick("Parameter update") + + # Update visualizations + # learning_rate = optimizer.param_groups[0]["lr"] + vis.update(loss.item(), eer, step) + + # Draw projections and save them to the backup folder + if umap_every != 0 and step % umap_every == 0: + print("Drawing and saving projections (step %d)" % step) + backup_dir.mkdir(exist_ok=True) + projection_fpath = backup_dir.joinpath("%s_umap_%06d.png" % (run_id, step)) + embeds = embeds.detach().cpu().numpy() + vis.draw_projections(embeds, diffVC_encoder_train.utterances_per_speaker, step, projection_fpath) + vis.save() + + # Overwrite the latest version of the model + if save_every != 0 and step % save_every == 0: + print("Saving the model (step %d)" % step) + torch.save({ + "step": step + 1, + "model_state": model.state_dict(), + "optimizer_state": optimizer.state_dict(), + }, state_fpath) + + # Make a backup + if backup_every != 0 and step % backup_every == 0: + print("Making a backup (step %d)" % step) + backup_dir.mkdir(exist_ok=True) + backup_fpath = backup_dir.joinpath("%s_bak_%06d.pt" % (run_id, step)) + torch.save({ + "step": step + 1, + "model_state": model.state_dict(), + "optimizer_state": optimizer.state_dict(), + }, backup_fpath) + + profiler.tick("Extras (visualizations, saving)") + + +class Visualizations: + colormap = np.array([ + [76, 255, 0], + [0, 127, 70], + [255, 0, 0], + [255, 217, 38], + [0, 135, 255], + [165, 0, 165], + [255, 167, 255], + [0, 255, 255], + [255, 96, 38], + [142, 76, 0], + [33, 0, 127], + [0, 0, 0], + [183, 183, 183], + ], dtype=np.float) / 255 + + def __init__(self, env_name=None, update_every=10, server="http://localhost", disabled=False): + # Tracking data + self.last_update_timestamp = timer() + self.update_every = update_every + self.step_times = [] + self.losses = [] + self.eers = [] + print("Updating the visualizations every %d steps." % update_every) + + # If visdom is disabled TODO: use a better paradigm for that + self.disabled = disabled + if self.disabled: + return + + # Set the environment name + now = str(datetime.now().strftime("%d-%m %Hh%M")) + if env_name is None: + self.env_name = now + else: + self.env_name = "%s (%s)" % (env_name, now) + + # Connect to visdom and open the corresponding window in the browser + try: + self.vis = visdom.Visdom(server, env=self.env_name, raise_exceptions=True) + except ConnectionError: + raise Exception("No visdom server detected. Run the command \"visdom\" in your CLI to " + "start it.") + # webbrowser.open("http://localhost:8097/env/" + self.env_name) + + # Create the windows + self.loss_win = None + self.eer_win = None + # self.lr_win = None + self.implementation_win = None + self.projection_win = None + self.implementation_string = "" + + def log_params(self): + if self.disabled: + return + from talkingface.utils.voice_conversion_talkingface import params_data + from talkingface.utils.voice_conversion_talkingface import params_model + param_string = "Model parameters:
" + for param_name in (p for p in dir(params_model) if not p.startswith("__")): + value = getattr(params_model, param_name) + param_string += "\t%s: %s
" % (param_name, value) + param_string += "Data parameters:
" + for param_name in (p for p in dir(params_data) if not p.startswith("__")): + value = getattr(params_data, param_name) + param_string += "\t%s: %s
" % (param_name, value) + self.vis.text(param_string, opts={"title": "Parameters"}) + + def log_dataset(self, dataset: SpeakerVerificationDataset): + if self.disabled: + return + dataset_string = "" + dataset_string += "Speakers: %s\n" % len(dataset.speakers) + dataset_string += "\n" + dataset.get_logs() + dataset_string = dataset_string.replace("\n", "
") + self.vis.text(dataset_string, opts={"title": "Dataset"}) + + def log_implementation(self, params): + if self.disabled: + return + implementation_string = "" + for param, value in params.items(): + implementation_string += "%s: %s\n" % (param, value) + implementation_string = implementation_string.replace("\n", "
") + self.implementation_string = implementation_string + self.implementation_win = self.vis.text( + implementation_string, + opts={"title": "Training implementation"} + ) + + def update(self, loss, eer, step): + # Update the tracking data + now = timer() + self.step_times.append(1000 * (now - self.last_update_timestamp)) + self.last_update_timestamp = now + self.losses.append(loss) + self.eers.append(eer) + print(".", end="") + + # Update the plots every steps + if step % self.update_every != 0: + return + time_string = "Step time: mean: %5dms std: %5dms" % \ + (int(np.mean(self.step_times)), int(np.std(self.step_times))) + print("\nStep %6d Loss: %.4f EER: %.4f %s" % + (step, np.mean(self.losses), np.mean(self.eers), time_string)) + if not self.disabled: + self.loss_win = self.vis.line( + [np.mean(self.losses)], + [step], + win=self.loss_win, + update="append" if self.loss_win else None, + opts=dict( + legend=["Avg. loss"], + xlabel="Step", + ylabel="Loss", + title="Loss", + ) + ) + self.eer_win = self.vis.line( + [np.mean(self.eers)], + [step], + win=self.eer_win, + update="append" if self.eer_win else None, + opts=dict( + legend=["Avg. EER"], + xlabel="Step", + ylabel="EER", + title="Equal error rate" + ) + ) + if self.implementation_win is not None: + self.vis.text( + self.implementation_string + ("%s" % time_string), + win=self.implementation_win, + opts={"title": "Training implementation"}, + ) + + # Reset the tracking + self.losses.clear() + self.eers.clear() + self.step_times.clear() + + def draw_projections(self, embeds, utterances_per_speaker, step, out_fpath=None, + max_speakers=10): + max_speakers = min(max_speakers, len(Visualizations.colormap)) + embeds = embeds[:max_speakers * utterances_per_speaker] + + n_speakers = len(embeds) // utterances_per_speaker + ground_truth = np.repeat(np.arange(n_speakers), utterances_per_speaker) + colors = [Visualizations.colormap[i] for i in ground_truth] + + reducer = umap.UMAP() + projected = reducer.fit_transform(embeds) + plt.scatter(projected[:, 0], projected[:, 1], c=colors) + plt.gca().set_aspect("equal", "datalim") + plt.title("UMAP projection (step %d)" % step) + if not self.disabled: + self.projection_win = self.vis.matplot(plt, win=self.projection_win) + if out_fpath is not None: + plt.savefig(out_fpath) + plt.clf() + + def save(self): + if not self.disabled: + self.vis.save([self.env_name]) + + class Wav2LipTrainer(Trainer): def __init__(self, config, model): super(Wav2LipTrainer, self).__init__(config, model) From 2c0759714535f5b1bed5dfb3c7498839f8cf5d87 Mon Sep 17 00:00:00 2001 From: Hihixiaolv <143249260+Hihixiaolv@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:05:37 +0800 Subject: [PATCH 4/9] Update Diffvc-Readme.md --- Diffvc-Readme.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Diffvc-Readme.md b/Diffvc-Readme.md index 7fe8176c..573c8676 100644 --- a/Diffvc-Readme.md +++ b/Diffvc-Readme.md @@ -58,21 +58,15 @@ VCTK:https://drive.google.com/file/d/12s9RPmwp9suleMkBCVetD8pub7wsDAy4/view?us ②运行inference文件的第一部分,配置相应的环境(注:librosa版本为0.9.2) -| | | -| ---- | ------------------------------------------------------------ | -| | ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps1.jpg) | + ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps1.jpg) | ③对inference文件的第二个代码块中的get_mel函数和get_embed函数进行阅读,编写相关代码分别对原wav文件运行这两个函数。对原始数据运行get_mel函数生成mel文件,运行get_embed函数生成embed文件,分别保存在两个文件夹中。(注意,这里需要调用spk_encoder的预训练模型,下载之后引用其路径即可) -| | | -| ---- | ------------------------------------------------------------ | -| | ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps2.jpg) | + ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps2.jpg) | ④在原代码中补充引用预训练模型、提取wav文件并执行两个函数,并分别保存到相应的两个文件夹中且正确命名的代码: -| | | -| ---- | ------------------------------------------------------------ | -| | ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps3.jpg) | + ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps3.jpg) | ⑤运行该代码块,即可得到处理后的embed和mel文件。 From 9fc28229d370f1d5f8d44d49b5d4e3e19975cb7b Mon Sep 17 00:00:00 2001 From: yaya <3181871180@qq.com> Date: Wed, 24 Jan 2024 23:14:38 +0800 Subject: [PATCH 5/9] fix the pic --- Diffvc-Readme.md | 20 +++++++------------- readme picture/image-20240124225137897.png | Bin 0 -> 44407 bytes readme picture/image-20240124225205428.png | Bin 0 -> 43198 bytes readme picture/image-20240124225219412.png | Bin 0 -> 79866 bytes readme picture/image-20240124225229981.png | Bin 0 -> 104317 bytes readme picture/wps1.jpg | Bin 0 -> 42731 bytes readme picture/wps2.jpg | Bin 0 -> 84701 bytes readme picture/wps3.jpg | Bin 0 -> 98261 bytes 8 files changed, 7 insertions(+), 13 deletions(-) create mode 100644 readme picture/image-20240124225137897.png create mode 100644 readme picture/image-20240124225205428.png create mode 100644 readme picture/image-20240124225219412.png create mode 100644 readme picture/image-20240124225229981.png create mode 100644 readme picture/wps1.jpg create mode 100644 readme picture/wps2.jpg create mode 100644 readme picture/wps3.jpg diff --git a/Diffvc-Readme.md b/Diffvc-Readme.md index 7fe8176c..56d903c9 100644 --- a/Diffvc-Readme.md +++ b/Diffvc-Readme.md @@ -14,13 +14,13 @@ ## 训练截图 -![image-20240124225137897](C:\Users\liberty\AppData\Roaming\Typora\typora-user-images\image-20240124225137897.png) +![image-20240124225137897](.\readme picture\image-20240124225137897.png) -![image-20240124225205428](C:\Users\liberty\AppData\Roaming\Typora\typora-user-images\image-20240124225205428.png) +![image-20240124225205428](.\readme picture\image-20240124225205428.png) -![image-20240124225219412](C:\Users\liberty\AppData\Roaming\Typora\typora-user-images\image-20240124225219412.png) +![image-20240124225219412](.\readme picture\image-20240124225219412.png) -![image-20240124225229981](C:\Users\liberty\AppData\Roaming\Typora\typora-user-images\image-20240124225229981.png) +![image-20240124225229981](.\readme picture\image-20240124225229981.png) @@ -58,21 +58,15 @@ VCTK:https://drive.google.com/file/d/12s9RPmwp9suleMkBCVetD8pub7wsDAy4/view?us ②运行inference文件的第一部分,配置相应的环境(注:librosa版本为0.9.2) -| | | -| ---- | ------------------------------------------------------------ | -| | ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps1.jpg) | + ![img](.\readme picture\wps1.jpg) ③对inference文件的第二个代码块中的get_mel函数和get_embed函数进行阅读,编写相关代码分别对原wav文件运行这两个函数。对原始数据运行get_mel函数生成mel文件,运行get_embed函数生成embed文件,分别保存在两个文件夹中。(注意,这里需要调用spk_encoder的预训练模型,下载之后引用其路径即可) -| | | -| ---- | ------------------------------------------------------------ | -| | ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps2.jpg) | + ![img](.\readme picture\wps2.jpg) ④在原代码中补充引用预训练模型、提取wav文件并执行两个函数,并分别保存到相应的两个文件夹中且正确命名的代码: -| | | -| ---- | ------------------------------------------------------------ | -| | ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps3.jpg) | +![img](.\readme picture\wps3.jpg) ⑤运行该代码块,即可得到处理后的embed和mel文件。 diff --git a/readme picture/image-20240124225137897.png b/readme picture/image-20240124225137897.png new file mode 100644 index 0000000000000000000000000000000000000000..c5b1d6deaa18f34bb2877ae005a76e41deb96874 GIT binary patch literal 44407 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 literal 0 HcmV?d00001 diff --git a/readme picture/image-20240124225205428.png b/readme picture/image-20240124225205428.png new file mode 100644 index 0000000000000000000000000000000000000000..08489a870a75617270f5f5fa74ad15e9c030cd05 GIT binary patch literal 43198 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

^HK&$r{yzUyf}7YGN-p+Ibjt#(J!wi`L= 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 literal 0 HcmV?d00001 diff --git a/readme picture/wps1.jpg b/readme picture/wps1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9ac70b00fbee21996f88c8fc79d6ce1df5eaf619 GIT binary patch literal 42731 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 M{}U>9=znJZ7Ynzm6#xJL literal 0 HcmV?d00001 diff --git a/readme picture/wps2.jpg b/readme picture/wps2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bbbffcc98acc68334cc40d4ecb9851f4a3c8daf1 GIT binary patch literal 84701 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 l1z}uYD#Wd zO`Gb4*--oPoS1gwVcq(rxhco=+If=X-KyBLPT(IRTlIo;Z%fBe7pL_agSqo9m9q~? zV<+(CilK3W)e1SG#MHVenA@hZ$Onj0m!;5;)1204=EXB|?l2ToS^O0u}Q z%96zo2Qq>fU;c@fTA|2*M_=l(V8K}i->57@5!F9Uk!|J~G#HfDu6iZ6A5Vz511{@C zimldMDP0AeIK!LD_x8P^OyXsR%nx5*lMi$KlD;!_oi_bLaI3`=^bF+vMf=lL(mRR% zQbGOa_swf;RA+2ME3(%KPEJMno^7hp24gw*55T{CfnA0@a4Ejw0yQ~aWI`2aVrQ^~ zS2sCYP#@l4@`h%>WTYbaH)Wl#S__oWSXSI;h2Z;mxo-k6?2f@DwBR?=ly5g1Kd!h- zO~$X!?kl2?@5GNs+$ex4%K)q!JY2ty!VJ1&*1D71^(?QJ?^kH2q|*#uj3KF}f9!bq zP9>+6dx|w8OD;?;zZmwl+Vm_D?5zgw?WH^Jo+*l(54KHco$q&X?JDrLGN0BhbhQ0$ zyZZ*lYBL~{d?V%sQ?v+0tKHnL++vdYAsFeJhy~WoG`*%y@NaVtP|Wk5+ywWgg^H?@NCg4$1BGonRT;d&)1wOgx(b> z#eZNOrAP)(8!z{=enC)Vr&?H|(8LEQ%}tO{pv5=3khh>Ewnc&+Sbe*Y)Iv;FI?{aE zG$d>Zcr=mNs!Xf#2xsOwAay(LLv z+_?)&QZzf#{ht3Pkn8{J_y0@C@P7l)3q0=}`ak`pf$U{{mu>#{P#f+*rKAb@KRx`* zRJkAfJ!%3w(*|}>>kPm%GkLQwUU6Cn8f15UaN4cOXC=nH7l}%KG^=?1R!di*SB_6J zuZsC+`rgiLT=vQysiEo4es5HltLL#*o(}jXAq^&&%dW{gB%R~3()o^@**Wh8QYGE)FtAjV@C)Vawc(|m9 zQTKisul$4u^;@pZSS zOr~T~lO7;gO}gicWLUIy2|IR8Lxu(k)3Rm zh^#&7r%|raIkzIBp_Gb~sJ74WzlZE6{3I6fOdU40cT>R&ecadlBHp_Im9n$TFxE-j z9`(22f{ZLlzPI61(_bK|q*)Ccy*VWZap38+fH@RerQ~BEBVz~;Oxv&sXL?*+hfAy2_SQR~m9N0qhp`hj*m&dV;J7l)3@*}Ib(WbO5pgHcS3%=4lj58g_FWj^Rh zd0r91HiPKE1kb6ZvX9`ehFyPGS35sjUo5rHdboEtornJmF6$3$aNByI5_h?Q=@{V> zHLl=Eqjnmn?#i$K=>QQ_$ZK1jw7;{EH#36|GZ~&R*m=q&8}*slgrx>$X~6_l$W($S zV+X*As|6M2Dw8T)Ldy@YuU>>NDP{BMO&8F{B^5|SKHk|3sQd(!yET^#-g;*JF#9JY ze){&@%!tVgA*nNVw^ZL0HR%OhbRql-_H7cDvbzi~j?4#xH6pdE16u9pKZW~rcxam6 z*j$SHtZ7jpPT7S!n2OO20jYRr;-xw$8L;VZTd}jA-=NK8|I$^139s&G{keuA zB>LV(cI*Lc=N({K@fFM5OW|K=Wj;lZxN*-2MDyRzpT!MIQ_ha_%^(@uH?=YPDrLUC zfgx;@La12d>(L#PV&lDTtqUc4-{*(zR9@T{%M8#|j3C7NiI`Y+8(rl)RuY`VBG2a< z;dS)su#z*^vR!f)h3Q9C?JS3Cxu3}gRVr6M@N*h;{2gmA<<>pwhvmF*M6ED2mUJZJ z8FO@K!lb_y<& zr+5xeCg(;Hx*vla2=yX-}m;s^Iv!`|1fc}a4@>bZ* zUIf<;w!;QMv6dMX$cU=I`0PukfM!d+2`>=z3t27};^csHeFBMzl(fiMtX$CP6$jOn zjX$~nw3`1$ClW)YSs=NBSVl}%_M}d2?Q5hxN_P%*v^~9(6H-y?z_iGzF7m6&qu2bLGB41AFSmV+aPOB^Zl$I~ zQkjO`tiz_d+-6RGdJ9Am^ZSIk4iL3yki`DkNInt^o;}4@mCm%$P&6BHIqT&3UJeYI z1?!_ZDDQ-p3awm5T<%n;{t4FPrg?{7Z zgbI2xHJ0s63}QY}`ZWcF21Q^cJo_uqDf>s<#y9UE_fIx_c{buqi!>Pyw$;VtkOeKJ zqHeXr9>#eqcVE|Z+Sz7u-z8mrS6OZw2~BPvvNuE)7^L^wI(MxMT>Xz@O>$0+Lhp-u zT}dCW(C;;}Sh4T;hzYnh=F$NngzUQyLjR@ILJ|$3GQ9dD%M}upyludoD>(_xewwX}(p5;!HN_|Je zd@=jLn02e=-DJR9Qd4vGj)&I7H$Ucw>0`M~=_me9RNR(Yy)QS9EC>fV8UDeXuDYJ# zy6T!LC$u`c03Don=64z6H_xs}F1fkaxS`WYNLzwUJJljtNGLg~)Jl>WS+GRy+?Z2! zwKSOYSGB;kOO{L7tDjQy0Emli>zg1+4k~4;#u@vSrKoUoGIH}N6W6cShzV3oF0Mm> zm@m~q9;Ej+G2wV`L)7hQ{hPEkKl(-}IGah0Khbyn|FIR<>vTiJZ#^^2$Y;BEbiz)qTmcKZfUHrD#%=lr$2nn0MFj< zG|Bvhz&c1O4Zsqq==@#G=%wF*g@6P*J(`&(eA|t;*`kQ(RN2^8JE@Y>dX)5&vg3 zuZA;9F1Wt=kw~~u!>+Y*RXuS#(@4_ooS0vuI>D&D7N~tj&aHgCSjEPJ^L`1CWorVi zsi{eD$(CY^-kzq}pgmXC0QB%I&={iAz2}w}KtN-A<-PcZ)A3>`y9){`AcDA+%=|{g zyoJt&F8!@pE)Kp@ea21gc+g&!JjSygoK>A}(AzxP%^{3~lhRW5m{_pVubq>`Et3WV z2l*g#L*8;iNLlAp=#6QLH1AuX)<(_+3@%wmsnF03V^^2BtoW7dPBW7lm zg~P|;2v(lm{t5o2{A*oc*{74ENS2{MrZKXp->6&;%ljiTM&?Px$<2m2{+07WHQwIn zBR20y**DL(de_zMyqFFunO6vbo)+{|PV)R-fw@lAEb<*9(BqI=K<%p|Dy{9w^b})V z$kQa^@sGe^&Ihc}8dhW*k|I<&3&3n>dLzF;o^q%y@90|F9-Cl~{4*KE%7T27P=}dt zH~hfg*LVirdR);g-jP0~-zW^sN#40$FVj7T9=tU5iTSxtXm=l;Ou|-|!r)urTz zl*l#cVK|VzH{wj8{Tb_j&_vMx?3KSUx_@QG{0t2Pi(Ax@wiYFE93;`^W4#j?S%4MA z6IGT(U+Gm_bddNOL+u%HNV&p?je|bg?(f$*?XMXa)yh|7!?2J>uUA_mg|V+R_Pvz@ zoY$O!^*&8m)wERGx40A*W*qd}Zk5WXQ3KovS1p3=#b7$yfhxPzU<`=nGbO2FV?46E zRpaoyT0Q+^tz_+FMD6(|$=}^7YHUwcd}>(5zlA4mlC8_u+?1~JzC0lyn)&jSDO2SN z$k9PIOClJ5nB)-&JrZn8WMUM<>pgB}{Q6kF0iPV{dmKh-PG?D<2D{|_t49W+K!DYJPGA5}f<4#|1Ndg5Z5;ejG~;#`Wdd$BsSgqQI^gD6(Yr*`uET+P@Jg< z?;fq>KPcIwvlYKI@E|ZQVfinNzD%F@fiXDiea=%&Yk4uPj{@xtwR?Hnc@2GT(AL5A z{%V~*wYxw;>keKx0(u%<*vEWPv#_cBwZm*Pg{;Am;o?$B)Wb-x!K4Qf`1BY*9EXJK zm0?hYbxiM|AgL(5Imz}(lCJ@Cv!p9#>Ra9D*o&*fSDZ4PkvYP=+*PL9*vxgI#5|A5 zFA?WbdfSHNT{c(=@7b5MMvmJ!MkPMxkp!DzwppPnPtbb1VI`=~xyP9moQ(*6A zVpIbOUt=6n^tv>hTYRdZz+&-ps;5AKg^9Ff4=(`1FJm5$J)xXLxd{J&Q^DvKi{X@mRDeBpK;BRO6X z5wA0exyFG?e=ULVq@RbgQHS{!yubwZ1m(BxY9By)9}Z-(iKl|85X^asPAhFQ)YJ$9 z4t~l$Ax{rVwqE*>@0Mlm_{E7F-TvuK6QwaUtb3|7(LE{$Ke;-+WBkMRN^#J<`>QDx zC7A8-nY_7@=Q}7V+s1Qt;!pdX%W4l^+Fq*IQ266#^I}vFmbtm;-lLNS7sj|RLK0=% z;x!X`K&R%&0xoI(G7#4IHGfaJ4|f9Hv<;%e(Fu#k`)3uBTey_8+J=T)r4!d9Ii4;5 zspurNJzbrS_a4A=?Z;B6ry~l{{16T+(@%}(A}|697g;jESYN^M_sWuf=n)s7hK)ZC zK>M!W=3-OLrGlx#5gAz$J${F|U{*tl133y31LAeK8_9X`9d7MAS>gR-^#|5f@gJ$p z167s$>kKVEvi>@FF-Y3!7+p@sz-lmtKzTXZZ&l<-Uz9my%Hav$t`5*1yatr3P}4Wy zz0bS3BaS@GZo#M4n1Eb&nV6(R_p9;09lXN^+8ud6?Pr9QLT7*1a`xomk|S8By17)& zTfPNc0xfjCurFSdhOy>;fgd5_z|>$$%RSPR{z3SlpFDRnWlKcWZG1b<-?^EtKYDg` z_tdlOawk{K6-|wux6?i!t^IOm^RoUE*4h}#*clM@|tbsA~*@sTHnf>?SpWsS!)`!xzAVfUoca%acfZ^6>>dQzq3-Fmo z(4=W4sdvD2B63ePi=u=%#xs0Bc4!SeGk_q)em|KOItIo$kQi9`ZxI368|5cDrN7}c z;yzxQY@(>s=ch=D)!G5WPHnn=y<3|-ETxZNF3}$Z>%|xPM-o=3V&vm&Wu=dZwlU%S zj!0YvA6f+@PY-;DJq}V@rLk^5n`bZFPpZsf8eDWK31@>D9xx0gtv`qAg^CEy1KtB( zpm&s=z34}aF#E%I9&x)Lt@rahIrZ$zJe`}ZcWwHrLL%KZ{)i||L7tzttfiK^kRlT`_s-tLJjlzY+Y|UCKT2hkzvBmA(RUf%Yqd<_4cxG{`JTBEvb?2wywyX+_r~omL#b z?zuRG>IW$>v=Rkb!FXSwde{P3fSq#5(WtEJMaw; zUh82`jj~^D>VXFoG>+Yv!SmYfjxuPAVt}?IeCl=x?8bN{?*ggH%Yi+5dEbjNv3;i; z;qFy$+}IRC2xjaBWKAXKRezd|pvqE%;EDKOw6;727tuJ!iP^rB4L*p+UaAS0u*EJf zFhOMSa8>C7@91>K0z?ETcc){t*clpy-gt4K*}18i%Sc%yHyIDq-9v@k=-qYwOmoi$ zw-}2?&e;xuBTnK8&9tywznapD&rF-vTyGm+!bPhJZ|}z7N10Q*iRCKgcKdcxKYWx) zyX%|bJafjvL0jKRZ44^5?rXeKt2g#I`a9~x5D_SU-}>(%alUS;VP7>_nu&9Hnm+!(AsE`M>b4 z+)DAD;9ujYVSZ=N`Licu@Cgm7KwAYmzuyu336U;7Nk|p|ytcC;=-wEaivZWaz|ph!hWt!=msr)b}i* zLJp39e*P?DqcMV--qrz4Y$2v7OE^3W)u9N_*oE;Rw|IsLU$}RHLUt|MiT9v|D2arN zZ(&bpHFp zwqEN%4%RkUPe6AL$YbQ)!T%7=yur=yU86#-E#f4D{oj{J^Dd5)P1sjJEAJR_Erv~R z8NEDs`@hZ?GxL$#?Jd0U_31{3e9aKya|4rkUJ)y|GYK-THe)3&pVX#uyNVE#x$S?_ zZWo4Jw4HmYsTbyvVi9SUuO9jcVR$Up$N&7NXrFdAvW2A=5+8uA=6|Iw*JF2?{~pRx zih%!^hVE}*2Piry=E%Y+{%wbZszF0#ZZR}90(&z0dUuH#>XbDHFNPK!M3etr3Pc`XJ3IF15lY^GIG{`fCw{pcNz!^t)G8l$(< zgs1bIAb}V>j5nX-#2v133P@Pw`xELCyjQ##XSn15>A3oB?Hdb8iB}LF2*VJ z(!VMNOihiM=yUKWIm`wTnbL(sw)dN41Pw+5$h$|KHCk&RFN?n)1O8 zbrF6t2-`|sr*ohu0NkJ#v`^7u8T`3hx%zxYHs1s&^^fLgUEw6R8z#rgt|^Od*FwH!fo1RV;Gi`!ji4$`g* z5?+k*PcuQ8z4l1m%H)2||9EaEmM#B1Waj_xAqsJX%#{0FvzPxHq zyTECW{$V9v6^sc-gDFiOx{3o&&FPi@bysHR`Kr@O$A>BzrXEWaNzo*yElxyi*mBMm zK$1y??=s(%%{d^I3WMmYQPa{cKB$B4p{Ff3NV$wE#_WBNLoNN}rnFX27t4`>M4IUzC>F=>`N9z8e6W&Y5cKW!UP+}RtypR8u1mPyDEad*+FF@? z6D-fT@;Z@0U=JwLKmND9keFMp(Lq;WkbT1`r|O&@?VxUs8r+2lR=n-Qz6@8|SxBzB zczh(Cn8XTef*;|57uz=U5RSE<+tRBXNkO)(+m3K401A4cxm?u@b3=@8*SNU?U!Dw} ziq3UIPlcXs0_iXqySf$X@j{NC-z6S2!yZ=!XMTjH-UaS(Hllh;l;|zX1&Rb|GrxK!?rfJBYQU{l;Be5nJ11rIYN0 zx(o7f38t!LBaJH$S4*z&BKA}h>jO(}hDKH|wRFL-^u^5;C+OV zo2n;OK+e`>TQ6nvgW2%dn)d3pGJJFHqUy7*x*W)z5$J(5~V59_eO!BTD3W?it&5&i&?U zWcjF+2rZr7dEp7r`6OcPG_5l{^Ko_t5#GYkIdcwjKDdAC^(MjE#yRf7T5V{0!oL06 zIvbi}XLT+8VMkGD=;s$)*E0-{TKnL-?0xk@f}>05uxQa_JzFCtMC$v8(tSkyC=;HD zky#v}Dg{%ysp#*s?p?IVSyVDha&vtPrUOAfNBj3hvBZ*6o;C&opzDDsHgb@}fsLjA z4H(S!GRZQa$44t`8XqS`l{5UmYJ1aYHrM|ByL)$3MNzbr><%c34u*hm((*Gsc)P@JG>4D2R2Exg$ zYmI{(Gl?=VrJ+YlWsMaj=frj~C|wcbx*)D&d1p68ff-!S+zZU0`$y-pez3np7hS2c zI`RH0e-)5i$WHzBeJT<(cW7CcU7vmB#o+f{h3jDgCKWisX!0%FAyw|Jm}kTiW$Ece9K1RA;nt zGxeLoyT8HzUC5K}xygz>spgD)J`H>dAdw9#eVo z!4y+vvSWbJkpRisbKbX{toA+aODAESbNEijQ=f<_3X~Zjg({#B5W1VIh_Vr>LLVu6 zqckKDu!qpV$Lm!ApfY(0sWRk9h!QKoG8E!zinqc^+1=n4m;EoRgW}mQ6+w(8iACu4 z;(L21$1ss)kwropb$UB^$9J}Kceh*y;IjuiJJ2d}$L4U#eAb0$Lq-60^&7W&fP5xh zZ-=H8MtNaaZQU;BIlr_%yJL)%^5yBX(!xkWXb2*XlrG)9b3B&nYoi4wkNx~zN}dY0{KJMPL9*Ui{4Cp1*LnAG)^c$8)pX%Gv^ zEoIa3xzfEMGBC%2b&Sy&8?93HVr<;oKgaCpp3@hueI4mB=H8$tardG9L2KXd@KcfD z>9(j}N3L&DTRf^^^H(nj9PJknGZpQJQ6>#`NB>a+8ONI}pYD)qh@~`<22-}R_hSyy z2d3FvUJ9D&H`?}twaN!}rXNc^Au~@P)sPgcU6`FTH9Tos0B!7)$+S3!kVDRSS0tb= zJ#GxT6=* zfT=+OT~CgnE8fu&9u@hVel_(Abm^}Ev#|gY-knc8K(vflS>C}Ox+|xF>f=+9sFG4(9d<$Eb+mC(sm3LyTF7cES!OZ&-)juoy zG5y#zT|M-DQw!(3&uXf4-2+zlR{8=cHI(+1y-{zasLdRdefh+~m~QH4xX)zjc%+(j zWW)uk$hZ@sNcP|}jx$&WK5kAg0kwz`@NU@0N-CRTj<4?zQodZ9W1*$;vwCrDE5v+M zZMB_%-rnLjLy7AZaN`jgs6f$*28@`==Fxuj&$+=>w36!;i=3TK3-EDEtBacV^Zy)V zU|om5_l9)5@7<^7#s2;-?yBFjacKdZ+o^Rx?D?>gRM&+&{Juq75cKOCA3Gn&IDr+3 zKKI_l$7;)-AL|Wu6%*csF=EDV(S}2_Z%^3nn*Oc#Pe1~ti@Swi63i1g`^dIpLjZJ_D&o1Ys7W1+NPepv&_M!U0%dNZ#8z=9|gz+RAi1GUYTS{QtTQ)i2k4ODT6XzmQoy(; zyY?j{Hyblsouab3_O*+HsgMG1ah)Of@E!uJDvFW`Gu}zu^GEb$$Sw{gceb=h=stK9 zB1dg|*)n*Gh;3CLbZr=rcnlCB@Jo8EhG=CecY;~NtJZxn-^ zitl5<0dfKp684P6P}G17(K^QoUm*=?l%F2nU^3HE3PpbcO{=4j7xcbcvwL@5t&bV;#i&#qE; zOa(l#shnu2W~cjS4e0Hd0!drEkH)1C1wv!pc!2u)JTljRc!bAWYCrkI*S=F*&%NEN z=mry+G51wK-o+K~zLzhF^^`Fg@&4FFc$pOd3c&ZYL@^Qn%LE_T_uV}Jvl|SMj%HlcDV%Ier+w&Ei>jsuWo()Ps1c)XNH+v4D z9EA};Tg>WV254#b)6;G6MJ;DHu157t=?_XmEB*1BRtKjD6uFkO=1JtxGIYG*(k+Rl zAR=&MQ)i_^En7Wq^jf-I?XXF!_3s?Mp}KIm=Sm&ThMlJAd71#|kGDcbBQA)IG9d#u zcyh)Ov3=27S|h;9;Xb>ONbi9Knzr_i6^4aS`&gLy%4@b^eUse2(u#`2g{n~b zT0%sBN7rNJG|$?+6VT(Iu0O72x~Ko>^90~C1AGicHcEqRTnq9-5g?tjMI8QbhG}5g z^#(S7nT!xjTDHFrFjo~fcZP&UsODv#YFBOvwKq6yssHHyN*s4vi?MjnXINrg2*w~g zSofLjN7b?@dpQ=8t8ihV_wnpUKXZQ-D3T z2rcOc3UC{@gBv3WA?RI6t)JctN)dL(bN2hItAWJi{nABbq74*r8EO8Ae6H*QQ5Gv3 zDeLm}%Xd$p-YIvufx8A6NlCSyA5$K3JRd=53T%td?%ja!1q&DEWP9W04QgbNq_H%&1`4z$?Ae zjgWJ9cyMkl+`ISz6Oi)ctPIFVxKl7nz6_d~*8yBiHVE%3-dtXLVp&IEuTk|oEBtd9 z`5m|M^T^gDd)WyBJCniqc>9X?#NSbU#E|1HYjqpv$jag$9hHb^_H@Uz3;|G0jv0Qg z6BCEa_dTe~?a{PzA6!0qMI$1}&3#}s1?vVXex3>dVA0%>AF?`bQ0Zv!^m(tg)3ku7 zf3f4JbjbT43p7F%e4!l=a0{FqFg-rI|26i9aHZ!>S)|g09CY|PuVGqZ8fj|mtI9L1)xB2Wh7^JS_ zX>zl0%=5Xm989zceoR)-+a1k0`~ki>eK`%xVg1E7Qvwx|pEZf!(TL9%KHD|b4&X*- zxBzUJ2_h%f;W>?xeFke6>cqiKqR5>>Fcp%7my+%vc1$mNM#*tsv<%%cG1efBv=wZB z0Uci(V|_~b)gKvLBmxRwK+ddyt#pYL;XUyEMq?B8@_P|~a|iIV@HYQ8;R0HXqA4O} zYHO`HB!NiX>#uIb3aj|rGZN1`HDM#%FvwMl5AeQGARma0gQ6F*?%jqxcjbKUP$CS& z87X=;7DlAeI~D=Y^D)FOZ*w)hjr~4vcFSFz$vC7Q8Xn+0X8*ceyQR3i+V@yD+hJrdO)lfab127r>15^n^o}o4JMmHPr4~adWkEh# zkd-fX7?il0xfBi_rNn!GFRcOdQh9><&@jU&yJS)8Kz~sk z)n@Q}(y}A2d5x(NHNyR+MG-pk3g|`EErHjWWIJdjQF1_V)*O`Wyd3=QWBo^NH~S_7 z48S}`d{&l&Zq-*#8rZV=17|?gwtTh8@H^H}eZ?`ng*u74EJExdsaAYr;agNER8#`z*wnv z;{(a5{=P&*pe=TqXQUK7xTLNv4*g94Vwn)K9-m=;OBBXy9(3?iIH|4 zp38Wan4kK$2fCNKRl;=-=}mif;IKJ0mAg}fDReZX{UfI zv7J6$?xt*O65#B_r|Q-Pn|z^N7bnel(AvNpMi#6IA&PHIX}?HLB?%hiINH6><_6%z zVFRpAY`53O_6aXGxobdzU6}qbV*A;?iQQnse&ql3A^dGEdov^Igyb6NrS>tGs;PXB z%`J`sbDnzLSAEF{RWDwFOnOJb@n*R7zW6Y2{mWn*#g_$cGLD_G?7NQZ#VyQMM6V?xt>N?Qk z1QOmQOBCGGs?cGjqcn#!j=%dYyfDDKgz-JUwCMP-TIDu1(1v2S#407B+ofne$AXNm z5mOVsBb4Tmwsql^@kb(7w)@B$YuJ@}_b5%z{RJPMPeZ6rF5Y8D(jMb3p#?{1e<<_t zuiw>|j#>?O$6{Dt9(AElMe`&(QB3SbI@b2tJ)UhXjFL zm5+;OW;JYsGx!31QyQm8x6g55^<#C@T$|J2-Ji3%|B;w&7PRP!MVDYkS=;UPWm?Cy z3h7Aa$@v^sN>!yGK_Vj#6Z~jv)i*>gJ=G}?)YIbD*ya4EL0@KC_+JX+6m0>e_}6nl z-+CK2^P??^3671wno@UL3uU4XM|B%KhZEZc<~&d{LcIV}Hc)`hu@g=wGT`oxF|+6$ zm5^K4J)HU=D`}$~WgjV@P#u5_V$W--_M4`J;rOOh{vu@zJEJnvX%1`e>vV zLe#YE%$a4pOA(CcV_Y9$J>VSjFDZ1dMcMc(CHj9K^sqy*989x(TvblgmPuRhpO?Gi z5SjrZDCKHyKhi?@_d5@`rYMwY@3M6#beVEvE4#C^WIwQ0?v&fgoq6LvGvWJ&@eOo<7MSs!pWOI5HkacZ zT_&+=1cQKQhxpNR-VdpsfIGWEFlG|^Hq;4W% z4+=@D({*p6-oC(}6MiUi3K(yzkys{9yJ+o=o%F-+f0a`lb%^@=DN@j8cQ>A;5N8)1 zrL>6OdQH62b(SLf-0_s7Le`5#E15+L4z$=8%~@Fhdie_Ks_>XeN|>v7nfhnh7 zqt+pSEE}0Rq;>TDbFmvC%e(hygfVzjViRC3jvi$zw}ZReIQbf1%|5rC78J64>AMcp zN^8IBKV0CIa}fR{?Q2iGuq!0^$e+_UbwUx4eCVlM>e^~#ak=}e3jF8^%!du{{c*PW zh-xG6+e|M7eWSd6{=@Eq^B41vbt!Dz%ZU!z_0wWX$HR)H(^}$zQ!Ynx`W!N^9(1kj zHAQCRd$}NqN2lAE8(WO(Hc+(D#%`L1=}J6W0bCL!Tg5+k`34Q>E8C}Z zVN3BhfohV7CI_pJzgmIx1;pu{l5McPpb~H*;KFK3NC04=ULYG3!(kU;QxR2u;&s^0 zm=;GtD{k}4@GJ`xKTJ_XIqg%I;P1e0YfMng7$(=-{n+XhtlVL?R&Z2o_xKadX-HI{ zTm|s|!6-}d%sHbr&8bXiE55F|AEhkR@XT$W(jQ_N)o;qWbDJA-j)H|*mekGTn6ZXJ zT67tg0@ohsCXj7rHeDB;!%SOg36dj~SLSfK{kWh?jj1{B$;?kr%8Q*2k1cqH-*Y|j zvLVN0^tSxjHy&zxCwr8S-x_;EIMbuD^kQ}4nogE+mWQ@Osmf4ZdRSQdP@HoUa$-V_QtCNfknc*hZlB-a=_Z2w`^YGJ|Xy{MpXgf^l~^MrPZsJYN838I-+{yMLik zK@g&HIS{#)Qh_D)wH54bsC@zHL#~Gv zsXtsBq0-{oIgU!YV?!&LF2@^wk(@2rti;wgy-3;*-^c$@(TZ@sQ>d z>{-bd5ZI6?#kJv3u$zGLcw}1TyCXxnT`CWE&b%%G!PKv6NFd{^a6y`88 zxkT23G}^MbSH-0%B1h8}ZaQzR_Hz8K741jT9^fC_6ZN`ae?K>1cC+*vWLszQjdZH7 z9U`OB2KqsiPBeJ6nB?UOH>te3ejjK=QFQUQfAyn&(O20pLCR%3GCPIC&^!RZ;1+ij z+n(bU&FT#kG22^D+d|7@wU?CcqR={}G%9K1K!8(Fd7 z>`=w$+-llPFqYVeX&schABmD6FyY?a>OyMVC+#A_l zgP4}o5tsUnqzjuhXy4_CwkbVGN`-m^YenF2yM1y_6?bIBkX81vv5wX`a=53Bdo%9O z=AlA}B~UHc#qM8TStk1tb18eAD&FjtRi`b7ZJe50*L_H~FPGeg!wH3C2cPr!?F+S} zM;}HPRmaTALka`48OI~KvcWJL*HZtUY*QY%74t$YZ6yXhUo=-m7DL|VbNy6f{lsuL zx^`fbbMcG!MLVrnx^4>2hGE~3DG^wUMsuOXS!|?sD`P*ee(|+U(5}Hs*Wi8^cagE5 zuk7yv2Kq+6LHl%lVUai=Atyj)pW>K3S&QP`n7;eveHMnYccrD^swGk0pmfL4_T4KCeEj1;%y;?VG-Py+cHLySl z280Snp%GPxh?VL<3Mm?n11L2iHs7m*UBD;?uC^dB|C&VzBkl7a z`9am){FBo6;B4g!KP@9`ViF2e&`R|sv<*967t9#S?=6i{NDN+m%~U^GQr$=pMEs3% z41c!ya?z8cm2);YV%KV;nqB$;_m;wF`W10VJm)t8ULB=;AG~9`Cjnd0dw5{|My!Rz z=L}0sS&M9G=$~5r3w}4ZncLQg)NCjU5{|Z83r2|+Dj6~x`~dt@m&&+~f#KHIFUS~B8eg^aT;i`164=@K%4iG1MNr#WCv(xCrw!b@VrUo z&k5jH0zux#vFt;N*YAnaWwNGx=CgBQL6Ie6i|vacDp7B9Vw<4T!p4+UYJ!(KB93D& zQ6IEYBwr8{yB}AXCK$!Vu7)7y0Fcf+w#Am{W3~9N<{C`A`PYT9pkW$gEa0Clqr-}A zI%&PcaO9>I^pLfgOTujz>M%B}g*8KvuNiimlbBMyr0n6xB5e-3>gEhpzPU9g? z^w~A4Ku%VmO--rSBi|>0wy&%~K%=jl$64};8Vd503+gzkPE9GH;YUvKJ!PL4>i{eM z?|MuOz!+{7x3}X7RtI6?Oy3@o#yRse8Kyi{a8{}3+@SXt-wVUothyI5^d~MVvqoEZvF%23&rh0JVRw$M$o&AlHc=uvoR z<}FXJ7&{s^KK~$Qgv$JWX85P)ITIH~X4A{31A2QdA}5eZp~q|1bC4I3jt1A-&YVJ? zxa*6G^>p=kc<_|<>d=m73YV6;!R&lqtL^6oKQ^bM# ze6)*L>V>^PS{a9)y4u2%klV`6- zz-uNgPCA%fz{ApzHFBc*StIZ5w09DKc&&$4OK>noKv5$g6eNxi$Rx*+?=u_zW-B}* zPOHO#_RzhCG5&f?bu!y7cICTeq@Qb$_hR=^e#`}?qa>%IL5|Xa_%5)AO-t$L#LR3b!JeE@!Rb+w>juWAx-o!DeL` z!^R`2#J=3)5rKCq6B#UTuXji9wXVh+eb;dNx|u$$f3~z)qVUjRS*{AHNQ8OJv@%{m zAc)f`Kh}G*+cEK+1Mt=3-hFLy8Z-N%_9eAh$!-?5oP^}J)rx`fCdG&40+keTA&2SS zh+_L^I6J|H&!n|fW`lWok)s&>imNIy5P%iu7NrKr6=L6Ro?|cGlW$D9PC0;ub9wzF z-HsPvrM%UmQV3V`6pI4JaODE3`ow&SN~`o~MfrvgWBLOHSQrTk54jpG2!X;uvMF3N z6QVRXWe!%xV^w4?!D7A(O98eOL6JY=LQKY;oQ1-{PIcY58%Sn6>g;b)e*dk)mrZ=b znZ{UquRkT>u$C!P7QQ9%g`L=uQ*-NzEXLjD6!4G1ZAsEI(WYFd@24wf9c{t zra|Y$OAiup1E|!y0nT(2f!=XZa@%l;I@Edfvn>nRALEOYSBljSEyt$OO|$DWFZpzq z+nOHP^^)gcTn}o(r#o@#!z~tCt<~@~r)#{xebCO>n}ZC=xnh%7zeE)GRub4^S0^zOOV;JOsV(;*l}~&+-xdJczqMzxCk#+#;uNa`060n8 zO%U>wgMDKVPH7(O>2S)kz^8 z4@TnFrY7clH=9c4)-z|<(i<<3*8x!# zFWX`oT85oJL5dUVCZo>I%bEl8m2Ij7j|&dOJN?RhfI!=gz4j(u3UU1oiMzYMS5jedrotZn|_ zj;n)%S^fkTzSjD2y1Cgg>=SSx)zXtu_cv@w2BPd3jy?e)x3nrtjr-iOHg!;(ow5X| zay_R=2O0C z*;Zs-KPXY4K$dN0QN|oLgz~;CRd1*h4|O$av529<{icyRLGt^L7dUwvx;dw{1uLz6 z=y@wSxmm^a!zXbNz@gHj0Y$u0ww{SZyAMf!BOpOc#gq;w35Kb~3pvB-VO5gz?@NbL z3ujsn+e^^E3K4)i5{Id4dclhLPHQ`|)8Q!{;=QFOqgcuu78iCtTwXB!%x zqIKUvUnwJ(zGC0JF_@}q*gY9+Qqfhd^v;a1ajENKT3uz&{@1;>diS&2R~}OiP<*Lz z-Gb~_2zl}um^;IVqrm{|%w)O({aDRVXYX=8NQVvE&52SSIo0Gg^EY zq=pL;H|AxMm$w!o`+VJq5!r)@4JbIum;CVBj5C&mY}osA>$!=h;JRw=J@mPat4=-- zU)N@KpbuJ?NY24Fr9Y7taHI$u5|5TcshK_5zS)p;YGKL(-YPWSi&{6yDM;6i_2Uaz z{nElAbb#?_^94A(lV)TLCePOi_>8%)kL-IwP0ZR&?q z=6!I>F9O64JBVl)0&4Nlozt==`2^IqT5H_|RtOa>FSUa4U0&;saHE>HbF>f<=c(>l zC-{Z3XX@x6QA4ydVO1uP6>ep|nIW}=-ymd2T^QiBl{+pqCp}mZsbqdm;0p5TAu-?J z(;|SkMGI2!=LW4bK9H{sw0NjC7FvUY`knx9^Q0CGs@UXeWUyU=e%m+yzr#y$;_fk?#fA(00(EuMEc#}GU?LGcP?d>HeUfk*~87# z$vIZ5KsA~BMRT{iNToK<0~>+umEHut3^67pj3+%RK1+WTWkSfI@Iv;$MQa!c426JY z0cyH;@xED_g|LDE&^F}T)__Lkk~FYSmGsMZXcI84_Y0;To*P; z+@QwJD9w=-qjxcd1XpJJiX8cnSd#w1iiwW%OTz|QDhyW`1mEAVANthlkO`eB?;U;P z0cH3LpwmIjpZBd59J2N65AE{KyV`4Xi+{LqH^F<~{M9+09_P{kZ>Svi*p>T%;aOrk zIcwqY{a_X2la{{Nma9c0P46NY%b>)S`V0JEy6r(o@(N4y{9^mXwqhLRW}xc2VRgpzZ*cRPY%OHIjH!$Q*EjLg zxmthDQAqa@DvSYJ`iMtTi9R=by0{7wMYTsJa@IUHCm*8x3l67)x8dd>3SB;7oM5Fu z;&%KMpo*lf;S=x9nHpSJrM@3C(B03$G9Niy9GWsZ@%Zrb6%tPo;0+DVD>;WI=9TD% z-^-albH?eiw@U_HFU&Bn__Gm#8|6YyOHZ_qI{yKjU<`?oM|kS^INg{Khhtu)BL4@4bU%%UKKpqGtXG#B-&*lSq^JkDx_ zt^XR7YLOhKFNExsct8czvNvpr^zeJrtCeFmMba7<_o1PJ%Cx&Id+8TO04edWW=^Jl ze~I2dTg;DM+Z?)TH`!>ERjyt!{q5_%3YW*z;p)TyTXQxQRh*VZ|7_E@WGalb6qY6k zX}idt;;tuR?jLZC*iYp1xjk??=0|vxHXDj-qdiuo?J$|tu!WOCpdEcqz<6m&-7~i? z_c7-#SDrYD2i;98H>16q2Z;(xl_A#9=YLcDnPa-IwDjQ+u(JTokh7~I0JjyQ|639| zgHOU4z{|(AEa5;%gEy^+(l5{zo3)C)W`1{ruYSkNUZwUJ+ycc+s z>-?x)@GRFq#aL931a=&m%O3*KyH*j8(JC!RDkNuziH^*@^SJ#Z61*@U;Q_!jmgx9( zlc;Tc`8tu$Wz%C4>t|%!M)u&L7pw-X>`OnsQB=oYByYdC)e)FRER~qRT(OTSkW>ydzW>A;WVS@=QbAQEeG!?-`Vot> z+i;AE>;FA1&)qkCe|d5BaHf^X(#WX{hFM9(kOLD^%DJ6R*FENaDlElu)Y4fo`RdUe z!Wo`oDdR@@6s=9qMslDusw3&D5KGzX2pup|0&3+eTD?g_xj=baZTn2$sO*K!%p2lI zQxu+~UaHSULWsy0+WD8T(}*HaM;cCN5J$=5(*0Yz+1#ZGZe4gX2L=0K#nX?-XE!WK z_!&-7p+ti#4g%rimC?%Xbu^WX2#3tw`#HxE2jC5N=y(Be=q8AEi18^_iUh?=WB3It z*?tLv8Bn+IL|WkD{_*3-scPb}q3rxCXZ78Wxi`FVC%8N&u*6n`uED(72qo^}k6Z*G(GO zF1tm&tk|Lu5qhfx1yjq(j_;+0Bx}$&YRuBacOcZs;R1qji#bHa@$XVp0@&RhK3J*J zvhoGah?m<7?vP;@P?{vli_2VA1AW8A8VXo+N^*yaNPoe*``W}AwF3&gV-j7hCj_C4 z#QHd5ENfbAxhl%^Ty?Kt^v@07=Wu|Nl5J+od2X@0`)fU)=E+VQd-DT4#Vn=UF&13| zbamp1Gv4)?<_KTU`!2WgJvg0w`}$d=M~Smj zdzQghYr%*_S)+rqUrg%`I^Bh+KLo`kOAX0Nu%WmQoRs%^@hFPbM6L|^9K2n)5)Vx5 zT4dtTM>rQ+6n2#eF&b^6l>`2q6a0FH#$_=k_UJI`SQ;VcW(J$}I(mV*lcXTxF3p3+ zN-B>VU9c1Of=Zo~ZTL*9Bftr0*ZE)0pc0?#2!=VhZqdh1%OqIuq%-uis1-s!r(f zPI`J}Fz!2yqz5l|WvZV{A07zE(#lgimF8Grug5Rfi@F9MS;GLX6=FsR(Kv2!Y)Uy{E1s|0FTn(rgPdp= z{(X+#(W3tzl=k;uGIGS{(tbH#fKc#w-KgtnGJnp&%fbHGs9W+O_2xTyxxpSP{%__l z^T445JP-DEPu^9{_5^+WIG%fllB#ta=$?_}OD{Q~>Xy0nzttE@;9-yIL9;MCOfp(Y zSukA5B>eD>TULN*g1D3xn^KZ7&+0c=5GIP0?abjkB?y-tZ6I1A=p05n)^G=JvY{Ae zJWNJ^5S^d#9mU3f3{M}cM?)g2~A7{9`RvE?t+H8cAC z(24`BkNqv0mAH0cpkt(#&=S9I%Ab4fgmoyfY}4LGr%2y9`FWnU150Ky5b05q8OnL* zS({AotQb<+uHd`fe`Byx|1mbj#?`RgHndy=JDKP2`s^dS$vtL2GS&IY*XfDmBMLoK z1xlM$)Nk`RbrzD|&6Qc8D7L7SNZ)83gfT)a`Vl)eduCIuom;B_YAuBdTRNewZN^Zz z3ttji1wY5D0v+3o4_xCRaapeC@BT!cE`sj_T4;Ug>q5i2^!OE{N!JDl*M9ZxGj ztJ;swb-x5wKd}NQrMY;{>H?R(DXu|(1;3VTV|*Rg(CAE@kI2l$w+{8?IMwdEmaT7Z zH|kpCOaJ2UB5i zo~$R4qIs4=Y`J*7YfzZC48^Vpbg71SFaJ6CeHo2yh7Vkae(^g0o{W;3BULho8VYCr zqO_TkeL?}hgeK2oQjzv7yt+gk;%r!SR z2)y;52Q3=<-eGbHdZlbr&)gyxY@kE>-7%@HsOF~=FxkO6KN5f?z_VbRcxM-W8@vZh z_I{Nfk$|g0D7vjr5w3I`xjP-t-TN6Xj1*#i{jfmOI5dJfQr#xZcf@Y^bSHsBpHQ%rnSouvGrPem-otJ zi4U~3tKdx5H@ksb^^QF$n6@aj_o$PCAT6IL4MR(ZA=62_4lgy_k!Wl2%HTc^ox#!( z3485JPHnxGwovnCOUd`Q9?w>GA-`oDInb5&EYHmiBgI8%g%3xyFHF5#VXqgce)Y`D z+|}iJJ419T@mTSR=ZVL*{BK|XqY>hN_CstDu#8nnA&zjN5Wf?whbNjK?eDDY)T%@8 zO)YaZWSM((@&(|6xB2{wL5L#Z;^e-OyW``YkG>Ay>dsS*buA51!1e?dU1~Ft3p^V7 zI``h!_VZc*HY&0D=pPtAyecrmhb41cRe-4lN8)qqQSyws)3BW`_TOR+b(yS!_z;72lUjQ zv)Si$b&h6rMY5xxHJ!{=;v9xKgXbXQICAI=;$eYWt@!(9G$xE5h~Lo?3R!lW<-D!C zfO=j8wwZ^K#qkGj*7NV~Xi85Hwu_PM+DuvDC;^XPT;g}12n@76X1TE|Nn!UwEnF$G zWirRW>Z~pWazp;j9;qTinS6mSRU#orFzncO?H8$SQW;043XU)gM%`<1u^0IDL2fG# z4CcxqT%?_Ba+_QCP^_9}blVJII(>1wd*af5-;Dd$wcJaNZ;N91)Qb`xH&u2K3H{}c zkM7^xT@pY_c%NM9T3xfZY~YT^5m(cqdxHZih3YS`Ji^y{x{KqCrtVrGEO<^nzM0=* zUJBC$4kXdqO=XfFFMGtBk`zqV6NPMlEspkpZH*;%L3^?uaO23BwMcOAFsmE%8!@Q~ zrKcRk{mt6JUzPRTyHq~#t^o6}bl%k2&Hc~$!4!w|ynWgZ zDKdKJox13zd(706^KEqX@BUU$T?}-yHs&a37sU!!Vx`(NpZ6=uZBdGwMJS8%PWp!{ z`xgRTVsU#mE-&Rtihif0-tV$EuhrkbBdL&1U0Zaj)_LAO7}@Dhfv!EE9wOA z)`@9VtF!(H%`Zf1yjp`)x1V@4>35DeDn?Ky6yK{XUKr^QDA0wrm$C{$L_l?Tx^|xz zP3N<&g}L7Ak@WPDsSd$x48y>(v*}`7IX0kgmAq2RJOc z7rACynB;t#wVyJi|FpZlv7P*f_{&3qlOIp(HyC>r(i-mqn*!>bNEUry5nZp)rLbqw zo6@xs>ut_kc64V$w9dYSsnc*$H9X$_023oQdzSSvTT2Vzwo0nY1;yf(*r|2AV$ZrP zsBfKJ;h`TPc1G=6@Qx{EmPt;7?m@&B{6fRto{;P1I1-x2_KN!wvc&kTLoy7}8JrSM z9Z1MJQ~dj1*{*>@bEU-tWm!WX?(?ks$6fci|J`JLvhrEk+3WM(d%xfN^?tuz z`|!)*tk79oD;q1JBS(%1{VjM49Zm{a{O9g}>-C>b{Fra^L6BxqsNY)IC)A~L{!j#cvk3_BS(+^a_s2wT;j6sjT7e_{Z3v9k<@#ZS$azGW<&3Jr!ltDwR?}B3yVleU$`ivtfH!R zRb3xwU}$vR*y5I@mGx~KTW1$nH+K(DFaP@i4+4Xru+T8nlW_FYh?v;8_=Loy$|r0K2ra{;LtxGK9VU^+Bp5o1cS+%`}Tc)VR31h z^J8;sduNyXbMHTP9T7VEKZfkT8}`50B`&b*mt)6{9y|FTyN>)4F1U`0A3J_o_k_d^ z$CG{`=dS2IJ0*EDv$UaCSn-+@`~1DfVv_g z(CMQ`1e14ET*zFA|3f2#3S7HK8EAhHBUSk4-V?$bxM2&O4EduKL0Ccr@RN7oL4NTc zvUW75qN+TLhAW9__W*|2gF@6|$?lg!A(8hTY=e@hS`Tjg^0+4up{E)@_#NeAd*OAm z)9Fgh($4fj%NMbmyve}L{t)5t#CyU&bbDFI4ZjsHve%84!W5fHaG^tV(G!!Xa${S= z_J}UI0))9rGuE{AZchww<}BQy-m=aM`kO&oWiL)^Fa!8?yjA*xA0oWULe}Sve&!P0 z&<V{~=JYttj( z53--wbWCXFf8uK&3O!Fi+r2*&x&(4$r}UePf{;=vOmis++QFHd5m1Hp+PJ-MpU+;N zVM&#tq!W6GmpQV%r@boT%KEE=Mnh0h;bEW-gvo(cfzI0A-Won(D-!vanQ1c@H>Y!s zXG#Vsve!2TAOKI44gdq@TF%v{o5^tkc2l%?7Answl8LKxQDvGW5q&vsBRS0%ArY z#-!Aa>GR8q9Y>9hWh#>q`VPW(r?}>~$F@d%DOQwDg{Vrj6U8Bc#E4rjG>-zwA*%<0 zk7&gzR2TfIo(DezBSv#SYH!Ba8hp3QbF@7Jt5tY_Qe68=_@o|J^|;TbZPWM(e#7^J z6>hTUT6k&^?YdL#rcUn+_o`M;|A@sbfHYhZU#tX4oRN+`ixKC9CwJO0MA`51bcxW@Aej4V+=_r0X;D{0#$MfKE)j~EXV=ZEi+DjF>j>z_sCA*U-Zxg$sN$o(4^)Q?wLrc9H$5TDtd8D9zV|>sPU2AakOJl_3LbcPZ$8 z$+QDo?R}c--)kgAg+6|JR}`zRltAifLar__wZh*=E3rpOVu}{D?u%S+lG$mtOM6&5 zv8ycwatROqP@IQ=egH+!v7v#NU*1F$qpc11*hVOL8*!2+Y+WIq>6@-3(DlA7y}s33$_ zbBhm!Zm{d}tGEenp#&Ix*BzW{XzA;R+`0AjG(qvC(nYH-oTi%W|muisyY(ZlfJ`n5@avhMYK>S`pQb?<3XDu)j zl8I}IHZ2E*ft-YXEgesGrwk+>ux7_`XQ}qqCx{;k{RuP9t>~(G0fNq+G`k$7H*HC2 z2D<{)y6L8c&|L*R^bB7Ft~^6RGB(}0*VsiMo4G@w3DWFnO7t~!u!1<6P73$^h0_{~ zF-Og%DPwLN3JEL3Y|FWQVwpr93LOVmk1!9U-P#cjH>g`3k;rqX^# z!phtpBnLl~&sXO*%*kEr#h&C{EuG==m&aL=au&0rseB#o9LZOUdYQc?FaLS_0ENm( z33Ye?^f>|7Ix%j^o;{$F9sZcqOo-gs zX-LV6yz}9#rE|fhw=xb8gH?8*>HRaCyzR+Ki?v>D6 zYy1O#aC7tb7EW+_hEBqETk{maGQ+jG@lYr>-Z}i~(*WF(OzWh%1TQ?0J^a2~k@KCO z8(urX4gR2mC)WT+kD`NrY|AfCQ!P)ladZawzp%AmG3%r(3=?^fQcu0ITlol-a#!cH zA9CNEcPhay<91;$po(G5Tsztqk373FtC3E>N=@{5mZeyBe-@c4IJzhBI;V#ch^>3=S| zw)~UYk#p((QCqa_dXW)H%T&o#Qaz zkPJBkR7i>bjkEDwzm)C#US5{to}@45Pon4Lo9S^Ry;NdmMGw@6SEYRwy@P_cecTR( zSO%{uevI0-BnBC4wz^B*@e!@H{<<}~DkkyC)ZB`qrJrV|lKG~AqN(*za}IjjlbtlhNU^!QSZTh ztVnm7B`y~;mOpuxqIZQR2hsn4-n}|u0COghYi&dDu1<0Fwyvs{eP^n2)NLwVVC9JB zyoOiKG2aI%)eyjN!k`Q3-qn$DoKrzeobBJK2St}|DGNvbp@~!5hdl~gw%4@s z!|;el?d*I~p+d4TuFU&n>V(%_^y8nJVMVrk%g4GnntDM$CcUgfDj+bqqLN;nBOXvd z=_J>o58-E)I|r89SbM@(>FK2E9||Qt@v<+PyG=B;-|x>iuE6UgYKGZ~+D&%d+?(Hi zSlhg!0e^9s{P*;f07+QR)^l$Iv62eHTs5W zgC)FI4u#H4EOpEG&?dorYWJ8vjS{?G@wP2_)L|2sA951telqA|kxsxxvh$;wx0`W+ zSJfSz%2(7;S91JRRh=Lm6=gRe#!DeL2^Y!#vN?EAv{Wt3qadu|?h6r*AWHw8GGimn zbEpQR6xypP<2#PYE^hM91l8|{LT3+!;FBeB1rEv7$~#$V|mw&m_xn4 zb7M;Y+I*>%JbJ=F1d{X;mC1sLjh5fwD7tgiXJj=&Mk|-*oT7eP+@bHCA5COZzPBW} zdkZTzmpF8`wNvVrAV;I+7TfQ70W+TSB=^GH+dsV--^(khUonSw(0NxtN2|?n1`RCP z{USGw`r{WnHMLMz*Vho8=E`}QP)Vr*kSHpNX+E*p7cIekR)x9ULL>tLXGaK&t^7;b zE(!h81m>|Zvx`gSqFU~McxRj)mOD5U;weV>Pl`plrmy&>&$w{JUKyvlb^qFy%;;&_ zoI#ccuBt*#F}}xEIg|Rt{4f8zSox{eEgNSUOQ))gum~GvBGZ?}R=u6H!R+HsuWXX!Msd2H zPh>LEo4+-2OsTv1?gib?loL{FaqQZWgF$@lhf2U{gG?k=rR}NKw{w|K3h>ZbWJ2*N zwtSg()0?G7OS?a~*ZLFyubU|MA`zliY~(X#`hE8JGv(quwjg&GISn33_OC*mYb|7|?^O>l*&bHi9nTYJl9!F0NVe5gcsHE1S8Ea! z(jsa6sj$)p%5Lhv!%f$@#MO8|E-y?Kz(t>H10gf$O{ej#E*XeaP%HiIa6S@h z3a9eR*Co1vJ%r@dnz+6Ol+^%@it!mT?ulkfT=(}WO!!LVMq;!SH}L7~%$YyLfnPx2 zdUVHJdK^ca-Y>J|o*#Wa#fB}P$&T1`Yl-yaPu7DB^B8KhI-RY350hH#&V89Yz}*CWF|$oC>Uvl;KV5BK)&HHu=N0y#Ef*SZ9r)>2;%)cW@so0}F;~?-Xvhh>?b98W+ zSY*HPJYH=1wodRm5Mi-z#rk1FaD0>Q-EUB~_DaX${7J9R?e+04g6tfo53LH#^}J`g zv4o9=kprKg4MPWIHuoGata!* zAc^DN-o;KZa8pGaiCQzhmg8F!&0qe8bxZhHy>FQAs>pr162o*)*p>xH+HhJ^cvy}G ziLvMXwEGf=$WJiKubA?Hjkiy~BrGC(oE|trb_4LCio2+9 ze4hU{rDC*nz@WZrDu`SYf$0wCi0*u#flOy>qgO~+k!|miL|+B==#W@wzs#BXnnge< z_Ya;JjiLmS-k6C2#!{U(e!S7ujU zuRWW&o~c=E-ZHKQa>-WyI!RAwa9ZI zczl5sY(=cCzbU97;GykBvjvAj_KojiRP#cPCMRdP_ZivzsKeH_(_PDeagJ%qmW(?) z7&8Bo&sBUf=1YzFd6!b(3lHf>ZEF@Q1zV@6Rdv6WE6h@p@G-eqq2NNm)qBO_(?Hyd zPmlUPZ)(}?FxV9(HJAsKdtB^X)XkyUnYd_m?(;(-pXQbpnFmdk82N_QHjaJVr629R zj4=Q^5*W+rtebp<YS^&1 zmbLr7a?`SOxWtWfc<)+nXy5c@j`VX3xXKc1x=WfIfjF~A-o2tMEz%^^9ecOd9wtr@DRGAx zK_VRSCi}OkIZPo3SW(@!8v)G#`9x| zYN__gh)ylKe@3T(ySP!IoXe$(a8THPt<-f>9i2q!i&R>=>9REXa#&{B z=(GD{d-2z=i;UsLdajpTp@FML1tEh*^Dpk2czmVoo4q28)k;6hjW|1eIjMyDbzG@R>C>KPKZ*U!#-t2~@y2nCHS@wM4^n}#`n-*Z9@;l_Yo*qS4Yt1aSoBKmR%5s1;^FeYQnUidD7Dq# z=8m5m4^8J+-YbaJ@ho1W2|OdM?U~a-dT5AsC#8<4rEb(|z`pBU5fkZ;Tb7E5ZYcM{ zO${$Gq8p%Cc*R`@t?8okwUn1>J`g6pZzQ}gT9R#sV|w7&L(h5|u}lDEZ5^l~j4?b& zz=2Dl3Ad?R2RG?%O}_=@8+w6i&$A5+e5U40B7(KF*YD+f2E3#zyV{33p{hP+7a7?^ zs=sKX#s=_5(d|Z_H*<~nn=`3Xw%JY@jbnc3=jzqzp;pcz1D^_tjL*EPcI%rZFkh30 z+ZLJZ7rE)RrwDX{6Bmgv{yW$9w@rXB57WgFxUzZRf%6Rt3Ve-OGMMqaTZNlhuC=#V zUpX~{D|tq&wv!8Ig;(<6eW& zcQLuZvquW^bEfu6t?@;f3*YmCqW6AN0 zkB9fN`)(H0t(%5qE6k=9SI=ra80ssCFM7%*SyFh{XVtbw&!Z_|3J6#V&14K9aGpVV z^2)7a<&9agvT8caj<@f8+@?P<#GKdu?v>C^6<|^`k*`6VMLo3CK-fPZ!`V?`FUw=? zxwQlF&)6GI)a{a^3}|~?OQ}?8#V9k%-IKbpm1f_!Jn2eo>&SBL(rj2A`QCQ0aptzF z8)9J4^&C#!_G_JsGv0_!eigMBw%9tlwrn*oDmHL8S7zk#9_qVoXHE$)0;uc!v5}`G zY<%r~FO$7>P2|FAU_+^6d+~I3)p8HQKKgPE|BHL;6W=r0y(OZ&TiI|2_LW1SfhP37 zUxMa(y{reZX@^4WN`zLherT2V^4=h!$C87X!c7-6Lf>l2oT(%^y``47rYvDo%*C^r zfiXQyO~lbV91sPKe{Uw<@Of*vT2y0r&7CEkE*LmDCFN;?DKYkJFM*p!o?o_mIcG_X ze`s*(g(tAL20rG>51j6Ie;G~uW@D-UJB zj6d#qEMdIbNdhnPB|YM78t8~1F=bN}&Juehcv2LM0y~*82No>$WWG9DV=xoW*P}cH zB2S~UnU*uj`WfP1ppQKUuy~kTOM6DVHgXLGCR^7mJtiG{QV_t)_!gNEvt5$fT5=W> zfJamMd}YaIa-LE3{Ij*#Ke}zU)BGP)TxsG{h%!@~-ap$t4JCppnjw!2c&~0j0kM!% z=WehQ;%C`^H<3G!*L~#{_E|Vki6bVCWZ?c!;6wCnCZd-0YX}239J2}Wq`tC8E`CN;=tMzH zDx*0&6&AhYDo2fYWOVn#bvwNgm`hguv;ICKD09w!>(Ote$$j?b6I5kQAjYX!{jrl1 z;kxyNbMH+FoU?uMzx;9HRz9Drz;jh#jCW@p3e6l9j9Og};ohOpqI^C7mqVeto{|TV z-DHJ-(~LE){f{Se!NR@v><)!60KR(0|I%9i{~wLBRD?AjMrbAuE!GGh9KZj69gsCR z#0INWy%QzG*KDd;iFGG6oQmII8!bIe+dhV_@182k~2W4^I5LV?{r`0 zF+z`JX$C8KOwc@j2GH!(m@R+V>z%R6U_#CW<>$M%gnre}ZxKXLOZ~-h%B1C`kXw!3 zL&qGY(~bIs4K^;9YyTVYm?{7@wNV0+ru(FzcxSystS|4`6);PzD&33rfQtowO`Yz5 zKUe0tKpVeMnH*(JUpYsQI7_Fh8U6FXv_Umx_R#snbQ+A{qI>@8=VF-)RhcB}uM>MZSm6)N#cvMv2jGOo$F`K+AsnNY-mlc(|S0UEP7!r>^jl2rh61A zY4iQIxBEMr^d0MTjr+GUJ}R#+n<;!o*?rguURQ4Y_} z!7K6#o<5ECA$C-818>nD>KCd|iLw{Nt}AETQEP(VfUT%^J*J@f;z_}X;MZAKxAs4* z`2@5)PWI1}N@iWC0P8n4G<^P*%};t#0PaeEi71CQYTjF_(OlD-YyRt2{_!l&%fwVA zaLD}Yzm>ABjpNeJ5M4u z9%Ml`9u$qIA!>C_qSYsRGrlcN|Eg#cSv|RurZQtouKBgCt4-&U`!zc#;u5YiUc&cE zrrf|*h+R-YRIKh&3T31m`WRL)-Xv9uf>y2+$Y(J33lrt1;<}0x!dbGD7ZLZ`n%~6* z-na&kH*u{(gT9InNT*@czqt|LgHxDE9S%8&+)eQzon1O=j4Rhjx9V6@q$W{!PE4v{X(_l#9UnCs1|vWG5KCQ7*uaIYbjx+ zTKG@$DbU38pIJ|Tvyj?BYO7IBiJ%ITw6yNuU5iA^h4zwe)##Z%ditg-v1LwL8ERBP ze=uJCZA*E5t@Y->({&k(R=BuB7?rSZdRloq?~zPC#J zc9~QIbmOY-@YN#zZRgL`SLR52V$(Qe&D3In<#tJsj%)avTCJ8>^<^WHPj(t6-Vtfdc5+~4-5Z2uHtmNxxER)>ccio(ci34IwVw z{_)Ngpm_C($Pxc%5H)TJ-=5@jDAW|q-r(1mOVCSR>M!JbdSan|TQe}=_>OzjRPJ<( zx4cSlL7qc+_(4-V9r-gfd8sx(AUps!z46^Vsi#FX#q3h_XRnREF6-WD0FfP4kyHZ^ zK|kO3;b`$2=8)%?%uj&qIL{X>TeJJ7FK|xAcgj_JF}T4$=zMX9L`Hoq5DX`^>~`z1 z*Kq{vgWT7G+(`>=&UWfaig1YUmgf$;aZ!FX)_@eASUtMFZ$>D1Jz8@HJH4%(_DFf#TUepzZGp7(<@jl**m~*oL;l*}`*l$f;WUon5yOQ+GWJ~cSGYRbZscCO0+a{DXWkG7Z z3-!V$T@v$lZ0zhJwzWmV32&mggvW07ZiRb24MjOQPhl)vqP_Nqwmy&hI927Kc9@8| zb4i=3d<58vZQEWe+x+dj9Y93S$sKA1>-g?AZW|0HoJ-;j>qF6WPG{(nxim##Z8w1PAqO9t&0Q~!4(CuTJ&+6=YoPUA--rk>4*y-aN zE-hnEylAWrX2k0LMsUljBxmPhjB}L@vT;fUp$pmRPqNCH{}ws;8&!R}8olSDe9C19 z#`%u(ta_CXdlOM-IU_RAL!ZMuEgW7;n z72Nr+EsTv=#C*%UG3!JifLzAzV7{dCuGK0e_#QJJeV(;reqwp3&rE?&VQ0adK#XMc z13?|ye9cP)GOUy}U6-r%x;=AhLE@S&5X-#7Q`uKdT{UOA^Gx;u{qX!wZd|~j&?)!$ z?hkVvZF1kTRtBHUOcXmLYx{i5oQ^{1-~uxzUgX_#>d{wLDoC?!A{_ha>h<{hw>P*4 zOOKnsRasrjL41G`uB%7dSWdiiOuG&oS^X}o>QGkRXEuGBn*_LCF&bxa)Zdm)vqPYl}s#AFLkSqb_Znf`71*u8@ag@}`h+8tuZ;i12 zve4eH!g{KdlV8_Dx$(J1e}6<}$ddf9x$EYNXLk<;p}%5+Fx%VhkBD2ZWyuZ?8~NG6 z3u_}?s3aQTdU@r3motMZJ#44;e@@)^Wo;!2Vu`+ z?<<@`Ut{%au}cIybiZlfd%n`=hqG+?+F7hrkfHf6d|jU1XOJhJ+b5tH`ycssH_dLG zQ7H;3MYl}epz2eUkoc)C<}0pBn?S^HoQAo^nn^he=ZkYA@m$qjhR&LuVLVcq)0!OI z>4~g~9xhg7JH&2Z?N&sGlct3@Dy47GNS1v4oJnMf*fiSPp5xnuUxJnmAEeYxanm(? z47Wr*x!&xd{%F8kZX`<~!R#2l^C*sl_l6dq9LHQ{9ys^zp}I39NaekL%<46>D5+0! z+L9j4i?yp|c51o)EgpXhoe=c!f#`5)jBMT)x_;H?J3|+4RB*V-#KGEfJ;E zgSyZfxouoJCAp_oqLgEa46JQ zd|Xhz&`F*a5yZH);JxD3IjjWS*dab_vSfnJH2kYG3L4I=Rq$lv1_Xp(3Xou?frhSj zUS+}2VcdL@nQG+Z@Z`0JvfD@Jqz8PQnbxb{Ip^j~TXLZ+xxn`jVW5Bk{%w(-?*wfz z{!~_q_KgbB)JAF{)&m+^y59X|p~?38w8Qzw*GNh2>^fLb4nYki)OIR{umhR5_>zs( z^rk>&QnwuUCIsLGTfUwFg%#__f8_wg`E?rpz-!xbNTb-*1s(_iC>|H8Y;yN5wgBQXB+>oXGR)dA><%!sv2MOVR7*@@PCMkCC4k zt%lA9bA#JepPR{Ge&-A}ghtic%wpHjz$u6$H?VGrVHz6|mX}vx^&bp3#@itX&;59qDWh*RxOu{mB1Z!fYi~*GqY# zcUIn;iL`OXixB7YO6F{MTBTstu&9w%{c34*Bj-|{SdG`DMPSu(UgnMC-$JYr1jzfu zE1mCbu8@fiPp@A0o4WdV%4tU+qk7j08$LS)R7WXjn!c)d)m1a=@-Rm-A6Ut{?7q5h zLvwV&UDRmTIYYCty3>jO^oHtVL^W4$c9?XZ3m-&I@E7Kki&A@zAVJa`lN~qeX)g!F>;3z4a1znoH^7&ARsleVy+J>0-w@aE`@^LZ9U}*t7o=ql=u@eC8&Iajc*|{UNX@)S-*2Uvkkpjn~eC(bUH}cATo{)?IwY!rt&imq%wlC@bz%EgnYZ%BPo`(5Rc=~Or+L`dWRq(YZFO^ja+2q&FS^V-xVq=!WOQ9s$X$@g zsT^e)$Cl>uWflBLXGv4DL@xF2_3X#hs(_Cj8I$>Qkiq(AJ;eIt&Wp?ghgku_?wxtw zbB=!vqr+**5)iUhWrPtnYk_6(5nC;`KflRdU)B zZV*wNAYWe--NlV`iShcpRpd1NW1Md?Wk#Nznix5~^J7>^ytCrE)q_)@E{+PkL4c%NvN%e!|r zDzxHtYnyqud&-Zd{UZ0Zd17r?fS5^dzS)_#?8e4LuahM63+M%=b?oZD1-L;XsD5@M zzF3)E5{C@d-ZrUDsh+I0ux@|XrPu8Kc;v*DbATu6db@>b@~{%9{h6NTtQ0ai&h|IQ zlnUIcX?cD#L1j65E2nI|@?)!}K}zggybHCsj$Nd_*|7j$(TjyBUCBr+`w!OT7TRaC zT=+_CWZyp6@OH{hA(4v-o4JG@^`Jre6`FR}0{r&Xg9;4$5z-`qlY%U&7qfYe96%CA zr;_^=1jM7`;of;v{mB;$X|UC(!XF1s*dRg1@Wj0FQ_rlJ0@IK*iInA2u`)e@8!0jl z8ReW?Zz0ndZ7zy4>e-xc_YR>)abojJs-`g}6rw-W*t+h^3IWcX&;g36_}rY|+ye9} zG==w?AA!mP$C|MTzLH%rEVHh;1MCnzVmDz69O%q>RU(1GvPP49FOxeRD}9zmLRezI z=t!XHK0XSTG{%o%Tk?E@eQU;t1TE?a8_5(KibL>(Lj(b-lYgq2${Mf5$QnND)YXYY z5L2FFxIj1-V3xZo>E>`j0O2YO7N6l*wJ}Q+79)`FH)ObQQpghhZ4kpMU4OJWC|WOQ3nIA~ zt!Ac7*#-ax2o9S0#*JoaTZXG~a{}ZzIr|{965Z<4&T*@Kjh>rRApO(n8LidRjPZQK z)8dlmdgA%A0>z|qj=_$*S4$gnLu9ESy*4TkiG)_d!llE<**C=cZWa#4!qMtSx2! zr#m5^dQ}b}u})JU-if>wGmw|J;K80rbkaFJC3pQ-nHsG>83v_>>pjHpS8b%jmP zpVobm8&bXPL4FB?7|TN;RX+lDq6ht-V-5;Q@D;{S(SFE}Co=g5g}VvZZ9i^a=jy<;XYepz z^3iS!-34B;MqXc^((&&fq2aQtTMvaqjo?00lV4c1;WS)XRCwc;Z7;PqU5X45wKsqL z&sSBrOnM14)Gq)P6^M9RWv4F_fE)Pt?APUMliuLS5V2SZuKye@ar#usd*;adk*YDJV213s#~dC`|FEF@ts z^ey`{z}ZE>pOV}fnrQmfE2a*AP_;@s-%`W&UmzbMYqb?U5$iSUYsGevg-}*N)2)D&W^#RHTnDz12Is6>+Dot_<}r1?uBdPDW@k>>a_Yh3-?*YHtWLFyrW#B6pYF z$0MRT1==;;(Wubl<$Au$hL65Lcc5CN!88;$Dg+r27+e$3-cc-z7N5@*k0-k$l&2P- z5hcH?9t58|QFnE060|9tzq zG<>kr4R|s7SLoR#bN0*0y8{(n-}Ykfp!1NY{^+?kUaP6El>Uj~a9ZnNYHNuAW5l5y&d-|AZGi)Vn+sB#M z4(#;G0;B1@l^Dx2FRRiXabpL)t$vs@6*Kz9eMLFtHx8N`RFmp9fKqYVo$uz~wdRIJ z*51$w4ombS=2&$*vUGl$h}rQ+_DMZ^Z+UhQ-*55>z#r0yi3-GcTy_R`XhQ%rmq{G0 zr$4jji|?-HZmP>@NgRwsNi3aj^qTPWVJsvSYS^i|2Ty#Gc3YoW5AkZHb=OxfwwQx> z(7BSy44Z>Go{vB&Zo$p16DNtOTK45Um*ish+OwTjxXJ|k>iVF?leE25ftST zh}hUiu)VaOZ(BJhlUBD@I!e}5TF{*_%$~uL$z8$Dce*o@W8%xk4%R2BGV}WZalTh5 zW-`kqXMlF*@;n!IUqf)~WV&XG%f+(XXTZVSS&hr^KxVD) z_^>CfQ@85N4TUw+-9(-#ZaKm}g|NafD5`;PgviI0&%p8w3W}R|GL!1EZQi~sHmS8- zdvnD*t|{67^nK7Xm-!YrdB+)VtD8l*;1YMy@m01{PSB^b1T|__aJhzULQ^f`dakdv zur2PO8~qWlcjs|Jv+J+*9}j9Cu)a#>Hx&Wh3koyTzwcnivf>OCUUULD3B6_~C}x*1 z!rZR?n-~xk3w^*&h}jYxec0_*V&`Sg0nVW}oj=!%tXJdEx-y?`$TwAe2#IoOvjez(^a|eWq_GE9&i? z0-0#vT*I=4NB~jjv?;CAf+O174o>JBk-$n|Y>PMgBWx9pEJ`t=2CEI~S&MEWXoOSRyHupjO$odGCCLc?azL(hZ!_cj=#V>}mRc${=ddL*{aP zE%Z4Ss92AKe-BCR5#|5J#oyxkvx)-y4dvD8m_WAF#%y_yG)9LlU?^hc#oDv`iZ5}B z_|>&=8TlBd&M{c448N#yiu({^;ivkq7z;32ie)0W3ziUu%nIuj_Dr`VH+%re%7~M0 zH;?ZWue96uS-!)&H^x79qnxKW{ww!c1=@Sohc@59M1+%yrMM$?%U*g*?dd7zshAt8 zJB|ncv~ch-^%zT?m^F|mv$|gT2F|i>lnIXtw=3-bdojCN<4l7=oE!y~2@M5~v=3pG zs~9E@A~oe7pS8neqQ}nzeLtbTUogEw^KikbhuT=N=Lw#)J3EiF^ECr(2yIEzIfO;p zZRXE%BDSsCwlna=*8+qS?n3DG0jqUe9P`6ZD0}$!0nzNr3{UATM`zF>Q2;6ZDSW`n zv+Y#9ax+%&#@wfR7A zzfam6u$s^>>pclR!JH?natOt@OK%~3Af9&|CYqgc{ET5aEd0q!_f>H9#B}X0SUm&y zU`ht%pYVp3bCsN+=_XKetGUI&Q0JHEaWbLLJDuV{rpD)DA8uTrKPvKS!H|nQyWtZh zuV$KiV)?3Qlis%&6E2Ft3!q|jYS2gL1}mD-iX?|QId5-n5zAbfi}e*CeV%KTCmQZe z#h5T-$r#JIcg-nZRM}tE^*dy3P@GC{XiIB*#Dli|TLLgg&*+qk5^U1ueLbhQ%>fuM zGGaYFMbfQXfvPWO(Yu2^gQ^$dR_roWa+z^Rsij7AhHGPM%wUN)PKy4R!F{G*krpUqLB_`C?D+a&)2U4eH6eP#pTRJLq=RdLjhybJxbcm zTll`KcQ)Q9@xu>@yv09NZUkQ($PMT>XK6*0(Z(x-^a{!VP)D16qdt#}YNy;%+uy1M zrR9F2FXXy<x&_;Ch{*uvLYk+N1a6YNIg^3?AT=zZ0dhOt6aAx=!oADIEII6|eTm zDRn8@2QGj4pbR4>$e0wzqA!#%L|wTF6=;ADRW zDt-4{RXMK4&La)Pw>gWmYBfrNg947jl^>0s zQYu&MYh@0Fyge{sD+3>ciSkxOSDoV&hGl!?_+PRy*WbPCrtsC5qE$ON&3l%(>B8CV zox~GN5qu!@I^rPi`Dv!=V(t zBWCtv-qf%8TK!VUGzt1ZYG&1+nRYrgDsc|ai}D5hTv!TOI^LdftA%*u$K>yd3t^M> z@q5#)b!fOX9b{UK&LD}wKz6U8#me#f-t2vdwaG+X7tRajG_z#2b2a!|tB=(wGiuXx z=e-*tkMDoVb4`w&KNM=#4Nb+z5M&+?(1OK#Db*JT>@q(LiM!Qk-1FVsaasEC?j@5O z{#%)|m{Ud6OM1La*o^WYcd76JcjvGMF^k>IB5Akhh{#*Z?XP}(!--%|qXP#^E};N1 z-9WZ^a(%SYUG6SRkmM?`OM0V?IOYk01*qZ6T>d{%J0a{fRDc)p=_7X|A^en@)lpKNPwL zYgsEtTUb-WC&h74IFY4*8Y zZfajPSn?0%H%nv6$Au4;v_W^2pZjrlhVwUAx2&+B=F41Be@j>+xS(ITx;ootrrGMH zeq}Ec?UO;Ku6OYE-SE_E@6(li=~p-ew-OrXODaU!_607nNYG)Qb8WZ?ibXO+0hf+LHaCy?Ia#OLo%0BjS6JC$NfraK9KEGkgWku&LrzHcsC`23Mxth4)B7h ziPi?&Z;Qi1Eg;U#`wAa)$cz)aIZEVhYf0e>yf&OX7) zV8W3sEC9}zp^hdtuS8}XT2l93#RKQe%0_GC)c0S6!K=LmwhJUO@RCK35FM3Fv`ZZll zQ~u7|%rv8MXPoq3DCtI6c`+pE-w?E&E9${)S<*nY>c#7hy?xo)+10M!W0>gKrO&08 zkfx{Z>~*<<3^zC~WiOrog*g%2C*OTcyY>Ah@v0+1`t*aVLlb?SSDksRmiEtKa0dqK z;{tPZO_8-B_7y$ZJFPv|2`U!ma*#EcTg8*sY_pI1~RpIvT!4=M?$O9G#rlN zs7I!GGowpc0P!@ykN`ttblH0p#~3%kDd@RjIe;w(kkEORxo?FPhi<~nE@W>x+i;)F zPDKvzHQDkh^%Kk8nrtOw&2ByN!(+Qz%$Fd`2PiEml4obK6lE2aa-h~*V_7=#q`fbQ z&UnxOOpSO@T`}eJ1SqQFkok#dkmL7ZmHn|G1)o-|t)0K|pzW&NgjL-2eXp=$dEM_t zB47SBQ-QIYHEwpPzbLqr8|44nPW&&rxkmn>?uB3<)_t7JZ208A?lYY5F)s`YU>WPBkU^3OXH-#5eO10UIv?GtMr( zRf;~(wtuD^M~3csEz29SS3{Tfq6^I@ct;1LPjNFt+53W1KvUAJG}2wP;m-4S?3OtH zSx6eYm%SmN0+TV`vt1BoNkZ6vBkfPalFa}3Z@8JMoHEmLD^01)wUic@a)Bu)O-;=` zapC8bDH@t7iHb1gt|?P)lcr{h)w7(C0G#+r0RCdzDdbPig&*RH~=y20hSj z_aFJ>R!VRT<47up%kfCN8iEmzI{JE_Y?&MN#D6j{IaFxuU0vl|wnu%BTJ3FABOHUO zE^t{YQ??5MvPIJ>)xkr9qM1J^YOPitFD@&nq1@>?)S9IJ0}na zKTZU|EY^Y+m`DEjs`=OR%^lYx%Mbq-SeS$sy^@&+qOG6Y(xNPsVgpASTFP|$CO+pK zEC24rDxZAI(=57LDuiBJ7B!7lvImTn`Ag zS|Z-mRrhgL*kSS_I4?E=OB&wJ^cxHwwf6qP=_yb@YwQ+fvX)bO&O3j!H9x8{c28bjb-kYPo`&l5?JAcn{=&1x3MoVwHKPWoVTFm_q1C`rCuvkxa&4A~8%1!Z z{Ur@Y+4a>NiBvj08_@OZa7+5A+07>-tDRjo_AiT)(O%q!rk1hll_+Op-0gybYib>D zJtLrfbJ%%NSe3m0cJ$M1H#gTcC(Fq<29p`rE~;Jm;s1}1LgO6k+urB@ZlfJXZ|&1j z!~7rLez?QP>DASJHGvFkkv6AgEGD5g=QsK@14Z|GIUGP0Z@j&3-0~_%Xe)rpEhVw@cBSb`c417 zeYkzZ{*!H|J2unv8^3g{c20arWEM9FIA_M4VylIM2ut9TL*3v{17RnIs{d-b51di( z!Rh}JQjt&a!-a)ydd?@NEe+5c@1uVrASv|Kk)iV#-DkGZG-2!GsL;?fiyx>H0l}pg z#8dvCx^G|mW{^8xp&A?)tnP4VzA^D^g4FO_T#VL>Lf1ABn1WJXlB-?7wz++ zENK|RwA6|4i3<&pBZU{=mK7MPbuFD(oLC$zgClPUH3H>Bx)9v}QxDd>ofodfNlB>; z#pGBqNR_^S1RU-AF1BM#?iJ~X}-ivOHDa@t9|*5u2) z{c&THx97LtQ$jG8mHQ9&!FJgizI@F?kNrao$Y?|r9pS(J@Givn0A_01UENJ>f$ED3 z^5@oxeBQ>X!!Z8dGxhl)5j@;K3*3VF-|9wk&vjpD0PqoKQF%`b&sn{3OVv50OO#uJO14%@ z03V2KV9Fq$lQ=y+n?|sZZhQ}>0}gHCEkkcVJmH3DGH3`v(QjT3b#8g)wf^Y^zRp5571Q&f-fN$mN_C|= z#t_-JPi&)Zi_4g zT>jpj=8ggW2gbSp3aKkF0oM_T??Y?IP6Gz{n-{@Bj;_WB3+21U4H)b6v}!PVjnpw} zYokv~i>pX4)}k;}Xp3#>GN&qhr#+Y9BwA=?4biWn#63%m(I-mqzWztdusFpem%n0; zMRkU?WxcoBVd7n`DA&wj8`H^Ix`&1dzsmIic#rGpWcPmNA!#|e315L4y07KKY}+F1 z0Jj$gvMsGE| zvYiYl7Ra|SU4q6Is}i|04z}|=U>1qYKv?mr{OHB%{B{=KA+Oaa|5jUdcELgt}fE!I7Ac+Q4$nuV*X zzjO~#r)#w2pe%Ii8Sc32Z?KP-5EcZB;y&g+FdCUz>H@6i5l#?7MWmk>5*^x~E})9i zbQ+4=qD)uqEO|D`E^e*q7z@|EGgon4r$P#Z80mI{E*HI#82*E+uPk=Bwg)3oZqZ;q z?$aD^wy*#r1-3|x+gMbO&PhBiyP}oeHC2X)&O?#2iCXZ?#Z}gi0*LS9o@#r1zYq%O@)}mq&nYXc zs?y?3_II@?w8uZ$>$h*NFCVnR}8+u1~2(#d}rz_OyHp^)b!TLT_ao%bSw4Pm&lI}H?81m_m zryMv!&*_Itt~b8`c0P%2FzvmC>fV{%;px}c!RjFk9xkjEf%#&^V<0;ON@V$gmAFtz zqm3krtjx}J6d~xFh;X?km>?CtW+~!ehvrX$?&84=<~c`0DDNdYGa}zNrxi+X%%dd4 z0t)^aLB;Hc@+bF{FDP?ME|Z}v^8FN8*Pdt41mE{=sa|v~z0oKZ9zJ@=?x2`r>>P`K zyS%rK-y0k2Q;**Nx&8P^&Bhn6Jl)-D%kK_(zqsPr>=8we7<%&1DH&DTFvoq$y^?0F zK^(qs0v{xe!Ckhh5GpcF09Qels}r06bfy?=6upC-M!}{awJMUd56Aj(UVvmn0#vyb zpuT>egn^NvC0{C?td^hVH!*BP5RAw#vt{c?tjh%{;;tB}YL*fErGu412ok{r3w~hf zQ-U5}7Bof6V*O})T)GehINn`#PZ!uiO@@Yq>fq9*wscuj^IYw zlL+Fz7eMHuCa>6Ieki=?ia_YPIO^%jsjyPI5!WZH-h)oS$`7L&+w;rbDwN(*ox8}8ORD~J6F5+#x z6)Tn|Z2Li6cmUUwfJl5$TUDsWbu#nO0-Zlv?CSTfvL*_ypw4-xmPD!jqvvGgRcM+J z@r{1xpU2O^y0gsiF6{!7I+JMcN~2nRzh=1IZCu4oCe4ctpdWM5sw3{^`hwA!65t6R?7-neLnb*wDnQ^lK?P-UHtDh7*Tt^ zHLt@qSbJ3FQ9Wl}b2s4&Q**0Ku7@IifV~{-M;;=a1uLH6pDK@~!!ls1Q3QQRR4w(E zw)F_JdBr59)w5;{bS21=+#y3J#`TnPQ|(?2>)gJyD#Dc!Tc;*Lz(VkGF0OM9D|0dY z^6$3$1b-?d=%Q%ZbQh#b1d2%B;}tlD_hO@#?-T5AH3xQw*Vo`9*&13kopo1fmh$0DiM6PV~7kKFP@V)_~xhTn~bRG9k(xH5MR|^D6soj zmycVp_?grC#p8-c?$4aC;6~{AcU}!~D3H7{p_>?8b!pDF2;UK24} zvM^8E=k?an&J~QcPr`3&l!)AV>Mp-t9>>>%$14lG7~XbldNE83+vcW7j}ymG{6(q+xtE;V0lz2Lk;MT!Uq~9Q z{GJ1Yugfm5*>h4q@yy*wu}j4dgexBi`r;tZukeElld3~4UC!@*Vf;C_@xIpd&tywG z&u@dvG!=ZxuhN>7MtntYVp&Zt)T-J$he~g6idY}cgxutMPAw&yo+DAy=k`JU>TZkP zZz>lqYQLBZ#x@XeAnxfT{zscKw|R*J_&~gc7Ja=dQ9?k5kDpQa?=BPR-RpOjGIM_+6(oFp_%N0Tf^0J-eo3!ZiOaJ=rTiR zQyWTx6;Q!mP7$`K61l{)BLo};FaIuLQeyxBI##QIZQ|An@nA}@os^VCC;d3MX^X!G zl_v`5e(O(66+nTVtl-95fr^>={~`lF7i$%uD4!D7b4z}i`=kd`>tQ*VO$ZZ6VDda9$6zDIN;j6?+guPPXJ-Hger`TW0?+?;*0+8K`rgxMN zVA`0Pz@1uHmVU86*8&?oHCc%bQENrNdUUYQA=yUm?P28LU8qU)xsazrkHVPjdNvXz z?2GV=`o?F3K3IwoX+tm9DK7Cq_ft^f(jxt7ELc8((2|CwGWMxA_V<4vSV>H^ zB{(KcynrRSfH#~2uHSEi&S8`i8k7xtVOhGlG=H_Airbr_z7NCivdr1P=d1+gU z`I0h+c>>a@-$iGTRO!NK9JD#_@R@9^Vx6{kL>K!2;WrfE{gKe(n}*qQ9^hJU#5pZ6uYK~5bYFK z$ywr&eg(F6@&)K@Pn?JEjezZT&)y&8*zfl|V6b+s9I3mf!`1No%TZffg6j>zD=ysb zPPU2fgZqh&DN#pipr zH}m1R>DJK`hl}J;@t3r-vSuzZdF9328m)dj>eWcJVcCy@fQvD67Vm@bZR|DFG%Ld_ zy2LJc?zfY#PtbQA7RIQ5UgLe6--lwBMJM3)RwS8H3OS$riao}i*QA%~AC{GM`7ng; zzUB3G7>hc4-unr9a#8W<&GNx2S87%GYcT1_g21AtHbxw&vwILVO8Z}XyY8WpCfZ8% zJzk&Cox6A}> ze&Gf?m*8X_LGzPyT1>LJrj)aX8$fSuz7}$8V86TOpFl5AUzGW(=-fWyZ-Az(ES>W2 zwi{a_;nr26ep6uZ(K!875040@N#U;8AXb>Q&oPQ7E+Oebgxg%rWLx{}wHIDJEsbW~@j7HkK7wE8 zz;+@dNGX#?AY$cl(bo(hw%xC9w&|siVd7yq?MWspH1|SKc~5_;Bxh zz0cRrkL}5S>7#1UixO00Ad?a4cD2x2{pH`dLu@6_0wZzwQ0&^uVW^s_cQ!9`jw{pm zt*_9q`oSKib*1UE$k~VTM%{VTj;X=iTYY2JE%tNVDT}IiyUw3;a*pt%y0VqoMC~Dh zUgexzTe|w_`B2<5)MU><6AivcrRC-2zoKYg&1eRHKkSSu7yD_qrtel$9#n*VyyhbH zW4jAB&r4@}SgA;;3K1yw%MEz8ND(ZHXb!|kckol`T~^(GZ_;XxEJr4M*PJZH?J7(m}R(%mv@11H!aqEg1cyW*PQ-LW<iJ>{~G*2jod z6W6zttKtRGg758GgPD0=?S)+pyh2GCrK9F8XFNYJ}z9HAC91xHq% zB*01O)ZSoCU=Gv}jPhQ<#!gstLr7c^FaL)4>-WnDL;KLZm85S=RGF%m*tmivP$(oaZz1oH?LQ<^*k;aGkMtY`Ljo#O0M-Ob~oLv;`y}GME^(E zO~d4`)di}Fle-7pOFW_ddHUaN+l8P+yu5Rcq|DR|lqpMRQyIr4a6u#owx?y8{t4f7 z=!e-Ll|&;%0h;NAi&9#5@G-EhMm~y)1-ZYx;+Yaqev4g%El&PcRlL6a0a~?iOkK14*mpsd{dxVZBuv$CgXDU{CQFX=1wSa=o!k}+Oryw}6qr{9{AHm!O_&~+6U5KdGEY1bJBWOPO0N-M~%h?ppW?Hq9#mZ?_ z(K{A2earQ&td_A`rLzHUS*sk*Ko^CI6VnDSZKcC`xCC1I!wn@px zhnkh!*m%{q-otx%;JaJ3#E2F1jq|PU4=?l|h%-<~A|8CHs1s=~h22Ajl|_pdWHX*13?Iy8@KKhO(7FK5t$oK%I$7N3_8;mwr}szi--nK6PTzT%|84+4i?D5E5$GNR z9Dl7s@Om{Q?7c#;DD6Y&Gl3S|-K;%DcEJ5oE}0k0=uv!xMr$J)Dg%)a=Du9dIx<989a#e?L+;;m{Ca+;`gfc#*|^f%9nT=o_}` zo(qLeHZ79Odr?+b;tLEF4xVNGjr@_+ZD$)VrA;|PETyUO=rn7;OpA)Klr<-5Z--xc z6Wz{`EkCJ#+t^K9)!bJ6DS zW@7aF(wZVVt~VQ(yx$OO`YRWK%WDH$g@{8kJD`mN-!K1w{@L*!;?P12{#svRUm;}w zv;I3#A$vCAA#4}Azh*&GIa=IKe3x*Hc&Naj`IxWOOUfe`mvpkJPkE^;SO+>vqP?bf`?-*!k@!<=jB~w^uWeA`l8_O5kjv4*rN&ccMBQ zWU*BV$kD#`ra&jFREd^auqgvL;UaqVK38nqvMD??XYE^B&1|DipjvY*CTZ97w`8d3 zH(LFRLBP}Rzk>cX=NkwX5VG<$A6swv3b!2!Oa$G5XBRQ`<=mlFJ3o;^&gv#-)OJS| zKK2*9txb~*&g5FySx=i;koV2`-2ot@S@onjFnAAg-$E^rieAX2ihfK{{@^3SW;W@76;gen>=!7sen`vt- zg*+SUWX4ZZ+JkvTF+nmjX@54+kZ`i}D$p-5Y|R!Z?+5}T`>Y{D9V0=17(Ev|{$>gr z*?VW0Q;&Ju{3z<-na{j8&+*w`8Ph?$Vtwm}X~F17%gQ%PdIz4pt;*d^D9Vmm^C+`8 z$Z^G=qbi2B)1B$SFy}4hre2Tpt)|-BB8+5~?7aJ=M{pmmY==K*y@s zo{VGoC8-{GUxD_yl7Sqkjw4s>u~=4z_Q_q4ieV+aY#Ds81Dum?idne~w9Z;i4Vd(L z!@XxYCs+&QHz;tAji~mbgxO3zFvmGr``5N1^T+{9fy9HiD%>9-u=Ul@C=o_i5r;`X zuLhwdt_5-{DdIt;Oo1PVR06;EX$to<4-Sf!^iVw0D}yx){g;6>dHodD(Q)+e@b97g;=hxYECrASfNOes(+w*ZB56=XV0+Q@Jfbwxsr zLTVTV%I;a&th{eurVNA^K+z*5-n{f|kTM~+?Deq4DC-&Of=^x}5o%S(# z&uu&~;_8-rjy|yafU^hHUMW4}t90eoicw!#EeAnm%Xh!xBF;cY`=_6Pb)X`NM3>+2C2~5WQ zTdNjuGc0MlTsfI`g*(V0r9b_5+oes?8l5aN4A$JdQM^?!c_zqf(_wtm>-|glKy(ID zr3mWN8*6?Xe@#52sp~Igwx0N(bh#gPj|m?>K{yZjV_}L6k0m|bvyrd@vpRjrmU86MtkWBitx@c;Z=2GLrLm>m-;B%M$@2@)?B@j@P;q++@r|` zrbHbtx6%t0?qt6VRQ86z)c|V}5X#8$%JFn^eOy8)y5Ht8S=~3b^eD{#@~;+zaS?pn zYV8()zKcU=pCPzGN*e?(3AA|o3&E-ZWd6I&xpmYuy5#Nh zrse-ak{xUk44bnfZ#ADuw9>3Dn)||MY2CrB#y3FqeA6G=tGFmXz0cEw;w7j=og5cttRA{?Pi#zUi%x?-t)! zX{@-K>M@LsqPr1g2xG{RJ&&rb7vcn4~3RCC!!_^j& zGA5pQWC;6}e*B*he}j>udGBqgdAN*wqyKKZiB1(D??JO@92`anK2sHAgB%n&osir} zIL(w+aI&U?e(JV1%q95Ozp0d+1FW(vmX*&>fffNv@ZLnL`z=Bx@a5a@=SSdP?C%h} z)}X#_@U&CwFvM=ej_GSUpT>4+9&dEpDrj2qj3~JN>9_pb`S-7>l@>fM_~hy0`?>bL zAo4A5jWs&rR}LKqggt{lN9ZaJ$wC0wYo;=fsrSh-bV^B?Q*Qdf)~XTY$?dOKxk;Py z8|F3q;&UODuoeRH|R-{dc4IW18HFmxfWyHR8beWFh^xmg_x2w$~ZeFH1<#&Hzxvh zw!PMnQf85uwHC)~b{C=E{`DtaUlW$#bipIi$7lrO6)|4hTA(%TIu=ykb`gJZcFAS) zG)CZ_zPe?#4~ zlFKFY$9FYGUF*P?@A65^BEs=I|J{ZL$dDaiCw}mCusPT@B~*5yMId*@v2?)@LUhE# zkK>c!3r*qm$+2}{1lvDp%j7sV%#VBY1!Rr0=^C~-5B@C??XWcX)Ri?MEHkPezt!yN zWLbi#&-GtLU-KPyDgTt)I_&Hh*w|7wT27@71xG@;fbwwRl`?Wg9A-ORu50?OL^u*MGNPOHBp3O=sjIcZRN)% z`dBtBQR^_&nG&q|x~pZ$sO6ve9ZZ%vH;C&tV7`K><_=Z&WE)kVgd)1z{$D;n@m7TY zC|o7+3u*GqylnIT=a+4rfd({DW4sq|=RqUs<{bO{&{E*om;5=aGlAU=gE~ts8=w8= z>Nt`50{=iqOo;mAqgbT^EKw9%w5rSRZ1RP;jID`LteP*H)_0y$y-T!Ruz~)Ehe$1u zuSff%`S-Z>T`{Kb1atAX|H5L;OdA~^wiRnE$Lww&jc1lOnllqOS(D9V#vj3N*66~g zn6}R?>m9czTqfEbyLD9=(iYSZbHse}U2de0e6++=&k3vSoIbr|Dq_4!agE&i^La`GFtLxuw ze#%=!f6`o_#qHmB|07we1{GmThEkFb=q*2j-i?6F_dDi=VlK7mdgeD`M&C+0#UqpR@lm-rT)?O~_h+>NaHd!I+ih3s9m#U{6Izoes1XIQXu zwVoI54Om9rtlhMzFZjpPwWZYA{n16MMBIO9!hEW}j}!dG;^QsK%IBGx;n|s{TBzji z2MTA?5gPal!nN$eIOLC(x$%txC>nnlb7cY5m$7*vkPu7ur*j$VKt+$%UJ8PS*~*`4 zbaw?St7KO`+{DUkD3S65!*NP($w3xy3PWl0B-s34)lL>3BsYakuLCVYX?6lhsb#Yw^Jlrr z@M?5st_XV^#oxEslJstF--~gAHaK9&$+Z{rvPe_zDl#OX&@If`B^CBsyJuR!l_G@E zj1EZCKpdSb;pHcblF|Mzi=j}zjbuW-$fmXtrycom8Qat#K3Vk+<7~@de7KcvaK9s(;XV zR2z4!T7Cjtg$#Scu$DnGM4vI_%GG6uKhViz?6q77nr`p=fsS>TP;&Ef02{H@;&k2I zMsy0@6zvWwicM|$N`x7N9pcU@$2IM+y&I_}UI?MwyeICJBB-CE8<}`ImuHteAmFEyJse2wH zhGbSKS{V4roi96II}U2IgZ1*+OpOmThL5PzHHZvBqn<2aUD#N@%3(!p%Ft@5*o zl0d{!uI=&q1yp%?NmF60DRm5IbFj%^C~nl|VxL;f^QfgZht{K4@1xIPDF?ip3MlHZ z(1R3wM}BPmyV6H(2|jFRYyC|@+{6iMUzbO(xWy;XXP!-sd#=TN{^u>}qdlkmFlaRe zOO>00^&%w)_jKXQ{{D(FFLuXTj+E`m{jD9ESXGh1QifoU@f1y=ZX}etm1$ur_ou_)oey|?bUFp zOUatH&K-8Z5cSXFEr4J_>qcYEUtqTU?Zz189{R@-5*9hu=_DP<$X9+ks^@+v6_O4%ww<&M^fIdr$J zii;377T*5N#HV1ipvuF|m2;kQp!LhQnu&8GX=;T_)~hF1!hY@#zA+#2#p_D*bitwD zsuHOG%`Z~;g8qSVrVfw(g}+Kt5zKdd?pWa`4yMc1!5d=jteYpyzenp7_nLr26K+0H@kFVd`a#w0jG}mm2Mj#1FADXp>9J5`nTE2|xr_LKogMlf> z#q9{atHc2<;bJ%TH|E*sgXbxvo*xWT>ExwNq4Jxl1YGUvvnW&5-l6b~R9?)Q;hJRY zkwV%ua0~d_x)FykfaF`JmRC#-LQrWRxd8^Prux@YL?gjJkuo){RS9+vY@^DLw@LqI zYLIqE@wxukQ@YcM#-vEv*d$UXoV52En4`3w#kgcX{o(U4!$M*j*3*$TNs~$fK^A51 z6f{rMNxHQBdljFqoW|4{Wst%|b21l=PGs6qI`0}+Fz7Wr&G16C+36s|4#r~sTGDDM z;0{^VIyBmhwYhqe*|*5-ugE*q5pT|Rez(60^%-fM5f7;z)a!L;E!!Is9AA^iG9>WV z(HXFPgySMwZu$OVG~Wx#%kJUK{*E{dW{8A66K8r_Phs+}e#BqvM;~H@NRpPUd|&ME z$BJ^`*;$AWpTq@0=6dVTpClM%Id9+aq%u=c1wW>fm)aZTYvg z!M#Lij1)PCp`R>4D}w#Kf;IC02#27|;IMq3?0P2lp7&EVITenM!cB3 z2c%x&S|rB7n(iWylYf1oQ1!VsjQnDV@nm2w05T=^f&(KG0FH-y` z&KbK^pDQT4OGnmrG9Jv?Z61*i03{`yT9|mE{g-M$Bo14j(n8W9L;xdhC6krF&U19qcKnauMYMZytK24$lu3S)>rvgh?ygCE6Kn`(i6_uv zh5PcuV1QgzS!Is(qL4{5Oqc!e3?ndy8j>%gm-b1>dFy^!c1`sXY^?XoW?&f4v9-Wp zaP>l0Q9{c)ueyuR`N%b8z5r%>k=vor{>tPDsA|C;hj8$1?eyjfe_j+~;F|jAV!2PX zvKgH@Tkl?rvx%kL$hajr?AFMrU08UBZ7o%T4b(9D$bTBv&Z zq|)8DC8|*ak&BPjZWh+QHNzb(EPOg%TG%p^o!xfqxBN&kOV;K3_TO#afZM~>R45XE`jW^e z6D+2n@qVDHlq$078&nDe9nd<#cI4j?`g1VN%2p639*1e-qu6Z3;f$aRh~Gq|p~In^ zUi6f8a6NVId0=?!H*|6ccJrqEZ+=GKkKoYYDT!W4h4THafMHg~n!`N{?U#CI6Z@A2q&98Y?J_@(cHj*KuRFic)3H|EG>0n662tc{`0`Ne_r5knnK2?E z>Fu(8vU7P};iaVarb!27ep?*Ev8Rm`bfhyS5qD_R$@P2dKLvlf`*=R?8d+X?vNGyZ znUhnvN?n}Yt$w(2QF7oF=B+q^54Ci!qwhm&NaW^)2;vElE@jD8D)JPi5tCf@1`?W* z^q&{C2>FxzaD^uRhK@aYgb9x*am+l%GfyhBcL206ijuVs(muD0nu8~>|EqB7n1lbj zEfk_Yd2V)3La5^=Xn2dRiPUc?2)MSE-ul7Tt%IB@L)PP?B~iH&;ck%C{V-C?hnfR7 zUkdwmn0a_Ia>I176OjDZEqsg>^rrUiApFHq< zgke=Rm=N}KVN$QF*cZsiY9Z|euU&rrL3Ron;Nl`Nn0TvS5$2W4ZfaYl+3VGFZe6C0Kc#(-&4mSih=)jbWpAA*60 zP4WBO@8yyJD~UocQa5M}KiiXdXx)-%z$YX%zIhUvB^n1?Tslcgf~zj;@Vi9Y0I;4z z(y`U2;SGBcJD8wmLN&7yS3i%?9&I4Fh@j54Ae~hSR$VA&moRIOX%q{5OKl-E_TQ*G zv>S_md>X6ICtFL=OQfk(FkV{R57UN2MB|N&{xU;%$himGoyD*h+q6mDvsQ&ah9mat z9v;2PK$KvqS(CHku)9YA*MbvqWMv`#t~kSY)EujQ1=x2b=AN^eu~%irldrrM3VOl2 z)@O!CaUQ+3YU1saQ0MFYVqe3wy05-ViEC>qE|S;O?uG7JGf6`gw4bQdikt5_71ti( zG+JdH_%g1#>)PL7S1s7L-Yr<~aZkx7D0a57h7ABkSpc9*6K`(qb=3+Fp|imANm4C- zPxOwhIak9g++$#4Y~;=dC-@5%vS;QA%*(A?_muB3m1QbYc=xgz!CZ>GFIQSXSd1P6 z%&MdjrhYxa=FpzH{u*Hi#xxAjz%%Sgb7|C*4SLaCNl~(%7fIuj`DqLkzt}NZnrzoX zIzK`DNNVO3_I?NkC7#Wf7zzo%QJYO#220g+1uBgd=O(wOh>1@-5M5R&`M>gQJ*YCo zD>8>a`Qs2>aaq^@OOD<2V7k~P;DP2#yq6exj}Ti6)?bp6Gfi<&EyA>Hz zgT|Nk2#~ogF7~x{FEMS!R!mi3Zwl=n>*R@%B0*F$W$;d`c;n+G?;5IB!91t4{~^oj zbb)x*=+!8_r9kAVgff16Cby|tZT5|k+vA7LvsF=k2hR4Db{(fm`zXe-L9c4L@y=He z!Na8MOAtkq&+eY0Wp)Gxt9(G-aNjL|7i`I8c!{*P&>RRDk;2eak|n~jY)!>>h8#KU zIV9+WVtu#hj-w14@qo3&vWEBm%~W9%B8)8G)hOwt?VVFuKCq1Je+~IkQ5mR9U2M7A)(SbbE!`lI)COUI07g zHAWmTm~kr@&t$U)#2yXvT4rT+pT_(HN!RCHB0m$Yw;)A2Yll1if?q!92M(oC(*8kETcL4#ZMy{=E9aB9IS{ni)W++HLN|2I+uDfBesTl7Hc!ZGrL!5K z(&-HWKH6`qXqkYm#|Ln%gJv@!OVz~nmkYrrFGgfY?6ZiDyn4}_m9y0qz1B;k+=>2W z=$VA5Z`Q5F;v8Z2DaEE&GmVnJ=;QF_wxiv3_c zd(CpvbR>j3*(bb|6V&n{%KVFksfpZQ7VEP0QoaW*3(A=qSeZN{ z)owviy|fwfx9ykdt2Z@IK?vtjx;9IDfwTw5QeN<#Jj$4erM#PZB>Snh?6f+)BYzOM zILaxU{8Rc#$-cjGs|axd|2t8DGVG1cW+_`JD-ikRF#d`cc8Zn;5wV0>1O-c~lmzLu zJIps}OS$o4T235m-^DOd@(&g^@1hrV)R%D!8AXaYI(8bpTf&Q>Mh-Cr$7s9yszbF~ zO6qm}I}I8QT1#4(xwzZ*%4SvgER@@F8Q$5ftm>+?+Mr4?#mSa0x7{)t5;)||e2 zb@i;SKXxFiC<~sXX_DV-w_ko@el-6J#p+$;MWgI1n0^p1Q?#rz%_PP> z{=iskDNFJf@#-}TnnGtjRT53p%Is5ICv&Z6H0>G zx!LW+x(?j{H1At9dU;>!F7<3ZwtXF)R`J4sD)p0CpH%5; z(6ym=(I~jUuFO)T?o3gSw5%PC`!sypGqJ;aVnPt(JMpQ+$6(&u*|YE}4`LUNknPHz zii-*j8X6i6Bh5>s!m&Zooi=HT$hS=7t~OtCqglMMpm+h@<4{y+!<0>rRv zgaKIWCjBNfL9;Seuwk6y5=2G}NlkOTBq*(MVzsO5)A#eRc8`lX4G*1+`b^gD|Mu{{ z&-k7Dp4pv=`p@*Shhf3{UV=u+yEbN=Mqa!p_HJ&}dtJ4`Tt^8(j{buYZ}M%TTiusq(_VS2b; z`D#>BgqU8T5%fh4Ey@^k1DD&ekN`P9H(}02)s$fUYs!{x2((x(>s*J1k*1PS^Wk_@{hfBGB3G^g zkJ`BjFXnq#mK1XCi-lK4%;@kJ1{GCv8>aL|3TH8YqOtl;$xYqB(}PEQT#g|E|1Z+s zE3C=%ZToe`v4I#71O$Px(3^!K2w^OMfFV+Yp=1;hLWHQa2!V|B&L{$7D3KCEk3ay0 zkSGZ-${VNQXwE^?jO!lV`)ya($KO zcOxpr?X~sJ{{q*1an|7b+!TkoUZ5ErEm2+PG!|H2otc`jp*dT!j7+&Xy8nx&sle5n z0oU}YQl=quwNdq@#|mK#JW@~vs7-{N@3WujC?WGTsYR5eB)&ilxQXirTa9_i4Qyy_<{Zb z75q7oHmMwUvAf?*yt@VzWJw3Ef@YiJw2Z7h34AM|w;) zI~CWwH4B@`fDQ~et|Fp+NNN@m1(R7Hp#4?p*%wUu*c#~<4DnNa3$F7OVK?+wXa%ND z;ycPP^_g!&Ky3lEAeOnSpNyk=kAd`=e|PK{;tlY2O#k&gy>L|8(3_Q^^&VGwu zLP})l=O)aYg`RJ26?+uIERvzW8g7QwK@x6?iv}rN|HY1TV-Y^rsgK8aOE;7g*9hGE zLeRyCzaI2VA}7}A9x!ltAHm%BMa+{Iq+EP!PZs^U80hn_-SSmn!UQVuHa%{ zjq{F5wZI-mU6Bi=ae5L%+QU%AAy_wc&0@0;`G9!}nFS_jjb33xQoshiV?&I7j8HR` zVJ-SpF&AP%=A&@i$-t(-KBEd z)z;`6jhL7YzYyrIHgLUn1e*CQ*!=rEA~-^ z>!)xbi!69_BYH-cF?s@&YOoa0?Td!_u>38yE9e)f$X`J(pr!3Y>wU5O708!Mo$fwyKZh!}~^RG@~aN@Qd3I@h4^nA%GK$-_Bl9!fS~g zk{hMkr^OU-{ma`CukO2Gh3M(D-RF(#bA}!gF}uO*wN>N@Yu0u*tP&_^b&GLKI9;?2 zKHlwj>FdoggW1g6MrV8d&w$8{hho`YqIQY6Xtr4M7w8F`*W1bNLgMY#iusu6T+ru) zd(3ekquay@j11T@hJ-`b7mV+$MGl);y2Gss3hx@tDM*F{Sir#IYt1b6rwL%I7b^tM zAHPN9_+OhQnZJs3QX(fYkOTNLAmN*U6w5Te6mH3>A)09-ogVDY5HBtyWvv9;M;!R{ zoC|$8yN&Q{V$j+*sbC{1{4mHi4C*yWh8`C&lW7Acui_5@*Tp+u`|F7YGtENdQA!{# zq>3W8fDgaxssy!ZkpPE9KG!_pFA$8lfJN*#Ok-%yN|mY;be;`0q}Y$5VW&K9{g}l) z7&WAKt(fd$VlVW%>py<@Xvmp$w2O#Y0DnuQy>VF+w`fJC?8Y;De#8MCG_oj&n$>1!% z{Tm8Ec|5svPy5eykN}-}_KgAJU%r`1qI5^akMJaXKgdS6ut6 znFGU*{cL|@=Dab3D_oymRIRukOjK{A)Q2cVoxNMA*~aYdhooC*9*88!Nt}lQn#FR= z#8!w#NWALE4Uut)(17_620Em0UhAJs4#EY&k&f}oJ`2P=IH`g#9T1;<9oyMNep+4U=1zIfAXEKhclZ_bu^ zUZGO=!j+_DwM;~`n}P-~WWgto8}(0qC9B5UZl^Pjf-WT;5q37EtN{$S+wNMj-bI-& z1kJ<11L0mtAO&wLf>LLVPQZb5K3p9*&jv|(aT)U+bJ7+Jx&{|GJB0Tt&^SJH;(1q7 zCiLDcxH%dGtFa$RtWxbki=&O=O<^*yDd?SbD46>pgCC2lb zQ4cG|l-?K}KMkbzI0vW~7FB?&z(LWZR3>1{8h=wnivb7WI!!gn<#8%Kl%M-^u*Od! zG`KF+RKjI?igOdJ9mFSod(lh>qtH`;vSSj=R%ZNsyYP7h3?wEjisW!+0 zz~89EeRqtWuLq6ky*S%=ZT7eDiWual|@f=m} z8FYK#K6lppNMxaYt`X5azZhvuI@3b2&(*(&f8$zu{2a!fbKi~XcFLK&Mwq2XdV9ivbFOub~kRLk096j+N`+r&T$h(&`6}2jE7^x26_f6RoPL6 zkE9FVjtG7jxOYp{z^YdMwhqh6x?^5o z5B0^Zx76`w{x+{K++X_)kKqqaE`ok2iFy^T4-WW<1u7CBtf*s=pOYRpONC5^kd(mq z{)2jNz(IP@M<}!ql@A=1YK&KM8m#>UsC_fRR*_F)W-5L`YSff3AoG*A8PWq&Q{Z~i z0Gt92N7kW^ASK6(JV(JqtIWJ(fN18kFtumEXBNv^G7ubE@Yfny`PvFvEYhJz#r~<) zEPkD);-zv*irM*X7s1TC&zFl3e=QUmkLlOO1e32{aDLQP;(gK8Epuc3d-oaF@Qg^U zx7<>YzI@(B=r~yTG;%$hh;R1)I(19%u}qs0K0=r+YG>$+_w+(!ByOBv^JcxsWjL@j zWjFqu^th<6M;*ahkKe!j476bf!y$|Y{-7d$m#8r5nP|O4;BAVr5IJPc0W;ipIbo|d z*q#ky9GSv}QiqtgAo~Gl@QL2Bn)?&j+9x`^@fGBgm>gd0I6$5SW7d-6qM0Hp7x)qB zDSr2`7SQ|+hy!gOjw99=wj0j=kjjgAsjV2bw3}V2uCWuXvPH}BBkSJ)AB$w`2g1z% z`~e@)3Yd(oT12)|)2-ppaPAjHpR@0P55k$xe5FpE6grF`45nLDW*hXbi@7t7Vg-cg zr2H4oU0mhWnIkzHUbOEta3HLVf2WLuC%0~^T>**&a0pOzA(edyBsYS&xW?d8%|CF7 z!l2Z(^PvcjCh50CX1YV+^e4)bCK6SKU(8e^*Hn*>`PgQBFIRHv9r&ru04(1 zlB`Y^f7V$kH2`eH(SqkpPztA}X&%r4(8}3T4d6SggTSJuYUqfVIiY5#WRx;qS&Vw! z*1<`aIEyWMH+R=i2Jg*w`f0L^(6+laO6!{$Rpr9_+LK&_S37eRW+VQSGY)b z0yTADP5M{7p1{YT1nvoT-Nh@6Gmb1eNso_w9J$jvt?V_F`6UK4F;>F~t9A#Sj7z-H zP=~`d9}CZT9@E1NXj!@BBOd5|b0V3}xBo+43GYTW#s`6A9lPFW9WHmHoEuGOzV|8X zM}Vs_YA5`U@wR_=C|ie%t@?UrPPPt%eHe&KqCvK1%D@eNENCZBfXjW^WF(d^o?3AK z0uwB0Z1SAOX1Jd?F!3)|o}VX-0w2E)v@i+(z9)k@TZ+_VuOyN7#UGY{xd}mRFG#5Q zcZUZ4EV!2heLiSnQ1aTm^-ltn%shBSdMWlrJ{NlyP=u)6T+krQ72r~xa~+0T2}+=Y z!IyBrn>%2h!>ovK4MbNp*4m#Y#(b}K39p(XWO&SZ)lzgGxMw?dA2^+7r#+GhqwM~n zI@Cv$PZ~vX%}(r%y-cgowFkjM?ohd5<}Ar}FOA979$e3qei)CGBe;yN6>X{SaRaM- zVymAuaij;GMFFm&JlrUt1=aDz$(S7IScriq7+Yg>QdYAg2E&6W4YVfig<*-o_Fr_= zmMQ+kFhe>19pNAyT-CC`jX&IthR_&b4Rh8&|%T1K%lmkAm zRgLDRh)Jp445eYCuwmgf$F(Djf%4MM>8knOyvLzV9%Zhc*_O_NCx@@pXlaj0inLBX z_VRL%JYG-CnznF!r#FAZDzxVXnL@lO0Y;bski$5V&>@GRherT4)g>kf@kT22L@=1` zxD6I+zHg+XgSHI3rSFK$%$2#Z2pdEnRE6G@=OykX^Zg)pKxW+sAC^vXq> z)fZ0_X%&0j6G9Fq-Q0Z5d@#&xxxew!J@iE#Z~Fd|O2Ntz7q{ZPVzSOR^2S#Re;dnw zjO%AJEXDDig1Qy?_;(D0@km4>%PJyC(G`jPMf2lZ6H~ zZs$V6i8(`gDGHEZ1bJC{-|UBqNAnPKjK+h>=Q`VD%o@*z}{~agBNU4GASXr?2 z{p2vR?QzEa=D0nnpq`0CW95hXIhYv!BU;G!jRHCK+i5RNNiW1j(7 zcFb3(`q0{S8VZ6T7Dsh>&V0DzcW1YbTrO)gqDkebi{_Ek+VV;epU_(7pX_S6TB5U=u&5nHkx$dZCsi0X$<(E^Nb_L4Pr}1_Cm0+=cHb5R=L&*u>Oz zrj1`&nz~3g>iZfkqFxh6vvXAzk-Q$0gZ_9#r>J2i%QPD+K%`^oX7@}U;;#n^O?DZV zl(oLSK7S1W2h=O3!S$CNv6TanRadlIUqn(#ba39;^()wIv`S4x)=AEj*iCr5LOTs} z_)c7&ySNB{woSaJ=R>^Ad?EKCxLAk?T~h0nUqnaRvT1bY z>CqIqf>*Nu81U4Tx!>BoW|6t4o{oipk@GJb1GOMoQ|v+#FZ9jYcB2k#=UyGcM%rA6 zH=myJ2flZM>Wc4XG9|%KY0cCIxhWm)>GX;$0s(^Jf9_%A{Z*2^sO`mdE~N^G*Cdz1 zJ4srGp-sl<>g~|(yLt6|{Ivdk7g2Wc^O~0iI##0<4l>0hh1S*GrxLJF-I9sdGTVn^ z2EX=++7Znop*M4Tkh^BG#US!e-PA9drE!Y0YD91ZvV}N03?t}r>Gx=+I~Kw@raTo8 zWJ@2?Qb6m##whJM!)MhyStvHD#UcR@6WXRRv_{QPhm0SJ!yQFWY~>|CVIN@jq~I?J zs6U~=6Y^4@*thRCtF_oro)rt(^`3?wD8+?%L=x zNr}>cj)Kcu7C)YB-YSUOz11CW{$2uqw%|I*F9Cy9dxbaytTow zkd1YGIAnT3)n=*w`@EO?kze7tc!#Y3Nw6rj*M!T!D&RHgBep>+6G8lMn09x&pxMw% zJu}vvM!ZR+phSCt)nm?7-%mVHB=EV_*8ZO6!%MOV_qqJdA4g%As=ctCxb{tO8b^0% zN->Z}(t^9|zieH9TaWH;(6ZG1Zi)8PJ?D<-La6?kK@9hJ5;$m@Kj&(Xz4B&25U6Kq zar<$DRlfc^nEgA7HL)nGs~hqGJF7$r#VS1v60Yo>@d>#PqJU;(0kRPEoB@Abh!zR? zgWV+vJFptz6SHb@AIO8+55Q+-VQ`m>Z)SP!qNTz#qslhU!H}J!)|W&Ezl}1?)>sOL0>5yO

~ziDx|#!YqvGOu5cS6pxKVe8o1r(4y4B!lj|ktQw8SP z0{d_=w01F&DH}93If$#*t`dX zWP+`2gPBlM+<&}XJg-a-adaA%aUi~*OI?y{0I${E=EG>A<7+G9yc=~P8{7^IHk68F zy0X^ta;Gg?)zRNQ zV!qeN!0A5wW{rKWVS6!V_Vm&B_nJM_VQBJ+F9WFcdtKs{1gy1!I8lh`$xS=u8`6RT zTk*J!C8A8%To`>QY}Lqt6_Z8S3r>%F=4Vd4H&Z*m#d+~utW{9^X!^~rHR=SFwhdk2 zyrU~`?U9@mG1<&RxNlNe!1Wty~i7!G;JZ%gcpQe}|P zu{FV)F_`Gm*ZkZpo=PN&>%*r!q+jB%?8m|R*dF*qMIT9dm9#H}2z|~xLe~e6JlOO>aAi7KE@A66uafysf=a1 zx{_L0UhI)iUbHDa*SfdEc@)dHXYS@-|L$P*F=@0Y%YzJ~XbV+85OEK_b@7jkJ+Z9N zIqar5l2MHAG4dgNnB>c~C(z(K?P?`{<{F2@#@PbW&x`Jo5TFF)hNX6-rb)HM9okm9J}l7G9$+EgBUMBDE%#E4E9a>RenQ-yt)i1|JAA z9rxhXZCO0oX|&Wz>}o}Qxa@P0NRY$L@a5`;gTbX3iPsGhCKqSXI+Oh@PP^nxLm`%KbPY=7*nIEZU zxngK`j%tVvTl(~4q>m>8U!J=c{f`4`wyXxEW@@Pl;8`uvEn>F~JP_OZiA{1NauYz9 z*b?I`aKdqH1tF+<#8;2{WP1cID4Jx-1n*9$RcUYCV=e^6)_(&j(m|SkMZoK{DdcWNH?#UWvCpE$1(8ddIOU=pnqYJ<5q}A;ASC)72c#cu2 z)^e@Ae=VHtIF_uXbM7@=HJ{0 z?s^_`94?*`o1S{{w`)YLTb({`;jcSpGI`nFBiF)9?wDD=z3*f?t)Ex;d_8>MJtc&W zmH#lU$#h;of$wsM_~&SMy!v#u|A+1f`{EPd+S|?__yKyCxSrKyH9Z&S3H&}$>Qxac zN2()n?hx{Y0PWLBi-dBjydt9iMFaJoy5bREt&M)#Ub z+EMh}(T*2~V@zWd&l;L*)2nZc#k|YM`>S?&^A~zkG!+u^&y80g>xH|Hw7VH+`o3>C ztmpQmyfJ2%qN|_7v;Wf$$p5+fvGj$AlG?cTAB)D*{X{O36|nyAjx!Mj&gY{#aYjK` z=DfpxOu5$nW}r zm$C61=;MRob#@eFFfrn^$>fQ)aX;b9oPCWE?9JdwMmln%g)2pLercR8FLH2b$Zy>m z>xyhGSeh_+Q@LdWVZ}gF8a}nYH!U*Ko_U~jy}UX3=5>v8744}}mJ_?^u0E6Oy%{qz zy0Z^Yq?lg#wvxvD_|?p0%i{m(e~j&8O%5L9>7;>v``)ggfCgwLnO9=f?_zN_cwLOd zY{>Tn9KB-XkhZ(*MyO(GL{nq&GDl(GL)4!C7NTHtQH;tRZjPK079F8=w3A)mi6?m&{)HFK){(Nk; z#D4q=me%FfJLvlSMLWGUA$XJfV`IGpVAkOw0Nf;d)c`B!T*m_yE*sHu??!Jl$0|CJ zZe!asnCUMAcr%Q|o?}IJ;oFJ))54tsu)0L8!Dj8;Dj4Odo!7i09ih1l78v=x123<} zGph?Ort^J1cHu6rh&hth+jAecOFq9sC2S^v%E>ZSs)93K7YZw5tO+WC9q;diX$q`xvG$pu0xP~zpd%f# zf@c3>u8M&dHfQK^rz`R=Cw?CmN@Zk){TTQk9bL>f}t4)(H$jzOiY{V+88h*f;EuCt`D z^&5qSO=Xyj#vch`><|aBN&6&MfL$V?kEpGtA#+s}UnU*_`y{D5<_k5de5+vK^Vj}al?dzs9QL&;hi#h#5=7sO zRnow#Mz`kC5b4fg?26g68w`UOGn~p$HF279dZ~?d=J^+^k++S<%r@Sxr@kKKs^)the%y?aK zXhUOrg?9VwL`BQ^Gfr6Rq7vs!%Ld=t4Dai+QQ?(+?tKYDX(hVf$NTNfn(Amt?Zs1% zT?QOun{$rm$WKn^TebQH!k-#k55MNW$;b~LMNo9kNz-+{4vG%hrrL^X$^iub98}}q z9axhL(8;Xc-u!raPsXq~a+mnp=nbOH4ZKTxa5FW{{+Zbyhi@t~oYL5styA%X0ljA> z7YK>cT`$Ds>7>oTm9!O8=h$f{>ARKJJP{Qu91ZOJ&be0TAv(k2+xUN4)9Dynm35Xo z>rPzez?*V--@Xf|5=Cbr{6{QMgLzo)HQWk@8MRA?u|_cHj0MGfNDza z06(-T0@6NQHr-@Pah|6_KRo+4-JxM(Z|mURT(yOcs;2^ojBOg#)QhuL_tz~tgCnsl z`(jlAc!6p;J>`?S_Kc{;(Qd$TtFf>(ujO%xPNAR)GmXG*=k0Pklr$Q6SY`O$r@6#? znT5?Ck?&~LBki62UgsoAN{;Qf^4P+8k1YPck=V5dk9|LXV90KlGWJTW5-+riY+0In zfk5lCg6vDZ9SZ1}TXzR3K#PZ{c;@FW@y~vzW1mJ^T0_p_py!Lyt75VH?J#A?A69|+ zCA58nEgQ+vQK=D{q)wGUZiI`m_RS>NFg;XD+H7WH>(ZGBZ>Nte-SBga_)?Fp7>=l7 zw)h;w#@(wza}f6iA_GEAK_%|$b}a!tn5?;nZX;wW<8)?28Ds%dY5vR?ZZAnt^mMfk z2qlP8HU#E1)f2!THq=0rnu&)AsQYobE?uH3Y9$chJ8E{GwEwjQJNqmv`WkWuaGM;) zYl<}%1*uEFn!S<0K$F^DLZa<0+r)o&sKu+}FjvLKoqXypAk!D%)Jc9Hkz5ya#c;pL z9Uv%pf0@w`A(B>k5R%H5Ud`PUaD(X|s^G$j^m%Y+*>|YMy`JWgQi8-TybNe$2{n^xCI{!ennMug{_5kq^Z7=EVa5?Dk6{dC*Gb=LsDh;g z#*YM8ZY-bk3X&!LWyHRQPSLAadN0fgbKcgB46C)`_fHZ$Vy>@$je}ucpd)@U8M5C;4VDin)^$qji&0 z$(|R2zy7H83dvW+ZsdnJoyzk4SpM%02fW|xpbW5axsX+Q=6%G9d~lwhH_x0@^GZ9} zgx2|Yq@4NY?zjD+aTK1<>*&A-*pmozj{M2zZ*zWX{K+;b_n{ch@BfRTjI%mIKcSQh zNhy^(zSsVda%$@Y9sW$K)~@+fhjN;6*jlLCKi6%WsU{|Y&$nzR!;*ML@vnJpnruz( zyS|35`>t#@5=t8)8L(&P)dDWVTQA`s;{yC4&nucb3vC z9K~;#u8b8Fbvd|?e8p>3`r{B@>1zxdpntuHz~!um01hJX`@&3?DuK6tTg7-Su!%Xd z2aE>^H5GjDr@P5_-ROxhtJ2{T1Sr2?=C!tQM|7fCV*Gd9Qb~T1`s|w z5a2LQH{cf;?Iu9JXlRROQqfgULsuJgU}93bA>L2Gdp-}I+qV)xk^0vWFgcH4cuU8k zwX&P(MF(pnG!8RVGmE4Im6eXPOAm^@l0_{&BnWt~$<1KWP^@Vze?LP}T$qKIC5RUN zff9b`S$=e{-yT6>f+UU~HNKV&f3?2BE&qTcTpYES#8t3j?t2eDBVZT&^R^Ju38|Ur zka3*ij-NJ8q>gt|G4S4GeZ1_IJytJ3vb8-?X=V9w0NmzM()n7qpfNr zMObubomuVTjM~UoBh#XRi|V)E1@rF?mmC>pz0ba`r++A0|87yP_6;j~v#W1Y;;Vqs zC&eBM?)NKeW0Citx|_UmuhAcVeZ!_caL}!mHMIVSH2zKEd$j5qMbD$>(>cRizP=6q zVXX)mTJ5>*7ys_~iN5c6h`jUcN@~l6+7Fl*|1zva5)y|s9hC3C0c{TmKgw;Xd>(Rt zeta=ngPVUycjK%+cUuu2-WKr^_axIA++fjHhi66GYtNfFlu*0n)8A$<>I?zv9DRx#HjBuTwdmWvZae9T)+QWlhN!K+W_aoF3s7dQ}ZmjHR3AGx0&LV zZ>Cf!wD&wAMx2LD0s#SQT z^+o+E_#Wm6#G&E8y_Z}NHrVz+b)4(4yFV~W_(mpdE|~rRwG?;_+Dmo)DL?nQ@77)Z z)p6HnQ^DQ1{DOB$fk@KH>e~@bYt@L7Kphu(*Zu{H?AuDPAM^D6ds$sp546q8!ObZJ zweXm%eDX<+@YIK5PnjGa2V#M$7o`DVsUIfO@a*3mF1p}SUQV0}mRNvhSN~CDhg7u*ULH{JFOE_@h>L z+<@pSsCvOt=m9EUUE9wUA2LTJ4 z{HB-BUvwxt^Upp*7dY3VZ^i{6pKFWKr2XMJ8#Fg?x3GGeV*Y#@oH4cF&2e`ZNH830lpy0{7|Is zdn~uNIB2EUW8SK{nef{X>Mz^thUo4mTWux{R=D7G`EsI!q7ulewUUvPu# zX~2jucVc9{MjZciedQ0SA)qH>^=jCNuwZdB=npK6Rt4QB%)LNAvi12`y=}U-O&>C7;DJ8 zvBR9*dK=&XNdQ4!8a>p%=AW|@Xx*J zmB)*#YQwuDh{eKPZgMZTEL0zF8<#VzvFeiW_wl;k5_M7R7n0&L5!k$u%sglvBp{_D ztim1&nTODXga3KacXK~lVQ>&usehQj z?JoL-fZ6_S>}4<+W_>{*XTmZ+v)EylLaPMeJ;cvTWKo)hj8s^2&?2=f3<}0euVwpy|)Et@=eK4?JK`+=JV6n z4?m~fd%_>Q*r=bc!U!J8%MKnTLrA5O31pdkgRK~uY!yy*t?QEprRU48Sv9d&JZl_p z)>u9H@~*N%SS!4etRBZ6Gi6qV`98KxY4GnS5mAd|c4(YNxsix2{^qoim*u2e{t|W(j8y$6 zR`vhq2jVkA_^5v4-yQy5%+2Fgy8rLreqIW4u0)^ZU8;qX=SM=k7HY87{ehuct9v`w z>n*VIFVYeJ`&w1pKKY$>jO_HgI_+3JFN@J)B!=sa z7*UOnrQ?|f|7}BOd&aBOm$?v-8WY-}y5iCmQ@K@%d)i5Fj{bLtd$9@j$M=k-$c?lA z3swdX{{M!Rk!#SzuolY9yj3qLuy!7qO&gC%d0K0xO}rQt1_b#bt;}4liL%jwhr4aH{&JvYqfzw!}QMC6}De=cgHA0Y0MfGySt(S zaPS^Io_b-}l%Rlf`(X{czrMI9)y#C%t@E!MW2bPEL7}0~J+CpX zXr?o^B5K0o)^f4s!bKS;d!nD)%GFUb%fJ!C z$~;%*kU!T?b^XW8&z$I(s|8Vy(S;Vf9qTp5CzkxR6OJ`CQRsnLLP`!37;_n!qV*E*bAV4AyR$&TNHUBsf7b}SGEQ9N$ zA%{7zk;`25oIH&#F-EWlTo9E>XZ~Yrs|5yoH5IM(#A@9j;@p4_$pD%DMPlb`&48-_ zp2BRIAqi2cIDdjl2nbDB!~TS`=;`>(Zoti}8m)cGuENB)P%C}r|Lob%#SKTLwuZj6 zFS&ZvIRD4z6b~KOx(Rc>>$~~3d~@>cs+u{+j?%aNk47me)lX>mlA~QOoY&?UR;X~_ zE~9(rdj?mzVCCW;CS@({T_TIb6{FQ3L8aZY)cs%D4}KU3r}R}$4FoBUyA%vhq3`xo z4W87`H6|%W-B)V2%fb-4E%-b$ZRx8a{Fl#T#ubHI`h5dApM7^l)+OcrJ=3x%cSdBM{2RDx)|1lB&AiL-)mx*{YaXnxayWob(7@23CX>YIb7} z;0;Cpw0EQw&Au_6WUAtnd!V{7l!wSaUjr2EE>7VpUIlIK-g`V|#$vnW-yI=Iz?wMm z6?FfI21EwkL0~tWW%bufPIah$PB7!`kaz&6XXAEyKJHcS%5)o&+(kI5GHG3k zK>;v{4G7mOWN{%$5!<#NZKDUge%xtXaAkXDMZiW_V-Y7S$zceqCqItuyWZ%%We2<8 zG;Poy<}_Mk!6kclq*qZRRo=XFL% zVB780()}VKkn>5l!E6=tSD0DbhzrFOp=zB=h951wnul&&KeM|vKwjd+0Wqfk?wI5_ z0+Hl5p=iFASh3WI(*ViAUjj`OoA#;U)qPuL(s#_m)^f8pmoC1K$UPFGd>W__D1$tv zH15$RTr3Q(w8)K4u;nX@TIVR4!h%@pR9DOf`x;Kido&``18R`UQsxjtpQvKjB|+?c zr5fG#P%RU*y_w;YhJSt_ zlQ@ni$;)qxtA;x%>Muu&v7+kU5Q`|LerS#TCT*s8s5k)QD)IH|Xm7yulq!m6YWO7@ z-+SZtw2yxUy%*_*Lcj2fBNAuy;lec2P9ytMg$9+FQnipn=9iJ{XoC1l<}2#4!c z5{{W;wouaJ&*Cb&t$J2^m-PKku>Zv6VEoSPi&yr>>z)bDteS7`IrW~l!iB77r%2R( zVDq*-C8!F=W0!*QmNHypN{ysb0Ou+Q8%wrsSaeVBn?>!Tunh<@T#0QSAC+#l6I`pI zCW_d$3M&j-Teh4{NfHv(XJqRFIITxLTn*ZfKNIl=AJm9eO)`cTI9fz;%tjV=C!wG@`~%0AG3wjzr+;V}8~u^>!6ipPyX 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 Lv;c-0{vH2cv@LHo literal 0 HcmV?d00001 From 3955f9b875e90e289595e5a156b0e9eeac8bc7da Mon Sep 17 00:00:00 2001 From: yaya <3181871180@qq.com> Date: Wed, 24 Jan 2024 23:23:48 +0800 Subject: [PATCH 6/9] fix readme pic --- Diffvc-Readme.md | 29 ++++---------------------- readme picture/{wps1.jpg => pic1.jpg} | Bin readme picture/{wps2.jpg => pic2.jpg} | Bin readme picture/{wps3.jpg => pic3.jpg} | Bin 4 files changed, 4 insertions(+), 25 deletions(-) rename readme picture/{wps1.jpg => pic1.jpg} (100%) rename readme picture/{wps2.jpg => pic2.jpg} (100%) rename readme picture/{wps3.jpg => pic3.jpg} (100%) diff --git a/Diffvc-Readme.md b/Diffvc-Readme.md index 80284f7e..be3fabac 100644 --- a/Diffvc-Readme.md +++ b/Diffvc-Readme.md @@ -14,15 +14,7 @@ ## 训练截图 -![image-20240124225137897](.\readme picture\image-20240124225137897.png) - -![image-20240124225205428](.\readme picture\image-20240124225205428.png) - -![image-20240124225219412](.\readme picture\image-20240124225219412.png) - -![image-20240124225229981](.\readme picture\image-20240124225229981.png) - - +参见 readme pic ## 所使用的的依赖 @@ -57,26 +49,15 @@ VCTK:https://drive.google.com/file/d/12s9RPmwp9suleMkBCVetD8pub7wsDAy4/view?us (以下涉及到的代码块在talkingface/data/dataprocess/下 ②运行inference文件的第一部分,配置相应的环境(注:librosa版本为0.9.2) - ![img](.\readme picture\wps1.jpg) - -③对inference文件的第二个代码块中的get_mel函数和get_embed函数进行阅读,编写相关代码分别对原wav文件运行这两个函数。对原始数据运行get_mel函数生成mel文件,运行get_embed函数生成embed文件,分别保存在两个文件夹中。(注意,这里需要调用spk_encoder的预训练模型,下载之后引用其路径即可) - - ![img](.\readme picture\wps2.jpg) - -④在原代码中补充引用预训练模型、提取wav文件并执行两个函数,并分别保存到相应的两个文件夹中且正确命名的代码: - -![img](.\readme picture\wps3.jpg) -======= - ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps1.jpg) | +参见readme pic/pic1 ③对inference文件的第二个代码块中的get_mel函数和get_embed函数进行阅读,编写相关代码分别对原wav文件运行这两个函数。对原始数据运行get_mel函数生成mel文件,运行get_embed函数生成embed文件,分别保存在两个文件夹中。(注意,这里需要调用spk_encoder的预训练模型,下载之后引用其路径即可) - ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps2.jpg) | +参见readme pic/pic2 ④在原代码中补充引用预训练模型、提取wav文件并执行两个函数,并分别保存到相应的两个文件夹中且正确命名的代码: - ![img](file:///C:\Users\liberty\AppData\Local\Temp\ksohtml9656\wps3.jpg) | ->>>>>>> 2c0759714535f5b1bed5dfb3c7498839f8cf5d87 +参见readme pic/pic3 ⑤运行该代码块,即可得到处理后的embed和mel文件。 @@ -88,8 +69,6 @@ VCTK:https://drive.google.com/file/d/12s9RPmwp9suleMkBCVetD8pub7wsDAy4/view?us 7.进行训练 -## - ## 框架整体介绍 ### checkpoints diff --git a/readme picture/wps1.jpg b/readme picture/pic1.jpg similarity index 100% rename from readme picture/wps1.jpg rename to readme picture/pic1.jpg diff --git a/readme picture/wps2.jpg b/readme picture/pic2.jpg similarity index 100% rename from readme picture/wps2.jpg rename to readme picture/pic2.jpg diff --git a/readme picture/wps3.jpg b/readme picture/pic3.jpg similarity index 100% rename from readme picture/wps3.jpg rename to readme picture/pic3.jpg From 38d2d2117488a77ca4cb690074a9cfa1ee2de9ca Mon Sep 17 00:00:00 2001 From: Hihixiaolv <143249260+Hihixiaolv@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:25:26 +0800 Subject: [PATCH 7/9] Update Diffvc-Readme.md --- Diffvc-Readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Diffvc-Readme.md b/Diffvc-Readme.md index be3fabac..73433f71 100644 --- a/Diffvc-Readme.md +++ b/Diffvc-Readme.md @@ -3,9 +3,13 @@ ## 成员分工及工作量描述 吕春吉(贡献度:50%):组长,负责阅读论文源码,理清架构,进行了model主模型的嵌入、dataset数据处理部分的嵌入、trainer部分的嵌入、推理部分的嵌入、模型的训练和调整、参数的调整等工作 + 蔡昕怡(贡献度:25%):负责数据集的查找、数据集的处理、模型的训练和调整、hifi_gan模块的嵌入 + 唐欣欣(贡献度:10%):负责encoder训练部分的添加 + 马翊程(贡献度:10%):参与数据处理部分的嵌入 + 孙嘉成(贡献度:5%):负责数据集的查找 ## 完成功能 From 5f1ec52673f49b9866c1542f623d920551fe8889 Mon Sep 17 00:00:00 2001 From: yaya <3181871180@qq.com> Date: Wed, 24 Jan 2024 23:31:52 +0800 Subject: [PATCH 8/9] fix bug --- talkingface/properties/overall.yaml | 4 ++-- talkingface/trainer/trainer.py | 27 +++++++++++---------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/talkingface/properties/overall.yaml b/talkingface/properties/overall.yaml index dab84676..48c8d7ed 100644 --- a/talkingface/properties/overall.yaml +++ b/talkingface/properties/overall.yaml @@ -1,13 +1,13 @@ # Enviroment Settings gpu_id: '3, 4, 5' # (str) The id of GPU device(s). worker: 0 # (int) The number of workers processing the data. -use_gpu: False # (bool) Whether or not to use GPU. +use_gpu: True # (bool) Whether or not to use GPU. seed: 2023 # (int) Random seed. checkpoint_dir: 'saved' # (str) The path to save checkpoint file. show_progress: True # (bool) Whether or not to show the progress bar of every epoch. log_wandb: False # (bool) Whether or not to use Weights & Biases(W&B). shuffle: True # (bool) Whether or not to shuffle the training data before each epoch. -device: 'cpu' +device: 'cuda' reproducibility: True # (bool) Whether or not to make results reproducible. # Training Settings diff --git a/talkingface/trainer/trainer.py b/talkingface/trainer/trainer.py index a715f2cc..afcf2bdd 100644 --- a/talkingface/trainer/trainer.py +++ b/talkingface/trainer/trainer.py @@ -2,7 +2,7 @@ from logging import getLogger from time import time -#import dlib, json, subprocess +import dlib, json, subprocess import torch.nn.functional as F import glob import numpy as np @@ -36,10 +36,10 @@ import talkingface.properties.model.params as params from talkingface.model.voice_conversion.diffvc import diffvc -# import webbrowser -#import visdom -#import umap -#import talkingface.properties.model.diffvctrain_params as params +import webbrowser +import visdom +import umap + from talkingface.utils import( ensure_dir, @@ -52,7 +52,7 @@ get_gpu_usage, WandbLogger ) -#from talkingface.data.dataprocess.wav2lip_process import Wav2LipAudio +from talkingface.data.dataprocess.wav2lip_process import Wav2LipAudio from talkingface.evaluator import Evaluator @@ -545,10 +545,8 @@ def __init__(self, config, model): model.train() losses = [] for batch in tqdm(train_loader, total=len(train_set)//batch_size): - mel, mel_ref = batch['mel1'].cuda(), batch['mel2'] - #.cuda() - c, mel_lengths = batch['c'].cuda(), batch['mel_lengths'] - #.cuda() + mel, mel_ref = batch['mel1'].cuda(), batch['mel2'].cuda() + c, mel_lengths = batch['c'].cuda(), batch['mel_lengths'].cuda() model.zero_grad() loss = model.compute_loss(mel, mel_lengths, mel_ref, c) loss.backward() @@ -574,12 +572,9 @@ def __init__(self, config, model): for i, (mel, c) in enumerate(mels): if i >= test_size: break - mel = mel.unsqueeze(0).float() - #.cuda() - c = c.unsqueeze(0).float() - #.cuda() - mel_lengths = torch.LongTensor([mel.shape[-1]]) - #.cuda() + mel = mel.unsqueeze(0).float().cuda() + c = c.unsqueeze(0).float().cuda() + mel_lengths = torch.LongTensor([mel.shape[-1]]).cuda() mel_avg, mel_rec = model(mel, mel_lengths, mel, mel_lengths, c, n_timesteps=100) if epoch == save_every: From a3710286c729b4695449949c664c04d72f0e7e9c Mon Sep 17 00:00:00 2001 From: yaya <3181871180@qq.com> Date: Wed, 24 Jan 2024 23:43:14 +0800 Subject: [PATCH 9/9] fix the test change --- Diffvc-Readme.md | 2 +- talkingface/config/configurator.py | 5 - .../data/dataprocess/wav2lip_process.py | 2 +- talkingface/data/dataset/__init__.py | 6 +- .../audio_driven_talkingface/__init__.py | 2 +- talkingface/utils/exceptions_libritts.txt | 208 ---------- talkingface/utils/exceptions_vctk.txt | 359 ------------------ talkingface/utils/valid.txt | 10 - 8 files changed, 6 insertions(+), 588 deletions(-) delete mode 100644 talkingface/utils/exceptions_libritts.txt delete mode 100644 talkingface/utils/exceptions_vctk.txt delete mode 100644 talkingface/utils/valid.txt diff --git a/Diffvc-Readme.md b/Diffvc-Readme.md index 73433f71..cebe4f53 100644 --- a/Diffvc-Readme.md +++ b/Diffvc-Readme.md @@ -48,7 +48,7 @@ VCTK:https://drive.google.com/file/d/12s9RPmwp9suleMkBCVetD8pub7wsDAy4/view?us 数据处理部分: -①首先建立一个数据集文件夹data,包含“wavs”、“mels”和“embeds”三个文件夹,并将原数据集上的wav文件按照原文件夹放入wav文件中,在filelist中加入训练数据名称 +①首先建立一个数据集文件夹data,包含“wavs”、“mels”、“filelist”和“embeds”四个文件夹,并将原数据集上的wav文件按照原文件夹放入wav文件中,在filelist中加入训练数据名称valid.txt exceptions_libritts.txt (以下涉及到的代码块在talkingface/data/dataprocess/下 diff --git a/talkingface/config/configurator.py b/talkingface/config/configurator.py index b8822b39..64773dc9 100644 --- a/talkingface/config/configurator.py +++ b/talkingface/config/configurator.py @@ -184,7 +184,6 @@ def _merge_external_config_dict(self): def _get_model_and_dataset(self, model, dataset): if model is None: try: - print("external_config_dict") model = self.external_config_dict["model"] except KeyError: raise KeyError( @@ -208,14 +207,10 @@ def _get_model_and_dataset(self, model, dataset): ) else: final_dataset = dataset - print(final_model) - print(final_model_class) - print(final_dataset) return final_model, final_model_class, final_dataset def _update_internal_config_dict(self, file): with open(file, "r", encoding="utf-8") as f: - print(self.yaml_loader) config_dict = yaml.load(f.read(), Loader=self.yaml_loader) if config_dict is not None: self.internal_config_dict.update(config_dict) diff --git a/talkingface/data/dataprocess/wav2lip_process.py b/talkingface/data/dataprocess/wav2lip_process.py index 1dd5a8b5..e2279a42 100644 --- a/talkingface/data/dataprocess/wav2lip_process.py +++ b/talkingface/data/dataprocess/wav2lip_process.py @@ -1,5 +1,5 @@ import os -#import cv2 +import cv2 import numpy as np import subprocess from tqdm import tqdm diff --git a/talkingface/data/dataset/__init__.py b/talkingface/data/dataset/__init__.py index 17c0c4dd..b884aeb6 100644 --- a/talkingface/data/dataset/__init__.py +++ b/talkingface/data/dataset/__init__.py @@ -1,4 +1,4 @@ +from talkingface.data.dataset.wav2lip_dataset import Wav2LipDataset +from talkingface.data.dataset.dataset import Dataset from talkingface.data.dataset.diffvc_dataset import diffvcDataset -from talkingface.data.dataset.diffvc_dataset import VCDecBatchCollate -#from talkingface.data.dataset.wav2lip_dataset import Wav2LipDataset -#from talkingface.data.dataset.dataset import Dataset \ No newline at end of file +from talkingface.data.dataset.diffvc_dataset import VCDecBatchCollate \ No newline at end of file diff --git a/talkingface/model/audio_driven_talkingface/__init__.py b/talkingface/model/audio_driven_talkingface/__init__.py index aa60c59e..04a35f33 100644 --- a/talkingface/model/audio_driven_talkingface/__init__.py +++ b/talkingface/model/audio_driven_talkingface/__init__.py @@ -1 +1 @@ -#from talkingface.model.audio_driven_talkingface.wav2lip import Wav2Lip, SyncNet_color \ No newline at end of file +from talkingface.model.audio_driven_talkingface.wav2lip import Wav2Lip, SyncNet_color \ No newline at end of file diff --git a/talkingface/utils/exceptions_libritts.txt b/talkingface/utils/exceptions_libritts.txt deleted file mode 100644 index ed5da501..00000000 --- a/talkingface/utils/exceptions_libritts.txt +++ /dev/null @@ -1,208 +0,0 @@ -1027_125147_000139_000000 -7739_8592_000126_000000 -1535_141642_000009_000000 -1974_139741_000015_000001 -8855_283242_000010_000000 -7120_118112_000003_000000 -5583_41919_000016_000001 -1509_145742_000007_000000 -1265_135635_000018_000000 -176_122025_000000_000001 -5655_46267_000030_000000 -2012_139358_000006_000000 -207_143321_000019_000000 -176_123269_000011_000000 -8699_291107_000027_000000 -1638_84447_000036_000000 -1050_134119_000035_000002 -4108_2777_000010_000000 -6233_61741_000020_000000 -4088_158079_000094_000000 -1731_142320_000096_000000 -2401_144485_000071_000001 -1553_140048_000009_000000 -3240_131232_000080_000000 -7402_59171_000003_000000 -8396_120280_000033_000001 -8238_283452_000006_000000 -166_352_000004_000000 -589_146346_000020_000005 -3513_163606_000046_000001 -500_125123_000112_000000 -4586_96498_000028_000000 -7538_100045_000021_000001 -1958_144503_000020_000001 -3857_182315_000006_000000 -176_122025_000017_000001 -8063_274112_000077_000000 -176_122025_000009_000001 -6458_232057_000041_000001 -7495_102612_000071_000000 -4807_26852_000071_000000 -78_369_000023_000000 -14_212_000011_000009 -4363_11049_000058_000000 -3224_167024_000041_000000 -16_122828_000022_000000 -207_143321_000019_000001 -1271_136861_000021_000000 -339_132718_000019_000002 -9023_296467_000008_000000 -5660_101884_000031_000000 -1845_145083_000010_000002 -2060_150855_000024_000000 -2045_158081_000020_000001 -4088_158079_000088_000000 -922_132300_000030_000001 -5333_5083_000012_000011 -6385_34655_000022_000000 -340_124368_000004_000000 -4044_9010_000022_000000 -2204_131732_000017_000017 -64_76974_000089_000000 -2436_2476_000048_000000 -4806_26894_000004_000000 -28_12332_000061_000000 -2531_156724_000012_000000 -4957_30119_000014_000000 -1182_134981_000027_000000 -3540_163612_000169_000000 -6104_58845_000020_000000 -60_121082_000029_000000 -4363_11049_000177_000000 -1958_144503_000027_000000 -7278_246956_000020_000000 -2401_144485_000068_000001 -1264_129805_000026_000000 -6098_57836_000021_000000 -2517_135227_000015_000004 -4680_16026_000096_000000 -4116_3582_000039_000001 -7511_102420_000005_000001 -4297_13009_000042_000000 -307_127535_000033_000003 -1841_179183_000009_000000 -8770_295462_000051_000000 -3513_7741_000060_000001 -7800_283492_000025_000000 -8479_276730_000026_000000 -3728_105386_000010_000001 -4800_73729_000026_000004 -4427_20023_000004_000007 -8063_274112_000030_000000 -7145_87280_000100_000004 -4243_187023_000016_000000 -6643_67857_000002_000000 -1885_136863_000025_000000 -7120_118112_000035_000000 -3513_163607_000044_000002 -1553_140048_000001_000000 -2053_138901_000004_000004 -510_130101_000054_000000 -899_126233_000033_000000 -1265_135636_000067_000001 -6981_70843_000127_000000 -6104_58843_000033_000000 -7078_271888_000015_000000 -335_125945_000035_000001 -1265_135635_000052_000000 -5339_14134_000072_000000 -7939_120318_000016_000000 -6032_58192_000008_000000 -1743_142912_000015_000001 -839_130898_000020_000000 -1121_176698_000015_000000 -501_125128_000068_000000 -7783_107486_000060_000001 -806_124221_000037_000000 -78_369_000043_000006 -1731_142320_000053_000000 -4837_285896_000040_000000 -5622_44586_000017_000000 -7991_102381_000019_000000 -14_208_000021_000002 -157_121907_000017_000000 -4108_2777_000059_000000 -14_212_000011_000004 -3983_5331_000002_000000 -497_125118_000079_000000 -8699_291107_000302_000000 -512_124520_000071_000008 -7120_118112_000019_000000 -78_369_000030_000009 -954_130627_000038_000001 -6574_70756_000008_000007 -4381_14897_000005_000006 -6006_60489_000033_000005 -4267_72637_000007_000000 -5731_50776_000030_000001 -2053_138901_000037_000001 -2592_5341_000039_000000 -1283_136983_000009_000000 -1731_142320_000127_000000 -1265_135635_000003_000000 -1731_142320_000059_000000 -6895_96175_000051_000000 -1027_125147_000080_000000 -8479_276730_000042_000000 -1335_163935_000018_000001 -1731_142320_000114_000000 -374_180298_000028_000001 -4088_158079_000154_000000 -8875_293959_000083_000000 -5876_8675_000009_000000 -7665_104979_000053_000000 -5968_55202_000071_000000 -500_125123_000032_000000 -1705_142318_000022_000000 -4586_96498_000035_000001 -6104_58845_000023_000000 -7739_8592_000024_000000 -2473_157859_000047_000004 -249_121331_000003_000000 -2012_139358_000012_000000 -6104_58843_000080_000000 -454_134728_000083_000000 -6904_262305_000001_000000 -1974_139741_000048_000001 -549_126410_000049_000001 -1603_139325_000039_000000 -8770_295465_000020_000000 -816_53638_000055_000000 -6701_71404_000089_000000 -78_369_000035_000003 -4495_18533_000041_000000 -2436_2477_000061_000001 -118_47824_000109_000000 -8479_276730_000034_000000 -298_126791_000064_000000 -8176_115047_000053_000004 -7511_102419_000004_000001 -1027_125140_000073_000000 -5583_41259_000007_000005 -8465_246947_000028_000000 -4535_279856_000055_000000 -6880_216547_000039_000000 -2045_158081_000012_000000 -1958_144503_000083_000000 -1974_139742_000065_000000 -576_129623_000056_000005 -5519_39481_000017_000000 -1027_125147_000045_000001 -5304_55856_000010_000000 -205_159056_000013_000000 -337_123025_000026_000003 -2368_157056_000070_000000 -806_124221_000045_000000 -2092_145709_000002_000001 -14_212_000018_000001 -1974_139742_000069_000000 -1731_142320_000069_000000 -211_122442_000144_000000 -7945_112011_000069_000000 -7000_83706_000006_000003 -78_369_000065_000003 -8190_284435_000073_000000 -806_124221_000040_000000 -1271_136861_000062_000000 -2401_144485_000092_000000 \ No newline at end of file diff --git a/talkingface/utils/exceptions_vctk.txt b/talkingface/utils/exceptions_vctk.txt deleted file mode 100644 index 95d78f7b..00000000 --- a/talkingface/utils/exceptions_vctk.txt +++ /dev/null @@ -1,359 +0,0 @@ -p234_280_mic2 -p234_122_mic2 -p234_010_mic2 -p234_097_mic2 -p234_304_mic2 -p234_124_mic2 -p234_075_mic2 -p234_318_mic2 -p234_125_mic2 -p234_355_mic2 -p234_157_mic2 -p234_089_mic2 -p234_062_mic2 -p234_317_mic2 -p234_279_mic2 -p234_094_mic2 -p234_199_mic2 -p234_272_mic2 -p234_054_mic2 -p234_083_mic2 -p234_336_mic2 -p234_030_mic2 -p234_091_mic2 -p234_055_mic2 -p234_191_mic2 -p234_258_mic2 -p234_038_mic2 -p234_035_mic2 -p234_346_mic2 -p234_222_mic2 -p234_200_mic2 -p234_173_mic2 -p234_262_mic2 -p234_334_mic2 -p234_253_mic2 -p234_241_mic2 -p234_139_mic2 -p234_316_mic2 -p234_099_mic2 -p234_207_mic2 -p234_325_mic2 -p234_093_mic2 -p234_118_mic2 -p234_194_mic2 -p234_006_mic2 -p234_155_mic2 -p234_259_mic2 -p234_081_mic2 -p234_063_mic2 -p234_046_mic2 -p234_177_mic2 -p234_024_mic2 -p234_213_mic2 -p234_333_mic2 -p234_189_mic2 -p234_236_mic2 -p234_135_mic2 -p234_228_mic2 -p234_005_mic2 -p234_108_mic2 -p234_257_mic2 -p234_100_mic2 -p234_179_mic2 -p234_309_mic2 -p234_165_mic2 -p234_040_mic2 -p234_074_mic2 -p234_181_mic2 -p234_242_mic2 -p234_170_mic2 -p234_327_mic2 -p234_013_mic2 -p234_132_mic2 -p234_204_mic2 -p234_342_mic2 -p234_056_mic2 -p234_111_mic2 -p234_095_mic2 -p234_031_mic2 -p234_275_mic2 -p234_137_mic2 -p234_130_mic2 -p234_245_mic2 -p234_290_mic2 -p234_129_mic2 -p234_288_mic2 -p234_221_mic2 -p234_019_mic2 -p234_043_mic2 -p234_077_mic2 -p234_050_mic2 -p234_350_mic2 -p234_167_mic2 -p234_273_mic2 -p234_294_mic2 -p234_187_mic2 -p234_156_mic2 -p234_266_mic2 -p234_254_mic2 -p234_227_mic2 -p360_262_mic2 -p234_303_mic2 -p234_295_mic2 -p234_032_mic2 -p234_025_mic2 -p234_003_mic2 -p234_328_mic2 -p234_291_mic2 -p234_016_mic2 -p234_322_mic2 -p234_248_mic2 -p234_102_mic2 -p234_356_mic2 -p234_087_mic2 -p234_012_mic2 -p234_270_mic2 -p234_104_mic2 -p234_073_mic2 -p234_209_mic2 -p234_026_mic2 -p234_205_mic2 -p234_017_mic2 -p234_343_mic2 -p234_086_mic2 -p234_212_mic2 -p234_027_mic2 -p234_018_mic2 -p234_105_mic2 -p234_249_mic2 -p234_311_mic2 -p234_041_mic2 -p234_326_mic2 -p234_123_mic2 -p234_329_mic2 -p234_299_mic2 -p234_296_mic2 -p234_171_mic2 -p234_263_mic2 -p234_216_mic2 -p234_321_mic2 -p234_090_mic2 -p234_069_mic2 -p234_282_mic2 -p234_117_mic2 -p234_286_mic2 -p234_233_mic2 -p234_214_mic2 -p234_047_mic2 -p234_022_mic2 -p234_106_mic2 -p234_239_mic2 -p234_219_mic2 -p234_133_mic2 -p234_353_mic2 -p234_052_mic2 -p234_277_mic2 -p234_208_mic2 -p234_033_mic2 -p234_186_mic2 -p234_256_mic2 -p234_064_mic2 -p234_140_mic2 -p234_354_mic2 -p234_182_mic2 -p234_240_mic2 -p234_298_mic2 -p234_127_mic2 -p234_071_mic2 -p234_034_mic2 -p234_324_mic2 -p234_175_mic2 -p234_308_mic2 -p234_159_mic2 -p234_152_mic2 -p234_183_mic2 -p234_079_mic2 -p234_053_mic2 -p234_112_mic2 -p234_072_mic2 -p234_176_mic2 -p234_323_mic2 -p234_285_mic2 -p234_314_mic2 -p234_349_mic2 -p234_115_mic2 -p234_061_mic2 -p234_174_mic2 -p234_060_mic2 -p234_110_mic2 -p234_224_mic2 -p234_229_mic2 -p234_261_mic2 -p234_250_mic2 -p234_188_mic2 -p234_310_mic2 -p234_276_mic2 -p234_202_mic2 -p234_265_mic2 -p234_169_mic2 -p234_339_mic2 -p234_193_mic2 -p234_168_mic2 -p234_274_mic2 -p234_082_mic2 -p234_029_mic2 -p234_210_mic2 -p234_068_mic2 -p234_107_mic2 -p234_340_mic2 -p234_301_mic2 -p234_103_mic2 -p234_048_mic2 -p234_058_mic2 -p234_185_mic2 -p234_120_mic2 -p234_218_mic2 -p234_001_mic2 -p234_237_mic2 -p234_154_mic2 -p234_161_mic2 -p234_109_mic2 -p234_143_mic2 -p234_085_mic2 -p234_180_mic2 -p234_057_mic2 -p234_009_mic2 -p234_198_mic2 -p234_313_mic2 -p234_195_mic2 -p234_348_mic2 -p234_306_mic2 -p234_337_mic2 -p234_178_mic2 -p234_243_mic2 -p234_044_mic2 -p234_347_mic2 -p234_359_mic2 -p234_126_mic2 -p234_002_mic2 -p234_023_mic2 -p234_246_mic2 -p234_039_mic2 -p234_092_mic2 -p234_096_mic2 -p234_315_mic2 -p234_147_mic2 -p234_004_mic2 -p234_358_mic2 -p234_160_mic2 -p234_217_mic2 -p234_164_mic2 -p234_149_mic2 -p234_289_mic2 -p234_252_mic2 -p234_020_mic2 -p234_021_mic2 -p234_172_mic2 -p234_244_mic2 -p234_113_mic2 -p234_264_mic2 -p234_153_mic2 -p234_220_mic2 -p234_247_mic2 -p234_360_mic2 -p234_101_mic2 -p234_338_mic2 -p234_225_mic2 -p234_284_mic2 -p234_302_mic2 -p234_260_mic2 -p234_145_mic2 -p234_144_mic2 -p234_190_mic2 -p234_235_mic2 -p234_320_mic2 -p234_098_mic2 -p234_138_mic2 -p234_226_mic2 -p234_345_mic2 -p234_197_mic2 -p234_331_mic2 -p234_271_mic2 -p234_230_mic2 -p234_119_mic2 -p234_335_mic2 -p234_344_mic2 -p234_341_mic2 -p234_148_mic2 -p234_059_mic2 -p234_307_mic2 -p323_011_mic2 -p234_114_mic2 -p234_319_mic2 -p234_116_mic2 -p234_008_mic2 -p234_166_mic2 -p234_361_mic2 -p234_231_mic2 -p234_076_mic2 -p234_015_mic2 -p234_070_mic2 -p234_158_mic2 -p234_131_mic2 -p234_088_mic2 -p234_142_mic2 -p234_080_mic2 -p234_121_mic2 -p234_192_mic2 -p234_312_mic2 -p234_234_mic2 -p234_281_mic2 -p234_162_mic2 -p234_268_mic2 -p234_352_mic2 -p234_028_mic2 -p234_049_mic2 -p234_293_mic2 -p234_151_mic2 -p234_196_mic2 -p234_037_mic2 -p234_042_mic2 -p234_201_mic2 -p234_332_mic2 -p234_067_mic2 -p234_292_mic2 -p234_146_mic2 -p234_223_mic2 -p234_287_mic2 -p234_141_mic2 -p234_203_mic2 -p234_211_mic2 -p234_136_mic2 -p234_036_mic2 -p234_150_mic2 -p234_255_mic2 -p234_134_mic2 -p234_128_mic2 -p234_238_mic2 -p234_014_mic2 -p234_297_mic2 -p234_278_mic2 -p234_184_mic2 -p234_267_mic2 -p234_330_mic2 -p234_251_mic2 -p234_066_mic2 -p234_351_mic2 -p234_084_mic2 -p234_051_mic2 -p234_300_mic2 -p234_232_mic2 -p234_045_mic2 -p234_283_mic2 -p234_305_mic2 -p234_065_mic2 -p234_007_mic2 -p234_357_mic2 -p234_269_mic2 -p234_163_mic2 \ No newline at end of file diff --git a/talkingface/utils/valid.txt b/talkingface/utils/valid.txt deleted file mode 100644 index a52eb1c8..00000000 --- a/talkingface/utils/valid.txt +++ /dev/null @@ -1,10 +0,0 @@ -19_198_000000_000000 -19_198_000000_000002 -19_198_000004_000000 -19_198_000007_000000 -19_198_000010_000000 -19_198_000010_000003 -19_198_000012_000000 -19_198_000016_000000 -19_198_000020_000000 -19_198_000024_000005 \ No newline at end of file