From 59d9325c2c27fc095a120290ba97a442f93c042e Mon Sep 17 00:00:00 2001 From: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Date: Fri, 8 Sep 2023 16:13:28 +0400 Subject: [PATCH 001/112] Create pos_emb.rst Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister --- docs/source/nlp/pos_emb.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 docs/source/nlp/pos_emb.rst diff --git a/docs/source/nlp/pos_emb.rst b/docs/source/nlp/pos_emb.rst new file mode 100644 index 000000000000..9b0a5fc15f97 --- /dev/null +++ b/docs/source/nlp/pos_emb.rst @@ -0,0 +1,2 @@ +Supported positional embedding types +------------------------------------ From 35ba3ec7687e37e095a41e0b4419ad697e2198a5 Mon Sep 17 00:00:00 2001 From: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Date: Fri, 8 Sep 2023 18:15:05 +0400 Subject: [PATCH 002/112] Update pos_emb.rst Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister --- docs/source/nlp/pos_emb.rst | 80 +++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/docs/source/nlp/pos_emb.rst b/docs/source/nlp/pos_emb.rst index 9b0a5fc15f97..59b22c501772 100644 --- a/docs/source/nlp/pos_emb.rst +++ b/docs/source/nlp/pos_emb.rst @@ -1,2 +1,82 @@ Supported positional embedding types ------------------------------------ +.. list-table:: *Supported positional embeddings in GPT models* + :widths: 10 30 60 + :header-rows: 1 + + * - Parameter value + - How to use + - Description + + * - **learned_absolute** + - .. code:: + + model.position_embedding_type='learned_absolute' + - `Absolute Position Encodings `_ are position embeddings used in Transformer-based models, added to input embeddings in the encoder and decoder sections. These encodings match the dimension of embeddings and are created using sine and cosine functions of various frequencies. Each dimension in the encoding corresponds to a sinusoid with wavelengths forming a geometric progression. + + * - **rope** + - .. code:: + + model.position_embedding_type='rope' + model.rotary_percentage=1.0 + - `Rotary Position Embedding (RoPE) `_ incorporates positional information by utilizing a rotation matrix to encode the absolute positions of tokens while maintaining relative positional relationships in self-attention formulations by leveraging the geometric properties of vectors and complex numbers, applying a rotation based on a preset non-zero constant and the relative positions of the tokens to the word embeddings. + + * - **alibi** + - .. code:: + + model.position_embedding_type='alibi' + - `Attention with Linear Biases (ALiBi) `_ modifies the way attention scores are computed in the attention sublayer of the network. ALiBi introduces a static, non-learned bias after the query-key dot product during the computation of attention scores. This bias is added in the form of a head-specific slope that is determined before training, creating a geometric sequence of slopes for the different heads in the model. The method has an inductive bias towards recency, penalizing attention scores between distant query-key pairs with the penalty increasing as the distance grows, and it leverages different rates of penalty increase across different heads based on the slope magnitude. + + * - **kerple** + - .. code:: + + model.position_embedding_type='kerple' + - `Kernelized Relative Positional Embedding for Length Extrapolation (KERPLE) `_ generalizes relative positional embeddings (RPE) by kernelizing positional differences using conditionally positive definite (CPD) kernels known for generalizing distance metrics. They transform CPD kernels into positive definite (PD) kernels by adding a constant offset, which is absorbed during softmax normalization in the self-attention mechanism of transformers. This approach allows for a variety of RPEs that facilitate length extrapolation in a principled manner. + + * - **xpos** + - .. code:: + + model.position_embedding_type='xpos' + - `Extrapolatable Position Embedding (xPos) `_ + + * - **sandwich** + - .. code:: + + model.position_embedding_type='sandwich' + - `Sandwich `_ + +.. list-table:: *Supported positional embeddings in T5 models* + :widths: 10 30 60 + :header-rows: 1 + + * - Parameter value + - How to use + - Description + + * - **learned_absolute** + - .. code:: + + model.encoder.position_embedding_type='learned_absolute' + model.decoder.position_embedding_type='learned_absolute' + - `Absolute Position Encodings `_ + + * - **relative** + - .. code:: + + model.encoder.position_embedding_type='relative' + model.decoder.position_embedding_type='relative' + - `Relative Position Representations `_ + + * - **alibi** + - .. code:: + + model.encoder.position_embedding_type='alibi' + model.decoder.position_embedding_type='alibi' + - `Attention with Linear Biases (ALiBi) `_ modifies the way attention scores are computed in the attention sublayer of the network. ALiBi introduces a static, non-learned bias after the query-key dot product during the computation of attention scores. This bias is added in the form of a head-specific slope that is determined before training, creating a geometric sequence of slopes for the different heads in the model. The method has an inductive bias towards recency, penalizing attention scores between distant query-key pairs with the penalty increasing as the distance grows, and it leverages different rates of penalty increase across different heads based on the slope magnitude. + + * - **kerple** + - .. code:: + + model.encoder.position_embedding_type='kerple' + model.decoder.position_embedding_type='kerple' + - `Kernelized Relative Positional Embedding for Length Extrapolation (KERPLE) `_ generalizes relative positional embeddings (RPE) by kernelizing positional differences using conditionally positive definite (CPD) kernels known for generalizing distance metrics. They transform CPD kernels into positive definite (PD) kernels by adding a constant offset, which is absorbed during softmax normalization in the self-attention mechanism of transformers. This approach allows for a variety of RPEs that facilitate length extrapolation in a principled manner. From 2939667abc6432ae340852d700dcd6dcc9dab5ed Mon Sep 17 00:00:00 2001 From: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Date: Fri, 8 Sep 2023 18:23:35 +0400 Subject: [PATCH 003/112] Update pos_emb.rst Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister --- docs/source/nlp/pos_emb.rst | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/source/nlp/pos_emb.rst b/docs/source/nlp/pos_emb.rst index 59b22c501772..4df0285b8d9b 100644 --- a/docs/source/nlp/pos_emb.rst +++ b/docs/source/nlp/pos_emb.rst @@ -1,5 +1,11 @@ -Supported positional embedding types ------------------------------------- +Positional embedding +-------------------- + +Positional embeddings are used to give the model information about the position of each element in a sequence. Megatron LLM supports the following positional embedding types: + +GPT +^^^ + .. list-table:: *Supported positional embeddings in GPT models* :widths: 10 30 60 :header-rows: 1 @@ -45,6 +51,9 @@ Supported positional embedding types model.position_embedding_type='sandwich' - `Sandwich `_ +T5 +^^^ + .. list-table:: *Supported positional embeddings in T5 models* :widths: 10 30 60 :header-rows: 1 From 99a23504b3dd5dc53c7be4016e54bf0d61517413 Mon Sep 17 00:00:00 2001 From: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Date: Fri, 8 Sep 2023 18:24:16 +0400 Subject: [PATCH 004/112] Update pos_emb.rst Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister --- docs/source/nlp/pos_emb.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/nlp/pos_emb.rst b/docs/source/nlp/pos_emb.rst index 4df0285b8d9b..6b3a98da9bf8 100644 --- a/docs/source/nlp/pos_emb.rst +++ b/docs/source/nlp/pos_emb.rst @@ -67,7 +67,7 @@ T5 model.encoder.position_embedding_type='learned_absolute' model.decoder.position_embedding_type='learned_absolute' - - `Absolute Position Encodings `_ + - `Absolute Position Encodings `_ are position embeddings used in Transformer-based models, added to input embeddings in the encoder and decoder sections. These encodings match the dimension of embeddings and are created using sine and cosine functions of various frequencies. Each dimension in the encoding corresponds to a sinusoid with wavelengths forming a geometric progression. * - **relative** - .. code:: From 2e5af172150df709d7093f0e115ce60ae1767355 Mon Sep 17 00:00:00 2001 From: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Date: Fri, 8 Sep 2023 18:25:03 +0400 Subject: [PATCH 005/112] Update pos_emb.rst Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister --- docs/source/nlp/pos_emb.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/nlp/pos_emb.rst b/docs/source/nlp/pos_emb.rst index 6b3a98da9bf8..37c53f6b2ca9 100644 --- a/docs/source/nlp/pos_emb.rst +++ b/docs/source/nlp/pos_emb.rst @@ -1,5 +1,5 @@ -Positional embedding --------------------- +Positional embeddings +--------------------- Positional embeddings are used to give the model information about the position of each element in a sequence. Megatron LLM supports the following positional embedding types: From 5314d2dbe8d30e323154785d55dfa8481b34e630 Mon Sep 17 00:00:00 2001 From: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Date: Fri, 8 Sep 2023 19:39:24 +0400 Subject: [PATCH 006/112] Update pos_emb.rst Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister --- docs/source/nlp/pos_emb.rst | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/docs/source/nlp/pos_emb.rst b/docs/source/nlp/pos_emb.rst index 37c53f6b2ca9..c491f861fa99 100644 --- a/docs/source/nlp/pos_emb.rst +++ b/docs/source/nlp/pos_emb.rst @@ -52,7 +52,7 @@ GPT - `Sandwich `_ T5 -^^^ +^^ .. list-table:: *Supported positional embeddings in T5 models* :widths: 10 30 60 @@ -89,3 +89,33 @@ T5 model.encoder.position_embedding_type='kerple' model.decoder.position_embedding_type='kerple' - `Kernelized Relative Positional Embedding for Length Extrapolation (KERPLE) `_ generalizes relative positional embeddings (RPE) by kernelizing positional differences using conditionally positive definite (CPD) kernels known for generalizing distance metrics. They transform CPD kernels into positive definite (PD) kernels by adding a constant offset, which is absorbed during softmax normalization in the self-attention mechanism of transformers. This approach allows for a variety of RPEs that facilitate length extrapolation in a principled manner. + +Positional interpolation +------------------------ +`Position Interpolation (PI) `_ is a method introduced to extend the context window sizes of Rotary Position Embedding (RoPE)-based pretrained large language models (LLMs). The central principle of PI is to reduce the position indices so that they align with the initial context window size through interpolation. + +Positional Interpolation is supported in Megatron GPT SFT models. Set RoPE Interpolation factor for sequence length :code:`seq_len_interpolation_factor` to enable it. + +.. code:: + + model.position_embedding_type='rope' + model.rotary_percentage=1.0 + model.seq_len_interpolation_factor: null + +Flash attention +--------------- +`FlashAttention `_ is a method designed to enhance the efficiency of Transformer models, which are widely utilized in applications such as natural language processing. Traditional Transformers are slow and consume a lot of memory, especially with long sequences, due to the quadratic time and memory complexity of self-attention. FlashAttention, an IO-aware exact attention algorithm that leverages tiling to minimize the number of memory reads/writes between the GPU's high bandwidth memory (HBM) and on-chip SRAM. This approach is designed to be more efficient in terms of IO complexity compared to standard attention mechanisms. + +GPT +^^^ +To enable Flash Attention while Megatron GPT model training or fine-tuning, modify the following configuration: +.. code:: + + model.use_flash_attention=True + +T5 +^^ +To enable Flash Attention while Megatron T5 model training, modify the following configuration: +.. code:: + + model.use_flash_attention=True From 36de4c34b765fc3faea1678a1a328510d0494b26 Mon Sep 17 00:00:00 2001 From: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Date: Fri, 8 Sep 2023 19:40:40 +0400 Subject: [PATCH 007/112] Update pos_emb.rst Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister --- docs/source/nlp/pos_emb.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/nlp/pos_emb.rst b/docs/source/nlp/pos_emb.rst index c491f861fa99..99c19af91073 100644 --- a/docs/source/nlp/pos_emb.rst +++ b/docs/source/nlp/pos_emb.rst @@ -109,6 +109,7 @@ Flash attention GPT ^^^ To enable Flash Attention while Megatron GPT model training or fine-tuning, modify the following configuration: + .. code:: model.use_flash_attention=True @@ -116,6 +117,7 @@ To enable Flash Attention while Megatron GPT model training or fine-tuning, modi T5 ^^ To enable Flash Attention while Megatron T5 model training, modify the following configuration: + .. code:: model.use_flash_attention=True From 28d5b0d8e8aa4e101906d3b1f2046114c3177956 Mon Sep 17 00:00:00 2001 From: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Date: Fri, 8 Sep 2023 19:52:54 +0400 Subject: [PATCH 008/112] Update and rename docs/source/nlp/pos_emb.rst to docs/source/nlp/nemo_megatron /positional_embeddings.rst Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../positional_embeddings.rst} | 20 ------------------- 1 file changed, 20 deletions(-) rename docs/source/nlp/{pos_emb.rst => nemo_megatron /positional_embeddings.rst} (86%) diff --git a/docs/source/nlp/pos_emb.rst b/docs/source/nlp/nemo_megatron /positional_embeddings.rst similarity index 86% rename from docs/source/nlp/pos_emb.rst rename to docs/source/nlp/nemo_megatron /positional_embeddings.rst index 99c19af91073..84a62f81c77d 100644 --- a/docs/source/nlp/pos_emb.rst +++ b/docs/source/nlp/nemo_megatron /positional_embeddings.rst @@ -101,23 +101,3 @@ Positional Interpolation is supported in Megatron GPT SFT models. Set RoPE Inter model.position_embedding_type='rope' model.rotary_percentage=1.0 model.seq_len_interpolation_factor: null - -Flash attention ---------------- -`FlashAttention `_ is a method designed to enhance the efficiency of Transformer models, which are widely utilized in applications such as natural language processing. Traditional Transformers are slow and consume a lot of memory, especially with long sequences, due to the quadratic time and memory complexity of self-attention. FlashAttention, an IO-aware exact attention algorithm that leverages tiling to minimize the number of memory reads/writes between the GPU's high bandwidth memory (HBM) and on-chip SRAM. This approach is designed to be more efficient in terms of IO complexity compared to standard attention mechanisms. - -GPT -^^^ -To enable Flash Attention while Megatron GPT model training or fine-tuning, modify the following configuration: - -.. code:: - - model.use_flash_attention=True - -T5 -^^ -To enable Flash Attention while Megatron T5 model training, modify the following configuration: - -.. code:: - - model.use_flash_attention=True From 85f4e639da4f5c24508a19843d5c149c0b5a9ee8 Mon Sep 17 00:00:00 2001 From: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Date: Fri, 8 Sep 2023 19:56:43 +0400 Subject: [PATCH 009/112] Rename positional_embeddings.rst to positional_embeddings.rst Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../{nemo_megatron => nemo_megatron}/positional_embeddings.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/source/nlp/{nemo_megatron => nemo_megatron}/positional_embeddings.rst (100%) diff --git a/docs/source/nlp/nemo_megatron /positional_embeddings.rst b/docs/source/nlp/nemo_megatron/positional_embeddings.rst similarity index 100% rename from docs/source/nlp/nemo_megatron /positional_embeddings.rst rename to docs/source/nlp/nemo_megatron/positional_embeddings.rst From ce6113d1325f0a7b4523e8b4ab60f4cafc578d20 Mon Sep 17 00:00:00 2001 From: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Date: Fri, 8 Sep 2023 19:57:34 +0400 Subject: [PATCH 010/112] Create flash_attention.rst Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../nlp/nemo_megatron/flash_attention.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 docs/source/nlp/nemo_megatron/flash_attention.rst diff --git a/docs/source/nlp/nemo_megatron/flash_attention.rst b/docs/source/nlp/nemo_megatron/flash_attention.rst new file mode 100644 index 000000000000..fd0d519e577c --- /dev/null +++ b/docs/source/nlp/nemo_megatron/flash_attention.rst @@ -0,0 +1,19 @@ +Flash attention +--------------- +`FlashAttention `_ is a method designed to enhance the efficiency of Transformer models, which are widely utilized in applications such as natural language processing. Traditional Transformers are slow and consume a lot of memory, especially with long sequences, due to the quadratic time and memory complexity of self-attention. FlashAttention, an IO-aware exact attention algorithm that leverages tiling to minimize the number of memory reads/writes between the GPU's high bandwidth memory (HBM) and on-chip SRAM. This approach is designed to be more efficient in terms of IO complexity compared to standard attention mechanisms. + +GPT +^^^ +To enable Flash Attention while Megatron GPT model training or fine-tuning, modify the following configuration: + +.. code:: + + model.use_flash_attention=True + +T5 +^^ +To enable Flash Attention while Megatron T5 model training, modify the following configuration: + +.. code:: + + model.use_flash_attention=True From 2e03e6cf5795ad4ac8288be81583e9209ef1f477 Mon Sep 17 00:00:00 2001 From: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:30:57 +0400 Subject: [PATCH 011/112] Changed value for model.seq_len_interpolation_factor to 2 Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister --- docs/source/nlp/nemo_megatron/positional_embeddings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/nlp/nemo_megatron/positional_embeddings.rst b/docs/source/nlp/nemo_megatron/positional_embeddings.rst index 84a62f81c77d..68dce5bd119a 100644 --- a/docs/source/nlp/nemo_megatron/positional_embeddings.rst +++ b/docs/source/nlp/nemo_megatron/positional_embeddings.rst @@ -100,4 +100,4 @@ Positional Interpolation is supported in Megatron GPT SFT models. Set RoPE Inter model.position_embedding_type='rope' model.rotary_percentage=1.0 - model.seq_len_interpolation_factor: null + model.seq_len_interpolation_factor: 2 From b27d077f0a38e67498c287a8da19c7e1da9e5b05 Mon Sep 17 00:00:00 2001 From: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:57:26 +0400 Subject: [PATCH 012/112] fixed flash_attention enabling for t5 Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister --- docs/source/nlp/nemo_megatron/flash_attention.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/nlp/nemo_megatron/flash_attention.rst b/docs/source/nlp/nemo_megatron/flash_attention.rst index fd0d519e577c..78b3b9c2470c 100644 --- a/docs/source/nlp/nemo_megatron/flash_attention.rst +++ b/docs/source/nlp/nemo_megatron/flash_attention.rst @@ -16,4 +16,5 @@ To enable Flash Attention while Megatron T5 model training, modify the following .. code:: - model.use_flash_attention=True + model.encoder.use_flash_attention=True + model.decoder.use_flash_attention=True From 252123da993d15a2bb8f893114a7fce01f56ee11 Mon Sep 17 00:00:00 2001 From: anteju <108555623+anteju@users.noreply.github.com> Date: Fri, 8 Sep 2023 16:30:25 -0700 Subject: [PATCH 013/112] [TTS] Added a callback for logging initial data (#7384) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ante Jukić Signed-off-by: Sasha Meister --- nemo/collections/tts/parts/utils/callbacks.py | 111 +++++++++++++++--- 1 file changed, 95 insertions(+), 16 deletions(-) diff --git a/nemo/collections/tts/parts/utils/callbacks.py b/nemo/collections/tts/parts/utils/callbacks.py index 304efad3f5f7..c1be48bdaa3d 100644 --- a/nemo/collections/tts/parts/utils/callbacks.py +++ b/nemo/collections/tts/parts/utils/callbacks.py @@ -110,7 +110,7 @@ def create_id(filepath: Path) -> str: class ArtifactGenerator(ABC): @abstractmethod def generate_artifacts( - self, model: LightningModule, batch_dict: Dict + self, model: LightningModule, batch_dict: Dict, initial_log: bool = False ) -> Tuple[List[AudioArtifact], List[ImageArtifact]]: """ Create artifacts for the input model and test batch. @@ -118,6 +118,8 @@ def generate_artifacts( Args: model: Model instance being trained to use for inference. batch_dict: Test batch to generate artifacts for. + initial_log: Flag to denote if this is the initial log, can + be used to save ground-truth data only once. Returns: List of audio and image artifacts to log. @@ -214,17 +216,48 @@ def _log_image(self, image: ImageArtifact, log_dir: Path, step: int): wandb_image = (wandb.Image(image_plot, caption=image.id),) self.wandb_logger.log({image.id: wandb_image}) + def _log_artifacts(self, audio_list: list, image_list: list, log_dir: Optional[Path] = None, global_step: int = 0): + """Log audio and image artifacts. + """ + if log_dir is not None: + log_dir.mkdir(parents=True, exist_ok=True) + + for audio in audio_list: + self._log_audio(audio=audio, log_dir=log_dir, step=global_step) + + for image in image_list: + self._log_image(image=image, log_dir=log_dir, step=global_step) + + def on_fit_start(self, trainer: Trainer, model: LightningModule): + """Log initial data artifacts. + """ + audio_list = [] + image_list = [] + for batch_dict in self.data_loader: + for key, value in batch_dict.items(): + if isinstance(value, torch.Tensor): + batch_dict[key] = value.to(model.device) + + for generator in self.generators: + audio, images = generator.generate_artifacts(model=model, batch_dict=batch_dict, initial_log=True) + audio_list += audio + image_list += images + + if len(audio_list) == len(image_list) == 0: + logging.debug('List are empty, no initial artifacts to log.') + return + + log_dir = self.output_dir / f"initial" if self.output_dir else None + + self._log_artifacts(audio_list=audio_list, image_list=image_list, log_dir=log_dir) + def on_train_epoch_end(self, trainer: Trainer, model: LightningModule): + """Log artifacts at the end of an epoch. + """ epoch = 1 + model.current_epoch if (epoch not in self.log_epochs) and (epoch % self.epoch_frequency != 0): return - if self.output_dir: - log_dir = self.output_dir / f"epoch_{epoch}" - log_dir.mkdir(parents=True, exist_ok=True) - else: - log_dir = None - audio_list = [] image_list = [] for batch_dict in self.data_loader: @@ -237,11 +270,13 @@ def on_train_epoch_end(self, trainer: Trainer, model: LightningModule): audio_list += audio image_list += images - for audio in audio_list: - self._log_audio(audio=audio, log_dir=log_dir, step=model.global_step) + if len(audio_list) == len(image_list) == 0: + logging.debug('List are empty, no artifacts to log at epoch %d.', epoch) + return - for image in image_list: - self._log_image(image=image, log_dir=log_dir, step=model.global_step) + log_dir = self.output_dir / f"epoch_{epoch}" if self.output_dir else None + + self._log_artifacts(audio_list=audio_list, image_list=image_list, log_dir=log_dir) class VocoderArtifactGenerator(ArtifactGenerator): @@ -250,8 +285,11 @@ class VocoderArtifactGenerator(ArtifactGenerator): """ def generate_artifacts( - self, model: LightningModule, batch_dict: Dict + self, model: LightningModule, batch_dict: Dict, initial_log: bool = False ) -> Tuple[List[AudioArtifact], List[ImageArtifact]]: + if initial_log: + # Currently, nothing to log before training starts + return [], [] audio_artifacts = [] @@ -297,7 +335,16 @@ def __init__(self, log_audio: bool = True, log_encoding: bool = False, log_dequa logging.debug('\tlog_encoding: %s', self.log_encoding) logging.debug('\tlog_dequantized: %s', self.log_dequantized) - def _generate_audio(self, model, audio_ids, audio, audio_len): + def _generate_audio(self, model, audio_ids, audio, audio_len, save_input: bool = False): + """Generate audio artifacts. + + Args: + model: callable model, outputs (audio_pred, audio_pred_len) + audio_ids: list of IDs for the examples in audio batch + audio: tensor of input audio signals, shape (B, T) + audio_len: tensor of lengths for each example in the batch, shape (B,) + save_input: if True, save input audio signals + """ if not self.log_audio: return [] @@ -317,9 +364,29 @@ def _generate_audio(self, model, audio_ids, audio, audio_len): ) audio_artifacts.append(audio_artifact) + if save_input: + # save input audio + for i, audio_id in enumerate(audio_ids): + audio_in_i = audio[i, : audio_len[i]].cpu().numpy() + audio_artifact = AudioArtifact( + id=f"audio_in_{audio_id}", + data=audio_in_i, + filename=f"{audio_id}_audio_in.wav", + sample_rate=model.sample_rate, + ) + audio_artifacts.append(audio_artifact) + return audio_artifacts def _generate_images(self, model, audio_ids, audio, audio_len): + """Generate image artifacts. + + Args: + model: model, needs to support `model.encode_audio`, `model.quantize` and `model.dequantize` + audio_ids: list of IDs for the examples in audio batch + audio: tensor of input audio signals, shape (B, T) + audio_len: tensor of lengths for each example in the batch, shape (B,) + """ image_artifacts = [] if not self.log_encoding and not self.log_dequantized: @@ -363,8 +430,14 @@ def _generate_images(self, model, audio_ids, audio, audio_len): return image_artifacts def generate_artifacts( - self, model: LightningModule, batch_dict: Dict + self, model: LightningModule, batch_dict: Dict, initial_log: bool = False ) -> Tuple[List[AudioArtifact], List[ImageArtifact]]: + """ + Args: + model: model used to process input to generate artifacts + batch_dict: dictionary obtained form the dataloader + initial_log: save input audio for the initial log + """ audio_filepaths = batch_dict.get("audio_filepaths") audio_ids = [create_id(p) for p in audio_filepaths] @@ -372,7 +445,9 @@ def generate_artifacts( audio = batch_dict.get("audio") audio_len = batch_dict.get("audio_lens") - audio_artifacts = self._generate_audio(model=model, audio_ids=audio_ids, audio=audio, audio_len=audio_len) + audio_artifacts = self._generate_audio( + model=model, audio_ids=audio_ids, audio=audio, audio_len=audio_len, save_input=initial_log + ) image_artifacts = self._generate_images(model=model, audio_ids=audio_ids, audio=audio, audio_len=audio_len) return audio_artifacts, image_artifacts @@ -518,9 +593,13 @@ def _generate_gta_predictions(self, model: LightningModule, audio_ids: List[str] return audio_artifacts, image_artifacts def generate_artifacts( - self, model: LightningModule, batch_dict: Dict + self, model: LightningModule, batch_dict: Dict, initial_log: bool = False ) -> Tuple[List[AudioArtifact], List[ImageArtifact]]: + if initial_log: + # Currently, nothing to log before training starts + return [], [] + audio_artifacts = [] image_artifacts = [] audio_filepaths = batch_dict.get("audio_filepaths") From 5341a7398de11c4339bc466b9c1f6a6f835e2409 Mon Sep 17 00:00:00 2001 From: Abhinav Khattar Date: Fri, 8 Sep 2023 17:22:05 -0700 Subject: [PATCH 014/112] Update Core Commit (#7402) * Update Core Commit Signed-off-by: Abhinav Khattar * update commit Signed-off-by: Abhinav Khattar --------- Signed-off-by: Abhinav Khattar Signed-off-by: Sasha Meister --- Dockerfile | 2 +- Jenkinsfile | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index e935d3bd810c..e4f04359e6eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,7 +55,7 @@ RUN git clone https://github.com/NVIDIA/apex.git && \ # install megatron core, this can be removed once 0.3 pip package is released RUN git clone https://github.com/NVIDIA/Megatron-LM.git && \ cd Megatron-LM && \ - git checkout 01c8704453af7e26134441224c8a351746ca0349 && \ + git checkout ab0336a5c8eab77aa74ae604ba1e73decbf6d560 && \ pip install -e . # uninstall stuff from base container diff --git a/Jenkinsfile b/Jenkinsfile index 45910b740655..04bc96ff1596 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -59,11 +59,11 @@ pipeline { stage('Megatron Core installation') { steps { - // pinned MCore https://github.com/NVIDIA/Megatron-LM/commit/01c8704453af7e26134441224c8a351746ca0349 + // pinned MCore https://github.com/NVIDIA/Megatron-LM/commit/ab0336a5c8eab77aa74ae604ba1e73decbf6d560 // ToT for 23.08 branch sh 'git clone https://github.com/NVIDIA/Megatron-LM.git && \ cd Megatron-LM && \ - git checkout 01c8704453af7e26134441224c8a351746ca0349 && \ + git checkout ab0336a5c8eab77aa74ae604ba1e73decbf6d560 && \ pip install -e .' } } From 68142d6716fd1a41581a31a5e1212ec003356b59 Mon Sep 17 00:00:00 2001 From: Maanu Grover <109391026+maanug-nv@users.noreply.github.com> Date: Fri, 8 Sep 2023 22:43:35 -0500 Subject: [PATCH 015/112] Use cfg attribute in bert (#7394) * use cfg attribute instead of arg Signed-off-by: Maanu Grover * use torch_dtype in place of cfg.precision Signed-off-by: Maanu Grover * move precision copy before super constructor Signed-off-by: Maanu Grover * use trainer arg Signed-off-by: Maanu Grover --------- Signed-off-by: Maanu Grover Signed-off-by: Sasha Meister --- .../nlp/models/language_modeling/megatron_base_model.py | 6 ++++-- .../nlp/models/language_modeling/megatron_bert_model.py | 6 +++--- .../models/language_modeling/megatron_gpt_adapter_model.py | 2 +- .../nlp/models/language_modeling/megatron_gpt_model.py | 2 +- .../language_modeling/megatron_gpt_prompt_learning_model.py | 2 +- .../models/language_modeling/megatron_retrieval_model.py | 2 +- .../language_modeling/megatron_t5_prompt_learning_model.py | 2 +- 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_base_model.py b/nemo/collections/nlp/models/language_modeling/megatron_base_model.py index a7b1b9521e3c..29b1886c957a 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_base_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_base_model.py @@ -102,10 +102,12 @@ def __init__(self, cfg: DictConfig, trainer: Trainer, no_lm_init=True): # this prevents base constructor from initializing tokenizer self.tokenizer = None + with open_dict(cfg): + if cfg.get('precision', None) is None and trainer is not None: + cfg.precision = trainer.precision + super().__init__(cfg, trainer=trainer, no_lm_init=no_lm_init) - with open_dict(self.cfg): - self.cfg.precision = trainer.precision # TODO: @maanug-nv consolidate into one attribute (requires lots of changes in subclasses) self.torch_dtype = utils_funcs.torch_dtype_from_precision(self.cfg.precision) # Mixed precision datatype self.autocast_dtype = self.torch_dtype # Mixed precision datatype diff --git a/nemo/collections/nlp/models/language_modeling/megatron_bert_model.py b/nemo/collections/nlp/models/language_modeling/megatron_bert_model.py index 73548fda3348..083b6391bf0e 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_bert_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_bert_model.py @@ -124,12 +124,12 @@ def __init__(self, cfg: DictConfig, trainer: Trainer): converted_model = [] for module in self.model: converted_model.append( - Float16Module(config=self.model_parallel_config, module=module, precision=cfg.precision) + Float16Module(config=self.model_parallel_config, module=module, precision=self.cfg.precision) ) self.model = converted_model else: self.model = Float16Module( - config=self.model_parallel_config, module=self.model, precision=cfg.precision + config=self.model_parallel_config, module=self.model, precision=self.cfg.precision ) if hasattr(self, '_nsys_profile_enabled'): @@ -358,7 +358,7 @@ def training_step(self, dataloader_iter, batch_idx): torch.distributed.broadcast(loss_mean, get_last_rank()) - if self.cfg.precision == 16: + if self.torch_dtype == torch.float16: loss_scale = self.trainer.precision_plugin.scaler._scale if loss_scale is not None: self.log('loss_scale', loss_scale, batch_size=1) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_adapter_model.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_adapter_model.py index 2985ab4df3bb..c6b4d055ef6e 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_adapter_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_adapter_model.py @@ -223,7 +223,7 @@ def training_step(self, dataloader_iter, batch_idx): # we can avoid this broadcast by updating the PTL log function to accept specific ranks torch.distributed.broadcast(loss_mean, get_last_rank()) - if self.cfg.precision == 16: + if self.torch_dtype == torch.float16: loss_scale = self.trainer.precision_plugin.scaler._scale if loss_scale is not None: self.log('loss_scale', loss_scale, batch_size=1) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py index 1d0e08abe6dd..1d2d68b9b6f5 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py @@ -625,7 +625,7 @@ def training_step(self, dataloader_iter, batch_idx): torch.distributed.broadcast(loss_mean, get_last_rank()) # (@adithyare) we need to check for the _scaler attribute to enable pp>1 for adapter training - if self.cfg.precision == 16 and hasattr(self.trainer.precision_plugin.scaler, "_scale"): + if self.torch_dtype == torch.float16 and hasattr(self.trainer.precision_plugin.scaler, "_scale"): loss_scale = self.trainer.precision_plugin.scaler._scale if loss_scale is not None: self.log('loss_scale', loss_scale, batch_size=1) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_prompt_learning_model.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_prompt_learning_model.py index d0dca0173319..688d3a49159c 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_prompt_learning_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_prompt_learning_model.py @@ -349,7 +349,7 @@ def training_step(self, dataloader_iter, batch_idx): # we can avoid this broadcast by updating the PTL log function to accept specific ranks torch.distributed.broadcast(loss_mean, get_last_rank()) - if self.cfg.precision == 16 and hasattr(self.trainer.precision_plugin.scaler, "_scale"): + if self.torch_dtype == torch.float16 and hasattr(self.trainer.precision_plugin.scaler, "_scale"): loss_scale = self.trainer.precision_plugin.scaler._scale if loss_scale is not None: self.log('loss_scale', loss_scale, batch_size=1) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_retrieval_model.py b/nemo/collections/nlp/models/language_modeling/megatron_retrieval_model.py index 4323d65a977e..e418151308b0 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_retrieval_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_retrieval_model.py @@ -262,7 +262,7 @@ def training_step(self, batch, batch_idx): reduced_loss = average_losses_across_data_parallel_group([lm_loss]) self._reduced_loss_buffer.append(reduced_loss[0]) - if self.cfg.precision == 16: + if self.torch_dtype == torch.float16: loss_scale = self.trainer.precision_plugin.scaler._scale if loss_scale is not None: self.log('loss_scale', loss_scale, batch_size=1) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_t5_prompt_learning_model.py b/nemo/collections/nlp/models/language_modeling/megatron_t5_prompt_learning_model.py index f085e38573af..f1a13c2abee4 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_t5_prompt_learning_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_t5_prompt_learning_model.py @@ -274,7 +274,7 @@ def training_step(self, dataloader_iter, batch_idx): # we can avoid this broadcast by updating the PTL log function to accept specific ranks torch.distributed.broadcast(loss_mean, get_last_rank()) - if self.cfg.precision == 16 and hasattr(self.trainer.precision_plugin.scaler, "_scale"): + if self.torch_dtype == torch.float16 and hasattr(self.trainer.precision_plugin.scaler, "_scale"): loss_scale = self.trainer.precision_plugin.scaler._scale if loss_scale is not None: self.log('loss_scale', loss_scale, batch_size=1) From 2a60fb0fc924e6739b4a262d166acb4e80a403b3 Mon Sep 17 00:00:00 2001 From: Somshubra Majumdar Date: Fri, 8 Sep 2023 20:44:17 -0700 Subject: [PATCH 016/112] Add support for bias conversion in Swiglu models (#7386) * Add support for bias conversion in Swiglu models Signed-off-by: smajumdar * Add support for auto extracting tokenizer model Signed-off-by: smajumdar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add support for auto extracting tokenizer model Signed-off-by: smajumdar * Fix issue with missing tokenizer Signed-off-by: smajumdar * Refactor Signed-off-by: smajumdar * Refactor Signed-off-by: smajumdar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: smajumdar Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../megatron_change_num_partitions.py | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/examples/nlp/language_modeling/megatron_change_num_partitions.py b/examples/nlp/language_modeling/megatron_change_num_partitions.py index 1f2195ad1b2b..f7ada6ab762f 100644 --- a/examples/nlp/language_modeling/megatron_change_num_partitions.py +++ b/examples/nlp/language_modeling/megatron_change_num_partitions.py @@ -13,6 +13,8 @@ # limitations under the License. import os +import shutil +import tarfile import tempfile from argparse import ArgumentParser from typing import Dict, List @@ -233,7 +235,7 @@ def compute_tp_splits( for i in range(tp_size): tp_qkv = torch.cat([tp_qkv_splits[item] for item in range(i, tp_size * 2, tp_size)]) split.append(tp_qkv) - elif ('dense_h_to_4h.weight' in param_name or 'linear_fc1.weight' in param_name) and fast_glu_activation: + elif ('dense_h_to_4h' in param_name or 'linear_fc1' in param_name) and fast_glu_activation: # For Megatron GPT model with Fast Glu activation # Handle gated linear units # concat all the first halves ('W's) and all the second halves ('V's) @@ -275,7 +277,7 @@ def compute_tp_merge(idx, name, param, partitions_pp, model_cfg): concated = torch.cat([partitions_pp[i][idx].data for i in range(len(partitions_pp))], dim=0) # Logic for Fast Glu activation - if 'dense_h_to_4h.weight' in name and fast_glu_activation: + if 'dense_h_to_4h' in name and fast_glu_activation: # concat all the first halves ('W's) and all the second halves ('V's) wk_splits = [] for tpr in range(len(partitions_pp)): @@ -977,6 +979,31 @@ def main(): if vp_size > 1: set_virtual_parallel_rank_safely(0) + # Extract tokenizer artifact from the model to temp directory + logging.info("Extracting tokenizer artifact from NeMo file...") + temp_dir = tempfile.mkdtemp() + tokenizer_model_path = None + with tarfile.open(args.model_file, "r") as tar: + for member in tar.getmembers(): + if '.model' in member.name: + extracted_file = tar.extractfile(member) + extracted_file_path = os.path.join(temp_dir, member.name) + + if tokenizer_model_path is None: + logging.info(f"Found tokenizer. Extracting {member.name} to {extracted_file_path}") + + tokenizer_model_path = extracted_file_path + with open(extracted_file_path, "wb") as f: + f.write(extracted_file.read()) + else: + if args.tokenizer_model_path is None: + logging.warning( + f"\n\nFound multiple tokenizer artifacts in the model file.\n" + f"Using only {tokenizer_model_path}.\n" + f"If this is incorrect, manually pass the correct tokenizer using " + f"`--tokenizer_model_path`.\n\n" + ) + # If input model has TP > 1 or PP > 1 # Reconstruct the model to have TP = 1 and PP = 1 # Note that this is a forward loop that will process PP [0..N] TP [0..M] in sequential order. @@ -1384,6 +1411,15 @@ def main(): with open_dict(model.cfg): model.cfg.tokenizer.model = args.tokenizer_model_path + else: + if tokenizer_model_path is None: + logging.warning("Could not extract tokenizer model file from checkpoint.") + + else: + # Extract tokenizer info + with open_dict(model.cfg): + model.cfg.tokenizer.model = tokenizer_model_path + model.cfg, restore_dict = force_cpu_model(model.cfg) model = cls(model.cfg, trainer) @@ -1458,6 +1494,9 @@ def main(): logging.info("Successfully finished changing partitions!") + if temp_dir is not None: + shutil.rmtree(temp_dir, ignore_errors=True) + if __name__ == '__main__': main() From 8b5ef4175af7b9cbc5582845b0a0da408bddb44c Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Fri, 8 Sep 2023 22:25:35 -0600 Subject: [PATCH 017/112] Update save_to and restore_from for dist checkpointing (#7343) * add dist ckpt to save to, in progress Signed-off-by: eharper * move dist ckpt Signed-off-by: eharper * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * clean up Signed-off-by: eharper * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update restore from, need to figure out how to initialize distributed Signed-off-by: eharper * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * launch distrib if needed when restoring dist ckpt Signed-off-by: eharper * when using mcore we can change tp pp on the fly Signed-off-by: eharper * add load_from_checkpoint support for dist ckpt Signed-off-by: eharper * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update llama convert script to save dist .nemo Signed-off-by: eharper * fix load dist ckpt Signed-off-by: jasonwan * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * setup TE TP groups if needed Signed-off-by: eharper * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * setup te tp groups if needed Signed-off-by: eharper * remove import Signed-off-by: eharper --------- Signed-off-by: eharper Signed-off-by: jasonwan Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: jasonwan Signed-off-by: Sasha Meister --- .../conf/megatron_gpt_inference.yaml | 2 + .../conf/megatron_llama_inference.yaml | 1 + .../language_modeling/megatron_gpt_eval.py | 49 +++-- .../language_modeling/megatron_gpt_model.py | 27 ++- nemo/collections/nlp/models/nlp_model.py | 45 +++- nemo/collections/nlp/parts/nlp_overrides.py | 196 +++++++++++++----- nemo/core/optim/distributed_adam.py | 6 +- .../convert_hf_llama_to_nemo.py | 3 +- 8 files changed, 238 insertions(+), 91 deletions(-) diff --git a/examples/nlp/language_modeling/conf/megatron_gpt_inference.yaml b/examples/nlp/language_modeling/conf/megatron_gpt_inference.yaml index f2912e27fccf..2570251bcdee 100644 --- a/examples/nlp/language_modeling/conf/megatron_gpt_inference.yaml +++ b/examples/nlp/language_modeling/conf/megatron_gpt_inference.yaml @@ -17,6 +17,8 @@ trainer: accelerator: gpu logger: False # logger provided by exp_manager precision: 16 # 16, 32, or bf16 + use_distributed_sampler: False + tensor_model_parallel_size: -1 pipeline_model_parallel_size: -1 diff --git a/examples/nlp/language_modeling/conf/megatron_llama_inference.yaml b/examples/nlp/language_modeling/conf/megatron_llama_inference.yaml index 9e82a7f8a4e7..e508b01858f5 100644 --- a/examples/nlp/language_modeling/conf/megatron_llama_inference.yaml +++ b/examples/nlp/language_modeling/conf/megatron_llama_inference.yaml @@ -17,6 +17,7 @@ trainer: accelerator: gpu logger: False # logger provided by exp_manager precision: 32 # 16, 32, or bf16 + use_distributed_sampler: False tensor_model_parallel_size: -1 pipeline_model_parallel_size: -1 diff --git a/examples/nlp/language_modeling/megatron_gpt_eval.py b/examples/nlp/language_modeling/megatron_gpt_eval.py index bc1a446226be..cbac3ab29ce2 100644 --- a/examples/nlp/language_modeling/megatron_gpt_eval.py +++ b/examples/nlp/language_modeling/megatron_gpt_eval.py @@ -169,25 +169,28 @@ def main(cfg) -> None: # trainer required for restoring model parallel models trainer = Trainer(strategy=NLPDDPStrategy(), **cfg.trainer) - if ( - cfg.tensor_model_parallel_size < 0 - or cfg.pipeline_model_parallel_size < 0 - or cfg.get('pipeline_model_parallel_split_rank', -1) < 0 - ): - save_restore_connector = NLPSaveRestoreConnector() - if os.path.isdir(cfg.gpt_model_file): - save_restore_connector.model_extracted_dir = cfg.gpt_model_file - model_config = MegatronGPTModel.restore_from( - restore_path=cfg.gpt_model_file, - trainer=trainer, - return_config=True, - save_restore_connector=save_restore_connector, - ) + if cfg.gpt_model_file is not None: + if ( + cfg.tensor_model_parallel_size < 0 + or cfg.pipeline_model_parallel_size < 0 + or cfg.get('pipeline_model_parallel_split_rank', -1) < 0 + ): + save_restore_connector = NLPSaveRestoreConnector() + if os.path.isdir(cfg.gpt_model_file): + save_restore_connector.model_extracted_dir = cfg.gpt_model_file + model_config = MegatronGPTModel.restore_from( + restore_path=cfg.gpt_model_file, + trainer=trainer, + return_config=True, + save_restore_connector=save_restore_connector, + ) - with open_dict(cfg): - cfg.tensor_model_parallel_size = model_config.get('tensor_model_parallel_size', 1) - cfg.pipeline_model_parallel_size = model_config.get('pipeline_model_parallel_size', 1) - cfg.pipeline_model_parallel_split_rank = model_config.get('pipeline_model_parallel_split_rank', 0) + # with dist checkpointing we don't need to set this + if not model_config.get('mcore_gpt', False): + with open_dict(cfg): + cfg.tensor_model_parallel_size = model_config.get('tensor_model_parallel_size', 1) + cfg.pipeline_model_parallel_size = model_config.get('pipeline_model_parallel_size', 1) + cfg.pipeline_model_parallel_split_rank = model_config.get('pipeline_model_parallel_split_rank', 0) assert ( cfg.trainer.devices * cfg.trainer.num_nodes @@ -211,6 +214,10 @@ def main(cfg) -> None: pretrained_cfg.activations_checkpoint_granularity = None pretrained_cfg.activations_checkpoint_method = None pretrained_cfg.precision = trainer.precision + if pretrained_cfg.get('mcore_gpt', False): + # with dist checkpointing we can use the model parallel config specified by the user + pretrained_cfg.tensor_model_parallel_size = cfg.tensor_model_parallel_size + pretrained_cfg.pipeline_model_parallel_size = cfg.pipeline_model_parallel_size if trainer.precision == "16": pretrained_cfg.megatron_amp_O2 = False elif trainer.precision in ['bf16', 'bf16-mixed'] and cfg.get('megatron_amp_O2', False): @@ -242,7 +249,11 @@ def main(cfg) -> None: pipeline_model_parallel_size_=cfg.pipeline_model_parallel_size, pipeline_model_parallel_split_rank_=cfg.pipeline_model_parallel_split_rank, ) - checkpoint_path = inject_model_parallel_rank(os.path.join(cfg.checkpoint_dir, cfg.checkpoint_name)) + checkpoint_path = os.path.join(cfg.checkpoint_dir, cfg.checkpoint_name) + # checkpoint_path is a dir in case of distributed checkpointing + if not os.path.isdir(checkpoint_path): + # legacy checkpoint needs model parallel rank injection + checkpoint_path = inject_model_parallel_rank(os.path.join(cfg.checkpoint_dir, cfg.checkpoint_name)) model = MegatronGPTModel.load_from_checkpoint(checkpoint_path, hparams_file=cfg.hparams_file, trainer=trainer) else: raise ValueError("need at least a nemo file or checkpoint dir") diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py index 1d2d68b9b6f5..4524ca5b21c7 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py @@ -1291,17 +1291,22 @@ def on_load_checkpoint(self, checkpoint) -> None: # mcore uses distributed checkpointing if self.mcore_gpt: - for index, module in enumerate(self.get_gpt_module_list()): - if parallel_state.get_virtual_pipeline_model_parallel_world_size() is not None: - checkpoint_state_dict = checkpoint['state_dict'][f'model_{index}'] - else: - checkpoint_state_dict = checkpoint['state_dict'] - # checkpoint_state_dict has "model." but module does not so we need to remove it when loading - checkpoint_state_dict = { - key.replace('model.', ''): checkpoint_state_dict.pop(key) - for key in list(checkpoint_state_dict.keys()) - } - module.load_state_dict(checkpoint_state_dict, strict=True) + if 'state_dict' in checkpoint: + for index, module in enumerate(self.get_gpt_module_list()): + if parallel_state.get_virtual_pipeline_model_parallel_world_size() is not None: + checkpoint_state_dict = checkpoint['state_dict'][f'model_{index}'] + else: + checkpoint_state_dict = checkpoint['state_dict'] + # checkpoint_state_dict has "model." but module does not so we need to remove it when loading + checkpoint_state_dict = { + key.replace('model.', ''): checkpoint_state_dict.pop(key) + for key in list(checkpoint_state_dict.keys()) + } + module.load_state_dict(checkpoint_state_dict, strict=True) + else: + # when restoring a distributed checkpoint from a ptl checkpoint we need to defer loading the state_dict + # see NLPModel.on_load_checkpoint + checkpoint['state_dict'] = {} # legacy checkpointing for interleaved else: diff --git a/nemo/collections/nlp/models/nlp_model.py b/nemo/collections/nlp/models/nlp_model.py index e7e8d3b137f4..0f0de87d3887 100644 --- a/nemo/collections/nlp/models/nlp_model.py +++ b/nemo/collections/nlp/models/nlp_model.py @@ -41,6 +41,15 @@ from nemo.core.classes.exportable import Exportable from nemo.utils import AppState, logging +try: + from megatron.core import dist_checkpointing, parallel_state + + HAVE_MEGATRON_CORE = True + +except (ImportError, ModuleNotFoundError): + + HAVE_MEGATRON_CORE = False + __all__ = ['NLPModel'] NEMO_NLP_TMP = os.path.join(os.path.dirname(str(TRANSFORMERS_CACHE)), "nemo_nlp_tmp") @@ -310,6 +319,20 @@ def load_from_checkpoint( checkpoint = None try: cls._set_model_restore_state(is_being_restored=True) + + # dist checkpoint is a dir + checkpoint_dir = None + if os.path.isdir(checkpoint_path): + # store dir for later use + checkpoint_dir = checkpoint_path + + # metadata is stored in common.pt + checkpoint_path = os.path.join(checkpoint_path, 'common.pt') + + # we defer loading the state_dict until the class has been initialized + # we need to set this for ptl_load_state + strict = False + # TODO: replace with proper PTL API with pl_legacy_patch(): if map_location is not None: @@ -342,7 +365,7 @@ def load_from_checkpoint( config_kwargs.pop('trainer') cfg.update(config_kwargs) - if cfg.get('megatron_amp_O2', False): + if cfg.get('megatron_amp_O2', False) and checkpoint_dir is None: new_state_dict = {} for key in checkpoint['state_dict'].keys(): new_key = key.replace('model.', 'model.module.', 1) @@ -355,6 +378,26 @@ def load_from_checkpoint( model = ptl_load_state(cls, checkpoint, strict=strict, cfg=cfg, **kwargs) # cfg = checkpoint[cls.CHECKPOINT_HYPER_PARAMS_KEY].cfg + # if the checkpoint is distributed, we deferred loading the state_dict until now + if checkpoint_dir is not None: + sharded_state_dict = model.sharded_state_dict() + checkpoint['state_dict'] = sharded_state_dict + # dist checkpointing needs torch.distributed to load the checkpoint + if parallel_state.is_unitialized(): + + def dummy(): + return + + if model.trainer.strategy.launcher is not None: + model.trainer.strategy.launcher.launch(dummy, trainer=model.trainer) + model.trainer.strategy.setup_environment() + # load the checkpoint from disk + checkpoint = dist_checkpointing.load(sharded_state_dict=checkpoint, checkpoint_dir=checkpoint_dir) + # restore the weights + model.on_load_checkpoint(checkpoint) + if hasattr(model, 'setup_transformer_engine_tp_groups'): + model.setup_transformer_engine_tp_groups() + # NMT models do not have a `tokenizer` attribute, they instead have an encoder_tokenizer and decoder_tokenizer attribute. if hasattr(cfg, "tokenizer"): if cfg.tokenizer.get("tokenizer_model") is not None: diff --git a/nemo/collections/nlp/parts/nlp_overrides.py b/nemo/collections/nlp/parts/nlp_overrides.py index 8b7970df4667..273768f7776d 100644 --- a/nemo/collections/nlp/parts/nlp_overrides.py +++ b/nemo/collections/nlp/parts/nlp_overrides.py @@ -41,7 +41,7 @@ from nemo.core.optim import MainParamsOptimizerWrapper from nemo.utils import AppState, logging from nemo.utils.get_rank import is_global_rank_zero -from nemo.utils.model_utils import ckpt_to_dir, inject_model_parallel_rank +from nemo.utils.model_utils import ckpt_to_dir, inject_model_parallel_rank, uninject_model_parallel_rank try: from apex.transformer.pipeline_parallel.utils import get_num_microbatches @@ -448,12 +448,38 @@ def __init__(self) -> None: def save_to(self, model, save_path: str): app_state = AppState() - if app_state.model_parallel_size is not None and app_state.model_parallel_size > 1: + + # Check if using distributed checkpointing + dist_ckpt = hasattr(model, 'sharded_state_dict') and model.sharded_state_dict() is not None + + dist_ckpt_dir = None + + if (app_state.model_parallel_size is not None and app_state.model_parallel_size > 1) or dist_ckpt: dir_name = os.path.dirname(save_path) - # first we save the weights for each model parallel rank - if app_state.model_parallel_size is not None and app_state.model_parallel_size > 1: + # dist ckpt calls save on every rank + if dist_ckpt: + # model weights is a directory + dist_ckpt_dir = ckpt_to_dir(os.path.join(dir_name, self.model_weights_ckpt)) + fs = get_filesystem(dist_ckpt_dir) + if is_global_rank_zero(): + fs.makedirs(dist_ckpt_dir, exist_ok=True) + sharded_state_dict = model.sharded_state_dict() + # dist checkpoint needs torch.distributed to save the checkpoint + if parallel_state.is_unitialized(): + + def dummy(): + return + + if model.trainer.strategy.launcher is not None: + model.trainer.strategy.launcher.launch(dummy, trainer=model.trainer) + model.trainer.strategy.setup_environment() + dist_checkpointing.save(sharded_state_dict=sharded_state_dict, checkpoint_dir=dist_ckpt_dir) + + else: + + # first we save the weights for each model parallel rank if app_state.data_parallel_rank == 0: if app_state.pipeline_model_parallel_size == 1: mp_model_weights = os.path.join( @@ -468,54 +494,56 @@ def save_to(self, model, save_path: str): self._save_state_dict_to_disk(model.state_dict(), mp_model_weights) - if torch.distributed.is_initialized(): - torch.distributed.barrier() - - # create nemo file from folder with all mp_ranks checkpoints - if ( - app_state.pipeline_model_parallel_rank == 0 - and app_state.tensor_model_parallel_rank == 0 - and app_state.data_parallel_rank == 0 - ): - with tempfile.TemporaryDirectory() as tmpdir: - - if app_state.pipeline_model_parallel_size == 1: - # move weights to the tmpdir - for tp_rank in range(app_state.tensor_model_parallel_size): - os.makedirs(os.path.join(tmpdir, f'mp_rank_{tp_rank:02d}')) - mp_model_weights = os.path.join( - dir_name, f'mp_rank_{tp_rank:02d}_' + self.model_weights_ckpt - ) - shutil.move( - mp_model_weights, - os.path.join(tmpdir, f'mp_rank_{tp_rank:02d}', self.model_weights_ckpt), - ) - else: - # move weights to the tmpdir - for tp_rank, pp_rank in itertools.product( - range(app_state.tensor_model_parallel_size), - range(app_state.pipeline_model_parallel_size), - ): - os.makedirs(os.path.join(tmpdir, f'tp_rank_{tp_rank:02d}_pp_rank_{pp_rank:03d}')) - mp_model_weights = os.path.join( - dir_name, f'tp_rank_{tp_rank:02d}_pp_rank_{pp_rank:03d}_' + self.model_weights_ckpt - ) - shutil.move( - mp_model_weights, - os.path.join( - tmpdir, f'tp_rank_{tp_rank:02d}_pp_rank_{pp_rank:03d}', self.model_weights_ckpt - ), - ) - - # create config and artifacts in tmpdir - config_yaml = os.path.join(tmpdir, self.model_config_yaml) - model.to_config_file(path2yaml_file=config_yaml) - if hasattr(model, 'artifacts') and model.artifacts is not None: - self._handle_artifacts(model, nemo_file_folder=tmpdir) - self._update_artifact_paths(model, path2yaml_file=config_yaml) - - # create tar file - self._make_nemo_file_from_folder(save_path, tmpdir) + if torch.distributed.is_initialized(): + torch.distributed.barrier() + + # create nemo file from folder with all mp_ranks checkpoints + if ( + app_state.pipeline_model_parallel_rank == 0 + and app_state.tensor_model_parallel_rank == 0 + and app_state.data_parallel_rank == 0 + ): + with tempfile.TemporaryDirectory() as tmpdir: + + if dist_ckpt: + shutil.move(str(dist_ckpt_dir), tmpdir) + + elif app_state.pipeline_model_parallel_size == 1: + # move weights to the tmpdir + for tp_rank in range(app_state.tensor_model_parallel_size): + os.makedirs(os.path.join(tmpdir, f'mp_rank_{tp_rank:02d}')) + mp_model_weights = os.path.join( + dir_name, f'mp_rank_{tp_rank:02d}_' + self.model_weights_ckpt + ) + shutil.move( + mp_model_weights, + os.path.join(tmpdir, f'mp_rank_{tp_rank:02d}', self.model_weights_ckpt), + ) + else: + # move weights to the tmpdir + for tp_rank, pp_rank in itertools.product( + range(app_state.tensor_model_parallel_size), range(app_state.pipeline_model_parallel_size), + ): + os.makedirs(os.path.join(tmpdir, f'tp_rank_{tp_rank:02d}_pp_rank_{pp_rank:03d}')) + mp_model_weights = os.path.join( + dir_name, f'tp_rank_{tp_rank:02d}_pp_rank_{pp_rank:03d}_' + self.model_weights_ckpt + ) + shutil.move( + mp_model_weights, + os.path.join( + tmpdir, f'tp_rank_{tp_rank:02d}_pp_rank_{pp_rank:03d}', self.model_weights_ckpt + ), + ) + + # create config and artifacts in tmpdir + config_yaml = os.path.join(tmpdir, self.model_config_yaml) + model.to_config_file(path2yaml_file=config_yaml) + if hasattr(model, 'artifacts') and model.artifacts is not None: + self._handle_artifacts(model, nemo_file_folder=tmpdir) + self._update_artifact_paths(model, path2yaml_file=config_yaml) + + # create tar file + self._make_nemo_file_from_folder(save_path, tmpdir) else: return super().save_to(model, save_path) @@ -539,6 +567,21 @@ def modify_state_dict(self, conf, state_dict): return state_dict + def _load_state_dict_from_disk(self, model_weights, map_location=None): + # if model_weights with the extension removed is a directory, we assume it is a distributed checkpoint + # we need to defer loading the state dict so we return None + uninject_model_weights = uninject_model_parallel_rank(model_weights) + + # legacy model_weights will have mp rank injected + if os.path.isfile(model_weights): + return super()._load_state_dict_from_disk(model_weights, map_location) + + # dist checkpoint will be a dir + elif os.path.isdir(os.path.splitext(uninject_model_weights)[0]): + return None + else: + raise ValueError(f'Expected {model_weights} to be a file or directory.') + def restore_from( self, calling_cls, @@ -571,6 +614,7 @@ def restore_from( Returns: An instance of type cls or its underlying config (if return_config is set). """ + # Get path where the command is executed - the artifacts will be "retrieved" there # (original .nemo behavior) loaded_params = super().load_config_and_state_dict( @@ -579,8 +623,52 @@ def restore_from( if not isinstance(loaded_params, tuple) or return_config is True: return loaded_params conf, instance, state_dict = loaded_params - state_dict = self.modify_state_dict(conf, state_dict) - super().load_instance_with_state_dict(instance, state_dict, strict) + + # if we're using dist checkpointing then state_dict will be None + if state_dict is None: + # dist checkpointing needs torch.distributed to load the checkpoint + if parallel_state.is_unitialized(): + + def dummy(): + return + + if trainer.strategy.launcher is not None: + trainer.strategy.launcher.launch(dummy, trainer=trainer) + trainer.strategy.setup_environment() + + with tempfile.TemporaryDirectory() as tmpdir: + # Check if self.model_extracted_dir is set, and is a valid path + if self.model_extracted_dir is not None and os.path.isdir(self.model_extracted_dir): + # Log that NeMo will use the provided `model_extracted_dir` + logging.info( + f"Restoration will occur within pre-extracted directory : " f"`{self.model_extracted_dir}`." + ) + + # Override `tmpdir` above with the pre-extracted `model_extracted_dir` + tmpdir = self.model_extracted_dir + + else: + # Extract the nemo file into the temporary directory + self._unpack_nemo_file( + path2file=restore_path, out_folder=tmpdir, extract_config_only=return_config is True + ) + checkpoint = {} + sharded_state_dict = instance.sharded_state_dict() + checkpoint['state_dict'] = sharded_state_dict + # remove model weights extension + tmp_model_weights_ckpt = os.path.join(tmpdir, self.model_weights_ckpt) + tmp_model_weights_dir = os.path.splitext(tmp_model_weights_ckpt)[0] + assert os.path.isdir(tmp_model_weights_dir), f'Expected {tmp_model_weights_dir} to be a directory.' + checkpoint = dist_checkpointing.load( + sharded_state_dict=checkpoint, checkpoint_dir=tmp_model_weights_dir + ) + instance.on_load_checkpoint(checkpoint) + if hasattr(instance, 'setup_transformer_engine_tp_groups'): + instance.setup_transformer_engine_tp_groups() + + else: + state_dict = self.modify_state_dict(conf, state_dict) + super().load_instance_with_state_dict(instance, state_dict, strict) logging.info(f'Model {instance.__class__.__name__} was successfully restored from {restore_path}.') return instance diff --git a/nemo/core/optim/distributed_adam.py b/nemo/core/optim/distributed_adam.py index 62bba769f652..d7bc049c1808 100644 --- a/nemo/core/optim/distributed_adam.py +++ b/nemo/core/optim/distributed_adam.py @@ -21,11 +21,7 @@ from megatron.core import parallel_state from megatron.core.dist_checkpointing.dict_utils import dict_list_map_inplace from megatron.core.dist_checkpointing.mapping import ShardedTensor -from megatron.core.dist_checkpointing.optimizer import ( - get_param_id_to_sharded_param_map, - make_sharded_optimizer_tensor, - optim_state_to_sharding_state, -) +from megatron.core.dist_checkpointing.optimizer import get_param_id_to_sharded_param_map, optim_state_to_sharding_state def _str_to_dtype(dtype: Union[str, torch.dtype]) -> torch.dtype: diff --git a/scripts/nlp_language_modeling/convert_hf_llama_to_nemo.py b/scripts/nlp_language_modeling/convert_hf_llama_to_nemo.py index 69f229ec5e10..c281088f8c5c 100644 --- a/scripts/nlp_language_modeling/convert_hf_llama_to_nemo.py +++ b/scripts/nlp_language_modeling/convert_hf_llama_to_nemo.py @@ -37,6 +37,7 @@ from nemo.collections.nlp.parts.nlp_overrides import ( GradScaler, MegatronHalfPrecisionPlugin, + NLPDDPStrategy, NLPSaveRestoreConnector, PipelineMixedPrecisionPlugin, ) @@ -179,7 +180,7 @@ def convert(args): nemo_config.precision = precision print(f"nemo_config: {nemo_config}") - trainer = Trainer(plugins=plugins, accelerator='cpu', precision=precision) + trainer = Trainer(plugins=plugins, accelerator='cpu', precision=precision, strategy=NLPDDPStrategy()) hidden_size = hf_config["hidden_size"] head_num = hf_config["num_attention_heads"] From b24fdb96c2dbc2eb69195432d720c174e3c5c7a7 Mon Sep 17 00:00:00 2001 From: JimmyZhang12 <67203904+JimmyZhang12@users.noreply.github.com> Date: Fri, 8 Sep 2023 21:25:54 -0700 Subject: [PATCH 018/112] fix forward for with mcore=false (#7403) Signed-off-by: Jimmy Zhang Co-authored-by: Jimmy Zhang Signed-off-by: Sasha Meister --- .../language_modeling/megatron_gpt_model.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py index 4524ca5b21c7..0362d6f2d1d7 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py @@ -884,16 +884,16 @@ def fwd_output_only_func(dataloader_iter, model): if attention_mask is not None: attention_mask = attention_mask.cuda() attention_mask = attention_mask[0:1] - if self.mcore_gpt: - # if first step, then clear KV cache, otherwise reuse inference_paarms - if set_inference_key_value_memory[0].item(): - self.inference_params = InferenceParams( - max_batch_size=tokens.size(0), max_sequence_length=inference_max_sequence_len[0].item() - ) - extra_arg['inference_params'] = self.inference_params - else: - extra_arg['set_inference_key_value_memory'] = set_inference_key_value_memory[0].item() - extra_arg['inference_max_sequence_len'] = inference_max_sequence_len[0].item() + if self.mcore_gpt: + # if first step, then clear KV cache, otherwise reuse inference_paarms + if set_inference_key_value_memory[0].item(): + self.inference_params = InferenceParams( + max_batch_size=tokens.size(0), max_sequence_length=inference_max_sequence_len[0].item() + ) + extra_arg['inference_params'] = self.inference_params + else: + extra_arg['set_inference_key_value_memory'] = set_inference_key_value_memory[0].item() + extra_arg['inference_max_sequence_len'] = inference_max_sequence_len[0].item() output_tensor = model(tokens, position_ids, attention_mask, **extra_arg) # Advance inference sequence offset. From c85dcbab468dfe4d003312290328c2a7441b96f8 Mon Sep 17 00:00:00 2001 From: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Date: Fri, 8 Sep 2023 21:31:43 -0700 Subject: [PATCH 019/112] Fix logging to remove 's/it' from progress bar in Megatron models and add train_step_timing (#7374) * Add CustomProgressBar class to exp_manager and trainer callbacks Signed-off-by: Abhishree * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix the progress bar to reflect total microbatch cnt Signed-off-by: Abhishree * Modify CustomProgressBar class 1) Modify CustomProgressBar class to update progress bar per global_step instead of per microbatch 2) Add the callback to other megatron training/finetuning files that are not using MegatronTrainerBuilder Signed-off-by: Abhishree * Add CustomProgressBar callback to tuning files Signed-off-by: Abhishree * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Abhishree Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../megatron_bart_pretraining.py | 5 +++- .../megatron_gpt_continue_training.py | 3 ++- .../language_modeling/megatron_gpt_eval.py | 4 +-- .../megatron_gpt_prompt_learning.py | 3 ++- .../megatron_gpt_prompt_learning_eval.py | 4 +-- .../megatron_retro_cal_shape.py | 9 +++++-- .../megatron_retro_fine_tune.py | 3 ++- .../megatron_retro_mutransfer_pretrain.py | 9 +++++-- .../megatron_retro_pretraining.py | 3 ++- .../megatron_t5_lm_adaptation_finetune.py | 5 +++- .../megatron_t5_prompt_learning.py | 3 ++- .../megatron_t5_seq2seq_finetune.py | 3 ++- .../tuning/megatron_gpt_adapter_tuning.py | 3 ++- .../tuning/megatron_gpt_ia3_tuning.py | 3 ++- .../tuning/megatron_gpt_peft_tuning.py | 3 ++- .../tuning/megatron_gpt_sft.py | 3 ++- .../tuning/megatron_t5_adapter_tuning.py | 3 ++- .../tuning/megatron_t5_ia3_tuning.py | 3 ++- .../tuning/megatron_t5_lora_tuning.py | 3 ++- .../nlp/parts/megatron_trainer_builder.py | 11 ++++++-- nemo/collections/nlp/parts/nlp_overrides.py | 26 +++++++++++++++++++ nemo/utils/exp_manager.py | 9 ++++++- 22 files changed, 95 insertions(+), 26 deletions(-) diff --git a/examples/nlp/language_modeling/megatron_bart_pretraining.py b/examples/nlp/language_modeling/megatron_bart_pretraining.py index 2b3c7ad1afef..72c7a755c0ec 100644 --- a/examples/nlp/language_modeling/megatron_bart_pretraining.py +++ b/examples/nlp/language_modeling/megatron_bart_pretraining.py @@ -21,6 +21,7 @@ from nemo.collections.nlp.models.language_modeling.megatron_bart_model import MegatronBARTModel from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -63,7 +64,9 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[ModelSummary(max_depth=3)]) + trainer = Trainer( + plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[ModelSummary(max_depth=3), CustomProgressBar()] + ) exp_manager(trainer, cfg.exp_manager) diff --git a/examples/nlp/language_modeling/megatron_gpt_continue_training.py b/examples/nlp/language_modeling/megatron_gpt_continue_training.py index 2cb18c9a6714..c829a91c4092 100644 --- a/examples/nlp/language_modeling/megatron_gpt_continue_training.py +++ b/examples/nlp/language_modeling/megatron_gpt_continue_training.py @@ -23,6 +23,7 @@ from nemo.collections.nlp.models.language_modeling.megatron_gpt_model import MegatronGPTModel from nemo.collections.nlp.modules.common.megatron.megatron_init import fake_initialize_model_parallel from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -156,7 +157,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) exp_manager(trainer, cfg.exp_manager) diff --git a/examples/nlp/language_modeling/megatron_gpt_eval.py b/examples/nlp/language_modeling/megatron_gpt_eval.py index cbac3ab29ce2..04125c6f750e 100644 --- a/examples/nlp/language_modeling/megatron_gpt_eval.py +++ b/examples/nlp/language_modeling/megatron_gpt_eval.py @@ -27,7 +27,7 @@ from nemo.collections.nlp.modules.common.text_generation_server import MegatronServer from nemo.collections.nlp.modules.common.text_generation_utils import generate from nemo.collections.nlp.modules.common.transformer.text_generation import LengthParam, SamplingParam -from nemo.collections.nlp.parts.nlp_overrides import NLPDDPStrategy, NLPSaveRestoreConnector +from nemo.collections.nlp.parts.nlp_overrides import CustomProgressBar, NLPDDPStrategy, NLPSaveRestoreConnector from nemo.core.config import hydra_runner from nemo.utils.app_state import AppState from nemo.utils.model_utils import inject_model_parallel_rank @@ -167,7 +167,7 @@ def remove_padded_prompts(response, nb_paddings): def main(cfg) -> None: # trainer required for restoring model parallel models - trainer = Trainer(strategy=NLPDDPStrategy(), **cfg.trainer) + trainer = Trainer(strategy=NLPDDPStrategy(), **cfg.trainer, callbacks=[CustomProgressBar()]) if cfg.gpt_model_file is not None: if ( diff --git a/examples/nlp/language_modeling/megatron_gpt_prompt_learning.py b/examples/nlp/language_modeling/megatron_gpt_prompt_learning.py index 278cb454a50a..89077f099aeb 100644 --- a/examples/nlp/language_modeling/megatron_gpt_prompt_learning.py +++ b/examples/nlp/language_modeling/megatron_gpt_prompt_learning.py @@ -21,6 +21,7 @@ MegatronGPTPromptLearningModel, ) from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -74,7 +75,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) exp_manager(trainer, cfg.exp_manager) # load existing or init new soft prompt GPT model diff --git a/examples/nlp/language_modeling/megatron_gpt_prompt_learning_eval.py b/examples/nlp/language_modeling/megatron_gpt_prompt_learning_eval.py index 038bf3c2587b..e096bafc6132 100644 --- a/examples/nlp/language_modeling/megatron_gpt_prompt_learning_eval.py +++ b/examples/nlp/language_modeling/megatron_gpt_prompt_learning_eval.py @@ -26,7 +26,7 @@ MegatronGPTPromptLearningModel, ) from nemo.collections.nlp.modules.common.transformer.text_generation import LengthParam, SamplingParam -from nemo.collections.nlp.parts.nlp_overrides import NLPDDPStrategy, NLPSaveRestoreConnector +from nemo.collections.nlp.parts.nlp_overrides import CustomProgressBar, NLPDDPStrategy, NLPSaveRestoreConnector from nemo.core.config import hydra_runner from nemo.utils import logging @@ -83,7 +83,7 @@ def main(cfg) -> None: raise EnvironmentError("GPU is needed for the inference") # trainer required for restoring model parallel models - trainer = Trainer(strategy=NLPDDPStrategy(), **cfg.trainer) + trainer = Trainer(strategy=NLPDDPStrategy(), **cfg.trainer, callbacks=[CustomProgressBar()]) if ( cfg.tensor_model_parallel_size < 0 diff --git a/examples/nlp/language_modeling/megatron_retro_cal_shape.py b/examples/nlp/language_modeling/megatron_retro_cal_shape.py index a50736e88653..008ce1445929 100644 --- a/examples/nlp/language_modeling/megatron_retro_cal_shape.py +++ b/examples/nlp/language_modeling/megatron_retro_cal_shape.py @@ -19,7 +19,12 @@ from nemo.collections.nlp.models.language_modeling.megatron_retrieval_model import MegatronRetrievalModel from nemo.collections.nlp.modules.common.megatron.mup.shape import make_base_shapes -from nemo.collections.nlp.parts.nlp_overrides import GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy +from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, + GradScaler, + MegatronHalfPrecisionPlugin, + NLPDDPStrategy, +) from nemo.core.config import hydra_runner from nemo.utils import logging @@ -56,7 +61,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) # hydra interpolation does not work here as the interpolation key is lost when PTL saves hparams with open_dict(cfg): diff --git a/examples/nlp/language_modeling/megatron_retro_fine_tune.py b/examples/nlp/language_modeling/megatron_retro_fine_tune.py index 15f1d541b6e7..031191c22b0a 100644 --- a/examples/nlp/language_modeling/megatron_retro_fine_tune.py +++ b/examples/nlp/language_modeling/megatron_retro_fine_tune.py @@ -24,6 +24,7 @@ from nemo.collections.nlp.models.language_modeling.megatron_retro_fine_tune_model import MegatronRetroFinetuneModel from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -102,7 +103,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) exp_manager(trainer, cfg.exp_manager) logging.info(f'Resuming training from checkpoint: {trainer.ckpt_path}') diff --git a/examples/nlp/language_modeling/megatron_retro_mutransfer_pretrain.py b/examples/nlp/language_modeling/megatron_retro_mutransfer_pretrain.py index 65a98c8b9b2d..79b25fd2c5e8 100644 --- a/examples/nlp/language_modeling/megatron_retro_mutransfer_pretrain.py +++ b/examples/nlp/language_modeling/megatron_retro_mutransfer_pretrain.py @@ -20,7 +20,12 @@ from nemo.collections.nlp.models.language_modeling.megatron_retrieval_model import MegatronRetrievalModel from nemo.collections.nlp.modules.common.megatron.mup.optim import MuAdam, MuAdamW -from nemo.collections.nlp.parts.nlp_overrides import GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy +from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, + GradScaler, + MegatronHalfPrecisionPlugin, + NLPDDPStrategy, +) from nemo.core.config import hydra_runner from nemo.core.config.optimizers import AdamParams, AdamWParams from nemo.core.optim.optimizers import register_optimizer @@ -62,7 +67,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) exp_manager(trainer, cfg.exp_manager) diff --git a/examples/nlp/language_modeling/megatron_retro_pretraining.py b/examples/nlp/language_modeling/megatron_retro_pretraining.py index 3d0de78dc4d8..e90fdddf1169 100644 --- a/examples/nlp/language_modeling/megatron_retro_pretraining.py +++ b/examples/nlp/language_modeling/megatron_retro_pretraining.py @@ -23,6 +23,7 @@ from nemo.collections.nlp.models.language_modeling.megatron_retrieval_model import MegatronRetrievalModel from nemo.collections.nlp.modules.common.megatron.megatron_init import initialize_model_parallel_for_nemo from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -65,7 +66,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) exp_manager(trainer, cfg.exp_manager) diff --git a/examples/nlp/language_modeling/megatron_t5_lm_adaptation_finetune.py b/examples/nlp/language_modeling/megatron_t5_lm_adaptation_finetune.py index 89e7e4e70321..0a10d434127d 100644 --- a/examples/nlp/language_modeling/megatron_t5_lm_adaptation_finetune.py +++ b/examples/nlp/language_modeling/megatron_t5_lm_adaptation_finetune.py @@ -21,6 +21,7 @@ from nemo.collections.nlp.models.language_modeling.megatron_t5_model import MegatronT5Model from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -64,7 +65,9 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[ModelSummary(max_depth=3)]) + trainer = Trainer( + plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[ModelSummary(max_depth=3), CustomProgressBar()] + ) exp_manager(trainer, cfg.exp_manager) # update resume from checkpoint found by exp_manager diff --git a/examples/nlp/language_modeling/megatron_t5_prompt_learning.py b/examples/nlp/language_modeling/megatron_t5_prompt_learning.py index 9923297f6c33..ba335e39c225 100644 --- a/examples/nlp/language_modeling/megatron_t5_prompt_learning.py +++ b/examples/nlp/language_modeling/megatron_t5_prompt_learning.py @@ -21,6 +21,7 @@ MegatronT5PromptLearningModel, ) from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, NLPDDPStrategy, NLPSaveRestoreConnector, @@ -64,7 +65,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) exp_manager(trainer, cfg.exp_manager) # load existing or init new soft prompt T5 model diff --git a/examples/nlp/language_modeling/megatron_t5_seq2seq_finetune.py b/examples/nlp/language_modeling/megatron_t5_seq2seq_finetune.py index 1c59431f0515..e8e4ff0f9868 100644 --- a/examples/nlp/language_modeling/megatron_t5_seq2seq_finetune.py +++ b/examples/nlp/language_modeling/megatron_t5_seq2seq_finetune.py @@ -26,6 +26,7 @@ from nemo.collections.nlp.models.language_modeling.megatron_t0_model import MegatronT0Model from nemo.collections.nlp.modules.common.megatron.megatron_init import fake_initialize_model_parallel from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -177,7 +178,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) exp_manager(trainer, cfg.exp_manager) diff --git a/examples/nlp/language_modeling/tuning/megatron_gpt_adapter_tuning.py b/examples/nlp/language_modeling/tuning/megatron_gpt_adapter_tuning.py index 66e03a94db74..7aebad7e9010 100644 --- a/examples/nlp/language_modeling/tuning/megatron_gpt_adapter_tuning.py +++ b/examples/nlp/language_modeling/tuning/megatron_gpt_adapter_tuning.py @@ -20,6 +20,7 @@ from nemo.collections.nlp.models.language_modeling.megatron_gpt_adapter_model import MegatronGPTAdapterLearningModel from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -90,7 +91,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) exp_manager(trainer, cfg.exp_manager) # load existing or init new soft prompt GPT model diff --git a/examples/nlp/language_modeling/tuning/megatron_gpt_ia3_tuning.py b/examples/nlp/language_modeling/tuning/megatron_gpt_ia3_tuning.py index 7467528c40da..b74d215b6ef7 100644 --- a/examples/nlp/language_modeling/tuning/megatron_gpt_ia3_tuning.py +++ b/examples/nlp/language_modeling/tuning/megatron_gpt_ia3_tuning.py @@ -20,6 +20,7 @@ from nemo.collections.nlp.models.language_modeling.megatron_gpt_adapter_model import MegatronGPTInfusedAdapterModel from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -89,7 +90,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) exp_manager(trainer, cfg.exp_manager) # load existing or init new soft prompt GPT model diff --git a/examples/nlp/language_modeling/tuning/megatron_gpt_peft_tuning.py b/examples/nlp/language_modeling/tuning/megatron_gpt_peft_tuning.py index bf4473594d24..2c9293b2600e 100644 --- a/examples/nlp/language_modeling/tuning/megatron_gpt_peft_tuning.py +++ b/examples/nlp/language_modeling/tuning/megatron_gpt_peft_tuning.py @@ -35,6 +35,7 @@ from nemo.collections.nlp.models.language_modeling.megatron_gpt_sft_model import MegatronGPTModel from nemo.collections.nlp.modules.common.megatron.megatron_init import fake_initialize_model_parallel from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -213,7 +214,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) exp_manager(trainer, cfg.exp_manager) # update resume from checkpoint found by exp_manager if cfg.model.resume_from_checkpoint is not None: diff --git a/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py b/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py index b5c37e4936c0..15d3dc53c348 100644 --- a/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py +++ b/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py @@ -23,6 +23,7 @@ from nemo.collections.nlp.models.language_modeling.megatron_gpt_sft_model import MegatronGPTSFTModel from nemo.collections.nlp.modules.common.megatron.megatron_init import fake_initialize_model_parallel from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -174,7 +175,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) exp_manager(trainer, cfg.exp_manager) diff --git a/examples/nlp/language_modeling/tuning/megatron_t5_adapter_tuning.py b/examples/nlp/language_modeling/tuning/megatron_t5_adapter_tuning.py index ad3a6465112d..c096a75d0b9d 100644 --- a/examples/nlp/language_modeling/tuning/megatron_t5_adapter_tuning.py +++ b/examples/nlp/language_modeling/tuning/megatron_t5_adapter_tuning.py @@ -20,6 +20,7 @@ from nemo.collections.nlp.models.language_modeling.megatron_t5_adapter_model import MegatronT5AdapterLearningModel from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -89,7 +90,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) exp_manager(trainer, cfg.exp_manager) # load existing or init new soft prompt GPT model diff --git a/examples/nlp/language_modeling/tuning/megatron_t5_ia3_tuning.py b/examples/nlp/language_modeling/tuning/megatron_t5_ia3_tuning.py index cc5cf931cfce..46b590acd725 100644 --- a/examples/nlp/language_modeling/tuning/megatron_t5_ia3_tuning.py +++ b/examples/nlp/language_modeling/tuning/megatron_t5_ia3_tuning.py @@ -20,6 +20,7 @@ from nemo.collections.nlp.models.language_modeling.megatron_t5_adapter_model import MegatronT5InfusedAdapterModel from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -90,7 +91,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) exp_manager(trainer, cfg.exp_manager) # hydra interpolation does not work here as the interpolation key is lost when PTL saves hparams diff --git a/examples/nlp/language_modeling/tuning/megatron_t5_lora_tuning.py b/examples/nlp/language_modeling/tuning/megatron_t5_lora_tuning.py index de796aef3f86..fb982a273662 100644 --- a/examples/nlp/language_modeling/tuning/megatron_t5_lora_tuning.py +++ b/examples/nlp/language_modeling/tuning/megatron_t5_lora_tuning.py @@ -20,6 +20,7 @@ from nemo.collections.nlp.models.language_modeling.megatron_t5_adapter_model import MegatronT5LoraModel from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -89,7 +90,7 @@ def main(cfg) -> None: if cfg.get('cluster_type', None) == 'BCP': plugins.append(TorchElasticEnvironment()) - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) + trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) exp_manager(trainer, cfg.exp_manager) # load existing or init new soft prompt GPT model diff --git a/nemo/collections/nlp/parts/megatron_trainer_builder.py b/nemo/collections/nlp/parts/megatron_trainer_builder.py index e5af76cbd1ec..02f0a7f4f4aa 100644 --- a/nemo/collections/nlp/parts/megatron_trainer_builder.py +++ b/nemo/collections/nlp/parts/megatron_trainer_builder.py @@ -16,7 +16,9 @@ from pytorch_lightning import Trainer from pytorch_lightning.callbacks import ModelSummary from pytorch_lightning.plugins.environments import TorchElasticEnvironment + from nemo.collections.nlp.parts.nlp_overrides import ( + CustomProgressBar, GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, @@ -83,7 +85,7 @@ def _plugins(self) -> list: def create_trainer(self) -> Trainer: strategy = self._training_strategy() plugins = self._plugins() - return Trainer(plugins=plugins, strategy=strategy, **self.cfg.trainer) + return Trainer(plugins=plugins, strategy=strategy, **self.cfg.trainer, callbacks=[CustomProgressBar()]) class MegatronBertTrainerBuilder(MegatronTrainerBuilder): @@ -102,4 +104,9 @@ class MegatronT5TrainerBuilder(MegatronTrainerBuilder): def create_trainer(self) -> Trainer: strategy = self._training_strategy() plugins = self._plugins() - return Trainer(plugins=plugins, strategy=strategy, **self.cfg.trainer, callbacks=[ModelSummary(max_depth=3)]) + return Trainer( + plugins=plugins, + strategy=strategy, + **self.cfg.trainer, + callbacks=[ModelSummary(max_depth=3), CustomProgressBar()] + ) diff --git a/nemo/collections/nlp/parts/nlp_overrides.py b/nemo/collections/nlp/parts/nlp_overrides.py index 273768f7776d..d7eaaa90e536 100644 --- a/nemo/collections/nlp/parts/nlp_overrides.py +++ b/nemo/collections/nlp/parts/nlp_overrides.py @@ -25,6 +25,8 @@ import torch from lightning_fabric.utilities.cloud_io import get_filesystem from omegaconf import OmegaConf +from pytorch_lightning.callbacks.progress import TQDMProgressBar +from pytorch_lightning.callbacks.progress.tqdm_progress import _update_n from pytorch_lightning.loops.fetchers import _DataFetcher from pytorch_lightning.overrides.base import _LightningModuleWrapperBase from pytorch_lightning.plugins import ClusterEnvironment @@ -1073,3 +1075,27 @@ def _fetch_next_batch(self, iterator: Iterator) -> None: assert isinstance(dataloader, Sized) # `_has_len` is True self.done = self.fetched >= len(dataloader) self.on_fetch_end(batch, start_output) + + +class CustomProgressBar(TQDMProgressBar): + """ + Add CustomProgressBar to remove 's/it' and display progress per step instead of per microbatch + for megatron models + """ + + def init_train_tqdm(self): + """ + Override bar_format to not have 's/it' + """ + self.bar = super().init_train_tqdm() + self.bar.bar_format = "{desc}: {percentage:3.0f}%|{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}{postfix}]" + return self.bar + + def on_train_batch_end(self, trainer, pl_module, *_, **__): + """ + Override parent class on_train_batch_end to update progress bar per global_step instead of per microbatch + """ + n = trainer.global_step + if self._should_update(n, self.train_progress_bar.total): + _update_n(self.train_progress_bar, n) + self.train_progress_bar.set_postfix(self.get_metrics(trainer, pl_module)) diff --git a/nemo/utils/exp_manager.py b/nemo/utils/exp_manager.py index c3e938bbb751..3deb814ae2df 100644 --- a/nemo/utils/exp_manager.py +++ b/nemo/utils/exp_manager.py @@ -190,7 +190,14 @@ def _on_batch_start(self, name): def _on_batch_end(self, name, pl_module): self.timer.stop(name) # Set the `batch_size=1` as WAR for `dataloader_iter`, which is not used for any metric - pl_module.log(name, self.timer[name], on_step=True, on_epoch=False, batch_size=1) + pl_module.log( + name + ' in s', + self.timer[name], + on_step=True, + on_epoch=False, + batch_size=1, + prog_bar=(name == "train_step_timing"), + ) def on_train_batch_start(self, trainer, pl_module, batch, batch_idx): self._on_batch_start("train_step_timing") From ae5d3ceae8fb25c403de4cf597756079273535ca Mon Sep 17 00:00:00 2001 From: Abhinav Khattar Date: Fri, 8 Sep 2023 22:45:42 -0700 Subject: [PATCH 020/112] Set Activation Checkpointing Defaults (#7404) * Set Activation Checkpointing Defaults Signed-off-by: Abhinav Khattar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * check for None Signed-off-by: Abhinav Khattar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Abhinav Khattar Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../nlp/modules/common/megatron/transformer.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/nemo/collections/nlp/modules/common/megatron/transformer.py b/nemo/collections/nlp/modules/common/megatron/transformer.py index fc8f363a556f..06a2e306482e 100644 --- a/nemo/collections/nlp/modules/common/megatron/transformer.py +++ b/nemo/collections/nlp/modules/common/megatron/transformer.py @@ -981,14 +981,14 @@ def __init__( elif self.activations_checkpoint_method == 'block': logging.info( ( - f'Using block activation checkpointing requires activations_checkpoint_num_layers to be set.' - f'Got: {self.activations_checkpoint_num_layers}. Setting to 1 by default.' + f'Using block activation checkpointing with granularity selective forces all layers to use checkpointing.' ) ) else: raise ValueError( f'activations_checkpoint_method should be "uniform" or "block" when using granularity selective.' ) + self.activations_checkpoint_num_layers = num_layers # forcing all layers elif self.activations_checkpoint_granularity == 'full': if self.activations_checkpoint_method in ['uniform', 'block']: if not self.activations_checkpoint_num_layers: @@ -998,6 +998,7 @@ def __init__( f'Got: {self.activations_checkpoint_num_layers}. Setting to 1 by default.' ) ) + self.activations_checkpoint_num_layers = 1 # keeping the old default else: raise ValueError( f'activations_checkpoint_method should be "uniform" or "block" when using granularity full.' @@ -1047,6 +1048,13 @@ def __init__( # TODO: Add similar assert for encoder-decoder. self.num_layers = self.get_num_layers(num_layers) + + if ( + self.activations_checkpoint_num_layers is not None + and self.activations_checkpoint_num_layers > self.num_layers + ): + self.activations_checkpoint_num_layers = self.num_layers + # Transformer layers. def build_layer(layer_number): if isinstance(layer_type, list): From 153c53b730a186585bb7367d080c3a230b61a763 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Sat, 9 Sep 2023 00:44:45 -0600 Subject: [PATCH 021/112] make loss mask default to false (#7407) Signed-off-by: eharper Signed-off-by: Sasha Meister --- .../nlp/models/language_modeling/megatron_gpt_model.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py index 0362d6f2d1d7..45586bffcdce 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py @@ -207,6 +207,7 @@ def __init__(self, cfg: DictConfig, trainer: Trainer): self._validate_trainer() # build the transformer config + # TODO: add type hint once pip package is out self.transformer_config = self.build_transformer_config() self.megatron_amp_o2 = cfg.get('megatron_amp_O2', False) @@ -275,6 +276,12 @@ def __init__(self, cfg: DictConfig, trainer: Trainer): self.inference_params = None + # default to false since this doesn't work with sequence parallelism currently + self.use_loss_mask = self.cfg.get('use_loss_mask', False) + + if self.use_loss_mask and self.transformer_config.sequence_parallel: + raise ValueError('Loss mask is not supported with sequence parallelism.') + def get_gpt_module_list(self): if isinstance(self.model, list): return [ @@ -826,8 +833,11 @@ def fwd_output_and_loss_func(dataloader_iter, model, checkpoint_activations_all_ 'labels': batch['labels'], 'loss_mask': batch['loss_mask'], } + if not self.mcore_gpt: forward_args['checkpoint_activations_all_layers'] = checkpoint_activations_all_layers + if not self.use_loss_mask: + forward_args.pop('loss_mask') else: # TODO: @eharper can we add this to mcore? forward_args.pop('loss_mask') From b61b9ab1ddba7af03b15cadfb60d3d36a40223e6 Mon Sep 17 00:00:00 2001 From: Sangkug Lym Date: Sat, 9 Sep 2023 13:54:32 -0700 Subject: [PATCH 022/112] Add dummy userbuffer config files (#7408) Signed-off-by: Sangkug Lym Signed-off-by: Sasha Meister --- .../conf/tp_overlap/ub_cfg_a100_h6144_tp4_mbs4_seqlen2048.yaml | 1 + .../conf/tp_overlap/ub_cfg_a100_h8192_tp8_mbs4_seqlen2048.yaml | 1 + .../conf/tp_overlap/ub_cfg_h100_h6144_tp4_mbs4_seqlen2048.yaml | 1 + .../conf/tp_overlap/ub_cfg_h100_h8192_tp8_mbs4_seqlen2048.yaml | 1 + 4 files changed, 4 insertions(+) create mode 100644 examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_a100_h6144_tp4_mbs4_seqlen2048.yaml create mode 100644 examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_a100_h8192_tp8_mbs4_seqlen2048.yaml create mode 100644 examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_h100_h6144_tp4_mbs4_seqlen2048.yaml create mode 100644 examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_h100_h8192_tp8_mbs4_seqlen2048.yaml diff --git a/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_a100_h6144_tp4_mbs4_seqlen2048.yaml b/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_a100_h6144_tp4_mbs4_seqlen2048.yaml new file mode 100644 index 000000000000..2c11fa89af4b --- /dev/null +++ b/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_a100_h6144_tp4_mbs4_seqlen2048.yaml @@ -0,0 +1 @@ +# dummy file to build hydra configs diff --git a/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_a100_h8192_tp8_mbs4_seqlen2048.yaml b/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_a100_h8192_tp8_mbs4_seqlen2048.yaml new file mode 100644 index 000000000000..2c11fa89af4b --- /dev/null +++ b/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_a100_h8192_tp8_mbs4_seqlen2048.yaml @@ -0,0 +1 @@ +# dummy file to build hydra configs diff --git a/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_h100_h6144_tp4_mbs4_seqlen2048.yaml b/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_h100_h6144_tp4_mbs4_seqlen2048.yaml new file mode 100644 index 000000000000..2c11fa89af4b --- /dev/null +++ b/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_h100_h6144_tp4_mbs4_seqlen2048.yaml @@ -0,0 +1 @@ +# dummy file to build hydra configs diff --git a/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_h100_h8192_tp8_mbs4_seqlen2048.yaml b/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_h100_h8192_tp8_mbs4_seqlen2048.yaml new file mode 100644 index 000000000000..2c11fa89af4b --- /dev/null +++ b/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_h100_h8192_tp8_mbs4_seqlen2048.yaml @@ -0,0 +1 @@ +# dummy file to build hydra configs From 3f36e112dcc979ff5e2c8b9291b896a2b67c808f Mon Sep 17 00:00:00 2001 From: Abhinav Khattar Date: Mon, 11 Sep 2023 11:52:25 -0600 Subject: [PATCH 023/112] add missing ubconf files (#7412) Signed-off-by: Abhinav Khattar Signed-off-by: Sasha Meister --- .../conf/tp_overlap/ub_cfg_a100_h6144_tp8_mbs4_seqlen2048.yaml | 1 + .../conf/tp_overlap/ub_cfg_h100_h6144_tp8_mbs4_seqlen2048.yaml | 1 + 2 files changed, 2 insertions(+) create mode 100644 examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_a100_h6144_tp8_mbs4_seqlen2048.yaml create mode 100644 examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_h100_h6144_tp8_mbs4_seqlen2048.yaml diff --git a/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_a100_h6144_tp8_mbs4_seqlen2048.yaml b/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_a100_h6144_tp8_mbs4_seqlen2048.yaml new file mode 100644 index 000000000000..2c11fa89af4b --- /dev/null +++ b/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_a100_h6144_tp8_mbs4_seqlen2048.yaml @@ -0,0 +1 @@ +# dummy file to build hydra configs diff --git a/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_h100_h6144_tp8_mbs4_seqlen2048.yaml b/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_h100_h6144_tp8_mbs4_seqlen2048.yaml new file mode 100644 index 000000000000..2c11fa89af4b --- /dev/null +++ b/examples/nlp/language_modeling/conf/tp_overlap/ub_cfg_h100_h6144_tp8_mbs4_seqlen2048.yaml @@ -0,0 +1 @@ +# dummy file to build hydra configs From 53224a253bcb2c88fb7b1888fd1847be9eb4313d Mon Sep 17 00:00:00 2001 From: George <37293288+Jorjeous@users.noreply.github.com> Date: Tue, 12 Sep 2023 00:08:50 +0400 Subject: [PATCH 024/112] New tutorial on Speech Data Explorer (#7405) * Added Google Colab based tutorial on Speech Data Explorer Signed-off-by: George Zelenfroynd Signed-off-by: Sasha Meister --- docs/source/starthere/tutorials.rst | 3 + tutorials/tools/SDE_HowTo_v2.ipynb | 2786 +++++++++++++++++++++++++++ 2 files changed, 2789 insertions(+) create mode 100644 tutorials/tools/SDE_HowTo_v2.ipynb diff --git a/docs/source/starthere/tutorials.rst b/docs/source/starthere/tutorials.rst index 8c74f88df08c..29ba2f300b74 100644 --- a/docs/source/starthere/tutorials.rst +++ b/docs/source/starthere/tutorials.rst @@ -190,6 +190,9 @@ To run a tutorial: * - Tools - NeMo Forced Aligner - `NeMo Forced Aligner `_ + * - Tools + - Speech Data Explorer + - `Speech Data Explorer `_ * - Tools - CTC Segmentation - `CTC Segmentation `_ diff --git a/tutorials/tools/SDE_HowTo_v2.ipynb b/tutorials/tools/SDE_HowTo_v2.ipynb new file mode 100644 index 000000000000..a6d3f8dd2723 --- /dev/null +++ b/tutorials/tools/SDE_HowTo_v2.ipynb @@ -0,0 +1,2786 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Introduction" + ], + "metadata": { + "id": "sHAHlxz1HYc0" + }, + "id": "sHAHlxz1HYc0" + }, + { + "cell_type": "markdown", + "source": [ + "[Speech Data Explorer](https://github.com/NVIDIA/NeMo/tree/main/tools/speech_data_explorer) (SDE) is a visual tool for interactive exploration of speech datasets and error analysis of Automatic Speech Recognition (ASR) models. This tutorial demonstrates how to use SDE in Comparison mode to evaluate two ASR models on a given test set and identify differences in their predictions." + ], + "metadata": { + "id": "9BVTGynbHSoy" + }, + "id": "9BVTGynbHSoy" + }, + { + "cell_type": "markdown", + "source": [ + "# Installation" + ], + "metadata": { + "id": "57pDMtWtHdxv" + }, + "id": "57pDMtWtHdxv" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "u_sKMlVpcAvO" + }, + "source": [ + "First, let's install NeMo:" + ], + "id": "u_sKMlVpcAvO" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "c3919489", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "87f4e2f4-a06c-432d-d986-429fbe6714af" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Cloning into 'NeMo'...\n", + "remote: Enumerating objects: 121443, done.\u001b[K\n", + "remote: Counting objects: 100% (1811/1811), done.\u001b[K\n", + "remote: Compressing objects: 100% (945/945), done.\u001b[K\n", + "remote: Total 121443 (delta 1299), reused 1238 (delta 864), pack-reused 119632\u001b[K\n", + "Receiving objects: 100% (121443/121443), 228.05 MiB | 21.34 MiB/s, done.\n", + "Resolving deltas: 100% (90608/90608), done.\n", + "Get:1 http://security.ubuntu.com/ubuntu jammy-security InRelease [110 kB]\n", + "Hit:2 http://archive.ubuntu.com/ubuntu jammy InRelease\n", + "Get:3 http://archive.ubuntu.com/ubuntu jammy-updates InRelease [119 kB]\n", + "Get:4 https://cloud.r-project.org/bin/linux/ubuntu jammy-cran40/ InRelease [3,626 B]\n", + "Get:5 http://archive.ubuntu.com/ubuntu jammy-backports InRelease [109 kB]\n", + "Hit:6 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64 InRelease\n", + "Hit:7 https://ppa.launchpadcontent.net/c2d4u.team/c2d4u4.0+/ubuntu jammy InRelease\n", + "Get:8 https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu jammy InRelease [18.1 kB]\n", + "Hit:9 https://ppa.launchpadcontent.net/graphics-drivers/ppa/ubuntu jammy InRelease\n", + "Hit:10 https://ppa.launchpadcontent.net/ubuntugis/ppa/ubuntu jammy InRelease\n", + "Get:11 http://security.ubuntu.com/ubuntu jammy-security/main amd64 Packages [962 kB]\n", + "Get:12 http://security.ubuntu.com/ubuntu jammy-security/restricted amd64 Packages [1,059 kB]\n", + "Get:13 http://security.ubuntu.com/ubuntu jammy-security/universe amd64 Packages [993 kB]\n", + "Get:14 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 Packages [1,254 kB]\n", + "Get:15 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 Packages [1,230 kB]\n", + "Get:16 http://archive.ubuntu.com/ubuntu jammy-updates/restricted amd64 Packages [1,079 kB]\n", + "Get:17 https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu jammy/main amd64 Packages [21.8 kB]\n", + "Fetched 6,959 kB in 1s (5,794 kB/s)\n", + "Reading package lists... Done\n", + "Reading package lists... Done\n", + "Building dependency tree... Done\n", + "Reading state information... Done\n", + "libsndfile1 is already the newest version (1.0.31-2build1).\n", + "ffmpeg is already the newest version (7:4.4.2-0ubuntu0.22.04.1).\n", + "0 upgraded, 0 newly installed, 0 to remove and 16 not upgraded.\n", + "Requirement already satisfied: pip in /usr/local/lib/python3.10/dist-packages (23.1.2)\n", + "Collecting pip\n", + " Downloading pip-23.2.1-py3-none-any.whl (2.1 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.1/2.1 MB\u001b[0m \u001b[31m12.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hInstalling collected packages: pip\n", + " Attempting uninstall: pip\n", + " Found existing installation: pip 23.1.2\n", + " Uninstalling pip-23.1.2:\n", + " Successfully uninstalled pip-23.1.2\n", + "Successfully installed pip-23.2.1\n", + "Uninstalling stuff\n", + "\u001b[33mWARNING: Skipping nemo_toolkit as it is not installed.\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33mWARNING: Skipping sacrebleu as it is not installed.\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33mWARNING: Skipping nemo_asr as it is not installed.\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33mWARNING: Skipping nemo_nlp as it is not installed.\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33mWARNING: Skipping nemo_tts as it is not installed.\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n", + "\u001b[0mInstalling nemo\n", + "Obtaining file:///content/NeMo\n", + " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Checking if build backend supports build_editable ... \u001b[?25l\u001b[?25hdone\n", + " Getting requirements to build editable ... \u001b[?25l\u001b[?25hdone\n", + " Preparing editable metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting huggingface-hub (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for huggingface-hub from https://files.pythonhosted.org/packages/7f/c4/adcbe9a696c135578cabcbdd7331332daad4d49b7c43688bc2d36b3a47d2/huggingface_hub-0.16.4-py3-none-any.whl.metadata\n", + " Downloading huggingface_hub-0.16.4-py3-none-any.whl.metadata (12 kB)\n", + "Requirement already satisfied: numba in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (0.56.4)\n", + "Requirement already satisfied: numpy<1.24,>=1.22 in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (1.23.5)\n", + "Collecting onnx>=1.7.0 (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for onnx>=1.7.0 from https://files.pythonhosted.org/packages/47/d4/f2d212558245e252b936247666c3f5981e6dba62ec470ff8be3df3389364/onnx-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", + " Downloading onnx-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (15 kB)\n", + "Requirement already satisfied: python-dateutil in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (2.8.2)\n", + "Collecting ruamel.yaml (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for ruamel.yaml from https://files.pythonhosted.org/packages/d9/0e/2a05efa11ea33513fbdf4a2e2576fe94fd8fa5ad226dbb9c660886390974/ruamel.yaml-0.17.32-py3-none-any.whl.metadata\n", + " Downloading ruamel.yaml-0.17.32-py3-none-any.whl.metadata (17 kB)\n", + "Requirement already satisfied: scikit-learn in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (1.2.2)\n", + "Collecting setuptools==65.5.1 (from nemo-toolkit==1.21.0rc0)\n", + " Downloading setuptools-65.5.1-py3-none-any.whl (1.2 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.2/1.2 MB\u001b[0m \u001b[31m13.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: tensorboard in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (2.12.3)\n", + "Requirement already satisfied: text-unidecode in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (1.3)\n", + "Requirement already satisfied: torch in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (2.0.1+cu118)\n", + "Requirement already satisfied: tqdm>=4.41.0 in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (4.66.1)\n", + "Collecting wget (from nemo-toolkit==1.21.0rc0)\n", + " Downloading wget-3.2.zip (10 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: wrapt in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (1.14.1)\n", + "Collecting black==19.10b0 (from nemo-toolkit==1.21.0rc0)\n", + " Downloading black-19.10b0-py36-none-any.whl (97 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m97.5/97.5 kB\u001b[0m \u001b[31m12.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting click==8.0.2 (from nemo-toolkit==1.21.0rc0)\n", + " Downloading click-8.0.2-py3-none-any.whl (97 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m97.6/97.6 kB\u001b[0m \u001b[31m12.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting isort<6.0.0,>5.1.0 (from nemo-toolkit==1.21.0rc0)\n", + " Downloading isort-5.12.0-py3-none-any.whl (91 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m91.2/91.2 kB\u001b[0m \u001b[31m11.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting parameterized (from nemo-toolkit==1.21.0rc0)\n", + " Downloading parameterized-0.9.0-py2.py3-none-any.whl (20 kB)\n", + "Requirement already satisfied: pytest in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (7.4.1)\n", + "Collecting pytest-runner (from nemo-toolkit==1.21.0rc0)\n", + " Downloading pytest_runner-6.0.0-py3-none-any.whl (7.2 kB)\n", + "Requirement already satisfied: sphinx in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (5.0.2)\n", + "Collecting sphinxcontrib-bibtex (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for sphinxcontrib-bibtex from https://files.pythonhosted.org/packages/79/59/fafc5c480506cc356e2a7ea009d7c7d75812475b4385fe851ae55575661c/sphinxcontrib_bibtex-2.6.1-py3-none-any.whl.metadata\n", + " Downloading sphinxcontrib_bibtex-2.6.1-py3-none-any.whl.metadata (6.1 kB)\n", + "Collecting wandb (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for wandb from https://files.pythonhosted.org/packages/fe/10/18b03623c460fd433525d9b4739af58c5e69f5974328dcdd037cfbc855d7/wandb-0.15.10-py3-none-any.whl.metadata\n", + " Downloading wandb-0.15.10-py3-none-any.whl.metadata (9.6 kB)\n", + "Collecting hydra-core<=1.3.2,>1.3 (from nemo-toolkit==1.21.0rc0)\n", + " Downloading hydra_core-1.3.2-py3-none-any.whl (154 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m154.5/154.5 kB\u001b[0m \u001b[31m16.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting omegaconf<=2.3 (from nemo-toolkit==1.21.0rc0)\n", + " Downloading omegaconf-2.3.0-py3-none-any.whl (79 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m79.5/79.5 kB\u001b[0m \u001b[31m11.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting pytorch-lightning<=2.0.7,>=2.0 (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for pytorch-lightning<=2.0.7,>=2.0 from https://files.pythonhosted.org/packages/d5/ef/39994adec1fe1d5f25fd0dd0a82abcd8bd61fc968283790b9da7463f0279/pytorch_lightning-2.0.7-py3-none-any.whl.metadata\n", + " Downloading pytorch_lightning-2.0.7-py3-none-any.whl.metadata (23 kB)\n", + "Collecting torchmetrics>=0.11.0 (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for torchmetrics>=0.11.0 from https://files.pythonhosted.org/packages/e3/86/47091c33ecf05f8826d134fd518485d4c68ca524c053b2fdd4e041c20547/torchmetrics-1.1.1-py3-none-any.whl.metadata\n", + " Downloading torchmetrics-1.1.1-py3-none-any.whl.metadata (21 kB)\n", + "Collecting transformers>=4.0.1 (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for transformers>=4.0.1 from https://files.pythonhosted.org/packages/13/30/54b59e73400df3de506ad8630284e9fd63f4b94f735423d55fc342181037/transformers-4.33.1-py3-none-any.whl.metadata\n", + " Downloading transformers-4.33.1-py3-none-any.whl.metadata (119 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m119.9/119.9 kB\u001b[0m \u001b[31m12.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting webdataset<=0.1.62,>=0.1.48 (from nemo-toolkit==1.21.0rc0)\n", + " Downloading webdataset-0.1.62-py3-none-any.whl (32 kB)\n", + "Requirement already satisfied: inflect in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (7.0.0)\n", + "Requirement already satisfied: pandas in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (1.5.3)\n", + "Collecting pydantic<2 (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for pydantic<2 from https://files.pythonhosted.org/packages/bc/e0/0371e9b6c910afe502e5fe18cc94562bfd9399617c7b4f5b6e13c29115b3/pydantic-1.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", + " Downloading pydantic-1.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (149 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m149.3/149.3 kB\u001b[0m \u001b[31m17.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting sacremoses>=0.0.43 (from nemo-toolkit==1.21.0rc0)\n", + " Downloading sacremoses-0.0.53.tar.gz (880 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m880.6/880.6 kB\u001b[0m \u001b[31m24.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting sentencepiece<1.0.0 (from nemo-toolkit==1.21.0rc0)\n", + " Downloading sentencepiece-0.1.99-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.3/1.3 MB\u001b[0m \u001b[31m34.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting youtokentome>=1.0.5 (from nemo-toolkit==1.21.0rc0)\n", + " Downloading youtokentome-1.0.6.tar.gz (86 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m86.7/86.7 kB\u001b[0m \u001b[31m11.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting braceexpand (from nemo-toolkit==1.21.0rc0)\n", + " Downloading braceexpand-0.1.7-py2.py3-none-any.whl (5.9 kB)\n", + "Requirement already satisfied: editdistance in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (0.6.2)\n", + "Collecting g2p-en (from nemo-toolkit==1.21.0rc0)\n", + " Downloading g2p_en-2.1.0-py3-none-any.whl (3.1 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.1/3.1 MB\u001b[0m \u001b[31m51.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: ipywidgets in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (7.7.1)\n", + "Collecting jiwer (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for jiwer from https://files.pythonhosted.org/packages/0d/4f/ee537ab20144811dd99321735ff92ef2b3a3230b77ed7454bed4c44d21fc/jiwer-3.0.3-py3-none-any.whl.metadata\n", + " Downloading jiwer-3.0.3-py3-none-any.whl.metadata (2.6 kB)\n", + "Collecting kaldi-python-io (from nemo-toolkit==1.21.0rc0)\n", + " Downloading kaldi-python-io-1.2.2.tar.gz (8.8 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting kaldiio (from nemo-toolkit==1.21.0rc0)\n", + " Downloading kaldiio-2.18.0-py3-none-any.whl (28 kB)\n", + "Requirement already satisfied: librosa>=0.9.0 in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (0.10.1)\n", + "Collecting marshmallow (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for marshmallow from https://files.pythonhosted.org/packages/ed/3c/cebfdcad015240014ff08b883d1c0c427f2ba45ae8c6572851b6ef136cad/marshmallow-3.20.1-py3-none-any.whl.metadata\n", + " Downloading marshmallow-3.20.1-py3-none-any.whl.metadata (7.8 kB)\n", + "Requirement already satisfied: matplotlib in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (3.7.1)\n", + "Requirement already satisfied: packaging in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (23.1)\n", + "Collecting pyannote.core (from nemo-toolkit==1.21.0rc0)\n", + " Downloading pyannote.core-5.0.0-py3-none-any.whl (58 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m58.5/58.5 kB\u001b[0m \u001b[31m7.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting pyannote.metrics (from nemo-toolkit==1.21.0rc0)\n", + " Downloading pyannote.metrics-3.2.1-py3-none-any.whl (51 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m51.4/51.4 kB\u001b[0m \u001b[31m6.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting pydub (from nemo-toolkit==1.21.0rc0)\n", + " Downloading pydub-0.25.1-py2.py3-none-any.whl (32 kB)\n", + "Requirement already satisfied: scipy>=0.14 in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (1.10.1)\n", + "Requirement already satisfied: soundfile in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (0.12.1)\n", + "Collecting sox (from nemo-toolkit==1.21.0rc0)\n", + " Downloading sox-1.4.1-py2.py3-none-any.whl (39 kB)\n", + "Collecting texterrors (from nemo-toolkit==1.21.0rc0)\n", + " Downloading texterrors-0.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.1/1.1 MB\u001b[0m \u001b[31m50.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting boto3 (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for boto3 from https://files.pythonhosted.org/packages/6e/7f/ffb72ddc9f465183af04623baf9d0ea70e73cb0957407e4c333b9e0263fb/boto3-1.28.43-py3-none-any.whl.metadata\n", + " Downloading boto3-1.28.43-py3-none-any.whl.metadata (6.7 kB)\n", + "Collecting datasets (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for datasets from https://files.pythonhosted.org/packages/09/7e/fd4d6441a541dba61d0acb3c1fd5df53214c2e9033854e837a99dd9e0793/datasets-2.14.5-py3-none-any.whl.metadata\n", + " Downloading datasets-2.14.5-py3-none-any.whl.metadata (19 kB)\n", + "Collecting einops (from nemo-toolkit==1.21.0rc0)\n", + " Downloading einops-0.6.1-py3-none-any.whl (42 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m42.2/42.2 kB\u001b[0m \u001b[31m4.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting faiss-cpu (from nemo-toolkit==1.21.0rc0)\n", + " Downloading faiss_cpu-1.7.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (17.6 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m17.6/17.6 MB\u001b[0m \u001b[31m60.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting fasttext (from nemo-toolkit==1.21.0rc0)\n", + " Downloading fasttext-0.9.2.tar.gz (68 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m68.8/68.8 kB\u001b[0m \u001b[31m8.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting flask-restful (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for flask-restful from https://files.pythonhosted.org/packages/d7/7b/f0b45f0df7d2978e5ae51804bb5939b7897b2ace24306009da0cc34d8d1f/Flask_RESTful-0.3.10-py2.py3-none-any.whl.metadata\n", + " Downloading Flask_RESTful-0.3.10-py2.py3-none-any.whl.metadata (1.0 kB)\n", + "Collecting ftfy (from nemo-toolkit==1.21.0rc0)\n", + " Downloading ftfy-6.1.1-py3-none-any.whl (53 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m53.1/53.1 kB\u001b[0m \u001b[31m7.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: gdown in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (4.6.6)\n", + "Requirement already satisfied: h5py in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (3.9.0)\n", + "Collecting ijson (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for ijson from https://files.pythonhosted.org/packages/6b/78/2cbeb7020a7a319d148c92331951cfc710864990e32ff6c7f4859729fb48/ijson-3.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", + " Downloading ijson-3.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (20 kB)\n", + "Requirement already satisfied: jieba in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (0.42.1)\n", + "Collecting markdown2 (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for markdown2 from https://files.pythonhosted.org/packages/f1/98/61276a753f078dd2f3171c9a69fd3f451d220e806b2b1cdca41b8e368b0f/markdown2-2.4.10-py2.py3-none-any.whl.metadata\n", + " Downloading markdown2-2.4.10-py2.py3-none-any.whl.metadata (2.0 kB)\n", + "Collecting megatron-core==0.2.0 (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for megatron-core==0.2.0 from https://files.pythonhosted.org/packages/33/f1/d94f2282b91950e31223efc39138748d71907dbf857e5523d1e73619fd62/megatron_core-0.2.0-py3-none-any.whl.metadata\n", + " Downloading megatron_core-0.2.0-py3-none-any.whl.metadata (1.6 kB)\n", + "Requirement already satisfied: nltk>=3.6.5 in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (3.8.1)\n", + "Collecting opencc (from nemo-toolkit==1.21.0rc0)\n", + " Downloading OpenCC-1.1.6-cp310-cp310-manylinux1_x86_64.whl (778 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m778.3/778.3 kB\u001b[0m \u001b[31m70.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting pangu (from nemo-toolkit==1.21.0rc0)\n", + " Downloading pangu-4.0.6.1-py3-none-any.whl (6.4 kB)\n", + "Collecting rapidfuzz (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for rapidfuzz from https://files.pythonhosted.org/packages/35/04/9ca97b17da457ed294519477da2aad0799c9ba8eebf37761a5ca94c35534/rapidfuzz-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", + " Downloading rapidfuzz-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (11 kB)\n", + "Collecting rouge-score (from nemo-toolkit==1.21.0rc0)\n", + " Downloading rouge_score-0.1.2.tar.gz (17 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting sacrebleu[ja] (from nemo-toolkit==1.21.0rc0)\n", + " Downloading sacrebleu-2.3.1-py3-none-any.whl (118 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m118.9/118.9 kB\u001b[0m \u001b[31m16.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting sentence-transformers (from nemo-toolkit==1.21.0rc0)\n", + " Downloading sentence-transformers-2.2.2.tar.gz (85 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m86.0/86.0 kB\u001b[0m \u001b[31m12.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: tensorstore in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (0.1.41)\n", + "Collecting zarr (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for zarr from https://files.pythonhosted.org/packages/ba/55/0f5ec28561a1698ac5c11edc5724f8c6d48d01baecf740ffd62107d95e7f/zarr-2.16.1-py3-none-any.whl.metadata\n", + " Downloading zarr-2.16.1-py3-none-any.whl.metadata (5.8 kB)\n", + "Collecting attrdict (from nemo-toolkit==1.21.0rc0)\n", + " Downloading attrdict-2.0.1-py2.py3-none-any.whl (9.9 kB)\n", + "Collecting kornia (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for kornia from https://files.pythonhosted.org/packages/55/da/72cb83aa364ebb4d0109965e20c5d33d7063ccab15332c3fd0acfd5609c9/kornia-0.7.0-py2.py3-none-any.whl.metadata\n", + " Downloading kornia-0.7.0-py2.py3-none-any.whl.metadata (12 kB)\n", + "Collecting nemo-text-processing (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for nemo-text-processing from https://files.pythonhosted.org/packages/bd/82/b776d01ba650c3ab42ba5e381e34b35e507a21b16ad1246f51026fa13f0b/nemo_text_processing-0.2.0rc0-py3-none-any.whl.metadata\n", + " Downloading nemo_text_processing-0.2.0rc0-py3-none-any.whl.metadata (7.2 kB)\n", + "Collecting pypinyin (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for pypinyin from https://files.pythonhosted.org/packages/00/fc/3e82bf38739a7b2c4f699245ce6c84ff254723c678c2cdc5d2ecbddf9afb/pypinyin-0.49.0-py2.py3-none-any.whl.metadata\n", + " Downloading pypinyin-0.49.0-py2.py3-none-any.whl.metadata (12 kB)\n", + "Collecting pypinyin-dict (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for pypinyin-dict from https://files.pythonhosted.org/packages/89/31/16c26425685a84191503a226450837e0f4d540c164665d8567f2472861a9/pypinyin_dict-0.6.0-py2.py3-none-any.whl.metadata\n", + " Downloading pypinyin_dict-0.6.0-py2.py3-none-any.whl.metadata (3.6 kB)\n", + "Collecting progress>=1.5 (from nemo-toolkit==1.21.0rc0)\n", + " Downloading progress-1.6.tar.gz (7.8 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: tabulate>=0.8.7 in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (0.9.0)\n", + "Collecting textdistance>=4.1.5 (from nemo-toolkit==1.21.0rc0)\n", + " Downloading textdistance-4.5.0-py3-none-any.whl (31 kB)\n", + "Requirement already satisfied: attrs>=18.1.0 in /usr/local/lib/python3.10/dist-packages (from black==19.10b0->nemo-toolkit==1.21.0rc0) (23.1.0)\n", + "Requirement already satisfied: appdirs in /usr/local/lib/python3.10/dist-packages (from black==19.10b0->nemo-toolkit==1.21.0rc0) (1.4.4)\n", + "Requirement already satisfied: toml>=0.9.4 in /usr/local/lib/python3.10/dist-packages (from black==19.10b0->nemo-toolkit==1.21.0rc0) (0.10.2)\n", + "Collecting typed-ast>=1.4.0 (from black==19.10b0->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for typed-ast>=1.4.0 from https://files.pythonhosted.org/packages/e2/ed/b9b8b794b37b55c9247b1e8d38b0361e8158795c181636d34d6c11b506e7/typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", + " Downloading typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.7 kB)\n", + "Requirement already satisfied: regex in /usr/local/lib/python3.10/dist-packages (from black==19.10b0->nemo-toolkit==1.21.0rc0) (2023.6.3)\n", + "Collecting pathspec<1,>=0.6 (from black==19.10b0->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for pathspec<1,>=0.6 from https://files.pythonhosted.org/packages/b4/2a/9b1be29146139ef459188f5e420a66e835dda921208db600b7037093891f/pathspec-0.11.2-py3-none-any.whl.metadata\n", + " Downloading pathspec-0.11.2-py3-none-any.whl.metadata (19 kB)\n", + "Collecting antlr4-python3-runtime==4.9.* (from hydra-core<=1.3.2,>1.3->nemo-toolkit==1.21.0rc0)\n", + " Downloading antlr4-python3-runtime-4.9.3.tar.gz (117 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m117.0/117.0 kB\u001b[0m \u001b[31m10.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "INFO: pip is looking at multiple versions of jiwer to determine which version is compatible with other requirements. This could take a while.\n", + "Collecting jiwer (from nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for jiwer from https://files.pythonhosted.org/packages/23/a3/92c29a5e422acd87e3b4f2e6dc0ce877070cc9b2f81d30fe84122032338a/jiwer-3.0.2-py3-none-any.whl.metadata\n", + " Downloading jiwer-3.0.2-py3-none-any.whl.metadata (2.6 kB)\n", + " Downloading jiwer-3.0.1-py3-none-any.whl (21 kB)\n", + " Downloading jiwer-3.0.0-py3-none-any.whl (21 kB)\n", + " Downloading jiwer-2.6.0-py3-none-any.whl (20 kB)\n", + " Downloading jiwer-2.5.2-py3-none-any.whl (15 kB)\n", + "Collecting rapidfuzz (from nemo-toolkit==1.21.0rc0)\n", + " Downloading rapidfuzz-2.13.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.2 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.2/2.2 MB\u001b[0m \u001b[31m96.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: audioread>=2.1.9 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (3.0.0)\n", + "Requirement already satisfied: joblib>=0.14 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (1.3.2)\n", + "Requirement already satisfied: decorator>=4.3.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (4.4.2)\n", + "Requirement already satisfied: pooch>=1.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (1.7.0)\n", + "Requirement already satisfied: soxr>=0.3.2 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (0.3.6)\n", + "Requirement already satisfied: typing-extensions>=4.1.1 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (4.7.1)\n", + "Requirement already satisfied: lazy-loader>=0.1 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (0.3)\n", + "Requirement already satisfied: msgpack>=1.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (1.0.5)\n", + "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib->nemo-toolkit==1.21.0rc0) (1.1.0)\n", + "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib->nemo-toolkit==1.21.0rc0) (0.11.0)\n", + "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib->nemo-toolkit==1.21.0rc0) (4.42.1)\n", + "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib->nemo-toolkit==1.21.0rc0) (1.4.5)\n", + "Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib->nemo-toolkit==1.21.0rc0) (9.4.0)\n", + "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib->nemo-toolkit==1.21.0rc0) (3.1.1)\n", + "Requirement already satisfied: llvmlite<0.40,>=0.39.0dev0 in /usr/local/lib/python3.10/dist-packages (from numba->nemo-toolkit==1.21.0rc0) (0.39.1)\n", + "Requirement already satisfied: PyYAML>=5.1.0 in /usr/local/lib/python3.10/dist-packages (from omegaconf<=2.3->nemo-toolkit==1.21.0rc0) (6.0.1)\n", + "Requirement already satisfied: protobuf>=3.20.2 in /usr/local/lib/python3.10/dist-packages (from onnx>=1.7.0->nemo-toolkit==1.21.0rc0) (3.20.3)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil->nemo-toolkit==1.21.0rc0) (1.16.0)\n", + "Requirement already satisfied: fsspec[http]>2021.06.0 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning<=2.0.7,>=2.0->nemo-toolkit==1.21.0rc0) (2023.6.0)\n", + "Collecting lightning-utilities>=0.7.0 (from pytorch-lightning<=2.0.7,>=2.0->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for lightning-utilities>=0.7.0 from https://files.pythonhosted.org/packages/46/ee/8641eeb6a062f383b7d6875604e1f3f83bd2c93a0b4dbcabd3150b32de6e/lightning_utilities-0.9.0-py3-none-any.whl.metadata\n", + " Downloading lightning_utilities-0.9.0-py3-none-any.whl.metadata (4.6 kB)\n", + "Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn->nemo-toolkit==1.21.0rc0) (3.2.0)\n", + "Requirement already satisfied: cffi>=1.0 in /usr/local/lib/python3.10/dist-packages (from soundfile->nemo-toolkit==1.21.0rc0) (1.15.1)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from torch->nemo-toolkit==1.21.0rc0) (3.12.3)\n", + "Requirement already satisfied: sympy in /usr/local/lib/python3.10/dist-packages (from torch->nemo-toolkit==1.21.0rc0) (1.12)\n", + "Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from torch->nemo-toolkit==1.21.0rc0) (3.1)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from torch->nemo-toolkit==1.21.0rc0) (3.1.2)\n", + "Requirement already satisfied: triton==2.0.0 in /usr/local/lib/python3.10/dist-packages (from torch->nemo-toolkit==1.21.0rc0) (2.0.0)\n", + "Requirement already satisfied: cmake in /usr/local/lib/python3.10/dist-packages (from triton==2.0.0->torch->nemo-toolkit==1.21.0rc0) (3.27.2)\n", + "Requirement already satisfied: lit in /usr/local/lib/python3.10/dist-packages (from triton==2.0.0->torch->nemo-toolkit==1.21.0rc0) (16.0.6)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from transformers>=4.0.1->nemo-toolkit==1.21.0rc0) (2.31.0)\n", + "Collecting tokenizers!=0.11.3,<0.14,>=0.11.1 (from transformers>=4.0.1->nemo-toolkit==1.21.0rc0)\n", + " Downloading tokenizers-0.13.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.8 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m7.8/7.8 MB\u001b[0m \u001b[31m81.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting safetensors>=0.3.1 (from transformers>=4.0.1->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for safetensors>=0.3.1 from https://files.pythonhosted.org/packages/6c/f0/c17bbdb1e5f9dab29d44cade445135789f75f8f08ea2728d04493ea8412b/safetensors-0.3.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", + " Downloading safetensors-0.3.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (4.7 kB)\n", + "Collecting botocore<1.32.0,>=1.31.43 (from boto3->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for botocore<1.32.0,>=1.31.43 from https://files.pythonhosted.org/packages/88/37/68fd026cde5d1c802ab34290285c5019e7ba3de3eea8e6c07756cfb827ae/botocore-1.31.43-py3-none-any.whl.metadata\n", + " Downloading botocore-1.31.43-py3-none-any.whl.metadata (6.0 kB)\n", + "Collecting jmespath<2.0.0,>=0.7.1 (from boto3->nemo-toolkit==1.21.0rc0)\n", + " Downloading jmespath-1.0.1-py3-none-any.whl (20 kB)\n", + "Collecting s3transfer<0.7.0,>=0.6.0 (from boto3->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for s3transfer<0.7.0,>=0.6.0 from https://files.pythonhosted.org/packages/d9/17/a3b666f5ef9543cfd3c661d39d1e193abb9649d0cfbbfee3cf3b51d5af02/s3transfer-0.6.2-py3-none-any.whl.metadata\n", + " Downloading s3transfer-0.6.2-py3-none-any.whl.metadata (1.8 kB)\n", + "Requirement already satisfied: pyarrow>=8.0.0 in /usr/local/lib/python3.10/dist-packages (from datasets->nemo-toolkit==1.21.0rc0) (9.0.0)\n", + "Collecting dill<0.3.8,>=0.3.0 (from datasets->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for dill<0.3.8,>=0.3.0 from https://files.pythonhosted.org/packages/f5/3a/74a29b11cf2cdfcd6ba89c0cecd70b37cd1ba7b77978ce611eb7a146a832/dill-0.3.7-py3-none-any.whl.metadata\n", + " Downloading dill-0.3.7-py3-none-any.whl.metadata (9.9 kB)\n", + "Collecting xxhash (from datasets->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for xxhash from https://files.pythonhosted.org/packages/13/c3/e942893f4864a424514c81640f114980cfd5aff7e7414d1e0255f4571111/xxhash-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", + " Downloading xxhash-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (12 kB)\n", + "Collecting multiprocess (from datasets->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for multiprocess from https://files.pythonhosted.org/packages/35/a8/36d8d7b3e46b377800d8dec47891cdf05842d1a2366909ae4a0c89fbc5e6/multiprocess-0.70.15-py310-none-any.whl.metadata\n", + " Downloading multiprocess-0.70.15-py310-none-any.whl.metadata (7.2 kB)\n", + "Requirement already satisfied: aiohttp in /usr/local/lib/python3.10/dist-packages (from datasets->nemo-toolkit==1.21.0rc0) (3.8.5)\n", + "Collecting pybind11>=2.2 (from fasttext->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for pybind11>=2.2 from https://files.pythonhosted.org/packages/06/55/9f73c32dda93fa4f539fafa268f9504e83c489f460c380371d94296126cd/pybind11-2.11.1-py3-none-any.whl.metadata\n", + " Using cached pybind11-2.11.1-py3-none-any.whl.metadata (9.5 kB)\n", + "Collecting aniso8601>=0.82 (from flask-restful->nemo-toolkit==1.21.0rc0)\n", + " Downloading aniso8601-9.0.1-py2.py3-none-any.whl (52 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m52.8/52.8 kB\u001b[0m \u001b[31m7.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: Flask>=0.8 in /usr/local/lib/python3.10/dist-packages (from flask-restful->nemo-toolkit==1.21.0rc0) (2.2.5)\n", + "Requirement already satisfied: pytz in /usr/local/lib/python3.10/dist-packages (from flask-restful->nemo-toolkit==1.21.0rc0) (2023.3.post1)\n", + "Requirement already satisfied: wcwidth>=0.2.5 in /usr/local/lib/python3.10/dist-packages (from ftfy->nemo-toolkit==1.21.0rc0) (0.2.6)\n", + "Collecting distance>=0.1.3 (from g2p-en->nemo-toolkit==1.21.0rc0)\n", + " Downloading Distance-0.1.3.tar.gz (180 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m180.3/180.3 kB\u001b[0m \u001b[31m24.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: beautifulsoup4 in /usr/local/lib/python3.10/dist-packages (from gdown->nemo-toolkit==1.21.0rc0) (4.11.2)\n", + "Requirement already satisfied: ipykernel>=4.5.1 in /usr/local/lib/python3.10/dist-packages (from ipywidgets->nemo-toolkit==1.21.0rc0) (5.5.6)\n", + "Requirement already satisfied: ipython-genutils~=0.2.0 in /usr/local/lib/python3.10/dist-packages (from ipywidgets->nemo-toolkit==1.21.0rc0) (0.2.0)\n", + "Requirement already satisfied: traitlets>=4.3.1 in /usr/local/lib/python3.10/dist-packages (from ipywidgets->nemo-toolkit==1.21.0rc0) (5.7.1)\n", + "Requirement already satisfied: widgetsnbextension~=3.6.0 in /usr/local/lib/python3.10/dist-packages (from ipywidgets->nemo-toolkit==1.21.0rc0) (3.6.5)\n", + "Requirement already satisfied: ipython>=4.0.0 in /usr/local/lib/python3.10/dist-packages (from ipywidgets->nemo-toolkit==1.21.0rc0) (7.34.0)\n", + "Requirement already satisfied: jupyterlab-widgets>=1.0.0 in /usr/local/lib/python3.10/dist-packages (from ipywidgets->nemo-toolkit==1.21.0rc0) (3.0.8)\n", + "Collecting cdifflib (from nemo-text-processing->nemo-toolkit==1.21.0rc0)\n", + " Downloading cdifflib-1.2.6.tar.gz (11 kB)\n", + " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", + " Installing backend dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting pynini==2.1.5 (from nemo-text-processing->nemo-toolkit==1.21.0rc0)\n", + " Downloading pynini-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (161.3 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m161.3/161.3 MB\u001b[0m \u001b[31m6.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: Cython>=0.29 in /usr/local/lib/python3.10/dist-packages (from pynini==2.1.5->nemo-text-processing->nemo-toolkit==1.21.0rc0) (0.29.36)\n", + "Requirement already satisfied: sortedcontainers>=2.0.4 in /usr/local/lib/python3.10/dist-packages (from pyannote.core->nemo-toolkit==1.21.0rc0) (2.4.0)\n", + "Collecting pyannote.database>=4.0.1 (from pyannote.metrics->nemo-toolkit==1.21.0rc0)\n", + " Downloading pyannote.database-5.0.1-py3-none-any.whl (48 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m48.1/48.1 kB\u001b[0m \u001b[31m4.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting docopt>=0.6.2 (from pyannote.metrics->nemo-toolkit==1.21.0rc0)\n", + " Downloading docopt-0.6.2.tar.gz (25 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: iniconfig in /usr/local/lib/python3.10/dist-packages (from pytest->nemo-toolkit==1.21.0rc0) (2.0.0)\n", + "Requirement already satisfied: pluggy<2.0,>=0.12 in /usr/local/lib/python3.10/dist-packages (from pytest->nemo-toolkit==1.21.0rc0) (1.3.0)\n", + "Requirement already satisfied: exceptiongroup>=1.0.0rc8 in /usr/local/lib/python3.10/dist-packages (from pytest->nemo-toolkit==1.21.0rc0) (1.1.3)\n", + "Requirement already satisfied: tomli>=1.0.0 in /usr/local/lib/python3.10/dist-packages (from pytest->nemo-toolkit==1.21.0rc0) (2.0.1)\n", + "Requirement already satisfied: absl-py in /usr/local/lib/python3.10/dist-packages (from rouge-score->nemo-toolkit==1.21.0rc0) (1.4.0)\n", + "Collecting ruamel.yaml.clib>=0.2.7 (from ruamel.yaml->nemo-toolkit==1.21.0rc0)\n", + " Downloading ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl (485 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m485.6/485.6 kB\u001b[0m \u001b[31m49.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting portalocker (from sacrebleu[ja]->nemo-toolkit==1.21.0rc0)\n", + " Downloading portalocker-2.7.0-py2.py3-none-any.whl (15 kB)\n", + "Collecting colorama (from sacrebleu[ja]->nemo-toolkit==1.21.0rc0)\n", + " Downloading colorama-0.4.6-py2.py3-none-any.whl (25 kB)\n", + "Requirement already satisfied: lxml in /usr/local/lib/python3.10/dist-packages (from sacrebleu[ja]->nemo-toolkit==1.21.0rc0) (4.9.3)\n", + "Collecting mecab-python3==1.0.5 (from sacrebleu[ja]->nemo-toolkit==1.21.0rc0)\n", + " Downloading mecab_python3-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (581 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m581.1/581.1 kB\u001b[0m \u001b[31m56.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting ipadic<2.0,>=1.0 (from sacrebleu[ja]->nemo-toolkit==1.21.0rc0)\n", + " Downloading ipadic-1.0.0.tar.gz (13.4 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m13.4/13.4 MB\u001b[0m \u001b[31m96.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: torchvision in /usr/local/lib/python3.10/dist-packages (from sentence-transformers->nemo-toolkit==1.21.0rc0) (0.15.2+cu118)\n", + "Requirement already satisfied: sphinxcontrib-applehelp in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (1.0.7)\n", + "Requirement already satisfied: sphinxcontrib-devhelp in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (1.0.5)\n", + "Requirement already satisfied: sphinxcontrib-jsmath in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (1.0.1)\n", + "Requirement already satisfied: sphinxcontrib-htmlhelp>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (2.0.4)\n", + "Requirement already satisfied: sphinxcontrib-serializinghtml>=1.1.5 in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (1.1.9)\n", + "Requirement already satisfied: sphinxcontrib-qthelp in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (1.0.6)\n", + "Requirement already satisfied: Pygments>=2.0 in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (2.16.1)\n", + "Requirement already satisfied: docutils<0.19,>=0.14 in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (0.18.1)\n", + "Requirement already satisfied: snowballstemmer>=1.1 in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (2.2.0)\n", + "Requirement already satisfied: babel>=1.3 in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (2.12.1)\n", + "Requirement already satisfied: alabaster<0.8,>=0.7 in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (0.7.13)\n", + "Requirement already satisfied: imagesize in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (1.4.1)\n", + "Collecting docutils<0.19,>=0.14 (from sphinx->nemo-toolkit==1.21.0rc0)\n", + " Downloading docutils-0.17.1-py2.py3-none-any.whl (575 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m575.5/575.5 kB\u001b[0m \u001b[31m42.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting pybtex>=0.24 (from sphinxcontrib-bibtex->nemo-toolkit==1.21.0rc0)\n", + " Downloading pybtex-0.24.0-py2.py3-none-any.whl (561 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m561.4/561.4 kB\u001b[0m \u001b[31m48.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting pybtex-docutils>=1.0.0 (from sphinxcontrib-bibtex->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for pybtex-docutils>=1.0.0 from https://files.pythonhosted.org/packages/11/b1/ce1f4596211efb5410e178a803f08e59b20bedb66837dcf41e21c54f9ec1/pybtex_docutils-1.0.3-py3-none-any.whl.metadata\n", + " Downloading pybtex_docutils-1.0.3-py3-none-any.whl.metadata (4.3 kB)\n", + "Requirement already satisfied: grpcio>=1.48.2 in /usr/local/lib/python3.10/dist-packages (from tensorboard->nemo-toolkit==1.21.0rc0) (1.57.0)\n", + "Requirement already satisfied: google-auth<3,>=1.6.3 in /usr/local/lib/python3.10/dist-packages (from tensorboard->nemo-toolkit==1.21.0rc0) (2.17.3)\n", + "Requirement already satisfied: google-auth-oauthlib<1.1,>=0.5 in /usr/local/lib/python3.10/dist-packages (from tensorboard->nemo-toolkit==1.21.0rc0) (1.0.0)\n", + "Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.10/dist-packages (from tensorboard->nemo-toolkit==1.21.0rc0) (3.4.4)\n", + "Requirement already satisfied: tensorboard-data-server<0.8.0,>=0.7.0 in /usr/local/lib/python3.10/dist-packages (from tensorboard->nemo-toolkit==1.21.0rc0) (0.7.1)\n", + "Requirement already satisfied: werkzeug>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from tensorboard->nemo-toolkit==1.21.0rc0) (2.3.7)\n", + "Requirement already satisfied: wheel>=0.26 in /usr/local/lib/python3.10/dist-packages (from tensorboard->nemo-toolkit==1.21.0rc0) (0.41.2)\n", + "Collecting plac (from texterrors->nemo-toolkit==1.21.0rc0)\n", + " Downloading plac-1.3.5-py2.py3-none-any.whl (22 kB)\n", + "Collecting loguru (from texterrors->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for loguru from https://files.pythonhosted.org/packages/19/a9/4e91197b121a41c640367641a510fd9a05bb7a3259fc9678ee2976c8fd00/loguru-0.7.1-py3-none-any.whl.metadata\n", + " Downloading loguru-0.7.1-py3-none-any.whl.metadata (22 kB)\n", + "Requirement already satisfied: termcolor in /usr/local/lib/python3.10/dist-packages (from texterrors->nemo-toolkit==1.21.0rc0) (2.3.0)\n", + "Collecting Levenshtein (from texterrors->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for Levenshtein from https://files.pythonhosted.org/packages/e6/02/0a4ed6a9e2b78f6b57f25a87fc194d7d10c2bbe95d985f36390e86285232/Levenshtein-0.21.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", + " Downloading Levenshtein-0.21.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (3.4 kB)\n", + "Collecting GitPython!=3.1.29,>=1.0.0 (from wandb->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for GitPython!=3.1.29,>=1.0.0 from https://files.pythonhosted.org/packages/0f/c6/bb9e2276b6fed126aa21e292493b45a3df4cfba7cbfcf2ab8809a6b0e718/GitPython-3.1.35-py3-none-any.whl.metadata\n", + " Downloading GitPython-3.1.35-py3-none-any.whl.metadata (10 kB)\n", + "Requirement already satisfied: psutil>=5.0.0 in /usr/local/lib/python3.10/dist-packages (from wandb->nemo-toolkit==1.21.0rc0) (5.9.5)\n", + "Collecting sentry-sdk>=1.0.0 (from wandb->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for sentry-sdk>=1.0.0 from https://files.pythonhosted.org/packages/17/22/dbd5f854f373214d48585eeb6844e50a8dd1600f435d9033493f76f66dfa/sentry_sdk-1.30.0-py2.py3-none-any.whl.metadata\n", + " Downloading sentry_sdk-1.30.0-py2.py3-none-any.whl.metadata (9.6 kB)\n", + "Collecting docker-pycreds>=0.4.0 (from wandb->nemo-toolkit==1.21.0rc0)\n", + " Downloading docker_pycreds-0.4.0-py2.py3-none-any.whl (9.0 kB)\n", + "Collecting pathtools (from wandb->nemo-toolkit==1.21.0rc0)\n", + " Downloading pathtools-0.1.2.tar.gz (11 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting setproctitle (from wandb->nemo-toolkit==1.21.0rc0)\n", + " Downloading setproctitle-1.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (30 kB)\n", + "Collecting asciitree (from zarr->nemo-toolkit==1.21.0rc0)\n", + " Downloading asciitree-0.3.3.tar.gz (4.0 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting fasteners (from zarr->nemo-toolkit==1.21.0rc0)\n", + " Downloading fasteners-0.18-py3-none-any.whl (18 kB)\n", + "Collecting numcodecs>=0.10.0 (from zarr->nemo-toolkit==1.21.0rc0)\n", + " Downloading numcodecs-0.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.7 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m6.7/6.7 MB\u001b[0m \u001b[31m116.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting urllib3<1.27,>=1.25.4 (from botocore<1.32.0,>=1.31.43->boto3->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for urllib3<1.27,>=1.25.4 from https://files.pythonhosted.org/packages/c5/05/c214b32d21c0b465506f95c4f28ccbcba15022e000b043b72b3df7728471/urllib3-1.26.16-py2.py3-none-any.whl.metadata\n", + " Downloading urllib3-1.26.16-py2.py3-none-any.whl.metadata (48 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m48.4/48.4 kB\u001b[0m \u001b[31m6.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: pycparser in /usr/local/lib/python3.10/dist-packages (from cffi>=1.0->soundfile->nemo-toolkit==1.21.0rc0) (2.21)\n", + "Requirement already satisfied: itsdangerous>=2.0 in /usr/local/lib/python3.10/dist-packages (from Flask>=0.8->flask-restful->nemo-toolkit==1.21.0rc0) (2.1.2)\n", + "Requirement already satisfied: charset-normalizer<4.0,>=2.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets->nemo-toolkit==1.21.0rc0) (3.2.0)\n", + "Requirement already satisfied: multidict<7.0,>=4.5 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets->nemo-toolkit==1.21.0rc0) (6.0.4)\n", + "Requirement already satisfied: async-timeout<5.0,>=4.0.0a3 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets->nemo-toolkit==1.21.0rc0) (4.0.3)\n", + "Requirement already satisfied: yarl<2.0,>=1.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets->nemo-toolkit==1.21.0rc0) (1.9.2)\n", + "Requirement already satisfied: frozenlist>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets->nemo-toolkit==1.21.0rc0) (1.4.0)\n", + "Requirement already satisfied: aiosignal>=1.1.2 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets->nemo-toolkit==1.21.0rc0) (1.3.1)\n", + "Collecting gitdb<5,>=4.0.1 (from GitPython!=3.1.29,>=1.0.0->wandb->nemo-toolkit==1.21.0rc0)\n", + " Downloading gitdb-4.0.10-py3-none-any.whl (62 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m62.7/62.7 kB\u001b[0m \u001b[31m8.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: cachetools<6.0,>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from google-auth<3,>=1.6.3->tensorboard->nemo-toolkit==1.21.0rc0) (5.3.1)\n", + "Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.10/dist-packages (from google-auth<3,>=1.6.3->tensorboard->nemo-toolkit==1.21.0rc0) (0.3.0)\n", + "Requirement already satisfied: rsa<5,>=3.1.4 in /usr/local/lib/python3.10/dist-packages (from google-auth<3,>=1.6.3->tensorboard->nemo-toolkit==1.21.0rc0) (4.9)\n", + "Requirement already satisfied: requests-oauthlib>=0.7.0 in /usr/local/lib/python3.10/dist-packages (from google-auth-oauthlib<1.1,>=0.5->tensorboard->nemo-toolkit==1.21.0rc0) (1.3.1)\n", + "Requirement already satisfied: jupyter-client in /usr/local/lib/python3.10/dist-packages (from ipykernel>=4.5.1->ipywidgets->nemo-toolkit==1.21.0rc0) (6.1.12)\n", + "Requirement already satisfied: tornado>=4.2 in /usr/local/lib/python3.10/dist-packages (from ipykernel>=4.5.1->ipywidgets->nemo-toolkit==1.21.0rc0) (6.3.2)\n", + "Collecting jedi>=0.16 (from ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for jedi>=0.16 from https://files.pythonhosted.org/packages/8e/46/7e3ae3aa2dcfcffc5138c6cef5448523218658411c84a2000bf75c8d3ec1/jedi-0.19.0-py2.py3-none-any.whl.metadata\n", + " Downloading jedi-0.19.0-py2.py3-none-any.whl.metadata (22 kB)\n", + "Requirement already satisfied: pickleshare in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.7.5)\n", + "Requirement already satisfied: prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0) (3.0.39)\n", + "Requirement already satisfied: backcall in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.2.0)\n", + "Requirement already satisfied: matplotlib-inline in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.1.6)\n", + "Requirement already satisfied: pexpect>4.3 in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0) (4.8.0)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->torch->nemo-toolkit==1.21.0rc0) (2.1.3)\n", + "Requirement already satisfied: entrypoints in /usr/local/lib/python3.10/dist-packages (from numcodecs>=0.10.0->zarr->nemo-toolkit==1.21.0rc0) (0.4)\n", + "Requirement already satisfied: platformdirs>=2.5.0 in /usr/local/lib/python3.10/dist-packages (from pooch>=1.0->librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (3.10.0)\n", + "Requirement already satisfied: typer[all]>=0.2.1 in /usr/local/lib/python3.10/dist-packages (from pyannote.database>=4.0.1->pyannote.metrics->nemo-toolkit==1.21.0rc0) (0.9.0)\n", + "Collecting latexcodec>=1.0.4 (from pybtex>=0.24->sphinxcontrib-bibtex->nemo-toolkit==1.21.0rc0)\n", + " Downloading latexcodec-2.0.1-py2.py3-none-any.whl (18 kB)\n", + "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests->transformers>=4.0.1->nemo-toolkit==1.21.0rc0) (3.4)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests->transformers>=4.0.1->nemo-toolkit==1.21.0rc0) (2023.7.22)\n", + "Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.10/dist-packages (from sympy->torch->nemo-toolkit==1.21.0rc0) (1.3.0)\n", + "Requirement already satisfied: notebook>=4.4.1 in /usr/local/lib/python3.10/dist-packages (from widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (6.5.5)\n", + "Requirement already satisfied: soupsieve>1.2 in /usr/local/lib/python3.10/dist-packages (from beautifulsoup4->gdown->nemo-toolkit==1.21.0rc0) (2.5)\n", + "Requirement already satisfied: PySocks!=1.5.7,>=1.5.6 in /usr/local/lib/python3.10/dist-packages (from requests->transformers>=4.0.1->nemo-toolkit==1.21.0rc0) (1.7.1)\n", + "Collecting smmap<6,>=3.0.1 (from gitdb<5,>=4.0.1->GitPython!=3.1.29,>=1.0.0->wandb->nemo-toolkit==1.21.0rc0)\n", + " Downloading smmap-5.0.0-py3-none-any.whl (24 kB)\n", + "Requirement already satisfied: parso<0.9.0,>=0.8.3 in /usr/local/lib/python3.10/dist-packages (from jedi>=0.16->ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.8.3)\n", + "Requirement already satisfied: pyzmq<25,>=17 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (23.2.1)\n", + "Requirement already satisfied: argon2-cffi in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (23.1.0)\n", + "Requirement already satisfied: jupyter-core>=4.6.1 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (5.3.1)\n", + "Requirement already satisfied: nbformat in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (5.9.2)\n", + "Requirement already satisfied: nbconvert>=5 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (6.5.4)\n", + "Requirement already satisfied: nest-asyncio>=1.5 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.5.7)\n", + "Requirement already satisfied: Send2Trash>=1.8.0 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.8.2)\n", + "Requirement already satisfied: terminado>=0.8.3 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.17.1)\n", + "Requirement already satisfied: prometheus-client in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.17.1)\n", + "Requirement already satisfied: nbclassic>=0.4.7 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.0.0)\n", + "Requirement already satisfied: ptyprocess>=0.5 in /usr/local/lib/python3.10/dist-packages (from pexpect>4.3->ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.7.0)\n", + "Requirement already satisfied: pyasn1<0.6.0,>=0.4.6 in /usr/local/lib/python3.10/dist-packages (from pyasn1-modules>=0.2.1->google-auth<3,>=1.6.3->tensorboard->nemo-toolkit==1.21.0rc0) (0.5.0)\n", + "Requirement already satisfied: oauthlib>=3.0.0 in /usr/local/lib/python3.10/dist-packages (from requests-oauthlib>=0.7.0->google-auth-oauthlib<1.1,>=0.5->tensorboard->nemo-toolkit==1.21.0rc0) (3.2.2)\n", + "Collecting shellingham<2.0.0,>=1.3.0 (from typer[all]>=0.2.1->pyannote.database>=4.0.1->pyannote.metrics->nemo-toolkit==1.21.0rc0)\n", + " Obtaining dependency information for shellingham<2.0.0,>=1.3.0 from https://files.pythonhosted.org/packages/57/70/0265437683625b2e6491736706d3d679d90e2a26f6bff59f4e46e09872b9/shellingham-1.5.3-py2.py3-none-any.whl.metadata\n", + " Downloading shellingham-1.5.3-py2.py3-none-any.whl.metadata (3.4 kB)\n", + "Requirement already satisfied: rich<14.0.0,>=10.11.0 in /usr/local/lib/python3.10/dist-packages (from typer[all]>=0.2.1->pyannote.database>=4.0.1->pyannote.metrics->nemo-toolkit==1.21.0rc0) (13.5.2)\n", + "Requirement already satisfied: jupyter-server>=1.8 in /usr/local/lib/python3.10/dist-packages (from nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.24.0)\n", + "Requirement already satisfied: notebook-shim>=0.2.3 in /usr/local/lib/python3.10/dist-packages (from nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.2.3)\n", + "Requirement already satisfied: bleach in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (6.0.0)\n", + "Requirement already satisfied: defusedxml in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.7.1)\n", + "Requirement already satisfied: jupyterlab-pygments in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.2.2)\n", + "Requirement already satisfied: mistune<2,>=0.8.1 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.8.4)\n", + "Requirement already satisfied: nbclient>=0.5.0 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.8.0)\n", + "Requirement already satisfied: pandocfilters>=1.4.1 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.5.0)\n", + "Requirement already satisfied: tinycss2 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.2.1)\n", + "Requirement already satisfied: fastjsonschema in /usr/local/lib/python3.10/dist-packages (from nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (2.18.0)\n", + "Requirement already satisfied: jsonschema>=2.6 in /usr/local/lib/python3.10/dist-packages (from nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (4.19.0)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.10/dist-packages (from rich<14.0.0,>=10.11.0->typer[all]>=0.2.1->pyannote.database>=4.0.1->pyannote.metrics->nemo-toolkit==1.21.0rc0) (3.0.0)\n", + "Requirement already satisfied: argon2-cffi-bindings in /usr/local/lib/python3.10/dist-packages (from argon2-cffi->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (21.2.0)\n", + "Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=2.6->nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (2023.7.1)\n", + "Requirement already satisfied: referencing>=0.28.4 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=2.6->nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.30.2)\n", + "Requirement already satisfied: rpds-py>=0.7.1 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=2.6->nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.10.2)\n", + "Requirement already satisfied: anyio<4,>=3.1.0 in /usr/local/lib/python3.10/dist-packages (from jupyter-server>=1.8->nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (3.7.1)\n", + "Requirement already satisfied: websocket-client in /usr/local/lib/python3.10/dist-packages (from jupyter-server>=1.8->nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.6.2)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0->rich<14.0.0,>=10.11.0->typer[all]>=0.2.1->pyannote.database>=4.0.1->pyannote.metrics->nemo-toolkit==1.21.0rc0) (0.1.2)\n", + "Requirement already satisfied: webencodings in /usr/local/lib/python3.10/dist-packages (from bleach->nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.5.1)\n", + "Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.10/dist-packages (from anyio<4,>=3.1.0->jupyter-server>=1.8->nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.3.0)\n", + "Downloading megatron_core-0.2.0-py3-none-any.whl (46 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m46.0/46.0 kB\u001b[0m \u001b[31m4.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading onnx-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (14.6 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m14.6/14.6 MB\u001b[0m \u001b[31m71.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading pydantic-1.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.1 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.1/3.1 MB\u001b[0m \u001b[31m81.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading pytorch_lightning-2.0.7-py3-none-any.whl (724 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m725.0/725.0 kB\u001b[0m \u001b[31m55.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading torchmetrics-1.1.1-py3-none-any.whl (763 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m763.4/763.4 kB\u001b[0m \u001b[31m51.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading transformers-4.33.1-py3-none-any.whl (7.6 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m7.6/7.6 MB\u001b[0m \u001b[31m90.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading huggingface_hub-0.16.4-py3-none-any.whl (268 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m268.8/268.8 kB\u001b[0m \u001b[31m31.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading boto3-1.28.43-py3-none-any.whl (135 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m135.8/135.8 kB\u001b[0m \u001b[31m18.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading datasets-2.14.5-py3-none-any.whl (519 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m519.6/519.6 kB\u001b[0m \u001b[31m48.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading Flask_RESTful-0.3.10-py2.py3-none-any.whl (26 kB)\n", + "Downloading ijson-3.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (111 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m111.8/111.8 kB\u001b[0m \u001b[31m14.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading kornia-0.7.0-py2.py3-none-any.whl (705 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m705.7/705.7 kB\u001b[0m \u001b[31m35.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading markdown2-2.4.10-py2.py3-none-any.whl (39 kB)\n", + "Downloading marshmallow-3.20.1-py3-none-any.whl (49 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m49.4/49.4 kB\u001b[0m \u001b[31m6.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nemo_text_processing-0.2.0rc0-py3-none-any.whl (2.4 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.4/2.4 MB\u001b[0m \u001b[31m50.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading pypinyin-0.49.0-py2.py3-none-any.whl (1.4 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.4/1.4 MB\u001b[0m \u001b[31m69.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading pypinyin_dict-0.6.0-py2.py3-none-any.whl (9.5 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m9.5/9.5 MB\u001b[0m \u001b[31m86.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading ruamel.yaml-0.17.32-py3-none-any.whl (112 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m112.2/112.2 kB\u001b[0m \u001b[31m11.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading sphinxcontrib_bibtex-2.6.1-py3-none-any.whl (40 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m40.9/40.9 kB\u001b[0m \u001b[31m5.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading wandb-0.15.10-py3-none-any.whl (2.1 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.1/2.1 MB\u001b[0m \u001b[31m76.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading zarr-2.16.1-py3-none-any.whl (206 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m206.9/206.9 kB\u001b[0m \u001b[31m21.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading botocore-1.31.43-py3-none-any.whl (11.2 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m11.2/11.2 MB\u001b[0m \u001b[31m87.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading dill-0.3.7-py3-none-any.whl (115 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m115.3/115.3 kB\u001b[0m \u001b[31m14.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading GitPython-3.1.35-py3-none-any.whl (188 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m188.8/188.8 kB\u001b[0m \u001b[31m23.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading lightning_utilities-0.9.0-py3-none-any.whl (23 kB)\n", + "Downloading pathspec-0.11.2-py3-none-any.whl (29 kB)\n", + "Using cached pybind11-2.11.1-py3-none-any.whl (227 kB)\n", + "Downloading pybtex_docutils-1.0.3-py3-none-any.whl (6.4 kB)\n", + "Downloading s3transfer-0.6.2-py3-none-any.whl (79 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m79.8/79.8 kB\u001b[0m \u001b[31m10.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading safetensors-0.3.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.3/1.3 MB\u001b[0m \u001b[31m77.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading sentry_sdk-1.30.0-py2.py3-none-any.whl (218 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m218.8/218.8 kB\u001b[0m \u001b[31m26.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (824 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m824.7/824.7 kB\u001b[0m \u001b[31m52.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading Levenshtein-0.21.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (172 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m172.5/172.5 kB\u001b[0m \u001b[31m21.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading loguru-0.7.1-py3-none-any.whl (61 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m61.4/61.4 kB\u001b[0m \u001b[31m8.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading multiprocess-0.70.15-py310-none-any.whl (134 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m134.8/134.8 kB\u001b[0m \u001b[31m16.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading xxhash-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (194 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m194.1/194.1 kB\u001b[0m \u001b[31m14.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading jedi-0.19.0-py2.py3-none-any.whl (1.6 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.6/1.6 MB\u001b[0m \u001b[31m66.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading urllib3-1.26.16-py2.py3-none-any.whl (143 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m143.1/143.1 kB\u001b[0m \u001b[31m18.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading shellingham-1.5.3-py2.py3-none-any.whl (9.7 kB)\n", + "Building wheels for collected packages: antlr4-python3-runtime, progress, sacremoses, youtokentome, fasttext, kaldi-python-io, nemo-toolkit, rouge-score, sentence-transformers, wget, distance, docopt, ipadic, asciitree, cdifflib, pathtools\n", + " Building wheel for antlr4-python3-runtime (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for antlr4-python3-runtime: filename=antlr4_python3_runtime-4.9.3-py3-none-any.whl size=144554 sha256=ad5d3c0bd8ee550570d532ca332fe7204baee65f2066eefcf5fc70cd2afdb382\n", + " Stored in directory: /root/.cache/pip/wheels/12/93/dd/1f6a127edc45659556564c5730f6d4e300888f4bca2d4c5a88\n", + " Building wheel for progress (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for progress: filename=progress-1.6-py3-none-any.whl size=9610 sha256=79cc5bc84a633414a3856bc3a35542e0489aaffa62e7cdae95e51474a38f064c\n", + " Stored in directory: /root/.cache/pip/wheels/a2/68/5f/c339b20a41659d856c93ccdce6a33095493eb82c3964aac5a1\n", + " Building wheel for sacremoses (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for sacremoses: filename=sacremoses-0.0.53-py3-none-any.whl size=895241 sha256=1fd4f16d193691e8ff7869591f5d11f5a32e21289fbb9a8422a800bc1bf781c7\n", + " Stored in directory: /root/.cache/pip/wheels/00/24/97/a2ea5324f36bc626e1ea0267f33db6aa80d157ee977e9e42fb\n", + " Building wheel for youtokentome (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for youtokentome: filename=youtokentome-1.0.6-cp310-cp310-linux_x86_64.whl size=1883668 sha256=d3e4925cf4b245a76184b4afe612a4fffd683de42889b5c1a07ca44be98dde9b\n", + " Stored in directory: /root/.cache/pip/wheels/df/85/f8/301d2ba45f43f30bed2fe413efa760bc726b8b660ed9c2900c\n", + " Building wheel for fasttext (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for fasttext: filename=fasttext-0.9.2-cp310-cp310-linux_x86_64.whl size=4199767 sha256=5bcc699963ee85f0f02846c1e278140b3ba3d95a7877d60b48c5b10f8f424bb1\n", + " Stored in directory: /root/.cache/pip/wheels/a5/13/75/f811c84a8ab36eedbaef977a6a58a98990e8e0f1967f98f394\n", + " Building wheel for kaldi-python-io (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for kaldi-python-io: filename=kaldi_python_io-1.2.2-py3-none-any.whl size=8948 sha256=d2e946cb4c2298a69803c0492c1533a3d08d313ced846ba7f1555a11ab164bd6\n", + " Stored in directory: /root/.cache/pip/wheels/b7/23/5f/49d3a826be576faf61d84e8028e1914bb36a5586ee2613b087\n", + " Building editable for nemo-toolkit (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for nemo-toolkit: filename=nemo_toolkit-1.21.0rc0-0.editable-py3-none-any.whl size=9181 sha256=d5c26c7630db00d8c6a483e19f89aa11a4d7a11920dcb052d8bb6a56689eb77d\n", + " Stored in directory: /tmp/pip-ephem-wheel-cache-4nra31ak/wheels/41/a7/71/00ccdddfb43c015e8d025853cafb90117dd722fd8ec557581b\n", + " Building wheel for rouge-score (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for rouge-score: filename=rouge_score-0.1.2-py3-none-any.whl size=24932 sha256=7f61e2fe8b2f326d21f3a0a06e5b58e2f1103a5523bac58ff728481bfdf203d8\n", + " Stored in directory: /root/.cache/pip/wheels/5f/dd/89/461065a73be61a532ff8599a28e9beef17985c9e9c31e541b4\n", + " Building wheel for sentence-transformers (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for sentence-transformers: filename=sentence_transformers-2.2.2-py3-none-any.whl size=125923 sha256=b4fb59313f79b135e68441f9b4780a35d2658bac193693e6177887cb018824a7\n", + " Stored in directory: /root/.cache/pip/wheels/62/f2/10/1e606fd5f02395388f74e7462910fe851042f97238cbbd902f\n", + " Building wheel for wget (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for wget: filename=wget-3.2-py3-none-any.whl size=9655 sha256=dd1cfe282f727f91b5a5db464ae58f43372f62a80d0a083733b36c68edc6ee76\n", + " Stored in directory: /root/.cache/pip/wheels/8b/f1/7f/5c94f0a7a505ca1c81cd1d9208ae2064675d97582078e6c769\n", + " Building wheel for distance (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for distance: filename=Distance-0.1.3-py3-none-any.whl size=16258 sha256=287628136639656b068a6f2e67ab0f5a80bc4ad466590f19499978136c5e3726\n", + " Stored in directory: /root/.cache/pip/wheels/e8/bb/de/f71bf63559ea9a921059a5405806f7ff6ed612a9231c4a9309\n", + " Building wheel for docopt (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for docopt: filename=docopt-0.6.2-py2.py3-none-any.whl size=13705 sha256=9af9a132b49b73c475f9838a56ff79e968af41bb509c8b38b81f7c08c3da87dd\n", + " Stored in directory: /root/.cache/pip/wheels/fc/ab/d4/5da2067ac95b36618c629a5f93f809425700506f72c9732fac\n", + " Building wheel for ipadic (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for ipadic: filename=ipadic-1.0.0-py3-none-any.whl size=13556703 sha256=9e40daac3101fb3d52fcf9c986017638569c576c3be36805d1437e722a2d8803\n", + " Stored in directory: /root/.cache/pip/wheels/5b/ea/e3/2f6e0860a327daba3b030853fce4483ed37468bbf1101c59c3\n", + " Building wheel for asciitree (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for asciitree: filename=asciitree-0.3.3-py3-none-any.whl size=5034 sha256=69d1478059cdad8cb54b8a1a29e66bd3229cf3458dcf96aeedb978381d8116f3\n", + " Stored in directory: /root/.cache/pip/wheels/7f/4e/be/1171b40f43b918087657ec57cf3b81fa1a2e027d8755baa184\n", + " Building wheel for cdifflib (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for cdifflib: filename=cdifflib-1.2.6-cp310-cp310-linux_x86_64.whl size=27681 sha256=f4ccba50f62b7265ea20b84bac762b6ed3dacae8d42ea7346d54e07da2a6ad9e\n", + " Stored in directory: /root/.cache/pip/wheels/87/a7/fd/8061e24ed08689045cb6d1ca303768dc463b20a5a338174841\n", + " Building wheel for pathtools (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for pathtools: filename=pathtools-0.1.2-py3-none-any.whl size=8791 sha256=95b669a715a9eefa4cd1222ac5c899a97064dbb765dccbd3a5b35c0208aa1389\n", + " Stored in directory: /root/.cache/pip/wheels/e7/f3/22/152153d6eb222ee7a56ff8617d80ee5207207a8c00a7aab794\n", + "Successfully built antlr4-python3-runtime progress sacremoses youtokentome fasttext kaldi-python-io nemo-toolkit rouge-score sentence-transformers wget distance docopt ipadic asciitree cdifflib pathtools\n", + "Installing collected packages: wget, tokenizers, sentencepiece, safetensors, pydub, progress, plac, pathtools, pangu, opencc, mecab-python3, ipadic, ijson, faiss-cpu, docopt, distance, braceexpand, asciitree, antlr4-python3-runtime, aniso8601, xxhash, webdataset, urllib3, typed-ast, textdistance, sox, smmap, shellingham, setuptools, setproctitle, ruamel.yaml.clib, rapidfuzz, pytest-runner, pypinyin, pynini, pydantic, pybind11, portalocker, pathspec, parameterized, onnx, omegaconf, numcodecs, marshmallow, markdown2, loguru, lightning-utilities, latexcodec, kaldiio, kaldi-python-io, jmespath, jedi, isort, ftfy, fasteners, einops, docutils, docker-pycreds, dill, colorama, click, cdifflib, attrdict, zarr, youtokentome, sentry-sdk, sacremoses, sacrebleu, ruamel.yaml, pypinyin-dict, pybtex, pyannote.core, multiprocess, Levenshtein, jiwer, hydra-core, gitdb, fasttext, botocore, black, texterrors, s3transfer, rouge-score, pybtex-docutils, huggingface-hub, GitPython, g2p-en, flask-restful, wandb, transformers, pyannote.database, datasets, boto3, pyannote.metrics, nemo-text-processing, torchmetrics, sphinxcontrib-bibtex, sentence-transformers, pytorch-lightning, nemo-toolkit, megatron-core, kornia\n", + " Attempting uninstall: urllib3\n", + " Found existing installation: urllib3 2.0.4\n", + " Uninstalling urllib3-2.0.4:\n", + " Successfully uninstalled urllib3-2.0.4\n", + " Attempting uninstall: setuptools\n", + " Found existing installation: setuptools 67.7.2\n", + " Uninstalling setuptools-67.7.2:\n", + " Successfully uninstalled setuptools-67.7.2\n", + " Attempting uninstall: pydantic\n", + " Found existing installation: pydantic 2.3.0\n", + " Uninstalling pydantic-2.3.0:\n", + " Successfully uninstalled pydantic-2.3.0\n", + " Attempting uninstall: docutils\n", + " Found existing installation: docutils 0.18.1\n", + " Uninstalling docutils-0.18.1:\n", + " Successfully uninstalled docutils-0.18.1\n", + " Attempting uninstall: click\n", + " Found existing installation: click 8.1.7\n", + " Uninstalling click-8.1.7:\n", + " Successfully uninstalled click-8.1.7\n", + "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", + "cvxpy 1.3.2 requires setuptools>65.5.1, but you have setuptools 65.5.1 which is incompatible.\u001b[0m\u001b[31m\n", + "\u001b[0mSuccessfully installed GitPython-3.1.35 Levenshtein-0.21.1 aniso8601-9.0.1 antlr4-python3-runtime-4.9.3 asciitree-0.3.3 attrdict-2.0.1 black-19.10b0 boto3-1.28.43 botocore-1.31.43 braceexpand-0.1.7 cdifflib-1.2.6 click-8.0.2 colorama-0.4.6 datasets-2.14.5 dill-0.3.7 distance-0.1.3 docker-pycreds-0.4.0 docopt-0.6.2 docutils-0.17.1 einops-0.6.1 faiss-cpu-1.7.4 fasteners-0.18 fasttext-0.9.2 flask-restful-0.3.10 ftfy-6.1.1 g2p-en-2.1.0 gitdb-4.0.10 huggingface-hub-0.16.4 hydra-core-1.3.2 ijson-3.2.3 ipadic-1.0.0 isort-5.12.0 jedi-0.19.0 jiwer-2.5.2 jmespath-1.0.1 kaldi-python-io-1.2.2 kaldiio-2.18.0 kornia-0.7.0 latexcodec-2.0.1 lightning-utilities-0.9.0 loguru-0.7.1 markdown2-2.4.10 marshmallow-3.20.1 mecab-python3-1.0.5 megatron-core-0.2.0 multiprocess-0.70.15 nemo-text-processing-0.2.0rc0 nemo-toolkit-1.21.0rc0 numcodecs-0.11.0 omegaconf-2.3.0 onnx-1.14.1 opencc-1.1.6 pangu-4.0.6.1 parameterized-0.9.0 pathspec-0.11.2 pathtools-0.1.2 plac-1.3.5 portalocker-2.7.0 progress-1.6 pyannote.core-5.0.0 pyannote.database-5.0.1 pyannote.metrics-3.2.1 pybind11-2.11.1 pybtex-0.24.0 pybtex-docutils-1.0.3 pydantic-1.10.12 pydub-0.25.1 pynini-2.1.5 pypinyin-0.49.0 pypinyin-dict-0.6.0 pytest-runner-6.0.0 pytorch-lightning-2.0.7 rapidfuzz-2.13.7 rouge-score-0.1.2 ruamel.yaml-0.17.32 ruamel.yaml.clib-0.2.7 s3transfer-0.6.2 sacrebleu-2.3.1 sacremoses-0.0.53 safetensors-0.3.3 sentence-transformers-2.2.2 sentencepiece-0.1.99 sentry-sdk-1.30.0 setproctitle-1.3.2 setuptools-65.5.1 shellingham-1.5.3 smmap-5.0.0 sox-1.4.1 sphinxcontrib-bibtex-2.6.1 textdistance-4.5.0 texterrors-0.4.4 tokenizers-0.13.3 torchmetrics-1.1.1 transformers-4.33.1 typed-ast-1.5.5 urllib3-1.26.16 wandb-0.15.10 webdataset-0.1.62 wget-3.2 xxhash-3.3.0 youtokentome-1.0.6 zarr-2.16.1\n", + "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n", + "\u001b[0mAll done!\n", + "Reading package lists... Done\n", + "Building dependency tree... Done\n", + "Reading state information... Done\n", + "The following additional packages will be installed:\n", + " libopencore-amrnb0 libopencore-amrwb0 libsox-fmt-alsa libsox-fmt-base\n", + " libsox3 libwavpack1\n", + "Suggested packages:\n", + " libsox-fmt-all\n", + "The following NEW packages will be installed:\n", + " libopencore-amrnb0 libopencore-amrwb0 libsox-fmt-alsa libsox-fmt-base\n", + " libsox3 libwavpack1 sox\n", + "0 upgraded, 7 newly installed, 0 to remove and 16 not upgraded.\n", + "Need to get 617 kB of archives.\n", + "After this operation, 1,764 kB of additional disk space will be used.\n", + "Get:1 http://archive.ubuntu.com/ubuntu jammy/universe amd64 libopencore-amrnb0 amd64 0.1.5-1 [94.8 kB]\n", + "Get:2 http://archive.ubuntu.com/ubuntu jammy/universe amd64 libopencore-amrwb0 amd64 0.1.5-1 [49.1 kB]\n", + "Get:3 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 libsox3 amd64 14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1 [240 kB]\n", + "Get:4 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 libsox-fmt-alsa amd64 14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1 [11.2 kB]\n", + "Get:5 http://archive.ubuntu.com/ubuntu jammy/main amd64 libwavpack1 amd64 5.4.0-1build2 [83.7 kB]\n", + "Get:6 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 libsox-fmt-base amd64 14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1 [33.7 kB]\n", + "Get:7 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 sox amd64 14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1 [104 kB]\n", + "Fetched 617 kB in 0s (5,224 kB/s)\n", + "Selecting previously unselected package libopencore-amrnb0:amd64.\n", + "(Reading database ... 120901 files and directories currently installed.)\n", + "Preparing to unpack .../0-libopencore-amrnb0_0.1.5-1_amd64.deb ...\n", + "Unpacking libopencore-amrnb0:amd64 (0.1.5-1) ...\n", + "Selecting previously unselected package libopencore-amrwb0:amd64.\n", + "Preparing to unpack .../1-libopencore-amrwb0_0.1.5-1_amd64.deb ...\n", + "Unpacking libopencore-amrwb0:amd64 (0.1.5-1) ...\n", + "Selecting previously unselected package libsox3:amd64.\n", + "Preparing to unpack .../2-libsox3_14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1_amd64.deb ...\n", + "Unpacking libsox3:amd64 (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", + "Selecting previously unselected package libsox-fmt-alsa:amd64.\n", + "Preparing to unpack .../3-libsox-fmt-alsa_14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1_amd64.deb ...\n", + "Unpacking libsox-fmt-alsa:amd64 (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", + "Selecting previously unselected package libwavpack1:amd64.\n", + "Preparing to unpack .../4-libwavpack1_5.4.0-1build2_amd64.deb ...\n", + "Unpacking libwavpack1:amd64 (5.4.0-1build2) ...\n", + "Selecting previously unselected package libsox-fmt-base:amd64.\n", + "Preparing to unpack .../5-libsox-fmt-base_14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1_amd64.deb ...\n", + "Unpacking libsox-fmt-base:amd64 (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", + "Selecting previously unselected package sox.\n", + "Preparing to unpack .../6-sox_14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1_amd64.deb ...\n", + "Unpacking sox (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", + "Setting up libsox3:amd64 (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", + "Setting up libopencore-amrwb0:amd64 (0.1.5-1) ...\n", + "Setting up libsox-fmt-alsa:amd64 (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", + "Setting up libwavpack1:amd64 (5.4.0-1build2) ...\n", + "Setting up libopencore-amrnb0:amd64 (0.1.5-1) ...\n", + "Setting up libsox-fmt-base:amd64 (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", + "Setting up sox (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", + "Processing triggers for man-db (2.10.2-1) ...\n", + "Processing triggers for libc-bin (2.35-0ubuntu3.1) ...\n", + "/sbin/ldconfig.real: /usr/local/lib/libtbbbind_2_5.so.3 is not a symbolic link\n", + "\n", + "/sbin/ldconfig.real: /usr/local/lib/libtbbbind.so.3 is not a symbolic link\n", + "\n", + "/sbin/ldconfig.real: /usr/local/lib/libtbbmalloc_proxy.so.2 is not a symbolic link\n", + "\n", + "/sbin/ldconfig.real: /usr/local/lib/libtbb.so.12 is not a symbolic link\n", + "\n", + "/sbin/ldconfig.real: /usr/local/lib/libtbbmalloc.so.2 is not a symbolic link\n", + "\n", + "/sbin/ldconfig.real: /usr/local/lib/libtbbbind_2_0.so.3 is not a symbolic link\n", + "\n", + "Collecting dash>=2.1.0 (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1))\n", + " Obtaining dependency information for dash>=2.1.0 from https://files.pythonhosted.org/packages/9b/b4/d522c16b41a8da013fd60a67f9618e57c504cd2c80e02a7a861413b93906/dash-2.13.0-py3-none-any.whl.metadata\n", + " Downloading dash-2.13.0-py3-none-any.whl.metadata (11 kB)\n", + "Collecting dash_bootstrap_components>=1.0.3 (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 2))\n", + " Obtaining dependency information for dash_bootstrap_components>=1.0.3 from https://files.pythonhosted.org/packages/cd/2a/cf963336e8b6745406d357e2f2b33ff1f236531fcadbe250096931855ec0/dash_bootstrap_components-1.5.0-py3-none-any.whl.metadata\n", + " Downloading dash_bootstrap_components-1.5.0-py3-none-any.whl.metadata (5.2 kB)\n", + "Collecting diff_match_patch (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 3))\n", + " Downloading diff_match_patch-20230430-py3-none-any.whl (42 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m42.8/42.8 kB\u001b[0m \u001b[31m2.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: editdistance in /usr/local/lib/python3.10/dist-packages (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 4)) (0.6.2)\n", + "Requirement already satisfied: jiwer in /usr/local/lib/python3.10/dist-packages (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 5)) (2.5.2)\n", + "Requirement already satisfied: librosa>=0.9.1 in /usr/local/lib/python3.10/dist-packages (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (0.10.1)\n", + "Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 7)) (1.23.5)\n", + "Requirement already satisfied: plotly in /usr/local/lib/python3.10/dist-packages (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 8)) (5.15.0)\n", + "Requirement already satisfied: SoundFile in /usr/local/lib/python3.10/dist-packages (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 9)) (0.12.1)\n", + "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 10)) (4.66.1)\n", + "Requirement already satisfied: Flask<2.3.0,>=1.0.4 in /usr/local/lib/python3.10/dist-packages (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (2.2.5)\n", + "Collecting Werkzeug<2.3.0 (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1))\n", + " Downloading Werkzeug-2.2.3-py3-none-any.whl (233 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m233.6/233.6 kB\u001b[0m \u001b[31m9.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting dash-html-components==2.0.0 (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1))\n", + " Downloading dash_html_components-2.0.0-py3-none-any.whl (4.1 kB)\n", + "Collecting dash-core-components==2.0.0 (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1))\n", + " Downloading dash_core_components-2.0.0-py3-none-any.whl (3.8 kB)\n", + "Collecting dash-table==5.0.0 (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1))\n", + " Downloading dash_table-5.0.0-py3-none-any.whl (3.9 kB)\n", + "Requirement already satisfied: typing-extensions>=4.1.1 in /usr/local/lib/python3.10/dist-packages (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (4.7.1)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (2.31.0)\n", + "Collecting retrying (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1))\n", + " Downloading retrying-1.3.4-py3-none-any.whl (11 kB)\n", + "Collecting ansi2html (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1))\n", + " Downloading ansi2html-1.8.0-py3-none-any.whl (16 kB)\n", + "Requirement already satisfied: nest-asyncio in /usr/local/lib/python3.10/dist-packages (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (1.5.7)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.10/dist-packages (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (65.5.1)\n", + "Requirement already satisfied: rapidfuzz==2.13.7 in /usr/local/lib/python3.10/dist-packages (from jiwer->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 5)) (2.13.7)\n", + "Requirement already satisfied: audioread>=2.1.9 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (3.0.0)\n", + "Requirement already satisfied: scipy>=1.2.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (1.10.1)\n", + "Requirement already satisfied: scikit-learn>=0.20.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (1.2.2)\n", + "Requirement already satisfied: joblib>=0.14 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (1.3.2)\n", + "Requirement already satisfied: decorator>=4.3.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (4.4.2)\n", + "Requirement already satisfied: numba>=0.51.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (0.56.4)\n", + "Requirement already satisfied: pooch>=1.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (1.7.0)\n", + "Requirement already satisfied: soxr>=0.3.2 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (0.3.6)\n", + "Requirement already satisfied: lazy-loader>=0.1 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (0.3)\n", + "Requirement already satisfied: msgpack>=1.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (1.0.5)\n", + "Requirement already satisfied: tenacity>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from plotly->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 8)) (8.2.3)\n", + "Requirement already satisfied: packaging in /usr/local/lib/python3.10/dist-packages (from plotly->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 8)) (23.1)\n", + "Requirement already satisfied: cffi>=1.0 in /usr/local/lib/python3.10/dist-packages (from SoundFile->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 9)) (1.15.1)\n", + "Requirement already satisfied: pycparser in /usr/local/lib/python3.10/dist-packages (from cffi>=1.0->SoundFile->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 9)) (2.21)\n", + "Requirement already satisfied: Jinja2>=3.0 in /usr/local/lib/python3.10/dist-packages (from Flask<2.3.0,>=1.0.4->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (3.1.2)\n", + "Requirement already satisfied: itsdangerous>=2.0 in /usr/local/lib/python3.10/dist-packages (from Flask<2.3.0,>=1.0.4->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (2.1.2)\n", + "Requirement already satisfied: click>=8.0 in /usr/local/lib/python3.10/dist-packages (from Flask<2.3.0,>=1.0.4->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (8.0.2)\n", + "Requirement already satisfied: llvmlite<0.40,>=0.39.0dev0 in /usr/local/lib/python3.10/dist-packages (from numba>=0.51.0->librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (0.39.1)\n", + "Requirement already satisfied: platformdirs>=2.5.0 in /usr/local/lib/python3.10/dist-packages (from pooch>=1.0->librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (3.10.0)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (3.2.0)\n", + "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (3.4)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (1.26.16)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (2023.7.22)\n", + "Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=0.20.0->librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (3.2.0)\n", + "Requirement already satisfied: MarkupSafe>=2.1.1 in /usr/local/lib/python3.10/dist-packages (from Werkzeug<2.3.0->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (2.1.3)\n", + "Requirement already satisfied: six>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from retrying->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (1.16.0)\n", + "Downloading dash-2.13.0-py3-none-any.whl (10.4 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m10.4/10.4 MB\u001b[0m \u001b[31m46.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading dash_bootstrap_components-1.5.0-py3-none-any.whl (221 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m221.2/221.2 kB\u001b[0m \u001b[31m23.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hInstalling collected packages: dash-table, dash-html-components, dash-core-components, Werkzeug, retrying, diff_match_patch, ansi2html, dash, dash_bootstrap_components\n", + " Attempting uninstall: Werkzeug\n", + " Found existing installation: Werkzeug 2.3.7\n", + " Uninstalling Werkzeug-2.3.7:\n", + " Successfully uninstalled Werkzeug-2.3.7\n", + "Successfully installed Werkzeug-2.2.3 ansi2html-1.8.0 dash-2.13.0 dash-core-components-2.0.0 dash-html-components-2.0.0 dash-table-5.0.0 dash_bootstrap_components-1.5.0 diff_match_patch-20230430 retrying-1.3.4\n", + "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "!git clone https://github.com/NVIDIA/NeMo.git\n", + "\n", + "!apt-get update && apt-get install -y libsndfile1 ffmpeg\n", + "!cd NeMo;./reinstall.sh\n", + "!apt-get install sox\n", + "\n", + "!pip3 install -r ./NeMo/tools/speech_data_explorer/requirements.txt" + ], + "id": "c3919489" + }, + { + "cell_type": "markdown", + "source": [ + "# Dataset" + ], + "metadata": { + "id": "TOOhimHTrL9r" + }, + "id": "TOOhimHTrL9r" + }, + { + "cell_type": "markdown", + "source": [ + "In this tutorial we use LibriSpeech test-other dataset to evaluate ASR models. Let's download and prepare the dataset." + ], + "metadata": { + "id": "Eg2mqxC0XrOV" + }, + "id": "Eg2mqxC0XrOV" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "LQYnm_hsYqyt", + "outputId": "da5ebf03-2fca-4800-f162-b3efb1e08668" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "100% 314M/314M [00:03<00:00, 110MB/s]\n", + "100% 90/90 [00:35<00:00, 2.50it/s]\n" + ] + } + ], + "source": [ + "!python3 ./NeMo/scripts/dataset_processing/get_librispeech_data.py --data_sets test_other --data_root ." + ], + "id": "LQYnm_hsYqyt" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9737e0f1" + }, + "source": [ + "Before starting SDE, we need to run ASR inference on a given dataset and save predictions into a JSON manifest.\n" + ], + "id": "9737e0f1" + }, + { + "cell_type": "markdown", + "source": [ + "It is assumed that you already have a JSON manifest containing the `audio_filepath` and reference `text` fields. Here is a minimal example:" + ], + "metadata": { + "id": "t5ruQ3x84Yiq" + }, + "id": "t5ruQ3x84Yiq" + }, + { + "cell_type": "markdown", + "source": [ + "```\n", + "{\n", + " \"audio_filepath\": \"/LibriSpeech/dev-clean-processed/2902-9008-0000.wav\",\n", + " \"text\": \"the place seemed fragrant with all the riches of greek\",\n", + "}\n", + "```" + ], + "metadata": { + "id": "ic-QvihbHMQa" + }, + "id": "ic-QvihbHMQa" + }, + { + "cell_type": "markdown", + "source": [ + "# Transcription" + ], + "metadata": { + "id": "O7XnVkY0rXka" + }, + "id": "O7XnVkY0rXka" + }, + { + "cell_type": "markdown", + "source": [ + "To compare two models, JSON file should contain predictions from 1st (e.g., `QuartzNet15x5`) and 2nd (e.g., `Conformer-CTC Small`) models.\n", + "\n", + "NeMo includes a Python script for ASR inference: [`transcribe_speech.py`](https://github.com/NVIDIA/NeMo/blob/main/examples/asr/transcribe_speech.py).\n", + "\n", + "`transcribe_speech.py` accepts `append_pred` flag that allows saving an ASR transcript in the JSON file with a given custom field (like `pred_text_QN`). `pred_name_postfix` parameter defines the custom field's name. In this example it is set to the abbreviated model name `QN`." + ], + "metadata": { + "id": "rYe0xVTzjbhQ" + }, + "id": "rYe0xVTzjbhQ" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "928a9127", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "6558faad-e02d-4ede-f052-55d0f91f9b44" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "2023-09-08 15:46:53.368750: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT\n", + "[NeMo W 2023-09-08 15:47:01 nemo_logging:349] /usr/local/lib/python3.10/dist-packages/hydra/_internal/hydra.py:119: UserWarning: Future Hydra versions will no longer change working directory at job runtime by default.\n", + " See https://hydra.cc/docs/1.2/upgrades/1.1_to_1.2/changes_to_job_working_dir/ for more information.\n", + " ret = run_job(\n", + " \n", + "[NeMo I 2023-09-08 15:47:01 transcribe_speech:181] Hydra config: model_path: null\n", + " pretrained_name: QuartzNet15x5Base-En\n", + " audio_dir: null\n", + " dataset_manifest: ./test_other.json\n", + " channel_selector: null\n", + " audio_key: audio_filepath\n", + " eval_config_yaml: null\n", + " output_filename: test_other_QN.json\n", + " batch_size: 8\n", + " num_workers: 0\n", + " append_pred: true\n", + " pred_name_postfix: QN\n", + " random_seed: null\n", + " compute_timestamps: false\n", + " preserve_alignment: false\n", + " compute_langs: false\n", + " cuda: 0\n", + " allow_mps: false\n", + " amp: true\n", + " amp_dtype: float16\n", + " audio_type: wav\n", + " overwrite_transcripts: true\n", + " ctc_decoding:\n", + " strategy: greedy\n", + " preserve_alignments: null\n", + " compute_timestamps: null\n", + " word_seperator: ' '\n", + " ctc_timestamp_type: all\n", + " batch_dim_index: 0\n", + " greedy:\n", + " preserve_alignments: false\n", + " compute_timestamps: false\n", + " preserve_frame_confidence: false\n", + " confidence_measure_cfg:\n", + " name: entropy\n", + " entropy_type: tsallis\n", + " alpha: 0.33\n", + " entropy_norm: exp\n", + " temperature: DEPRECATED\n", + " confidence_method_cfg: DEPRECATED\n", + " beam:\n", + " beam_size: 4\n", + " search_type: default\n", + " preserve_alignments: false\n", + " compute_timestamps: false\n", + " return_best_hypothesis: true\n", + " beam_alpha: 1.0\n", + " beam_beta: 0.0\n", + " kenlm_path: null\n", + " flashlight_cfg:\n", + " lexicon_path: null\n", + " boost_path: null\n", + " beam_size_token: 16\n", + " beam_threshold: 20.0\n", + " unk_weight: -.inf\n", + " sil_weight: 0.0\n", + " pyctcdecode_cfg:\n", + " beam_prune_logp: -10.0\n", + " token_min_logp: -5.0\n", + " prune_history: false\n", + " hotwords: null\n", + " hotword_weight: 10.0\n", + " confidence_cfg:\n", + " preserve_frame_confidence: false\n", + " preserve_token_confidence: false\n", + " preserve_word_confidence: false\n", + " exclude_blank: true\n", + " aggregation: min\n", + " measure_cfg:\n", + " name: entropy\n", + " entropy_type: tsallis\n", + " alpha: 0.33\n", + " entropy_norm: exp\n", + " temperature: DEPRECATED\n", + " method_cfg: DEPRECATED\n", + " temperature: 1.0\n", + " rnnt_decoding:\n", + " model_type: rnnt\n", + " strategy: greedy_batch\n", + " compute_hypothesis_token_set: false\n", + " preserve_alignments: null\n", + " confidence_cfg:\n", + " preserve_frame_confidence: false\n", + " preserve_token_confidence: false\n", + " preserve_word_confidence: false\n", + " exclude_blank: true\n", + " aggregation: min\n", + " measure_cfg:\n", + " name: entropy\n", + " entropy_type: tsallis\n", + " alpha: 0.33\n", + " entropy_norm: exp\n", + " temperature: DEPRECATED\n", + " method_cfg: DEPRECATED\n", + " fused_batch_size: -1\n", + " compute_timestamps: null\n", + " compute_langs: false\n", + " word_seperator: ' '\n", + " rnnt_timestamp_type: all\n", + " greedy:\n", + " max_symbols_per_step: 10\n", + " preserve_alignments: false\n", + " preserve_frame_confidence: false\n", + " confidence_measure_cfg:\n", + " name: entropy\n", + " entropy_type: tsallis\n", + " alpha: 0.33\n", + " entropy_norm: exp\n", + " temperature: DEPRECATED\n", + " confidence_method_cfg: DEPRECATED\n", + " beam:\n", + " beam_size: 4\n", + " search_type: default\n", + " score_norm: true\n", + " return_best_hypothesis: true\n", + " tsd_max_sym_exp_per_step: 50\n", + " alsd_max_target_len: 1.0\n", + " nsc_max_timesteps_expansion: 1\n", + " nsc_prefix_alpha: 1\n", + " maes_num_steps: 2\n", + " maes_prefix_alpha: 1\n", + " maes_expansion_gamma: 2.3\n", + " maes_expansion_beta: 2\n", + " language_model: null\n", + " softmax_temperature: 1.0\n", + " preserve_alignments: false\n", + " ngram_lm_model: null\n", + " ngram_lm_alpha: 0.0\n", + " hat_subtract_ilm: false\n", + " hat_ilm_weight: 0.0\n", + " temperature: 1.0\n", + " decoder_type: null\n", + " att_context_size: null\n", + " model_change:\n", + " conformer:\n", + " self_attention_model: null\n", + " att_context_size: null\n", + " calculate_wer: true\n", + " clean_groundtruth_text: false\n", + " langid: en\n", + " use_cer: false\n", + " return_transcriptions: false\n", + " return_hypotheses: true\n", + " \n", + "[NeMo I 2023-09-08 15:47:01 transcribe_speech:227] Inference will be done on device: cuda:0\n", + "[NeMo I 2023-09-08 15:47:01 cloud:68] Downloading from: https://api.ngc.nvidia.com/v2/models/nvidia/nemospeechmodels/versions/1.0.0a5/files/QuartzNet15x5Base-En.nemo to /root/.cache/torch/NeMo/NeMo_1.21.0rc0/QuartzNet15x5Base-En/2b066be39e9294d7100fb176ec817722/QuartzNet15x5Base-En.nemo\n", + "[NeMo I 2023-09-08 15:47:07 common:913] Instantiating model from pre-trained checkpoint\n", + "[NeMo I 2023-09-08 15:47:08 features:289] PADDING: 16\n", + "[NeMo I 2023-09-08 15:47:10 save_restore_connector:249] Model EncDecCTCModel was successfully restored from /root/.cache/torch/NeMo/NeMo_1.21.0rc0/QuartzNet15x5Base-En/2b066be39e9294d7100fb176ec817722/QuartzNet15x5Base-En.nemo.\n", + "GPU available: True (cuda), used: True\n", + "TPU available: False, using: 0 TPU cores\n", + "IPU available: False, using: 0 IPUs\n", + "HPU available: False, using: 0 HPUs\n", + "[NeMo I 2023-09-08 15:47:10 ctc_models:346] Changed decoding strategy to \n", + " strategy: greedy\n", + " preserve_alignments: null\n", + " compute_timestamps: false\n", + " word_seperator: ' '\n", + " ctc_timestamp_type: all\n", + " batch_dim_index: 0\n", + " greedy:\n", + " preserve_alignments: false\n", + " compute_timestamps: false\n", + " preserve_frame_confidence: false\n", + " confidence_measure_cfg:\n", + " name: entropy\n", + " entropy_type: tsallis\n", + " alpha: 0.33\n", + " entropy_norm: exp\n", + " temperature: DEPRECATED\n", + " confidence_method_cfg: DEPRECATED\n", + " beam:\n", + " beam_size: 4\n", + " search_type: default\n", + " preserve_alignments: false\n", + " compute_timestamps: false\n", + " return_best_hypothesis: true\n", + " beam_alpha: 1.0\n", + " beam_beta: 0.0\n", + " kenlm_path: null\n", + " flashlight_cfg:\n", + " lexicon_path: null\n", + " boost_path: null\n", + " beam_size_token: 16\n", + " beam_threshold: 20.0\n", + " unk_weight: -.inf\n", + " sil_weight: 0.0\n", + " pyctcdecode_cfg:\n", + " beam_prune_logp: -10.0\n", + " token_min_logp: -5.0\n", + " prune_history: false\n", + " hotwords: null\n", + " hotword_weight: 10.0\n", + " confidence_cfg:\n", + " preserve_frame_confidence: false\n", + " preserve_token_confidence: false\n", + " preserve_word_confidence: false\n", + " exclude_blank: true\n", + " aggregation: min\n", + " measure_cfg:\n", + " name: entropy\n", + " entropy_type: tsallis\n", + " alpha: 0.33\n", + " entropy_norm: exp\n", + " temperature: DEPRECATED\n", + " method_cfg: DEPRECATED\n", + " temperature: 1.0\n", + " \n", + "[NeMo I 2023-09-08 15:47:10 transcribe_utils:235] \n", + " Transcribing 2939 files...\n", + " \n", + "[NeMo I 2023-09-08 15:47:10 transcribe_speech:303] AMP enabled!\n", + " \n", + "Transcribing: 100% 368/368 [00:53<00:00, 6.82it/s]\n", + "[NeMo I 2023-09-08 15:48:04 transcribe_speech:349] Finished transcribing 2939 files !\n", + "[NeMo I 2023-09-08 15:48:04 transcribe_speech:350] Writing transcriptions into file: test_other_QN.json\n", + "[NeMo I 2023-09-08 15:48:04 transcribe_utils:284] Transcripts will be written in \"test_other_QN.json\" file\n", + "[NeMo I 2023-09-08 15:48:05 transcribe_speech:368] Finished writing predictions to test_other_QN.json!\n", + "[NeMo I 2023-09-08 15:48:05 transcribe_speech:380] Writing prediction and error rate of each sample to test_other_QN.json!\n", + "[NeMo I 2023-09-08 15:48:05 transcribe_speech:381] {'samples': 2939, 'tokens': 52343, 'wer': 0.10062472536919932, 'ins_rate': 0.007298015016334563, 'del_rate': 0.009361328162313968, 'sub_rate': 0.08396538219055079}\n" + ] + } + ], + "source": [ + "!python3 ./NeMo/examples/asr/transcribe_speech.py \\\n", + "pretrained_name=\"QuartzNet15x5Base-En\" \\\n", + "dataset_manifest=./test_other.json \\\n", + "output_filename=\"test_other_QN.json\" \\\n", + "batch_size=8 \\\n", + "compute_timestamps=False \\\n", + "compute_langs=False \\\n", + "cuda=0 amp=True \\\n", + "append_pred=True \\\n", + "pred_name_postfix=\"QN\"" + ], + "id": "928a9127" + }, + { + "cell_type": "markdown", + "source": [ + "`transcribe_speech.py` also reports overall WER for the given dataset. In our case, QuartzNet's WER is 10.0%.\n", + "\n", + "Here is the first line of the newly created `test_other_QN.json` manifest file with QuartzNet's predictions:" + ], + "metadata": { + "id": "OfhJrSkvqrs6" + }, + "id": "OfhJrSkvqrs6" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5e274c99", + "outputId": "0a1a3a56-32ec-42c4-f16c-c298c643e851" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\"audio_filepath\": \"/content/LibriSpeech/test-other-processed/6938-70848-0000.wav\", \"duration\": 3.315, \"text\": \"even the sun came out pale and watery at noon\", \"pred_text_QN\": \"even the sun came out pale and watery at noon\", \"wer\": 0.0, \"tokens\": 10, \"ins_rate\": 0.0, \"del_rate\": 0.0, \"sub_rate\": 0.0}\n" + ] + } + ], + "source": [ + "!head -n 1 test_other_QN.json" + ], + "id": "5e274c99" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "21761c16" + }, + "source": [ + "Let's run inference with the second ASR model (`Conformer-CTC Small`):" + ], + "id": "21761c16" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "e88e4ed3", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "c55fda37-a235-46be-b32d-c25a9a1464a4" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "2023-09-08 15:48:30.111329: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT\n", + "[NeMo W 2023-09-08 15:48:36 nemo_logging:349] /usr/local/lib/python3.10/dist-packages/hydra/_internal/hydra.py:119: UserWarning: Future Hydra versions will no longer change working directory at job runtime by default.\n", + " See https://hydra.cc/docs/1.2/upgrades/1.1_to_1.2/changes_to_job_working_dir/ for more information.\n", + " ret = run_job(\n", + " \n", + "[NeMo I 2023-09-08 15:48:36 transcribe_speech:181] Hydra config: model_path: null\n", + " pretrained_name: stt_en_conformer_ctc_small\n", + " audio_dir: null\n", + " dataset_manifest: ./test_other_QN.json\n", + " channel_selector: null\n", + " audio_key: audio_filepath\n", + " eval_config_yaml: null\n", + " output_filename: test_other_QN_Conf_small.json\n", + " batch_size: 8\n", + " num_workers: 0\n", + " append_pred: true\n", + " pred_name_postfix: conf_s\n", + " random_seed: null\n", + " compute_timestamps: false\n", + " preserve_alignment: false\n", + " compute_langs: false\n", + " cuda: 0\n", + " allow_mps: false\n", + " amp: true\n", + " amp_dtype: float16\n", + " audio_type: wav\n", + " overwrite_transcripts: true\n", + " ctc_decoding:\n", + " strategy: greedy\n", + " preserve_alignments: null\n", + " compute_timestamps: null\n", + " word_seperator: ' '\n", + " ctc_timestamp_type: all\n", + " batch_dim_index: 0\n", + " greedy:\n", + " preserve_alignments: false\n", + " compute_timestamps: false\n", + " preserve_frame_confidence: false\n", + " confidence_measure_cfg:\n", + " name: entropy\n", + " entropy_type: tsallis\n", + " alpha: 0.33\n", + " entropy_norm: exp\n", + " temperature: DEPRECATED\n", + " confidence_method_cfg: DEPRECATED\n", + " beam:\n", + " beam_size: 4\n", + " search_type: default\n", + " preserve_alignments: false\n", + " compute_timestamps: false\n", + " return_best_hypothesis: true\n", + " beam_alpha: 1.0\n", + " beam_beta: 0.0\n", + " kenlm_path: null\n", + " flashlight_cfg:\n", + " lexicon_path: null\n", + " boost_path: null\n", + " beam_size_token: 16\n", + " beam_threshold: 20.0\n", + " unk_weight: -.inf\n", + " sil_weight: 0.0\n", + " pyctcdecode_cfg:\n", + " beam_prune_logp: -10.0\n", + " token_min_logp: -5.0\n", + " prune_history: false\n", + " hotwords: null\n", + " hotword_weight: 10.0\n", + " confidence_cfg:\n", + " preserve_frame_confidence: false\n", + " preserve_token_confidence: false\n", + " preserve_word_confidence: false\n", + " exclude_blank: true\n", + " aggregation: min\n", + " measure_cfg:\n", + " name: entropy\n", + " entropy_type: tsallis\n", + " alpha: 0.33\n", + " entropy_norm: exp\n", + " temperature: DEPRECATED\n", + " method_cfg: DEPRECATED\n", + " temperature: 1.0\n", + " rnnt_decoding:\n", + " model_type: rnnt\n", + " strategy: greedy_batch\n", + " compute_hypothesis_token_set: false\n", + " preserve_alignments: null\n", + " confidence_cfg:\n", + " preserve_frame_confidence: false\n", + " preserve_token_confidence: false\n", + " preserve_word_confidence: false\n", + " exclude_blank: true\n", + " aggregation: min\n", + " measure_cfg:\n", + " name: entropy\n", + " entropy_type: tsallis\n", + " alpha: 0.33\n", + " entropy_norm: exp\n", + " temperature: DEPRECATED\n", + " method_cfg: DEPRECATED\n", + " fused_batch_size: -1\n", + " compute_timestamps: null\n", + " compute_langs: false\n", + " word_seperator: ' '\n", + " rnnt_timestamp_type: all\n", + " greedy:\n", + " max_symbols_per_step: 10\n", + " preserve_alignments: false\n", + " preserve_frame_confidence: false\n", + " confidence_measure_cfg:\n", + " name: entropy\n", + " entropy_type: tsallis\n", + " alpha: 0.33\n", + " entropy_norm: exp\n", + " temperature: DEPRECATED\n", + " confidence_method_cfg: DEPRECATED\n", + " beam:\n", + " beam_size: 4\n", + " search_type: default\n", + " score_norm: true\n", + " return_best_hypothesis: true\n", + " tsd_max_sym_exp_per_step: 50\n", + " alsd_max_target_len: 1.0\n", + " nsc_max_timesteps_expansion: 1\n", + " nsc_prefix_alpha: 1\n", + " maes_num_steps: 2\n", + " maes_prefix_alpha: 1\n", + " maes_expansion_gamma: 2.3\n", + " maes_expansion_beta: 2\n", + " language_model: null\n", + " softmax_temperature: 1.0\n", + " preserve_alignments: false\n", + " ngram_lm_model: null\n", + " ngram_lm_alpha: 0.0\n", + " hat_subtract_ilm: false\n", + " hat_ilm_weight: 0.0\n", + " temperature: 1.0\n", + " decoder_type: null\n", + " att_context_size: null\n", + " model_change:\n", + " conformer:\n", + " self_attention_model: null\n", + " att_context_size: null\n", + " calculate_wer: true\n", + " clean_groundtruth_text: false\n", + " langid: en\n", + " use_cer: false\n", + " return_transcriptions: false\n", + " return_hypotheses: true\n", + " \n", + "[NeMo I 2023-09-08 15:48:36 transcribe_speech:227] Inference will be done on device: cuda:0\n", + "[NeMo I 2023-09-08 15:48:36 cloud:68] Downloading from: https://api.ngc.nvidia.com/v2/models/nvidia/nemo/stt_en_conformer_ctc_small/versions/1.6.0/files/stt_en_conformer_ctc_small.nemo to /root/.cache/torch/NeMo/NeMo_1.21.0rc0/stt_en_conformer_ctc_small/5d2d8e5b2b5adb8f5091363c6ba19c55/stt_en_conformer_ctc_small.nemo\n", + "[NeMo I 2023-09-08 15:48:41 common:913] Instantiating model from pre-trained checkpoint\n", + "[NeMo I 2023-09-08 15:48:42 mixins:170] Tokenizer SentencePieceTokenizer initialized with 1024 tokens\n", + "[NeMo W 2023-09-08 15:48:43 modelPT:161] If you intend to do training or fine-tuning, please call the ModelPT.setup_training_data() method and provide a valid configuration file to setup the train data loader.\n", + " Train config : \n", + " manifest_filepath: /data/NeMo_ASR_SET/English/v2.0/train/tarred_audio_manifest.json\n", + " sample_rate: 16000\n", + " batch_size: 64\n", + " shuffle: true\n", + " num_workers: 8\n", + " pin_memory: true\n", + " use_start_end_token: false\n", + " trim_silence: false\n", + " max_duration: 20.0\n", + " min_duration: 0.1\n", + " shuffle_n: 2048\n", + " is_tarred: true\n", + " tarred_audio_filepaths: /data/NeMo_ASR_SET/English/v2.0/train/audio__OP_0..4095_CL_.tar\n", + " \n", + "[NeMo W 2023-09-08 15:48:43 modelPT:168] If you intend to do validation, please call the ModelPT.setup_validation_data() or ModelPT.setup_multiple_validation_data() method and provide a valid configuration file to setup the validation data loader(s). \n", + " Validation config : \n", + " manifest_filepath:\n", + " - /data/ASR/LibriSpeech/librispeech_withsp2/manifests/librivox-dev-other.json\n", + " - /data/ASR/LibriSpeech/librispeech_withsp2/manifests/librivox-dev-clean.json\n", + " - /data/ASR/LibriSpeech/librispeech_withsp2/manifests/librivox-test-other.json\n", + " - /data/ASR/LibriSpeech/librispeech_withsp2/manifests/librivox-test-clean.json\n", + " sample_rate: 16000\n", + " batch_size: 64\n", + " shuffle: false\n", + " num_workers: 8\n", + " pin_memory: true\n", + " use_start_end_token: false\n", + " is_tarred: false\n", + " tarred_audio_filepaths: na\n", + " \n", + "[NeMo W 2023-09-08 15:48:43 modelPT:174] Please call the ModelPT.setup_test_data() or ModelPT.setup_multiple_test_data() method and provide a valid configuration file to setup the test data loader(s).\n", + " Test config : \n", + " manifest_filepath:\n", + " - /data/ASR/LibriSpeech/librispeech_withsp2/manifests/librivox-test-other.json\n", + " - /data/ASR/LibriSpeech/librispeech_withsp2/manifests/librivox-dev-clean.json\n", + " - /data/ASR/LibriSpeech/librispeech_withsp2/manifests/librivox-dev-other.json\n", + " - /data/ASR/LibriSpeech/librispeech_withsp2/manifests/librivox-test-clean.json\n", + " sample_rate: 16000\n", + " batch_size: 64\n", + " shuffle: false\n", + " num_workers: 8\n", + " pin_memory: true\n", + " use_start_end_token: false\n", + " is_tarred: false\n", + " tarred_audio_filepaths: na\n", + " \n", + "[NeMo I 2023-09-08 15:48:43 features:289] PADDING: 0\n", + "[NeMo I 2023-09-08 15:48:43 save_restore_connector:249] Model EncDecCTCModelBPE was successfully restored from /root/.cache/torch/NeMo/NeMo_1.21.0rc0/stt_en_conformer_ctc_small/5d2d8e5b2b5adb8f5091363c6ba19c55/stt_en_conformer_ctc_small.nemo.\n", + "GPU available: True (cuda), used: True\n", + "TPU available: False, using: 0 TPU cores\n", + "IPU available: False, using: 0 IPUs\n", + "HPU available: False, using: 0 HPUs\n", + "[NeMo I 2023-09-08 15:48:44 ctc_bpe_models:314] Changed decoding strategy to \n", + " strategy: greedy\n", + " preserve_alignments: null\n", + " compute_timestamps: false\n", + " word_seperator: ' '\n", + " ctc_timestamp_type: all\n", + " batch_dim_index: 0\n", + " greedy:\n", + " preserve_alignments: false\n", + " compute_timestamps: false\n", + " preserve_frame_confidence: false\n", + " confidence_measure_cfg:\n", + " name: entropy\n", + " entropy_type: tsallis\n", + " alpha: 0.33\n", + " entropy_norm: exp\n", + " temperature: DEPRECATED\n", + " confidence_method_cfg: DEPRECATED\n", + " beam:\n", + " beam_size: 4\n", + " search_type: default\n", + " preserve_alignments: false\n", + " compute_timestamps: false\n", + " return_best_hypothesis: true\n", + " beam_alpha: 1.0\n", + " beam_beta: 0.0\n", + " kenlm_path: null\n", + " flashlight_cfg:\n", + " lexicon_path: null\n", + " boost_path: null\n", + " beam_size_token: 16\n", + " beam_threshold: 20.0\n", + " unk_weight: -.inf\n", + " sil_weight: 0.0\n", + " pyctcdecode_cfg:\n", + " beam_prune_logp: -10.0\n", + " token_min_logp: -5.0\n", + " prune_history: false\n", + " hotwords: null\n", + " hotword_weight: 10.0\n", + " confidence_cfg:\n", + " preserve_frame_confidence: false\n", + " preserve_token_confidence: false\n", + " preserve_word_confidence: false\n", + " exclude_blank: true\n", + " aggregation: min\n", + " measure_cfg:\n", + " name: entropy\n", + " entropy_type: tsallis\n", + " alpha: 0.33\n", + " entropy_norm: exp\n", + " temperature: DEPRECATED\n", + " method_cfg: DEPRECATED\n", + " temperature: 1.0\n", + " \n", + "[NeMo I 2023-09-08 15:48:44 transcribe_utils:235] \n", + " Transcribing 2939 files...\n", + " \n", + "[NeMo I 2023-09-08 15:48:44 transcribe_speech:303] AMP enabled!\n", + " \n", + "Transcribing: 100% 368/368 [00:46<00:00, 7.88it/s]\n", + "[NeMo I 2023-09-08 15:49:31 transcribe_speech:349] Finished transcribing 2939 files !\n", + "[NeMo I 2023-09-08 15:49:31 transcribe_speech:350] Writing transcriptions into file: test_other_QN_Conf_small.json\n", + "[NeMo I 2023-09-08 15:49:31 transcribe_utils:284] Transcripts will be written in \"test_other_QN_Conf_small.json\" file\n", + "[NeMo I 2023-09-08 15:49:31 transcribe_speech:368] Finished writing predictions to test_other_QN_Conf_small.json!\n", + "[NeMo I 2023-09-08 15:49:32 transcribe_speech:380] Writing prediction and error rate of each sample to test_other_QN_Conf_small.json!\n", + "[NeMo I 2023-09-08 15:49:32 transcribe_speech:381] {'samples': 2939, 'tokens': 52343, 'wer': 0.08180654528781307, 'ins_rate': 0.007450853027147852, 'del_rate': 0.006820396232543034, 'sub_rate': 0.0675352960281222}\n" + ] + } + ], + "source": [ + "!python ./NeMo/examples/asr/transcribe_speech.py \\\n", + "pretrained_name=\"stt_en_conformer_ctc_small\" \\\n", + "dataset_manifest=./test_other_QN.json \\\n", + "output_filename=\"test_other_QN_Conf_small.json\" \\\n", + "batch_size=8 \\\n", + "compute_timestamps=False \\\n", + "compute_langs=False \\\n", + "cuda=0 amp=True \\\n", + "append_pred=True \\\n", + "pred_name_postfix=\"conf_s\"" + ], + "id": "e88e4ed3" + }, + { + "cell_type": "markdown", + "source": [ + "Conformer's WER is 8.1%.\n", + "\n", + "Here is the first line of `test_other_QN_Conf_small.json` manifest file with transcripts by both ASR models\n", + "- `pred_text_QN` by QuartzNet\n", + "- `pred_text_conf_s` by Conformer-Small" + ], + "metadata": { + "id": "MyvMpkcGqhff" + }, + "id": "MyvMpkcGqhff" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "551a2b33", + "outputId": "7dbbc35f-44fd-49c4-c035-2a07f033cdaf" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\"audio_filepath\": \"/content/LibriSpeech/test-other-processed/6938-70848-0000.wav\", \"duration\": 3.315, \"text\": \"even the sun came out pale and watery at noon\", \"pred_text_QN\": \"even the sun came out pale and watery at noon\", \"wer\": 0.0, \"tokens\": 10, \"ins_rate\": 0.0, \"del_rate\": 0.0, \"sub_rate\": 0.0, \"pred_text_conf_s\": \"even the sun came out pale and watery at noon\"}\n" + ] + } + ], + "source": [ + "!head -n 1 test_other_QN_Conf_small.json" + ], + "id": "551a2b33" + }, + { + "cell_type": "markdown", + "source": [ + "# Launching SDE" + ], + "metadata": { + "id": "wYQAP60iretG" + }, + "id": "wYQAP60iretG" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "67a15347" + }, + "source": [ + "Now it's time to start SDE!\n", + "\n", + "SDE is a Python web application that can be run either locally or on a remote server (a cloud instance) from a command line.\n", + "\n", + "In this tutorial, we use Google Colab. So we need to get a proxy link to the instance:" + ], + "id": "67a15347" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "Fmh2uHfLgTrL", + "outputId": "dd93bd85-ae6a-4024-fae6-d18e9eb402e8" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "https://1p2l450rmms-496ff2e9c6d22116-8080-colab.googleusercontent.com/\n" + ] + } + ], + "source": [ + "from google.colab.output import eval_js\n", + "print(eval_js(\"google.colab.kernel.proxyPort(8050)\"))" + ], + "id": "Fmh2uHfLgTrL" + }, + { + "cell_type": "markdown", + "source": [ + "Now let's start SDE with the following shell command. `nc` parameter provides names of ASR transcripts for Comparison mode." + ], + "metadata": { + "id": "hABptmgK1amL" + }, + "id": "hABptmgK1amL" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "e69fc876" + }, + "outputs": [], + "source": [ + "!python3 ./NeMo/tools/speech_data_explorer/data_explorer.py test_other_QN_Conf_small.json --port=8050 \\\n", + "-nc pred_text_QN pred_text_conf_s" + ], + "id": "e69fc876" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "710ea9c4" + }, + "source": [ + "Please click on the aforementioned proxy link to view SDE application in another web-browser tab." + ], + "id": "710ea9c4" + }, + { + "cell_type": "markdown", + "source": [ + "#Quick guide on SDE interface" + ], + "metadata": { + "id": "rLNS2kFhr-GL" + }, + "id": "rLNS2kFhr-GL" + }, + { + "cell_type": "markdown", + "source": [ + "SDE provides general dataset's statistics (number of utterances, hours, character set size, histograms, etc.) on `Statistics` page. It allows users to do interactive exploration and analysis on the dataset on `Samples` page. All these functions are described in detail in [NeMo documentation](https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/stable/tools/speech_data_explorer.html)." + ], + "metadata": { + "id": "AI1Wkahb2w2-" + }, + "id": "AI1Wkahb2w2-" + }, + { + "cell_type": "markdown", + "source": [ + "![снимок 1.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "IsNRQkg9CQ6n" + }, + "id": "IsNRQkg9CQ6n" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "67693388" + }, + "source": [ + "In this tutorial, we are interested in `Comparison tool` page.\n", + "\n", + "The `Comparison` page has two main modes (levels) of comparison:\n", + "- word level (metrics for individual vocabulary words are compared)\n", + "- utterance level (metrics for individual dataset's utterances are compared)\n", + "\n", + "In each mode, the dataset is visualized as an interactive scatterplot. Each marker represents a data unit (either a word, or an utterance). X-coordinate encodes a selected metric for one model, Y-coordinate does the same for the other model.\n", + "\n", + "By default, word level comparison is selected on `Comparison tool` page. In second (2) and third (3) box you can choose what will be shown on the axes of the scatterplot (that is, metrics for 1st and 2nd models). In our example, it is word level accuracy for QuartzNet and Conformer-Small: `accuracy_model_pred_text_{model_name}`." + ], + "id": "67693388" + }, + { + "cell_type": "markdown", + "source": [ + "Depending on comparison level, these fields provide the following options:\n", + "\n", + "\n", + "* word accuracy (ratio of the correctly recognized number of words to the total number of this word in the entire dataset)\n", + "* utterance WER\n", + "* utterance CER" + ], + "metadata": { + "id": "dUXwvaU8ASxv" + }, + "id": "dUXwvaU8ASxv" + }, + { + "cell_type": "markdown", + "source": [ + "![image.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "xHfBizBuv5Pg" + }, + "id": "xHfBizBuv5Pg" + }, + { + "cell_type": "markdown", + "source": [ + "![Снимок 2.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "WhRiRrVCDUVO" + }, + "id": "WhRiRrVCDUVO" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1c0c797c" + }, + "source": [ + "By default, the scatterplot displays all data units (either words or utterances). To allow users to select more interesting subsets, SDE supports flexible filtering queries like in the following example (for word level):\n" + ], + "id": "1c0c797c" + }, + { + "cell_type": "markdown", + "source": [ + "![3.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "BtabXT7PEGe-" + }, + "id": "BtabXT7PEGe-" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1f8cadb0" + }, + "source": [ + "You can type the filtering queries either in a specific column header, or in a custom filter expression textbox (putting lowercased column names in curly brackets).\n", + "\n", + "\n" + ], + "id": "1f8cadb0" + }, + { + "cell_type": "markdown", + "source": [ + "Below is an example of complicated query, that allows us to display utterances where both ASR models yield different WERs (for utterance level):" + ], + "metadata": { + "id": "9COXr5cXsp8X" + }, + "id": "9COXr5cXsp8X" + }, + { + "cell_type": "markdown", + "source": [ + "![image (1).png](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABVwAAAGnCAYAAABLmpx0AADRrElEQVR4Xuz9e5RV1Z0vfP8qOd3j9ElyTjqBiK2JUMXFCzmgpk/wUlUIIjH9/vFAoZFCEKkyQseM80TTaGmBSJVUoCV5zoh5wLZARSgUq+B9x3g6ighSVSrktFxsQUWogqQlokW6czrJ0xknHfc7f/Oy1ly3vdfetfauffl+HFv2rfZl7nn9rTnnqkoJBAAAAAAAAAAAAADD9in9LwAAAAAAAAAAAAAMEwKuAAAAAAAAAAAAAAkpyy0FDv7sf+prAAAAAAAAAAAAAF7Tvv7f9LXkYQ9XAAAAAAAAAAAAgIRgSwEAAAAAAAAAAACAhCDgCgAAAAAAAAAAAJAQBFwBAAAAAAAAAAAAEoKAKwAAAAAAAAAAAEBCEHAFAAAAAAAAAAAASAgCrgAAAAAAAAAAAAAJQcAVAAAAAAAAAAAAICEIuAIAAAAAAAAAAAAkBAFXAAAAAAAAAAAAgIQg4AoAAAAAAAAAAACQEARcAQAAAAAAAAAAABKCgCsAAAAAAAAAAABAQhBwBQAAAAAAAAAAAEgIAq4AAAAAAAAAAAAACUHAFQAAAAAAAAAAACAhVSlBXy8r999/v74GAAAAAAAAAAAAoKxdu1Zfy4+yDLhysDXfCQcAAAAAAAAAAAClJ9+xQ2wpAAAAAAAAAAAAAJAQBFwBAAAAAAAAAAAAEoKAKwBAmauqqtLXAAAAAAAAACDfEHAFAAAAAAAAAAAASAgCrgAAAAAAAAAAAAAJQcAVAAAAAAAAAAAAICEIuAIAAAAAAAAAAAAkBAHXSnLwUaqqepQO6Jtg+aCLGqqq5MmF2g+k9J1QMkTe/tSnkLcBAAAAAAAAYORVUMD1HHXdygE1BGXA7wA9+uUFdNWBFKVSKWq9JoczupuA7eo39B1RVD589GCSQV3xmt/6FPJ2KZIHQVSg31wauj7UD7rObZ+nHtt+Tt9jU3mKH+NcdW57g3zuowfVowAAAAAAAABQWBUTcD23/Tu04IV2eiP1EF2j74NoMmhzaxeFhXdypmfYvlFsE0g/OE2HaS6Nu1jfzsXFjdTzT9uoYdX1BQ90cd6+/YU25O2YZPAyT3k7fsBbBUk/dc1h2vZPKtCvLh9Sw//vosjPt7PxO9T1gb4RYcz8Hvqway61XoMAPAAAAAAAAMBIqKwtBVbPQEAK8uficXSlvlpwjyBvlxJzAOi1T3qo0RPoH0ONz/+SttECurDNFy69ZRttW72TFtybOVg8ZtxV+hoAAAAAAAAAFFpl7uEast8jz+j033egjZfpust7zVJdc/HMZDQz3MwS4Vu76MN0MznlEvQG6vrALAfXr+sJspilwgfUbDjPex6gR83f8CVkRhx/fufxqvaYs93Ue17YuJPohQV0oXztbe5rW3udyov1eQ+0BZe1y/vEZ/t/+LNc0yruaaXrPsV/G3P2nUlPc7E/i/6snC7mce/sUpN+52RaOM+z0krOdvzyAtop/lvwZX488wxcb7oW2dLtkJmWKt+G5W03Jb3fqUHOunTo13zjgJu3/XnNw/kM3jzqXQ6v8r3J2/y4k47+PBayTYPKa+Y5MfOSzg9/0dhj5W3ru0S+r/4eIq+7qaLuU3lLfBadt6+Vf5spDx2gTaJ8ze1qovDdK8ZQ473tRCv3Bb7XjBVvULv47N/R2wcAAAAAAAAAQPGpzIDrtBnURitonxMoO0f7du0kSrVa9x2gfSuJrho7Rt7ioNWFuxroQ7P095+20eFr/MG2Vrp2zwz1+I5GujDjVqAc5PsOpR77o15O/Aa1r7zWF9jhZcSPEf0wRZ+I5zw0je/hYM+14h/9WcTlja8uoAut4BEH0K59e5v1eavpMRkUymQMNe5IySXJPKNO/v2OBeJegQNSX+6hBmcJ9Ie07W31edk1K35J225ppcdMYO3go3Tdw230hkiL/88K8fwD7eLOdnr9E/7bzMvfZTDUs+RavN9XTaJyGlxIC776Bv3R+Y7qNzF7WRo7Gy+kfbNU+sk01gErNmZ+t/y7ueI/9T4P0bVpfjfOB550ld9pmPyBPs+Fg/L6eXGIvN0u8mEgb3vu03l7nMrbHDC8lt5Q30d+p6to4SXzfO/bStftuUE9Ln5P9ZfpcPBxH80wrynSmMTv4A26unmbnyPzdlgeO369L7BfRde9/ayVt8fJvM3X01N5+5ddDVbe1t8l7fteQw9xHln5GG0Xj7MDbddS6+o3qGf+GJHvP3Hy9hvyb9PnIbOFRUPtGIp82sXjxDPs38wQn0W8l9xaQH8WAAAAAAAAACguFRJwVUGnuePH6dvX0IxHiA6f1sGfD/ZRzwvt1Oa5j4Mi7TRjGodEeEYa0bYfWoGmixvp+6uJWvfYc9Dm0rY7s1vY3X6gR86sVFQwRc5ss2Mpq7/vWXZ8bvtjMtijgq/KNXduo7kv9NA+DpJ90EWPrRSfxfd5e4YZHDzw1AKirp9Yn8U/E0/d3tm4Sdw+QI9e00ptrz+YMbAaTqT5gp0yfTzvt0J9J5kGHDRbcY0btBLf8Sddc2nnrn10zpN+dlpdQ03mOfqebJw+tZPoq+PcdJ32kPXa42jcLf48EQP/NjJQF3bxLzn3U3l7zvix+rbI2yJfcj6WSaDzdrvnPpO3+XoXreeguEhHx7Qm2nrLLurpt1NoLm3NKm9zANsKqtu/jb5L8uXtA0/dHsxj32tz85jO21vXB/M2B6hzlfF9xXt8f/VOuv1pcevgo3TtynZvmiXt4nEUuSmAyHNviM+y8L40M40jA7YAAAAAAAAAkG8VEXDt+tZfUM+cD+VsNGPceCv488Fp2rl6Bj14U7tz37n+HnmfjLfKAJVZcu5erl3Jf2y7KssTL4WcqEkGSg7TmQ/ciKEbKFZk0I9nwlqfRS2L1/j73NJAM7L6LJmco9Nvq9minvf1z5qVwSCe3ahmAD6Yyxn/mfgOR6hNBQVDcBrMnTPDDbppY2obaO4Lp+mMvs386TccMrAt09637F4aQ43Pf0JvkDvrN2gMjfuqvjpsvET+Qpm3u+dfqO+z8jZ/PJ23H5ql87a4z+RtGS7kx+VsVOs35ZnDO/zfjfN2Nr9lsCzIfUXFb3Na32be30bksWMZ8pjO2zdk9VkyifG+wjUr3qC2h6+X97cfyPMJysT3THciN/lZuhc6M7UDZBD/DfHEKnrUc/QGAAAAAAAAAPKtIgKufBKahl3e5cxjauc6M0IP7Gml9lnXUBUvx5b3qVmD8j79fHe5sO+Sz1luaczt+jD4WTLOhhy+dmsbA/fiDT5xwK9smdmoctk97yPqXfLvLM9Pky+uWcHL562AIS9ntwN9nku6LQXGUOOOD2XenmftNayCzj306gcpJ2/LrQbkfR86edsxz9oiwbrYBygKKU4ey4fM76tmMA+bnL26U84gjgyFykB4ugM419BDb7SprQVC84e77chDuR70AAAAAAAAAICcVMiWAmNoxpy5tPOUNbfu4hnUcMtOOv0B72epl1cTL8fm+07T6RfMfYIMkBRmea6cfZhhBp9ndm4Us72A5dzpw/paLtTMzIzL5T/oou80XkVv6P1o1+Q6u06k+ZWefXa9otJApd84Mgvs82baQ/TJJx/SNpGH3KX3aqakJ5gZhwnihl4yBdFV3t51yprTa/L22ZC8fTaYt+d2B/NKPnDwl8zM2lAij02Okcdk3vbmq2Hn7Rjve277d+j2ya/L/Vpbr4l7oq4welsL3nojtHico64fZkorQW8tsODefUT+WdNyhqz1OwMAAAAAAABAwVTmSbMkFahqvYaXvruBDQ7kyftuGUfuYme1L6Y/yHJu+6PZndAogLcpsM5oLoOVeql8mklpagaj/0zlB+hRs4x9WpMMBC6413sGdn7tuMKWf18zi/drvdZ7ojDxuo86M4fFZ/gy7/PaJFJMBZVWXLfGTbOQ7RKiib/fxr+FPcPzHHW1qe80Zv735cmvLhTf2Xk1/R3b741zwrLcHGhLF2jjYKa+GolnHlZRQ5c7IzV5Km+vuPa6QN6W99l5++IZNPeWXd68IqT/nnG00rXWSdzUvqeZg9HXzOJ9U9PkMZ23PfuX6t+dA9RxjBl3ZUjezvC+4vNf2Ej07OJp+jOI72dvG6Hz9umY9cGY+T+Rr3H9p9p96Szyx6f+ghaQ2p84E95agMvBAv/2JnKGLAAAAAAAAACMhAoOuJIKXIp/7SCQuc+/PygvA1d7k7rLvS88NWOYS/jn0rYD42j9p3lpunhNGaxUe82mjRfyjEh91vdPOZ/nMRrnnNRILTXfRgvoQvP4vSn6STYnzTJBJf7bW7ep4Na0h/TsPvOe4vLl0zRDLj0/R13f4mDeNvqJXorOQaWtt6xwA2/mxENf4e+bOaA3Zn43fdhF1t65F1JPzQ36d7mGHtKzaJ000GeZt08mljw7D6j9U0dq6X06Kh9XheTtKl/e5n1nf+nNK+LyWM0SJ1Cbm3Z6497T7mvKfU9j/DaheWxQ5zGm8vbWqoVW3iaZt/l6LJ68rfNmuvf9oIsaxOefyyfVkie4E5/hh2ovX96mRIZ5dd5WedU6iBKJv0eKftl11Ld/7rXUGjNwrOgT7QEAAAAAAABA0ahKxZ0WVkLuv/9+Wrt2rb6lnNveQBee+v6I7bkawEEcGSDM/76rUCg8O/E6ojc+yXPQ14vz9l+IvP1JseTtg49SlfgobxRg39XyxLOgryV1yq65tC2XOsL3G3AwtwyregAAAAAAAICchMUOk1QxM1zlEvmV+zLOqkzMB10071N65qrvYp+8a6QcaKuyZsfaF/8S5/zhQGHw/fmS7kRR+cZB0/DfrcpeQh7mg9N0hOakOdFRfsi8/XBh83bUSb6cGZ8jRm3ZEPbZMv5+CeKTp4V+hhgzu9Xs7bh7+IaTe9p6tkUBAAAAAAAAgEKpmBmujIOM165sL46Zd5jhWl7k77mAaNsvqafxQn1n4XCA79qVbcWRtzHDdUTJ2fyNIitadQsHezHDFQAAAAAAAEDBDNcE8T6sKQSBIB94X91UakSCreyaFZ8gb4M0Zn5PzjNjAQAAAAAAAGD4KvqkWSNKBugQFIEyxCegQvC3qGB2KwAAAAAAAEDhIOAKAAAAAAAAAAAAkBAEXAEAAAAAAAAAAAASgoArAAAAAAAAAAAAQEKqUmW6uR+fbQwAAAAAAAAAAADAtnbtWn0tP8o24AoAAAAAAAAAAABQaNhSAAAAAAAAAAAAACAhCLgCAAAAAAAAAAAAJAQBVwAAAAAAAAAAAICEIOAKAAAAAAAAAAAAkBAEXAEAAAAAAAAAAAASgoArAAAAAAAAAAAAQEIQcAUAAAAAAAAAAABISJkHXD+igeVr6Ei9vmx4S9+vnN3A9//fNDCk79D+5cX/O/JvYAS83eX8Tuq36aKz+iEP+Tz+3cTjKX1fpeDvvnw3/Yu+Cem8Re+Z8i0u//jiR/r+YRjaTf8oXy9Yn8SnPtd7/6hvJg3lo+LLh2rzxCW0XVP57x9/KsqDzs/vva0fAgAAAAAAgKyUdcD1X17soX+lq2ns/gfpyl5xWTZFP5Len9/81/L5Yx/4vL7HRw5GI4J+MELeovfuOUN/9jj/1o10UZW+GyKogxH5Cajk87WH7+yGv6d/u+2vVJ0gLv/15gv0I8Mwejb9V369xz9P//q3u+lfii6gifKRneItH54Dghzg/1g/YIL+djDVF2i+aBn//k30n0//PYKpAAAAAAAAeVTWAdf/98yv6dM3TKU/jwguqMHnX1PNaH0HFKcxX6RP0+fpP4rf6c8v/jzR179I/0k/5Bg6R/+bxtLnvqpvA4T6iH5/hujPro938CVrX72C/uxnv6L/V98sGigfZYGDrWd+8Hn6kj5YMPYBon+9xT7493n69OnjGWYwX0BfuOHz9L8/8M/sHkN/8nWiP/3yBUSjx9Cfitf6kzH6IQAAAAAAAMhKVUrQ18sMzyLaRL+7oSk4g41nAt1yiP4ov/lYMXhtpIvkA15ycHvmOmtmLC+5/Hv6N33L9Xn6zy/8NdV8Sd1Sg+Jfqxu+1+clnb8a20Rfph7nOX/24wfp0v8qr2bGM5buOaNvMPP66vv+YaF4LR1UyfT5P32/SJtv6rThNJn3K/ri/ivoN9P1c75+NY1dN5v+XP8dPe6+tnkt730jyHx++7fktHr2i/o7uLzpotLtX3+mHiOe+eikl5umn3ttDX38HN8nfuvueEF6fp9/ogb64plN+m/Fb+2kV4zX5s//XfFby3wafF9vPhOc3ys9zoPm89g+/YBbViLzsEznQ0TWc9XrqedQ1GvbeS0TXx4P5lNRdtUt7+/Ff/faFTR27OvOZw++r8i3In8Tl7ls863n9/Cml4vLxXH63H6RXlnPIuW/fZ3+xKpLEhNWPsLuY75yE8gL1ncLrc9i1gnpy0ec11a/5b9F/B75LB/ePGqXTV2f8MoK8176ufzZ3bLuFa98hOVd8X73i3Zuuvj7rx2Vv+dnHvg1/U6kq/ysvt/SMGkfzL8AAAAAAACQhDKc4cpBC15qqYJof/zBJnfppdlbkZf/8jYD3VfTp/Vd8UyhS3lmkfw7DjCpWUZylqwJkIgBrncG0q/pY9/egfyZZMBPPv55+retMfcW5ADJPb8Wg3v9vvw5Ygd1OBDw9/S/H2jSn/mv6E/XbfItKz1DH09/nf5Evv5f0Z/97BB9JB+fQp+7jejfXrOXqh6nfxNpUNQz5nhmbIbZhmc3iHwyziwvV0tt/Xt6/ts9a+hXl6h0+9Jtv6Z/7Y6/ry//1uZvr3x8rHgt71YU8rXHhry2+a138Ofiv/08/es86299+YxfOy5nWfHXVQBL/r242MGkM2tD8jAHtrjsiPf64w96VHkSzzXBVg7aRb52VsFWK497/laU7XmH6E+d1xV59Dnf7yVum7LFafLHdXtVmnF6ynpABeg43Z39TOUfZiLe2/49+HMlHqxSMwypUMv95SzGX9Pv0+05G1af3Z+hPns2Zn0mZCof0a+t67P7rfpMPNepz/JZPjgvfdfKo6ZsysDvBVSzTtWd/yTzJecbFWzlIOmwykfoLOUL6D+Kr/bHM+f0baL/ePN19Kf7j8b+DQAAAAAAACB5ZRhw1UFRPajlWUlqUOudHZgvZ1/jwbU7Y+zPxeD3z342QP9sBzV4ppWelffnX6uhT//PeEuQz3bzzMKG3L7H0FH6HV1NX3aCRFPogvs/7w2iCn/2Y5NOKshqlp1eNO9q+vRzx51gCH/PTz8w0zszLgLPpnL3HHQvie4heO5X9Ef/VgMyoKRxIE8HvuVWE2N5rexb9Jvnx9KXnBmtF1DNwrH0x1d9wYrb/soJiFx0/Vii0+fiBzOsv5XLzf0BLn5c/yb2a//LmwPqtzaB/K/OFPn5DP1GptlHNPCsN58lSebhH6fJw19tJLmUuXt3wp9DfK+tnK/C8/i/vPg6/ZsoOxc4ASdR1jmoav9eVtnypLfZY5WDtFV2sCubz/5r+t2b/mXYfhw0Fb/TMX0zAZHlJ5uTa4WVDxPgZRxEnK6Cnf/ywa+Jxo2RMyJD67P/GVKfLbXqs2y2VMhUPvx1pXltU599w6rPOCAr67P8lg9ZNu+38qgpm85vLvJl99VEP9hLAz8VeVZ8xzgzfjPi31BfTU/U3WPNwbJwvDWLLDd6djAAAAAAAAAkq6z3cC28j+j3p+3Zc3wJbkEg95XV19Vs2/wEBjx4sP6zQ3TGCth4lttK3tlTPBvLmdU1eip9xgn4qUDlF2PO8DMnIfNfEglCyECR+D6vXUFXBpYLc0BJBXDOvkb0Z+PcQNCfXiw+u5wxdoY+ttLEu12D4tnv86uNIe+TjV/TH9zJaJGvzUFhnt3nfC49Yzv/4uXhP7+5gf7z6UNydnAiv6N0jv4gvqP8baLoQGDh6SDaOvObRM2M5RmOTfQnW9M9JzuR5SfONiRpy8cF9B/Hqfz4L2/+iv70W+QEO9UBCbXfbSAv+IJ0nn2yZWB7OPWZt3wE6krz2qY+4++mP1uwPsuPyLJpp4v4rF9+4Nf0rzxT3Dmgk4QMM5I1PkD2v30H0zy4rln4Kzpzw5rAjH4AAAAAAAAYPgRc88BeKqouhZldmxHPFvN8LnGJHQxQJ1qRM8h4O4FvXRE7qJLXGa4yYC2+x/XHPWfjVkxA6S36zekv0gXXE/3mbQ4o2ieDsbeG0JdhBVQziX8iGnd2tntJLriZXmgetvYV5a0YfnfDX4VuwZA7a8ZlFN/sYjkbs1BMXhPpIZfWhwZUeUuTHqL7+HnZBh45WBusK4Y1wzVt+SD6T2P55Elv0T+/SvS5eV+kP7z5kQwo2kHvwtZnWZyoiesz/Xs4l0SDm9FCy6YdAOctDV6toS+FbCmTMz0D2A5Im6B44CRwfIDs9Os08IG+7SPzFAfhRfolvzUGAAAAAAAAIOCaC7lU3cz2tOmgpG8fwqRwcMRdPq32s1Qn/nI5Z56WexhawSh59nSzr2Bu5HLi516nf3z21/Sf58UPbOR1hquRbr/WD84R8Uw58Zz//dpe+sPPPk//kQNGetbuxxvSzARL0NkNf0//9vUa+kKMYBVvLyD3Sf1Y3+HBgWRyt4OQe0oGZ+am53sNR4w8LPIW79v6xZunUM3f8NJpvZ+rI+q1MxHvPT36vdWScnup9Fv0kcjjf7YwnwHycLwkO5rOX9ni37F+Df3jT71ldFgzXI20+xmfoz+Mu4IuEvUavbqXfuMckEj/eyQtm/KRvj7LZ/nQZXOtP8/b9L6tIl9exDPByf85cy0fU+hz3xJ/Z+2R+y8v9tC/HgzbS5u3R/k8/e7V6AMSahYzAAAAAAAA5ENVStDXywyfVIVn4TUFZvDw7J6w5fTq7Nvq7wLLt2+zz14v8J6g9lmqX3BnAAZen2di6VmT5szbuc0qsj+beM/Ha+h33/0VfdFsScCBBXMGd/GeX7phgD52zsbPePadd3k4z16TgU/5t/q10py0R57B+7T4PmvF9ynUyX3iMJ/fN6tQ/RakzyKu088+g7gnTRX3jOHqsT8szC04nC4fxHptzmPWWfHVbFzz/ezfUtz/whfpV48RfTmr2bne/GCfhT30s/NvfkzleyffCOqs7vaZ2pnvtWOdhV3xv7fnb+08LgTOHO85I7v4DHwSOC6b9ucKnOk9Bk95Z/7va/D3Pk6fy1COQunvRlmkVWwR5cN8L/N7qt/Szmf5q8/Sl484r61+S3uLAzdf2vkv+fIRzA+m/dB/Y7cX+rmev/e/duzf3F9fmfcVVwO/sX4PX7oyTvt/ooacfjcAAAAAAADIrIwDrhWAB9i3WAHXAhhOgCWvogJKI0gGlDwBbyh/HOTKMeCaTygf5UUGcckTGM8GAq4AAAAAAAD5hS0FID4xyFdLyYtwkC63eYh3QhmAvOH9jb/+RfpP+mbRQPkoL19tpC/dZp/wL5ttHz6if37Vu08vAAAAAAAAJAszXIuFnIHmLpX28y5H1Qo1w9VZPhu1jLpIOJ/TWmabJ4Hl0B4qnb7w5kjM4PMuVQ7wb41RQHHSbGTylkgz39J0jzhpZpVfe7uFooLyIVR2+VBbRogrIdsMAAAAAAAAQHIQcAUAAAAAAAAAAABICLYUAAAAAAAAAAAAAEgIAq4AAAAAAAAAAAAACUHAFQAAAAAAAAAAACAhCLgCAAAAAAAAAAAAJAQBVwAAAAAAAAAAAICEIOAKAAAAAAAAAAAAkBAEXAEAAAAAAAAAAAASgoArAAAAAAAAAAAAQEIQcAUAAAAAAAAAAABICAKuAAAAAAAAAAAAAAlBwBUAAAAAAAAAAAAgIQi4AgAAAAAAAAAAACQEAVcAAAAAAAAAAACAhCDgCgAAAAAAAAAAAJAQBFwBAAAAAAAAAAAAEoKAKwAAAAAAAAAAAEBCEHAFAAAAAAAAAAAASAgCrgAAAAAAAAAAAAAJqUoJ+npZOHbsmL4GAAAAAAAAAAAAEDR58mR9LXllGXCdNGmSvlU4J06cGJH3LXZIl2hIm3BIl3BIl3BIl2hIm3BIl3BIl2hIm3BIl3BIl2hIm3BIl3BIl3BIl2hIm3BIl2icNvkMuGJLAQAAAAAAAAAAAICEIOAKAAAAAAAAAAAAkBAEXAEAAAAAAAAAAAASgoArAAAAAAAAAAAAQEIQcAUAAAAAAAAAAABICAKuAAAAAAAAAAAAAAlBwBUAAAAAAAAAAAAgIQi4AgAAAAAAAAAAACQEAVcAAAAAAAAAAACAhCDgCgAAAAAAAABQwYZ23UOXrn9L37Kdp+6lU+juXUP6NtHR9VPo0ivcy7rD+gHpLVqn75OvaZ4X+tpQDoZ2fdeTHy5duos+TukHJZUn3Oesp6P6EemjXXT3FfdQ90cqb12mn3f3rvP6CaUJAVcAAAAAAAAAgAo2+pJxRCcHyQ2rGmdpsJ+o5iuj5C0OiN12chX1H3+L3uPLvlU0sNAfdCXaLO6rHbzLeU795kWB50Dp4/xQ15qi9n06P/Bl4xz6UpV+ggy2LqKB9r30rn68v/003eYPulI/tc6YQhuq99I7/LxnF1Fv6yoZhC1VCLgCAAAAAAAAAFS6/kE6y/9aMw6VWqq+SPwj7t+wuZbaH5lDo9UDRBfMoWVLiDa/6pvBumQLvXffFHX9glqaVUs08PPSnrEIPjI/EN255cc07wJ9n8/Qridpc+0qap8zikwMdvScu2gJbaGXfQH4+va99IR53lU3iuf006DMkKUJAVcAAAAAAAAAgEp2UTXV02k69RHR0Bv7OMZKe944T/TRIA3QOBp/gQmXqZmI7vLwKXTbZv2QpV5GaI1RNG/jWzKYBuVGB+PT6V9FtVZ+ufSKRRSSZajmEjt/TKHlx9+i5VfpmyUIAVcAAAAAAAAAgEp2QTXVyCvnqXf3WFr2yEyi3f00dHaQemur6S/kY6zWu3zcXMxsVgC/WmsLCutSysHUOBBwBQAAAAAAAACoaBdRdW0/Db7RT3sm3EhTeauACXup9+fioQnVNJonuMqtAfqp9eFdIXu9QsUx+WHmD337sbpGXzuT6vtXUeuu8+Q5j1YFQMAVAAAAAAAAAHLGZ6O/bCmCcKVtFI2fQLS5dS/duOC/ynum3jCOWlu3WNsD8NYAe6md/EvEgyfNgkqgtop4bsmzdJudH0Rd8LGJrl4wh57Yt4qodSZdZj8ncNKs8oOAKwAAAAAAAABAhZt6Hy/1fpxuGaP3a73qPrn02zmRkaSCbNHLw9Xem9ivtXJMve+oNz9snENfcjOMCrraj8vLfTRVP2weL7ctBhBwBQAAAAAAAAAAAEgIAq4AAAAAAAAAAAAACUHAFQAAAAAAAKCknafupVPo7l3n6ej6qZ69FO19VY+uF/etf4voo120dHLUXopv0Tpnn8WwxwXx93dbz6lt7adUSZwSh7/bPdT9kb5pO7xeftcjztfIkA4yDdRryXTVz+PfAMqTp2zxRZQlO9fzXsb24/68YJc/t/yU/16mlQoBVwAAAAAAAIAy0Ns6k26jp/UeiVtoSdjZwTcvoktnDNLdb/Nz9lJ77Ra6jYNAEgcZF9FA+179Gm9Rf/sZus0OCnGwduYqqnnW7MXIz6mlKmuXz+Klz8R/Vt+0DP38NFFttXgGC0uH0950kPqpdcYU2lCtn/fsIvEbrAoP6EIJUwc0btu8kJ7T+UFe7pvi5HoOtta2jrMe30I1ojwGAvC6/C2Tz/GXPygnCLgCAAAAAAAAlIMlW+jd+8ypaKbQne211Lu7n4Y8EddF9Nzx++hKGSkaRfWza4lODsqZsEO7Omlz7Spqt054NHpOMy2hLfSyPgv90a2rqPfOZ0r6BDcDP1dBMDlj0Q52Taim0SJdhnY9GZIOd3nSwahv3+ueIOqqG8VzwgO6UMIOP0Ot/bXUtvde90RPHm/RU639tORZ60RQdvnT9yiq/KnnecsflBcEXAEAAAAAAADKVf8geeJ/zixOZfScx+VZxUfr29S/imqtZdGXXrGINuuHeKbfqZNEddX2K5SSUTR+AlGvjIi+RS+fup7qN78iZ62eHeynevt7pU0HV80l9tn41Rn6y+1s68DG0fgL0s/i3rzQzi9qq42ATOUPygYCrgAAAAAAAADlyhfgyah2FfU7y6LdiwoiqoBlXwlP4byoulZdOfwKDd60ipYt4VmrKpDMwVMnpJY2HQCClljbbDgXBFMrFgKuFeEtWjd5Kq3zLX1Q+9LwkRds0gwAhaLqnWB9VBp4b6bLUGdCMdAn9kBehPKGvmpmubSramzgzMLC3oGKqFcvmxxxMqVS9dEuauVlzt+eQ19KPzHPMfraGVSv932NIgOWm/c65VLtXVkqJ80S3/GScXIJd/erp+nGa79IU29fRQOv8kzEWjITXEdfOzNjOsBwnKfuZVNL5wRjcquILTR/2c6Ipf9T6KYlPMMV7VXelGDfFwFXAAAAAACAijGFlh87KmdePbdE3wXlY/MiuswE02fspVn7spyRecEcemLfKqLWmW5QXl7cQAcvgd6+5Fm6TT9WO3iXPFlUaZw0S7ioWgVT6S5q4CXiF9TSrJPidj8vGdfPiZEOUEl4q4i91F612rvNxPq3nMMMU+/jOnWLUy7MpWSCypA4BFwrmtpf5j1nw2YAAAAAgGKBvipA1vikWWYp8/HHaZ4JIGocFMq4xJmDjc5rmIu3HE69TwXt5eW+KURX3UfvlsrSafP9nDPMj6J5G4PfMWM66MexxUClEPlkg5Xv+eLkIUWWL/txcXFOqCbEKn9QNhBwBQAAAAAAAAAAAEgIAq7F5qNddLdnCrq9ZOE8dS8V93n2WVL3+aepH11vv8Yi2pyy99PRr2MeX7orfB+SwGcpsz2NypbJE0PefCB+54+dbKD2++J84+YM7x5gvBcT5zXnNdYfta6X5l5fcv9Nzu9y/xf9XUS+fuGcmwrye4tyd8QqMvI+k378t+L6Ufk88fdLd1rXI8pSMRHleunk8KVQ5nua73B0vbW/G1+sJTNM5ge+z1NXBF87UB/p+4uVygPWZw77XdPkIUmms/UavnRx09reRw91bKlxfkdffrB/x6Fd33V/64jnMH95i953XV8WbtH3A5Qjf181uGeeU7bSlD8mn+c8zhdvG1+KMrervvoipG0uR4H22/O9VZp4x0zqPm99O47Gk3cMtO5QiWcYgCiy/hR14jlfnrfLhOznq7rV7qv44w/+8he2jN77nJnU2oeyVWr4N4w1nhbP+TiVqe9rHlOXcuz7IuBaVESGepio3Zl+vpfaa7fQbXKgFh9XhLedtM+ouIWWVNkT3c2Sibeov12fodGPK9YZq6jGOstefztR64xgQYHi1Nt6I22o3uvmgf5VtGJXluHAzYvUa/CeTE/dQbfRFnmdNr9Suh13kQ61f1ftlI/nlvTTylXZlTF+jdt4r6p9q2j6a4/QbbtnUr+4Xt+/l3qLvXxcUE3VdJpOhXzOs4P9RBOq5RIXbgDnn1zprUd4T7D1vl+e75sxSMvkc3SdZQVm+XUC9ZF+rBjx561tFfXwPvN5xSWw7Ed8R38e2mYfhFB1edvb5jXcutw96CFwXrziSRr3yhHndVofzjIvwsjj33Eh0XNWfgj8jvq3rtb56rklr3meEyhvoj4ZWGgP8rnDuYgG2ve6y0S5LgYoW96+auS+kIE23Vu2OJhQt2KsUz7V5T66MuLlSkHmdtWtL8x37m8/Q7eVe9BV/Na1reMCv3X2W1GI9lrve6rSrpaeWvTDEkk7XW58S5wB0ttC82d68/zmhf4DU6JvM2MKbRi3R5Ut0QfpbV3lxAU4wOYtf1uopnWmJ+jKB7+8zxH94zrk1JIU0vcNjKfFc+om231f1T82Y6FAW2b6vk7QNdiWlWLfFwHXojKFlnsG9qOofnZEQDTKR7to41PXU/sjw9sX5OjWVdRbu4rutPajGT1nFbXX9tOeN+xZkVC0ljxj7RdzEfHJRLPvfS2iZc5rLKLneH+mkie+h1XO5FlWs06YWmq/3aSFuD7M8lZ4r9HgWf7XnSFvynQ9n5pV1CMbNhPdeZf9vUT9JAPv7hlplUW0/ZgZ0Og66+QgDfELytcpofTR33vJs8H9zrxC8tDJ01YnQ9flTraKqstF2ux7nG4Zo5449QbRiegfJPnTQAkR+cEa1POZjoMHX9RvbfLV1BtEL9X81rqctK2yyskFc2jZEqKn9qtA/tCuJ2mzaJPbRX2MoQmALaxN99tCLwdmzZSoGO3q0K5Op74wRs9plmfXLpt0iJTEd/TW16Pn3EV3Vj1bAWkHlUv0Qfb+2JPnub7Yc9g74q9v30sb5+qaR56xv1+PJ96ip1r7Rf/ZPsAxhe5sr6Xe3f26fyyes8L/HChdIX3f1/YF+r52vvKMc8LaMt333fyq6fsG27JShIBrkeFIvz2tulZUXlk5O0i9vBQmbbAgJj3Tza9X1awAUJJU8H3g5+dFY9dPe0RjSNwZSp2nUyeJai4xjVotcew1yDc7trZavKKLz1rLM0K/xFGhJOujgon63tnhutw5Q3BkXe5Lm6vuo9xm40DxMYMQI9Nv3U+tM71bCty2mZwDIfbscwDIAp/EZ8si2rzQLVvBJYslJG67KmfVu9+5FLbzGTauV5/N328t+00AFcSf53mM4B70VSc0tE8WZpc9vnj6vh8N0iBdn0gfG4pVWN/Xmiag+77uChM1a9rOM9z3Ncql74uAaxHhqfi3beajBXrKtLhELvmPclE11UcsF06KnAEHACVqFI0fr64NvbGXar69imbRXur76CwN9scJNmYRQC1AfVSMTF3edWwYdTmUuGwD97XUvtd31lu+3KdCsmoW9aB3qRYAxCMHebpM6YBcye7JGbddrbW3HHAvZX8m9bDfOqGgq3tAGqAyZJvnl1hbEToXswJBbmlmVthBecqh72tv4WYuekVtufR9EXAtZh/tolbPrKhRNH6C+MfJeOepe9mN5HmKrsx42b+i9r7wnjQrMznle/MiTydlaNcq8V5qiTmWNJY6NcvRna2s84m+VclGXzJO/J8HM6rMqD2J7EJW+rgB6x3sp97d4+imq0bRvG+Po1cODIpHdDD1glqaVdtPrZ69eEQeWbiF6lYvij8DU9RHNaS2IVGKPJ+Z7z0j4X3uAnU5lC/RLj+8inqX3JVhWwpLaHnzkvWSvU0Bn6gAJ80CyJ5cBlvCYrSro6+dQfX9os8ecsKaihL4rUP6vpPvyNgnObp+ET11/UrPNmsA5YzzPC/lXnxV3BH/FLqJl4IvTNd/VuXPLBdX25rhpFnlQfV999/ZnH3fN825K0ZfMjbQ971sEU6aBcNg9ku5zUyr5hPR+DYGnnqfOvmRWiY0k/bM2kPPeXoTU2j53oeJWmfqqdm8UTGfsMWqMMXg35yFUAaSnNezzi4XsixHbXKd/HJXDmjJM93p20XJpFmxf87YRtG8R1ZRPZ/wSP6+Ip89y/lEP1zJRN6XG3/r5b21fEIsXzksdbIB27yK9sy+Q5VnMSipXvGI6FyZ7QH4pAsiP1SttpYkqk3Lnb2bYhH10b5VIfWRfrjoqJNNPLfEqof5kmW5N3X5/Mn670Pqcigndn4R7fLsvc7R+XjCypu6OLPwRL2kTlypH+OTBImyVa8ehbLCAbQSX/aeBF9fNWX1VbM5eavsY+rXURfVlv3N1aU6dSBGu3rBHHrC8xxzsYIhPHDVbZRcwin6g4HnlBj+rb3fV/3W7qxet+9rHqdn9njTTs4g9i5z5RPGvrNxLrZ0gdzJ8lbMZaufVljbGsmTJJvtwWKael9I/1lc3JNmifK34Rl5Al712Ewa/Db/DaZxheETjBV33CHY931Xr8qKR/d9yb/9jdX/Cen79r5Sen3fqpSgr5eFY8eO0aRJk/Stwjlx4sSIvG+xi5Mu3EGq2z2T+qyTHhSOOrrGlYR7gqkQ3PGfoU4k1p/Q50SeCYd0CYd0CYd0iZZ02sjZ3nwAIu91tZqxRVuO0vI8BEWSTBeZJnk6GFloKEvRskobOQODrJMJZkvn/2eLf/k38kw4pEs0pE24xNNl2PVQXPmtr8omvyT8eySaLuKzXbrwtDy5kTmJaykrlzzDAdfa3TOKMu4gYzei77u9TM47wWkzefJkfSt5mOEKEAfPFrD3oQEAAAAAAAAAAAiBgCsAAAAAAAAAAABAQhBwhcKz9uXii9ybS/yn8BJ/cf/6t5x7zH28B4y5T+7TJPc14Q3vzZ4zwX29jq5334cv7j4yao803nOEt7Ht9exzZe+xY56nL+JzhfG/j/d57ue3nwcAUAqOrtf7GXv2/FYX/z6T/rrQPhO4ecxzdnBeyibuu3vnkNU2qBPAbF7k7idWrPtYjZ7zOL1XJkuq8onzkNv+2lQba+ejdHnIeb64T/YDzPM8fYaR4XyehVsolfLuY2f3X5x+ju/xaOL5y1RZ8DzP15fiMvKx8yZ2vyN9OfKko7yU7h6eAJUufj0UUtd66mFVb3jaeNNecz3kb6+tc35cunRnUbbXZuz4sf4e6uIdO5rnZDu+9PeFAuNH+Xvoh4oNn7fl+ONlsZ1Askx/Y8jbZnv6G24fRu65aj3Hy5cfwtrZQHykTz9QfLjv+y76vrEh4AqFJSqTpTNXUc2zb4nKXV3622upSvyXNTnwf5LGvXJEvg6f6Mg+0x03mi/f4L4PnwSMA6uqUZxCy+X96kQD9e173ed5KhDzPH59fZcPN7q3nVxF/c7fb1EbgvsqW37vDdXmfXB2aQAoDVPvOyrrLa6rifexduo6755tPEDz1IX7VtHgHVOdgQifUIFf46lFP1QdTe5cikEI17/yZGxm6xauQ8XDS7ao95UXbOdS0i6qrrXODG75aJAGqJaq1dn6MuYhg4PxtYN3Oc/hk+D8rScwW3gq+C4+j+hrVFUtoufMdxAX3iNe9XI4EDqTWidscR7jz88nNwoNusoBmHj++Ked1zH3+/tSz00Q6bDMG+iQ/Y5xe/Rz1ElX+cz1TkodXq/3IHZfBwcQAEpXvHooZOwi6qGBhW7gkNt9bq83m7POc52zSLXXsh7yt9dWXfReMZ9gTNSBdQvJSRczdnQPVgkZxpeZ0k4G1ybfIU/Y5qSJ/D30w1BSNi+6UZ0MVf/WYf0NPuBQO9jkeY4nP1yhTuBn8kN/+2m6zQ66cls/wx8fqdMPQqlDwBUK6ujWVdR75zMJbaxeS+373CNyU29YJBrJQTJDOu50eN7nqhtlpyBRooLcsFl0NL5tBwOm0HI+I/nmV7xHr5ZssU7MpUeXAADlQNSFG5+6ntofsepCMSBbemcVbX7VPfjE9fL2Jc/Sbet3UffDoj3Q9SLGIRXg5KAasMrZRevpiDNeGUfjLxD/xMxD0pJn6L379EqRC2ppVi3RwC/SzRItEoefodZ+0Xe53VrlIr5je3st9e7u9wRL5YwxMwDznfk3rC819XYxyHttH/XaM7FE+ZIHM6SLqNo+G7tjC73sC2gDQBmTYxdRD/nq2mVikORvr+VZ53V7vV/UOe44plQt8py4Stab/Xup7yM7gJZmfBkj7YZ2PUlPXf8wtZd8WgGrb3vFzff6t35qv79PssU9Q7/pk/xcHdzk/LC5dpUnP4yecxctsdreo1sfkf3hfJx4DkYeAq5QQOfp1EmiOjOVZdj0IM2QSyKsmRmiUfQst9PLXpLnzs7xOk2nfEtQAADKVz+1zrDr3Cl022bvLAA29b5naMnmVdRKq6jfBM2grI2+ZKwzYD366mmqr91CezjienaQemurrUOQ8fJQ/Ti70R1F8zby7C1n+FvkfH0XwzpgzJa0r6J68S8P4oMpIGy+w5NOHJzdH/rENLjf9KzoG1nLgf2ziQGgHIXVtfohy9T7eNWeaq/77i3Xue/95F2AkWF8mSHtzg72E00YZ03EgbJjDiBr9Z5AgOmTWJMJfNtxeWMS5+nUKf9rQDlBwBUKaBSNn0DUF7asMHFv0Tox+CDPVgFq2UvhRAyqAADKEs8KcZdDORdPUJX3o1RL7XgJdG1gnysoSxdVy+Aht80vn5xJ7d9eRE+9+o809PPTYmBabQ1M4+ShMuUJPAuXzKEn9NLEpbzHsR/P8vWn0/HHaV62/Q4ZTNB/r4OvCLoClLuY7fVStRRablnyw8Cuk2UiauJMlPRpx1vo0MnTnoAclBlPvyUG33Zc5qJmtI6i8eMpfNslKAsIuBZApo21Qzcml9RJD5y/LdJNyB1ymWBwY3GbbIQ273WW2vM+q96TZqmgrDxyJO/S+5318/XhObr+jpAZrur9/Ev5YpPLBrx7+8hgr9yX8I4y3AdNbfpdqoMxzm+X4YQgADkZfck4ov693iXLhqgLb6x9zVcXBh1dP5NWpFbKpVVq5syiwIk8zNLnzf4lW1C6LqimGl71sesVGphdS6OvuoPaTr1CveIhZ1ZHzDxU9GRwOWKZvtzaiJfoWnn7o13UKjo53q2JtAvm0BPPLqK+lTd69niVS1yfuiP5tjgfWy8BQOGlq4dCxy5B3F7zzFbTXt/51OKQvaZ1e+3f9qUkiDGm3NroLmqIe8KoGGkn+0qvWX0lHh8X80mzID7xW962uZZWL4h/EHj0tTOpXu+fHkXFR9ytCHm8WlfEJ82C7CDgmmdcYG4TA0p70/L4+3OoKen8NzmfWCph/H28Z8LNjrN/nw4iy5Ne8Ebi1neTg3De1HwyP2em3Kg66oRV0abQnbwnWutMJ2C9YdzT8gRZfub93Kn+VkBOBpHV/XK5iPgtzXPU/nP8G+2ldtEhcf9eHQ0u/X2OADKTQeyQM18DnzQheLKfknbVferkEdZSOvf7ibpwwyu+utB+jjqAKDuqq0xgSe13zfX0ZevtwyDitR5ZRdOfspZMFyqPiTr/ssmFOCiTzMEr2SabNBIXz2DYbKvjSzt5ELjgM4t5UC7yTivRMtk2jqL6m87IQGPNJaatzJSHSsQFc6itrc6zTN89qMAn4lQHGpzvp/dpjewbinLXJ15P9mfM7ybue3eLdysAecnywLw//5j+S2nvI6fqmmBgCFSfFgedg0p7MkGotPVQ2NhFXZz2etlU2V67e5WKumvLQm89JKn2mmfiO69T1BOEttB8Ob7kixpj8szU+CPsTGknyDq7yu0r/V019fNqhZEfxqehfvOC1JslVg/1rrjR/Z3lCdfc/X1j4QOn4vfnk2M6ryMvbhqMnvNjtV+yfozjI++I/nFxK1S9WfptelVK0NfLwrFjx2jSpEn6VuGcOHEi5H05g6jKfLjBN+4U1+2eSX0jfKZm/hy1/Dk2zKEv+esarkAXnpYbjZslbeHpAqw004YrV9EApBscDlM+00WWo9ZxtL0Ez8JcrPllpOum4i1HHHC9Q/Ttj45Y8AL1b7i06cIBV1HF2SfVyI/h16WyPZZnmNeflQOseisd2eeQt/cS1fZTzbfd95FnWBaDTv8yfeSXaEibcMWZLsn1vXNVtPlF9tM5YDByfaDiTJv8920zQR0TLsl0MW0mt+9XFnXwM7Pk8wsHXG+kPTdZJ4jKlzzXQ8mljaoXBvikWc7JJ0tX8nmmUPVm/tt0TpvJkyfrW8nDDFcAAACAUmKWoT9rDVgu0Ge7b33GM3Nk1uxFJbrcEwAAAACgdCHgmg9yqjxPCVd7j9rL2u1lFnykzbk/w96n6fERBvM6fPFO0+f3kUt+nc+l3u+Fc97JzfJ59utYS0bMPrS83yqfaU8t91eXwFTys3oZo7iU2QTqkScG2UsjlrvK/GQtHTV7AzsX8Xvav4ZZVpoyy07lJfja3j2I7bMqGv78N7LT/r3lSlzCliJnKAsqna3XCClT6nXFd59s0jlYhr1pJy5hn2UkyN9cfV77M/p/t6Fd3/V+fisP2XVCynf2TX+d4E+H/C8/yZXKy/z5PPnIs3xO8JQZvpgtRuzHRFkR9Z9nya/4/e3tWPx5tZSXy5Q657eQ+6y5y7rM7xKoO63H7fwcuie7rm/k72vnD/GQN39ksQyTz+5Pi+gm36wCuVeYf9++a++g9pNPDqOPAZB/ubarbr1p+iIhfe9A38Z+j7DHi4invXY/c7C99rYndrvFj8n+vajfeEm1v36z+fuOpdFeW30Vf3vt5AtzsfKUSFvV1wurj739NX9/yN8uAJQmq97sSwXqTadvy/x9X7uMmMc85UYtBTf1azb1EBSxLPqxzm+uL57f2YzFPXW2zo+evkCcNr24IeCaD84ZX/fKPUPr260z5W+c6yy75f1M5X28r4u+L3ucMdWeW+Y9+tvPiErMlxE5ICKn76vn8D58K1dZlaLI9Lwx+jv6cfmZrJOZTL3PvLb4QrWrqO+Yfp64eKeR91PrwkFaph+rqirxNRvF5oJqquYTj4QMnM8OitpInzWRByTzT660zoio9ozz7pMo8H0zzO/F+VWdzMO0r/w6t520z6woXkc/pnBj6s1/fBmpZXz8eWtbyXv20MBSd/EdeT8l/bgsC9t8lf3DRG1vm9fQ6SIqf8/exTLI+CSNe+WI8zr2JvrcyHjTTlxGeEsQL7UX54Zq/dvJvTRXuQMRUSesoIfdz+47W7ZdJ1T5zr5p1wmBPCReZ0A0zsU7iFOdB7m/tP68/L3dzyvyB6/aNt9H54/5y3Qng/dqkveLsiLqvyXPmueJi/j9zVYsnD/UcnDz+BaqEZ0JdDpHhtMe857iVd5917k+My1Zpvw89b6jskxsXqjbYB7QL+KTKOqlUHb+EA9784fbP8hEnt3ff1Z7Jk9O5TeK6mcT7XkDAQIocjHa1ZdvsMqM3gNalT/eH5fvD+l7e5au6n7z6j3O4/3tp2W/2RNcKCq6vR6nP3NIe819eOf76nbLtCdcv72r/44oWL8Zgb6jqd8OFW/NodrrJs/3dttr7qO+QjeZ7yMuah9yXT+L+nijHM+E1cduf43zXV3r2EB7bfpDUBpUO1/62wkky6o366oC9aaTVhxk0/uNm8efmyDqaxNglX0bUY5EHW5ODnV0/Y3U2s/1jap/49ZDxUOlTTlsJ5ComP1YM87pcmJG7jhHtigcL/PU2SqmsJnHlLL+Fel/7Kj4u0xtevFDwLXEDe3qlBmTzyBpjJ7TLAqA/8yUboXHpt4uMvhr+9wzKIpMbw8q1VkY9fWs1FL7vtIqBKXnNRo8y/+qI4dOxSXIsz2LRnHDZqI777KDe6LS4kDCU3t9R4QWWXsV8qBc/OgnB2mIX1C+jr1hfrTe3f1u8H6k6O+95Fl3D+FwoixYHWl5ZsiTp63PL9KKH3cKg06XAM7r7sbp8qzR/YMkfxoj6ozuRcIJAjF5dup+nbf49n200e4AmTohm45qWB4SDfUy0UIX9RLnJdb+lvp7D/zcBEJF/thglwmVP7I7qeFb9FSrbzm4eF15or9iKEsQLmZ+5kGFPPnB+l3yDMj773xmxAcTo+fcJTq6z9BbxRs3ARDSt6tctjwH+WW7lZ2hXU/KfnPbHLcW5/LB/eY9h4u3gHB7vdEM/EPaa08dk0sfXtdvbc5JDQVdvz21v7jb63fv0y2p1V6rX5JPcOQdk8g8lRXVXt+55d5Ae9338mtor6EiHN26inpFWbPrXxlL8IxzRP94H58cahV17+Kz+V+PmEDFcsc57gEOa5xjmloO3j67SE5SeGHnKhWgt8bo5QIB13LgW84bvuw7jNVZk0f87ddQ07eh2PCZnnXw56N+2iMGJyQrrvN06iRZZ3uuJY69Bvlmx/pmSMmjv6Kik7Pw5JLVcTQ+bfAy5GydI3p20qjvnR2e5WEvgZBbaQT40kbObHc7FpyW/e2i3+Gc0T24NHKkufmFiY7ScXt2qqgTPEsudZ2Q9XhUzcxxX4fPVK8fKlLywIWD87h/FpB3yaXcViH7hPEuw9GvA8UuXn6eet8WWrJZdB5F3dh3b56GG/4DPOyjQRoIrQen0E1isPRy8U7hAxDSt6scFPQsaY3d3/UR/WZ7a6ycX6eAuL12D+uFtNeedMm1Dy/qt5ne9o3rt2KuNaLaa5NWPMvK/j5qOXP2Ni9Kpt0HKFmbF3nKwKUzVolxos8FvJe8GPu0ij7Qlh9nmAAD5S7WOEe08zxJYeUKXqFangF6BFzLgW85r7l4ZgGEMoMyPYWbjxI7f6+mb0OxGUXjx6trQ2/spZpvr6JZtJf6PjpLg/1xgo2ZAqiWi6qpPmL7Ai/VwXXyTdVqd4lJCeLO+W2iU+EugdBbaeRABrCd16ii1hnFF3QNp+uEO592Pr9TJ7gjvph4xpKbls7Fd4b0UsH5Y/7mhZ6lUHJbhewTxrsMx1zK8MhueYmTn1X54a1WeMld/Q99W7kkIHSvVoHbhagDZTwbZfDv/r90St8GKC1v0ToxwCfPskK1pDFrot9sb41lLsuvzr4eH3luH979Lrn24UX9tpeXcHrT5T0zg7TUHF4vBvi+babkcubsLdkSki5ZbAMDUPI8dYy5+FYVyjI3jp7j7Uju+G6JjHkgX6LGOWZ7NSbH3SdXUe+z49ztXsoMAq4lbvS1M6je2i8lHtE5k8scmyOPPA3t4mnd+oZl9CXjSC2TxlHdkcJL4HsH+6l39zi66apRNO/b4+iVA4PiET3Ilkuq+qnV3qOXByoLt1Dd6kXxjxzJvQD75b5/Cs+gyDQLZBTV3zRCkXrzvZOurD9SZwMfLllW9fVS5NQJvqIv64TXIrZOML+JtQdf2dH5IzjTRc1GD986gWcb8pHf8uxYlDR5oCkYyJRi5uej62fKma281Q/PdL3zqcUhe/Omyx8x6KWznjyk86J3qwqL+Jsbq/bSnpP6NkCJO7r+jpA+ySgaPyF6qyN5sEL0m1fsKttWSbTXj4TPcI1Tv3n6juVG9YWDMrfXTy36IdprKGNqQk9UvSm34vDsjxyCVyCI8iX7IDzTtY0nmoT0c9PVQ1BC4o1z0i6sMgfFHplDX9IzXfmcKd48mL5NLwUIuI4YPiJtT8nnQJG67ezJaS2dkktXnK0DrFlyvPfFPt4vxT5zG1/8FZzIwM5jM2nP7L3unkc8Q/HbqiI1y6j5hDHPhU0bkIWhn1ZaS47SVr6QuNGXjBW/1SrxG96hBtVX3UjVKx6hzc72ADzjVM801b8RL5fj2VbO/l+xmL14TN56kqr3+WdN+JexcV4dO0L7r6iZtrKytj9TlrNtzV5u881yQz6pWA4zInhbAuczyNfhzeYz7S9bLFSdUPXUHc7nd+oE/wQgUSdsv/M1p/7ii1sn6LxobzkReE5p4fxxZ9Wzbh7T+SM4w1V891UPy83gne9tnXiNTzoWyKviEgzMQUGJNrWtrc6zDMrdJztTfhbt+rKpdJtnn1dRj25ZqM6u6jkTq3itR9TJApzXyWo7Fv1Z+IR+5u/1CS2iV7eIv7lrHPUO//gRwAjQ+79Z/d0N454Onckpt/TwbLdl9Yl1v7lq5Y3O65jnlOaOG6Jc6z68+S58EqnQPrxc7ssn9XO/t9vm6DrF03dUl2I+aVZaV90h8oc7vpL92GdFvasfdoXVx27fkdvr7Uusdl9f7q6Uk2ZZ41F/2kD5mHrfM4F606kTeXsXudemlQ/svHB4vYpniH6t6YOMnvOw7qP4VvelrYegdITVm24/1oxzGj3b97h9arndy0I+qewqZ2zstt3eOFbaNr0EVKUEfb0sHDt2jCZNmqRvFc6JEydG5H3j4Axd1zqOto/AGd2KOV1GGtImHNIlHNIlHNIlGtImXNmlixjoXLaIrBMg5gb5JRrSJhzSJRzSJRrSJlzxpota3SYnbVh74xZK2nThIN9C8pwUOj94kpSarDScNBja9V2qbe3Tt/SJ8MzrcVCbA5a83ckGd8k1T964jfjEcFM874tyFC06bdzfMe8nMtV5k/tl7kmjsuVfWapOdG1ej+M7PCGvvu0VesKZUKX+hrYcDWyRgzwTjdNm8uTJ+lbyMMMVAAAAoFRddSPdSc/SBjNDhAdu698KbHIBAACQlcOv0GZaRMtGINhaTtTkp7Hu/v96BeFSzyzpWqqnVfQ0Tm4JHDidfIc80GH2PlWzRX0zO2triVZsKanZnpUIAVcAAACAkjWFlu992N3+hbcWuME7GwYAAABGwEdqf/U7t9zrzsTVy+r7fMGyWbMX0eZX/1Hfgkp1dP0ieur6lfJcAIY8J0CVdXBdmkk3LsF+uMUOAdcKwGdKf3cEthMAAACAAuB9Kc3MGXGJ3scVAABKW/D8Cf59dnkpujkvh7x49hBXf7/usHU+Ec9z1H7k8j55kjHvfvPZ7sHPszs9n2WY+y86rxfy2fx7gR5d755zhC/2ZzfnWvB8H14Krl8n5aTzTHkSOt4/2v0eWXyHs4PUS4to1lXew6DyBH5VvmDZtXdQ+8kncXb/RPFsUc4H7u9o8kPwd/SXLXtvb1Ne7qEXzrnljfMjP5fLoLlu8qZzPhBxib9P7Xk6dZKobnat73woU+imO4Mnj6pfsIoG/g77KhczBFwBAAAAAAAAitlHu2jp5EW0eckW5wCbPMhm7dco9/08uYp6j5nHt9ASPrGNb6uZzQtn0uC39XP2qZPfqODjKJq34ai6X540dpG7FF5csjqgd5jPQj6OupzPwpfhTQJSE4nE64R8NntvTk6H+SdXUr95XHzHgYUqMMb4pD798uRNOqjG2/HIk/ioPT6rePWI/Ft1wmC+X76vvMT/DkM/P03knNjYckE1VQfWooyi+tlVtOcNnEQqOeJ3PMb52f0dTX7x/o4cmPUu4+9vP20t4+cTC/LS/n5auco9WRjvo7qET1gqyiDnTfm3Om9ut/J9/H1jz9JgP1HNV4LPv6g65CyRF9TSLNpLvQjSFy0EXAEAAAAAAACK2NGtq6j3+oep/74p+h6fj3bRhs1ES77tnnhJBpw4ALT5FTpqRVw58OQETy+ophp9NXlbaM9hO9RbADIdaqlt1Rx3luAFc2jZEqKn9ruzfTlAJvfG/OFO6n5YnWU/7ydUymD0nGaqaX1mWLOAIXtDu56kp0TZspfxj55zl1zGb89CnnrfXmqrWk2tu3bROh2gH9lVRaNo3rfHUetWexY7FBMEXAEAAAAAAACK3YRq31Jjv1qqDkynZKfp1EcFDnxedZ+c7ffUHe7S/my3JMhdP7XO9G4pcNtmCpxQUu6N+dRqaqVV0YHs4eofpLP6quOjQRqk60N+qyl0E/blHBn9q6jWyi+XXrGINqf8OWYUzVvF++avkjPN8xWgH/h5cJbzWZ76Glb+r7qRlvABFX0TigsCrgAAAAAAAABlaxyNv2AETqd41X30rlla/ewi2szL+gsSTKyl9r16awT7cp+9GQDvy7mIBtr20HMTVlGtZ6/bZMi9WkNm+Q69sZd6U/yb6DssU29X+3Ke0rehQGpXuVtQWBfvDNa3aN3MR6jm2b3UfnJRFnuzxsUBd6K+l717tfL7vvwUUX3o0ZQpdGf7adqwa1DfhmKCgCsAAAAAAABAEZt6wyKip+6IDvLwfo61/dT68C762InvvaWXPt9BU0cg3urBM/H01WG7qFoGMkNngpp0MHttRji6fqac2dr2f4yWM115r9tg2o6i8RP0yYpymSAsPwvR5kU/dGcgfrSLWlv76c4t94bvBav35dxzUt8uMf6TthVuVnM61u+o77HJwPhrq6g1bQBVBeg33/k0Lb9qFM17ZBVR68zg99N5M9etNGQ5f+0Rz2c5un4RPXX9Ss+WBzb+/LR7Lw3o26XFOlGfvNxTVieOQ8AVAAAAAAAAoJjxbNG9KsjjBifcE0GpE/vspXZaRXXOGdIXyRMBqRNBFZZz1nbnoj5LInteXjCH2uVJr9zXd4OlOh2qVvuWiJu0Umecv21zLbU/Yva7VXvdyrPY+04wJoOx/Xaa+s9un47+LHXP0m3mc8xYRTX6REvhxN98exz19uubJYR/89s2L/KcKG1k9zh1md/RzRPW7yjy08aQsuU+5y1ad8VMau1fRNvv1WFyKw/evdMK4+r7n1rkBhGzmgnL5XyLzov67+WJ8DbMjd5OhPcontBPvfpmKeEDHytS1gnujj9O80JmfpeqqpSgr5eFY8eO0aRJk/Stwjlx4sSIvG+xQ7pEQ9qEQ7qEQ7qEQ7pEQ9qEQ7qEQ7pEQ9qEQ7qEQ7pEQ9qEQ7qEK7t0ObyeLl1I9Jzn7PjZK9504UD2TNozey9tHIEAPyuvPOOm53D3ii3edOFA9iKiLUfTHIDIL06byZMn61vJwwxXAAAAAAAAAIB8kVsqbKENZrbjR7vo7jzsGwvlYhTVz66l3tZnnJm4R9eX13L7SoCAKwAAAAAAAABA3kyh5fusZeu8tcANU/RjecSBXb00XV18QTvx+FJnu4Tg43JriKW7aOjwerrMeZ71HJ65K+/jJfckl8I7e7jy3+mnxXV0vb2fp3oNd0/iyjJ6zuP03JItznYUt52cSfUFWG4f2A7E/zs6v7m5rKcjzm+ktuzgbRQ8v6X1Gu7rL6LN4vZma/sF3tIjK4H8bW+zMvIQcAUAAAAAAAAAyKcL5tATzl6VBdjflANjM1YRte913tOzRyYHq8Tj1c8cdR7vbydqnXEPvXDOClrx3qd/V029b6vnPLdEnZxNBtCuuk//7V5qryWqF+/1rn6t9zbOid53NAQH4uaffNjaz1O9htpntzJNvc+bFtmkZy74pGe1rSLL7It4Xw68LzrteZyDwo2TvXsbc+B9w7g9+jlq/1w+ERjnKg4kO/eL20u2uPnvvfuyOQjxFq2b+YjcE9n5e3EZqe0JwiDgCgAAAAAAAABQNs5T95PPcjQrcg/Qo1tXUW/tKlp8lRugGj1nFbXX9tMrB+wTPS2i56zA50XVtepKPvTvpV4smx8ZH+2iDZtFlnk26sRVIk/93RZK3dnseZxPSHZn1bP08mF9BxP5buNcE6a9iPKZZTa/WrxbcyDgCgAAAAAAAABQZuqrL9LXIkyoptEhEwJ7B8/qa4XDMx/72quodYZZHo49SwuvljJlmag8NfBzO0hfCFNo+bFnaMnmRe6WAuvfkrNoiwUCrgAAAAAAAAAAIGUM1ObJ6Dk/dpaGh25vAEWr5pLwmdT5NYWWO9sJbJHB18vW25sbjCwEXAEAAAAAAAAAysYoqr+Jz3I/k9bZS70tU29YxGcsor+1TjI0tGsVtfYvoqX/R753C81s9LUzqV78W8FbuBbWBbU0q7afWmd492N1iTw1u5b6Wh/xzDw+un4RPXX9Sroz33sSZzSFblpSXPkFAVcAAAAAAAAAgDIiZ4s+u4g2LzRL9H3L9PmEV+Lxp+5wzxJf2zqOnjt+H105AlErPmHTZc7nFJcZq6iG9xMdg5BrYYyieRvVSbBus3+HpfoEaUJw24cpdNvJVdS7YW7eT+gV8NEuWjrZ+pzms9w7VT9h5FWlBH29LBw7dowmTZqkbxXOiRMnRuR9ix3SJRrSJhzSJRzSJRzSJRrSJhzSJRzSJRrSJhzSJRzSJRrSJhzSJRzSJRzSJRrSJhzSJRqnzeTJk/Wt5GGGKwAAAAAAAAAAAEBCEHAFAAAAAAAAAAAASAgCrgAAAAAAAAAAAAAJQcAVAAAAAAAAAAAAICEIuAIAAAAAAAAAAAAkpCol6Otl4dixY/oaAAAAAAAAAAAAQNDkyZP1teSVZcB10qRJ+lbhnDhxYkTet9ghXaIhbcIhXcIhXcIhXaIhbcIhXcIhXaIhbcIhXcIhXaIhbcIhXcIhXcIhXaIhbcIhXaJx2uQz4IotBQAAAAAAAAAAAAASgoArAAAAAAAAAAAAQEIQcAUAAAAAAAAAAABICAKuAAAAAAAAAAAAAAlBwBUAAAAAAAAAAAAgIQi4AgAAAAAAAAAAACQEAVcAAAAAAAAAAACAhCDgCgAAAAAAAAAAAJAQBFwBAAAAAAAAAErWP9NPW2bQjJafimv5pt9rBr/f3xfg/QBKEwKu+fb2RlURycuD9PdDKf0AO04bIyqp40/MdP7uwZeKvAo7/1N6cMZGOm5/NQAAAAAAAAAonLGX0Bf01Xz555ceo8fo+9S9bx/t6/irvL8fQKlCwDWvjtPG/76Dbv0foiLiymjfGvqr0VX6sfSuuHuv/Jsff0vfEaCOKm18G1FOAAAAAAAAAMi/D39+kKbVT0OgFSADBFzz6fzP6Rd0K9V+Vd8OuIKW4qgQAAAAAAAAAABA2UDANZ8+/AUd1Ff9/vmlB92tBp44pu+N5/gT/Hfz6LGfEe3471FbD+jtCsxjL/5K3y84WwBYz8l2rxd7q4RbH5PfE3NtAQAAAAAAAArtC3TJWH11hHCcgmMSwbjAcdo4cyZtfFvf9MUqZnBsQj8iyXjFg/TT8+o1Z+rnFf1WiwA+CLjmgRNM/e87xK0d9F1TkTxx3Kl8vvCNNXLLgO7l06hK/JeNK+7m7Qm66ftfJ7r1f6itB/iy5htmnixvN/Bd+sXybv3Yj+krj91qVXBMfK6Z2+grO9Tjt/7sMdrheTwNrgD/z1/Q9+XfisuO79M0cbfzLcTjAAAAAAAAADCy1IStsIsKambnOPU/T/SVi4JrdC+8ZBod/PmH+pZFrvz9On3lQr7BwVYVq9ir4xjdy39B3/UHXekgPXbrDNp2STe9ws/7H7fSwXWP5fB5AUYOAq55YIKpXCkQ3Uo/1hXJvruvyDK0mqPzB+k1+j593wnAXkG3/s3Xaccb3irs1v/rUfrmKL52BdV+i+gXZ8OORgUd73mM6G/u038bYtQ39RUAAAAAAAAAGClqwlbYZU30mD5AzVKdMaOfasXfLo3aNvHMz+mfOaggV8TaQdSv0CXivf75pW204+sqVmFiI1/4xgK6lXZQv28C2LTl3XJSmXzeV2vFcw7SL0LiuQDFCgHXcsRbGfzsMZpnHb2at86/uQHvLeuGf7kSdiqzYcNUfwAAAAAAAIBCmnaJnEaaB1fQ0r28uraW+mfwybv13ZYvXPQVop/9gjgmevyNX9C0r+sg6oe/oJ/9t6+Q88l8sYoZM75LvDbYzzuLVp3/JjLQC1CEEHAtV1//PnV7jl6Jy91X6Afz6/gT8/Q1AAAAAAAAAMg3M4nKL9ktBdzVsQEXfkVvNXic+s9cT99vvFWusv3ns78gGnuJe6LwsFiFuCCYCuUGAdeSpTbFfsG3TYDE0+1/9hg9lqdNpXlvlp/1/kzPYz1OG3HSLAAAAAAAAIARok9EFXIy7GS2FIhh1CX0FfoF/fylfvpF/TT6wldvpe+f6Zexgq/rmbdf+Nr1NE3HKhA/gHKHgOuI4JNaqaNKvNQ/9fx31RGmJ0zw1OyPMoO++zzRwXXz5PUHX/yVfly54u4f0y07/rv6W37cCbDydPsf01f035lL2LT/XHzhG9+n+2i9XgbAJ976MfFutWY7gisavq+vAQAAAAAAAED5u5C+8vWD9NjfEi2QM22/QNPqf0GPrTtIX/mLP1dPGfVNWrPj+0Tr5tFMK1bh3e8VoDxUpQR9vSwcO3aMJk2apG8VzokTJ0bkfYsd0iUa0iYc0iUc0iUc0iUa0iYc0iUc0iUa0iYc0iUc0iUa0iYc0iUc0iVcMaQLb1Gw7RJ1QqtigjwTDukSjdNm8uTJ+lbyMMMVAAAAAAAAAAAy4i0GD67bgRmpABkg4AoAAAAAAAAAABl94Rtr6Mff2kHf5a0AWv4+sGdsfum9aq0LQLFCwBUAAAAAAAAAAGJxTsTV8VdU2I0F+Hw19om/9un7AYoPAq4AAAAAAAAAAAAACUHAFQAAAAAAAAAAACAhCLgCAAAAAAAAAAAAJAQBVwAAAAAAAAAAAICEIOAKAAAAAAAAAAAAkJCqlKCvl4Vjx47pawAAAAAAAAAAAABBkydP1teSV5YB13wmWJSRet9ih3SJhrQJh3QJh3QJh3SJhrQJh3QJh3SJhrQJh3QJh3SJhrQJh3QJh3QJh3SJhrQJh3SJlu+0wZYCAAAAAAAAAAAAAAlBwBUAAAAAAAAAAAAgIQi4AgAAAAAAAAAAACQEAVcAAAAAAAAAAACAhCDgCgAAAAAAAAAAAJAQBFwBAAAAAAAAAAAAEoKAKwAAAAAAAAAAAEBCEHAFAAAAAAAAAAAASAgCrgAAAAAAAAAAAAAJqfiA6xvvpujBLX+kb676d7rm+/8u/+XbfH+yztOO5supqWdI3wYAACgih9bR+ElLacc5fTufxHvVTLzcuXS8mXSbmyTRft91BdWsO6pv59NR6rDSpWbdEX0/AACAodqKpp7z+nYe+dvrQ/p+AADIqKIDrut6PqGf/P0ndO2ln6Jn7/0PdOCx/yD/5dt8Pz+emEObqaVvMX1n7mh9R6GphrkSGsmhnqU03uoYRHVGzPMK0lkpGv7B/FHyhDl8naqaiQUKvhTKuR5qnmR/P/X7h4Z6xHObIp7Hecd+DXVZRyUTGvF9N740dedyMMiXnzKkwZF1V6j3KrYyZ9KjuZu8qSC+3yT1mQOX5h7fc9Pxp5O4hAXvfOUvH+kk825Wnz1Prl5OA++/Iy5d1KzvGlEy7YuhDE+lFpku71C3SJgqfe/I8eXdHPPOUM8y9zXExdMXCamPnIvTRsUoQxGvU7r9Ht939n/fkPb6+Q+L+cBFnvh/92I9SBH4nLkdwDmyznqN0N/c326F1GvZfBbxXNVvylf9mEwdk00+cPpwTv0SZNI5vB02nzm/bYYcp6C9TpQZ+5l8kls/S02iMq+RPh+YvFK89XP4mEZdsjkYHnyd4BjS3xdQl2Loe2XJ1DfDLJ+mninuSQcRQtqR7L+Fvyyp1/Hw93VKcSJCqsy8/fbb+lp6a7v/mGp99t/1rVTqZyc+Se068Ef5r8GP8/PiyPS+h9dellrSPZRyX73QjqTWTLgsteZNfbNA4v4eiXlzbap6wtrUIZPQH3anlvi/t75vSXe3TBP+XUbCSKRNzcQ0eUCm3d0p0SFwcL7l9DysbxdCQdPF5I9/8JbMj7vvTtX40sLGj1c3dac+1rcLIa/pItKhKV3eCDWUer7JW6/J/BKVLjL/3Z1aov8mKcNLF/Ud+DM/Lz/7CzF+U/E3zZenqtce0bdzoepjTzoEyl/Ic7IUljZp867+jaLyfX7otslXBvMpNM/I9I+q65L4zbPH5almbeFq32C6qN/G/d5uecmm7gvUpzKtM/3mbv4Pf1ZI+ZD1eX7yb17r3zAmjdK01/6yKvNLObfXYZz+nMkHIl9MLHxZ9Quki/U5VX72l614Dq8V3836jYN9FdMuuyXUtMsfmYJk+j1O3tLlOvSz6NdrEu1GQnnLmzZuOqiPl1sdk1U+0PUE90Xc9/WR5S+8v2L6xc9zW5pgeQsrS/L3jUyLkDow79R7ZtdXHJ5E6xjdzqcdI2YULC8fdy+NzAuq/Im8xOX0l6G5LSd5r3t1GXhuGJ85LF3kfdmW7yzlN21MHfWCHjfk+F1MG9+t82AB+sCJposoOzxu9NS54ntUZ9VnDWl7/HW5rq/dNk7V7UnXe/kuTxU5w5W3C3jrdIrabv80/fzjFC35v/6d/vvf/ZHWdn8i/+XbfD8//o/iecPeXuBcDz3eWUezrxtVBLNVypw8CrucrjIJPaaeZtcRde41R13O047WPTS77x3a1FBDVFUpv8hR6pj/NDVtO04tV+u7fIbODIr/V9OEMeo2u3LmYn2tTOn8cern1hFuUV4faCFa07uBbrXSoqyJdLipropOncnmSP8ourWTy5Fbr8n80reHXg3Mijb5r41m63uKwVBPK+2+uY8GOhtogrgdqzY4tJke7KuljkVT9R25mErfaCbaP/CBvi3qpQ1P0/SOdivPTaWW7Ytp/4u9Iz+7BSrSUM9G6qTF1L3c5HVR5tvbaHrfCuqMPWv0KHW29FH9mjY3b4t2mmfvbtoYPTPEvPc9Vv3i5S9D5UTVl83b30nfXqdC2utK6dJoR7aspP11bfQDkU8UUW923SE6fRuLanXOkS0rnM+pfiJVv2f1OUXf5CebUiJfLKcr9V2jG9ppTX0/tWwxM4JMu+yupnPbZdULlp+lucvKW7pcd74cmGXGbWQLtVHHsmp9T7LsOkalSy51TDb5gMcAK4hEW3vPRH1XgCh/jc+IdG4P9lcOraN51CXHGTeImxVW3EpfxjFiDHrFqtsucjkUZaTuaXrJn2c5v3SK57bP0neUCtUnpealdOuFuefy0dfdSNNpkE4WUV08XGbccKpznhw35IbbeKLu90Ubf52+q8TIdqR2tbfOFW1a1aYnsmh7P6CBPqLpNRfr24Iuk44xDbTp/Y2esdE3mkTfr8TGRhUZcP1//uETml+nvvojXX+kd339db7N97PbxPP4+cMx9Poe0bkRlVZY8EZ0oLxLnH3T7/3Ttf2Pm2WQvuc5S+ic+xtFp0Y0KvPd5/iXzyazzKLYcWfULrgV4tDLslM7++rohnN0w1JqpqdpXnMPfcw9D847YtA3vWOJ07kvO7LjVEc3XWsaDF1e62bR9DEV1JXWQUQ+KJQPR9aJ+kcMhprS5L+RMLphowwYx6c6oammu4dXh4iy9biokJtnZgjaXlQjBp4DoksyfGY7h2ktoncjBrPTrLo+sOT6rK89CSx1Ep3FTEtWE+JdPivapZy2vojmLG8TdR1x/We/V0gb6Pk8gS0o3HQ2l3wtZw8s3cvDEqsPuCfcfJNV/6tgxX5xLfbBmXMDdIrUAWeHHIQSpfoGI/K2CtKmbXvilqFSpNvrb0QEWxm3101Vz8j2WuZB3V7XP3pn+bbXAefp5EkxWLu5ntzwogqWiUqOBs6qe0ae+Jzvh3xOWedk8TnPDoiy580XPPh/sFfUz+8PDG/wOaaGxvsDIyJPPSDKYfOyBvqSvitpmeqYeEGw+PlgqGeFDJbxgZwo3F/ZVPsINYeVPw7YWYG2fDHtDLfXqUzttahFPUtyA9tDiLSw/r6g7XVZjiPDjKIJE/3toirjJTmG0mMjnlgwnF67OhAyi24oozG3GTcMbzQzlVredw+clR7VptWHtGmpVDZt71Rq7qij/S0ir+l67ci6uox1dCmqyIDr0cEUTZtURf/z/VQg2Grw/fz4NZdWyefnTg0cQgcFHCytW0GpR3v13jh8sYKB3IEWj4/fbh57hw52ELXU+fdEEYPEugG6x3lOPXXO1w2qPDLA96t9d3jGhPNenfOcgsKDt2kt1bTjhHm8i8aLAsCN5XC+/Yg710u7uT8n0r+CwmcBcjZMXQ3RTu8g3dtx4wbgHeqeuIKu5YMAdWYmcHlVerJcmTQQjUPz9o30LesIrhwATKyhD/7WDpyE7GWbsRNc5Hzp0LRt+DN6j+wVA0h/50q8D8/w72gXg7ZSL4S6E7pmYS4DLmvQI+p16ujzzDC64WbudGy2BkJm4JnM7IArlx+XdftB0bmhujY6aNoBcfHOohOfa77dntTRpo07rcE8f49GOvXofufvD3YM0jwxiDuccGPBwdCXZrqfc2D7Yup9qD7Rsja6YYPz2sQzrcx7iUug7utspMdrRGdSPi7a1P6HPbOweLB5y/uPuGnb10an5ocFrIdJ9B0eoHbnc/L73LBpQcKDWx0kMjMPRDlunlRHu2/uIs5CsWeWyiBRNY3XB7DkgJxndvBMiKrwvG3PbvVKV4YMcV+dfg5fSnCvr6Ezp1V77QuqB9rrE8dley3bIdNez3OHP+VPzY4ZP1bnEzkBoZFo21bZ381uxUY+RXzO7apfHjewKPtxtTWkSqRoH5ovp2kvzqIdXCDTHJhz22VVBuWMV9/MT3lQ1BecDM6ETVqwjmmamEMdEzsfiPHYg2I8Zs0QDtD9lTVtDVYgofCuXK7qdm5/q9K21yKdWhppYJnbFkz3/La6vRZ1pfn7gx2nZXuddM3I40h/e20HUopSLmPEq2+Sk1Met9tbkef4IKKdZ4d6npATDdwZgKVCTSyQs1tzGBPYB4Pndd5B3Z0hZck/firF/Usrmq5zLwlp06qyWy3JAWzTV5Z5Rq8giA5GH6WXNvkPsBW/igy4/stvib74n4l++c/pCzg/zs/j5+cscqaCqdC6qDOig2yWINlHWXn5UEddH+1+3c7MokHrs5YYjR2nr8Wlg8KiE+IsszBHHXjKdsnWg6KjIQZl/jSsWKKB+0mqzdMZ6pxvb+KuBrPz3m+jN05wp4yD+2V4hNo5CMGXLiKu5P2D8s5G2j3jmJNWKi3cgJJsIJzXUJ1inj1eUkFXfzosmBwyMyI+7mRxh5Nnw7g1mgoa8vK90p9VbnVCc1pipQ5oqPTuo9kv1nk22+c81d1sz7BsJbqZg4CF5m9Pqon63cG8DIaJOrXDWrJqZsfvPpRsY8HBUM/gUgx0mkby0Jlor90g7MVUU2cN5sMG6qKM3SNG/Jv25V6uQl293BsM1luC5IsMktYN0F+/x0Foa+lXNmQw5XIdsE7fmY6e3Zq+DHnrNHHRgejh1GsjRrTXj9tBdd1eewIpk66Q7TUHY5z2OuEZ4KVBBR9rNtTItGj52gjWEWmZz1mtPmeu/VI5uK1TQbbOBvpymjrRbpedA56i/jD5xQQ8Xqppo+n6YUkGkLzLpvOJ65jxeuJIznVMhnwgZ642bU2T7m5/5ZZhLKMutOn2gSc5U9kNnIcF/UY33C3b68Dy92HiPoy/veaAd/HKdYwo2iHRtogC5JSfmr3V8iCBQ7R3LQ+m5ESDUgoKSdbs1lx4xkfbSfZp7bGRc5BbX+RB/cYrSmv8BJqvzs2hTZMBemdyYRc1i/F39Cx8fr9G6kzdUXIzYCsy4PrnnyX61b8S/cUX0jeo/Dg/j5+fG5ExxAA93XICz74VYSbWhFbW+di3zLPdgLjIpaeaOmKlOvt8fbx1PXT234hTR3W5o3Ew7OhaJfIFSejqJTJ4//IbKmBgAimcXtwx50aTG8L9La1F+PsmRXSceKbVpj3eyr25ix6wOuuZAkrqQIgoQ3tLcGAviXSQ+50F93CLg2cicn3h33PQ7P9Wekf4Q8iZEKoTOvyhmNmnzrvfrZnVoi4b6dax4s5iXI7Vt4Ku8WyFo7asSZwO0nneJ2XKoO7oyS1Q3OtDzv3B5f75Jwa+9d4tBeTSef1octTBMfd96qiFlxVrpl3mg2lRbXdmaokkz1AyQVL3gGyMvovB22KIuvOWehNMsesC7x6kkj5InbkzHV6GPMY0UMeaeqte0+kmD7CZ61xni+u8RUYxBWZ9QRLTXpuD7dxeb6pd7fRvTHvd++CKMm6v/dRBj875bvDR7us5Mx5HnP9zuqvLGH/OOG2KOvi1gqaFDW7rzMxXF5f3sHaZeYIi/FpyH0EzOUSUB7n6J82BkUPraLxoA2SQRAaAQ65n5K1jTvoOxMSuY+LkAx1A3vE30Yd67P5K7m28XZe4dUzKU98UmG82YUHba/0QU9v3eMeO/jaqcPj3GMYY0X9wb7k6XKFWs4o+SOsKStn7lpcQOSM+x9mtAXq/9nRjI7UPdVUJj58qka5zG+uH2faaCX+mjeKD6rzy42maF6gruW+vtht44US6g/bFqSIDrlOrq+jgiRT9t4lVdFlEe8738+MH3kvJ5+dED9DzsS9i/I5IfJ7tBsxFFCJ3GbA9OIq6Xgy4w7MAwVaL7KgHlpypJQEmGmCW0tvpNfq6Wd5ZD2XIu0xPNCPcivB+aHaURO5DmI5Ky3yUy0Ixy1iz/QbcWb6mpTdkUHeeXn1RJIqns8+NpRpg8e1SOqKtVhwkGPzUS62j607RueADdsW4bEbUrQec7WfcS7Izy0Q9Xr9SLht330MtV/LgOsvcZddfEQcr86uOOnrV1g2ey/Iku4YiX9wl2rdmXnZl3qNPpJU/7Tlvmftya69lXegPfmbbr5EzrkTduMa7t6gc1DXN8nWaxXfb+IzojNh7OqaRsQzpds1Xr02vdm/ZdXax1N9ylVJUe62p9ro62F4nWQSL3iiawGctEfWRZ4ZajD1wC0sFFof9OeXBC1E8fKtIXn2pL9BORB0EjaK2D9DlTn4uDmBawbP5orySXoHhzCoX7fhF8ooQdT09u45xsq5Vx8TLzpnzgaxvxOe/xTpQyAfDeDUTn7uiqef9zP2VLJY9R9UrI1LH8FjIbo/0JU6+iE+013qbF/c91JYZXv62KKyNyrc8jBHlrFBdlvU2Bftb6p28xjP49jsHZPO3h+6wyZU6JnCcBN+2IaFKf/xUeVSbVlW3OlDnbqI74rdpeo9/b3uhTojq5QZbu30H/0tGqsy8/fbb+lq019/5JNX4t/8ur5/56JPUnT/6Q2rafe6Fb/P9bIF4Hj8/k7D3Pbz28lT12iP6VtDH3XenqidcllrzDxGv/+Za9fib+rag/mZt6rC+rZ5zd+r5D/VtJu+zniMNpZ5vuizy8xxeKx4Tf3Mo81fNSpzfI1lHUmtEmlU3vZD6WN8TTTx34uWpJd1D+nZhFTZt1O+/pNtNFZOXzG+ubnvzkskX3ryUXwVNlw+7U0tEflnygp1bVB6yy6VMh6bulK4WAgqRTnlNF5EOTRM5f/jLgq43OI1CyknGOizA5MPkylxS6cK/YU1UvaHziV0XB+m6J+PzmH5uZPsg0qlZtB8iz2Wux6KFpk1Ym2GIx2omBtuTmolW3g4tM8MRLG9KsH6Wv1Gs9E0vNF3S/sb69/D8Xuo+9/PpshKr7YlHft+1/lol+Fm4HHK6DLdcBdPFn0/1dwzJt6YuCKsn1eez8pXOg8/90vfEsPwXKVMZEuTrDT+/sLzWvwEqne3f8+PupSLN3HIYSFNBlY8ybq/DmPrISatgvTESAulifU6V61X+DX5OXcb4sZA6Vo4rAvnA+5ub+iBuvo/Vf5FlNpm85U0btxyrdIlTx4TUsTnkA/m9nfcNEyyHtrC0H47QspS2TgzLQ+o+57cPpMtw+V7fEfwsKl8Nv/5Nto5RnzNzO22Vw0xpF6edkb+D+B39bd4w5KPulfVLhn6nqV/i9E9Vu5W+Tc9Hu5WPtAkj83hkOsTMQ7qMxh9H5S7RdBGf2ztu1HVAoN0S6cB91tB00OXRru8DdZZJx2TziF++80xFznC99rIqmjKuilZs/SNd8qUq2vx//gf6H9/+NN0/71PyX77N9/Pj/1U8j5+fPbWpb7qjRHJJz/bFtGlBxMl5+GyYct8u8xgv86+W0f3s58vo5Xdybwz9etaSyyuXvyP3D7zVs0xU7d+Z/HLI/FEn2xD6VvqW0LhHp+V+dPI+tTzVHL327AVXdsTv39lF4x+c7qSHyUvmSJFakujd10vtD1d6U/cj6WVw5vuFn2hE7c80YJVLmQ7WbG83D+nHM27yXWT0sj/nItJh1v7jvuW+mailIKyz0a7DxEUu85YPFTcrHeTSb6fesJe3nVcnr/LPnslGyFI74hUF9v54nufU0e7Z+wPLdBIhl3h5TyyU1UxjXkonykfVQ9bsDXnJ7qRZnnpY3HbykLOMaCo1r1FnLzXv8XhNF63J116l4nv9oEPtxWzeL7v9q7mO7aOOqocj255Y/Hmyc4G+bWbFiPdZytt/uG35tAGRX5vzkS56eZfzXnXUMlHUdVnu68hty4GOKjfPzR+kjj7vyQrlrCM+w23T3eFLGeOUIX+9Np9ox3tJz+QqBN1eW3l/Wss4T98vkKbiovZfL6P2Og5dH7n7KaoT+mXXlhWA9Tl5RqX8nB19WX9OPvmhvde3Otmt/ZurdplrHP82YW6fX5Q16/6R7b+4dYxKl9zqmILlA6se4hnEKTPrV1zysmJHtNcvNPV7ynku7bWbLuaS3SzLQHtt8pbcLoG5Zxs378HtNW+1VUzijBHj8IwBSrad8RHt5y2bUr4Z9Nnxj42mvThTjCGtk4ELR9Z5xwtc//i3Eylqvj6aOyvePidKJjxjU6eBnP0c1gcucqJu6ez11bmiTYs6L1E4fx9TpQfv5+rU3XL2OF+xz3GhLqV0jpkqjrrq62Xh2LFjNHnyZH0rvXU9n9Bbp1M0v+5TdM2lVfIEWbxnK28jsL3vExmUXd4QLybtf1+5zPbFWXSgwpe0Z/N7VBqkTTikSzikSzikSzSkTTikSzikSzSkTTikSzikSzSkTTikSzikSzikSzSkTTikS7R8p01FznA1OJj6nb/6FL3x3ie08If/Ttd8/9/lv3yb748bbA1SexfW31xX0cFWAAAAAAAAAACASlPRAVfG2wWsWfRp+umq/0AHHvsP8l++nds2AgYvB+Pp0Ai3AgAAAAAAAAAAVJKKD7gCAAAAAAAAAAAAJAUBVwAAAAAAAAAAAICEIOAKAAAAAAAAAAAAkBAEXAEAAAAAAAAAAAASgoArAAAAAAAAAAAAQEKqUoK+XhaOHTumrwEAAAAAAAAAAAAETZ48WV9LXlkGXPOZYFFG6n2LHdIlGtImHNIlHNIlHNIlGtImHNIlHNIlGtImHNIlHNIlGtImHNIlHNIlHNIlGtImHNIlWr7TBlsKAAAAAAAAAAAAACQEAVcAAAAAAAAAAACAhCDgCgAAAAAAAAAAAJAQBFwBAAAAAAAAAAAAEoKAKwAAAAAAAAAAAEBCEHAFAAAAAAAAAAAASAgCrgAAAAAAAAAAAAAJQcAVAAAAAAAAAAAAICEIuAIAAAAAAAAAAAAkpOIDru+99x5t3bqVVq9eTffff7/8l2/z/ck6TzuaL6emniF9GwAAoIgcWkfjJy2lHef0bdBE+33XFVSz7qi+DQAAMJKOUsdEHlee17cBAKAYVXTAdefOnfTTn/6UJk2aRN/73vdo7dq18l++zffz44k5tJla+hbTd+aO1ncUmmqYOw7pmyVkqGcp1ehORUrfF+bIustjPS+tcz3UPIlfZx0d0Xd5iMebxHvw+5j3Kj0qL9RM9AVWfN/NXEoxz6Qz1LPM8/3S5hcnTez8YNIv5NLcQyVxSCXkt27qzv2TH1l3hXyNdHmFy+f4DM8ZMSY9mrt9v5/4rSep7xa4ZP1b+/JN2N8fWud9D38ZTYCsT0slnxaSTPuIer+ixci3Mfjr3Ux1BT8nun015TJN+Qitu0uRSf9ge636Kt5Lx5s5935Kl789W4dSrPjLrr99KxbJ1DHZ5AMzruCDaFElJpF6aJj4c45He508X17Jddxo8oi6uG2N/N2s17cvxT+mimhzsuSkQZoD1W45LP462/zWw/v9fHWdL22c9NCX4YzLCsaUpZzrKV+aRPbZzPPW0eES7OZUbMCVg6m///3v6d5776W//Mu/pM997nPyfv6Xb/P9/HhSQdcje5+m6R1L6MoqfQdkpgvxAzSLmvVdkcRgeV5nHU2v07dzcp52tK6gVG34i8iKsG4Pze57hwbeV5dNDaP0o6XjyLpG6qwTaaVve9VRh/X9+NJytX6oHIh8cs2D46jbfL++NqKWOvpB6CBV5QcSaeU1lVqs9FGXPurgp02soZE6pJKVMQ20yf78Ih2qHqrPviOhA4QvVT8SkZ8E85yaNqrXdxUPtfKgppVodmglI37rE8et35kv4reuFxV5Vr81dxREuWvuolPmNWgFTbM7KJxO8wc95a+7uU9kTwQBYaS4+dbJ+/58GwO3nde0pNy8vX0xdc6PCA7GaMu5DdtUWxtd50TW3aUn6/b6axXWyeR+Yp34rTv6dBp0UfOm29MO8itDSNmtejjrspt/MdrGOLLJB+K5D7RQ+vFCIvUQFCMZCAwZy2VXc6rgzzwy5Ysvy+lK/ejoho06P1sX0e5VVdVRzUX6SUUqfZsTky5j9enKWJxyWAxEXTB+khrDDCtN5FiokWi7lSeWT9UPCuLxaS3VnvFpTuOygtErvyLHT3HwGKyRTjn1No97nqZ5vvpfBbtfpho50C5NFRlw5e0Czpw5Q42NIuOnwY/z84a9vYCoVB4XDffs67Kt0CuZKIQrVIO4qaFG3xdFNHzzn6bm7e00W9+Ti6GeVmqhNupYVq3vseiGoaNvI906Rt9XimQncjF1t8/Sd1SYq5fTqRNup4jG1NNsUX937gt2yod6Vsj88IOw/OAnZ7CLwe8iq/EsJSIdbqqrolNnspmxzeWOZOeg5fqoms16znUkOpv67iLBZX73zaKh72ygCeJ2rI8nfusH+2qz+q2HejZSJ4lyJzpX6j1G0a3tovPWt4I6dWdq6Myg+H81TbDqlytnLtbXAArPzrdKMN9mdpQ6W/qofk2b23aKerhbdNA3bfQHVUR90fhM+rZct2E72qLbMNOWc91dbHVOVuz2upS/Rx4d2bKS9teJ39o5+D2VWrruEI36xrzNOiwFQz1PBMtu2+osy27+xWkb44ifD/TBmI52umeivisgmXoIipAey63p3TCssZwMSvLBDDtglpbIdxueplTT3cU9hkxkjGiXsaiGy1sOi7d5U2OYF95TY5jc8etwnCLNJCbRL7KD9mZc1rm3OA8eyvHTN/Y746fciPq+0zt5TY57+vbQq6be5jwpD2wspxv0XaWoIgOub775JtXW1upb6fHz+PnDMfT6HtrfvDS8khWVv3dZWHDZmGeJjP9xkRHl9Gvf85wjIs79onEQN3lWifNavuVF8qifeUxcRna5vCiET8YLbh5Zt4A6RUereTgzMWUj3EfNyxroS/ou29Drr4jO3Cy6oZgbyoxUJ1LOtNb3QASRH1oe7JX5IfMsRtWRoqgyXgp0EJEPCsXHM32tzkGoOM8ZOTwLIbtZ6rl1mj8Y6BP54yYrHVRnc7+4xkFunuc3umEpNZN1ZJfrbtFBS6q8mm0fpol6jsRgdppV1weOoJ/1tSeBmYiiLvFstZDlLFzZbgXfVy2nsts4kU48A9l5n+SXWDlL3UVaE6e//V4hbaBnCWHIEl2TzuaS3ewEka78d/5ZWbodtz+Pdykj/01Wv0AsmfJtLOcG6BSpA84OOagjSvUN0gf6LqZmjD2Spi1Xg5a0q4WstrwkVhtEsr6ruIV4a5jzdPIk0fSb663fWvVzRCVHA2fVPZUotOyuWJld2S2AOG1jZvHzgTyQ3reY7knT7idSDw2Tqd+5vU5laq9FLeppJwOzesXntf4+6fb6+Q/NrxTSXo/oODJIjsfFWG76mOH8cEfpJdF+Nc/MYoKFnpSxZmEWf1NwqswMt88pD3hmKGNxnlMc9BhmuOX80MvywNI3ymjFqBo/FaCXxYHo2Ac2ildFBlwHBwfp0ksv1bfS4+fx83OnZneEVszciNWtoNSjvc5U6oH3rSAjD7LE4+Ot6ecHO3gFtH9fFTFIrBuge5zn1FPnfN2gOkuHu+SyfD664rxX5zync8INJ09l33HCPN5F41vqct7XpmBEGv1kUy11tA9vcHVki+jgNXdFHnmSncKJNfSBZ5A7vP1tCo1nEmyqXW3NAAjDS5it71juy/LO9dJu7u/P8JZPmR+atsXbTqFUZ7fqII78ncXgoWnb8I74V4ScOs1iMPi+GAzWXKxuynSvo903d8ltKPYPmJATd+zeoe6JenCll7wltW3JlcvV1ggH+U3r2uigaQfExZvPRR0w325P6mjTxp1WYJEHb4106tH9zt8f7Bikednsq3T1EvndvUfuz9OrL/aJzn67zodi8HZXPbVMtJbs6SVWSQ7iRjdsUK+9nWcTL3aXc4lLIO07G+nxGmvJav/DnllYPEi+5f1H3LQVn/dU1NL5UFOpWSbMy54BsRokurO3OKg77337N8zHMupgvm2eFJZvMzg7QPupmsbrAa4MJPCsd7m8cpBOmjZUvD6vBFrTFt2Wy9lwOh2ixj+Z2vJSYX/XaMH2uqj7a4n7QJQxovFjdRrJwFAj0batsr9bTIHFwtIBSH+bM3trdmU37+K2jZnEzQdiPPagGI9tT3MQOKF6aLiuXK7qdm5/q9K21yKdWhppYJl+XLQ50z2zenV7bS3ZPdhxWrbXsYOuVnvt1i9We30hpwIHW+sC7TVv2VVMQVdnLPe39oHRLMdy+iBiDVl9aF3/hhNpYyZlyLQqTvHGiJnomEe6MhbrOeVFrmCrqyGSByncPJP2gLwYn77cl8ousF8GeAtOKvkJbkEVGXD93e9+R5/97Gf1rfR4T1d+fs4ij2qYCriLOueFN+ty4CAaWvso6+iGdtHw9dHu1+0GjPfxciuu0WPH6WtxuZXfVU5boAZ++1/spaGi7cGLNGxdQSl7qWIu5Gwbe+lVBDHYfmmm7kjITktV6eyvKDqRchlN29zowHTIvp7TxXcu36Cr6IjWqTLWdLXVCdL5YcffxOkKiDy48RnVkSq1xsHze3cRLZhc/gH2YRl+p5mDTeP1wbFNDXqQ6VCzUEwgTdUvIzFDxN+eVBP1DzgzEc1gs8M6sm1m5+4+FLexGEW3LlvsDSzKgx/WjIewrRtEnu1YU6/aJX1XQYn22g3CXkw1YhDqDObDBuri897DS+dDtiyJYtLyJacjrga2zoxN8T4/2SQ64Z4ZnGb5rDdQmxQZJBX59q/fC8u3MclgyuU6YO0faKm2nJcX3hJVtnQblvbgaty2vNjF+a4ib3U6B8jd9np8RZ4wSuQfnlm3oUbWnRW3j20apuzKNmfel/W9xSd92xhX+nwgZ642bU1zMCaheqjApnf0ud9pTA2Nt2b1yq0ldHDYGN1wt6+NycRqr00Tb7XXMqXCJh6IOuoHZhyp7yoKop7cPeOYU3eqiUzZnohHpPkGoh/o15AHPcXrhvbXrLQp2ppJ5+u0Y8QYzFYL6Q54ylWpZXBQNGt9K+hxanfyndrLPirYr8anr9auHt7q3RLDK8549VPpr1AKqsiA62c+8xn67W9/q2+l95vf/EY+PzdqgJ5uer5zZDdKxIlZ8nGE2rPdgLjIpaeavdRTbT3gXh+pmZ5mn7aOucMplqJSm8/7qsQ40uZrILLvtIyUGJ3IMLqzlK9B/MhSR/25I3qws4G+5CSLmx/cgw9pyCOQ2e3nWZzyG7ApC3KAkUuneRRNmMizUOpksOmkL9hk2gATyOT8yDUaz7zk2S37W1pHpH5NS3Qcr/FshaO2rMmKnDXzND2uByhyDz7P0lLm3dPW0ccBYD24bu6hj1Pu9SHn/pE4I7cYhNV7txSQS+f1o/Gog52dG/TWEnKw5j9oG3XiDTVj1F7qGdV2Z+bNtxwktevEjH0X46Iami7ayVvqTTDFnj2jfl9nz1UzeA9w27DoA1ui7pb7LqZry7neF7+LDEqa6xwMF9d5i4yiOOAkvuuKlRm+awjdXld17qmgOlwd9OicX6dm+Om603BmPFYcUXYneMtuWJsz8uK1jZnFyAcxDqQnVg85dYlbx6Q89U2B+bYkGE57/ZOdqlVVKwmyaa85fXn7Hu/Y0d9GFYQYyz1gBeOzP2DMRF/QE3S3Jinpeww5Y6+oJ2W4+TqrMaJfnAOe4jm3bLqj9A+K5sJ34EOVKf8EOsZ1hRqfHnjSXYlc7rgeuKalV/Th0uxzW8IqMuBaXV0d+0RY/Dx+fk70AD27fRHjyUeHybPdgLl4glF2Yxp1vVDUrB/vwL9ODExV5433oo21d56cgewLNs8XjaNofOVefno/xYu5N/f+gLchlctKSoDMh266yO/IMzud5YjRs3Tl8pu6GtGdLSduY2aCW46M+cEbxJGdztobqRyWPgydOV2Gv3Vy1IqD3Ja5yPqDrNkgzGof+D6z1M3Oj6Ovm1WcZ0DmjqA9u05fsptZNopuuNkMUI7SS7w1TNwDF3Y+5TQzb2unX8TByvwSg7BetXWD57I8epAfRv7u+qQBR/Zms6eav102CZNbe23nW0e2/Ro540r0Wdbc6fkOchDaNEvc57blblDAbcv5dsfL+502zKmX/W2YqLs3USpQd6dS3racTa92azm7L1UUgSiRvryM0P9dX03Fba+rK6gOV4FFro88s4DKcL+8bCVSdgsgTtuYWeZ8IOsbPvBjHSjkg2E825H7xU097ydTD8k3jq5XRqSO4b6uv00Sl+yCGqq97hVpJNvrTt9s1nQ8/co4bVT+OGM5O7aa7VjON4vYkPWv3zle+SLG18W8LFyWt9zGiDbuq/j3wZ/XKRKaV0qK6zz7V5ZD0yY7zxF3dS5wnlOO5EoxfeDBpbZC8fKOT934S3njYCtP8mvqOl6+M59TZebtt9/W16K9++67qfXr1+tb6fHz+PmZhL3v4bWXp6rXHtG3gj7uvjtVPeGy1Jp/+ETf4/PmWvX4m/q2oP5mbeqwvq2ec3fq+Q/1bSbvs54jDaWeb7os8vMcXiseE39zKOKj5CrO75HZkdQakQ5LuodS6T+e+o6hzxNpUjNRfUdvuoQITT/1GezfQv6+Td2pj/XtbCWTNjn6sDu1xJ9v/ELyXyHkN13E7zgxy98tND8IMg3TlN+E5TVdxHdpEuWDy46Xrjd0+Ytk0iJdXtHvkXR+SipduA6saXohPF/E+X66jggvM/oxUf+q3BKsj1Xd7i2Tpl7OWGdFCE0bmZ8jyr6sJ4PtSc1E6zPotFjyQq41n03X7U3iuwfaJl1W7fsTykOh6ZL2Nxa/V7O/PVf3ueVC/6ZReShL8rcX6bIk8Pvrz+Kpw1RapS2jMQTTxc23SjDfGqY/w5/rI1+VyI/V2HlO58HnfhlVd6r3yVznZG7DPHl3GPJa/2Yi833m7yrrngK1R8aIpgsz9ZGTV5IpC8M14ukSVnYDddjI8KaN+zmj2kbDrWNC6tgc8oGsY533DZNQPRRTaJ4Ja5MdKu28n0/d57RjgXQZDp2mUe01/zb2/fq989Je50ynj1VPqrbW327pfBiRdjIv2m1wxHc1r51En8Avr3VMmnyt2vN43ytTHIRxGtWsTaKVduUlbTLlZ1lWOc+E9TmCdYmqz+zn6jKUp/zC8pVn0udz3fakK0vp0tVi0izpWBXLa3kSKnKGK58Ia+zYsdTV1aXvCceP8/PinmDLi2fspD+qxWd44z08Ni2I2Lybz8wm9/hwjwTxia26A/ufxTGKbm3Xe3Ka97Jm6125/B3qbn6abvUsE1VHm7JbDpkc92zPavmLc/TNmqlSOFOpRZ8AxaTNPD45in+GZCk7pM5E6lz4xCZZHwUvbnJT+JTI0YElVjFnRDvUEpzA/q+lwv9b1+2hWfuP+5b7ZiLSgJduy7/no+HWzGC5hC74nFf5YLf1nBFnpYM6a/pKnS/s5W3ub+2ZPZMVPiGW2uNLzSDQJ5ewllVxe6D2EtPpw59J7ueaS32fhmhXupvNzAV1ySrvj2mgTaIu5JNXmb9Xl2z3QGN6GV4f0ZqFU/R9hkizE9tkmjnvIfJQzbY8HQHXS7Lt9ja72Raije3so46qh4N1S+yTZrmuXCTa6z5OGO/MUPk+T/ZSB9l1mDqJWVInWHO5+Va9TzDfxsF5+4Dek1i+zvxB6ujbSN8azvLFSifqrvF2f82015W2f6muj0Tm0mmRr7JQakLK7oStRXi258xtYyzlmg9Ee/1CU/+w22s3Xcwl3qxFL9Fer1HtdXB2qz+/iYtor/mkz8U1hhCfU6THgDXulv2sLGcTcpvmnOA06ruKOrpc96OsHOnGOdmUIO4fqpORm3znj+fI/Zb5yrDHpwVi9UHkagHnc2ezPYg6hxCz+97yYmI959yT06mtLt2VCkWZLhGqOOqqr5eFY8eO0eTJk/Wt9Hbu3Elnzpyh2tpaGVTlE2Txnq28jUB/f78Mts6dO1c/Oz3/+8q9KF6cRQfKKSCXg2x+j0qDtAmHdAmHdAmHdIlWCmkjlxJxW/lk4ZZPlUSe4U5m3R6a3buhYGc2RlmKhrQJh3QJh3SJhrQJVwrpwvuwTnvxxoJONkF+CYd0iYa0CYd0iZbvtKnIGa4GB1O/+c1v0okTJ+hHP/oR3X///fJfvs33xw22Bqk9yepvrsNRLQAAgDCH1tE0fcbnStmrKp6j8gy1wz6JBQAAQBJEe33NgynfyaIAACCTig64Mp7Zevvtt9PKlStp7dq18l++nds2AgZPHeez8aJJAgAAsKmzFV/uLC0v3rP3FpizdKqRTnX0yeWwCLcCAMBI4VUopr1+dP8GtNcAAFmq+IArAAAAFM7ohg36TMkItnrwfnv6DNLYgxIAAEaaPN+Ibq+x7zYAQPYQcAUAAAAAAAAAAABICAKuAAAAAAAAAAAAAAlBwBUAAAAAAAAAAAAgIQi4AgAAAAAAAAAAACSkKiXo62Xh2LFj+hoAAAAAAAAAAABA0OTJk/W15JVlwDWfCRZlpN632CFdoiFtwiFdwiFdwiFdoiFtwiFdwiFdoiFtwiFdwiFdoiFtwiFdwiFdwiFdoiFtwiFdouU7bbClAAAAAAAAAAAAAEBCEHAFAAAAAAAAAAAASAgCrgAAAAAAAAAAAAAJQcAVAAAAAAAAAAAAICEIuAIAAAAAAAAAAAAkBAFXAAAAAAAAAAAAgIQg4AoAAAAAAAAAAACQEARcAQAAAAAAAAAAABKCgCsAAAAAAAAAAABAQio+4Pree+/R1q1bafXq1XT//ffLf/k235+s87Sj+XJq6hnStwEAAAAAAAAAAKDcVHTAdefOnfTTn/6UJk2aRN/73vdo7dq18l++zffz44k5tJla+hbTd+aO1ncU2lHqmHg5dRzSN0vRuR5qnnQ51YjvYS5NPecppR/O1lDPUhqvX8NDvE+T9R6ey7qj+kklLsN3zDVNS9GRdZfLfOCmwTo6oh9zhKRXSZclgfO//X2aunM5GKTqFfd1QtJOM+lclOlmft/mbopKBZNeOdc5/jwUVpccWuc+Li6BugmgAnBd4ZaDpfT8h5XUIoUR9eykK6w0sS7NPZF1VsUI1K1RrVCl8bXPadq3SuOvY3ac0w9AWTK/d/r+pykvueWHOH3qI+vserw88l35lyV/+xs9zsnMVyfb44CQcWZu47Ji52+XKqQPkyozb7/9tr6WXk9PT2rbtm36Vjh+nJ8XR6b3Pbz2stSS7qHUJ/p24R1JrZlwWWrNm/pmgcT9PXLyYXdqCX+nf8gyVfXfLenulmnCv0tmKv3iPTeevKZNTtzvOHL5dOTThctqdVN36iOTCCafWWXn4+67U9UT7k49/6G+owASTZc314rPvzZ1WN/k79g0Mdv6YSj1fJM3v5i0+1jfluR7idcW5c2fjkkYXrqo78Cf+Xn52V/wfnYWUl9kXT4CeUi/79oj+rYg08nOU8Ovc4qvjikOSJdwxZAuh9de7qmbuK6tKXBdG6b48oyoQ5pFWtl1yAgY8XRx6mdTT4p6cyLSxbQfbjro/OJvn0fASKeN7Kd46pilvrZ3ZBRfHVMchpUuol9Vw33bGP1P2fY03S2el0NeiNGn5nxXY/cxdZ/vuV/mNuIqhvwSLEuFHxuFSS5tzDjHrTUDY8S4zFgo7hgop3FZeiOfZ9x2SSWfOwYr93apIme48nYBZ86cocbGRn1POH6cnzfs7QXO9dDjnXU0+7pRVKXvggSMqafZdUSnfp7NLLDztKN1D83ue4c2NdQQVcX7RYZ6nqBOWkz3NIzS95SfoZ6Nznes5Hx65czFRH0D9IG+TWcHaD/VUc1F+rYw+rpZNF1fL0lXL6eB95fTlfoml6Wb6qqoc282M7hH0a2dXI7c/KLSbg+96hzhPkod84m633+HWq6LXdwKZqinlXbf3EcDnQ00QdwOfjxRX6yw6oscHdmygvY3d1HL1foOTrv2Npre+bI+Ui7eZ8PTNL2jnW4dI+8QplLL9sW0/8VezEqCyiD6Sj/ZlKLm7W7dNLqhndbU91PLljJZXZKUQ5vpwb5a6lg0Vd9RmY5sWUn769roB07fTNSbXXcQdW6s6FmLps/avdzkD9HmtK2m6X0rqLPEV+cMixyPka+OaaOOuj7UMWVH9T9feE/1P9M6tI5u2XQHdbfP0ndkKVOfWscB1rQ1kLPOVfxNd3MfvfxGia5kCi1L7WVWlsw4x12d7I5zsll5w3nxaZFWIi8644AMdB46daZ8VrqZOAO3S2q8pcdCFdAuVWTA9c0336Ta2lp9Kz1+Hj9/OIZe3yMG20utgbRFVFjeZfK+6fjice8Uc9/jcgnqOjrie56zbMK5v1FkctEHne8+x7+8yCyxN48X/XJWuU1DHd10bTZBUK48N4b/FpGOUueDfTS9Y4nbmJYd8R1byv07xqECX9R8E11pom9XL1EdiLp1dFi2r6LhrOMAWkSZBosY/Nqd0CIzumGjDBhHE/XFk9nWFzGNqaHxNEgn0wUFLqoRHREr+D8sahlPxyG1n7jTDgS2NlDPcx4X7YvK90z/bXMPfezpa6q/8Sx/8rddvmVDcvmdvM9+v5FYjqa+E7d3nqVxIcuc0i0ZVI8Fl7/za473tLXB9PUsT5PpptJB/q15r0rYXkIe3FpM37AGJHxQ5MFekabvDwR+j8ql2qlU090V3gadp5MniabfXO8GMbh8NT4j/u2jgbPqnkr0wUCf6sfo2zLPrFgpyheV1QA+a6F1zAoxlhBXiqqOUe2Eaq+t5cwhWxF5l3Tby+bF394l/jbQlun22m5TQtpru4037fXHqZFur7Oh+58ZD/SL7zT/aap/9M6C91Uvrqmj3lKtqCLa6+IrS0Xg0Msy0GinVUb6oCpP1isXoe1SqxhPi2vcLnl7z+WlIgOug4ODdOmll+pb6fHz+Pm5U4Gs5pkhsxA4WFq3glKP9tLA++/oizW45wZQPD5+u3nsHTrYQdRS52/knqZ5dQN0j/OceuqcrwdxYxpok7y/i5rFTT664rxX5zynk8qN6bSWatpxwjzeReNb6mSDXFQFwO4UyKNFG+lbF+Z32hwfkdmUuqNiZrdWIndfpTpqoTY66Bx9Y+oI58GOQbpVHhxpJOJy5MwcKQPneunlvlR4PZWFI3ufJqqbRTcgEO0hj4j7ZlwdWccHwUxQYBTdcHMd7W/ZbAXfTEckQ1A2S53z62hgma7n+3iWbaM1QBMDj0kL6FSH+Fy6nVD53gQFRVlYFnJ0X3cmv2NmAYS0Xd0TV9A0/8CvT9w3cSPV9OnnNPdRS2sw0FkI+0V793iN+d6ivRSf7QFrQDrUs0y2kTxb2zxn/EPTnUHr6Ialoo3to5dft4MZR+mlTqKmpWZWCw9WG2X6ntKvw+k7L7AnGB/guVx+npP8PJ7p3NJa5IPb4Rs6I/patTV0sbzFgYbLadqLs2hHR51IkqQOPJQBfbB5zcLKnt0qhm+iDiMaP1b3W+QEBNE+b9sq+7uVG1jUgegaVZJUv7mOds/eSlyU9g9UbkkaOnNa9FH8dcxM6i7SOka210uPqTYn0F6rYOu890WfVbcn/JxT803QVbTXS+/Q7bV8uqLba6e/L/JHc32wvb72Lt+ev6JNvGZSsL32HnwtPXL8U9dGHdYsxmHz96nlikyRXs9aAXOR7g+09FHq/VMlGZyU7bVdlu66QrbXxVqWkuKOc+LHHkxake+gvV2WJV+Mo2nbhjI6qCrapfdD2qWbuyqiXarIgOvvfvc7+uxnP6tvpfe5z31OPj9nkUc1ROUkZ9J1Uee88EpeLkMVjUCz9bdmuv5uz6Cujjr6rCn9Y8fpa3HpoPD25XSVU39MpWZRAuRy1mJqTJ0AMl+6iHjGbl5PkKDSpn5N4Y98Fg5mt165/LjT0Tx48x6a5pmppjvl5oDE9sVqpnjYSY9K0lE5Y/fV2tWeuiZbfNBmHi8vWmYtmQLl6uX6YJnuSInLSzVi8KQfZjzbtrv5aZpnOlsTW4luXqwfTc70DtHpN7+znGXrkgeXRD5wl+eqQGJT1TP0kukY6hnf9jI42QG1ZoUHt1AQZWwRLxvyDfxk2+UeZAxs51FI4vO6s50vphrRAXSJOvJB1Ua6daRoI9foNtLcFm1m70t97uBJt/+zr1YJYwZ2nL6mqVWB2qfd9NX4d3K267j6JhnMrZgZezJwpg8MdDbQl0M2+6hcpu+4lG7N88Hm0qHa6JoNNTLw1PI1pIshZz/qCRmb5n1Z3wveOmaeDhoVH24HHjD52ddec8CCl6l3tFt9LjFGuqeZ3KXsur22x4xH9j6j2mtzm9vrpm3B9rr/lUB7vabXDQCNaHudFBn0JJmGX0qs2gjrU/PEjS5q3nS77t+Ji+jizeYjQ6XOlKW7j8n2uljLUhLscU7W+aVvBT1O7c5YU40lfRPo/DGOBZPLaKzp4nZpvGmXGso5x7gqMuD6mc98hn7729/qW+n95je/kc/PjeoYpwtkOZH+KBNrQoMX+TgS4NluQFymtfC6AMUsl+SKga+Pt66P3LIStcdh1aY9vtlBCfLP3CpH/qPdFc4c1HACSnI20WLqfl8fkOC9mkS+I99Mg9KkZtxxEOjAk+6M92yp2X8ckMpif6IKwwFVp6MlLmpPMe/BuCuXu4/L1Q5jxZ2FnjEsZ53abYHIHyn7qJuajdvb8pSqd82gb5E9K1wQ5cNuT3g1By8b8qqmCfZ38++DlpYOsshlju71Ief+5M/IHWwje/Ujitzbud/sRWW1/3bChKWvfsjmzNqTeGlk/LJl2mU+aBTVdhej0WOriUT6TTOBM/v7OjNpKty5XtrdF1LeKpI6MOLM2heDfbsN85ahSjKKJkywZ+1769SM444yJiekcB1cNnWMWg1ht0scEHL5Vs+I9vonm0L2fu5c4HkNbq9fDUy24fbaqnV0e+1O1EmP+4l2WxTWRhWWaKNbV3C0NcFZhOn61KIdP+FO7jDnDvBuiVI6ZHttlyX7QFcZttecT4c1ztEH2x0hB0O8RH6R+5Gbcz2UOtEuTXTbpZMV1i5VZMC1uro69omw+Hn8/JzojnE+9t/IR8b0bDdgNQjuURx7cBx1vbC8SxCTpgbMnv08y474jhu9R7tBL1O8RJVb77IZTc44K3Vux/Cgp5xnhzsh17T0ItiaJTULNF250wG7QnfGOT/42wFxsX9be9ar3KM8LCjc3BV4Dc+WOUnhg5Im79oHKCMOVg5HVBvpvM+YBvpOkz5RhtX+e4pWjPRNhj04Lo72OiPes1j8450lf55efamvZAelSVMrn7Bti6ICi1ymPKszIleWVQ7eG5IP6HkOpOdxTFIyouqYF0u1juFVIsH2xN7yyl5FIdvr2rD2elvwNfLRXgfaorA2qkBkeVABIDvQvN8JYtv718eRZZ9aH6yendV5SIpI2ZWlaMOdVKKC0/7Z4GqsmY53C5TSZ7dLTvGI6iuXm1SZefvtt/W1aO+++25q/fr1+lZ6/Dx+fiZh73t47eWp6rVH9K2gj7vvTlVPuCy15h8+0ff4vLlWPf6mvi2ov1mbOqxvq+fcnXr+Q32byfus50hDqeebLov8PIfXisfE3xyK+Ci5ivN75OzD7tQSkT5LXvhY3+H6uHupTLvqpu5U8FHjSGrNxMtTS7qH9G2fsLRNUF7TJi7xHWsm5u875mKk0yVQFiLLYWHTLdl0EXk/Y/lgut7gchZSTjLWYTZRXpsmetMxCUmlC//uNU0vpK8vdDqEf1udppweGb6jyWPeOtom0r1ZtB8Zf5/0vGnjfn6Xus/5vPo3iqwTLbKOXdst80fg+4aUGb9AW1ZA3nQJaxvVfXY6yPY8zueV7dLa1PP8/fztrWmzIvOQoJ+TdDmJY6TrXuZPZ84nNSOUT2zFkDYjmTeijHi6WGVKEXVaun5dgYx8ftHtkVMH6TYlzZikUEY6bfztrxovFFsd47bXblvha69N2xWjnyDbW6e99rU+chwQr71OeowYRyL5JW7dKZ8X0bfX/ZrwvKLLW9w+m1Vv5ZqkI1/HhJWlkevX2ZJMG9UHiZF3dDkK//7BPmXGtBJ5JG5/PK6RzzNuu6TyfVj/e2TkO20qcoYrnwhr7Nix1NXVpe8Jx4/z8+KeYMvrKL20idKehEYuMd2+mDYtsM5AaS+r0EuX7WWM6qQdcZdc2kbRre1qw3Xnvawll7yUlfcPVCcFci9FddKsQ+tovP356vbQ7D7elyq742hyTyv5GmqprHN003NCl6PyrJVyn7SynUmivmNln+n4vFp+bPKUuMwjnplnLZOKLIf5OPpfGEM9T6hlzIHlzSGbuEdSe/+yzka7DhMXucybH7HSVy9Rc9KxGPYlkntPqc/Dy/BSfSt1erj1sHtCNbX0m+sLeeZ4/wmg0uKZD+p95HvpPOapx89Zm+XLE5zsDyyRzbsxDdTZ28abzTqfVV38J3US7dd1N4r2ZIU8yVxg79+QMiMvWaVZceF9nr177KqL50zPTJ4c42lq4X3h/Ms2eX+uPpW+Mg85l2D6Vip/Oqu9s3Pp85QbUZfySfT8szkrnVWmVFlqpFOP7rf2Y65UvA1JFzU7ff46apmwtbxO9pkjM95x65hxOY6rRhrvC9pHHZS5Hye3u9HtdZN/DwDRXp/qCmuvk9+Wp7C8/U/e0sjtfybb4sbpU6stffT9Zvxqz/QrQcGylGuMolipcQ4PZ4ZXPtQevnwycvP3gbSyxiPyIvLIrP3Hy6wtc9slVRZEuzRRjIcqoF2q4qirvl4Wjh07RpMnT9a30tu5cyedOXOGamtrZVCVT5DFe7byNgL9/f0y2Dp37lz97PT87yuX2b44iw4UesBcZLL5PSoN0iYc0iUc0iUc0iUa0iYc0iUc0iUa0iYc0iUc0iUa0iYc0iUc0iUc0iUa0iYc0iVavtOmIme4GhxM/eY3v0knTpygH/3oR3T//ffLf/k23x832Bqk9jCpv7muooOtAAAAAAAAAAAAlaaiA66MZ7befvvttHLlSlq7dq38l2/nto2AwVPHeakAwq0AAAAAAAAAAACVpOIDrgAAAAAAAAAAAABJQcAVAAAAAAAAAAAAICEIuAIAAAAAAAAAAAAkBAFXAAAAAAAAAAAAgIQg4AoAAAAAAAAAAACQkKqUoK+XhWPHjulrAAAAAAAAAAAAAEGTJ0/W15JXlgHXfCZYlJF632KHdImGtAmHdAmHdAmHdImGtAmHdAmHdImGtAmHdAmHdImGtAmHdAmHdAmHdImGtAmHdImW77TBlgIAAAAAAAAAAAAACUHAFQAAAAAAAAAAACAhCLgCAAAAAAAAAAAAJAQBVwAAAAAAAAAAAICEIOAKAAAAAAAAAAAAkBAEXAEAAAAAAAAAAAASgoArAAAAAAAAAAAAQEIQcAUAAAAAAAAAAABICAKuAAAAAAAAAAAAAAmp+IDre++9R1u3bqXVq1fT/fffL//l23x/ss7TjubLqalnSN8GAAAoIofW0fhJS2nHOX0bNNF+33UF1aw7qm8DAACMpKPUMZHHlef1bQAAKEYVHXDduXMn/fSnP6VJkybR9773PVq7dq38l2/z/fx4Yg5tppa+xfSduaP1HYWmGuaOQ/pmGTqy7nKq0Z2PlL4vK+d6qEn8Pb+GeZ3yo/JBzcR1dETf4/B9//JNAz8dTLG/+7qjVh4yaea9lHraDPUs9X6f7lwOBqkDSfbr+INSply6l5C8N9JM3m/uprBUCHyHiOfFY/JTSGDz0Drv+4Q9Z5jk797cM4zPX6Zk2hdh3qxA3vK2lJ7/MKcWvQy47fVhfxKIOqt5kp1Ow+j7lDp/32UdSrHi67sMq90qL/46plgPMnJ7PR7tdWKC/VFzya2d8b6er/8QqJfscUX5KJWyNFzmew4vjpJmDM4KMAYYef52qTLqt4oNuHIw9fe//z3de++99Jd/+Zf0uc99Tt7P//Jtvp8fTyroemTv0zS9YwldWaXvgGSJSmpeZx1Nr9O3sySDEHV7aHbfOzTwvrpsahilHy0P3FiMn/gy1XREJNKYBtqkv7u89LURtdSVdZBeBQzrqGXCVs/3nt7ZSM2+AGTzdittxKWk84coL9Naqqnb+s5VD9Vn+VvrtJvYRaes1+G0swf+Vy7Xj+lLd/PTNK9oGlgdMG4lmt2s7/I5su4Kmvd+Gx10vkMXNfc/TNNynO14ZN0C6qwTdZW+7eCO1vxB6rDqoO7mPlEEEQSEyiHLW+dip2462EH0UP2ysh3ERVGDu/TtdecJt64w7fUP3qywkCsHNepWkKg4dVqI+nnT7ZiNLge1jdTZ3KXTpY86qkS7heCdLFveOqZKFJ3yDRSBy98fVb8/17HVNGFMNgN0FTSaR6Z88WU5XakflQfE6lfQeGfcIMrf+400vswOBgXLEjdDZVaWRN98/KTL6aUaMb7Rd+UiY5seOgboL7MxgNsuqXGjKBe0oiLapYoMuPJ2AWfOnKHGxkZ9Tzh+nJ837O0FRMX7eGcdzb5uFCHemg+iAM9/mpq3t9NsfU9WxO/zQAv31zfSrWP0feWGA9KiY3BSdAhu0HdlNKaeZot24dSZcp7l+oEYqBJNr75Y3xb09y7rwnr1ctHQWZ1D8Z1vqquizr3ZDFJ12tWEpF0aV85cTNQ3IP565A31tNLum8VAvbOBJojbwZ/8PJ08Kf6ZWEPu2oSp9I0mfTVbohzesukO6m6fpe9wDZ0ZFP/nTr+6zWRaAVQK0Rb/ZFNKtOVu3TS6oZ3W1ItBx5YKCqDp9prr6Kzb659X1vLiI1tW0v66NvqBcwB0KrV03UHUubGiA2hDPU9QJy2m7uVT9T2j6Na21TS9bwV1lvVB9AzkeIwPoNt1TBt11PVVVh0D2lHqbOnLekLUkXX6YIZTvryObFlB+5u2UcvV+g4uf+1tdMOmPeUTPAstS+1lVpY4vkD0wnvvUMt1+q5c+Nr0sKw2dOa0+L9/DCDasjIy1LPRaZdUGqhyUQntUkUGXN98802qra3Vt9Lj5/Hzh2Po9T20v3lpeDBPVFjeZWG+I0Pice8yb9/jfESEp6b7nufMVHPuF42DuNk5332Of3mRXLpiHhOXUlkyrWaMtVGz07BlR/4+dbPohnINtjIOsDkVXExyGwx1oKB8TaXmjjrqfdCd3XlkXd0Ib/9RKlTa7bdmVZm0u0cMfsPz2nnaseFp0UO7yQ32jqDRDRszzFQWnYGlPHhXs3YZ15O3dNZSx6LwjnY0dWCo/tE7Q7/76Ial1EzW7F+uu8Xz5UBAPmN4eOYg1+vTxOCCROdmmlXXB2Y2n/W1J4FZc+K7TLK34fAegXe2LRDtE88MUM/JbdaDmhVgXRJeFjvUs0y9rkhr4vS33iusDfR8npDPYtLZXDxpa7fXVruf3cxyRaax9T6RS9RKydkB2i8649+w2nI+KPJgr8h/7w+U/QwIh26vs6Lb65uuLef22k8dEJt+c711QEzUTY3PiH/7aOCsuqcSfcBHQz3trGh7V6wU5avcD6JnEFrHrBBlR1wpojrGtDPcXqcytdf0gXdrp8DsblEmrL8Pa6/ltgWyfTLPSWKJvbgU+cw1EwCK7rOGOUovcaBxZpZ19JgaqqFBOlkuB4Ii2utiK0vDM5VaeHJKVoPnEDHa9NENdwfGAM2NyY0BikFou9S6wmmXynl9TkUGXAcHB+nSSy/Vt9Lj5/Hzc6eOnoVWzNy41a2g1KO9cuq4ulizLHlQJh53lyRETdcXBbRugO5xnlNPnfN1g+osE+8SBZmPRLmvNdA5z+mkcoPLS4x3OEvUumh8S13x7wkm0ugnm2qpo73B6nBnR1YAE2voA09HocyWRMTFec6kgZw1XMazfjUOup3qa6NT+mCEOQp5la+B9R6sKO5OZNbO9dLLfamsO5CcdryUdWCBCjKZtPN3DtxOeB21UBsdzDaYMJLkbGBVH/J3mPbiLHrjRPblQnbs69qooyGqpuKO3TvUPVEPrvQWJ0ltXXHl8uOybpfL58TncLdIeMeahcH6qGW+3Z7U0aaNO638zoO3Rjr16H7n7w92DNI8MYjz7DPJg8QNNfTGe+o5cnuE1uzKDQdDvds5iIvVbiVhdMMG9brbeTaxuzSOL4G072ykx2uspcv9D3uOynM+v+X9R9zPq+sVb8Batdff0enC6eu013GJvoNnSxB5CZa7UiNnedfWkJozr7b74PK2g/NskcyKLyoh7fW3LhzuyLCUqFUW48fqcioDRo1E27bK/m7lBhZ1INqsPpH5pI52z95KXJT2D1RuSZKzyOr8dcxM6i6yOsYsfef2oSptey1+z5ZGGlimH5fbOtmzu3V77Wy5wa95WrbXnjZHt9fmfbi9fmhFtu310pD2OvexWf5Zs1v1PbGcG6BTJPqDZNW/fLEC3XJ10qYnPONInhW7KVU+B4Jke22XpbuukO11sZWl0hEcA8zaf7yMtjcU7dL7Ie3SzV0V0S5VZMD1d7/7HX32s5/Vt9LjPV35+Tk79LI8emYfAVJE5SRnenVR57zw5kguSfDN3DTT9Xe/bnck66ijz5rSP3acvhaXDgpvt4NMevbai700VLQRV3VkJLWmbfhBQTGQfmmm20lQge0ymDGULc8+rl1EHGQs873Q5NF958BGFzWLvMAzANzgkWoEnQ4kP4c7p2UTdBUdcvH9X61dnfUscTnLTvxtzTYO5rlp5y839r5ZB2/eIzoTJXRAQw/i1YBF7Td0TbZn8hcdC7ltSXsDfSkyHqJmoZgBi9pXbiRWGvjbk2qifrfzHBY4NrNzdx+yG4vF1C0GW+b7XlwjelS56NtDrxZLXhHttdv5vVjkfSuoI35j3jpoTZs1wBT16T3NYty1z65DVfqatlamb06eppdymBlbEmSZq1NBBJGHvpzd2ozKEdpeV1yvRVCBsxodMGr5GvKLIQ926gkZm+Z9Wd8L3jpmng4alabpom/iBGHH1NB4a3a33FpCtNfulhvcXquZdN72Q7XXpu1S7XUO5aiY2usM7Nmt2RNpvoHoB1b9y/1fp7929XI6sEb14UxAlvcArVePlhdTlu4+JtvrUi5LI8s/BuD9668YgTFA/nG7NN60Sw2VkWMqMuD6mc98hn7729/qW+n95je/kc/PjQqqpjt65tn/MIxn70BXPo4EeGbwiYtceqrJwIoOksgAlXV9pGaDyqULJAb+SSz9FgNp+6ixCSCU7YA2lqnUwrO+Ol8u48CzOtjQ1HVc//4cXBUdJ/Hb3/K3Ud9ap0sJdSyjqdkP3CE/8GS2MwfNgRozwHXTbt666LOxhh80KlaiDt/4jBzQqEDbKLq1s486eE/J2LM11YEh6mhPe2DIBDIP6kEPz7zk2S37W1qLLzjdx0Fnu71QW9YkTaUBH/wy72Mvc9RBluYe+jjlXh9y7h+JM3KLQZjoILvpwieUoORXifCsa1EH2W22PYvWtMucVlFtdzFSwX13ppVnJpczkwbCqXapqpz2CMxIHfTonO8G5+02zJn5WnFG0YQJPPOxTs/I985+zzjuKGNyQoo1m7Mi6hj+vlablL/2emOgvbbbGrV9j3fs6G+jCuc8vfpiDrNbHXW+lZXWJCV9j7N6Rl94D9CqqrAJWKVJttd2WbIPdKG9zpo5OOKOATbSgY764hwD5ES0SxPddonPKVNJ7VJFBlyrq6tjnwiLn8fPz8m5Xtqdpz0w85Ex/WdhlxdR8N0ZWfZmzlHXC0U1lt6BP+8fqQoz70Ubd188eSTXv9+MXDIC3iUjZcgsDbpI35am0jea0x/bDzvBUelxg63cwEfPvIyQJu3S8y0DLWphn3UU3fCNLGZrynZA1UvOgKeO9yzq0wMTNZvabG1iBwxGXzdrWGdFzRsO0NtnSNeXfMwsk9tW6NdXR/x9Z6znNDNva6dfxMHK/BKDsF61dYPnsjy3IV1acqsL/frbF9OmBVf42jyun0zCjHR7HdNFNTK/Ny+zB7KirX9JDIw9+3RCGO+WDJVABRa5PvKszohcWVY51AxF3+y9PI5JSkZUHcPBt3KtY7iPZ7dH+uIJNifE314Ht8Dzt0VhbVQBDOccFb5ZxIbsw6WhTqQ1K8cAbxGqxLKUR+FjgBuLcwyQI7tdckYLVruU/AiieFRkwPVrX/sa9ff361vp8fP4+bmQZ0+NOlkWD9pv9p5wxk/uAdPZ6BlEqQ2pc1kCoWcChJ6FXAVJeB85zx58FtWIqqMRfP2UdX1k9o7jmWb+DkSf3AeEZ6OdErc9nYlD5uQtweXOcjZrn/cMeWHbOVQcuQy6zBtO3XFqedYqF3JpMFF9dcSw1aRLSW9k7g22Rv++erbgxJCl7SbttlizWXXapTsgJM/uWjKDYV1vbrBnsx6lzgeDHSOVpiqtPIEvz7JffeF91uTScr6tlpfLjojvzNqyHkp4IKJmJeQ+O1sGgUV92dJT+Pmj8r2dHpluA+TJAN3r7v1Zlk45eBjGqgZ5lnhRHrLc9y4RV99ETVZX1bTLnK+i2u6iJMrKd5qqPHvaqpNm3ZHjss8Kotul+m/UVdRA98pF6sz7Dzjtk6iHG3lVQvmcaCQXzglYnC2hRFvOJ82KHJNUCL3Ni7eO4ZNm5bq0PL/UrP/htNc3+spH4fgPGKsZn96xo7+NKgxRFjY+Q5S2LKTp++rZrJ5+oe77eoOPLl5CPa9zMe34mzKqlULLUq4xijKQJs4QR/gYgE90WMQHybPknBzYWQUpyhmfNKsC2qWKDLjyibDGjh1LXV1d+p5w/Dg/L+4JtryO0kubROWb5iQ0spHRM1OcmU/2soqQZYPqRBm5NExiENrOm6nzHov69awll1cu503Sn6ZbPctEVUMTEYMtI1NFA+GeNIkvcg+VtIGoEiM6A2Zzd7VVhKjw9G1nKarch8dNg6RP2lOc9DL4Tbdb31vt57rJ7K1spV25pItcusJXREfcu9Qs/sxwJ+1EncIzylXa6LQTaaPCP26n1VyiTqw1Iqw8L5d+963U6WHqYVFvPtkr921100mfMCrhE39xe+BdjqfroaTTSrQr8gRW1vvE/80FDiCL+rLqoXrn79Ul+oBdrvxn/Ff7BW/IT8dMfK8f8CDKagey2zuLA7191FH1cLBMRRxUzZVammm/h8qT+ZixVGh8cjfui5j2SZ3Ms0jqi0IJaa9v0X0zp6w6gzt9Me1SxDkBypauj0SFptNClYXy7rfE4bbPKl3qqGXC1sTbrVJkxjtuHTMux3FVAYj2+oWm/mG31275MJfcgkLpcFDR8x6yP1h8J95VB/FqqWNR7mWB+2vOyY2c72pP9DlKHZPc/kvUyXhLXbAs5RqjKFbWGEb8xnw2faePmM1+6b42PWWPwXV5Dm6hxWMAPglrOaWnf9wo2qWJomxUQLtUlRL09bJw7Ngxmjx5sr6V3s6dO+nMmTNUW1srg6p8gizes5W3EeCZrRxsnTt3rn52ev735T1prnlxFh0op6BdDrL5PSoN0iYc0iUc0iUc0iUa0iYc0iUc0iUa0iYc0iUc0iUa0iYc0iUc0iUc0iUa0iYc0iVavtOmIme4GhxM/eY3v0knTpygH/3oR3T//ffLf/k23x832Bqk9jCpv7mylnYBAAAAAAAAAABUuooOuDKe2Xr77bfTypUrae3atfJfvp3bNgKG2kNuUwPCrQAAAAAAAAAAAJWk4gOuAAAAAAAAAAAAAElBwBUAAAAAAAAAAAAgIQi4AgAAAAAAAAAAACQEAVcAAAAAAAAAAACAhCDgCgAAAAAAAAAAAJCQqpSgr5eFY8eO6WsAAAAAAAAAAAAAQZMnT9bXkleWAdd8JliUkXrfYod0iYa0CYd0CYd0CYd0iYa0CYd0CYd0iYa0CYd0CYd0iYa0CYd0CYd0CYd0iYa0CYd0iZbvtMGWAgAAAAAAAAAAAAAJQcAVAAAAAAAAAAAAICEIuAIAAAAAAAAAAAAkBAFXAAAAAAAAAAAAgIQg4AoAAAAAAAAAAACQEARcAQAAAAAAAAAAABKCgCsAAAAAAAAAAABAQhBwBQAAAAAAAAAAAEgIAq4AAAAAAAAAAAAACan4gOt7771HW7dupdWrV9P9998v/+XbfH+yztOO5supqWdI3wYAACiUo9Qxkdug8/p28RvqWUY14jOry1LacU4/kCjVNtesO6pvl4BD66x0uZw6Dun7AQCgDLjtdUrfU+yGepZa7VK+2msAgNJT0QHXnTt30k9/+lOaNGkSfe9736O1a9fKf/k238+PJ+bQZmrpW0zfmTta31FoqvEuxYGZacSjOh5H1l0x/EbeN4CNfJ1zPdTEjzf30Mel0gsKUHkh9DvGTYcS5g3iROQr8Ts3T7LSISQYc2Sd9ThfRJ4oycMpJk9PXEdH9F25MOmRLqBnymrRBf2cct0d+ht6f+ul9PyHORR+f9ny5ynnd3AvRZdOWdFtzpu5V5SjGzbQwPvv0EBfG03X95W+BA6+Xr1cpcv7XdSs78of017oS471nL/eDfZFfO8TWh/F+Cxl14Zl117nVDeVOn/duW44LVk58ZeX8PZt5CVTx2TOB6LuvSvzeIHb+/HpnhPSVpf+QS/1Gwzne4xu2Oi01zdU6TvLRkJ5tJiJ9mR8hnFPHN7Ae3ieCo6firVuyl2cdCh5gTr36LAOEEWOEcugzq3YgCsHU3//+9/TvffeS3/5l39Jn/vc5+T9/C/f5vv58aSCrkf2Pk3TO5bQlWXXCOWRLmAP0KzIQSVX2rd03kHdcvD5Dh3sIGqpy3LQwYOW+YPU0adegy/dzX3idewBn54F1Uo0O/8j3Lw6sq6ROuvqggGMWOlQ4sR3vObBcU5+4Y6h+IL0AzsoxPmubgWlHu3V6dBFzZ2NnoaE892899vooHkdfk7fCpqWYwdl5Ih83bqCSOSHYRHpOq9T5Kl0LyOec8um2vTPKbjM5Zo7AJ7fens1PTR9WXZBHM5TnrKl8pSnUzGmgTY5+UlcdN4sy04alAgeZIr2orlL58s+6iBRz2U52OSBxzUtKTf/b19MnfNFh9mpd7kcNtKpjj46pfN/d/PTNM/zPjE+S2gb1l/SbVi69np8Y7C9fqj+b8unvY5Dt9ciIXQ6iLp10+05BwvKR0h5qXo467Kbf+7nVGU/tzomTj44sq6OHpywVT9uxgvr6LDV/ZN9u87FtOOE/Rwr6KrfZ/x29bh6Tp2oz0r9wA5ES6YdLGq6j7pm/3H9Hd0+ahajadnWT2vhYqjLh2nrrX5soE/N79Uv6qYyqrPjpEPJE3mmud5X54o8Mz7XA55RY8TQOre+5Orcigy48nYBZ86cocbGRn1POH6cnzfs7QVEZnm8s45mXzeKEG+NSwzAVuyh2aKy2tRQo+/zkelK1NT1N3Slvmt0Qzt11PXRg8/Gr7iHzgyK/1fThDHqNrty5mJ9TRnqWUG7bxaVSmcDTdD3lSQZGFtM3e2z9B2uOOlQ8q5eTqdOLHfyC42pp9micu/cZ3XKt6yg/XVt1NFgZqNPpRbRWFLnRtohA/nn6eT74p+JNeTOV59K3yjBQPxQTyu1UBv9YFk1VeVcOYnOaOMz1Ly9nWbre4LEc+Y/TU3b2tI8p/D4+9vlOpAEoo75iegAdLQ3uL+1yEMvNPXT7tezmH0qg6kb6VanbKn8sv/F3ugOu86bp86U8ixXKGVDPRupk0R7sXyqvmcU3dreRtP7VlBn7IHDUeps6aP6NW1u/hdlqFvk/00bzYBVvG4nt/VuH0m2PX176FXdoY7zWYbOnBb/97dhd+hrJchur32Vk2yvUyHtdYV1Mo9sWSnb6x+IvKOI9rpL/ObcXldwAGyo54lgeWlbnWXZzT+7XKusm0sdEy8fXLn8HTq13On90ejrZtF0epp2H9IhJT2maN6+nK7S5ciMKVq26APuZwdoP9VRzUXyYUm9DpSrZNrBImf6qBeaBsTqo8aOuKq2fnpHe6Ct79xg2noxfjop/vGPn5r01bIQJx1Knxwr16721rlirFy16Ykc2t40Y8TQOvfGkqtzKzLg+uabb1Jtba2+lR4/j58/HEOv76H9zUutwbZFNPCepcv+5Svice80at/jPKODl975nuccRXHubxQNhijs893n+Kfw8xEZdxnNSC9nFQ3ak3aAIoQshItp9tXuCEMGkPrElfcHYldqoxuWUrPodDmzaTjNRMGXM5LlM/g5G+RgsLSpwJj9vWx2OsjtEkLSofypYOr0m+tptJOtVEMgRv80eJZvi7y5jAOw7tFfLjs8w7NjkemQlQDx+z4gCkvzMiuYmAOegbWp9hFqvlrfEULN0mqjJqusFgNeApdLub64po72D3ygb+WJ3IZGHahL1gdqVq+p6wOzCkR+t9oBbl/sGUAZ+ducRmsJp29GSGBZ2QjPcPB8npDZK1zO7c/b1B23lWEmXetkG7W/pd56Ld8MTNmuu+8z3O0+cvXBgPigzTdZ9b+aEb9fXIt9IODcAJ0SnWVPPpaBRKJU36DIjfHE+SyjG+4OtOXNjaXahql2x3x2f83J7XVT1TOBfkv9o3dWVnstBu+yvdb3mH4Ot9cDsr2uTKHlZcXK7MpuAWQq1/GanoTygR5TfMPqy3jGFPxhrl6iArDOrHnxPnXi80aN8YbtA3qh2WpDQ9pIfzua1Sw6f3udZozI72OPEStlFnki7WAlSNPWU9+AbuvF+GkpHwhxV3hxv+qWztrSGj+lEysdSp0aK9f761zRB0mlsm97044Rw+rcetGW5a3OzY+KDLgODg7SpZdeqm+lx8/j5+dOHelonhlSkfCgSjTU7tJlvlhBRm4IA9OofctbJDHAqBuge5zn8FRrnTGdZapdcll+s/VaA53znIKipr9XO8to+PnjW+qyXk5QSHKGR10NXSxviQaw+XKa9uIs6u6oo6osBnLyqIz4zt0TV9A07kTUmZm1SQc6RhYfpd3kORrl56bDtXwQoEzTweNcL+3mvtQMUz4/IO5bjR+rZ1rJwEcj0XZVfpzO1dW8h6IqI9wB5Xx30DODsfjJo5PNXdSSJlCakaijePb+mrY0QVv9HJ4l+qXiirdmNqaebqrrl7NbHOL7tDzYm9VBnaCj9JLofHkHiIIz+BEX0XFp3p58ntrf0kgDy3Q9z3ujemaCiY7MpAVyabdpJw52DNKtk7II+PnbnC6zRE1cOt18woO3wLIy0QkfsUGceO/Ha8z3Fp/FN3uF9yDlNtLZjoTL/0PTszgwqepXuRyxTvz2HXa7b8265zonsCxetPEFD7rqg081qoXlvNk8qY5239wlP3/sAw4yiFFN48eowi+DA/OJunkmRNUgnYyYCcHbMFHdLLpB5v+4nyXYls/af7wk2zA5q8ozY89PfNcTx4P9lnmRNXEZcttrybTX27Z62+uKowOQVnlpmijKy+yt2ZXdvAuWa/k5s61jcswHcjKMNWkj3ZjCEzDqVO3iPNlWc/9Q1NPO7MdkyfZ66THVFgTa65B2VDznVDZLl2OOEc37vGGNEU17XaxjxGQk1A6WHKuPGrffrtt6s+pCBuh1W09ktfXW+In7ulzG3jhRWuOntALpcIXT5/GkQ0nTde4lvjqXx8pVVdm1vaJMpR8jhtS520SdmKc6N18qMuD6u9/9jj772c/qW+nxnq78/JwdelkuRbCPmCqiMd8gBhTNXdQZ0UE2S5vtmWNmeYt3OavIqH3ugG302HH6Wlw6KGwto+HOfLNoTbJbTjBCDnNBr1NBBDGg181iFo7KmUem03Kwo4pa6kr9hDU+okJ7oIVoTdvc6MCYlQ7cqVLB/TJLBw/xfXlmQuhRNe5si0ZyQ43ME4GgpG5c1J6Dej+niBMwFCXx+efxUtVhNVjqKL9oJekWZxmSn/uc0uxM8Uz7bWpgIRt6cWklmjWs5U+ct3g2yWK6xx9McQY/akBDPNsk4QDkdJFnnfw8pobGWzOAwg7KmJl0LyW5dE52sETz55ldPVVv3fFygQOLmmiL3cDcxVQjBlNup1G0kQ+qNtKd5SLayDW6jdT3DJ/pF3iP3F+5nAfDTyf7G2RBBknrBuiv3+MDcNm3sJIMplyug9p2OgapFQP+/KGk/yz+tpzooforSq8N0+21ZyuTAD444u6F57TXWc26Lhdcp4p8Ydrrr8WNEJQ/U154QsameV/W9xYfGZwxnzPXOiabfCD6QNPEuEfOIPc/TTw2flLUmEK9h3PwTbRZclZong4Ucnv9gPkevvbaDlQ49YToQ9zTLJrRvQl+Hqu9dgMiVntd3hFXRyLtYEnw9lGzrk2ttv5kWFtvjZ/MfrjXTCqt/ThjMelQvT9jn6d0+ercrCfwiL/POEYM1rmbFojxeYnNsK/IgOtnPvMZ+u1vf6tvpfeb3/xGPj83IpOIwVO65WzOUbMonn1OXPk4quZZSiIu3Bkx1FJKVSHy9fHW9agzfebb6LHVRH2iot5QHSzozlHqzMxMkoN69hVvH8Cb4O9vaS2TBsCt0KIDY9504E4VL7cur3Sw8cBcLWEw31dRgZbO+aKzzbMKdJ4w1AwKU677ZICmSh5941lrfdTSGlyGXHzEd5d7rg6vA+Ds/5qmQ2Y/p3SZmYn6IvLERPGNA7NTYxF55656aulbLDoOmdJ/hAKQok6VM+aciygnqXyMqLx7MrnizgBQnTBe+v9xyr0+5Nyf/Flvg21kr34kWVH9grizBky7zCePjGq7MxtFEybyDKs6J0jqHpCN0XcxLqqR+yTeUm+CKXZd4N2DlKmZxBzcttv0eJ9F7lvpacs30oGOeqsNUwFZdfZyc5077Sp4WRwdeJF/V6zMeJDKHByxvyu3170PrijD9jqK1V7r4Fiwva5EorxM8JYXu62JXXbzzluu/cGZ+J8zy3zAAR+5XYfpwylmTDFtQw298V7EmEJu9WO13zxbT7bTjXpWqV2XuHVMylPfJImX2qr2yFzk0uXEpWuv4/UPuG6326KwNqr4JNQOlgzuP/G2R3H6qD66rTcrbu2y5bb14vU38tZ2quxx+srxU31/iYyfYvCng2dSXbDPU5p0ndtYP6y2N9YYMaTOPdVl17mloSIDrtXV1bFPhMXP4+fnRC5XzscefPmp5D1LScxFFCI3GGVXFFHXC0hWajwZyC7o5+nVF/uo/uY6T+FPR+7P4wtsl9Um+HrZPHcYnP2XeGan01FTS1XLPh0c3PF1g63efKI6VyQe88x61TPV1dIz3/I1aRTdIPJcSRDfZZPo/nuCR3LfHV62LK6H7F0ZpMqZNzhn9qZUy4Q63hzK/JwSaiwd59SJtLKv13VHtveO2B1Z7xLHAuFy4W8HxCX7I9e5yrI94TrLFFW7/oo4WDkcUW1k0u8TJbsAEqejSZjc2mveq5j8M7Gz7dfIWVmiz7LGu7eo3DKgaZbnPh54X9PS6wu2KnE+S3gbFjy5wvRqt0TZfamiGDyL7/RyX8qpI+VFtNevpsLa62rfdxXttdVslT8VWOQ6y7OHeOTKssqRSNktAPtzOlnX+pzxsnMW+cAXbPUwYwrPTE7V1zFLq0Pb5KtvksvxbVH1SvJ1DK9wDGmXCrbc1m5n4vC3ReZv47dLhVYqZWn4RB819oSAEKat900yk229swduxPjpG5zGZSJWOpQ6NVauqlsdqHM30R0x294Y40gxRpQnQ41R5xa9VJl5++239bVo7777bmr9+vX6Vnr8PH5+JmHve3jt5anqtUf0raCPu+9OVU+4LLXmHz7R9/i8uVY9/qa+Lai/WZs6rG+r59ydev5DfZvJ+6znSEOp55sui/w8h9eKx8TfHIr4KLmK83tkdiS1RqTDku6hlP/j8eeusb6rSZ/A9xBpUjNRfUdvupi/8aahSQ//c5l8rKk79dEw0yqZtMnRh92pJb7vnG065Et+00XkpYmiXIrf72N9T4BMG5HfXjDP8Oc/XZY8r6Gek668D1de00WWj7DfWX9X/f3TU89N/7w4z8lOUuki65KmF6LzBQvkDZvOA+Ji19mKSccs6ljzXsNIK2/auPnYpe5zPq94zyZRTybz++jvvDas9tCPhZSh0PcOqa+Gw5su5nPaZTeYT2V7nlBdmC6vRdbDoXWW7/cbpmBZ0nnaSZuwtFLU51af09828mM19nfS/Zbnfuk+0fx9ZH8oxmf5uHtpSNol87vltf7NRJZL7/cKpKkg81VZtdcxBOpJkU9EG59MHZa7EU+XsPLSnH5MUijetHE/p6d/FfI53TompO6Mkw/0OCBd3jB9XtNOq/e0ypSsu8LGZd6ymIuwdOHP6taI6j73vXVapevPxhad7vb7uHV72OfTQuqr4SjKshSZVoWTbLro7xSj/VBtj8oP/nwXKAu6rXfzgngfroc8f6vKapLpOdJ5JnM6jIxE00WWc1+dy3VCYHykf3NdX6Sn8qHneaF1brCvN1z5zjMVGXBlPT09qW3btulb4fhxfl4cwfdVFUjGgZDuAMhOhLz4MpDOaO7FVxmGFWJ5X0ilqTskzmv5Oi2qs249Li6hjWkWhpOB1UDJ+3nkxVfJe58X0Vg46Rz+uKoczWuIi78hCfwO5pJ7gc934U5L5oXgZ8+YDgWQz3RxOgohF09Z1Q2JeSzYSJjOSbrnJCuv+UWWj7Cy4X7PnBrKgDjPyc6w0iVGuVYNe/D+IN0pF5dAvR/5PiotZB0beE7udYvhTRvdGfKkvbrPn/c97YS8RNSrmfjKkbc+yVSGxOO6k+a/DDf/eNNFfw5PZz88n6rBeBKfRfUP3Nfxpm+mejiybRzmgCW8LLn5Ot17OJ9ZfNawg5Hpy5HvPeyL5/Uyf5Zg2mU4iBJTXuvfTGQ5Cmuv7TTl7zr8A8HZGtF0MXx1VvgBscIqinQJlJecavHEBdMm2zomokynzQfB9sa9eOtf71gopO0LtNU5to8+3nRRacLti1uk1X3e/kX498o49gzjb/s96Rx8n0B7HZG+ybbXIyVeHi2kRNMlXR/VV5864yhfv8TwtsFh/diQvJRwnV0MeSZzOhRe4unir3M99ZUhfu/hBFxZnupcW77zTBX/T092LQvHjh2jyZMn61vp7dy5k86cOUO1tbV06aWXyhNk8Z6tvI1Af38/jR07lubOnaufnZ7/feXSuBdn0YECLjcsRtn8HpUGaRMO6RIO6RIO6RINaRMO6RIO6RINaRMO6RIO6RINaRMO6RIO6RIO6RINaRMO6RIt32lTkXu4GhxM/eY3v0knTpygH/3oR3T//ffLf/k23x832BqU/T6iAAAAAAAAAAAAUPoqOuDKeGbr7bffTitXrqS1a9fKf/k23587Pusen6EP4VYAAAAAAAAAAIBKUvEBVwAAAAAAAAAAAICkIOAKAAAAAAAAAAAAkBAEXAEAAAAAAAAAAAASgoArAAAAAAAAAAAAQEIQcAUAAAAAAAAAAABISFVK0NfLwrFjx/Q1AAAAAAAAAAAAgKDJkyfra8kru4ArAAAAAAAAAAAAwEjBlgIAAAAAAAAAAAAACUHAFQAAAAAAAAAAACAhCLgCAAAAAAAAAAAAJAQBVwAAAAAAAAAAAICE4KRZAAAAAAAAAAAl5A9/+IO+lh9/8id/oq+ll9TniPt+UDj/63/9L31teP7Lf/kv+lp6Sb1flLifIyllGXC95ZZb9DWoRO+88w4dO3aMqqqq9D0AAAAAAAAA5QMBV8g3BFyHB1sKQFnhYCtfAAAAAAAAAAAARkJZz3Dt7OyU/0Ll+PznPy///eSTTzDDFQAAAAAAAMoSZrhCvmGG6/BghisAAAAAAAAAAABAQhBwBQAAAAAAAAAAAEgIAq4AAAAAAAAAAAAACUHAFQAAAAAAAAAAACAhCLgCAAAAAAAAAAAAJAQBVwAAAAAAAAAAAICEIOA60n75Ai3684X0wi/17ZEmPs/CzxfR5/n/t3dHMVFdeRzHf2MF9amY1NDqRlpp1l1qoo2B7LZ11Gyg20Qak4KJpnTZFpqubJ+WxULxqQgNJfviak2ZNiZuNSuYuNKkLe6mduymDa5RN5YEd3EXs7USjdInFVT2njtnYGacGRHujDrz/SQ3c++5dzhn7sN9+HHu/wAAAAAAAAAPkOwLXN1AMU95ibb2Pnvhg6mvPU/zY35TxzFp3J4HAAAAAAAAkDrZF7gurNSekRGNuFuvmp2m5sPhY2drKAld96CxQfL2JQO6PPH7nO1ws04NDduLAAAAAABANrh69aqCwaAGBwdtS0ii9lRJd3/3xkm1/bhIbcftYQbbvHmz1q1bp3PnztmW1ErUX11dXVrHcbcoKZAR+tRRVKvlh0e0pyJfPtvqKq6/vQ0AAAAAAGQsE3IePXpU165diwo6E7WnSrr7u1cuHtilgP8d1ay0DTN0or1IhYkC3AsHVLM0dD683Xbd8fao868duGRPzIwJN/fu3auvvvoqLWFnov7SPY7pIHCNq08dUa/lx69pal7fn7ymQ33HOkKf9rwx3F0VcU2V9vdNpUaq0//8+ZPfq+5Ssjmqw93b1bK+U1XFtiEZd4zh8Uzlb1dpfmyZhdi6s+G6r+5vC/9tex+i+ou+N6FzTpv5fsTvNSUQJg2rqzr8fbNRXxYAAAAAgETCIWfYsmXL3M9E7amS7v70zTb5fD5n26avbVN6XNIXnwZV85uXtMC2TJsNSj8rfEdrbFOUCwf0mn+rCj/+VoNn+t3tmza/Ahvf0J+/t8Ukzd/YeFZtwdD5wTN79WSj35PQdfHixXrrrbfcfRNymrBzaGjIPU6FeP2ZT9O+ZcuW29rvJwSusdzwsEynAgMRr+UvV21RdBBowtay/k4NhK/pL9D20hZ7NmS4+xUtrVmu3vA1I2/q3POvq8eej8+EvWXS55cn+u8tqtXS6i5diFuIdVjBT3pUvs6vfNuSiBv+lp5SZ394PPZvxwah09Kj2p3j6nD/7oA617eozASkOxbbe2Tb2vs0HvU7nLYm6b3LV9zxDATK1VI6OZ6+9qXOve+dGO9AYLk9AwAAAAAAIsULORcuXJiwPVXS3d+FfRXy/eEJff91dC6TFsc/UmOwWr+c8ezWk2rbJHWf6Vfjs7Yp1neDOiK/liyyx44Fz5a64WzozeZL2v/+bq1pa9GGR90Gxwo17qvWkU+/1EXbMhMmAI0MQcvLy1Madsb2Fw5X47WnMvy9WwSuMfr+VKuepl73NfwJxfXqbZJavrAx4PkubW8tV2dr5WTIaWrDHjYVYcP6tKe2R82H6zVZFbZE9d9+oHJ7FI87W9Xp/3fFk0UASl7uVPnBQzp6PvHSV8sL7hS3OuOpMePZo8qIZ1xJg6lj26Jg1KzS6XDux7bw/chXZZ25F+X6ILatNeiMJPJ3hO7jo/bn5le8OTGecQ1rqN+5YklB6KQjv6I+avwAAAAAACB7w1bj0Y3dGt+/SRMZYxqd+JsJOF/V0/Z4+laocaAh+d9Z+ara/EE1rX5PJ9yGk87xVh2peUOVjyUpJrmoUGuCg/qfPZypRCFoqiQKV2PbUx3+3g0C1yihgK957e0LZ5WsbZb6h0Kv3383pJ71L8qf7Hlxfkj/1NvyT+U1/whDZ3uk1jLNnx/xGn1RrTsrNnHcqjsvjOWM55Sa44ynRP6mKXx/Wpar4I7P1GTXhELanpqlzn3wYhYuAAAAAACZad68eZo7d649CgWfRqL2VEl3f/fUhQP6Y8Cv5599xDak2iPaEDBlBP6jCrc+6yZpX78GG1bYGa6PaO0Lfh1p/MgGssYl7W/eqiM6q39dsE0eMK/1R4o99lqi/tI9jqkicL0PlQcGdMW+Qj+57dGGRfH+W5GvgiKp5+z9M23aU8X1tpTAqVCJAoJXAAAAAADiKi4ungg7zSJV4YWqErWnSrr7u1cu/v2vOuIv1dq0Ta29pP01RfpZ4xNu6YHBfdUKbCxSYfvJiUl6C17ape6a3TaQNVuz9EK1PesNs2DV5s2b3X0TcO7cudPdT5V4/Zl6vbHtO3bscPfvBwSuUULh5UTpgAh9X7RIRQX29XjHwUMKxizeNDx0yu6Fbbv9VX0zO9buxlOwpFw9nwQ1nGw6awx39m3r9uSLSS0s0PK4pQP6FGy9Q0mC8MzesDv8hlTIr9ijkZFeNfvi3FMAAAAAAODOLo0XdiZqT5V093dvnFSgyaPFsqbK1ovtCpceWNnghq4KbNK7/5gMkp5uCC+YZbZd2vC40+hRMBwv5Hzuuefc41Qw/dXV1bn74bDV9BcvhF21apV7fD8gcI3h1kttLVNVd0TEeKxDZaZm68u21EBxlTrX96i2KWKF//Ndqq+JiCEXVuq3jYpaAMqEmx1l2+x+fPnPvKjyg7X6fXfkPG/ne7ctNhWhuF4DAU0s7BV1mTN281vGVaIqd0Gq6FX++9rL1LK+U1UJSh+44/nL69ozEXI6YyltSTwWTw2rqz3iHgMAAAAAgKTihZ3mtf5E7amS7v7S7nivPtSvPFgsa+ou/ves5C/Uj+yxa2WZauxufHYhrRdWzzgYNvVR0xlyhvsbHx+PCltNe7wQ9n5C4BrLLH7V3ym5dUNtDdVSqXckcrGpfFXuHlCnzAr/9pqmcXVELZplFqS6ot4mu1q/uwW16nTyRbPC/fte/+lk/3nbVfByiXxJ6h+7M0Cd750qzdP8ie85mzP2Nyvy3Voe5ppwMBs+X6ZejeyOWPwrljOe9zpNUBv+TlB+p5/yJGPxVH/EPc4rkz6/rPq7rIsLAAAAAEA2iQw7CwsL3eNk7amStv6+2ea+Yu77ucllmvWM2d/wsTwsWRojFGKubv21B4tlTd2Cx5dIwa368PjkLLiLB3YpIL+WLLINUUwJAr8a9Y7efWnmdWZNuGkWqTLhZjpCzkT9mfYtW7akbRzT4Rs3MXGGqaysdD8DgYD7mTbHOmw4W6/bl92yznepquiQXuyPDHDhFRPMGrdu3XIftgAAAAAAZJqxsTG7lxo5OTl2LzmvxjHV/u4bFw7oNf9hPf/l+9rwmFfZwyXtr12txi/jxHQ1e92FsVzH2/Xkpt0Rbx5Xq/uMLTFguGMzi2SFrGkL6sNphK0//PCD3ZuZhx9+2O4l51V/iUx1HF4hcPVQX7udMdqQMG7VcPcrWvpJuQaSzSrFtBG4AgAAAAAyHYHrvXWivUgV2qt/N6xw3yjORASuM0NJgWkZVld1ZG3WUJAaVefV0df+SvRCVsc69JPaQ2quI2wFAAAAAAB48JzUZwGp5heZG7Zi5ghcpyVfBUWRtVnztLRmXJ0xZQIKlkTXS80rbdHbn1+hBikAAAAAAMADaYUaz/SrMY2LZeHBQ0kBZBRKCgAAAAAAMh0lBZBqlBSYGWa4AgAAAAAAAIBHCFwBAAAAAAAAwCMZW1Kgv7/f3ZCdKCkAAAAAAMhUlBRAqlFSYGYyMnB96qmnCFuzHIErAAAAACBTZUPgevr0abuXPsuWLbN7IHCdmYwMXAEAAAAAADIVM1yRagSuM0MNVwAAAAAAAADwCIErAAAAAAAAAHiEkgIAAAAAAAAA4BFmuAIAAAAAAACARwhcAQAAAAAAAMAjBK4AAAAAAAAA4BECVwAAAAAAAADwCIErAAAAAAAAAHiEwBUAAAAAAAAAPELgCgAAAAAAAAAeIXAFAAAAAAAAAI8QuAIAAAAAAACARwhcAQAAAAAAAMAjBK4AAAAAAAAA4BECVwAAAAAAAADwCIErAAAAAAAAAHiEwBUAAAAAAAAAPELgCgAAAAAAAAAeIXAFAAAAAAAAAI8QuAIAAAAAAACARwhcAQAAAAAAAMAjBK4AAAAAAAAA4BECVwAAAAAAAADwiG/cYfeRwM2bN3Xz1i3dcrdx99PcNG4dkLl8Pp98zuesWbOczed+PmS2hx4KXQAAAAAAABAHgWsCN27e1I0bNzQ2dsMNVwHAMCFsTs5szZ7tbISvAAAAAAAgBoFrjNGxMTdkNTNaASAZM+PVhK+5OTm2BQAAAAAAZDsCV8sEraOjY7rF7QBwl2b5fMrNzSF4BQAAAAAABK6mHuvV66NunVYAmAlT33XenFy33isAAAAAAMhOWR24mlmt166P2iMA8MbcObnMdgUAAAAAIEtlbeB6fXTU2cbsEQB4a05ujrPl2iMAAAAAAJAtsjJwvXb9ukbHbtgjAEiN3JzZmjtnjj0CAAAAAADZIOsKDRK2AkgX86wxzxwAAAAAAJA9sipwNWUECFsBpJN55phnDwAAAAAAyA5ZE7iaBbKo2QrgXjDPHvMMAgAAAAAAmU76PzF7K31nWxXXAAAAAElFTkSuQmCC)" + ], + "metadata": { + "id": "5OQDmR00sSub" + }, + "id": "5OQDmR00sSub" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f7f64b2d" + }, + "source": [ + "The scatterplot is fully interactive: you can zoom, swipe, and view extended information about a data point hovering over it." + ], + "id": "f7f64b2d" + }, + { + "cell_type": "markdown", + "source": [ + "![4.png](data:image/png;base64,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rdttlULFXV3yJpWaSOHozJkzY8OGDbFyZfnEXTuTZtSmID1dN826HewyFS1vOjFb2tb0g8szevPGCcYAAAAAttN3hmkyd+7c0lfvK2eur0gnRUozNSuzbCsGOpsynYzpn//5n0vL6QRju/L5z3++9PN3fud34vDDDy8t763KDMrtH2vlue+sv+9sy92pzJBMM4T35QRjldmXezO7Nm3zkSNHlu4jfZ0+ncQqhefb/24K/1Kom36mWdCTJ0/O1gzM9vtIRWW77Wn9nuzq9/dkX/e9yizavjNq+/ZX1u3N67699JpuP2Z2pRonGEt1itPs3VTq4LHHHivtMxdddFFp3Y9+9KM4+eST46STToobb7wxDjzwwNJJ6BInGAMAAADIsVSjNgVJ6evaDQ0NpUtaTn2V+rWDadasWaXamkkKZHd1qehPzdpKELe39uX66Svl/fla+b78Xppdm66bwsA0azaVREjtjRs3ltankhQppK0ExykMHKygdiiphLR9w9nKpe+6wZDqEafXIV36U9N4OBDWAgAAAOxBOhlSkr6qnWbZpktaTirrBttZZ50Vl112WdbavTvuuCPuv//+rLX39jZ8Tfb1uvty/Yp9/b10cqo0kzSVP3jiiSdKfY8//nhpVmqqmZpmQx966KFx7LHHlmbfsnN9g9qKSnuwgtqK9AHHYM2gHYqEtQAAAAB7IQWAKbhKMzbTJS3396vqe+uoo46Kj370o3H66advqbWaZpSmduq/8MILS33JbbfdVjrbfR5Ugr59CeXSdSu/tzfSzOZ0YrHnn3++VPogzZpNv5vKQaTtdtppp8UxxxxT+ip8OsFVraR9otr7xUDt7PGl/blvMLt9uyLvz62vVKO4IpXMSCoz45O+J5vre939Sc1aAAAAgO2kWZkpkO2PiRMnlkLCWkkzalNQW3HBBRfEueeem7X23s7CuX0JT/tKtUorUgi7fTvZm+vsSUdHR+nkVem1SjNnDzvssGxN9fU3tMxDzdp02/ta27ZiX363Ui+4r0pN4Up5iooUsg/Wyfr61ptNdYqXLFlSWp49e3YsXrx4h+W+10n2Z81aYS0AAADATuxsVuHe6E+4OVBpRu38+fNLy29729virW99a2l5X/UNbPsb1CaVoLFy0qjK7aY6pX3DulRjtnI/fU9GtS9BYppZ29XVVQrfaklYm18pfE0n6euP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)" + ], + "metadata": { + "id": "oalX8opDEdMX" + }, + "id": "oalX8opDEdMX" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7f557223" + }, + "source": [ + "Let's examine an utterance level mode.\n", + "\n", + "You can choose either WER or CER as a metric:" + ], + "id": "7f557223" + }, + { + "cell_type": "markdown", + "source": [ + "![5.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "WdYYdwMdFUHo" + }, + "id": "WdYYdwMdFUHo" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ee18d226" + }, + "source": [ + "It is easy to add complex filtering expressions:" + ], + "id": "ee18d226" + }, + { + "cell_type": "markdown", + "source": [ + "![6.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "I3JT9-afEgAY" + }, + "id": "I3JT9-afEgAY" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3aefad51" + }, + "source": [ + "The scatterplot is interactive. Clicking on any data point automatically selects corresponding data row in dataset table." + ], + "id": "3aefad51" + }, + { + "cell_type": "markdown", + "source": [ + "![7.png](data:image/png;base64,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utrNAwAAAAAAey4rAgmlJSVW0bXcNmzc0pid8NnjDnfzXN2WbW4aAAAAAADsnewIJJQW27kTTrEf3nSfjTrlIjvmiOF2dMOgoMKd98505R7dK4JXAwCAbPG5z33Oli9f7u5T7ay0beqSyg/x23rjjTdadXX1bvVnnnmmrV271s1PRfx6tmzZ4pYhGqscXw8AwJ7Imlsb1JniMzNvt7lzptv3zxvv6hQ8+PU917my+khQh4wKLnSodx6yEaMmBsNDNjeolnVPT26ad+Vzti6oBwAAe08NbQUdFHzYU2rUqyGeauP+vffes2OPPdYmTJhgeXl5rlMu9fCdaDv0ur1p4Pfr189mzZrl1qOhW7du9tBDD7l5V199tS1btszVa6wyAAB7KmsCCRlBQYSLzGbMme4CHnPnnGkjglma9/mpQxrnzRjaUJ42L5gJAAAyzf3332/Dhg2zO++8s7FB//zzz9ukSZNcI/+MM85wdaKMBNnTBr6CEt27d7clS5YENU0UnBg0aJC98sorrvzYY49Z3759yUoAAOwxAgmRmWe3X7TErn80FDwImfvSczby6rGN80Z8dozZb99olrEAAMDe8rcCqJErPuU9XA6n04fT5cNX0XWlfdGiRa4B3NpVfr/ON954ozG9vrVl+XR/vVbjcAZAeN4jjzxiJSUlwZzktH/KBhgwYIDNmDHDrcPvu98/fww0Dpf9695991275JJLrKKiwq666qpWsxKOP/54W7hwoV133XVBTYyCCsoK0Pyw2bNnu8CDX2+Y36b4wR8v7ZeOw8qVK4N3NDnooIPcODxPr/X1AAC0FYGEqKxdaR/aQrv+dH9bQ8PQmHGw1hbHnljZZL9+NtKW2OK1QRkAgHagK+Lz589vbMSOGjXKampqmpXVSFejVo3XcFr+5s2b7a677nKvkz59+tg555xjAwcOdMttzaGHHuquzvtnbidblhrFarCrwa31aqyyGvRqNJ911ln24osvunlPPPGE9e7dO1hKclq20v5XrFjh9kemTJlir732mlvO5MmTbfz48S44oNc+8MADrqx1+tsC/PZv3brVbr755t0CBG2xatUql0Gg/fHeeustt19jx45tVi/aJm1n/OCPvYICynK49tprG4MM4YCEPtMFCxa4aY1VBgBgjzX8Q5MTduyorr9z2iP1GzdtCWoi9vaM+sOv+Gv92qBY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CCYSiIiIiIiIqKxs758t/49tbWH25lNlMJFARERERERE5TWqpEAF+1QRTCQQERERERFRWRXcEgKV7lNlMJFARERERERE5TVcUsAdGNd3emWfTxXBRAIRERERERGVlW1LPQZe3QUT9WdnPlUGEwlERERERERUXlJAwOtEpcapIgxbUjc05x09OegOzR9NtWHdn4/HTpUn5wvPFSoVzxcqFc8VmgmeLzQT8/V8keMmOlMskUBEREREREREJWMigYiIiIiIiIhKxkQCEREREREREZWMiQQiIiIiIiIiKhkTCURERERERERUMiYSiIiIiIiIiKhkTCQQERERERERUcmYSCAiIiIiIiKikjGRQEREREREREQlM2zFHSYiIiIiIiIimhJLJBARERERERFRyZhIICIiIiIiIqKSMZFARERERERERCVjIoFmQRce/MwN2HiJ0938UJc7nWj0uSHdnS+6szwv3ls0/17scSfTAte1Ezerc2Ls9eTEQ18YOV8+sxMn3Om0UE31/bMXd3rniurGXXtogRl9Pmy8e6873cXvIlL23D3J79gpzw/+DqaFgYkEKrs9d38at7V9Fnse+5bqPovlt3+aP9jI1YV2XIdH9Lmhuru2Y8fNX8CD3nesBIs3H8Rt9zvzH7nlIK5ncEhyXlxzLx53R4epH3KX396KHe75tKNNjY8NBmgBkR/vn8Yjl37F/f75Fu66qr5o3h04cIs77/7rcKD42kMLzJjz4bGv4Lb2O0YCPn4XLXhekvr6+9wJxaY5P/g7mBYKJhKozPZi530rcdvVa93xtdh+LbDjSf64J7EWn/rydtS5Y1h/Hq7HfrR3OqMnnnkaj197NT7o/vavO38rtu1S0/hjfwHbizuveRqX369+6G9xJ7n2PLkT2265Ehvd8Y0XbQfue5Z3DheoEw/drX+8jyQPinTtwSO7tuMT3rz6jbh8y3488gwvLgtTF9p3AcuXeOdKPdragMffcM4HfhdR3VW36kTADvUbdqypzw/+DqaFg4kEKq+uIzjgDnoWL1sJtB9hJp/G0+fLSrQ1OqMdb+x3Bjz1zVhelGighUaKHt8B3HXr8A+2ESoQaHcHPY1N2IaDaOeP/QVJXz/uu2O4OPHGS4pKHHQeHVOiZXTgSAvNWtxwy0rsuNl7xGV08MfvIprKlOcHfwfTAsJEAs2CVrSN+9FPNFYXHvzze4FbbhoVJG5bxpOHhFP0GHd9C59a706awMgdRVrYnMTS9ep8cYoTS3Fj4LZrip5d3tKExe4gkb6LjJU4cK8kne7AjqI7zILfRTSVqc8P/g6mhYGJBJoFvCNI03GeZZ6oGDLvEJLDKXosdwydu8vqfFHjj9/+6VEVVx04zPOFJlZ31dW4Hjux03s2eddRdLiDtNB5j0zdiru+LIknp46E4goX+V1EU5n6/ODvYFoYmEig8tLFu0bTRcDamkeei6cFbiSJsOcm7xlChy7+V2zMow+0kKzFp9w7y07n1JGw7ZavuMknp2j6KLr4Ou8ELUzO+TA+seReP/RjL8WcEgy867xA6e+W4mtFPbZdOlL8nN9FNJUpzw/+DqYFhIkEKjOpVGY/bnvAy+rLc4fA9ReNDhhpoXKSCLpW9TFJBKGLmt73wPBzzbpCoy1qGn/r0wSkcsXHb394uOi6VL6Ia88brnyRFhbnfLh75Prx0APY4V0/dOWKO3HPcK38UvniSlx+Pi8uC5IO9orOB/Xd9PgPR4I9fhfRVKY+P/g7mBYOw1bcYaIyce8473LGRu4g0oI3WTN+1xaVTpC2mW9WAaG2HTseu46BISkjSaji64k00XX57W7FV1uuwyPFrYLQgjPqfBh3/XAq79zhjkl9ClPVv0E/58Z+HxV/Dwl+Fy1oo68lYiVuu7+o4t8pzw/+DqaFgYkEIiIiIiIiIioZH20gIiIiIiIiopIxkUBEREREREREJWMigYiIiIiIiIhKxkQCEREREREREZWMiQQiIiIiIiIiKhkTCURERERERERUMiYSiIiIiIiIiKhkTCQQERERERERUcmYSCAiIiIiIiKikjGRQEREREREREQlYyKBiIiIiIiIiErGRAIRERERERERlYyJBCIiIiIiIiIqGRMJRERERERERFQyJhKIiIiIiIiIqGRMJBAREc1T6XQGn/vC3fiX7//InUJEREQ0+5hIICIiWkC6e/rwG//tT/DM8/vcKeVRju1KQmTjJTcMd1d++FNof+OIO9ch25d5si/Zp4dJFSIiosphIoGIiIjeVl4S4P7vPYrHvnsX9jz2Ld396R98Ah+8/nPjkgNNDXWwbRvf+vZD7hQiIiKqJCYSiIhoXvPuhBcHmzIsgakEqELuav/6TZ8fdXf7q3/7jxPe+S6+s+0tM/bu92QmWlc6GfZ4x/v4j/foZWV+8bFOdlwe2bY3f8sVn8D3dz7tzpme7OOOr/0/vLj3dXz8t7+kt1H82mRfsk9v+95xe69r7PsgxyLL73vtjSm3O52f7WvH7hdfw5/cciOqknF3KnD+ptX4/Gc/rhMMxduqq03ihl+9Cg89smvc+0NERESzj4kEIiKa1yTwvEAFnG8c6tTjEvTuenavDkyPHDuhp5063YuWxQ1obqjT416AXHzn+zd//85RQekf3/ENXLRlg57/f//mj0YFuNMpXlfusP/0+X066C72Z3+xA5+4/n16mS/dehNCocC0xyXbKL5rv+sH9+CXtm/V80oh+/jsb/061q9dgW989XN6G95rk33c+qV78Nd//qnhbXceP6WPSdb749//GJY2LxouBSDL/9N3f6iXX71q2aTbLcWTu17AVZdvQduyZnfKiHdfdK7u7z/Qofued5y7Rq/zvYefcKcQERFRpTCRQERE896ypY066JUkgiQPYtEwtl24AS+8tF/Pl0C1cVGNDoglAH6t/bC+o+1Zt7oN565fNby8kDvhckf8rSheV4Lpa977bp3c8EodCEkSFAfO0x2X3JGXJMLvfPLDM0pqlEoC8g+979LhY5L36mp13JIEkX3LuCQ+pBSAlKa4Z8d3Ry3/Vsl7In+76Rw+ctwdGvHeKy/GT3a/PJxoISIiospgIoGIiOa9DeesxJsdx3QSQYLuc1YvxxWXbdHBuwTBEqhKCQEhpRMkEL7kfTcPF+Gf6SMCM7WkeREOqUA4PTTkThlvuuM61d2n6wWoqU7o8XLygnkpSeHtWzp5TKGYJA1u+uj7cfNn/0InZj7wS+9y57w95HikZIIkNaZ6b4mIiKi8mEggIqJ5r6YqDsMw0HG0Cy/tO6ATCyuXL9bzTvf0o68/NSoAlyL4xZX6ed3bHRhPd1zVybh+rbNFSlKM3fdkjyh4JUDOlJR0kKTEdCQZMxEplSCPsbz4crs7hYiIiGYbEwlERDTvSaAr9ST86KnncfTYSR1sh4JBPe+7Dz2OeCwyXD+CJBTkzr7c4a8UKZYv9Qt4xzSRUo7rdE/fGR+3HIMcSzEvmPfqmZiMVy+C1IMgJSwe/o8fu3Mm3m6ppLTIZBUnPvrkbt33EkNjeaUk/uHBR3TCiIiIiGYfEwlERPRzQepJ+Mfv/AdWtS3RiQUJjrectxbf/Pt/Ha4fQUhCQSpe/KPbvz6qJQCpyPCZ5/e5Y+UjwfHdf/cdXd+AdwwTme64vGL8xZULSiD/Vh/JkHojikkwL+9VcaWQchxS2aKUPJBhOTapF0Hqf5C6GuRRiLHv2djtlsKrC2Lsa5dtyz6mqxdCKmTs7u3Xj4YQERHR7GMigYiIfi7I4wxNKhj36kIQE02TYF5aSZASDMX1EUhFhpPd9Z6p4roGPnj953TFitNV3FjKcX3yhg/oRwq8eanBoRm12iBkP16libINr5lGOb4Hd3xJJz287ctxSIJGSPOOUuLgyssu1OOy/Md+7T34wy/eo5Mlk223FN5rl0opi1+7bFuOabr3TpIMsi4RERFVhmFLOUoiIiI6Y3Ln/vN//k1dEuLtrm9hvpOSEVJZpjQ7KYkGIiIimjuYSCAiIpqG3Fm/+Za/xIt7X3enjCcVJd75+ZvxV3/7j29LIkFKBfzm79+Jo8dOuFPGk9ILlQzM5bEIeVxiKlLfwkQlDuT13Pqle/CFz41uJpOIiIjefkwkEBERlQlLJJSXlEqQx0REpZMgRERENDkmEoiIiIiIiIioZKxskYiIiIiIiIhKxkQCEREREREREZWMiQQiIiIiIiIiKhkTCURERERERERUMiYSiIiIiIiIiKhkTCQQERERERERUcmYSCAiIiIiIiKikjGRQEREREREREQlYyKBiIiIiIiIiErGRAIRERERERERlYyJBCIiIiIiIiIqGRMJRERERERERFQyJhKIiIiIiIiIqGRMJBARERERERFRyZhIICIiIiIiIqKSMZFARERERERERCVjIoGIiIiIiIiISsZEAhERERERERGVjIkEIiIiIiIiIioZEwlEREREREREVDImEoiIiIiIiIioZEwkEBEREREREVGJgP8ENs5pDdwNDfoAAAAASUVORK5CYII=)" + ], + "metadata": { + "id": "64rWT4JeFy93" + }, + "id": "64rWT4JeFy93" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "e3441c51" + }, + "source": [ + "After selecting a data point, you can listen to the utterance. Just click on player icon.\n", + "\n", + "Also you can analyze audio signal in time and frequency domain using interactive plots of the signal and its spectrogram.\n" + ], + "id": "e3441c51" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "a41728b3" + }, + "source": [ + "![8.png](data:image/png;base64,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)" + ], + "id": "a41728b3" + }, + { + "cell_type": "markdown", + "source": [ + "![9.png](data:image/png;base64,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C6O3XzDN9HxxkG3yvmrcENPz/a5mgh78iMoEslmaNWvmbZqZbYIKqe3XENlB0hSUKi/uz4pl8ODBdgRtZBmma75NRMZmHhc3q5qIiIj82LydSsRhVz5k7l70oDmsR//g70Gz+JaHGPCkUN5sM2G6MZMfuDEqH9Hfk9Ho/k16maFD25i7Lwin93ygjRnOgCcRERERERERFQODnlRCmpjz/jLMzJ4U/nkz+mj3hKBmVC4S/mxWcKRTX/VaX3NYNJl2T8jaQ+YLGiL4/ubNmxe9k4iIiIiIiMiPzduJiMjanZq3ExERVQRs3k5ERFR6mOlJREREREREREREWYVBTyIiIiIiIiIiIsoqDHoSERERERERERFRVmHQk4iIdlsyaNL48eOjKUWHechgS5gn5k1ERERERETlg0FPIiKiYvrPf/5junfvboYMGWJycnLsYFCPPfZY9CoRERERERGVNQY9iYiIiqlr165m8uTJ5s4777TPDz74YPtHRERERERE5YNBTyIi8po3b55tpr1+/XrbZHvr1q1m8ODB9jVkNmJ637597XNMx3P8K4+nTJkSa+6Npt+6+XdBzcml2Tm+B/A9mKd+npeXF1ueVE3L8X68D3+y/PjDY7z3+eefNzVq1LDvBbxf5qPXV89f/2EbYVlat25tlixZYt9LRERERERE5Y9BTyIiSqlJkybm0UcftU22Fy5caC677LKM+qpEILFBgwb2c08//bTp2bOnnY7nEyZMMMccc0wsYOozceJEs2DBAptBCT169DDbtm1LeI6g5AcffGADlZhfv379Yt+jm5Y3btzYvPrqq3aZ8H6sA7IysSyYjtcBy3POOefEmqiPGDEitr69evWy09w/yebEsjRt2jQWTGWfnkREREREROWLQU8iIkoJgU5psp2bm2v/zQSCf2PHjrWPV6xYYTZs2GCmTp1qn2eaEYn31a9f3wYP27RpE/s8niP4iaAogqN4vHTpUhukxPO3337bZl5KUBXfPWnSJPv4hBNOMPXq1YvNa9SoUWb58uX2MSAw2rt3b/v48ssvN61atbLzLAg+17lzZ3Paaad5A69ERERERERUthj0JCKiCgmBSgQTzz77bNO2bVuzaNEiU7NmTfsc/yJwiQAoAqOZBmRbtGhh//3mm2/svwhoYtAhQND0mmuusQFT3Xwd0jVvFwi2Yn6+wCsRERER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fZmcWJ9B9z9lhtx6hW3aT0REREREVFQMehIRERUDMz1LFrI90VUAlHXAloiIiIiIsgeDnkRERERERERERJRVOJARERERERERERERZRUGPYmIiIiIiIiIiCirMOhJREREREREREREWYVBTyIiIiIiIiIiIsoqDHoSERERERERERFRVmHQk4iIiIiIiIiIiLIKg55ERERERERERESUVRj0JCIiIiIiIiIioqzCoCcRERERERERERFlFQY9iYiIiIiIiIiIKKsw6ElERERERERERERZhUFPIiIiIiIiIiIiyioMehIREREREREREVFWYdCTiIiIiIiIiIiIsgqDnkRERERERERERJRVGPQkIiIiIiIiIiKirMKgJxEREREREREREWUVBj2JiIiIiIiIiIgoqzDoSURERERERERERFmFQU8iIiIiIiIiIiLKKgx6EhERERERERERUVZh0JOIiIiIiIiIiIiyCoOeRERERERERERElFUY9CQiIiIiIiIiIqKswqAnERERERERERERZRUGPYmIiIiIiIiIiCirMOhJREREREREREREWYVBTyIiIiIiIiIiIsoqDHoSERERERERERFRVmHQk4iIiIiIiIiIiLIKg55ERERERERERESUVRj0JCIiIiIiIiIioqzCoCcRERERERERERFlFQY9iYiIiIiIiIiIKKsw6ElERERERERERERZhUFPIiIiIiIiIiIiyioMehIREREREREREVFWYdCTiIiIiIiIiIiIsgqDnkRERERERERERJRVGPQkIiIiIiIiIiKirMKgJxEREREREREREWUVBj2JiIiIiIiIiIgoqzDoSURERERERERERFmFQU8iIiIiIiIiIiLKKgx6EhERERERERERUVZh0JOIiIiIiIiIiIiyCoOeRERERERERERElFUY9CQiIiIiIiIiIqKswqAnERERERERERERZRUGPYmIiIiIiIiIiCirMOhJREREREREREREWYVBTyIiIiIiIiIiIsoqOfmB6HGJ+3j6jOgRERERERERERERUaJjuxwTPSpZpRr0JCIiIiIiIiIiIiprbN5OREREREREREREWYVBTyIiIiIiIiIiIsoqDHoSERERERERERFRVmHQk4iIiIiIiIiIiLIKg55ERERERERERESUVRj0JCIiIiIiIiIioqzCoCcRERERERERERFlFQY9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I7sq2Gp3SqZWJpL7akR59WfveZY1+HuofRSwCbbj4wnbaKI5yW32WgIK3N8pJG/D5PKUdj9nUE50GbPNf2PLlfy9seOhwPlmUkbSZBLL5zFSffR5m9Upy2YzDREQlu9Wf7ovy5dVGWk91vT2ZGUmNqlOlbnpStHvrv1L7gO1VLVq6+wHT/+igVn6PVGT/MzOb+HxJt0fJJWFQFgGZ8XLS7BLpxYmA7eobBP34PjS56loWsKNpoyPc9ysuSM4H2WWdU5ERERERLQ7y8lHO71ScvPNN5sHH3wwemZM75x7zY1lUdGsEFaaURc1N4v/mEmAoiIJl9sNGlQ4yIprvXg3Kk+lpZLs7/Lw8b0m55G2GQV/iYiIiIiIiKjw3NhhSSrTTM/S6tuyQsqgbz4qOgwus1uVJypjK223D4XtPoKIiIiIiIiIKoYyDXpWpBGjS50dXIVZiKUFzap3q/JEZSwc6IfZr0RERERERESVUxmP3k5ERERERERERERUuhj0JAcz3HYv3N9ERERERERElH0Y9CQiIiIiIiIiIqKswqAnERERERERERERZRUGPYmIiIiIiIiIiCir5OQHosel4uabb44eEREREREREREREYUefPDB6FHJK/WgJxEREREREREREVFZYvN2IiIiIiIiIiIiyioMehIREREREREREVFWYdCTiIiIiIiIiIiIsgqDnkRERERERERERJRVGPQkIiIiIiIiIiKirMKgJxEREREREREREWUVBj2JiIiIiIiIiIgoqzDoSURERERERERERFmFQU8iIiIiIiIiIiLKKgx6EhERERERERERUVZh0JOIiIiIiIiIiIiyCoOeRERERERERERElFUY9CQiIiIiIiIiIqKswqAnERERERERERERZRUGPYmIiIiIiIiIiCir5OQHoscl7uPpM6JHRERERERERERERImO7XJM9KhklWrQk4iIiIiIiIiIiKissXk7ERERERERERERZRUGPYmIiIiIiIiIiCirMOhJREREREREREREWYVBTyIiIiIiIiIiIsoqDHoSERERERERERFRVmHQk4iIiIiIiIiIiLIKg55ERERERERERESUVRj0JCIiIiIiIiIioqzCoCcRERERERERERFlFQY9iYiIiIiIiIiIKKsw6ElERERERERERERZhUFPIiIiIiIiIiIiyioMehIREREREREREVFWYdCTiIiIiIiIiIiIsgqDnkRERERERERERJRVGPQkIiIiIiIiIiKirMKgJxEREREREREREWUVBj2JiIiIdjt55uWb+pvDeoR/A8flRdOJiIiIiLIDg55ERERExZE33gz0Bg7nm4cRVLxpvFkVTRGzn0SwcYSZHT0va7OfvNHcbfqaCZOGmdnB39DTmkSvEBERERFlBwY9iYiIiIqjyWGmZxdjJn/nBD3zVpjF+Hd6rlluJ4g8s2hR8M/FR5rDwgllbL4ZP9qY7iceZhpHU4iIiIiIsg2DnkRERETF0sS0axf8M/rThMzNVTOnmcld2pvuZrwZPyeaaOWZRdON6b5v+WZXtm3F7E4iIiIiyl4MehIREREV02HH9Qr+v8QsUsmey79baLqf2NtmgS5epl6Y86kZbtqbnkdFQcc5I2J9a8b/dNP3qP9NTzN5aVr/sA6qRtP880KT+weD7zdm+EDP62k/G7JN87Es+r1Pzg9eCZvzo5l/2Hxf/qJ5uPO2nyEiIiIiKh0MehIREREVV7PmprtZaCbMlOBm2IS8bauOpvuJ7c3k92fHAparli0J/t/GtFOJlt1vecj2rSl/wy8eb/rFAo5N7DzM9GlmstOCfvbYEWZyl76mf6doAgKoF4wwbYemmldHc8Okm02/4FH8O/uGzewL/KwyfYTp+Vdj7o7eN/vKjtELxkx+4EYz/jiZx0Pm7i6YR/9g3rnmCnn/i31N99EPJgZriYiIiIhKEIOeRERERMXl9utp+/PsZXp1MqZxqzaqX888M/n9hYn9eXbqmzSQkJs52vi03qZfQlAV3L4555uHB463wcwbJAgaOOxKBDndJvauPPPyiPHBct3sfDYKWrpZmV36mgl/6eXtEzTx+6OAbbAthktwFaLtNXwKsz2JiIiIqHQw6ElERERUbE6/nitzzeQuzU1LPO50pAo6pujP0236PXB8MHGhWbQyfBkZmr0uNgkZo8nN5J3nMcGyuU3sXXmzzYRgufodF8/YDEXrtWhFctN6IiIiIqIKjEFPIiIiohKgszNnTxlvTLsWUSakCjp6ApO2/8sLRhijm7gPxbwS2flPH2GGRRmb9jsu7m3OS4hxLjR3X6CCp/bvRnP3dM/o8hlquS+a1rsj0BMRERERVWwMehIRERGVBJvRiezMPLNokc6aDJt4I0tzttufZ9548xSaqN/yUFIT9ySdTjV3S5Pw6HPJmZntzd0vRoFT90/1u1kYGJDJSNYqEREREVElwaAnERERUYkIMzqHj3jSTJje3rRrFk0OSL+e4xFA1P15Rtq2KiDgaUX9Y47+1Lw8c5qZHPUZGhMFXRP7/cxQyj42wwBuPGuViIiIiKhyYNCTiIiIqETIKOsLzeQuXU13HceM+vUcjqxO3Z+nL9iI/j1tn57JGh8VzDeYz90PLDTdbznVCZ52NP1vaW9HT3dHRV81bkgBI6U3Mef17WWMM6L67CfRNL6XGV7ELFEiIiIiovLCoCcRERFRCbEZnZCUGRlmgaL5eeJAQ03MeX+52fQb/WC8D86/GnO3p09Pq0kvc8XFeOAbsAijvA8ys1/saxYP1H169jc9H2iTmBXq06lv0mf7jXZGXSciIiIiqiRy8gPRYyIiIiIiIiIiIqJKj5meRERERERERERElFUY9CQiIiIiIiIiIqKswqAnERERERERERERZRUGPYmIiIiIiIiIiCircCCjYsj9aXP0iIioaJo32pPnEiIqETyfEFFJ4fmEiEpKYc8neD9RSWGmJxEREREREREREWUVBj2JiIiIiIiIiIgoqzDoSURERERERERERFmFQU8iIiIiIiIiIiLKKgx6EhERERERERERUVZh0JOIiIiIiIiIiIiyCoOeRERERERERERElFUY9CTKMgNfusBc98r/Rc+IiIiIiIiIiHY/DHoSZZHN2zeaWcunmplLP4ymEBERERERERHtfhj0JMoiO3ftjB7h8Y7oERERERERERHR7oVBT6IssmPX9ugRHjPoSURERERERES7JwY9ibKIDnTqACgRERERERER0e6EQU+iLLJjp8r03MlMTyIiIiIiIiLaPTHoSZRFduaroGc+g55EREREREREtHti0JMoi+zQAxmprE8iIiIiIiIiot0Jg55EWUQ3aedARkRERERERES0u2LQkyiL7FDN23VTdyIiIiIiIiKi3QmDnkRZZKfK7lyxbmn0iIiIiIiIiIho98KgJ1EW0aO3L179dfSo7Pz74wfNwJcuMHNyZ0ZTiIiIiIiIiIjK3u4V9JwzwhzWo3/0N8LMjiZ75Y03A2PvzfAzROVsZ358IKMtOzZFj8rOwlVfmlnLp5o1m1dFU4iIiIiIiIiIyt7uE/REEHPgEnP3i8PM7EnDzIRblph+N4036UMz7WPvD//6msOiV4gqoh07t0WPjNm+q+A+Pd/5aqzp9lgLM/i966IpmXvxi+dsVuf7C9+MphARERERERERVQy7TdBz1cxpZvLFvc15TcLnjY/qarpPD6blhc+JssGOXbuiR8bs2hXP+kxlV374ft0sfszn/7GB0L99OCia4rds3SKb1fnDhuXRFGPyo/ntzOC74d2vX7bfNeidq6IpRERERERERETFt9sEPZd/tzB6FGnSwrQ1C82ildFzr4Xm7gvYtJ0qjx274pmeO9SgRqnk5+fbf3cF/4n84D+Y/8Nn5sNFb9vHPvJZeX8i37RkEmyVYCkRERERERERUUnIyZfIRZab/WR/89S+D5mhp0Wpnma+ebjHg8YMHWZu6BRNSgOf77eor5nwl16mcTSNqKIZN/9Vc/WYS+3jAV2vNneccq99nMqLn48yf3rlGvv4xA4nm2cuGWOemjrU3PdumOV57qEXmr+d/28z47upZtriyaZLm+PMsW2Ot6/dPe5mM2z6v81tvxxirug20E4bMOpXZuLX75qhFz5jzjj4PDstnednjTC3vHadOa3j2eaJi4ZFU4mIiIiIiIiIime3Cnr2Mzeb2Vd2jKYULuhp+wS9INdcofr1zP1pc/SIqGJ47+tXzF3vXG0fX3jYAPOHXwyxj1N5c/7/zL0T/mgfd21zonn4rJFm5KwnzONT7rHTTt7/XPPnU54wz8x4xPxn+kOm/1HXmd91vcW+9vCk283YL5411xx3h7mkc/idN772azP1u4lmyCn/ND33P8dOS+fluSPMX9+/2fTY7zRz/+lPR1N3L80b7clzCRGVCJ5PiKik8HxCRCWlsOcTvJ+opOw2zdtb7ts+ehTJW2EWm/amXbPoOVEW2KmatO/KpHm7aoa+S5qrq6bmu6LX5d7ITvVaTuyz8XmI/JzoQQFkvtK3aHnZumOL7Z8UTfqJiIiIiIiIqPLbbYKeduCi0WPNy9HARXZgoy7BtKi1+6pxQxL67Vw1bkTsvTB77Agz+eIjOXo7VWg71IjtOzIYTEgnestDCX5CLAAaBTF9gyMlzCOncEHM/PxwfuUd9Mxdv9SORH/3u9dGU4iIyk/ez7nm6ekPmzfmj46mEBERERFRYe02QU/TpJcZOrRNbGCing+0McPT9M/ZuJVRgxhF/XnGmsYTVUx68CI9OFEmJOtTZ3+u2rjSfL8mPghYvmeer84dad788nn7WAKgOhCaTmyU93IOekrQNdPlJiIqTbgR8/SMh20XIEREREREVDS7T9ATOvU1sycNi/7ifXNC49MGJU5LeG/wxwGMqBLQzdv141R0gDMesIwHIL9Y8Yn5x+S7Y6+N/uxf5v6JN9rHEihcvv47s3zdEvu4SnRK+fO7A22fn2gyns5OyfQsZIC2pO2KliNV8BUZVyM+HRo9IyIqXbFzYwYZ+0RERERE5Ld7BT2Jstz2nfHm7ZkEPXOk3Xpg5tIPzbnPHWU+XTYlmhJyA5K+bE55rN+LQY7QZPyWNy+z/yJzyeXLHC0L3R9vbbo91sLsiLaXBBikeb62cdsGm3H13CePRlOIiEqX3JDakV/weZyIiIiIiPwY9CTKIjvzddCz4AwhnekJP25YkRz0DN6SkBEaPdafTNcn59yVn9qMz1Ubf7AZk8gWFbKMO3bFP//OV2NtkPT1eaOiKSUv1pw9CrpKNpVeD4xCj8DoH1+7JJpCu5Ov8+bacvjYR3+OpoQ+Wz7NlmP8S1Ra4l1uMNOTiIiIiKioGPQkyiI7VfAwkybjbtDTZ8b3H5hnZ/wtembMm/P/Z/74Wh+VI5qeVN7Xb1ljMyZHf/ZP+xziFfv4cixf950Nkq5Y/300pXgGv3edDV7l/bwymhL/PvlWPSo9jPn8P+bG135tH0s/qXoZKbugfPz+1YujZ6H1W1bbcvhN3rxoSgjTUI5nLUvfdQNRSdADy/198l32Rszznz8VTSEiIiIionQY9CTKItt3bosemVjT7XSKGsgLsz/j0N8lKuPf/vRVNCVuZ9Q8c8uOzfbfVRt/tO/95b8PjDfBV0FHaWqeyejzmZiz4hMbqNq0/edoStxjHw22y/LYR3fb59hmCIC98MWz9jls27nV/osA7ZQlE+zroz77p5mTO9POlyo3lEvsx1lJGc5hmZR/hZTZTG4YEBVVfnT+274z3rxdbsDIOZKIiIiIiNJj0JMoi0iAETKpGBc1bIOAjy9gqgdBEtKEfeuOLfZfgWCSBJS+WjXHBhOHfjREzaPwS/dV3hwbwEI/nEL6xHODVyBBYgQwAcuKzy9ft9g+h207wvcg+LlqY659/bs135pb3xpgl3n1pjz7OlVOqQaKQWAf3OCmlCMGPak0SYZnvlHlU8qe51xGVBa+/OFzc9xjLc1lz58aTUnv3x//xf5OfrFiRjQldPmYM+wNx7krZ0VTiIiIiEoHg55EWURnBe1KMwAGRldHheOdr16MphQOAp6+oI8vsChBJcmYFMiqlKy5DVvW2WDigh9nx4KkRanYPzjxJlvBWvzT19EULJPMz/6TYMeuxGzYXTrAENmxK549K90HYD3dJvJUOcXLbOKelO4h3OB+LBjlK1BEJUyXMymrvvMsUVmQrmAyPf99s2qe/W1fvXlVNCUUK8M8jxIREVEpY9CTKIvogYz04ECuWOU5RZZbgVJUun3ZpdKsfduOxKAngqBuX5o7guWXoGcmfZK6JIgqgU6Q4OmY2U/ZQO9f37/ZPgd3mXzBBB0YjQ0qElTUJOjLrKvKLR5kT6x8y351g/tFPmaI0njxi+fsAFnrNq+xz3dG5z9p0g4ScGfQk8oCsjoRsJTfcNgV+43NrAxKNw3JN4+i6c75lYiIiKikMehJlEW2q348pbLhsyuqaLhBx0yFIT9fZSV1BWbJmm+iR3FSgRI7du4Ili25koSBlJDBOXPp5GiKnzTv1wFTyc6M9RWqAqJJmZ6eQLHepg9Put3+iwqfrH+mGS9UMUmZSSrP0X51p8uI/3hEVBTfrlpgg0kS4AQMToQBslZvDrvLiJ9X4uUsnn2cfJ4iKmlDxv/e/u6uWBcfVFB+R/2//8l2xMpq4vtj8+HvJxEREZUyBj2JsogOIuoMIZdUmouatZaqoiJZcz6vzB0RPYrb7gQdEYSMZXqqiv3Cn760QYKfNv0YTfGTz+rlkCCqZIEu+GG2/Rf0wE+gM0SFbt4uwgpfuA0YgKg8Xp83ymbT6ZH8pZy5ZTpe/vzTWVmnoho6dbANJs1Z+Uk0BeUqCr47wU7cdJF+g+U1fW484z+Hmr6jTzKbt2+MphCVDClv+jeusEHPePcyie+PzzNx+sJV8+1v/c9b10dTiIiIiIqHQU+iLKKzGPWgRi6pcPiao2cClW5f0EdXxjPhDm6EoKev+ZxUnHQw8/evXmybq3/83QfRFKxP+BkMTISgwr+mPRAPokb/oo8xsc3N9PQsv870FDO+nxTL0sq08kfl79V5I2023Q8/r4imxMuFS8qCW85j07nfqYjQn7H9V92YknNxPEgUlrO1m38KznHv28cyTZ+n1m9dazNHN23fFE2J2xxMQwAJzZSJCkvOcPpcF7+xmtn5T86v7vnS91sLf/twkP3t/vJHllkiIiIqGQx6EmURqUxDqkoFyGs/bUyfOZkKKjC+oI/OCMmEm2m5I6ggxZrcq3nFsz/jASqpfOnArWRLISMUlX1kjcSyP1WTd7Hd6dPTDcKCb5oesT3ddqaKJVZmVSAzVUa07NdMK+tEmdoeZY9L9jm4N3t0OXODnb7X5Fy2ZtNqezPolKcOMrnrl9kA0qB3rrTnw8WrvzbrNq+2jxf99JV9P1EqvvK2c1d4RvTd9PSJ/T4775duZ9zza/z3PLP5ExERERWEQU+iLKL7qNQVapdUYtY4I6pmavaK6ebN+f+LnsUhs6gw3BHdd+7crjKe4hUtX/anBKvimSfJ0xAQkECpfp+Q4ENxZFr5G/3Zv2zTajZDLX3nPNvZBn5+3BDP6ATZV7qina9G7Mfrt7x5mf3spG/fiqYmksGs3P3+2xfPsp+bufTDaAqRn2SPJwaTwscyLeH8Fz2Ol9t8s3zdYhu8lNe27wrPpbHB3IL5SUAUfTIi+PnMJ38znwfnbpsF//ED9jWiVOQcl1AWPX1upxPLXM6x/8Qkl+lQPNM5cTr688bv5xcr4l1CEFUUHy562/7+3/zmb5LKNBERlT8GPYmyiM5aS5XBBhVlBOoJ37wWPQptz98eqwzJSMUgWZp64CVpvp+Q6RmtszTzn/rdxFimpm4aL9xM06J49+uX7cXumU8fZp6a/tdoarKRs56wTat/3rohmkKlRSrM7jEgQSBdodblApUVKX+xgLxT+ZZymTg1EE1fs3m1/ZcoFcnI1zemYoOwpQj6gJRflNkxs5+xwUuxbYc0mQ8/H87HKbu6rHvmT6TlxMpPvu1CAV0tSJl1AzvvfDXWlkf0m6zFgptOeZObTe50GUxQ//7D9O8n2d/Pz1d8HE0hqjji163hv0REVLEw6ElUiSHTB39CZ3pK5dlHKs8Vjc30jCpViSOwS0XeM01V5KXC78ty9U0riaCnjCiPJu9f5821j33ilT9eFBcF+mJFWddBYwQmMW3eylk2gxaV7htf+3X0alixfn/hG3b62C+ejaaGpi15305/buY/oinBD2JOldj+kUG2Eqveej86lfXoOZoPE6Uj52l9TpLzdSyors4Tci6MnwfzYzd4xLadcnMnnI736nMoYJ6x8ptUsqkiw/lqyHu/N2/MHx1NKX1yjsO/N77R15z+n05m6dpFmGKna8vXfWfPxcvXfxdNCUmZdctbqumxoL/9f5wcDwwqUUUUux6Qf4mIqEJh0JOokkLmBYI2+Fu69ls7TVeEpVLh4+vfsiLYkb8jFsTUQaXYNLVOUgn6edt6W9n6Om9uPPvTE+CUZnnaNtUHalHpoPNOZ2AkcefbV9ksGZBKHppe47PfrV5sn1N6D31wqy3r3/40P5oSbMOfc+20u9692jYZxvZEdi+2MuAYWLlhuZ2+6KcFsW3/dd4c81XeF3b6Vz9+YacJKScSmHKDm6mCRTJ9zZZwP1dkyLBGU9Hv1iyMpoQWBNsC06csmRBNodKwPTo/+W7i/HPqvfZfXc5igdCoifAXudPNPGdwIslMluxmPHfLLrLiY8FUVs4rlSlL3jXjFrxg3vgyuVuZ0pKfE5YRlNMd0Q1CKV9u2XJvBuE88twnf4+VNxlc8Mlp99vn7vtFLBjqKbsQK79EFUi8XCaWWyIiqhgY9CSqpL5b8030CJXgmfbf7SooiIAGMjN83KZjFcWOnTtiAVlduZEKj27KLpWvb/Lm2srU4PeuNdt3RBUz9T6BARhc0g9eSdkRNc1zfbEy3g/ZIx/eYZvDX/r8KXa5R858Jnpl9zN35SwbeHT7dvWJVZJ1pUJVnHVms1SYsT9k+rag0i7zwD5A4Ai27Nhs/xVybEhZku+b9O04W5HH4Fg+8p1rNxWtn9yyhC4Z0FQUgWAN+wPTP1w0LppCpWG7BChVmZWyidHYcUzoci5lS27+IIC04MfZ9rHAPOfmfmo+Wxb+FoDMU+C5zFefX9G3M74TAx1RxbRh63r778JVX9p/y0LsnBuUv21R/9dyM8i9htCZypu2/WzPI//++MHYb/Zny6fYMjZ85mPmN/87JaH8aXLTyc1SlmVxyzRRWfvFE23sNZycx0HOq265JSKiioFBT6JK6oCmh5hXfzPLPpYsQjfTUDd31ypqtgSWV7JVdeVGLiiXrV1kK06rNv4QvB5lNEXriNGIJYCFZvIu70BGUWCrpGBwkcen3GODmfdPvDGaiu+OBze2RH2MbokGfZKAhhg28x82uLY7uPXN39htlbdxZTQlNZ25/M+p99nPzV4xwz5H+dD9vW6OygHKiJR19O2qA0kbg4o5bNkeD3p+umxK8JnwPTvywzIk+2f8N6/aivyXToZdXPi+TYUczKs8yLGlbyKA79ijkidZc3o7S9/D36760pbtZ2Y8Yp+DL1Dp2hqc82556zLzh5d+G02BeHkHZMrH5hVl8cHMpR/Z7/zP9IeiKVTRbNi61v6LgGLu+qX2cUnBOQ2/OWhCr8WC7UG5k99K6RJGn0shfn7Oj70H5Bpk0U/xgHqYhRyWZczn563r7fc///lTKct4bAA553s/+X6SLbvIKiUqC1JGdeA/fo5OLJ9ERFQxMOhJVEndN+FGO0o1yKjp0g+hkAEzXOkqz+UJFf9Y8zZVuZHm6mPnDLMVHIysLVmVvnX09dXpBnigpDM9URnFgEUIzM7/YZZ5dd5/bWVOAmywTYKeToaheHrGIza4VtiR8CsyjGjuq5hKgBAVBck2k6bXyKzUJJPozfnPm4nfvG7fi+btgM/rwDICA9BvdC/zxNT77ONwpP54mdoUjaK/ZUd8O1/78oWxLhS2xQLi4Wfc/SWVcBEPYFWCSk9UMXO7gUD3EqC3pUDfqBcO62r+8dHd0RQqKsnwnBMF7X2WrI5n8mcSjMZ5xT3H6RsFgCwkmYeunMs0X7cgVDEgMCi+/alksz3RJzJ+c6YseSeaEtLnNDn/oTUGoPy89eUYe34d9M5VZkHeHDsd9HWIBPO1bTu2xm9SBfPBTS98P347d0prCaesy7K4x8CPwWfxW1DS24QotfDcqcuinE/dX39k3+N6BsF5IiIqP+UU9MwzL9/U3xzWA39DzMt58WkDx9knRFQAPWAABnGBVEEMl1txqEik4q4Ds27lHc9lmj/AmbzevmnoB7K0IGg54tOhUQAz3D/gLq8bqIhltFTgfVRY0q/mt6sTm1PLdrloeLegYvCQDYwOevtKu80mLnzdviZkO7355fOxsh8GMo35IZi/3ABIBQO96G0q3+0Gl79aFVbeZd6x4FAB+yNW6ZH3V2CyHdzR7ePBtcQyCT9sWGG3++Zt2ROML2kPT7rdNntExpqY8f0HUcD/0WhK/Bzwwbdv2de+UgGjdNNFXqcAAP/0SURBVNKdt1Fe3f3m/h6gafTd71xjH+vePqQ8MOhZcUnzdkjVxUZRSblCwBE3nxCkeWnO8GBKWEh0EFPOi9t2bjajP/+X+W/wGzfh61dN3s8r7HSc/xDUFL6bd9t2bY2VVfStPObz/9jHOB9JMFRuej474292eWSQQpeUXd91QFGgX2P8VunfbKpcUF7cG6y4PsC5ecX676MpRRc/D8dPojLN7fZhdu50ez3zcfA7UNnk/ZxrWy8REWWDcgl6zn7yRjPhxIfM7EkPmbu7RBNNE3Ne315m8vuzTcXvEY2ofCHgh4FwxKZt4QW6m/WYKtPTl/VYUaDvRdAVfLcyjuWX5u2+YKavb01fwMCXhVJSVqz73v653C4Hnv34SfPnd681t4/7rQ2ASDA3JycatSQLyIBROoPQ3fbrt6yLHoXQ5BZ9wt07/g/mkUm3eUdF/yQaOT8TyNzU9RGdOaVtiJZDAuIoa6hELVmDUYvjZiz9MHoUijVvUxWhiio2QEm0P26OuhlYtyVsQus7VqSvMl+XGQiQoEL5wMQ/RVN2T3K+1WVbAv5fqj44JRMZzXzx2tpN6Qe/QsAZZfB7O3K2n830dAJD7k0vvV91OZXpvnMpVQybtsXPV5+tmG7LzffOQGRFJfsfx7gtazMeNi9+8bQNYMJW1XehdM2Q9/NK2xWDkMz5UZ/901zxwpn2MXgzPYN5yHfO/+Fz2yICduwKztFRGZbvfmr6X+3yxLuDiZdhiJVdz3mpKO6fcIM9F+pMayoZ6zavseUWXRGVlo3bNtjygmuHxNZCYbmS60ZAQA/Lg+BeYUjZ1OdTnFHt/52gp5TXxPdWfMjiPufZo8wTU8OBx4iIKrtyCHrON+NHtzc9j2oSPVeaNTfdp+ea5dFTIvJbsX5J9CgkWQm+zE5f4NOtOFQk0vxb+plDk3Z35HVkicRHKU7O8PBVgNyAQHnxBRbe+WqsbT6MC3BR2S6S05EO/3W5czNp1kV91glkHH2zar6ZGGyXF794zvbj6kKGZ6a27nSDrInf55J+YRFwRyXKzXhYvSnPzI6CDxALejqVntJ2z/jrbcDx9XmjoikFi/dbGh4Ts5ZNteuBCiP4yp7sO1/QM1V/e4DzD5bv+KGtoinZAc38sV4YmEXI+VeXNclA26z6jnWlysgX83/4zJbBdEEu3Fhwz+vIwNNQZoUup/F9m7zfqWLQmZ7Sj+U/JpdMVxNyQwM3QeT3F5mVUi5kGvgGCQS5WQQ4dwtfxiSOCTnH6Gxz3GjaKQMZBa/rLkXkmHLPTZIZqgdxLA65wZpNv78VxZsLnrfl9vGpg72/FSVBZ/y+MndE9Ch+3bU96p4BRs560i7PJaNOjKYULPG8GX8cKy9OuZHf2tJa39ISHgfo1iL5+CUiqowqVp+eK3PN5C7NTcvoKRH5uQMZbI76JfQ1T/RVqN3KcUUimSHSxyMq+m7AEheY0hTXF4TxBRZ926Y8bNuRvLw+ZR08K00S9NQVSclOFhu2rIkexW3dsTVtM0PdH2dBEvqRywCaYEKqz2D//OHVPrbShAq/HFP5ZZygK8fG9kKUbyla7jEkx56vebusn9tvMEhWrO8Gi7w/24IIEghCtqaIZXpujweJ5KbM6k0/mHe+esn2WevybTft8xUfR49S+2jx28Gxkng8fL1qXvTIJ7nCXlLZclTy5NhssGcj+y+s/LlkUgTk2EZWO7oBATsInH2UWMZTNSMvTHNwZDpLlrmcgwDrKL/TCBLp42hrFACdnTvD3gQb/N519twrrSlKquzKedQ9X/198l32JgcyWSm8oWKzz1PciDnjP4fa7bVy/bJoiol1+6EDj+i+4OxnjjCvzRsZTQld/N9fmF+POiHtjU2UoxdmP20Hj5TzmT4H6mxdKVf6WlLei+uMgjw57X5b3jCgoY9cr7nBTSlHle33T25ubN6W+mYdEVFlUg5Bz46m/y3G3P3X8U4z9vnm4YHjTfcTDzONoylE5Lc8Cnru27C9/Vcu3vTFpJCBBzS336GK5Pu130aPQriIjA1uEMHo3T9FWUs7M1znitKkf6NqpphOZQ16IisNwWqdpSOBH70P3IGBdKaQQKAonZ+3hpmJmUBlvTDb1NfNgobR3WUdwuaf0bzLeL9JZU6anQr0v4lKKbKIYcGPs+3zKUsmxCpmUgGUgIqsz/sL37SDOmlyzvAFPWKZYp5jTAf0simQvzXaDrLtIBY89mR6oknnn98daB6ZdId9runMuKL6+LvkPuMwQE0q67eETU3RTFmgy5Q73v6dDVagiTIq+ehegsqXZOgi4HnhoZfZx7CmgG4RCvL2ghftOWH5urCPZAQ/JQCEDEw590k/sFBQgD5TMm+cjzQJhmKwOjl3gSzXFytmmDGz/2PLLv7WbA63je9mTFFI82f3XCbnV/eGMYJ+WA65Hsk2Uxd/aMsIWjVobwf7Btnn36Zoqi6/JeiyQEjwXALr8F2w/dCKY1xQFuV3CX0dY/qin742G7YmXhegxYX87i9Z843524eDzISFr8mvb8L5WP9Wye/X6M+etP+C3DSVm7KuObkz7bpjIEb0u4z9PGxmvK9QfW0Qu+np/Malml7Ryc2IwtxYTmfZ2kUJvzVERGWtXDI9G582yMzum2t69rjR3D19obn7Agxo9KAxQ4eZoad5mr0TUYIVUSVlv8YH2X8ly2JnfvKFv77oFL7gREUh/d0JXHy5I2Vr2zzr58vq1P1JlifdxDSdn7ettxfZGFihMhn83u9tsAQZHgie4G+tBHWCC38ZKMINaK7dktxnZ0lWJG3QM1Y1KliswuRUcoUO0m7diQqeP8Vz7BfP2u2B7gtKigw2gownWSc3o3vZmsW2UvrG/P/Z57YCFzz/IFgOqYC5n9mqsmRkwBIhFTxp9q9JxQ59rGIkZ03PJ1UAuTLaHgVhdPBegp7pMuN8AU7dDLOsfJ0315bLcQteiAWtEShHsAnQxy6O09m5n9jn2U5aFixe/XU0peLYFHU7UbdGA9NjvzNM55bd7PPiBstfnTfSnhMkIw43QSRohN9L3/nSPS8UlT5GNLlhgIzo/84aah9DQhZzcB6R5ZQuORAMxW8Nup1AuX5j/mj7LzI0C0P35axti45t94bx8E+H2u9576uXoynZZfK379sy8vF370dTQnLOx4j7WP9J346zz/G7hD+5Jt2mbkDLdaceQFLOnzqoulgFUvX5FQHyC4cfZ778MbyZI2VAXzPq9+tAuNzEXLw6Pm/d5Yj+nPhCBiH6blLsBqEuhzoALseK+xsXe+5Mr+ik24mE464Ynph6nx1Myu1yhYiorJRf8/ZOfc3sScMS/m7oFL1GRGmtiEavbt9Igp7hRZ8vC0MuXtAMDBejyO6oTMEHVDLS9XnnC3AiC89VmKbNFcG3P31pKxP3Trg+mlI5SOVgU1QmYWPUlB2VBBko4pOlH9lpQiqvmm/woqJCpaYw2RZSkVq2LrH/XJ+73r461uUEKnafLZ9mj7VPl00xS1Z/bYMpxR01FtkvqNTf9MalNqCMytizMx6x3wfusS+Vczn+JVgRVu7C16Z/P8mOhi/0gCXfrlpg91N8wLTwM9t8NxSiYwsBfVRq8DkJCuvjU1cSK7t41lJ8m8m2RtN3bAvs92UZjH6LAGR5WbdldaysaJKNJTfNJJiRrXCsotxiAJSK5ooXz7JBlT2r7WnaNdrfDD3/xVgz98KcIxEkwj4cOetx+1zKrpwbEPyWoGN4Hk8uFyWV6ZmKzt5ftfHH6BGWNR6UQkByaxSwkt8W8eUPs2PBa/w7b+Wn0SuZkRus7k1SWW/32kl+U1bbAHTy9qrs4r/nidtZbpRIyw5sb7z3meA3Cb9NQmd1Sgscfc7Ur0vGZeLr4WN8z59e72sfS9av/KuDl7r/WbkxBZK5qIP2ur9K3ZUCMhLxuy/B2fELXzVf5YU3nxOCnsF/j330Z3ve+PKHz6OpfvPVQHaVgdzAk66z0sG1T0H9q8e6OMrCY4SIKoeK1acnEaWFJpKotMwLLgBhvyjouSm6EJMLVE0u1lds+N5ejP5t8iCzdG3BFfGKApUM33oJ9NXo8r0/3TwqIrm41hfjlUF+NBDFms3xppcS0NwZXPJKf6sjPo1n8ZQFVHZySumCGxUy2V+oBE///gN7rCGoKH3EFbf8SYYlKlpSEUGml8z3k6Uf2qwbjESLSqgMvORm12wJKq9SoceAKLr5ss52wT7DfL5ZNc/++2M0wu26YL8iexWZq5Jp4waT8X4JeutM7PyoolzZoC9OnHeXqq43pBK3NtgeCEb3fLJ9sK3DfT1n5Uxz5tOH2cowPluRrd60KimQA9KcF78fEmTHn+xzrLcEO7KBlGEdhKkIkMGIUa+hRtWa9l9oGAU99Xm2IDgmsQ+fmfGoPY/IuspvKM4LEmTCedpXLgrTd3BJ2qKCWVguOVdJFqyQzGrpM9o30CGym3Fs6hs+It6tyU4b+EIZxzlVbt645zrZRms25QVbtPL6b/B77LupIb8fbsafO+jZhq1rg/cmlw2cP/A7g3nHWiSpMrRtR3z/xIKZ6hiUsqm/X/a9lF+ci2U/SN+voDM95XXdp7q+trrpzf622yQca8hIvGh4N/Pvj/9iX8ONP7mJp/uuxTwl6Cu/j5LxibKFrHn5HSwoKFrRyHXFFme/+4ya9U9z3nNHm/999u9oSujrvLl2v3+0+N1gX4TzIyIqL2UU9JxvHu6BJuyZ/I0wlet+GFHZsf3yBZUWyb6SoKdciPkyHGPN26OLdVy86Qu3ig4d3KPSkYqvSb+vb1PfBXlFJhfLJdWnUlmRysXaTfGmlxKUR4Vio9N9QVlB5UmyU0oT1j8eAN0Vy5iQfwsiWaKoSGlSgcsPKg86s0VGtUem2rOfPGreiir0/5sdVkCk8iLfr4MHrrVRcEUb/uljdn7IEAcEQR+edLu5fdxvY9mrUtHTXp8/2vYF+PGSidEUvC8ZmlRjXQszEn9ZQ/NznHe/VF1NbI2CyLKv8a+cd2Rwlcpg7ZafUKiiZ3ESrEb56jOyh30Mvx7V0wZ5H596ry0Xd0bdGaDCi3JbmH52KxIpw1u3h0GWimLy4nejR8bUql4nehQf0MjuvwxJpp1kJG+NAk6S/YYA9oeLwmbKCHa5AT6Y8X1y37FlQTI7Qf8mui0EJGAWb16dvD/RhynOOcs8N3/lfLkrWPX/fRaO7v3ml2Ni518JAgrJXl+9OZ6V6oPzG74T57uKaNjMx+w57vevXmyPBQzYhON5w5awD/JZy6eY0//TKdZ9idvl0Pqt67ytab788XMz9buJdt5iuwp06psM8tukszVl/+nfLbnhJDdgQIJzurWCBMB1dvL2aIBC2Kiug9H9y9er5iaVJ5f+zlGznjQro98t3OgCbLsBz59mbyaif+QlayrnTSEp51vUcZdKrOsH5/pK6ivoIzyW6ekpI0REZaGMgp4dzQ2qGfvwi43pfstDCU3bZ096yNzdxZh+Q/uaw6JPEVHc6/NG2SauYs9qtU2zeq3sY2ni5RvAJ56Z4Qs5VH4r1iWOZA/rtyY3+asoAxll6ttV8+2/OvuuMpBKo+5vTrJxcBFcXsEtBKV8lfiShkqP7DNUHKTCJYGGgiBjExWFGd9PiqaEpAKHbFlka4q8jfEbAmiSL5V9qaxgsAed1YRlS3UsSHaohsog+MrhkPd+bwNgvhGNEfR89+uXzReqT8ilaxbZir++ifHyF8Ps8mEgi4pKujrYsGWt/Reksq5vIC3PoCuEigbNf5+f/XT0LE7/luh9L8fym1Ffsd+u+tI2l0Zfiii3hQnCVQToO/KqseeZp6c/Yp9j+X2BsvKiR4uuU71e9MiYhrXCIT8RkMNI5liPgsiNj3jQKNyvco7C4DHTloR9N2L/l8X5MlM66CWZr6C7UQFZl0+Xhd2n6ACbkICOLwCjMzrlBh2adsv5173BI8+x3Y57rKX517QH7PlMMielj1ycDzF91KdPBM/Kb7sOm/kPuxwIXusbTrItkJV57UsXmqEfDbHH8w8bwnM1Bj5Ddvec4Hz+dd7cpLIxN/idGDTut0nbB9830enPWmf/S3YnSDBbT5MAqP7Nk8CjDo5ujsqyDo7rbhvE+uAcjv0yN/fTYD7x4DlglTZuz/ymDX735HoG8w3lxwLkUF43eYtL1gHnwp+3ph98U7Jx3e5rJAiet/EH241KqPzKPhHt3sqheft8M350e9PzKHfAoibmvL69zPAR7qjuRAQYdADNRETL+vuaKjnhISxNEd0sBJCmrBWpAlOSfMEYPciMkD6dKgsMUgOVLXNKLnzXqr7m3Ippeck027I4vl/zrW3yBqgISX+0mQY9JVtwZ9R80PYvtnZR7OYF+l/UZd6tkGyMnkswDgE7VGzRRBkw3a2YFtWPP4cZ577jTTJmsD3E/z7/t61wv/t1vMm3ZOaUxb4pKtmW64KKv4g3u4yXbenXtTLB+QXly6WDuZpv8BydIewO3FTRIbsRg6hI1wXoQkaaqpY3dAGCoEq9mg3M38973gw45oboFWMa1tzL/ovgM7KwEYgqyE51jOHmlOwr3ZxY4BxTUueJkpAqA0+asQsEbkEGwfOt245YNmfy+sm5FYGyn6Pv3LRtfez64a0vx9gs9z+90d+c/czh5r2vX7HTxbyVs+z59p2vX7ZBw9fmj7bTJVCHsjXwpQvNda/8n32OmwY4Jz78wa32eXEgo+7P7w5MGiwG1xL4jsen3GMWrpof3XhaGbvhhKbJ+njH62JddG6X6xC0Mrp93O+Sbpxhu7sj8QP6NnZb6uhzhL7BgP5pEZBcEPWfCRK41M2sZVtuUZ+V5d+i9rfsX92cHr+H2C/YTu5o4uu3rI41aS8qBI91f7CVNeip1+Hud66OHvnJPnKDnlIvwej3OK+C3BQnIiprFatPz2bNTffpuabiNnIjKj9uQLNujfr23/p7NrT/IvghF4O1q9e1/8Iznzxis7H+Oe2BaMruSQ+KQOmh0oPKB5paF5ZkjKC5m6goF/5uJac0oPKHiiys2vRDLPNIBxwATaCxnd3MV2l+h24bkL2F/sUGvnJRrALhNm/XfZ3BuijjxJ0utqmmfsWFCm0qkh35vQqoLcgLO69BdweobONPggxvLRhj/vbhIFs5uvi/vzAXDutqp1cEUqFGc34cF2M+/09sP7kDqeyOdBlGJhhaJWAgrIrm5jd/Y38LJy4Ms+/A13wTAZHrX+1TpKCflF/06VrcQNbcaBCem0540Bzdqrtps1cH+xzqR83bRTiQTly/0b3suiIIh/MezjVLooAgYBRrZO5BqszWynCjtKDmyMgexLrrdYzfCE4dgMH5VgJtL80ZbptoA25soD/jKYvf815T1KxWy/4r3bsgQxIkUxFBTywPAkHwU/AbgecznIH9igLnUPQh/OaC5xPK7uwVH9vvQOa9bAc7GFSK/a75ujxBi4Di9M+M3wbJjNT7BecRBCTl9xPkdX2jTzJBdaan9M+ps0RxMwfna91FhPB1M7Dgxy/sfi2W4JjR/d7q5XH98l8H2mM0niVacciNAUDZx291qn5JZcAo9yaCL8CJmwa9h3WxGdEu7GuUU5yziIhKWjkEPZuYdl0WmgkzPZWllblmcvSQiBLJ3VTRqFZT+2/1PcLBDXTzrz2q7BE9woVheEGi73ZT5eIbcKE0TVsS9sGFJnmSIZjOG8H77nrnavN5ULmSoKc0oQSdDbc7+SmoFEuwE83FcGMCF/XItnl57nBbkXh13n9tphkqP78edWIsuInM0J+3hVmbyK75dnUYREJlTWd8u9bGmpH5oSKIwVHKis4gkkDY4p++stsBfxJ4QTlDU/pVG3Nt4Bjr6faNidexzf4++a5oStGhAofvTxUc1mTkYozEjuPiv7OGBpXZcD9Vtj53S8Oi1fEgxQtfPG3un3ijGTp1cDSl/CDDDFl1t711ud3X0sRyxMyhZubS8GrTFzSTbL2iQDYzyi9uCExaNM6bRZsp6VriwKbJnT7JQEYCQTYcM/itQP++kjmNQc9enz/KHjfPffJ3Ow30evu6xYFsuGZAhiLW/fV5I20ADOdaCdi9uWCM7bN2wJjT7fkXwRgJFuJ6q6BmvT6S5S+ZprkYQDL4XukPWPpgBgT/JED2s7pJWBjfB2UN80dAc1V0U++HqM93IRn5OBdLH++fLPsoo+DSkp/imfraQhWYLCwEt2Q76MClb1AuXL9iny36KX4TBZ/HsqPrFiGD++mBjBCgxvkafUuXFQT+dP/xW9S1D465F2Y/baYsGR97Dr4WWuVN930KOB/qDHh0tYXjCjcAd+RHN2SjY0f4+tvHfLBffOUd0zFP9IVKRFTSyiXoiWbskx940rycEPecbx4eON50v+VU9ulJ5LHLGS1z77ot7b/V9qhm/926Ix5c2COnavQIFy/hBdiuaFRtqnzcyhcq1bryVNLkYhzNJl8LKsw+GOkUF6iomOM9aOY34ZvXYxXKGd+HmSxQWQc3KS6M6LtNjr/g39m5n9htdve7V8cyWHDxL5W9DcHj2PEa/KsrBqujrCJUDNINJvKdqghWVAtVBRajwQtUynVZ+TIaGfervDn2OdYNzzE6fio6yArTv/vAfLEi3q+oQPNU7IvPl0+PpoQBBEwb9PaV0ZSQBEalL0HcYJKgvu5fcHels10l+83X1zL6/bzlzcuSzmfIjETQadK34SA6JQU3AD4LyotU0KUcoDw9EZy/ZiLw84M/8CM3HwoLA7cIbIsFUdnNFJrZo4yj/2PcEKhTo65pUX+f6NW4BrZ5e074JIB1fXH2M3YAlVGfPRHryxLZivJbkZB9r44TX+AX5ByVDZCtiQAYmmBL02wEm1ZtXBnLYMO5Vdz59lWx805huNnvOD/geyUYqruHQNN7aQmA6QhcIoCJ9+JfDCqEcovuQ975aqx9jJtkeB/288tzR5i5K2cF83/EvDZ3pF0XwEjqKEd4/70T/hgLVuG42x6NXo4m8ChjRSWZ/EWFGxIo59IXJzzvjP4NaL101djz7LoKZE/e+tYAm1EucPMJ22ValJFbXhD40833N6hz3epNP9rWDH96vV80JfTZ8o+jRxWHrzso/duK/qux/3AzVsrUp0s/sudx/GE76GxRlwxuqcl2Q8Yvsm5LA/qfxnFR7IxeIqp0yqd5e6e+Zvak3mbRBXrU9geNGTrMDD3N7euTiMAdGXPvOi3sv1WiAOeWaNRZDHCkMz1joyY6QVOqPBAMQxNFVGSQ5XbFC2eYs585IpYxUBJQOcPFIIIS0k0CpOqL8qu8ufaiF1kkazeFgSsMZuAbyGh3hWyfnVGldsnqhUFlNgyw5K5fHrvAR6VIMtBQuY1XglcnVJjWbc0suFYWTfiLS2e/6b5fka2jAzAzlk22ZfKe8b+3zyUr1BfwRyXpsY/+bJ6a/tdoSujBD24yV449x47ALlBhl+AEMkvFN3nzbJlGtwLIYMF3IzNHSJ+eCB5IBj2F5yeBfvkAgRcNzXkxwvuHi95OyFxHcHFpVB6QSVtS0AQXgSM3+0gg4HPdyxfZrGuf6nvUiB4VjjRrFoXNFvzHR4NtubtvQth/5yHNjrT/uurXaqRCnmGz6bwo6PXVj3NjfVkiS1n6VNT7pKiZhZUV+m4FnGvlNwr/rvZkF5YV3LzUgdWX5jxnA6QY/Aj/YlAhnI9w7p+1fJp9/ODEm2zmYpjR+1H0O5tvj0HJLkWwfcSnT9j346aO7n5iYwVpdYFMa5RzHTz1NTnH+cK1Yv2ShHMO4JoFg3pJBnd5Qb+WOnNaX0vJ/gHp4xI+W1H4boRKyxUvnGlO/tcB3ps1iV0MhOuVF1z/bYuyQnETSWC/pssUd29Ogm6tlrs+sZVHppatW2KXH4HXJauTbwB/EVyj4riojIMOElHxlGOfnokjuuPvhk7RS0SURI802m3fk0znVsfbx1WjAOfWneEFSdU9qpoqnqCnGzSlyuONL/9n+3a89a3LgwvLyUGFNQwMuRVqBJPcTBNUJJAl4cvqGPTOVWHTux8+t8EmXAyiaaTO8JHmcALvQXaJNNvGhbD0J4d56HJKxvwQBRyw3Z6d8Tf7GBf8P20KM+KmLHkv2P7hvsG2lObSc3JnJAQmfH2rZYP1KhCDrDTd/2tu1LWCTJNtgHLvVpque+UiM/qzfyU0icf7pHsGncWlm0Xq7gB0n4domox9NiPqew8k6Aq+AVJ2V76sL2wrBJpQAb30f78Mtuc70Sth5R99tqJi+ur80bFzxkq7j/xBysJAUPzP717rDZhkCudAvb8zpQMb8H00QFKmvl8TBoAlGHtAk0Ptv66GNcPR2wUCm8vWhkFj/AZIdzg4TuQmgQSkYeNumn2PPjalL0/07Tnsk0ft4/Jw9djzzXPq+9dsCvfPkihAK9B1jIwCr63ZtMpMWRze+HQDhtKXKIL/2nrPoHPlQQ9mlw6uNVwYsEkHE+Gm1/vbIHF5Qz+4OzzNugH7W7z1ZbzfVbdf7/KC60p0G4DrOIxu75JuXkC2/8fffWC2RVnl2lVjzzP/mf5Q9CyZdEeg6S6R0B0Efn/dG5wIaqaDrnvkxulyT+BUbsaP/yZxsC8cJ/g+96bx13lzS7wFAhGVj4o1kFEFs2rckHgm6k0cVZ7Klx4Ipft+p5p2jfa3jyWrUy5CquZUM1WrqObtUeZYto7evjtAn46A7ICV6+MXyLgIxYUa/tAc76IRx5vhnz4WvRr6eMlE89f3bza/fzUcLVYgIIQmbjazZOEbsbvsuGBEpptA01AhzUR/+8KZQaU6LFcIeEjQY8O29SxnDmRC+Mio5qhovDZvpH0McpGPQB+yEsS6LeWXkVSa9CAOeCx9l4IEKqUSI4FiWKkqishskhsA+Azmg+4WJi+OB9oQmEflEs09X54zPJoaVqJQpq975Vc280rkbgi/e8W65Owj8AX6KNG8lZ/afYeK4/hv4qNco9uHdVvDAM8kdX7BvsPfo5PvtFlDv/z3gfbPl62J3zu3QizZ6mg+6guWFFZhsyGlqwO0tjiq9S/sYwRoCkNGkRf7NzkkepSoQa3EPj0B21nIsiDwLBmg2kMlMFp4ZYTtsjNq9YKbTNJvakUg53gZNEnITUnX+q1r4hm9zvnIHTFdrN6UnCVfHnyDQGUDnKsyGaFc3xyZrG4ICWTF44Y0MoGxLwe/d509t+kMUR/3RriYtuR928WDL5tWFFQ2pn8/yWYYo5uQ2bnxbmF8A1UWdMMIv7WjPvunvXaVuosOev7306F2fUcE/+I9OI/it+Ci4d1ifYNL38s6AK4DyPpGp5Abp+7NqUc+vMN+38tzh0VTQp8v/9h2pTD0o/LroxotgNAcP123RkRUsHIIes43D8eatPv+Rpii9zJTguaMMD0faGOGR1mow9sFz5/0N4MiKgtS8Tuk+ZHm8BbH2scg/XfKKJE201P16SlZe+lGKqXKQ1d+0IcXLtTwJ0EaZIQg2w2ZnagsyejZuHDCRSL6K0TW6FUvnWenA7KRRgcXoD56ICMdDJWsoWnfTbD/AqZxwKxEqfrFS5XdsXpjeDGOfiR101vsv93Bm/P/Fz2KZylh3VG50VlLCEaifzv02Ylm6QKVKYzSjeyox1RFBc2On/nkb3b+unk13o95o1kk/hVS7penCHpSwX5QAX8JwgGynyVbXTdHzd2wzHwd7Et0LYCm56jA4+/xj+6J3hFWZHEOO/Gf7ZIGKMGgMdiHRemL0Uf3N5iJ1ZvDY7dpnebmF+1OtY91hbwgUr7r77mX+V3XW8zdpzxuuuxzgp3malQrs66gEDBxs/93Z+u3rja7on6nK5rCjuKN92/1ZMyBHnRGy2TgtsJqVq9V9Ihw4yGTG2LuoE24AX3m04fZDHVA/8e4IX3HW78NrteOtP2r49z28ffv29cBQUxc090+7rd2sCeMiH7+sKNj1xw4d2Igt2te6m1ueP0S89AHt5hhn/wjVpdwrVI3FX2QSYu+ZNFdjA4o+m4OuTekXPh9HfrREHvtKr+x2zz9KKN7Gbzn9nFX2PWBP7830P6LAerw2t3vXmO7A0F26sqf49dVP3lu9qyNMj3dG0Gzo/6eX5n7X/uvkGb+oz//l/fGQzrSpUZRIUCNVhKnPHWQ7YP8+lf72N+9P7zWx4yc9USx5+/Cb3Rx+vklqujKIeiZ3Kw9/HvI3N2lvbn7xb4VYiCj2VMSB1U67LhewVnv04oRkKXdkgyscFjzY82+Ddvbx7BHlNUpo1ZWrVLN7JETP7S3RZme0nl/Ye1ZrVb0iCoaDPQipD88XGziwmVccJGMUWl1n4RTv5tgA5dowvOtGn0V/RstVQElFy6acXGpMxLkohbNm7RUQT5KpPvH0qSrAEqE8qez916a+5zt327QO78zHy6KNz9D+ZM+InW2CSqZuv9OsTRqTgy+4BDLc9HpGybQusF+9t/v18YzarUV678PKtPJTRIXrwkzex6fcq8dYVtuviAI8OS0+82/P/6LLR/SD2ZJkRuJmZKmk3vVamLq1qhnH6MM9hvdyy4fBlpJBdtKblZ2aHyI6X/UdeaX+59nalWvY6f51K1ZP3qU2tyVyc1Ud2eo2Pv6ca1Zdc/oUeWBdfE1LYbSCHQ32LOROa5NUBdy7NMgfj1KmXGz9p6c9oDdZ1OWvJtwg9PNzERwExAERRDzD69dbN5f+IZtTj5s5j8MbibdMe53NksUgbJZy6eYz5bH+wx9Y/5oc9xjLU3f0SfZ5zgnoak3vrMwZUY3Ay9u/+2frfjYnss/ybA/Vhy/uP6UICian2O9xs551ixeHc+ErREc00gAeOvLMbbrG2wT+Qz6TkdwGtsTWZwSqMb5GgMaIoCK7lfeXvCCnY4WTNJdCm5kIRiJzFPcgDv3uaPMyg3hbx22NboxQh/WA54/rcDgbyrIgr18zBmx7P0Dmx5mjmjZ1Q5cixu9j0+5x1wy8oRgvf0DjRYWsst/PeoEc1mwzFhvBLaRbVvYG4goCxhQr+/onnY++Ltg+LF2myBbdeNWtpCh8pMTHMgVpi0impP3/K63mX1lx2hKeckzL990o5lw4kMmNrBS3ngz8IJppueLg8x5waRh0/9t9sivbxrWSuxXyYUMFDnJeuXEmx3L3bf4Lkl87r4ee1eK98eeu/+mnF/i85wC3x+X9J6EV5NfDyaE/0TPU30Hptj/p3q/9ORf1M+neC7zwxT7f3k9x3lu/x+Sj6RaBnmDfv3nbetNvZp7mepVqpo9gh+zantUN9WqoHl69Gen4XF1M+bzp2wFfMAxN5gBXeKVOzQ1RjBg8Kn/tE2cW9Zva2pWqxFrAtKkTnOb7dG0botYM+nCwOixkpVDFUu9mg282SF/+MUQG6DUHcsDmkrqJpACge2aVWsV++KVCta+cceUg6dQ5mpXr1uozIvm9VqbOtXrmW9WxbNCqXR1btnNVmZFrw7nJPWl5jrlgN52pGoNwdLn+062mS5l2cTvgsMuM3/8RTzLVKAiiLJ3YJNDE4KSWDf8Bp/Y/kxzz2n/Mk9Mude8PHd4rEJ9aItjzENnjrC/qS70r4xsL+h96G/MDT3utY/T+dWI7knN4alo2uzVwTvwSVHg97Q0MioFguooU7hxVpbXZ2332t8c0fJYm2GonbDf6Qnd4FDxPHbeC+baly+MniWqU6OeGdN3irnpjUuTbmhg8DV0d+UreyiTJ+x3hhkXBfFgv8YHGgyWiHrBVd1uMyM+fazSXOs/es4o84dXL4nVp3wQJOzU/CgzfGZil09iTN+PzCtzR9oAo3ZSh7NMrWp1bCBV26dhe/PXM581y9ctNX98rU80NXRmx4vNbT0fNne8/buE/ndxLX7hYQOiZ+lhMMYPvn3TfPzd+7Hr9H5HXWv+7/Df2hsOAgHZd75+yYz94ln7/KyD+5hbT0rdf2o66PP50cl3mdfnpQ6e7l23pene7hTTo91ptpWhHihXINA5YeHrsaB8Ooc062yObHW8HaTv5+B3FC3ENmxbZzCoJeIlyBzekb/DtK7fxvz1/LAffKKSUKGCnmFgMddcMam8sz3DoOeivmpwJSfo2fbuhtELRGXrTz3vNFd3/0P0zJgLnznNzPz+Y/OXc4aam14daNo32d/UqFrTzMv9wr7eYM+GwY/JGtOsXnOzcr2/yVM6DWs1Mmui0bmpYmjZoLVZvja5vyJx1fHXmxc/H2Xyfk7fXAllAhlJ3+Sl7yeKSk7/Lr+1N82yQf09GwQXrIVrkilKurLeptF+pk71OkGFIHV7jEa1m5ifou4DqHDq1qxnNmxJcwPXo3XDfc3SNfFMpZt73WUeHP/n6FncntX2DCrg+9t916xei+B3Kn5zrnaNOjY7pG2j9mbxT/4M0aK67ZeDzX3v3hk98xv4ixvNDSfdbh//95NnzJvzXjZfrPjMbNq20bxyxXhzWMv46OrPTf+X+fO4W0y/Yy43d5/+F/P6nJfM78deHr0amnjtJ3ZdBM69v3w83l0N/PmMv5p+Ryd+zuei4Lf/k+C3X+PvdeEd3uooc1zbX5jHJz8STYnr1OJwM2fF59GzuBb1W5kV6xIzmUXjOk3NKs9vr5Tl4jpg744GAxLlrvN3keLTtO7e5scNxevP86h9uphDmh9uy7l2TqcLzKtzXoye7X5Kar8KXN8/MTl1sEfKXpOgnO0KqvDH7NvVXHnc74Nr/wPNZ8tmmJdmPx/8hd3EHNvmeNOlzXGmQ5MDTOsG+5qJ37xrnp81IuEcW5nIb/i1Pf5kHpv012hqIjkHou5zasezzf8+TeynUzxx0XPm3QVvmVe+GGP2b3qgDRr7rh+O3+/EoD41O5jnanvdcly7HuadL99Iup74e+//JJ3vG9bayzx07j/Nvnu1NU9+9KhZtjaxJQMG8cN10NK13yX8vuJzj13wdPBd/u5N4O0vXzd/fOl3ZvP2zfY89e//G2l/PzP16dIZdnlRl6iSU8Vc2uV3Qf1ykKkZ/B7jd+nt+a+Zdxa8EatLArbpSfufEvzudTYLfphvPl7yUdLv8rmHXmj+r3N/W+5g9cZV5r2v3gr+xpkpiz4wW7Zn3uXL4rvj3eIQFReDnl4FZ3oOefs2s3JDrlkbnASrV61ug0w1qtZI+heZe0QlYeLX79iL7ztOudcM6Hp1NNWYi587y/7wDD7jIXPnmzeaA/c+2GaHuhfqqS7EC4ILq4KCZ1S2Tj7wdPNecLGWSq8DTjPjgwuMVFBparjnXqZH+572Yuf+9+6KXgntXbdZ8J6DzYcL4/11nnvoReZv5//L3PXWTWb4jKeiqaUvmzKNUfH8TZcrvYGfQ5ofljZgVxHh3IB9g4tuH9yg+esE/wAA+zRsY75fk34k1sKY9sd55oaXrzJTF8dHWwdUAtAMNNUyuto17mAWrSqZjK9sgQobbo5kWj5rVqvprdj87fx/m/vevSPp9wSVI5yPUInTTuxwsqlSZQ8z4auij8KeCs57c29bZq598bK089+/6UHmnaun2t/Ou8bdbN6aFx+QCQFR/BajIggPTRhiA2d/OPFWc12Pm2yl8oKnT7GviWcvecGc0CHeRHjytxNNvxG9o2eh//T5n+kZVCwLcuX/+tpKqahVvVZQIT3VvDE3s5GxKXTryX82O/N3mb845+WDmh1iTjnwTPPoBw9EU+JwnbXgh+SscdwcqF+zQVJwA1CWvv4x3q1MUR21z7F28JgvVya32kCwREaB13CMTV8yJXoWh5vkC/PifQPi97ZVg33MmYecb8uz1rVtd/s79dTUodGU0AWH97E3AzI9x2Yb90ZNOgiuLVu71N40SaVTiyOC6/d410U+uPH90oD37DWFDwJsTersbfc79qf22bKZ9vV3FyQPsiZJEgXZq3Zj+/v/1Q8l22rl/47snzJICWcF5fL14PyGQORH38b7NxXN67c0Y37zljnnqZ422HZw80NjQTv8tuPzM7+fboNvuCmH6xccL//t97IdgKr/fy+w7xW/PPAMc8vJd5ufNv5k7g1+uz4Ptp14ccA7ZlfwmX9PfSzhehvbvH3j/c2k4PpZzgOZ1L+ObH2M/eyJHU6xNxgy8fWPC8xv/9fHfLd6sT32h174rOnWNhxELxXsX5zT5OY7bhj/o/dTttz5YB1wfCPQi7LjgxtHZx/S2/Q+/GJTL023K7guwG/ehK/fNrnBMdMwKG8IUuMzOTnSdDOEAXnxG0tUUipU0HP2k/1NP3NzBWjeHi7LU/uqoOecEeawgcYMVwHZ3J92zx94Kh8YzfGjxe+aXx1+hW1qIK575f9s/zEDj7/T9k1zYNNDbT+f6OBcQ/OIgkZU9JHm8VRxnH3wJeaL3Bkpm+O5TX97djg7YRCiIac+aacB+mbCwEawX+ODbB9EaG5yeZcbzX+mx5vM/OaYP5gruvzJPDPjkYTpJQn9qrl9XVbW8te4dnCR64xQi2ar+zU60Pzl/ZujKXGXHn29ee6TR6NnxYOMhZLqi7J+cFGqB6HR0GUG+reSQa1c6Zrpddz7CDNf9Ukr0I/h73/xZ3PGfw6NpoQ6ND7YTkffh/eMvz6aiu32e9Op2TGma5sT7Sir6N9L67bvSWbRmq/t5zQ0VUPfXQLNto5sfZxpUW8fM+S930dTsweObd2Pb2Hh+Ee/XJlo12h/s+in5EEWhp7/ovn7h3cldTGA3zRUePAbJ94YMNs240WT75fnjog1k0c/luh/DuXhlAPPN2jK+faXL8b6vsP0ujXqm3k/zLJl0+efvV82h7UIK5WvzRtpHpj4J/vYBzew379qkR0ACyP/ux4445nYoEX3T7zRNhO86cQHzbmH9LXf/++PHzRzcj+J9UeL5vJoNi983/9477HmiBZdo2ep3R987vXg8+i64fSDLrJNMtGf7SvB9nJhRHk0ZcwWR7U6Pqn7lqKQLgwweNajkxOzfv9x3hizT/125rznjk5qRntU6+4Jg3AJDDBVt2YD728zRvTHtVqmcFx8t+abpH6z8d3oLsm3/rj+QzNZ13lBecRx5PrzqU+Yu96O30QHdMPQq8O55pFJt0VTQkfv08Mc0KST7etPO+fgX5sJC18zabvyipR20//CKup1sVaYrhFQptC3ZCbNgF1Xdr3V9mEMd50y1Jyy//n2cVF8vuJjc/XYxM9ffMTvTLc2vVL+Zgv8dp5/SD9z0eGX234lizOgDgbBalF3H9u10vota8zwPhPMgOdPj/WRKcZf+bU9v+H4QZ3HvcZBVyow6ORHzd51W8XqRQLXAVd2u812L4Sun+585+rg/By/1vxvn/eD360DbLdg0t8pYH/hXAMY8b3PyF/Y3x+U43d/u8DWtXB9hMGV0Cwdhl38nv0dwu8ZBqQS+O3q0Ohgeyw2rB3GFnLXfR9sg9bmyFZhVmRR4Fr/zneutCP0A7oqOLhZeE3vmrp4gh0AUuoHvz5yoLk6eH+mUD9A8/1Pgm3bBt1dtOpmDm9+TNq+p7XmjfYsVOwE7ycqKRVq9PZ+i/qaCRUg4AkYuGjyA2/HBi7CwEbm4iMrxCBLtHv6vyN+ayuNOuAJVauEh/G2qIKHu2O+Pld2FXEgI2SNUsXSpE4z07J+m+hZWDEHjPoLckGDvn6mXrvCBjlxYS7q14x3z4F5oa899NtzbnAhe3mXP9k+Y1s3aBe9I7RXrab23wZ7+vsxvqRz4kjKRVGjWvIFTo3g4rYi0H0qabLNXS3U/hEI6iIg40JAwjcdgbwbT0jOMjq0xdHm4bNGRs8StQr2Gy76S8oeOVVt9o9PtSo1Ug4AgnVC/8SpoGm8T4NajUyDmsnbGpUjVGy6t008/w045ka7naBujfg80V8ZyvHFna8ye+0Z3bwMoJxj+mXH/MGWfdG1zUm2v+RDmx8VTQmhYofvxY2EyqwwA7WgQueq5ZmWagTxZnX85Q/bvqH6DILm2LY99jst4TOoSDaMzjM99z/H/u4hmAMnB+eqt6+Yb649fpDpc8RVdp+90H+aeerCN8wNJ9xv7jntSfv+v575nHnl0pkmBx2nB/529ihbqQ3FM0pwAwnnyDM7JvbRJhC4RL/JqQYC1CM1y0Agsuw4L197/F3m7+c+b45re7Kd9siHdySMkCujbF92TDygmmlZqxu9D+UY2wFBC99nce6qnWGltLKo6SmPBcE5ydW4Vpgphyadrhp71DTVqyb//iBogT+f6sE+T3WsZTrivmjf6EDbR7urenBeRWDVp1mKc3+q34RqVZJbo1UNzvk1PK3UquXsYa8vXeh7HkEolz6/ilS/o+WlVvXkMlFYvmMOfRO7Gtfe27Tb6wDbF7sLg5PeefI/gkeJ2W5wfNtf2nMU+nc8o+P/meu7Dza/aBveaCmq9o0S69oIWuFchX4WC3Jl11vs7yKgj9FUjtnnBBskT+eMg35lz9cjL/nAvD5gtj13yvlTICtfjt02wfYDCXi2qL+PDfJhHviTct6zw5n2X3Fd97ttwBNQh3ru/+KDcoIcm7huwLka5RnT5HcH8PzBM561CQOnHXiB/Z0C3Bh+5OyRpnen/vaaGwFPwHsQTMUNbdxUffTs0XYZ8ZuG3z38YX8WJ+AJKH+4HsQNYPjn1PvswHm+P/RfivrBIc2PNM8G26AwAU9AGca+/+tZw801x91hg8mZBjyJyls5BD0Bo7R7RnDvm2t63jTeVIihNDr1NRNuWWL6VcCALJGGoARIVssewQVxFVNyQU8ZUVaTIBuVPl2Z6LLvCTZg0zW40EBGmkDlHhdTj5+XOABI24b7R4+M/Qz2GzKcDnYubDEI1suXfmIv2vAXBn+OtkEJsWdUkWvoqbTcc+q/zIFNDomeFZ2vsugLhJYHrLfvAr+WpyINdasnvxfrV88JbuJmBgJwMtqztnedVvZi2dWoVtNYoM+Ffi3RtUoqvsppOmGF1n+8Vw9e8wXIAOsqlQKf+p7AJsohKnTI+nPnW79mGFxGtoS8hkxAfYNHB3YOb97FlmNUKBqoID8qJJiOjFKZJ0jQ2Q3qX37MjfbYkgpTZVWY/Y7AjatmteSKTYMUAzki89CnZb19E84fZx70f3bboozrz7QI3uc2dfvHuS/Y8nHRYZfboAGy5jRktuDcJSPE43Wcv3C+xB/OnRgQAo+bewJArRsk36QQU5eMt32v+UxdMiGWBSgjz+P41FBhP/+QS4NH4TrpbPYfo1GQ967TwpZnyLQSKQG5mlXj5yBfAAZ9z9XyTC8JqW6IlLZUgWFfYFOcc8gl0aM4Ob9ipGcXtq+vi6rzO/3GDlbl/b0Kjh3f8QMNUtwgSwVdO/hu+lULlkvfuBRYHsk6dvkCrgjWYF6uKsF5G+d9F9bLF/SsHmwj9/04R+M4dqX6vSgv3kB47cTjV2B7+fiOLV/Z+EW7U+wxXk/dnBOYd4/9TveEPE1wzXZ09MiY23s+YoNOxd2O+loGI/L/49zn7WOUXwQZU5ly7XI7cJVIda5CcBuDXqW7BsBNKvwGuHRAH8d5133j1zoIuul1xzVu3yMHRs/iuu4Tz9YE/VsPDdRz/NbUU8fT1u2b7Xp1Dq4d3PMMzhdIIri62x3RlLjfdb3NXHtcPFscN8Rxo+23x95knuj9kjlo78OjV0rHb4+92QYjEbiVoKr7h+ueZ341zvz7gtcLDEgTZZtyCnqm0Ky56T4912TeNXfpanzaoHhA9i+9jP/ynqh8yUWF3PnERanvwhQdnhdFNc+8MLL87iJdJaoghQkOp7pjrjM0jmvzSxuwQdPgfVQm5viFr9sLGlxg4e66XIi2Dy5+xIntzzLHBxfdJwWVtUwumJGVhWZHQoJC6H/HhZEYfdkLheVbLmTbVASIwfiyYlJtyzqezE2Mnl/b2c99jrjaXNL56oQsRYFKd11PUMFXQRe4SK+Zptz5MprSqZZTLWU5xnnAV7mDPavX8lachS8AULtafNu4209XFJvUCYPxbhYWRmcXjYLKkdDZhU3UdJ11JOUb208HCOW4rF2M80BFUM0TPEmVdeULkNb0THMzckTz+ol9yCHbBZlKoAOCOtCPTF7hC5oiuIaMdDStLAxkUOKcCRhhF4/1zRyBijOW0XeTYfB715kXZj8dPUsko9GjK4tFP4UDwqFvNdc+e7WLBTS2qv5Of9gQXvGiTKNJP5ahVb3UAVhN9hOCTsJ386R2cFz4zgkIEKcKHvr4zvEIFpRH4BPXOL5yipsiqX6LUOF31agWbhffeaxhzUbeoGPVPcJrIvmshmVKdX5Ota11dwcago++ACp+h3zriHX3ZYBi3cLrmMSQWp3gfIusURdaD/mCvbiu8AWxcCO1uvPbiHO/77fGFyQrTBksaXWrJ/9O41h0r/twTvKdGwA3Gl17BNvQPb8e0ixsReALWFevUsNeS7RSLUROO/BCe5Omx35nRFNK1qkHXmCP3f2bdrLXlALlyMe3733HzcDjB5m3Lp9j+h91nani3LzSOjU70l5nuvSNZNxs/OtZI4KSG5+PvgGZ6rrZPce7LXLwXK7d0FRf32RDk/Ynzn/J3NDjvoTv1XzlGNsy1fKUFQSwR1w80d5M9P3dfcrj5sCmbLNKu6cKFfRcNXOamdyluUk+BRJRKpLpJM3bbaan5+IEHW4XBbINXL7m89nKd1GX6YWNr2kcKkW+Zl+pLvx1hgYy64QO6ujswS779Ig1uZSmQHBIs85myCn/NBceNiCaUjDcnUb/RLhYkgt+N9CB6chSqFeILJZUleSaVT1BzxQBtxPbl05FIJWcnCreIJ7v4hd8wUo0lXIzPSVg4VZicaGPfbWnL8Mu2ta+zBMsj287isI2C60WDdTng4pxqu9CkKVqTuqg5wnt4tkiQmdquttDr2vT2uHxg+b1mj4udXBNZ2/q40ZXhPR+aejJYNyzHAI7JcmX/VvbU7bADfTgnBUGXxIrgHulCHo2q5sYtNT7FQNgCL2Pm6sbLC1SZIqWBQzC5JOu3zr0lblle7yZew2VeSmQ5YobU7As6n8U8n4Og8FN6zS3/xaGBNd0NrzvdwT9IvsCUL/ufFVC802R6nj3nevQEqSq52YQMthLApr9+tgsSM++wvkIwRQfN9sL5KaaG6hEEATbraon+CfBH19wE9su1U2nVL/xvz4Cg2ElL9seOcE6euaFaTozTeDGjS+IhwBeTU8zbpwvdcBcoPWQ78Y2urjw3VDHNaf724hzv+/ayfd75guypeo2pqTV9pzX0aWTBLZF7vqlZv/G/tYsvnKAa/A6zv6Wa2nfvpP90DbWBUfYtQpu0uhAaEnaL/iu/Rt3Ssr4q6NuPmq+mwy+pAD9u4/ueY5omdw/MYK5yG730Tc8fQHltuq61t3Gmhxvd/Ty90XdZZ8TbDlzWw0AuoNKdfOEiCqnsgt6YvRz21T8QTPcLDR3X5DYnyf+ej5gzN1/YkYlUWFIgHNLlOlZLbiw2qNK8t3JVM3zNN9FKvp3cqXrqy/b+PpCy7SfSd/2REXNd3fblzGITAEdyNHZF7qSVEdVdHp1ONtWrnEhh6ZAxYXmwJifVEx00BOVQgRGoX6N5At58AU469fwV2h82yBVEAJZX2UrJymbBWpV819013UqNrjIx/ZyMzpln7p9eh7ZqpsNlPj6HJNAODr2d+FCP13z9lRBSl+FBnCDI1W2rQ16ptg/NYLv8d0cQR91yGbzZTDpsq4zO0ddMimhCZtUVKtXTTwP6W24V0KmZ7y8NYkCptBQ7SOdISVN3/Txq7NPUm2risLXPNObMVY1uTwjE6R1w8R+BBHk8a1zqlFam6usTdDlVPevqo8FnCckwIOM6PLSUC2fD45JBCGOa9PTHs+wZtNPZpManKWqp9zD3tF2GTnr8digL5Lp2TRFACAduYFRUx2fvhtyx7Q6PijLyecR7FdfIA4BUt+52NeVB1qC+AJh6LvQB9uvMNA01AcBQe/1SrDtq3umg3vuxe+abC/3PIaAh2R/uWVfrom8N+mCfeE7X2JZfdsUEDTzzQuBR/3dOC7wO4LmxfXVuiDzDedUZFTrACLOA3g/Mu4QiHWvCrHvfcFNBOd8mZ7o4sL3/urB/ndbQSBo6NsOvox5X6BWX9MgCxzr7GvGnIovaOo7NnxBM6xLYVp1+NYTx4R7bO0RVbl9Ae7jor762zWKt85xu8koaeiHHTe03S4RfNsJdL/wQro90nTSBcqsux64eYJgbqrAdp0a8d8LX5B8n4ZhFyY4l/zqiN/Zxz62D9VG+4cDf3ncd/p/zLjL59o+pYko+5Vd0LNJLzPUNhW/2fRL1afnpEHmvPTXm0TkqBpc/AP6oQGb6ekJVGYymrM/qzN5Xr5M0mzly+7LtJ9JBH5c6C/Jf0GdXBlAs0QdyKmvmkvt02A/O5gCOkU/olW82RUq4riQ/ce5/4umlCwsj1QG9R33VHfFfZW5ein6xvJl1dTYw7+tfUEYDORRWqoE/3kzPT0VoXtP+7c566CLY0E/BHOkia2b5SEZHrppNsiNBd2Jv5BBpXwBUWRGpKr0Q6osJF9WMiBrw1fe2+61v+m49+He/Quo5FdzsmVAtldt77LHK4m63LvbXSqZ7nGkP6MzRHSlS1e09PGkAzoYTAl0Vqx+HYEEBBl8ARlfIMbHF2wqKvfGgq9rBd+NGpRP97Mob+7vhw1geNYLGbRuIAAV2nrODRA3ECPczLuHzhphR9Iuz0qoO0q364T2Z5ox/abaTGwJoK/evCo2MjpGvk7VbQAGmsD5eeX6ZXYAIwQ+0b8nykyq7Pd0cP5HObxdZTLpbEwMVoHfgp4dzgn2U/L+w3HlNodGM9d+R14XO3cJ9CF8rNNPHuD6wHdeDLtTSL756gucpONrrg97BOXU9/uKLMVUIxdjG0t5RWDnsfNfiN0YdI9b3eewe26U9fWdS3HDyZdNj3NoqqAZugLxnQ/xe6hvVmDfDDjmj3agFf07opdDnwMb125uf3dsH5CebYVt4LsZgoCdr1sjnAN9AW4spxskxXPf74bvvKeXWbSsHw+Oo5sdBKgwYI1AwOuFflPMa5d9Fk0JyXWI72a1L1PZF/TE/vUFfX3zxD71ZVGjX313XeX62re+raP1PavjxfZfXGOVdtAzFd92QlP4py96KziiE49ptz9T9NPZwxls1c1W9pUhTa6F0Ay7/1Hh4Dya7Af8rqTLgh396w/Nf/t8UKQbSkSUfcqheXtHcwODm0QlRoKSW3dGo7cHF2wFXVSkorOghL9CkzwtW/kClL5puk86UcOTSZWDy2FPtoQviwaBG33XvaPqiwcd4v/pxAdtk/VMAy0lARfjku1ZNah4Ct+FMircvpEpG3iCnghiImPE5WveB75sRl/guLAQFMMopj6+Y8G3fIe3PNaWBwkI6uwYbD+9nHL8uk389M0GNyCSLtMTlZBU2wxSbSNfmQass28gI1SmsZ1880P3DRgsaw9P83ZU8O2/vixVtZ46GOMG7KQy7VZM9ef1NtABDJ2Vowcz0MFbKd+6MqvLt+xPX8DX3Y+p+MpSUenR7tF83JfZ4rtJgIq5uw4I7ruZ/AgkVQ/OZW4IC5V3NyCEir6u1LtlUWdSut184FyB/g1T9RVaFtIN4oFjuqXqr1QGZVq3ZbWZk/uJfZwqsAXIFM+LRmvfsHWt+WFD2LTd99tRVDrQgqAJshlRdt3jFH2jYgRn97cDgVLcPKoS3UwV95z6lC0DLlxr+I5zX7NpSHXDEAO0+Ppa9QXwIacKym7yeQnnzYM8TfaFBO/d/ayD+id1OMv879cfRs9wznCCntG52XeeRRCxpnNM7Nf4IPPXM4YlfIeGYxM3OF01gnOEHkU98ZwW/w3V89XHnr5O8wVicW3hO+/jprbvBiSuUfTvksA83PMZyoTvukQH3y849FJ7jXBLz4eiKca8c8WXNlj1lzOfjaYk7qvYeSyYhlYD+nyOQH2/o66zN6V8AUNfU2m3j21AJq+vD2RfFwHYHr5ygGVxr4nkmJKANUYex/rixoU0sUbfoXg+ZeDy2M3lsuYLTPuCwODu4x7tTotlwAv3s75sYU2+H+uPQXlcss+LWs8hot1TOQQ9iagkyUXo1h3RQEbBBZtbYcmULwvD17zdV8mpbHwXdj6+4IRv2pEtk4N7vixFXMD7gnu+4BEqJboSU9DFYlmRwYxQ8dR0sAIVAVS4G9VKbmJfz9O8HeXYV9HyZaJAdc+2TRdwyBSWu71qYgZYLzQ781UQsczuhb/0aSWVcre8SGBMV5ZQHqWS4waodFnFKPsYrArcwOEdvR41V3a91VvZFKkqCr6mzoAy5wswS7DLV+E7Yb8zzNXdbkvanxho68Ye99vHbpAfZUWvp26+6W4/+U53O+lsWR0I0INv6UCmzvrU75e+KnUWr15eCejoTFsEbLAOnZrHR9pNx5dJVVQ6ELF64yo7AIvLl8mLirmbLYdsJDewgTLjC3xjm7jTkTmF85aUZfc8K/2l4rt93XyUNzT1RNDE10UAMjRPObB39AwB5vA9n3z/YayZupuxraG8yW/sF7mfmM9XTLOZpU3rlNx20NtbHx9uE9xVG1fa49E9V8j+dK8h0GwcwW8XyoYvo9stQ8KXcQidmh+VdE4AX3YdMjlxrPky7JCV7FtOOefKZ9z109vBveZxf4Nk/r5MU9zscX+b0HwbwWTfTRL5zdLrKf0X4/ypz0v6XJvqphDeI8eeXsdUmZ6+8z5aD2FgHRfOd+hWwIVt7p6Lq+3h735A/2Y1q9fa7kfdbczjU++x/bgmzE8HPaPzmGQi6yAq+t3uc8SV9oacvhaRmwo6KzG+3RPPT4AgXXVPmfZdo6Gc+9YT0915yzLLsbhi3ffB+bbiVcPd5e531LXm1pPigWlNbmIKDODk0scpgrsPnP5M9MxPbvSnSq6QY8IXmCYiSoVBT6JKTi5C4wMZ4aI/+cI0E76gmq8pe6o+yyoTX3M/H98dbrcCCb7MFl9ACKNZpsqi0HDhiYqgvgBNVZEsa5Kt5WaE6cFmZFl9F66+AXhQIfBtF982BF9XDDU9lRI46+A+sQvpgqASv4ezzGs2r7Kd/fvWBdvArTjKIAhSSXKPR2my6ZYtaZrnTtdlQG8jt0lkrFLl2Wao5KE5dqquKXxBLcD6uZU69CGHTGPwBZqlwuqeJ5C1JSOv6swjBL+Q8aPpDC93wCIJHriZZzrgpCv5esAdHURooLKldOVNghw6qKErzBKE1fM6pnUPuw66CWY6boUNWY96VNrCkP0ucGy4FXTfbwIGN3P7MsT73MA4jmVf4BvbxO3nFu9F0EXKrNv8EeUGmUyTr1ma1FSyIrBBz2MQ9EweWAgZ1/pYkOb5H3/3gdm2a7t97MuoEzguZeTcf069zzw86Xb7eC/PwFlFVVcdAzpw5AYn5PcmOePRf+z6BqsBTPf9LqW6BknVvQYGQ/Jl2eLYd88/A48bZPt4rOm9qYigffJ3yzzkXzSP13Rgxl0fOVbRvzXKbiyA6qzLZcf80Qbd3AGWZHl8XaHIcarPJUe2Ot4GA9H3dXLgOjxmEjI9nflKSwC9Tvp8gPMtAvvHt/2lN7iJ9ffta7Qs8E3HzUA38I39qfcbzm34zrMPDptvh8J10fPU5wSZrsuFu//weyivI0td6N/Qv5zxnD03P3LWyNj8/3zKE3Z5jgi2s3sewnWw+5sDbjkEGyD2lGnsczezNCc6JvR+8V1Lljd3EMZUrUDAvfGq94HQSRJ7121hbwCkI7/jqb4Xg77h+NAjzhMRFST57FRq5puHe/Q3D88J/3UHMYr/jTCzo08QUcHk4k/67MSFoq8SkglfRaWq586tN+DkuSCsyHxZF60atIsexbkZDOANhHqCqL5AQapRwN0gRdu9OpjfHntzLEAGqZoMljVpFuwGB3VgUQIn7rZC81X0d+kKKxrJ28W3nxA8cCutUN35PMokMqtQEdVNmdNBf4a+yjfWx3dTAPvSDfzIvvRl14EM5OQ2m6xTPQx6uuVDB470NnKbt0tmj6/MIpPo0qOvN9U8x/hBex9u9m2QOHgNoPn6Q2ePSNqH1wXTpcKhK+tC3u+O6qyXHdtYgrO+0Z91JpMbbJbt5lZMdVa0DsbqrB99ntJ9euptJu/R0/RNCSkH+jvk3Okb7MrHDXra80IG520E8F1u5RDbVgLvAmXDPUd33Ltz0jSc291yjv3mllVA0N0t47JPZWAzX5PSyuAXTr908H+H/y4hQCLN29ds/tFs3LbBPvZlK2oInLl82fBFlXjcxMuFPlecckBvc+cvH7OP3XONlAf35ogtU57ffZRZX3AibH6eWLYg1Y0pLKubfQnVgvLlBhHRRQz4fl9RVn3XPzKQkwSZcLxpOviUvE3C1/ZvmjjKtXtzSTK/3UBWPNgVP18g+I3ADf5Ad3lz8wl/sUE6NxNXbwe3VYUmTeVb1G1t/wUdaEN/mAjsH9Xq+KRzK2D7+fYpAly+bYvt5Z7DcQ7R10RyM2Hrjm3RlKBMRftRn2dxU1jIdD0up7tvQH5v9e+2niduTmA7vzZ/ZKzPXiwvlgc3fjQEQtG9gW+76HkKXAP5rlHwe3CA082CrK8OlvvWp7y5XS34jjMRDmQU30G+Y1h/3r1G88G+Qvm/vvvgaEoiZHrj9WuOuyOaQkRUsMRf/VKFvjyHmRs6hf8mD2Ikf31NvNc6IiqIVE627gwHMsKdZ1+mQyZ8wUxf/5PeJu+ei+FM+bLFiirTebkVKZAMCc1X0fPd2ffdsfdnz+XYipnLHZRGKmU6460427gkSRNVtxxIs26Qi193mfdtsJ9p2SC583lsZze4BvtGI3VqGLndX1arxipAgEFCVm38wTaXk4pnQRBMRV+HLlTifZmoCHL5poNUhnRFDqSvL2mGKGQ0bDe4rQNHujKW3GdYWGbcSjDIMrqVfbjz5H+Y0w68KHoWh2wiVPTcsq0rav5Mz/C7kgJvTlmQfeU7xurr5pvO+kgF3q2EJvTpqSrcWF7JPNRBBAmEu+sggSudDa6bn8p21iPXyjpkWol1K9WoJPsqjC7dx59wv9MGqJwuSHw3WzDNLYOYpo+tZ341zg7co89lGBEXFVNkQ+rg7Yv9PjZPXfiGDQxKNpzOUq5McIPg6KifPXHw3okBEhnI7eetG8zXefPsY8mATgV9abrO6vir6FHxYd9LGdcBaX0MIdCDgBe4NzolKKkD8FJufN3ahBmgicc1YKRqXxBfB980HA++m674rXSPcymzboYj4Bzj+32VY1nOC+7vh755I4NDCvldcge9cYO6Eox1p0uTXx3s6rj3YTZwM/jUf9rn+kaDbvKs96E+prEN5Pzvnp/l2Nu3Yby7Ir3/dcsE328u9kP1qsnbEDc5fNd+KB/uPkKZ0MFAGZysujoHyX7U5yVf4FJnD8p5SJ+2ZN/q3za9XvI9OqtVB1JlO+JmAAKhyCLWXRPhXIeBvHw3nasF+8TN+D123xPMES27Jl377BF9qbQgcG90VxRulxLpmpHLtkN3IAgYS9nT9L7ATYyCYB7Y5ugLl4iopMR/IYioUpKKxZbtYaYn7jwXNTjmzWTzzMufHZBcYclUJpX9TGW6HL4+rnwjkPuy0PxN2RMrOnC0Z0ARG5DwfD45gBWuh2RP+gJZ5UWyTNwgin4uQSC3rCD46FYKwVaenXIw+ZrvTZeo/0oN5dRbQQ62q9vkWY4FX19TPig/voAq1sfXFyvWOVWlQPaZG2iUpok1ouwjUTfqx9KtiKbK9HQrJ1JmfIEFyYp09xkgq8d3zpD3JjXXVBVfN7Ot75EDzelRANWtILtZphLo1xVdofv0dPmCOoD5oPkp/twy9t7vvrLT9aAxGHAB0yZcuTCaEtozWi5dadfHp5wnddM+Cei6GVLIzDx5/3OjZ3FuoAIVe1//bsnBhORzh3veRjlwA1EI5ie/L/hOZ58gWKWPLXldl4HLu9wUy0RLyOJR56g60f5zz2uVybXH32lvmAh3oB0EN6Q8LVu7yP7bdq/97b+pdGqGvh3Dsomgx3FtenoDocUh5wW9H/XvnT7W3TIhgfwcdeNH5uP73a8WnBfdADvgfOv7LXYDRAK/s77zrj2nO83Y5Xzh+x3BOcY3H8mylBscx7fpFaxjHJZVjjX036zJdPc3x10XubmVdL6Mqlo6yOXehNPnZX1M6uPfna9cr7jTe3e61AagugTlU5NjsV0UrAfftQx+6/R1Dwbcwfxa1G/j//0I9r/8ZuGcivee1bFPwrlB6JsnkjWtz5m6n3A5vydMiy1vfO/J76j+jde/ofL7q4O9OmNb3otm00LO59hmONc9fNbIhOXECPpYz18dfkVwLk9cz2779jSHteiSdG6V5+jKBPP829mj7POKpra60Q6+m/wC5wtsyca1m9mAsY/81h3Y9FBz4WED7GMiorKWfIVNRJUKmuPCtijT0zbvci62MuX7nC8Y4mZsgVt5KoziBExdvsxUH1+TJN8gFL5l8wUgfdPQUb8LFTZ/Jkri8kjFTprsuoGw8tRlnx72gv+EdmH2htDbKlUQSCqGusI68PhBdoAeN6CDSoIbdEcA6eaT/hJcaCf/fOG97vdJIFW2Z0EQ0HMzMwHHhi/gFFbK4/tTV1KlDze33ysJZLdpmNidgmQ3op8xTYIYyM7Sy+CWYdn+vkGepM813/GMyqWvnEvQMinTU83DDcohECbBIbeC7HaHIEEAN9sQ5CaCL7DRY78zbKWx/1HXRlNKVrc2Pe380b2E0EEjORb1MSvbT68zmuFdfsyNdmAnl3s8o2Kvg8mAbCG33LrbG9x54TNJAc5g/u6xpM9FmC+Cbwik6DIi36+zrfQySb+K+Cz6vBRSlitrpiegH0IZ7OrUAy8wzeomj7Kumxm3brCfbcabDravlHv0NXv6QckZ1sUlTdz1b6G+EaLLgXvcS5mWm1YgZcn3ux9meiaWNUBQT65NNN/gP4CAqls+ETjC8tSslljmpfz5AqhYRneQsNMOvND8s/fL9rEcP03q4ByVeN6RbbSvM6Cj3NRyA5Xuusj1kz4vI/B4SlB2QE93t7sch8ii1ucVff5zMwPvPfXf9jzlBpIkY1EH7EHmtY9qPaFv2GE0dMwPgTwd6Oq6b89gfn+02Xypbo5JGUHgEN99xkG/Slh2CTLqG1WyH/W20OcWX7+avhuJ0i+xnqZ/s+T3PLw2CB/r98r21suBkfNBB0d1OUcmItYT3fW4XRvJceLexNXBeHz+0BaZDXpX1tybqemapDeq3cxerxycpn9NuWapV3Mv27UCEVF5iJ/1S1VB/XjqP/bpSVQYUjmRPj1R0fD1d5gJ7yiszoUb+IJ27kV8Ybh3xDPlC4j4ls2FjtR132fwu663eC/ofcvm+w430/PJ3q+Y/T39tyF44PseNzAm210CZL5gR3lBH5C44Ee2gyZBMlSaXrl0pn2sg3Tjf/e1OfeQvvaxzkSTYI8bsEQwzM20lf3hy4pDxSsp+y8ql+5+RIBm3OVhk1QN+8a3zzHdF/TEd+rl1hUjqTg2DSoGGrK7EOw43uk3UJpP6kAKSODIDTrId6FbBlRWD2vexT6v6cmwkWVMdTz7AhoSTHMD+nodJStS+Jo/AoJi+zc5JHoWimekJX9Gtp27PwGVb1QaEWQqDdj+mL+MtA06Y1GCF7qyGy/D8W2D4wRZPb51cLODbbcKznn7sBbHJFTOwQ1wYt83d26u2AC9c9wg+0h/FoGgtnsdGKvU4/fjX71ftcusM/rkWND7XAfT5DhGxo8+H9eLbtboLgAqI1Tmw/4I/5AQjBIN1KBs67eujh6lJxlnbRrtb05of6Z9XJKk/059HaCD9jogrs9d957+VFDuj7OP9e+5PHaDOGDPi57ffnzGd02wp3MuETjHuDc/Hj5rhP1XNzUGOSakn07RPTifnnLA+UGZja8f6HIvZdS3zPIb654jJbvQvT5yMyxNbLniy9ur/VmxTG/dXYb7HXIcuce7brlQ27lGwLGK81SmgSQpA/oGhnZQ00PD+dVvm3Aeb2Ozl8N9owNg0i8p1lcCzXoP6u0gNyn1dYze3/Ibo7dLjWjd9XlRPq+DkXJcJrxPbTdZX30e1ts5KKnRozj5rdfLqM+B+vvd4LuULfc31ffbWxG5fXrqALgLNzmHnv+CueGE+6MpyaRLlqpO2SYiKktldAYqqB9P/cc+PYkKQwJoW7eHo7fjwtwXwMiE73O6giR87/MF8jLlVvZ9fJmUvmZsvuVIzpB4Kqnvtf5HXZdwgQv4Tt93+C4C3b72ECzwbpNgXX1NypJGdo22CSo0aIL7xoCKfztI1ldf3OusG13R0EEZyfDRwRaZl9ukUirSvsA+MgndYI9vmSDcP77ygwwlz7yD+biD1CDj9LQDLnCWOz5PCYRjtFztqNa/MHf0etScc/CvoykhCcS7FfJYcFB3RBaQABvKIyqfMiq+23UDmpzf0vMh+9hXJhEU8A1wJNvSPfZ0kFJXbMHN5pTPXnLElTZQrknQwj3uQD7nO/+UBz0QjGQW6fNKrFyq7Stlzpcpk3y+SGxWDiijbll0t0ej2nub5m4GYvCZanvE54XBc1rVbxPMK75sZx18sc0q0+dyCbzI/kXwA5Va0DcCEoMVYVlzy5U0kXSzoCobDOb1wBnP2G3h03DP+O+Ie7MiFQniFTToUVHVjm6U6bJSU92c0MEb2f843tCXoZxD9DEuj33nDpy73XMuoF9k3/urpykP+Iwmvxfu+UeOCTfoiOB0zw5nJ/1mIBNfSqw0c0+XherefJPfe3d9JKCPGxv4jb7g0N/Y5wn936p+CRFIk2XXxxPIb4u7DXTAVw+EVBQ1q4fzSjpWo5tq++4VzwzV21AH+HRT81uD3xTcbMMNodgNQU8gE+ScoW8e6XOPfJ9uwRHrv1N9vzSZ1+dK+R3R2y4xuBrOM1XAVR77rk/0+/Tvvz4v65YVh7Y4xjStE56P9TbE77N0X1PR1XWy890bbYUlwWZddoiIylr8rE1ElZJc/GHQFkAFxRfMyYQvS8tXcfEFSHyDHGQqflmZmm85vM2QPeuODvg1BDL1+2IVEeeUiO+s4lk632AibgftuFh2K0+A7/IFctzAbKZ9UFYkUpnWlQed0aYrCropo1R+9HaRSqG7rWS6npdAJoFb4ZUyrZcJsH90hUZgvu68Lzj0Uts/q1sGD2p6uG3KrddR928nAUE36JnKlV1vtZVn3awaYpmeToVYMu7ccusG5RG4OqBJOPKwW4G59Ojf24qouy3+fcHrdvAGSG7eHl8ON+jpzl/2n29/SaDfd2zXq9nQVhSRkVgR6IHGZHvoGxV6HeLNYcNtqs8N2KboGqKt028gMpfdSiEq++5+cbcvtqtbtnFu0+fjG3rca5tp624YpFKvA+myr+TY1N+t97n0UQgS5HaX8/xD+tmAyAWdLoumZCcd6GxQMxzNvSCSGYasdx3QKSky8JnObNPHqQ4YxbLSnDKkA6PSHFpPEygz7jkX8Bvru55wb8ho7m+3LJPbN2RsunNekvVyb8rqDP0e+51uj79Dmyc3LZabGe56xjNAE+cbb92RuA9rqOb47rEpv/Optre7DfQ+dJsdF5Zse/c7Yq0uwoHNrYSgZ8J5IL4NEgOU4XLq9dLN+d3zA+jrN2nKrm8yy7rrefqCkZJBmzA/tc/lGPM1lwcpL/q4kM/r4zNhnuq79OOHzhxh+/oF2fcN9mxkHjv/hdgNpIrO7ZIkXaZnJprWaWF/y92WHkREZSnxV7cMrRo3xGnWPsS8nBe9SEQZczMRcfHsXpxnyl+pSZ6XW6kANzstFTdrA9yKgY9vOXz9d/qyTi5zMsxwcawvpOX73SAq3uNmZIDO5BBugBOf9W1PfIdvug6gINAzsNug6FnlIZVcvW11c1+9fWUbIPvvvKjJu65USCZnqoqwL2sA5VJnc0CsQuPOB6Ebz77FseNOR5YXghtuX3FS2UkIeqomkJd0vtIGftyMzsKSSogbHIwtj9re4AYJdPDBDbJglGpwj2ldkdPHLLJGG6hAj9uvqBvoSNVsEyQA4O4bkCb71xx3RzSlfOmKoG/03cQKc1i2JUitz0nd9u1ly/y+ql89QABhD8/5JykI4pxnsF111vPFR15qurf9ZUJZkcd6mmSbS8bbmR0vju1neZ8+jvUxoW/IyCAe+r2AAX5Q0cUgKNmsYa14oFM/Tue2Xo/Ysi03IkqaNE/VN4x003w9XX5X3XOe7E+cu6Q/TF8QE2XFd71hu2tQ5U34fv8l+y/VOcjtOkaCbe4NF7kWcpdT/zZ32/cke/yhv13XiIsn2JtOGHBFk9+lpObtseMlcdulCjCDBOjcz8SCbM5xpLeXe2O0sGRkfn0eANk++hhOvFkZPy+5I8gLWX4554HuR1b/7sj2SQi+yzlTzTM+Unt8mmwPXV4lY1pvO1+mZ7Wq8YxfvTy+ViO+/eG7toTE86Seb7idffOvyKRLJcANAve3qrDQNzXOd1d0+VM0hYio7JXLmRgBz54PtDHDdbP2F7uaCRf0Nw/Pid5ERBlxL6gQfPQFETLhVlzBF8z0ZXZk+p1u5QoXp+6Fvo+uqAlf35pVnQzJf13wmtmyPcyCFbgw1xfn8v3u+tsKnWe93EoD6IwMwDx9fcDhO3zLrfv0bFFvH7OfahZXWfiCJTqQqTNDJBNNv1cHg3UwX1fApLwHpdz+q2G7umUz9n61zzDAwyVHXu3dtwgC6EoWyDK6fXpKhqUOhupyimApAj9o/lgcvTqcYyvjaGaryfZy18MNEugm5+7xLBVPt3Ku94vO9Oh31LWxUYPBLeN7OIEBXyBcxDI9PUGTikjWVffh5quEx4Ke0bZO6M5CyqOzvfHc3Q44h+hjBpLOM/hPbdu2jdqZxrX3jgWbQcpJYrArXCZZDl3m44EwVYFXZVwP6BLP8Epcn91F+0YH2X0AGJgoEwh24rygm/qWpP5HDbRBBpw3hO7/Up8PYuds53dTzimpyoBAWdHnbfxu4Vxl+4b1nF8l8CZw/Dx81n/t46Sgf7Rd3YxOKe86qAZyjnGXM/wNSpx3YSADFMvZuHZ8dG+Q86x7btNN0t3jV0Yad4+XVAEyfYNVd7FRFB2jPkAb1WoaTQnJOUWfRyAWiFTroM8r+pzhO7/r82RC4DLKqNX7yXfzRD6vg+Gx840qr75MRH2elDJcXWV6Jix7VE51IFTO4b5poJcz8dyopkff4SYmVHRSzrCOuEHAwYeIKBsk/0qVuvlm2AMLTb+hTt+dTXqZoUN7meEjxptV0SQiKpibHYFgkq9ykglfxdWXreHP7EieprMUhBugwYWVL/jk8q2Tb1qvDudFj0K4aHXnj+f64lQqV272IAICbkUAdCUPcCHu9tvnqwQAroP1xXOrBvvYCmrbvfaPrU+qz1Z0sUqS2o46O0JvS8ma0ZWPVH1m6e0l+9LdpxiICn0X6qwebM+Rl3xgH+vKCwao6XPElQmVOYEy4FY8ZVmSg9XhPHUFy5d9XFokiKUDE6D7fkOTMl3JdZdPypx7/OpjSx/H7jlCmoQKdxvFmz8nb+s9o+xJXxZ3RXTZMX+0mS8NohG9Qba1DibLNpBypAP/UpbcijC2jzsN73XPBeE+j+9v7Hu9T2S/6YwsOSb0cVQ9Ov70fhYyTR8z4Dv2ZJ5uEHd3cagdYTwMZDWolVnQs7T5brboQXR09zTxc0ji/pObgrr8ub97gLKig+n6nOoL9rgDW+F7ZRRrtwzJOSNpdOzofBcGHdWxEH23DsyBXr6iuL77YPP+VYvsIEmaZCwGVxj2X6FHm3eDyfGm2InT5ThytxmONTkei5vpid9IXGsg807zZXqCnMcSb4iofa3LRqwcxafp0e31b5QELvW5J3btoMpAPHs0+bP6fXIzSi9nuE7hcylHenAjfW6TG3P6N0oC7Ynro37r1HT9vYnB1OT5VgZ1a9Y3wy5+z7x9xZfRFCKiyq+czsTtTTtf4kuz5qb79FyzPHpKRAVLak4aXGj5gjkuX0DSreSCr0lbqsqPy72IBjdY5bsg9C2brrALN8Ay6OS/2yw+DRehOjMJsFy6iaYsk7u8mO5bB3eb4D1un56pggBYXx0IRF+BkvUj6+OrLFYGss56m+kmgXp6LLCi36sqJTqrUe972ZfuPm3fuGO4DdV79T5IeBx9t287I1PYLaNSwXHLvSy7DrRKMKks+DLyQCqGcP0vBptOzY+KnsU/A3p/JGV6qTKuM5fc46Gmyj46rEUXU79mPCAI1TwZvUIyPX03ViqiSzpfbTNfdIaeNK3U6xDv0zNcZx0Ulz2l+8UEG9xwyjTOUUn71jnPBF+S8LnOrcIAkpyPkFUoTcx1wEKWUT6rz/3xYzPxu+WY01nykolWWfZhSUPgCzesQHf7UNHoQZP0zT3Z/+41Q+w8oY5b77VA8D59TtFlxvd+9yaJPi+453Q5ftzrATkm3Obt8n53lHkd7CpJ8v3uuS0x2Jf4mgTo3CCz/Ia4maEw+ZrvbfZsnRqJfS2WlFhw0Tne5fdO/0brAaD0+5N+MwN6/+jziwQU9Tkwvv7x9/kCnHL+0+e8WPBZLacuM1ImdZ+eCa1OovKr95UEzvPzVUenAVlPfWNUL0vCPKL56tcrC9xALq3yRkRUHuJn5zLTxLTrstAsWhk9JaJicSubyKzyBSBdvuwH34Wrb16+ab5m8PoiVLgZZbhIdCsGvgCnm00B7rojE8KdF9ZJlg2vI1MLQQv9PllO97O42HYvWHGB7Qt+uf09yTx1dgZGUO64d+fg8/H3PnTu49GjYL2jypmbaVhZxCoPujKj1lVXiGNZJKqSo8tkp2ZHRo/i7wUpo0kVtKhcJTSFV+VDlxVZPnd/g63UJZWD8LleF5BF0AHv4gzoVViyPG6wQDcH1QEqSKi0qmPWrYTrc4EemMPd7jpz65GzR9osWi2WwaP2s5Bjw+2SojKRzC0d4Ik1HY+2oW5WKWVJb3t0GXB2xz4J0wDnEPcc6p63MT+ZJwIqh7cKA9zStLJujfqxoIA+78hjWcaE4zBal6TvjsqIXs7YaMqVeB8WV5/OV9nflcObHxNNqXh0tmTCvo6OT3f/yflSnyPd91zS+Rrz++5/Dqarc0rC++PThQ4Igv5N0OcckOWUfoORwYpMxb3rhqNj49iTT//jvDGmc5TBqMv5gU0PM83rlk6/svFm2onbRZ9/3d/yeL+aiesaOx6d83BZkOCie7zHRpRX06vtEV8+PV0e62nIGIxR0yUIrX+T5bdMb5dYgFNPiwVC4/OT8pG4nPEyIKpXjZ87dbmTG376t00+n3SdEZ1/9XfpAKp+t1zrujcUiIio7JXDmbiJOa+vvxn7qpnTzOSLj0xs9k5EabnBRlRMcfHfJRpBMhU3cAdu4AN8FRffhbmvWa97YQ/6YhFw8eleFPqWwx1EANx1x7yT54/AQXgpiswyZGqBr6KmA0TIFMAIr4mXvOH83PXC5xP67QvId+oK0TmHXGJuPemh2HLjAr5TiyPsY4g1M/Ns88pAgtW68uSrfIBkQfgqGqAr5rpsyT5y97PsE53lqPeT3g9637v7Esu1hxMolCXxBZz+n703AbSruuv9150T5iEBQhhCCJTSQqC0pROlbdKBVm1psUrbhKqtD5Wq70EB+8Sm4gAU9GlR0T6fJfH90Vcq2omqwQ5YKz6rjfjQUhpmaJvQkSl3/J+1z97n7HP250vuyj3n3HNvvh+ld9+dc/fZe+21fuv3+67fWitS7OYf29wvrbsuO+4FjXVR29pPea279nIqt5lyOy4Ly3G6acvGPWURta1tlt91OZAseKaNjBpZkiXBcKFRrJXYGsDndTt/L2XbUNTrcnlsPOM94Yef87ZG3S7ILGNe3qce+cJMWIvrqbaWcu23xrWa/9JoM+X2VXp3haBRtIXyu2ssSdD2TcVny0JOQ6xoq4N7E69c84asX+nndZjLWXdlm1DUk/aBk+H8M/TZgsJ2l+t3XDeygPr/8n1Eytev2pb69QthPYqccVZEkcVXXssxnj+wtOxEcd331exxfD/doHiWdhvbks3a9m/FIFH73xTrQtPsg27TGJhqrwP5e22pA6VBvZmSfWj8bcmOTE1P5kfNdxkpssPLGcKFHSnbxaIvL/fXhRBarq/N7Nnm35azOovzLZmeLc9UtYGNzNPS5yJNMbR5vjxLoiyAFudp00VjjDG9pdWa94S7wnUXbQ3hji1hXcvu7ReEdVfdE8JNV5fObQnb8r8yxjDtAmR0YGOA/KJjXpmfYdqngEXaMxYiJHC272IdKTuwBeV15QrKDmwkcx7bHMv2bNBI+99FZpM1mp3LTV2LKJPPWlp96Inhb376P+u/lIibFnzgtb/f+jc1Yhm1P1f8TLu4V9xHawZo/VqFQ93+TI2sGyjzhUBcWiAGnysPbC58X5RD+7M2g+1yoNEs15astFIZFkFhObiIFNcvC5Pl7yyLMuW62h7sx+CN6lCkPQO5uPPiWeLame07/3aTIuum/X7LU/sq09ZLz14OcsvB3dWv/5OW9QBbpwq2Xq/8/trvI1K0lfa/i6w48JisvnRrF+teUEx1Lw/KNAcv6uXbbhsirXWwXoY0mFL822lHnpkJaweMNTeRikTbVvwdt6XmuaJNvf7Zb21kYRX1ofmpeG9crxoZUaX7bGSJtd276T+KulruNws/oL3uFe+zXAeov63/rJ+P4tMlr7gqO44U7eC5RzwvE+xj/S0PSrVT7vfK30ub3ESUkBspbHK7kNdJjj34hCzz9JfX/4/8TJ3Wjfdav78plLaeL/p+spPdpngn7WVY9Lvley3brcKvijSfs2lJWvqXmeb5QrjETM+W917vd8rf0xRCm+eKQb6yIBuzSYtvLH62zsho/n3xTOWyV++DxNDytVrO59dtz/Y1xhjTe+bBEp8cLi7v2v6M/7VtdmSMqdCeGUDTbwjKJqSpuWWRpID+ls61bygQqQRXtftszyiiv2tfH+zHT//p8Ovn/GH+W4m2584kz/xcq0NaddLL/974m7Z7i458effbSLxWVfSs/13repT1azbLtHW9qEb2VVtAtFB443PekQWBrz7xTfmZSL0cymUbaa572HzWchmWs0RGS2VYBBDt9aio9+U1D8vCflmUKbeZdvE/ln17myrqQBHwFBR1pBk0tv5dt2lk4rTdb2tmZmu5l+tWuQzLx+3vqsi0irRfryw8kKDRzCJq/bvI8496WVZf3nXmJfmZhUfxzsuBedHmi7rakumZ24VWsb1+rr3+xDJrL7f2rNh4veJ7yp9tbiBVv3akEcjn3xcp7rvlc/l3tH93IXqV61CRbbdQbdbeRGEXyn3SUJGV3PauG0IQ1OvIpa+8OvzwyW/Ljovz7fW3qFNRYI+CZxQ+27PluU9uPa92SS+mNROFcF+u150mirxx0CZOoW+Hsg8jha1uby9N0a9796soNmFsv9exfDp4eWmSsu9T/nzx7srnMtsE559z+POycjtk6aH5meY9lN97MQ2+LHA2RM9SvazXgzjw3CzTcl9U1OGWjRJLda3wXct1XQnt1J+1P3OBsqPGGGN6jy2xMQuc8sh7pBmsPLPzXA5gCihAoGnl5Yy8AlqXrz3AiZSz9o46aHW45g0fqTmFrd9L4lHhbBYcc9Dx4fu7vpf/Vic6zGXxIRLFhaaD2/yewrkuP3P5PgpHu71M4r21Z7pGZ7lwkgsKUaNcBsW9FeVXdvAjReBDAvJCpQh6KuIi1pdSUFL6fFGGMUMt7kAbaQ8kiu8p76JfLscWsa903C4YRkG6/drF7+Xs00hRNxrvre3vus0BY4dkwePJbUF3bCvNYLP6fAUtx6V7b3+OYiMjer6p6an8KITJqYn8qEnxHb0um15R2NGyfWrfyKhcb4pzLXWwOJeXVdxEIm5c8qNrf6oW3Bfvsf6ZcmAfp7o/67BTGra+eOeRxrqcJZvXGDgq2bSGOFT+XPYs8ffmuUhTXG1+T9HeynXJ9CdLR+tCXFkQavoLzXcaKd5nuY8qv+Mo9DU3yKqfb88qb4hJpSy/ch2NlPvr4vqrD31W+MkX/NfsONLMNG+tj0Xdbb9mpLhW2a71kmIwoN3uqT6+6OPmY/CgyIBsv9eiPy7XgfLgc/nzhb/T5i6VMiOb//Cel70/G+x6dmkphMYAXtm2NOxo6VxeruVBzEJgLg/al2cnFLa5PCDa6g/U7638PMXftJdJc53T5t+3DKiWPh+Xu4h2/H+//XO1mttWMMYYY3pKqzXvFTu2hotK09pb//OUdmNSaA82G7+3OWvtUFYnOWa03l77d0boemURq6D8DfuO7JvtLN3uWLKo2nou/k27oBOvXhVQ47n658qOdxEMlUf3y/fROG67t3i+PTCJ58pZipEimGsV8urnis+2l3dxvrzu4kKHliqIFO+pHPSWM3zL74rKi8TKSHkKW3lAoEUQEkFKJP5N+7ki6Kss69A4X6+bvQ6w49IMMXi8+BW/mZ9p0phi3f58pTIu1+PW6cmt9bIIIMvBb0Es17NWvza89bR3NQLFMk2Bv/Wai4Xi+crlTAF8+27p5YyuomywjBrXLT7TvOZJh50Wfv5lmxrfXV4TtHgX5UtSOyree4ttzOt5e90phJry+bhBzrJ9Dw/HHnx8fsb0K1e+9obMXqw8aFV+plZP8n67XTgs6mlLvSjZ8vL5om9u74+LgdB2u1EWXcv9dfG51YecFN75wl/MjiOF/an0i/nn2S7V75UE0V5w/Zs+Gm5917+Hw/Y7Mj9TRy0ncPqRL87ezc+85JfzM72jMYOhrRwb9qJ0XvXRxSBmu/9VbM7YbkvaIeH16AOPywb14jICBY1BllJdW1KrH/Fby/0P+Z5lIbRcV4t6Xf7uRv9Z8k8i9Dzta4AbY4zpP565F+oKO8ItH9wSwuXXwnR2T2k3JpV2Aa65ltUzN+9yxlBB/JuyY/jr53w4vP6kt+a/NSFxoywwFZB4V3b2C6e57DxHSFRtv34M3NufMTqoLHrWP1f+fOHItwgApX9vCKWlf4/Ea5Qztw5cenD4zdf/ce3+WsukeKZypmxxrplBkf1oUFxjpm3a+0KmeN/tgWkhkpfrQ3NzldZ60wi0SgXWLjAWGSjlYKfcNspBTksGaOm7fvdNfxb++/rfbvlspKg3RRBYUGQwNafhtf7dfFLUtYoQkZf7YfsfGd7+vJ/NjiOtGTytFbORXdNW5gVXv+FPwi+e9av5b600127lv13ovPrEc7Npuycse25+JtbjelmW618xxb1hd0p1pSjt4vNlO9Vuu8oDQqevfFH2s37N5lTSSDPTs8kz2dXypiRNEaC1HhSfLWdpv6b2/B//yX8NP/2iy/Izpl+JmXVRRCovSVHUmXZ7WtiNch0oC17lOlqcb/dFGgOmFXtSbwsnH356OP95P1OrefV/b6/rTVr/vqD4vvLAZUFjgKpyrd4Qs/zKGysVnH/6f8nEzbOOe01+pk70I+K7iVmuvabo19rLnQbOyjakbNMLcbCc1RtpTinnd1jQHDhs1qF1J74xK6vzTn1nfqboi1ptXRRFf/KF/y38zIt/KT8TBc6qfxGhAZ3iXNnXa/T1bWXSEPhLny2+o1pvjTHG9AvzYKF3hO13rAnrnr88/90YMxfaA9mR3AFrFxLbwUzP2t+URZL4OwXK7cFNBD+Hf9s81xAA2u61HFxF1h55ZnjLqT+R/9ak3cmMAVq7uBId0uJzZae2eM7yd7c4wvm/t4utMcOwPFU7BgYxW7VdECuuVRZDi3NNwbn9uQuHuvUZFjJFMN3+rtqDkUgzO63134rgi95fQVHXyusnlsXN8ve3CHwtWRr167ffa1FHyn8XKT535AGrKhkp80/9ntvbVlHuh+93ZBaAF7S3+zJx+mCcpveFn70vPzN7Xv/sH81EwWMPWp2fWVwUO3eXxYpiUKRcx4vBpKLOlG1oUa8b2ectNqn1PZZt6qkrXpD9LL6n/G+NjKSSLSnWxi1fv7DH5XMNm1c6F/mx096dZRUftt/K/IxZ6DRtbuu7LupA2RaW61fZFhcDKe12s7hG+bOR4jonH742vO30C7PjSEPEbLuX4rrtfXtxfR5crd9Tu82eb+KSPrGviNnR/UIUaOM9HbbfivxMnWbGerPcy+XZInrm76D93X3ip7ZlfUd5YzxCiYztvPZZb65d7+FsQL7g8P1XZn3M848+Kz8TbV3d3pb9zUjhH5QHbhp+Qum7pRBcnC/VOeXHGmOM6R+euXfpCsvD6jPvCdu/kf9qjJkT7QJkM9PzmR2w9mzNl65al2WCtDt5vPZi699GSAhtFwIjZSGgEcy0maJ2RzXuXLz2yBfmv9WJf9v+d/GRy4FZdIT3Hzu49tn6d5YFgOZ3N8upRVQrnPjKvbVuZFQ4uuUM2UjxnS1lkH9nEUy0O8nlhfYXC0W2T/uzFu+pXN8aAUXpPUSKzKDyNcrvOVIEvuXyLq8z2yrqNY/L9bsIXsp19Ko3/K/wwmPOzo7LGb6R4n6i8BUzUkiYny+a2VfN54sUAV71fZTLp/Vv5kLMBIyiYAz29xaa7btZpo0BjaKOtdTH+rmBvC6Wy794Lw1htPSeCqjetg/WRIq+oQzZwcK+ldtmJK4x+pZTLqjYOrNwOerA4zLbFXcgx/6vVAdaBK/SZ9vtYoGyNUVWXzm7ONIU/VvrXdEftF+nfXCszJWv+8PsuQ7eZ1l+xiii8BzL6qWr1udn6hQZ62U7UPYbyz5mw561vbvZ8vLV52T+2nOPOD0/MzcKP6DdTyjOl+tOUddbfZFiun1b3W38ffOzDRu6h89ujDGm+8yDhV4eznrlmrB5y9awMz9jjNlz2tfcbKwxt5vm3R48v/S412TTzdo3I2h35I5fdlI4BAKJducywoF381zhTrYH19Wp4vFp2p6n5oxWgqPS7/F7otgSN/toOqVVJ718rvXfi79p/Y74mfJzlf+95dny8+VnKQLFYgp2e7biSLFbatt3LmSKetb+TEX5lwPfoqza6yatK1smivVLRvbLjsuiZ/nvWgWh5nFL8JPfY/mdxmy6g5fW63t7cN/P70lmXxX1vq09lUW4xVT/5oPmZhfNuj0yWA+iizre2vbrn2vYpFKbKCg+X7zXSGGvip2MW+p7/rnyNPrC7pTfdWGzyrZvuPb+42/tdcQsPtQO5IWdKNuCsuBVtpHFVPJyHYqo+qwG/QqRtN3+NM63rZ3Y/N5qPX3uEfUdwsvrh5o0rv2RPw33bvpOzTd8dX6mTqNPL9mH4h2Q7ZoNcdA9+mu0E/6ecNDSQ8ON5/9t+PhP/kt+pk4j07NkA5t1vXnvJPhGGkvwlG1oo55X66Exxpj+YF4s9LLnvzicdceWsM4bGRkzZ9p3TSfxkWgsXp9TBCAtGXG1/2vPFLvwRb8Unn9UcxpRQXvmaKQ9YzNSdiILR7n8nVFgitPF22kPbOJ1Ktms2bn658oBVeGkt2SWNK7XPFd22Juj/63fEXe5LS8NUP6bIgMqbuqy7+i+2XGrCFf/bGPH59JzR4opWeWpUwudpqjS+v6K8+X60Jx+2/pZClQixTXe96rfCsv2PSw7LmfLlsXpch0rt5GyGFrcS0sdLXWTY/l9xLXaYmbM8rbpgP1E0R5bNyhq1q324LS13Zu5cMHz35PVj5cd99r8THONuaJOlutdYauK+l22OYUNLWxLy9/lb6poL2Uxv1hLuNxminPlzzUD9uZ3FqJUuR2YvYuinpXrRbnuxX8pqNvT+HvzXKRp+1vPN9a0rnw+/862ekd9eiSu73j6ypeE04+sr21rekPTr2u+j+a7a31H88maZSfX/LD989/q/Prr/zizzSsOODo/w/W02FipUndzG9riIxT1M/tfY4wx/cg8eLTeyMiYTtJYjyincNJ2F7C2T3UsApCyEBevVRZDMmrnWoOfOu2CVGR3QmjToWxu3HPGUS8NF730ivy3OvX7aH2eeL9VQTI72/itoMjmK1+DnPSW4/w65b+JDvQpK86oBW3Nc+W/KQSNt576rrB0pC56FruNvuHZP9YQQYpsrfbMwcL5rpT5AiaWWZy2dukrrsrP1KFAo5kB2yzfSFFnKlO183IqB8mjebCy39j+4dznbsyOIy3vvlS+xc6rH3jt72drx0ZaM1ia91fccxRhYxZRP/Mb53y4Lszu27qWWlFHy88VKbfpZhsye8IxB6/J1+2rC/GRItOzsfxIqbyLutnIOm6xQ/XjsXzzGbKpND2z0WZK31OcK2c3kx08cdkpWd2JG7SZvRPKuixnGZf73sYAS8Wm5PUtr8MFo7lw1P75IpOz/fNFHW3vF2KW6vVv/mj44A9vzs+YXvD8mo+WZdHmy85Eij6z/R31G0UGcJnjDjkhO7e8tNN+Q5hve56LXvr+zDY+94jmwHzhh/T7sxtjzN7MPFhob2RkTCcpZ7NFSAgi2jNEi0CjHIxnEmIpQK5TO9t27vXPfms4ftnJ+W9NyjuXF5C4Ur7XeiDUFgzV/r09QIp/0+5kxt+Lc+XAqXDIy9cogrbWAKt53MiAKv3NsQevCb/y6t9tXdOz9DfF0gIt35O/nyMPOKYhguw/dkDmZMe1Ssv82uv+MFv0Py7Wv1iIgWmctvbKNT+Un6mz+pBnZWVQ3jyhEIHLdSTSyJZte9/NJQiyHxnF9OF9RvcPP/yct2XHkWIqZBb0HNUMeopMyPI7K9fHch1rZkeXvrBPeY6Y3lmsy1dpO+V2WS5Q0xGKjYUKu0uDRMX6suV3U9TFfWrtKFL+u/aBmfKAUiFEle130Y7KfQYJU3HA4PSVL86WBjF7J4WdKNvC8gaDZRvR+GybXaQ+NKIyPRtZ6G2fL/yN9s+b+SFOe4/C3z75kjKRuInQZ959V/iFl30gP7NwePvzfjZ7npcc+6r8TAgH1Wxf7D+fc/jz8jN14oZ18Xzcbb8g2svot/3dz3w9P2OMMabfaHozPcMbGRnTSdozNkl4I2hTi0g5SI7XqAYgMdOz1XRE0XPNoSflvzWhHeLLmalFEFMO8uNxuyATE0Hbz2W/l85FhzsG6sX9lu96aLD4nuZZFkKb12sGYM1zxWdbdzBt/v3bn/dz+eZJTbGgEBrKotJh+x+ZOdnvf831+Zm9j7hzeCyD8uYJh+6zPDt3zRtuzM/UKQScdjG08Y5Kb7vI9KzUofz35xxxRiOjM1JM9R1oJhu3fU/z2k0hqe3aC4ii/bULCOUs2nKdN53hjc95R2YbVh54XPZ7c3Cq+R6aAmSz/AuhqcgcLz4TKep/IS6V/60QNst1NdrHeA8vOuYV+Znmv1tQMmWOPri+y3gcrCtoqXuleltewqVMcxmF1rpVDEyV636ksEFF31zQEPptl/qWOEAS/zvigKPyMwubuPFe9EUuf9UH8zPGGGMWMvPgQXgjI2M6SfuUX5rKTRTiaDutmZj1oKQc7AzMxJC8+ZlI/Fw1IzQGQ63fEYOoX3vtDflv8V7r128NfmpXawuG4u/l68dsvUP2Wd4SHBX3Wjx3+fkbAkP+mUgz2G9SfG9cuP933/Tn2XH5OsVnW4K/0j38+Ok/nWU1xpH/gkJ8aH9Phol1pH1N12KJgPbsuKKutky1LALqljfb/Gx73Wq8X3G+JbjPM5TKovdCo5HlV3neUp12Xe04cXmLaBuKteSa9qf5HhoDMaVzcXO52CbKGdGF/WkI9vnPsu2mDZNiu4r38IJjzs7P1LOcYpbSRS/7lfyMMXWRPoo+rz7xTfmZaH+bNqJcR4uZDxXbKmxukX3eLm4Wn482t/wXRx98XCbWv+HZP5qfMcYYY4yZPfMQ2dwVbrzqnhC8kZExHaGcQRgZGSoCjWdu3u3/XgQmcaOeBvm5cuAcP9eaCcfnIuUgacnw0iyI+p2/f39+Jn5Xcf3mvRT3UT4Xj8uB06bX/H629if9XfFc5c83z2U/MhrlVjpZXK/luuXwK//sAUsOzMSISPtU/3aKLBhnz+05zY1VWsu6IWaX3mHc3CJSEUiLz5bfZ43iGuV3HqF6tPKgY7M6/IHX/X5+ZuHRyPRse97YfuOzxf9M9ymy5cvvobCh5br4C2d9IHsnzz78tPxMsy436m7uypVtcJHJT3bZmD2hpS6V6uhIrR5GK9luW4sBFnV+prSWd6Rho8sddY2VBxybifWvfdZb8jPGGGOMMbOn6bX0jJPDxbiBUfGfNzIyJoXy5gIRtV5WOxWRJ/98eR3OIiBv3e0XRM/a/7ULUpHyphvFtVqyJPPjcuZoEfhMz0xnPyPx2uW1CYt7b51uV9xrfs3S/TSCr9K5ptjVPNcQuvKyiJT/vTg++qDjwxWv/p3suD1Aa6fIDiwLwCaNkfzdt9e7YtOt8nttZIWW6kaksYFM6bORZt2ov/sCqkdRuI9Zd4XgvRA55YgzMhHt519azeyLzxb/M92nWPpjanoy+xkp6my7bW6n2ECksCnRJkcr1FxzNoSxfKCgvR0Y0wnKWZqNQam2kEL5IiPDhS1urZuFrS0vgWOMMcYYM1ee2bPuKXeF67qc6bnz1iubGaWX7mZ6/Y6t4aJG9mnxn7NQTf8x2La+ZkOsaQs0iLE8Ky4jF+/aA5RIWRCK2RntAlGkXZCKG6m843k/l/+WTvvO5pNTE/kR0541Uv69vEZeQVNYKH+Onn335fhMFILx7oQMo2lM4217P1QPG5u1tG2ipd4iXSPy2Z/Znk37bWkji4C47loUNo9f9uz8jJkPCrtStnONpRR2Y3OKOlvY/rhO6E+e+d+ytZUL4iZesf7+0quuzc8YM3fiwE87hfje3leWlwYpQ2t9R5yVbIwxxphuMP9R+J1bckHx6rA5rAmbbu5Spmfte9ZdtSpszjNKN6+u/X7DXfk/KuL9OAvV9DflLMxIkdlz8L7Ls50mFVFAKmcBFYLStT+8Jbx41Suz4yJzoyzYZX/XJhTFc+1ZHpte86HwH9/6Sv5bMyAqf65xrnz9IqDP7ycSP1cWp4q/azmXf74QOMsBV3H98nc3r1/6nuIa8LeR4t8jBy05NFtn7LxTfzI/w1z8it/MxIe3nvau/IxJ5ZiDjg9nHvuKcOqK1t3uac24A5cckol6pxzxgvxMnWZA3fxspLF5Ruk9G9NtaHCqmbHcWkfbKbL7i7p75IHHZNN/zznJax6a7kLZyMXa3e0ZmsVny/Y50tjcr+18o5+2LTbGGGNMB5k3z2LbDXn25EVbs983Xh9FxSvCucuzXzvOti9uDWdd/rqGaLn2petDuOnLztw0C572QKPYwCIKP//lRZdnx0QMRCizIm60sebQ52R/v//YAdm58ufi37VPS4tBevvaojO1c0VgHykEw1YxsjjXNEVFgNRyrvZf6+/VaxV/1xRCm/9WZESVg6+GYFb+XP63reeax+XzBy49OBMazjv1nfkZ0y1ed9J54bd/5P9riPEFTeGoWTdWHXJCNn370ldenZ+pU0ydLNeBCNUDY7pNIXCW62Nzrc5mfSYKe+zMONNrigz6cr0tlrEZbpt1otZRbi4d0vr5tSvOzAYSX3zMq2q/2R4bY4wxpjM8s2fdcYop7BeEjTcVQudlYWP+r91jR9i+PT8sOGJFOCvcF7bvyH9H7gmbzsvFWU9tN31K+07SZaGxXYgsEwOR8nqF5SDmZ17yvkw4ilPUI8Wam3++4fbw/KNfXgm2499Wd5FvyyTNL1/87fL9jggnLHtudtz4xxKtmZ2tGxkVx0VQVad+rhApy59vZqeUn7d+/XJAVvxN63eV76N53sw/N2/8xyyLttgR+5loCtqtFHXb79b0ksZGRqUa2dzIJfshOf/0CzNx6MAlh+ZnjOkNhb9RFixPWPac8MX3PBz+9G2fy8/U2V2mZ7lvjTx3xRnZQGLM6jfGGGOM6RQDMzXy4y4TBc84hX192NwyTbx+Plx/Y7j4lPxUx9kRbrn0krB9Q+k74pqd530prLt5dtmlMTN14/YN4bZr1odl+Tlj+oHvPPlY+PGP/HB46LsPhCfHnwj3bvpO/i8hfPZrfxt+8n8313kr86ZTfzR8cfvnw47Hv5X9/lvn3hDOXftj2XE7Z153UvjWD74ZPvfzXw7HHrI6O3f8Bw5tbDb0sZ/6m3DovoeGV/zuGdnvkS/8wlfCvz/6lfCz/6eeCRl3PN92+X3h/Z++NGz+pw+Hd7zgJ8OVb7gu+7cNW94c/v7rn82Ozz/jneE3fvi3wym/eUx4fNcPsnPve82vhne/5D2N7/zSf7srHHHAivDdp74TTr+6fj93/fdHwtKRpWH7zq+Fdde/MBy09ODwr5fVRzviM/7vf/5f4Yj9V4QfP+OC7Nzf/uenw0//2dvDmateGv7snZ/Mzt14xx+FTbdeFs45+Y3h99/6kezc//zS74Vf/+tfzo5ftOpl4aZ3fiI7NguLh7/7YHjwu/eHlQceHY4++Nj8bAhvu/FHwpfuvT388dv+LLzqxNfmZ43pLt958tvhedccH/YZ3Sf8v/c9nJ370//7v8IVn7o4vOKE9eFP3v7R7JwxC5U//OLvhKv+dlPN13hr+O03/2F+NoRf+sQvhj/78o3h58++NPzXV/5SftYYY4wxpjv0XvQ8s104nKPomYmXW8Lt+a+tFAJrXfS87ZXXhuvPyRXORNGz/vlHw7tLgu2jjz2VHxkzvzw9+VR41R8cnx3f/nMPNrIp77j/c+G/fvxt2XE7r33WW8K/PPwPYcfjj2a/v/81H8rOEW/8kzOyz330gi+FlQfUBaOX//6x2eZCf/aOL4RjDl4THvneA+G8zS/K/i3ysQvuCHfv+PfwS5/+qez3/ZccGP763f8RfvsLV4SPbvvj8JZTLsjWu4zEe4z3GnnTczdkU5Nf80cnhcd3fT87d9HLfiW87fQLw1m/d0y22/HHf/IrYdm+h2X/Hj8XiRvPxE1nHvru9vDWLS/Lpp/f+q7/l/0b8cV7/za895MXZNP4Y1Zr5OZ/+0j4rc+/L7xyzQ+FXz/nj7Jzf/6VD4ffuf392fEZR700fOjczooRKw5dalsyj/z8X/54+OcHvxA++MNbwktXrcvPGtNdxqd2hS1fvj7bcX3j89+Tnfur//en4eq/uzS85NhXhWt/5E+zc6nYnph+4aZ//cPwob//QLY8ya+8+nfzsyHrY2NfG7OVY2an6V9sT4wxnSLVnsTPG9MpWueWdJWTw8VxKvsdW8K6bLr4leGWZ5xaPkuWrw/XNzYaav+vECiXh9X1ZLAm33g03B5WhdVdWkPUmF4Sd1T9+ZdtCr/5+j9uCJ6R8nqU7cQpZ63T1PVno+CYTaccOzg/E41HPj0z/47ytPoYxO8/dmBjra+Dlh4aNp5xUXbcmGZcurfWdTPrlKfPNb+r9W/L12gcF5/ZjXkbyJ+drlE+V74Pr/u4+GhMMy69c2O6zejQWCb4FIJnpN3OGbOQKXyCypqexfR296fGGGOM6QE99qyj8FkXJDefX6yXGae8d5+4cdHtV32msS5n3NgonH9GI2tz561XtqzbufPWLS2i7LaPbQm3lz5vTL/x46f/dDj7+HPy3+o8U/Acxbz29UAVP3bau7MAfb98Y6NIIWgWG3KUN1S64Pk/n312KL/+wUsPDW9/3s9lx8W55k7F0RDVj6O4ev7p/yU7Lq+/WQiPxbqhRahU/kwUESK05hgx1BAYmtdo7ORdMo3Fv7/2WW8Ov3vu/8mOzeIhbpAU1wWN2XXGzCeNtYdbBqOMWZgM5ZsqttfnmN0cKfffxhhjjDHd4plVgS6y9sI8G/P69dnvmy+6IKy9dGvYmf3WBU7ZEG67/L6wsdhIKa7PeeHJ+T9WWXZUKG1itPvPG9OPlDcSaidmWZSFyt2JhO20f77ImKtTlyWL3Vxbsymr31OcO+bg48NRB9XTsovr/+6b/jy89bR3ZcfFufafZRrZmtn/amjDo8bfls41v2N3VzTGmD2nyLx3BpxZDAyL+vzG57wjW1LmnJPOy88YY4wxxnSPqmLQa07ZkE9FL6a+d2+X9GXnXJF/V+2/tg2J6v9W2mCpcV/8eWMWAiQKNhho7rj+e2/+WHj1iW/KjmdLce1CICxnRzZofH93lg4ui5Nxrc/IbAWDFxxzdpbh9ztvvCk/0/zb8lLHze/o0fLHxpi9Ek9rN4uJkw5bmy2Lc9Zxr8nP1DnywGOytbQP339lfsYYY4wxpnv0kYddTH0v7+xujJkLz7SuZT3Ts56J8YziqKCYmjYwk4uepalqhVDYXOezKUTSGooNAbX0ucY6n6XPFaJA8bnB0ufj2UjczCgGVKceeWb2ewrFdVvvrTguf5cxxnSWYsmNPbHHxvQbJy5/brYszlmrX5ufMcYYY4zpPfasjVnEPFPwHIW9Yv3L3W36QxRT14rvKG+KVIiHwwPFup/N6xfi6FBJJG1M6yyLjblgWv5cQxzNfxYbIkSKaxy49JBs6lyx+3oKxfXLAm6RwWohwhjTTdad+MYs+/w3Xv8/8zPGGGOMMcaYueAo3phFzGDLOputzDXTs30qJm2+0fz+6nTxmVlOFy9/riF65qLq9Mx09jNSnpI+d6r3a4wxxhhjjDHGmIVDutJhjFkwFBmLccp3O1E4/OO3fjrLLHruijPys7MnTh+P08jHRurXLtYHjRRCYTNjspTB2SZcRmgtO/rcqStemH1nIaaWxdqOiJONazSv9azDTs3WJWvfGd8YY4wxxhhjjDH9S1VpMMYsGgpRcNXBJ2Q/O8mVr/2DbBr5wUvrW3y1rOmZ/1y+34pMMHzzKe/MzzRFTBI9y+caomdJzLzmhz6Sfee+o/vnZ0J2/fhfJ6DvPGHZc7J1yV6++nX5GWOMMcYYY4wxxvQ7Fj2NWcTEjQRiJuevn/Ph/EyTTk/bHmpsWtQULw/dZ3kmGJ773A3Z75Fmdmbz+weK2eSleyrW0iwLoUS8fvyvE5xz0o9m5fUrr/7d/IwxxhhjjDHGGGMWIhY9jdkLGBmqbyhUptiIqBuUNxhqp8gILU9pLwTT1unq+edspowxxhhjjDHGGJOI1QRj9gJok5/ObvxT591nvne3U81nm2Ha6UxUY4wxxhhjjDHG7D1Y9DRmL4A2Cgp0bo78xAv/626nmm8446JsCvnPvfS/52dCeOcLfiFbq/PMY16Rn2lujFTO/jTGGGOMMcYYY4yZDVYTjNkLGB5qrrfZjxx78JpsV/ZD9lmen4lCbX5gjDHGGGOMMcYYk4hFT2P2AgZDdf3Ofp8+3shOtfppjDHGGGOMMcaYRCx6GrMXUN5ZvaD/p43XxU5LnsYYY4wxxhhjjEnFoqcxewFDg9WmPtDncuJgbp68oZExxhhjjDHGGGNSsehpzF7A0pF986M6r33WW8JbTv2J/Lf+pMhO9UZGxhhjjDHGGGOMScVqgjF7AZNT4/lRncP3PzIcdeCq/Lf+5H/92K3ZLu8nHbY2P2OMMcYYY4wxxhgzOyx6GrMXMNi2pme/T203xhhjjDHGGGOMmQsWPY3ZC2ifIu51Mo0xxhhjjDHGGLOYsehpzF7C0OBQfhSx6GmMMcYYY4wxxpjFi0VPY/YSBgbKoqcxxhhjjDHGGGPM4sWipzF7CcMl0dPT240xxhhjjDHGGLOYsehpzF7CYGl6uzcyMsYYY4wxxhhjzGLGoqcxewnlzYyc6WmMMcYYY4wxxpjFjEVPY/YSWjYymsl/GmOMMcYYY4wxxixCLHoas5cwNDCcHznT0xhjjDHGGGOMMYsbi57G7CW0TG/3mp7GGGOMMcYYY4xZxFj0NGYvoWUjI2d6GmOMMcYYY4wxZhFj0dOYvYShgdKans70NMYYY4wxxhhjzCLGoqcxewmDXtPTGGOMMcYYY4wxewkWPY3ZSyjv3u41PY0xxhhjjDHGGLOYsehpzF7CUHkjI2d6GmOMMcYYY4wxZhFj0dOYvYTBwZLomf80xhhjjDHGGGOMWYxY9DRmL2HIa3oaY4wxxhhjjDFmL8GipzF7CYP57u1XvvYPwtuf93PZsTHGGGOMMcYYY8xixKKnMXsJxUZGg6UNjYwxxhhjjDHGGGMWIxY9d8eOreGis7eEbfmvxixUBvONjIqMT2OMMcYYY4wxxpjFikVPyV3hurMvCGvP2xJuz88Ys5Ap1vQsxE9jjDHGGGOMMcaYxYrVD8nJ4eLP3xi23bwhnJWfMWYh4+ntxhhjjDHGGGOM2Vuw6GnMXsJAPq19MFj0NMYYY4wxxhhjzOJmYKZGfmyIuKbneY+Gd39+Q1ibnzJmIfKX//Z/wn3f3h7OPfXHwrGHHJefNcYYY4wxxhhjjFl87H2iZyZiqnU614fN7eLmM4iejz72VH5kjDF7xopDl9qWGGM6gu2JMaZT2J4YYzpFqj2JnzemUzjTc3dY9DTGdBEHFcaYTmF7YozpFLYnxphOYdHTzCde09MYY4wxxhhjjDHGGLOosOgpuStcd/YFYW02FX5r2BiPb7gr/zdjjDHGGGOMMcYYY0y/4untc8BTPowxc8XTx4wxncL2xBjTKWxPjDGdwtPbzXziTE9jjDHGGGOMMcYYY8yiwqKnMcYYY4wxxhhjjDFmUeHp7XPge49P5EfGGLNnHLjfiG2JMaYj2J4YYzqF7YkxplOk2pP4eWM6hUVPY4wxxhhjjDHGGGPMosLT240xxhhjjDHGGGOMMYsKi57GGGOMMcYYY4wxxphFhUVPY4wxxhhjjDHGGGPMosJreiay89Yrw7qr7qn/cuaGcNs168Oy+m/GGIMk2Y0dW8NF520Jt+e/1lkfNn9+Q1ib/2aMMUhmPx4N77a9MMbMltnaDfsnxphU7twS1l60Nf+lhvUTMw840zOFWqNdd9WqWud+Y9hW+2/z6trvN9yV/6MxxgB7ZDfWhE031z9f/88BhTHmmbgrXHf2BWFtRZAwxhjFntgN+yfGmNmz86H7wsbrC3txbdgUrJ+Y3mPRM4FtX9wazrr8dY3Ofe1L14dw05fDtvx3Y4xpx3bDGNN9Tg4Xx4Di5g3hrPyMMcY8M7YbxpjusuycK8LFp+S/hOXhrFeuCWH7I2FnfsaYXmDRc9bsCNu354cFR6yoOQn3he078t+NMaaFPbUb94RN510Q1sYMjLO3WCA1xhhjTB9g/8QYs+c8fP89Iaw+0tPbTU+x6JnIcUctz4+MMWZ2JNmN5evD9Y1pYzeGzedvDRsv3eoRUWOMMcbMH/ZPjDFz4c4tYeNN68PmC0/OTxjTGyx6JnLvQ07rNMakMRe7sfYtG8JZdzwaHs5/N8YYY4yZb+yfGGNmzZ1bwmnvuS9sutnrAJveY9Fz1iwPq1fnhwXfeDTcHlaF1U7+NMYgthvGGGOMMcaYvZRsB/f7wq/8nyvCuY5/zDxg0TOBuAHJ7Vd9prF+TdygJJx/hkcrjDGS3dmNbTdcENaWpoftvHVLuKWUGLrtY1vC7bYzxhhjjOkhhX+yY6b+u/0TY0wymeAZwubPXxHefFh+zpgeMzBTIz82s2DnrVeGdVfdU//lzA3htmvWeyFeY8wz8kx2IwYVG7eXzmXOwdbs3zJsZ4wxu+WucN3ZV4fN+W8Z518atl34nPwXY4xp55ntRuGfbL16fVg+UDth/8QYk0hmR27Kf2mwJmy62VmfpndY9DTGGGOMMcYYY4wxxiwqPL3dGGOMMcYYY4wxxhizqLDoaYwxxhhjjDHGGGOMWVRY9DTGGGOMMcYYY4wxxiwqLHoaY4wxxhhjjDHGGGMWFRY9jTHGGGOMMcYYY4wxiwqLnsYYY4wxxhhjjDHGmEWFRU9jjDHGGGOMMcYYY8yiwqKnMcYYY4wxxhhjjDFmUWHR0xhjjDHGGGOMMcYYs6iw6GmMMcYYY4wxxhhjjFlUWPQ0xhhjjDHGGGOMMcYsKix6GmOMMcYYY4wxxhhjFhUWPY0xxhhjjDHGGGOMMYsKi57GGGOMMcYYY4wxxphFhUVPY4wxxhhjjDHGGGPMosKipzHGGGOMMcYYY4wxZlExMFMjPzbGGGOMMb1ix9Zw0Xlbwu35r8RZl18bNoUbwrqrVoXNn98Q1ubnjTHGGGOMMc+MRU9jjDHGmD5g561XhnWffXG47Zr1YVl+zhhjjDHGGLNneHq7McYYY0wfE8XQtZduDTuz33aEWy69IFx0613Zz7Vn5//dcFft3+4K1xW/1/676NYd2V+U2XZD6W/OvjL8xbfyf0ghZqiWvide55bqVxljjDHGGDOvWPQ0xhhjjFlg3H7Vx0J4741h2+dr/928IZx109Vh7dkfC6tvbp4LV93QIkZuu+GdYWO4rP7v8b/rV4VffWuqYHlXuO68LeG46/Nr5Ne57Z+tehpjjDHGmP7CoqcxxhhjzALjrMsvDOcuz39ZvjasO5PO3dMUI3dsDf/zz9aFzReeXP89csrrwvvP/HqaYLnjkXBvWBNWH5H/HjllQ7j+nOKLjTHGGGOM6Q8sehpjjDHGLHa+8Wi4PWwNG1umpV8SNv1j4tLuuZi66bz6NWgKvTHGGGOMMf2ARU9jjDHGmL2C9WFzMSW99F9alubycO418e+uDZteNBBuv+oSi5/GGGOMMaYvsehpjDHGGLPYOWJFOCvcF7Z3TJtcHs69+iOZaLr5/BBuv9+ipzHGGGOM6S8sehpjjDHGLHaWrw2vOvPrYdN5W8K2/FQk7gx/3Z35L8Xu742d4oE7t+Q7xRfsCNu3h3DWsV7T0xhjjDHG9BcWPY0xxhhjFj31zMzN57eu67nuqlVh/Sn5R+78ctgc1oT3X7I+LMtPVTjljLAx2ym+uMYlYdPqy7yRkTHGGGOM6TsGZmrkx8YYY4wxZi8lZn2uu/8t4SsXnhwG8nPGGGOMMcYsVJzpaYwxxhiz17Mj3P7ZEDa9xYKnMcYYY4xZHDjT0xhjjDHGGGOMMcYYs6hwpqcxxhhjjDHGGGOMMWZRYdHTGGOMMcYYY4wxxhizqLDoaYwxxhhjjDHGGGOMWVRY9DTGGGOMMcYYY4wxxiwqLHoaY4wxxhhjjDHGGGMWFRY9jTHGGGOMMcYYY4wxiwqLnsYYY4wxxhhjjDHGmEWFRU9jjDHGGGOMMcYYY8yiwqKnMcYYY4wxxhhjjDFmUWHR0xhjjDHGGGOMMcYYs6iw6GmMMcYYY4wxxhhjjFlUWPQ0xhhjjDHGGGOMMcYsKix6GmOMMcYYY4wxxhhjFhUWPY0xxhhjjDHGGGOMMYsKi57GGGOMMcYYY4wxxphFhUVPY4wxxhhjjDHGGGPMosKipzHGGGOMMcYYY4wxZlFh0dMYY4wxxhhjjDHGGLOosOhpjDHGGGOMMcYYY4xZVFj0NMYYY4wxxhhjjDHGLCosehpjjDHGGGOMMcYYYxYVFj2NMcYYY4wxxhhjjDGLCouexhhjjDHGGGOMMcaYRYVFT2OMMcYYY4wxxhhjzKLCoqcxxhhjjDHGGGOMMWZRYdHTGGOMMcYYY4wxxhizqLDoaYwxxhhjjDHGGGOMWVRY9DTGGGOMMcYYY4wxxiwqLHoaY4wxxhhjjDHGGGMWFRY9jTHGGGOMMcYYY4wxiwqLnsYYY4wxxhhjjDHGmEWFRU9jjDHGGGOMMcYYY8yiwqKnMcYYY4wxxhhjjDFmUWHR0xhjjDHGGGOMMcYYs6iw6GmMMcYYY4wxxhhjjFlUWPQ0xhhjjDHGGGOMMcYsKix6GmOMMcYYY4wxxhhjFhUWPY0xxhhjjDHGGGOMWRTcFa47+4Jw3Z35r3sxFj2NMcYYY4wxxhhjjOkbdoRbLr0grD27/t9Ft+7Iz6ez89YrG9fpxPUWEhY9jTHGGGOMMcYYY4zpE7bdcEnYtPqysO3zN9b+uywcd9Ulc8vcPHNDuC27Vu2/mzeEcNUN4Za9QPe06GmMMcYYY4wxxhhjTF9wV9h605qw6S0n57+fHNafH8LmL96V/w7s2Bre84oik/PqsDk/jSw/MhwX7gnbv5H/voix6GmMMcYYY4wxxhhjTD+w45Fwb35YsPLYNSFsfyTszH9v5a5w3XlbwsylH2xkhm7M/wXJrr8+rD8l/30RY9HTGGOMMcYYY4wxxpi+YVVYvTw/3B13fjlsDuvDu845LD8B3LElrCvW9DxvS7g9P73YGZipkR8bY4wxxhhjjDHGGLNX8qapM8NA7WcUynr585ahO2r/m7Nja7jovC+FdTdfEc7Nhc+4GdG6z7443HbN+rCsfqrJnVvC2i0rwtar14fl8WLZ7u1Xh3D9jeHiU/hvn/F6iwhnehpjjDHGGGOMMcaYvZ6BmYEQZgZr/9V/9uz3Mtmam608fP89Iaw+UguUdzwaHs4PZ8Oy5784nJX4NwuRtpI1xhhjjDHGGGOMMWbvY2a6/l/If/bs9xbixkX3hE0fKzYuihsbhbDxpcXGRm2cckbYGLaG2+6MeaM1sunuz8zOf/5SuP3MFWFl/vtixdPbjTHGGGOMMcYYY8xez5t2vYTnn5eVsy78+1+O/kPtoMyOcMull4RN+az3sy6/Nlx/zjMs8nnnlnDae7aGTOE7f33YeNPW1untV91T/1zBmRsW/dT2iEVPY4wxxhhjjDHGGLPX88anX5If9Za/WtIueppO4OntxhhjjDHGGGOMMcbMDOTrbPb4p+kKFj2NMcYYYyR3hetecUFYe3brfxd9+lv5v88P226o3celW8PO/HdjjDHGGDN36mtsDvT8p+kOFj2NMcYYY4gdW8NFZ18dNv/4pWHb529s/nf9+vD317w3XHTrjvyDxhhjjDFmMTCQ7aQesy/hZ5aV2Z1/N90hlq4xxhhjjGlhR7jlg1vC7WduCFv/y3PyczmnbAhf+dxlYV3+qzHGGGOMWRzEXW+y7Ev6WfzXhX833cEbGRljjDHGVMh3zAwbwtar14fluxmAj7tirr9qVbjx5hXhw+dtCbfn58P5l4ZtF7aJpnHKfMwgzX8LYU14/0evCG8+LP81Z9sN7wwbbyq7afXPHf8XF9TO56cK8h04Q7Y7Z+0+PndGuO0V+XeUd+eM2avl+wvrw+bPbwhr898K4vT58necdeaacPsdq0qfjdP+rw73XnZtrYxuaOwIuvFDHwkXn/ofbc9XZ2O+g2hB9h3ba/e24dGw7qKt+dnic607lqr7NMYYY4zpJD/y/ZfnR73l4wd8IT8yncSZnsYYY4wxFZaH1atrP+7YEjbfOdvx4a1h4wdD2FRMg795Q3j5n13TOg1+x9bwnigWXn5tY7r8bZeH8Ks/emW4pfGxKPhF0XFd2FxcK//cvd8MYe2FN4bN59c+FsXM4t8LUTNja7jgFV8O69v/7c4tYe15W8Jx1+fna/9tPv+2sPHsLWFb9neR2ndfFsXWKDI2P/fu1V/P/72V26+KwvCFjc9dfGquDpfvLf53fe16F10Qrruz/s8NauW77otnND532+Vrss+tPfuSsH1D8ffXhk1n1sr2hrvyPzLGGGOM6Q5Z9mXlv2LtTfXf3P/ddAeLnsYYY4wxwNoLLwsbaz83v+edLZsYVYS7BuvD5rL4uHx9eP9la8LtV30mFxXrU+a/8OOXhuvPWZ6diSw758Lw/hd9Pdz2z7nqeednwqY7YlZna2bjsnOuaMmU1KwPN36uPSuy9t1/elsI51/Wco21F36wVVCsffcH7jg+bLq59e9XHnt8ftTKWZdf2/IsdU4OF7eIsDVOOSMry3sfalsHNYqjF56c/1J7xue/OJxV+9maFbo8nPXKNSHc9OWSOGuMMcYY0wWKdTdbfg6I8x38d9MVaiVsjDHGGGOqnBwubmQa5qdq1DMRn0n8bLLsqFW1/70vbI9a345t4bY7Qtj4kqbIV2d5WH1cCLffXxcEt31xawhnvjic1a4lzoXad//dHTNh40vhu2NG6/ZHsp3gs+9+YSe+u56t2hSL69Pdi2c0xhhjjOlLZgaydTZ7/tN0BYuexhhjjDHPyPJw7jXFVOv432Vh40AIm7dszYTCVNozR+N/2dqdufDYa1YeuyaEOx4ND+e/z5k4jf7s+nqozSnu9axZY4wxxpi+Zqaeldnz/0xXsOhpjDHGGJPEyWH9j9ec0z0UCuNmP00BtfRf+5TwHvHw/feEcOaKsDL/fW7sCLdsiZmqG5qbJxljjDHGLBB4zc3u/2e6g0VPY4wxxpgKd4Xr5MY5O8L2e2s/ZiEUbvvibc2p6svXhnVnhrD5H555Q561L10fwh1fCrd3ciZ47btfdeZA2PzF9u+uPcv22o/VR2YCZceyPvPrGWOMMcYsKGaaa22GtrU3u/q76QouWWOMMcYY4qars6nn1/1b60JL2264JHzgH2fCxg27yWS8c0u44M/Kn1sezq0dD7Tv6B6pfbZx7pTXhU1n3hM+8KPlXdVD2HnrlY11ROviZIowWvvud6zLnqm8Fum2G94bNt2xPmzONxNads5bwoaBtp3Sd2wNH7j6nvyX3UGbDsX1PetrehpjjDHG9DUzzbU2Q3nNzW7/brrCwEyN/NgYY4wxxjSIYt0lYdMd+a8FZ24IW69eH5bXfNWCKEiuv+qezHdtEndgvyK8+bD81wZ3hevyjX2arAmbbr4inFvaQGjbDe+sr/XZoL4r+2n59267Ia4FWj8uppOH2n2su2pVy+da2LE1XHTelnB7/mu24/zn23d6b7u/2rVvfOWXwgW16zY/W/vMK64O915Gu7e33Vv2bG8J28+rXfP8y8K2XGDNPrO9bRp8fn/HtezeXi/f+FzVezXGGGOM6Rw/9I2aPzUPfPKIrfmR6SQWPY0xxhhj5khd9FwVblykolwmOn72xV6n0xhjjDGLmjc8Mj+i56eOtOjZDTy9/RmIGQjNnVVbp5gZY4wxxiw+doRbLrsy3FKeNp9Pb9/tdH5jjDHGmIVONu28dc3Nnvw0XcElK4gZDdmUq3xH1dsuvy9svHRr2Jn/uzHGGGPM4uTrYdN5pYHf87aEVb/7kZbp5sYYY4wxi5GZTISs/f90/rNHv5vu4OntgmydqdBcdypuMLD2ouC1pIwxxhhjjDHGGGMWIW948LX5UW/51NF/nR+ZTuJMT8Hat2wIZ8VdW/PdS7d9cWs46/LXWfA0xhhjjDHGGGOMWYTMTM/Pf6Y7ONNTUt+x9bawJtx+xz2131t3N/3FX/2N7OfoSF03Hp9wLTWzY2AIxhoGxPjD1GR+0ES12KF99smPmgzuWz0XGRiE76NzNaaefDI/ajK0ZEl+1Mr4N7+VHzUZHBvLj9oYHMgPmkw/9XR+tHsGRkfyo1YGBofyoybTT/N1B5cuzY+aDI6N5ke7Z+rxx/OjVgZGqvdG5yJT3/9B9jPaksKODC7l8k0pn7mC9bTGzFTV1g0u4Xc8MDycH5WY5go8tP9++VGT6V278qNWZqbB3k5O5QdtDFfrAzGzazw/amXowAPyoyYDA9W6G5ker15jBs5Fpscn8qMmA0PiXuHrVF2g9jZ88EH5UStUjtNPPZUftUGfFWU2M1m1W/0Mvc8BUaepfLE+1iD7wjWH7ezUD9i+jB19VH7EtNgSuN/J7343P2oDnmP4kEPyo1bomWVdp3asOjLoh5QdGNqvajOoH4zQO56eqLbByAydF/0joeoDvXuyGZHpJ57Ij5pMPVGtI6kMH3RgftRkQPTRWFdFOVBdp/oUmYJnC1PCfgOT0C6GhK8zA9edmWD7hD6FuC8qM+XrDJK/JOr/1A/q/kAZ9X4GR6u+yrSyvfR9uwn/ynZkRvXFUD5T3/t+ftTK8LJD86MS4h6ozdPzKiYe+3Z+1Aq94xnoiyPYH0sDDvZwN+XbC8h/iVAMoHwg6puUjzoDfhja0xrk89UMdX7QBtSzadGOyYdXPkkvfWoF+c/TT3N7I59aPRv6KuKz1I47SdmWEAOibf/25ZfkR/PD6+97Xe1/Y50st+Xu//7pVbfmx6aTWPQUxOntHz722nD9Ocuz37NdS69a1RA+j3/52dl5Y1JBQYzEoRrYQakObv/986MmFOBkQLNHx7wGdUYquB1/9Bv5UQllYii4pWCoBpXPgLhfCrSUODl8EIhBsf8B8B0JB40+K8UkcqxVIPB4tXyUINAtUCASDssQiG2ynpEoIcRqKvcpKJvI0H775kdN0BkUZU5tUAVfVM9UQEXC09C+1XuN0DseOqDa3iODIPqkXHdSBYsj4GyLoAMdaGG3lOjTSyi4HRRlNgAiuhrQGAKbrIRtCgyVzRhZBlsJCTFqGO5BQXVdCYPUNlW7mPx+VQSRA1Ek8Cg7C+Wunpc+S8F8ZBjEVPneoL9RNnliR3lnqDrK7kx973v5URMlgqtAlhg5/LD8qIkSiunehg9gEYXekRIBx78Fg6OiDVH5JtlOsi9CjKXvUvdF/ba6Bzov7R7dm2jb5GfseuCB/KiV0RUr8qMmKf2rFEvgOaQ/S88mxBbqQ5SfQe9o4lvVthZBEVy1H/qsaK8oIop61ktGDqvHsLNBiZP0PqVIBoPaapANB++ErafYAkXTGnS/0naSrRb3MFdhUPUhNKgh3wVcQ34W2oUcvBD1uleQ3xr52mfmV/w7595z6pJkLJ68WvTi908fZ9GzG1j0ROpZnts33NhctH/H1nDReV8K626+Ipxb60MWmujZrx3y3gh1yLLzJsFRdLwkQCjREztf0dFT4ELZCJHxhx/Jj5qQGBuZAYc7RcCTo/7wHOq6JEqQSBahTBYVuJBjoUwtCkfiHSvxthsoIYccLPUuhg+tZolRdlYEHTRRDiQGKYeS3tE0ZEwN7gOZSjVUphtBor0KvlLEbgrKlPNKDrTK9EQBWgTYFIwrUY7epWorkztge0AVdCSIOymgKKFshhpIAsh2TguhbfSII/Kj3ZPy3qhtqnKk4FRmelLbVO1ViSsAXVcGiySuKIGIgkXhA5HoKdsFtFl13SkQfyceeyw/aoXKbPLb38mPdg/1bREavNCfrbZjJQIOH1htF6rP2/Xgg/lREzkYBu+N3ruyL2R/VRvEwFu8S+ovhqAMIjS4Okh1twbZHdVesc8UAgi1K+XH0XOo66JPIsqM3pv0MxLsAN2byvSkz6qZGziwLmwc1XV13ZR3PFdokCOCfbToz4cPPjg/aiLfBbRDzOyuQXZH1jO4RkoMoHwKykpW5aD80blCtk/1mVSnUmQcVSepf+vW81LdwZkCNe7+9Kfyo/nhnO2vr4uStf/KJVf83v6zYK7//unVn86PTCex6CnINjKKu7dfsz7EnIqFnumJDn+XDJrpHCQaYaBXY3JnNXgaO/aY/Gj3qPqgsjUIcoDlCCs4N7Kjh/qbEtSpZyNRWQVfdL+qbPDZhIiCzq4wyyTWyYyRHqKEzOFDwFkWQQ5lG6lno0EcyiaLUHvB9yacwZSlIKj+TpCoF4E6qbrjFPtNQQdOK6xBdU/VfxI7JkXQTNfAcqxBgfeMWAJhBoKRlPqvgqRBGOigZRwUlP0Zoe9TGQ3k9I8/9HB+1MqS41blR03Ue6NMTRVYklghs6ugDanrUj1T9ZcCThUQYeAt6hm1C9WHoE1W9gHOqzKb+HZViFHvmKDszwjWKXG/KNap7E2yR+K6JOypABsz4oXtm/xOVejFjEr1XSCWpAhM5FdFqMxGlkMGdg0ShGXfRv6dCtPgmaVgk1CnyW4p/4WEfGkzwEapdzH53WpdHxIDk2Tj5HuDuqN8EvRHRZml1Clpf7vAyBGH50etkP1Wg+rYtsn21qBnUz7Q6IrqQJ/yKWgauqoPWP/UQBTFLGLWUMp7o/JV/Rgu9aXugWyBqHv0WeWHzXcSlMqSv/vW+RX/zrnn9dnPWJLlt9Tt329dY9GzG1j0lNSzPTfdkf/atqbnXEVP5Qx263XMdXTIdBfVEZFYoRxKyvxSWTrUIe966KH8qBXKQFIOHq3pOQAdeoSEjWnh8FAgq8oMA0CxPiU5u0PCMcFnFvdADpYK8sk5HxSZv7TmknJU50onnCOavqSyYdT3EfTMSnilIJACThXUUcCqBh7oGpMgdGSATZZlAJ9VGaSjR63Mj5qo6auEatvo8Iv7pTJXQSjZuOknOXAnOlH/UwaXsJ6JvhSDadH3Yx8t2poSOAnMwBPvmMpSCQL0WSUqUzajugfqs1TbRoSdpWnZav1DEkh3PVKdxRAZO/LI/KiEeMckwJGoF6HAW/aPNCging1tshI9of6pdkH1RIkElFEshSewqWTPlD87CXVPDcTSjBu1PEmAchgVWXVUvsrWY38lypGWclA2g96FGmROAp5DZZBS3VHlQG1FrSFN60uqmRDUt8hBFVgbUtV/bPPCFtHavCQep0JlqWYm4MBkwrPhwHENsjtKiKdyl34yiOBq3XlCDWLSQJLqx+aaYKAG2VLWs8e1Y4V9oDak3rHqL2ZLiqZB9VQNBs97pufX3pAf9ZZbT5jf516sWPTcQxZcpicFVH71fYNy/IYhc0CNsFKQNLYSArIaJJxOqiwSCmaEY50iQBQb+JRRzhE6cyJQI8dEBlTgaKqRcXL8lKNATogUq8HJUwvK0/TpXo/QkrOsgqfRI6vriEnHD4QNmc0I5U5CRQTfEbU3GaBUy3xkOa+TRZ/ddd/9+VErJP5SlnJEBiMAOeyqrVBgqNowCm3C0aVr6IX5wZ6Jvok2SVABNiHtLAivKvOABKa5OvwRslGqXdG7UMI2BacTQmije1N+AtZJFbDCO1JTjEcOrb4Ltb4q1XUlIpKYKgNLeGZli+izqk6iqCz6c5rKniJoqcB9CLLv1TqdeG/iflPqAw46ifow/o1v5kdNRqGPVgMBLJ4pW1R9BuU7kCAsB5nh3lQdkdlgsySpH1R2C96bsnFT4LMpG0ffpz6LMzeUr0N1UnyWZsuo/oYEQyX2pYj+ZAemQNSLpMRpVH9pQC+SYuvpOZTNoLalyoFIsbNqRsggbFalBlJT+t2UQQJqLxTPZcC7mAC7FyGbnGL7ZNuE66a8txToHpTdm+9Mz9d99YdqN1w7iPUvvtNY5Xrw+2dO/GTtwHQai557yIITPRM6ZNM/kAAnRyHB+cQNLyLQ6SixgzpZ6kwjKRlaKFwJEZGCp0GR/UMiitpNlJ5NtQvclEdl0IFZVQ4T3ZsqB3JqZZklQI68dD7pXailBsD5HBGBIdko5aBRXZVTVSH4wUxEIZaMHl5tg0p8oAwmvTFQ9Xll9oR6FwBNX03ZfITuK6JECWLyO9VykOIZZIKr7KqUe6Dvk8swUEa9qHvUBtWGOGR/lX0hp18JYlQflOiJA56iPk1Cu1L3m9Je1WAWQVltKvue2jyKbzXomaWoLNohQe9IXZeEnImdavkLsDFiGQVCiVQU3KqgmfoxVb7kl6gMxQnYlISWQolgXSVbIuoerTOuZhsQJKBEqA9Q9YbEUPV+SDRSNhn7beEPoNArbHLKQCoNnCm/iAbWVZlRG5L2EJ5ZTVFGu6XaCiCFQWrz4n5T+rG5opa2wWQG8WxkH9TaviiCq/KFNqvqLyVJpAwSy3YBMUuKuJmCWv4C4zF43gywh7JOCvtLoJ8grtsNlOj5tb/+TH40P7zuqz9c1yTrv2b04vdbn/WJ+i+mo1j03EMWnOjZJbHEdBdaS0e+N3Cw5O7t0MGN38+7fpLYpyCxgoSvCO5EnjB9RAkYGNwmBN0qAKR1/1TQQOWrPktrFckMGZGh0g1UgEG2RDksA0uqAY0SMChQUuI6TeuT2Tv0HAnONl5XvB8KFinoTgWzNw9kIZPKUQ2UUACoBDwStGSAAllMMgiF99OJoIOuq7KtaaMnlemZIghQ/VXvgoQRJYjRwIGycdSH0NqSEZwCLmwy+hQiyKI2pOoOnVf1YfSw6nRims6vUO+SpuMrcYbOq92Kyd1W63RSFhOtaRuhPk/ZZMz8SqjTag1S8hPUxku0uUpKf0PPK+03ZPalZEPKTDlox0r0Rz9O9CG0w78avMZrCIFJPTNB7UIukUJiiRJbRPkQ1OaVjZNLEMwS8lMyoByk3YLZOUrc7Iewm+xWSv+o7AvVB2W/STCnXdoV6h6onkhxvZc+NdiyiKpTexP9Knq+9j+aomcvf37m2RY9u4FFzz1koYmeZmGCmSwiOCDUjtQoDNK0nxrUUaugDkcyhaNLoqcSuQiVTUCBh8xipQBBOCZqh32EAgGYmhtRQS9Bgol0whMC2RRIKFM73lPdUY41bjAg6jo5qlKkgi4OgxHRFaJQodbfwgBQOLQJ7weD0AM4sxWDGRVswv2q4ACFBhHcknCakvGaMkihwLon6hMJyNK+wHXV+nGEHFSB75NCPrxPdb8pmVgkjKiNJWjwIkmsJttbgwRZEo0idB53Xq9BdlIt6aIytBCwD+OPPpoftYL1T5QDij6ij6Z6ot7xMNV1lekJyJAhQSDC4F+UA52nJUNknQYbpewWBd5q4Bj7MWHT6d5U9maKAIL1SdwD9SGqHOi6qt+m+1V2lt6lFDKhv1Ezdgb3rZavEhxpmRX1LnBTTLH0EGfrCT9hgUEbDqn2SnVdlQMuaSTqP9lDOZBK9kUMMqTEHHNF+mFAt/z3fkXZ76995tb8aH543V0/kh/1ls+c/PH8yHQSi557iNf0NL2ARETs0Gvw9A925mi3wCmYkhqhekKZGhF0WEQ9S8n0pGdW4gyKlir4gvJR7YKm4crAhQIMsaYhrvMm3jGVpcoYSQGfQ5UDfFZNTaQgUgZE5OQJx5quIQUXcDTpGdTf01pbMpinjBEldkMgoOoTirxiOj89LwkdEQp6VXBL70JlD+GUsYTgNiXwT0GVL4r2ou5Ruausc2zb4h7InklxBgIEWu83krKWJX2fahc0yCDXKYT7lcEtiBVyWQIQPZXtpIBT9TdJUzTVeYCW0JgQG3ylrN9MdUrVHbLV0u5An0WbxkRSBuTGH6mKwmPHHpMftUKZpbQ2Hs6YiMA9KKGDxF85SAeDoDKjnnwV0b9SG1IDenRd9d4JaQegzNQSKTiALmwG+mzKP4TnUIPXhHoX1A/JtSETNkgiAU7ZjJQZTf0A2VS59j2gBGgUPUX9pc3H1PR2yvxV/gc9m4xDSLRMsP+d8NUXA+S/q0zP+d7I6LX//iP1dxztVPazdjK+8sbv7T878+9//Zy/qh2YTmPRcw9xpqfpBTTCqoI6mhZC01IiLDSIDjka4jZUZh85fmpNLBy1VyOhdF6YLupQlbNBwV7KqLSaFkUOt1qjB9enFEE+ihLCCU8BRQnhJNJnaZfTCO5aK+pvSvYlBXtSBIf6S4GLKkcK0DHzIQJ1T713epfK4ccgVAjNVKdTMrkUKe2KAgy5UQMNEAixZK4owR2X8BDPRrvAyvoAz6bqGQaA4h5QnBHtKil7noQrcQ+IEOXomdUyCsSo2DgMAyVo7xEMWIV9obVNlaiM67aq60IWq9roDO9N9I/0jpTog5uziCwbQgk5lDWr+jF8DlF3qJ7Qu5zcIdZGhc8mtSshCFMGqNxwi0RpYeNQMBTvXU45B6gNSvEN6p4SJehdSOEK2pXqm2hZGLURDL1PJU7KmRcALc2kNuRL6c97ieoXUvpYiiNUluUobZ4q2jah2iaVpboH7HfVoHgfgDZV2NmUDNCFlOykBvHv/tT8bujz2n9/U37UW/76uX+ZH5lOYtFzD3Gmp+kFtPu1cmJot78REYxT8K+CL3I05RRYcjZEcIs7MQvBkRxuOXILdV0FLsSAcO5JvFWBADksSnyg6VJyuiy8e1W+hLpfErGl2AF2gzauiVD9Vc4cOaqyfCkIFPdLSzFQcKqc4hnIIFLr3FIAqLJTSAxVgQi1efVZWoNOtVcKONWaWjQFnNZWjZBQIe0ABKEpGT2dYOSwqqgmBRuqv6pOQ1tRtojesRJpyU6OHiGEV6iTynaSCKJsEbWXlEBaCURUT9Q6nWQfpBBENiMhgFSb8tA1lO2kTE9lO1FEUT4blLsKjqkdq1keo0etzI+aqHeMmcris1R3lPhF5UNZlpNi6nNK/0iozUdSBt6oPqi2jWWTkkEq6h7VX+VLkn1Q90ttUC1/RDZVbSBI11X3S6jlVOjZlOiPy0OJdoViqOgXqE6m2M5egxv9iWejQSAlrtMzq3dMs0qUTaZZZNQXR+geOpFI0EvUu1hIMX6/ip6v+bf5ET3/5tT+Ez133nplWPfZF4fbrlkfuFfsfyx67iHO9DSdRGYTJExFo8BFTkGBTlI6nwnZDxPfqi7Crxz2lAwvchKVIEb3Jhfxh0B4UGQtpux6S5kdypGijFclkKaIXwRmO9WgQFhNF8RgT2TzStESoPJR7w3XulXO8iydWp1hUP37EZFlSSiHnzb5UGIUoepeyvtBYU8Et1SOagdX2oBH2Qwqh5Q63QnIzirRE8tBBM2TsCGOEifpuihqRMB2yvoA7UKtZUntVU1ZT0H1AbNFTTGm9qKCJxKkpPAE9kHZTipfFYxTuVP2Z4REFNwhugbZGFXmVFdxk74aVKdkxhTZTxGMoy0Qn8UM8aHqu1AiFz1DSjaZ2umdptaOHMz9AoVZKguQ6qSynVRm6rp0Xl4X7IvqX3FQRfg6WJ+EKJ3SR9PUe+VD0WDjoJixk7Ju65RYb5eggST1bP0A9o9i0IrsrxrERGFb1Enqh1Rdp7oj1xSH+puSTalIuW637mEhofyt+Z7e/ppt59Zno9f+6+XPv157S+1/U9kRbrn0krDpjvzXGhuvvzFcfEr+S41MuLzqnvovZ25IEjBbRc+7wnVnXx025/9WZ33Y/PkNYW3+Wz9i0XMPsehpOonKEKAMJClKUOAjRCcKiGQmInS+KUGscoBJ7FDrheHunGrklpxH8Vm6NxU04y70MNU1QhkCMrsK1j9UDg8GLglin8xeA6drYFgIAjSKrgIiCvaEoEXPoZ6NptOrNoSZHSnBPASWtHt2BrTNiW99Kz9qhYIGla1E710tMZEyVTWlrdC7UEIbijsJokQnhLYUcPd2aQeqdU+1bXo2VQ40QKWuS3VHDgJB25Y79EPAmZLpqbIWaVBF2QHqx0aPhCmTNWgzJVUnB0ar9Xr6SSE0QJkp+0BZbarPozZE03gjJH6p66KtF6IE9WOq7mDfL+zsBEwvV5sA0XOo90ZtgGaJKEGYykb1r1RmSoRBX0X4ZlRPMfO4Bt6vaCuUAa3EMxIXUwZVZL9AvqD47JN3/Ud+1EQNzNMzq7pH7UqVA60NKQe6ASm0kR8nPttLQasTWYA4UCJ8dcz0FP0NzrhRfR74rmpQnAaBJr7JfhjVdTVQTSgfVbXZ2aJirJR7myuq7hAp9Ymuq2Z+zLvo+a/nxhuOD9jTn39z2l/kd5DCXeG6Sx8JFxRC5p1bwtqL7gubbr4inBulhNrvp70nhBs/Vxcmt91wQdgYLgtfufDkTGjdHSR6hg99JFx8av2vU683H1j03EMW3PR2cMb6eWRxMUNBqOo4cX0c4VhTlqVa94+ccDUliUyEEn0oMFRBAzks4zBFP0Llo7J0Aoh1ahMhagPKmaMghaY+R2jdNBU0U4audKTAQUtxrnC6eQ1yamWQD2K12iiKnP6ULkft2kzXUCItZYHIqarAJGw0gu2yBtV1tWQDvWPl0KLwFB0kgMpMBVmznToaIbul7oHaFQWbCto0LNKtqfC09qyyA5gxKAIUyoSl3ckjKLgIO0CBocpIo/tVwTjZVLV7e+act6FmC5DNoGUYIrhhirD1JPqo6e0ktGGdroEbGal+DGyfui59dlLYhwlYS1jVMwz+Rdukna6HRYaiugZBzywDdLBHatAVhS6oe1IQhgFT1WeiPyDqE9kH1V4R4X9T8K/ul4QRVUco61y9d+ovpHgGzyHLAd6bejZc/1MN7JDdEu0K647wD6UvCNAyRarv76XoqYRiLAfRXqkNyEFXGLxQsSZ9Vvq+UE/UdamupkyFl9nHyp7NEbpf5SdjPyQ+q9rWbJH9WBfqr7Kz8717+2v+9S35UW/5m9M/lh/NhVyYzLM9oyj54WM/GK4/J9+IOBNF6yLoaaKrz4TMm/JfIo3s0KroWYiiW69eH5aL6803Fj33kIUmelLHp5xE03tU5zJ69FH5URMl9uGGK0LkomuooIPEL5V5Q8GXChbJqd11/wP5USsoCIgpSbhGpuqkyfETI80kIKtF8XEBfDH9iQRSudM7ONHq2ciRojXaIhRMqKCBnER1D1gfVEBEQZUItAjlhFP9w+ApwXFUgQSVgxKCkgJkuK7KjFKBIULPLOwLTskTTjUKTCnPm/Au1DIMuPu1uC6uWSZsMtUn1ZdSdopcEgBECVX/x44+Oj9qojJDxh+t9gsjYmOglGCGAkOVoYvZVSC+RSjDVq3pSahywHassgspuFU2DgJv1YfQ+1RlRv2uGjjAwUYlFEM/pvpovIZoF1Tuqg8hG6UG2ajfpLqDMxAESiwhcF3HCLxLZeOUYEjQmso446cGtWP5bHBe3S9lgsvMd7iGrP8A2r0atFSAul+qOyrrHGfsiDpN/ZAsX7APchPDOYpRKShfJSX+wxlCoi+lpTLUmsGY5ZvwLpStJx9mSizpQnVHbeqaMgMF27zyrRLqA/ZZom+ifkzaIjif4g+kQL6V8lvvvvXT+dH88Oovz4/o+bdndED03LE1XHTel8K6LNOzPvX9tleWRM/i3z9a+/f8VJn6VPhVjSnrs8n0/PCx14YPnbPcmZ6LjQWX6QnOQi87XvPMqAB7bNWx+VET5cSQs0yCWoQyPeUaSHB+9HCekjT+TdhMSQTYOC1QBC4kkKpAjVDOMjoLwjGhoEGJiHRvytkgE6wcNLXJzGyRIjiUjwwaRqqfVU5tSvciMxIAymZRmZ40gkyBi3w/YCdlAJggiCnxi0BbLeppirNN96DsAKGmSeP9CgeaAkOVraQyMAiq07KOQfmkrPOmMpLpXVAWYQbUSZllBs+hhAYKJsbFkgu03qjyE0gEV8IVtTfaeC8yAjZK7d4+vAwCwCnuS2egGcrBGiVsEGDj1JqpZAvUZ6k+4PqWEfIfhD0bOqBqu1IGSpIGVUQ5UvlK0RMGMYfhGWQ9hfcjpx2D2EH9XSRFCFK2j6A6opYqIFs9LGbhkA+FWdU16F2o906+q/S3gJQ+RNp/uAe1szfZZNVH0z2oukM+gZx+Lb6vl6CPKnwSGlCmtegjtHmqEgsp01j1Y1jPVH8O7W1CLq9TvYay/1N0DRG7oY8qPkt1SmbdUt8v6tNCivvV0lvzPb391f98XmOtzYJe/P43z7+5/ssesyPcctl7w22viCJn9J/qouf2DR8JF58SvyGeeibRsxBJr83/fjZreoZw1uUWPRclC070hI7er75/UM7GGGR6qsB9AjLKlFOLIpcK1MAJUZmeGACqDZLgOdQ94Ii5CqjgHlRGD92vyuwgJ0Zm28FnldhBAc2uBx/Kj1pJEX0IJdISKggNEBiqpRHmCk07jlAgqgItPE+CgLCH5FAqkYt4+p6v50et0BR51bZxxF4ImeQ8qnVF6TlUIEEil6rT5NyrYJHqmRLc54rKDBmAgH5IZCJSm1ciLZaDEkDInokgiZ5DvTcaQFFiKt0vDWRFqO4o+422XtgMEm3UBjEkNAyMiXr2RLXND45x+U49Xq3rKrCk+qDKjEQQZWdxYxTR5yFqoAODfK6TNL1XtaGkdwzvUy5TAXaZbLoUkqBOK8GdbKrKsqQMc8oYj8xZaBA2A4VIIXZT3VN2IOV+0QcSfSkKmUrsBl9HxixwXgmOtNxBykAfrf8coTakprf3EjmYC2WmxHXKrFa+CtUHVZ9ShEGqJ6o+0Pcpf4n8EnW/NOik/Bp6tiFRd7D+qvgG6q8c8IR7UDHEXPUAWb7wHPRZNagy76LnP51Xu+FaG4rl08Off/uCj+Z3UKcyzbxEVWisC5abVl8atl34nJZzs8/0LETS5kZIu8v0bEynbznXX1j03EO8kZHpBWPHwDRGIbSh0y+c5ZQRQHKMlXOU4tSSo6kcVVpXUQnF5Gwop4A6ZLlbK2QSqmmXVGbKKcg6uTZUsCgXYweoPpCIHiGHQwWh6HyKaf5J04GgfGSZgWOs2gWJTPQu1bpeajMOgu4X10CtMby8uuC/aleEqv8oSohunta5ku2Vsq3F+0FRQrRB2gBCBqHQXpWIiPWJyiYC5aPWnEwRD8huUaZdBO2syH7AgDM6zABl86qAiq4hp6xDfVBtkMRQWXfgHanrjhwKyxKIwY/BpdX3NvUDDrAJNXin3hFBU3nVGq9U15WNon5XBeMk4qm1HVP8BCx3JfDDu5/YUV2TPELiIg1IqPuiEEctH4NL2Cj7Am1F2QESGlS/QO9HtVcaDEt5P7TpjCJFjFK2iKBlmSLUR+OGljVoqSM1RZnstyoz6kvV7ClqbyobEoU21Va6BL1PNXCWInqSD6N8ChS6RDnMVjyLYFmKOkkDILgEQg18n6JdEEowp7KUa5CSv9QH4vpcUb7vvE9v/6e35ke95W9f+H/yo1QKwbO6oVDamp6zy/RsFzgr39FnWPTcQzy93fQCNR2ZIKdAiWSjMAVFOV2ULarEAxLrlDhDQd2kmL5HgbfKAkTEs1HgLqcugyMkM2kpcBdOIgkulLUbGX/o4fxozxg79pj8qBVy7sdhY6wMKkvRjSQJZaqeAOREy/cGO7Oiwy/uCx1zFQCCWKfW9aKMYiU+0Np4YyuEbYDnUEEH9gEq6IDPpvQhSoShwF2tf4vBohAECFVH6F2ozAPsS8VgDdpksUTFkhPW5Ee7BwcpROCjprsSQ5SlI+oDidgq8xfrn7AZVJZKWByFNoDiW42hA0B42smfpWdW9YzshmoXZA/VupeUWU07pEfIdqr2RoN6o4eLAAWEAvVsVCdxA7YadL8TO/nZ6B7o71VGG9VJ3E29BtUdJT5QOSh/AOuTsBnoD4ipufTeVP1X5wkMC0V7RdRn4R2pAa6UwSVCLRFE9yBtPfTzKmTG9XZFW6G2qaYzdwuyW6p8cdMu5S+BeKXqOrZjUU/JPqikAypLNXCGg3qiz8MlE4TdoYEOirsyqA9RmeuA6ptowEgNPs83/Sp6rv/H+RE9t75oT0TPqlDZQuLu7a0iZ+3al703bJp5hzM990YWnOiZ4BSb/mHJ8avzoyZqehkFT2p0npwbFczQ+RQBTwUNFKTTKGaE6q9yNhBh5sgxSXL8xLsgZ0y9NypfNT2SduhPgTKHI+QQqgyvua4rqlBBKwJOohpFp3pCto+c9QxwKKdkPQWHXWSckBirgi+qTyo4QAdY1P9JyDJLyVbCdlmD6o7KBu7W0ggpkKimpsuOwRIRyg6ggCeEVxLS1TsehPMpQbPaPIREGxWMUIaiKgdsQyAARsglVfcwBmuQDoyyHZmZhPMJ7q+auk/PrIJ8qlMqw5HKYUpkB9I9qIEOqn9qBgDWSTXgA8+sfArqb2h38Qj5D7TGqxTc4bz0dWD9UCUS0NqFSljBTdXUPZD9nuZ6ShmOymbQeulq6j7dQ8pSBeoeUEQU5UD9tmyD4A+oekrZjGrKLz2H8otaFuXLkbMbEgStFLAchK9OopoSz6he04ZFEeXnElQO6u9JwEvZZEyVOZWZyqhX9YQgH1P63/COJkU9mxab2c0WaXfgftWAJ70jpSfMVmLqW9HzSz+WH/WWrS/+8/wogWy6+pZwe/5rg/MvC9suPDk7zITMq+7Jjps7sSvyrNE74vGasPH8EDZvf+Y1PTfmO8X3KxY995B+FT2lcACdt0XP/kG9tyXPOjE/aiJFT3BKlYhCo+uq01HBE5EkuMDUTzkiDE6Bqr/ocKtADZxSKZ4BapMmDH5U2wTHYtfDnNGppsTNlhSnS027VJmLBL0LFQhgkCLKjMQ2+d7AqaU6qYIkzFYVQR0uwyCCDqojJIRGKKMtRbBR7QozDIRTTHZAlQOVmRrQmCaBtMduCQqRoj5QPRsWQiaWpajTJDbL/hzOqw1BKKstRSCV07fB1j/94IP5USvUt0jxAMpd9WNLjqlmrlNGZ2T6SehDxkX5Uv0T9WFgqPrZGaHj0MCkEqCpj1bTZVPsLIqeCWs9K1BMEr4DLbmgZjfQs1F/pfwBKgcl7mD2mmiDlPUl1/+kbEZVn+C8Eh/o3pTNwP5GCUzw3pRoRPZBDYrjuxDPRqKc6kvJh1LXRWGQ3nsNFIJE3aE+T248RiTcgwLfvbguPbNsFwl1Ej8rfApa11yWA/RZ6r1hmxXtje5NbaiaIlbTe8PnrUH+7PgD3JfSUgNKpKU+WvlWSfUMbLJ6xwTVUzXYPt+i57ov/lhWnWKxxbuOpdeL37e+ZA9ET7NbLHruIV7T03QSlTG1z0nPyo+aqACQduRVQg6thaYybygYUTtPowMBI7QRGjVVHe8gTRsTThdmpAnnnr5v5AjemR6DSLEBBK0HqBw0Ov/kf/xnftRKirNBjoVaSJ1ECbWZEm3WoIIcCgynRT2joEw54eTAqo1nMg+iDXLYUaiugWUu6mlSBgdkgI7AOp8RDFhFGyTUFMIhcjShvCIYsIq6RwMwKWJ5UjkKSNyRO/wnlC8tMUEZ+RlQllSfImSjlIBB9VdN9aO6qu4BpwKLdkEZeCoTKyWriKBd5SMjK6rvc2CAXdqZ8er7nJkS5TtRLR+V6Uaij3o2GsxS4mSSYEJuvKpnYDtTgnFZJ+GZVTumgVvlf0yC3aDd25VYQuVLg1MREi2V0IZtSNVp6oNEH0LlOCmmt9MzK7s1CANqakCD7kH6L/B9ql8g4UrVkZTNAmkQR/m+JOjKvh+eWfZ5MIij6hmh7leJi7NFtVeyD6rupGQ44vcJW0Rr6KItq4E2VTwbDfSpdkzvSNnkFL+EfGJaxz1C96uWPSGBkwaOI6qfJ1IGQruBskXzvZHR+i/+eO1/a/UsUyVjfYv1s/u/b32p2LXIzAmLnnuIRU+zp2BGpeicKANDiZ6UTaCmUlLQoTL7qKNXThA5aDRlLILOnDBHOOovHFUKJlSmG01ppuAggsGtEhzJoRROF92vnEIO5aMcNEJOY8w621bUdVMyeuh+VX3AoFU4/EMwtUoG2FBPsJ6KOkLT91TAOgmZWDjlvQYNHOilFaDNiwAbMwZVAEhtRdRpQglBWJbqHmBQRL0Lujf5bAkBCokd0nZSO04IsFVWKAbewh7ikiHisyQeSPsNdSolM0StK4pZqGBzIiTEq1kIlGmv6g4FkeodY+aYaBfUL6i6R1PZ5drJwMAQ17OU9QTJ1xgVA33YZwmbrGzXbFF9CNUpKnM1sEPisfKhVKY9AuWg1hmnrGa1VA310Ur4orqeNKgihExl1wkSylJERHW/2EeLfpdsn8okR99MiH00UKemdZPYR/5AJEVE7CXqvaFNFXWHUO+N4pChA7kNUX+h3ht9n2pDdA9KAJyiwQdVf+EexlYdmx+1gvGYWvZEJFoQKIaKfleVT6/oV9Fz3e3n50e95bazLHp2A4uee8iCW9MTgrL5NnJ7KzLQAlLWjyORSgWL1MGoNWR23Xd/ftREBQcYNCuRi+qkcI7IoVQ7LE7T94kAkNqAyvQkkUqNxpIwIp17ctDE/dJGT6rMiFExjREzb5RjTnZDOFIYjIipQ4O0Ppmo64TKPqYMIgrQ5WACBeNCcMT3JsqGRuxp3djI6OHVOqkCVvo+VZ+oTqr3Tt+nNh9RwQhBdkvuegvPkZIho7JecCd94YRj4K4GNED8UoEllfuI2ISIRDXM2q2RkiGD7VXUhwmaWaD6JrhfZQ+pHavyHTuymqGoBBsq9+FD+Lphulqnpn7A/RjZzkmRtUi2SA1wYVaysiWUUS+EVyoHtcEGfRbrUwTuTQmZJMSrfoyuQW1QiZ7092qZGPLN1PPSMhfK30q5B/JrVFvBjUpEmZNPovobepfKJtOsFikqg62WPhTUPdXn0TtS9QHX2BbmEMtdtEH0fcV63nKTpR6CZan60gQfkzZKo34wQnVVDRyQgDco+mhKABkRm7VhHCxkEfJdlU9BwqkaXKL2IjM9YZNH1ebpHcvPQl1XfmM3ULZz3qe3f+Ft+VFvue3l/19+ZDqJRc89xKKn6STKmVO7bRMUnC45mtdwpOBUjT7jWluq7oA5kRsZgdOvRljRYReLmNOGKUqkJUdeZkPCOyJBOELTlCfEVCea4oOZOzVmwGFR7ZiChiGaFliDyleKnuDk0XdFcC0zFbiMQz0RDiUJAuoeZuv4qcEIcvww268G1V81bZOcTLWrMF1XTWemYE+KkNBe1WfpfpXoSe2KRO0M6pvEGlUykAXIiVb2hUQfJeDh1DnxWRRMElwuJWRSmangC229CEJxcEm0V6zr4v3Q96mMTKx/osxorVtlt0hMHV4uAtYfVOuqynwnu6Om+dO9KftAtg/taQ0MhEX5EkvWHJ8ftQF9ixyYhPJNsZPKpxj/RnVTP+qjVTmOP/JoftRErf9M5Ti4vxj8AHuoBmBoRolqr/QcUnyAtimFV6h7StAlEUbdAw0Oyesm2CJsb6J8sR8jwbIGipPiHtBXEc82Df6LescpmbT9QIoAPQBCukp8wIEd0T1S/RtSbRNskbpf6rPUGpl0D6ru0PelLC81KdY4xg0hRT2jdiH7my6B5Q73qwZK7v7UJ/Oj+eFVn3tbdrvlouzF77edbdGzG1j03EMWnOg5z6M4Zs9Yctyq/KhEtIgAiV9yKiWNLApHjDpJNVpIHbLKJiBhTwWWBGUYRFTgQVCZKaGBHCnlQFB7UwEglbtaB5WuK+8B7ldNHUKBSL1jdD5F9jIFsqLLobXblFBGTrSaRo67fsIUNZUpiru6i8CH6pOq01T/lThPmVEqoKIgVAWW5FjLKfZQf5XN4KwX0d9AfVDrf6b0WRj8i0B4GNqFCpop8FZiNbpXShCAcpfiZEJ/TuK4quv43lQGHtybskU08KXsNIm0ynaqTFiC2uHQ/sImf79qM1R9oLJUwmuK6EkCp9ywAuyOEqBJ8FMb8uGAhMr6AnukRFq6rqrrI7PcHVz1V5QxReJmBAcvhJ2lvkkOvIn2RuA6kGIt4mHIikuxs/rZQORKsIdqQANFS2EP1W78BL37pEEgUXfIT1YD0iTAyaU2YMA/pW/rNSlLV2Dmu/ATMPtS+Ie0HISalYX9sehvcDCA/NYa5HeqZ6M2hGuY1kDfSsy8m6HsblHPsP6KDNJeQm1TZe3Ov+j59jBQ+7/oy8X7nqn9Xy9+v+0Vf5rfgekkFj33kIUmepIBVh296R9Q9BQjloQKLCkQltOMyEkUgc/EN6tZGWoED6dLCceaRAkpypHDIhxrmgqvdmCda7a0CogoiJSBMAS9KVOl1D3gBg4iUCOhTIqetU68nRShWGYUAzJTE949BePKsaf3k5IVOrGDsyHp2Whnzgi1V1X/aZMy5fAn1Wk4r+wAla8MJMBhn4Dsrgi5K0oQo+9Tgg0FI3KaHZQ72cgMqP/ys2DXVXulZ1aZLCjKCUGBykftqp3iU0x8szoVfvggzlSm66o+ZPQwCJqhzCOUfamuG4OPWZNg48g+yGmMEIzj0i016LNygAvsXMp60wp6Znw/EairaqYJ3UOKiD4FAqdaG5XEKLW5HNkizMKqQWtQq/tFOynqI9lO2TeRMCJsBpW5mlmgxGqEbLLyzcCWKNEfB0dFaEuipxJ02Uedvf89rQbvaHBJlEMvUXYvRSYgP0rVdbm8wmxJ8GumnhR9HvSF6l3gevbCblE/pmwJtcPJ77CYSr4VDtZE4L2lJJZ0C6pnyv+e7+ntr/q7t9f+tylGFjR/j2Xc+X//u1dZ9OwGFj33kAUnepJwJUaHTO9RgTvuqqo6+oSgmQKflEBYiVEY7AlHipxl5RxRpy6zCeD7lCBA96Cce3KM1WgsORvqHdM9qEyUMFh9NrU2GKGmvtGO7Opd0DOr7FgKXJSDhpkOCc6nFLYpaxanVXFXSNlkSuwj1BIIKe119MjqNF615iTaB9VewWFXohwF46pd0XVTMiLUbsXksKuAleqT+iyVpVo+g+5XDRBgdqF4b+rdzxrRXqltKqGCghHVLzx9z9fzoyYyow1EJvW8dG+jIhORMqDlIBvYLWWLhvalARgu3+nxqt2RQiaUJU3fjtCGenrgYPb2iDLilf1OqZNoE4VNxTakhCcI8kkQGExY41sJFdg22X3BDZJUOIUDqcovImFP9G2UtasyBum8qk+UQSrba4J/SPVB3i88s2qv9D7Vdck+qH6M7jdliZUpIVz1MuxOEddTPqugeiKXD4DvU4Ijtk3x3qgfk2IfXUO8nxTBkPx9OTsN+k0levZDpmY3UPbla3/z1/nR/PCq2zbkR73l79ZtyY9MJ7HouYcsuOnt0An41fc/S096Vn7URE1NJIFJZSvR9I9dDz+cH7UyDJljcmdMcGLkNCVwbtS0wGmYUjQo1qeke1AZK7TounT84H6VQESjlsqZIydGiRK4YYUo3xRHlaYZ0XdFUsQkcvJU3SGnNsnZVqIyiOP0bANqKj08rxR54btU4EMop5jEamm/4bwS2qiuy8EwuK4UmGDwg9bWi1CWjhI9KThICZpT2qsSPbEsxbugnblHRSY5PUeKgDcCm11FMKAX7Qo/KwJLesfqXdBnlY2je1MDUfQuVKbbIBTlzDg/G73Pp+67Lz9qhWyfsg9kz9QuvbTEQ4o4Q2vrRTBLWNhvOi/bG5xXn6V3pD5Lz4afFXUaN/sRfSbVSRoQjNA7Vu+HbLXMooXnUO8HB3iFf4jvR9h62nWcZttE6N5w8LsG+aPjDz+SH7WCQrHwzciXlANRNBArypf6EMpaj6S0lcUM9tEJg8QpdEKk7RZUH9R0fPqsTGYAWyKXEQF71K13kQI9r/Lf5zvT85V/Oz+i52dfbdGzG/Sh6HlXuO7sq8PmMzeE265ZH1qSwXdsDRed92h49+c3hLX5qfnCGxmZXrDPs0/Kj5qoETHKLsGprjWog3l827/lR63s86wT86MmclorOJQqqCPnSAltNO1XBRi05pISMCiwVDvZSocbQGFa/D1lwKl1o4iUkV8lqlGmkJpqjcG4yFqh75NBGQQYuL5ZDZompAIMqqsUCKtsBCwH0W2mOK9Uf1WATaKPDLCpHEUdoUw55UDTsykxleyA2qWaspdV/Z+rw64ca9qsRK3TifVX1AcMsMU9kNihypf6ACXoYp0U9UEJhgTZHdUG6d0ru0UC9NiqY/OjVsh2jgrxd+Sw6nUnvsWDbFhPhI2jZ1YbGWHACnUkQhlllF0YoXtQ68dh/RXPRv2Yqr/4PkW7oHpN9TQixfE25GANCVdqyRFACRXU3tQzUDkoYZCECiXukHir7oH8/Wkx5RenHYt7SMrsg3qmpqzjOsui7lH9TxGV1RrSeL8J2X5qoLuXpNTJXt+v8hsJkipSfCAFXaNb5aBiC8pgHn+Ik1DwfkVdxxhL2cl5Rs1Cm3fR8282ZvU0q3+xvtZ+Nn+vfSA73fl//7tX3xi/3nSYPhU9PxbC+SFsvumesPH6G8PFp+T/ZNFzt8TGgpChFAGK6R+WnnhCftRETfGhtR1HxJpaFNDLzRfAOVfiAQXTKhinzleObkK9VoEATd+TZo6Ef3HdKcg+U7vekrOh2hudVwHRZEKwRmU2etTK/KgVEhrUFECadpOyAY8MiAD13ihIUWuxYkaxCjgBDMqEnaW14nRgWW2DSmgmp3ZYbOKCQahwium8DBahXUg7QFPZRZkpIYegtjJbUSQVNbhE54dE1nl0YCsouwV9tFrnCjO0VPmCeDZ6hAi+yM4miChqgIuyo+TyARD8qLUhqQ8ZFkIxlbsKbnGQWNhvqg8Tj/Fgzfij1Wxn2vU5gjsbT0F/VYOEU7VDOb1PueQIlY+wydRfKJGK6pkaSMVgGO5BDuLDM6jsQhqkU20QMzJFOZKdVHUP+13RttF+i/dDNiNljUzVj2E9S7jfKTHQTQObsp5CH7DrwYfyo1bGjjk6P2qiBh6ofGR2IbxP2ZdC3emWGKX8b3pHnehL+yHBhu5BzppIWK5grqQMoNDGTRldqjtkk1PkIdkuZvnu+1X0fMVfbwxxrc1izc1e/fzsay16doO+FT1X33xFOHd5nvUZ1ofNUei06LnHoAijOmTTNyw5fnV+1ESKiPA+VeD+9Ne350dNlp6wJj9qBbOCEgIflVVBTq1ygMlBUxthUFAnnVpw5GkXzggF40rAIwdYbowCZTapMnrgHadkHowdfVR+1AoFE2rq2wSIv5g9VGMUps2r0XncaERldpAjJQItDJCpHNW7hDXhlACi6hmB6xHChheRYcrAU88LzqdyKKmeqrU36b3J61L5irKZ+Ha1PtFyFhnwbCllrkBxXtg4ErbV9GucGi6CAFXXCbJFKcG4st/UjlV7pYEDOW2Y3pG4X+qzUmYsDO7Ldmtw/6qdnf4+19/pJ2efITNI/Y34LK2HPCkEUrTJwmfDzXpGWKymekL1P0J9qRLrJnZW+3laGzKCtlaFItTm4e+lMA6fnRAzCGjDIQXV6WHYaT5C9VT1N+Mg1qkZGmgzRL9A36cGmWkQR/Xxyj4QVGZqNgdlKqt7oGebmeR6SgzAbIMMqJKq70e/Ufk64BOr+tBL5ipcRdQ7IvohBqX6q2xcCnRd2Y6hz1PLAVH9k2VOz6HeMV23A/VhtvRtpudn3pkf9ZbPvu4j+ZHpJH0ueuan7twS1l60NZx1/voQbgoWPfcAEsq6NbJoOgfu3q4AZ1dlKaR09CkCBgll6rqUMaiETNoVdUBsXICdeoKZS5kOpEaPqXyVY0JlpnZMnoHMg5SsRSV6ki1QDg/trK26ERLSZUaPKEuCspJlUAbtgsQHFczgDq7iedU1CCU0EJShq+opiehy2jKUjZp2THaA1sSNPL393vyoicqGpDJT0w0xCyoBVUdop2u5LiPYMzUQJW0JIdobQWKHCiCpbcu6B8GMWpOZbJzK9CQ7qdoKXXd0RXUjrwjZKPWOR1aC4PgNLgcqy5S2LcUOWspEtDey6zPToh+jIFTYKBKTVD+G11X1lD4rBDhqQ0owx7oO70L5OiiICd+XriHXUIdrqKyxERBDlXBA5aA+S8+hxAPsM8W7pD5a9c+qPydorWY1MJ9iZ6ldyTpNdU/VB7iGylAnW4+idA1KBEgZvO4EqiyJlFiR3qfqm1Rs0EvmmuGooLYl1/QEG6P8elyaSbVBeg7lu8K7oLKJdEM2Uvb7a5+5NT+aH17x6fkRPT/3eoue3WBhiJ4ZO8Itl14SNt2RZ33mZ+eLhSZ6moUJrWWmHCly7lXQTY6JEhTI4ZYBClxXOdYYnCYESZNCGKTykWtkwr0NiSnG1CkrpwADLeHgUZCi1tybhGmiasoX3cOS1cflR63Qe1PlS+vzqamUtLyCEiVoDSO1Lh06bkJomG2Qo94P7aSfkl2oHFKqT7LuwftRgiPVSRW0kMCUkgmgBAEK/mXgBPZFTe3CexD1n+yWsodUf1W7IibEeqXDkM0us9+gTin3jDJLqY5EyP6S6B+hjGLZLsC+qE3KMLNOPRsMfKkMOuov5KZHB1SfY+p7oq5D3VH1jOp6UpkJW7/rgQfzoyaqLx2EKf00RTkDykxu/gR9qcoEp6xBOYgJ716K4NBmMfNdlA32xeK76LyqT9iPifeesrwU+WFSwBP+BwL3Rtm5ERykUMIrDMQqu4WD4updJIipVEfUesiBikyVI50XS0xQpqcqB7VeYy+Zq9iHvkMkoU6qd79YUWu2U/1VGdDUjlPem4L6txSxOwWqO8p/mffp7Z/6iXqdjmUcq3Ys6h78/rnX/6/agek0fSh6LgwW3PT22Kja8KvvfzDTM0GUUI4fdbJKyMSgTjikJOApgQizKoRzT86yCrCpHFLWrpIBBqCcNprKrpxEeg4VNFN27Ix4b9S+abmECD2zCsZpZ9dBkXWL606CLcogeyRsFJWPeheYKQT1V/09rUeo3g+dV8L4AA0QiDpCG0vQFOcIZRXJjWuovar6RCJigi16+l7e/ZrWYpVr+9I9COEV6464X7kmJ4D1SdQHEk5VJhbVSRJsIiTEKFEChUxR16n+yTXPoF6rz2LWohoMAFs0sqwqHkcoUFNi9dASCOom+L1R3RlcynVn8nvV/k22IXhmtekR2XoFtQFVp6nvVwOpKX4CBsiiTtL9qv6c2gv9vWqD2N8k2G9VNjToJO0hlIOq/1Rmsr+BdqzulzIyd8EyRxEqXyXYyOcAqBxoUDFCfqPqS+kdq8wxugc5QAB2YEaInpQBqu5XbuoHdCt2wzaU8C5T6nq3UPeAgwwpzyb8MGljALo3VScHaA1dMdsF+27VLoCUZ+glyi+ab9Hz7E/+RO1/a+Ub21xWzrHtdf/3z/+QRc9uYNFzD+nbjYzASY2g85qQrWS6CzkgkdGVR+ZHTWSAAteQUzTBAZCONZxXghjdg+zooU6qNTIpu0Q6V1nH0YrcPAecm5Rdm5V4QJkz6rN0v7QpVYREELXjPQk8VJ8iJB4ooQzfsaiThBKVaW07JWBQ/RsW6/5NwpqRmNkk6hOtYZoSNKt2hY61cOIxk1Z03TR1WTnxSlQjsL2JeyBhRIklZB9UIKyyzGaLsrNYf0WZUfaasrPUhtT6lGojOYJsp8pIw3chMjhGl1cFaFV3SOAcF9PbKXBX2aYkcGJmXw16b/IdH1D1d8Yf5PpP9lC5yjQgN/4NFjVooEKteT0NQvGQyKin9qLWpqalI9SyJ8QukaVG62GqQVfsx5RQDBs9DUEbVPWU1sdWS+NQ5rp6BhTPRH9Fn1WCDbXNlIGSlJBOTaGl9qraFfk1ypbRO5LiGTwHTe2NKH+pGyj/BQcmRfYaDWio+j9XVExISH+WBh4SMslpXfSIGuggMF5QPnUPUXaH/D5Zd2jJBCGuU5uXfhy0IdWOU/QAarMqHpvtZ5WdvfvTn8qP5oezP/FTUZIspMme/fzcD/9x7X9Np+kj0bPYtGh39HJ6ezGlvv7bWZdfG64/px4ULLhMTzDMKhg3/QNNR1adE3Ukapdecs6f/M+v5ketUCaWcjYoaJA7uJITI4JxyoaRnXSCSSMHQAYY5NyL0djRFdVyl5kHcF2V/UPTaNWO3ySGqt3bUXgVWVtUd1KEeCVkYgAn6hk5eSqrAu8BzqlMANoFVgUSmJ0igjoSOGXGCXxWbj5CzyvugZ5ZreGIwpNw+Keh7sipUvDeVABIGxylTMFSyyVQnVT1id7x2FEsGpF7peoO1XW15AhO91b1n/p+0a7w3kQwQ+Uul0iBclCCS8rUfRqgGjqoWv8jA8PV55j6LpcZramsAlaqJ+PfrK57HEmxOxjcij6PxCC1KRqVmeqjaXBHClpoz/i9UX2QdXKWQpnsK6DMkmZ+CH+A2ooSPcl+q/tF/0W0bXo/KX6GGrzGexO2nu5BiZMBNoGTfROVg7BFtHyRtLPUR4v6T+WgyhdFT3Fd8hvnOqCnUHUypd8kQUqJnmQPVV2n96Y+S88hhTbq85SdFdfoBqo+4D2I+LyX99tL+jbT869+Kj/qLZ9/o0XPbtDHmZ5qbc9eURc8b3tlU+gs4zU9TS9Y+qwT86MmKpCgIFIKeNRxClOAwZcYWaQAY4SmONfA4El16ClBDtzbFGXr1SDzp9anVCORBGUSqjKjNf5UliWVL+0EGiGnVq3pSaKPCojIcVOBACGDEahn2lkGsVkEhrPOBhN/T2We5MSL4IICLbVMAJUZPoNA2QFCLqAPz6GmHZNgLrNxoG2r7GWZ0QDQM1OGWIREVmVnSfxKEcRUPcOgTAgCZCeVfXr6gQfyoyZKpEUBQtwv2W+55EhCH0J1Sg6qQHArBwPg0SYfF6LyQdXvm36C+yaaNqyEHMoAVUEstcMpMeWdrkFLrEQGSHgS5YuBt6gPZKOUSEvZm6pfoGvQ1H2VHY7lK56BkPYb7K9aw5SeQdlvaitqFkOKXSdUG6S+X26GB6gBU+zHhJhK5UAbIUXo+2T9hz6WBtMiVD7K18HMamU7YfmWFBGy11BZDo4lCPxKwIMNStVyTThlXZRZvyb0pNjkFHum/NF+hXwrlQxx9yc/kR/ND2f/5btqN1c7iKFiD39+/o3/s3ZgOo1FT8HOW68M6+5/S9h24cn5mVYseppOooKkJSeekB81kaPH4PipQCIl+wfFL+HMUQaSdGrBMcHAvwYFDTLjBO6NprhlwDOrKYS4g/wwPxtt4CODBjDBanokgbuL16CgWa3pSQGYWndqBLLlVD2j7kWJM7iBgxL4oa7TrvIRym6l62rHHIIvkZUhM1wArDvCDlD5qrZCbVAtik8ZyTJggHal2jaJXyqDlDbCkFNd4d7UPdD90i7tERItlU3G6ybUaRX40HXVZmI0kKTaCgVJSpykgF7VM3oXtJRERF2DINFStk14trEjeQmPoX1EkE4MVe3WxGMs5AyRLRFuNb1POcAFwv/AiLCzVD5QnyIkOKrZAtgGlI2i7xP3gGKouC7ZhzAI1xV/j+KBqE8kPKm2PaBEHyClXxgGG6XaNi7PI0Q5FNVEOZBvpTZgozY4LvpiEsrkzA/wzaQQBJ9VghgOSIvPUt+ihHxa3mFmUtgtEFNTRE/VN9G7SPmsArNFRb8r+2MCPisHGeA5lJ0l4bQfpA45eAftWw0S07volmDeibpDUB1Rgwl3f+qT+dH88PJb3lX731p/E+tPo9vp/u9fOPfD+bHpJBY9BdtuuCBsvCn/JWNN2FS6lwU3vZ2C5gU2OrSYUZ3h2LHH5EclVCABHb3a9VZ1ZrNFOX5Yp9R3UZAvgmacvicCNQySBJgdK4IcWshf7V6tHHkEyuHpr92TH7VCWQoUxEYoEJZrepLTJTL+0AFW7zjFOYJrKHERM+tEV4bZJXS/4hlIsMF1PiNQ5kqoIIdfiogwHU5lFdF0TCVykditsixpargSbMieySnr8H1JAaAoM8peU2VG735KDJSgTVYkuFdkO1UGKQkbKmuLrpuSJSzrJAw8yMwxCOCU/4GZnqIcaNOuwf35uoP7V+vf1E4eiJqZrNaHCVhzWCJsCbUBZWcpg1+Kk2Dj5BRuWj9OQALEtFifbwjuTQ2GpWTHUjvE+iv+nuqvKnNsg0JwRJ9a9Hc4+CEGQXHNYPFZHEwQ/iHa+gQ/TvlFdA+qbeM7EjYSB7pF30T3oGwc9sfivdGGenL2FNyDFFPhHXUrFFexBb63OYpZClV3KDuWbEOEBG9Vz1L8D6p/3SoHOSOE/I+EbGnZ3voUel4pes7zmp4v/4ufzo9iPSm32+7+/oU3/1F+ZDqJRU+kPrV9+4Ybw8Wn1M9kmZ9XrWqsJ+o1PU0nUevujB5zdH7URAltKR0fdeoq6KANJ9S0VtwESFyXHE3lJFIwrQIXuq6q65Qlqaa3kzOm3hvtWi43lYL3NiE2BKFnnn6SnTkKWMfE9HZ6DrmREQWRwkHDqaYiKKN6LbunhLpD18V3KYJbClhVljE50Grd15QlASgzRE1JpfqrxAcsRyWeUZAkAhRiZprfJQaLqnwB1QapninRiN6FKjPMaBPCIN2bCsYpUJNtEO5BTlGmOilsJ9koWt8yQoGsytJBsU9kbVGmm5reS+U7fCiXw/QPquWgAmwqB9WHoHAk7BbZDbV+M4o+anp7gi9HmcaqHVMbkEIZXENmNUP5UKacBHwKVQaT3/xWftRE9SvYtpX/Qp9V7wHKUfkZ1A8psYTqv8qoJ/9OvUuyRcoe0jVUH0JlpmwGzc6RAnRC1i3NelCicsrsE+zHxLOZOtgGRHtDsU+0N/T5RF2nQZVeyyLUjpVN7lvJZo4of/hrn7k1P5ofXn5zIXr2li+cZ9GzG1j0RKqiZ/1+rg7h+vo5Z3qaTqIcKcrMU84nZaKMrViRH7UBQboSPdG5F2aDnF2VKYQOC31XDQqaZRYglKVyIMjBUvdLApHMxIJsAPXeSIB+6qt350et0DWk+AukTG+X2YEJzifVKemoQlkq8YscJOXUEvQMsp5C4DL+yKP5USs0fZrW+YxgexP1n96PCvSSAtaE9ooCqXLMIftY9TfUNmV7BZTdonal7Cydp40pIvjMwh6S6K8yb1BEhGfIgPOqXVG/IHd6h/am6g4KGOJ+SWhQgsAIZNSrHe8po16VL7b5Qb7f8Z3VvkWVA9Vr2d7ARk2KDFK6rrIlKbZvCN69WqaF6hntPB2hNqRsCW2ypAYD6JlJLFHi2QSInp1AtTeC+qukTevU0jiA8knoflOeQQm6E5ANKZ8NkP0j1AfZL4D9VXaAlkZQ09BxbVOV6QntVYq/PSSlf+wE1OZVPSNbpN4xZpKD/c+Avok2V4woe9YNpI+Z0A57mSXcS/pW9Pzof6kVeu0gFnEPf37hvD+sHZhOY9FTEKe3f/jY8iZGrfez0ERPFBRUur/pOcoxocw8FVCliCjkWMi1lSiYEPdLgTtmT9SgoFeJXBTUqexCJWwQVA6q852g6YaizIYPhUxPMcUHBV2RXTVNYpJox1S+S09Ykx+1QlliKTu7qqCbnDxVz7DuiHpGz6ZENWoDJBSrIAmFIOVsQ3eqpklT16vqLpWvyoxKqf+0bITalZWmlyknnoI9ylaNdCv4onLAzSZqSGEPwGBcPAM9s8papHqmgmYKvJXYgXVS2M7xhx/Jj5qo+kTTpFXwRqKCEtpIKFZCA4rKohgGRqv3MPkYu79kd6aUsEgutOjzyK4rP4xmIaj3lkKK6EnvTQ5EQf2jJTEimNmk7ADUKRrwlNl6UL5ydgT4A93K1lN+EYqeMBAQoeWLlGiEYpToM6meKVEaUXYA2oq6X1xmRfU3UCdxlkkNWiJICV9YZuJ+kwTkHiLfG70L5UMBqs+kzQJVe8NMT7KnNega6n7JX0rxJbuFasdUzya/xbO9yFdPGfTqB6juKP9lvtf0POvP50f0vP2tFj27gUVPxZ1bwtqL7mus45lNb//si8Nt16wPcWJvv4qeypGiIL0fRiFNHeVAjMH0duUUEKozpOnpqvMnp0mOolP9U4IAOInSAYa6qjI91TRygq6bkimhRB+6hxRRWW1kROu0qaCZ6tTYqmPzo1ZoWYJdj4psRviscj6xXosyo8+q+oCIdoHBNJxT5TgzVf2sEoRxOqeyySQ0qGcg0Ujs9E6CghSlqcxFeyURRgVUShjpJRTkqCngNO1dtW3coV/UnVFY/1OJiFTXpYgIn1VLjhBqSnVKBinaZLFhFj2zuu4SGugT74IyVmUm7cHVNjD9A7b1E9+p1nU1wIX9mHi28W/yBi8EtdkZsZ4mvTclkKI4KOwOZZOrNX9pMw6VLU3vSPa70LekDMSSraa1GiN0D1LkShCIUiCbPHIYB0FUjnKZC0Blx9JGRHL5Aag7MnuT2rFo2zPUZ4nPUt+tBHdC1gcqS9Gf96vwJH11eG8pAqBKDsB6Itom+h+iXamlPQi8hriHJB9zjijRk6bjq81XaSC1WwPHvUQNWs2/6Hlh7X9jm4/tpXc/b/+xP6j9NJ2mj0TP+vTxzflvmvWNdTW7TX0dz2JDkdbvXWiZnqa/UUEdiZ5KaKCOPmUqjnJilGhJYAAonBhyNuT6ZjDKS1PkIugUqF00qczEdSnLUpVNSqYnXUNtmoGLuSunDUz76NFH5UetkEhFGUERKl+1xiWKvyrzgJxS0T1RPUvZ6ZrqmbovytSUGXgJWQ6UDaPWnOSdnIUdgPqkgluCNkKKkHigBtnIrVCZFhTIdisQUYLj0AFVhxsF+xpkM9S7oGskXVd8Fp9DvQu4rhwoSXgXdF210QgtlaHWe5zrmp6qLyVBa+ggFmemnwCR9mku34mETZo465BtJ69pyAIGiUyqHHCgT7SLFCGH3qfM5sVsU9EuwJak2LPxhx7Oj5qkiARK7J5rhpispyQeq7WI4bOqLybxS75f6neV2Kf6NwDfsejjacMhtT42+Q4ykxbOq/48Bfq+fgivU/obBQ4gKptBvqTyfakNiAEj3IBKFC/N5El53m6Rkn08ITI9lT0isP6JcpjvuqrqyHxvZPSym35mFhJl53/efr5Fz27Qx5me/c2CW9MzRVBwleg5SnDEEX7hsFNgOXqk2K2bRn/Fe8eMESFgYMel6hl0vioAnKBd3ZVTC+WjRIlJ2IWbBMsMuK7qqEk4ksETONxK9KSd6VXGKznhY2JNT5kNAKBgKBxrCuyUsE3i5LhYj412uJWiGjnscE5tAEFrwqmsJAryKVM0Mkhrcoo6TUG+2kV5rlmWKUGSCtxVphtB76ITmQsoiIl6nvLeyA6ojDYqB7WeJq2Np3b+p2cbWc7ZYFh3lGBD9S/hXaj1hSlLWA1akZBDtiFCg3pDB7OtH1xSfeaZXSKAHKnW9cmdbF9IyFT1nz6r1k6mpSdoPcIM2CRM2gFob53IUqN3oa5La5WrAVqqq7g8ibDJNGiVsg52t8QSKcpRfyXsLA2ypSy9oto2Dl7ToG8Nqr/qfuneZH8D96DaCpHi06SInup+qSz7IQNPvQuqD+p+qa4OCt8XZ6CI61KbVTaD3qe6X/Lrey16UpkNCBtF65VSn5kB11X1l+KeXma2KqgNKfs975me/9/P1m64dlBuLj34/fbzfz//xXSSPhQ97wq33Lo8nNtYS7OVnbduDQ+fs74nmZ7PxIITPfvU+Jk6ygFesub4/KiEcGLIWRhO2D1VTm9P6KBo13FZz8iREtelaygHmOq6ciBIrFBZL+Rg0XTzyBhtQCWCEXr3KPLWmHi0Ou1dTpuHMsPlEmqQ6K6mcFP5yqAZAjgVjGAwIdoFfZ/K9KR1z9AJF10hOZRq6hFmOYhpOyRoycCd1vdLCNw7QbfEyV6i7GyKG0TlMJSwE7Oyhyn1HwdQlDBCdUr1IZSJKAYD6N2rZ8MMdRGE0rTU0SOOyI9aoQ1eBg8U5TtGoicE6DVmJqrvYvoJfhdUp1S7oIEV6jMjVJaqzCgDTtpkQPZ58H3yveH6fPnPNihzUdWd2YozyteZAAFE9W3Uruaa0dkJ1IAp9f0pWbBycIpsvXg/2L8Ju0X9rurHyA6ogagpGEhVA9Ioyql+AQYWVf2ncu9E/4j2JaG/Uv4W+TVS0IV6Mij8GtxkTNUdaLPqs1TuamAHyydhs6pu+TVqYJ6SJFIEfpXUQYJ3Sr/QLei+1Ayjuz/5ifxofnjZn5ZEzx7+/Pu3W/TsBn0pepZ3SS8TNxfaeFPvprc/EwtuI6MuOXNz7ZD3RrDjE2W25MQT8qMmauSWglPVkdB7k9OiICBSQRIGKMKxJidGbtQATogMxskpFc4GZU4G4fhRdqFylilrRe3ASu1QZW+SsKemlGI22IrZiwfK8Zv4VjXzEYXFCNSHp+9/ID9qZclxq/KjJiqAmwRHVQWGZI9ouriyh7TcgcqypPulDV8imNkk6ikFdXLzhS7ZZOVYE1LA6CFdKwewv0r0xAEjUY67HnwoP2oycvhh+VErJI6r+k9tRe2GTu+N1veLjEBGvLLfJMqpwRravV1NC6RyGD5QtKHvgy168MH8qJUlx1TXYlWZ4CS2qfo/CYGsCtypP1afxU0+RL9L7UJNn8bBciH6yIwyQA3uELMVIJRPQn3ptFhzkkjJAlRQ25TiGTwvvbMI2bNOCNh0DSWW4DI44rrkWw2KtalpOSGcHVED15tWg6vQx6pno0HxlOn8nag7c0VmvFJ7VSI4iVTiXeBSJKINUz1T7R3fkfgsDUoPDKl+oWqT5WBAl8AsVmUfEu6N/A913fmmX9f0fNmf/lztf6P9jba2dz///h3X136aTtOf09t3bA0XnbclHFcSPuuC55rGxkLzzYLL9IROq9eG3aSzz7NPyo+aqGBxYmd1d3HVkZATLtdlBHFGOVIU7CmHEkeaxbNhBpJyKMkREkEDZWrSmnIZdA3hdFGQnjIaq94FCY60q3yEAkuV6Un1RIkzEyC8yuAJrqHKgeyRGhkngXKXEFPHaB1Tuq54lyniQwrk8Kv6TwKnElbwGgm2Xl2XgpEkO9APAaBYRgTXARZlhkKmKIchsiXC5UJXLOEeVLtCgUnYQ7o3VQ6TkI0u+yYQTtVGI0uOrW62pqY+73q4ul7jPs9ZmR+1MrC0+o6ndopsMDI7QyrABsFR2EOyJSrrEIVMYaNwcEi8Y7KpUvSE96n6BRKF1WfpuimDKmSrVZlT/yiznaCuz1Z07SZSUKb2mnC/cnkemgEjhGqyRbJ8qU6Kekrr1A6JAUQSudTa7Hi/tJREBJaTUDYuZemhFLo1eJcCZW+qvhSFTDGgTEkZyv+gNq98CuqzaLZBhL4vxQdSkJ2Vm4wl2DOyBWrAfq5xv7I7KdcgUnzf+c70fOnmi/Kj3vLFjRY9u0H/rulZ2j199cf6J8OzgERP6kwzyJHq02I384MSd5ae9Kz8qIkKDmitRBl0QOergi9ag05O2aVOVjgK5AhRJkwkZdoltUPVSaOD1gGngDIfcZqpYBzEzQiWmZhqTQGREj0pW0m9C8yUSHC6OiG4UFBF7zKC9QTuQdlvrE/CKab7UgEgtiHRVigAlIElvB/cCbdGSsYVZemo7ImUaYH9IIamQHVa2UMSx9W6XpTxNCUCNSJld2V1vyiCiL6J2pVarzdlc7klq6qip2rbI8ur/djk99i3Gj2a7AB/ltb6nP6eKN+Rqi2YfIzbG9mzp++/Pz9qA2zUFEzVjuCmIsLOUp1UuwrTWqqqP6e1j5WdHNq/Ws/Udcn/IFui/AGaCaE+S+K68tVn4Br94NenxCFyIBbOS2Ecvk/NQqAs7pTlmpQ4ibZTLGmEbUX4GQOwcVjS2qaiDfaynki/huy6shlgv9V1eT17Uc9o8EP0N1RPqF+JoN8ono3sVq+hJBTlU+MAq6pnZH/VZ4V/1ivUwObdt346P5ofXnrje7Iii002llxsub34/e83fqh2ZDpNf29klAmfW2sH/SV4Rkj0VNPLlPHqJdhBxZYFKEe1l/TD6GY/sM/Jz86PmqgRMVzcXwSL1MFJBwKCkdHDD8+PWiFhb1qJHQkCET2bHI2F71NO+OgR8BzCCafvIzEqQtmFaurnGKxXp6ZSUjlQFlWE3pvM9KRgTwQCJLyq+6X6oOoklY/MJIRnk+IZPAfZEvVdhAqaqcxUlgMhy4ayTcWyBikiIj2z+nvK7JDZWWSrhf1OWruqT/sFlTFFkOCTAc+mAjIKLFWGOpXPkMjSoXtQwjitRanqzuTO6sZsKsghG6XaxegKEM9EdRjct9q3TH6D2/zwMhDVHud7CDPVIH1S7DJNdmv80Ufzo1aoL5SbW9BAhxIcqa6KPm+UNlIUnx2H5RmkuAiDbGpjKxpkIH9W2VnqM5U/QPVMiQG9FAnU4GqKXy8HyQAqh6EDxEA39AHqvVN/o/xZmrkxsnxZftQG9QsJcYxaK5QEOHlduAe1XmkvUbaT6lRKmand27EdCiFzEERlZcDJ/1AzbrBdiHugjc5SfKgUlAhObUD6YQk+Wwq99K3o/Sg/Y76nt7/kT0qiZw9/fvGd/Sd67rz1yrDusy8Ot12zPghL3Pf0t+gZicLnlhV9V8jdmt5ODkRSRxRbC5BiVE3vwU66Bq1zqIQGWrtNrSNJ3zf+TRbPKEBRASs5u6ozo89KJxHqNa3FEyHhSbYLcLrUuyBnQwaWVGZCGKH7VeVAa3qqaTvk5GGmRQ0UtESZkd1QAik9swoWKcDY9QCvuTcA60mpdf9IMKTMVrWkAA0GyHcJzyYFYSjzlIBKBZaEshn4fcojgOr/BgZYAAD2h0lEQVSQIh7IjEEodymmJoBOvBIq4N7UZ8n2DYjd2+ke5BR7GkwQNo7WiVX3S+1CuX3UBtX90rOpuo52SwwYjZLoKQYkRmHH+qHlfN2hA6qDBJM7eIr9AOzerjY9mvpetcxUOdASNFmUA9DMC2XraeaGtCXwfSrLcsnq4/KjJioTaxxEcCW40GAhTanOoPKBuqf6bWxXYsCIMuJV2dD3SfsCqP4VbZHoX3GtUHG/VI5KcKR3TH1uBtpZvt+B4arQpto2la/sx6jfFTOX6N7UwFuKSEvl2w+iZycgf0e1N0LFmiS8qoxieheqfyRkvEv2UNhO1Q4JFMxF28SEKWUf4H57nViFfYAYmKGyRL9I+NTzLXq+9E9+vva/8X7L5d7937/4E7+TH+8pO8Itl703bJp5R4t+lgmXV91T/+XMDUnaWqvoWd9/Z3P+b3X6L0GxnT4RPanwFIt7IyMcfUswtIqU9HnTXchZUBsO0Vo6ytkgB0vtaJpSp0hcSdlYQt0vXXf8G9XdySMqeCLIsVBZrHRd6ZiQ0yWEMnQShVOAbVNkZUw8Vs2Ymvw271pOjsnoUbzeHYm0ajd0ch4pkyuyZM3x+VETFeRQ/VUBNgU5MuCEa+B7F22CRDkpeoKDp94lrdk3RJtl1aAyl9P3IDBMyS5MEUtU3cOBDlW+c+yHlH2h+iDfG9kM8d4owwUzVmrQVMhh2AAoQvc7/vAj+VEro0euyI+aqHY1rd4nQLZIDVpRZrbKtktZn5I2SFJroS05upolP3wYT7Ef3L/ajqe+LQYFx6plpj47/US1/qlno4FJWhs1gnVSlC/aLuHaqzZLLIWNFBU4vVeIVDSYpfqmWYsr6nlhYEYJYnS/0h4C6nkpyO+E/62+D6E6IvpXnKIsbByKOwl9sep3cTmgBCFIZVvj5kRqTU9xb7Oln0XPJFEO3oXqb2h3fLX5E9kzlUFKmcaqntHg0LSYaUWDeqrNS+F0jsw1i1W2tx6S4ofRZ5WfMe+Znn/8C/lRb/mHn5qL6Lkj3HLpJWHTHbXDsrB555Zw2ntCuPFzdQ0t2ysnXBa+cuHJmey6O0j0DB/6SLj41Ppfp15vPuj/TM8+Bdf0FB1yt4oYjYzopJ3puTAZO7a6i6xa94+cPJyeVoPqjhIccVMe4fCQsyCDZgiS1Ig7Ofdqei9lJMiMBhppFs9GUBAboQxbdQ+YiaKyDsGBmBTrvBGUORzBTCHhSGHgLWxfyhQ1cvxk0EA2VQRwKHRBG1JZA5QNrDKuaJBClaPMGAF23SfW/QMomFG7ixOqvdJz0OYNEXpvqhy6RbeCA5yaK7LU6PuUkE9CvBLMqU6rz9L7VHUPl7kQ4gyJqSr7mOy6ygYj+zB25JH5UStDB1TtwPByzj6ema6W+8T9nOk5vLJq16efEO8Ypr1PPSGCfLA7qt/F9ib8OxRthN85MFJtF3KNV8ryFbaepukrsWNkRVWsVlA7pgG5KTUAQ/2KEkDI9imhLeH9kJ/RF8teiYFYFCXEJkLkm6nrUh+t7CFlS6sZRrgRjIq7qO8XA4i05IISxMieTQv/RYmL3UAlKFD5TKvs2IQYduTww/KjJqpd4Pqo4rMoeopyJNuZkhWKsXWNlNk1hJo1QbZa+SpU/2TWOJzvVt1T7Zi+DzM9Rdu++9Ofyo/mh5f8z1/Mj3rLP7zrf+RH6UTx8cPHXhveH24I60vT0evnPxiuPydvo9kSknUR9DRRheqbiOe/RBoialX0LETRrVevD8vF9eYbi557SLdEz5SMTHIsVOfiTM/+IWWEFbMfRH2iTlJlSM61nqmgmUYnlYj49PZ786MmSkxFIUdkr5HDIq9LbUg4tVTuSmhQ3zdb1Cgvte8U0XMMNgmJpLw3GiyR9YGEadEZYl0VjhShsk1Hafd2KMcJUY4U7KlNqbANiX6B6o7KzKZdYNV6soRqK+SEq34MM3HFe6dsMtmu+hQ5+AHlo7I30T6I8iWBaVIE47R+J234kgE2TtktGqxRg6Nki5RAOgHZjMpuja6sCpxLj69mjEcGIYYcORKmkNeYfrr6Pqe+w+2iZaZZzsAQ97uU6amWisHBJVG+T/7Hf+ZHTVSdxHchguYRGAhVYhIG6cL/2AVreib10UJooPZC4oOc8ZDgJ6t2MVuU7aTsNS2e8XvrBqrMaVBFLj9Azyz6bfLjpABNbUX4ybLuAOjrqP4c2qZaliPlvan61w1oIDZC65imZBdKn3qk+tmZCS4b+qxK6sB7gLYdoQ0AVXY32j7RjtEHEvdA71jFY5jUIWxRih+FQqSKHxN0im6gZuHMt+j54j/6xaw6xOLp5c9/ePeeiZ5FtuW2C09uy8ysZ3/e9sqS6Llja7jovC+FdR+9IpxbHavIp8Kvasyunk2mZxRbP3TOcmd6LjZSprenOF3dYq6iZ8oojklHjcYOH1LNRFEOHnXIKpihTlY5n4S6bso90GfpXISeeRp2iFaoqVnkWCshBwNL0Ybos2oqGl1DTrcCZHZs7DnbQAGwBpUDCW0RujeVBULvSGU/UIaA2r16CnaJVjsQU/1LeV6y33KAgBxz9d7JeYV3FqHNqsg2RNSmDAg55iq4BaTYTdPLhLhDAkZKf6MCCWpXKf2VEphwzbFpdqPosymbCCmbTNeQ5UB2SwToVNflkgBwXVp+I4I2SrzjsZXVgb6Rg7muDx1UtUVDh4psJdiRPQzye5uZgnchprdPfrtazwZFm6cMfiWQUtA7I/o8sjvKx6R3QcslRIZh93YFZV9KMQnKQbZNCFHIdqqySfFrqM2nzIhSmVxDCRmDNB1fCSvYhsR7T7F9OMgsll5Bf1T0Y3Rd5c+i6CNsBiHLDFB+xsBg9TmU30mDF6oNUrtQy6nMNUSnmQkZ1C+I+0W/Rry3MFR9R3LXffAbU/p+9Y5pcyLlqyj/uZcoX4Mge0TxfSQpxoeyVPVhrtB3yUzPT34iP5ofXvJH/zX7GVth2Rp0+/d/+Onfzo9mTyZK3v+WTPBs/N4mem7f8JFw8Sn5Nz2j6FmIpNeG68+pD5SS6Nm+LOVZl1v0XJRgpqcwwN0yHIQKsKmDSknVV9fFaSU9fF5FP7yLFFRwQDvZqndBoo1yIOj7aDfeCAXT6n7J2VX3QFPD1U7kKeIvOkfisySIqbpDzyYzm3ZUs41UkENTqNTUfQxcVDAD52mTkAiVz4TInBwAp7YTGZm0432SAC3Kl0Qb/HsR3KY42ymBJTrmQpyfhqlZQwm7deM0shq0/pZqr4QS2khoUHWa2lVKxpUMvsjWq/dGWfLKxkH5qmyaKZjeqz5LzyHrJImWym5BnZSCLog2SvSkQRXK6IyMP1qdwq0GokZTRM+DQawY5DY4MFStDwMj/NnJx2B5EuHFTzxW7QOUfaDyUWvN4fIBqg0lQAOLSaKnCBkmYSBK2Qdq3ypzHbPBqG2rFwQDEtJOw3n1War/ckMzqA9TYhO4FKGC2nEnMg7JL5KiJ/QXHQkrqdxF30R+mPId6Loym52W4BBlJu0vQHVHDbrOlSE1A4DekajrVKdU3zQMmeTTYhmclN3bafMxGkzIgPJVvjrZnZQ21AkoppOD5VDu0h+l95Zgz7olD9H9yo2MPvHx/Gh+ePEN/y0/6i1fuvC38qM6lWnmJQqh8d/UZ7Ip6WvD7UmZnoVIemO4+JT6md1lehb787Se6y8seu4hc93IKGU0KwXlZJKzMCNET1eJ7kLBrXIoacqiCsbHH6lueqE+S/egRHD6rOpkZ7tTdoScPDXap76PoKBOjYRihq1qmxCEpowSpzjW0+K6c22bJKJHKEBQAjQ5pSlCvFo/jqYIqzqphBgE7pfEnQmRcUV1RAWmNIVKPS9dQznmlL1JTmoGiNIqu5DEh2khkFKwp+wWBRIoltegqW+dyL7A8kkIDhQkNNDARYRsnxRpgQmxmVjKWovkE0h7CrZP3S/VdeV/0K7lKmNqDKa3jywT6wAvqT7z4L5sZ0eOqg7qTT/O/eP0k9V3PLVT1HXapEkII1TuJBZGyPapwQCyJeodY7kLv5Oy71UWKwbYor9CuyMEXSoHWqN48pvfyo/aoPornpf8DyXukC2R617CZ5XIS355Sr+v2iv18ep+A+1wLmZo0HIUcjAYBjyHRR9CYp2Km9C3EmWGvq94x+h/iKx+9GeF/01iHQ2Up0Llo5degWdW5QvvXtk4WpJL9q/0jkTbpOUrlP/Boif7NSrDlkgZkCBU/0j+syozqpPK1tM7Sun7VSIBMVdNQ/nJ872R0Yv/4OL8qLd86Weuy4/2nFaRMnVNz9llerYLnJXv6DMseu4hcxU9U1DBbcqrI9FHdVrq/GzpxP32ctSpH5AbCcDi3UrAoPcmRU/ooNTURES9C3K6hOhJmSwT3+JsU3JipIgCAtEQTUmtkTJVBKeuiQ6dxF+5gyW9N3EP9NkUgQg3xqpBjpDK9KR3IYF6oqZPU52UTiIELirYozZA71065vCOlRhFgoC0W3BeLmsAnx0YZucVy0bUJypzFdwiwtHFwFLUf3S2KdisgeKOeO9UZnPt2xRK9MdNu0SZkZiq1iEjYVru0A8o+zL+SHUzGuqDIlSnKHs5QiLIoLjuyKHVZSpUpufwsmobGDqIxbOZiepnp58S7WKkWk9ICI1Mfb9qH1RgSf2Cam+UATopypc27yPhKoOm7IpMLJphoURPmrmhNqsahncvN++Duo4ZYqJfoqU2VCCOAqvKlANbouwLL4khfAfI6k8ZlFGQnVTT8dHfGmE7SwOQUgBJeQ7s8/ge6H2qz1LWuVoqhpBiFNQzJSrTNVLWZk9B1V8StFTshjuyC5tBM1DUgAbNbJkRPhD1sWqwEUV3VSfhOdTapnMVPVWdTIotEvwz6oe65QOlQPVMZnrOs+j5ot+7uHbDtYOyOerB7//4s50XPVN3b2/9+x3hlsveGzbNvMOZnnsjPRU9VVAHRlGmrlNgKT5reo8Ud6DTUjsxUz1Ra4USk0L0JBOhHD96DrXpC11DZbJgQCTqL2WOSWcDghElYFBHLQM1yKZRo6bkUCpIOFJZCoTM9ITyURtDUO+odj/FkXjhWJOYPyl23CQxidb5jIweATsFk8j7Lc4UogEJJUZhd5rQxarsn0ESNYSwgiKBCL5I/JJr4NFaoSCgREi8VVPRaNqaEo3IPqQE0krsSwlm8BriflOW5aBnU+IBXUP259De1MAZBVQp2R5ybTy4XyWikOipdm8fXlFt81OPiWUfwEwOHsTvfdf9VTs7tpKDMhJTp37A1x2nJWTEeyO7rmw9CQUyi4/qg8qcJ9sl6vr4Aw/mR01Ulhm2WVHXqc+jgT4p7oCQqdo7loPor3BDHFGO5DvIrN2EzLNuQdPbVb8wCjvxKxtH2YzK38L+QtQRGthUtpNsXEqZq0QCqg9qoI+eOWmwMQG5pj68T9U/0v2qpXgw1lTxQkpmNVxXJVRQm1W2k9698sOUGDpbZN0Rg7wEvQsZW9C76IO4nwZ4abO3yHxPb3/R711S1yRrXWGsVrFH7MXvX/q5a2v/Ozcqomdx7qp76r80dmJX1LM9N90Rj9eEjeeHsHl7a6Zn+5qeG69vTofvRxaQ6BkL+GNh9c1XhHOrS4b0nH6d3q6CRXJCUsSShYZyulIC5H6AdlqVQiY0ZTVdnD6b4nTJTpY6ZBFgUOAhMyXgujRir1ABEWaLiulLFKSrzDwqS7WOXtbTtaECAcr+SbEZSvSkexsXWbeDJNLCM0Qo+0E9G47ai+6J7JzMakbBpXoP0iaDzVCbj9BnVeCj1jQkcHo71McMqA9q91QSRlJsZIqdVRuNEDIISAmowIlWdisl+4HqjpqiSfZBZhVBXZfCCAjbY2JdRnLxaNOZCA6+CfuCdkfZARK2ScivMXZUdW3fkUOFmLSkWg6D+3LdITF0eAVnV82MV+v1xMOcmTo4CnVygu3WJIh1ai1t6h/JnmaQ7RK2kzJ31aArvk9xXWpDar1SakOyroP9pCxjOXiI98XliH2T8KkRZUfgGmqmCmV6qnaF2aaqz4RrSL8I+ibVP2LMIcqMRDUVs8hnJuDedNYi2B1RZlQndz1cXUZKoe6B3pFqKyn9MaHaNj2b+izNzlGxxTAMEisfNWmQgXwCVUegfFXfTwMoNAstQjv3p8TRsg1Ruav3Tj6XsDtzjfFVfSBheq5Skhp4m+/d21/0oUtq/xvrWXy+3v38x/d8sPbTdJoFJno2VeW4cGuxzsB8kCJ6UmA4145MIY0q3MNcU/VN51AjrMOQ0aamwpMwqAIJavYqU46+T4kd5ITozDFw2EXnjQGVmgZDDnBCNqXKJsAMUuF04VqWolPHd6ScZeGMzRbaLCgyAKLnpNiUhKY1qfpAjrzcEAHqr1qnDXehl++tWmYpohytWUZrFGaQs61sPYjrKoM0JWOWppRipmgNErtlG6TARQlicA1VRzCjWJTZXN0VFWCrZyboOeQUcPisbMNQf9U6ZCSyqgBlHNrQ6BGH50etUF1VAjRNtVaCLmXOqDJbcjRs3qeEESiz4cP4fgdGqs82OCaCW5jKPv6QGKSAOqnul/pC1T/iVHgxvZ0G2VSWJQ74iPtN6ZvIFshlNahdiAAdRUDs4/m7UtYjTMlwxOxCEk1TQV+H6zTZZGVn6bz6LPU3KsuN+hA1SwT7bUHKUjHo84l3gYPPok7TM+968KH8qA2Y9UCDlQpV/5UPP1tUTIj1V2VvQlmqLEuKF1R7w3ch3jENNKfMZFNJHSlLutBgSScydOkdpSQzdItOZKbOFpWRfPetn86P5ocX/e6l+VFv+cefvyY/Mp1kYU5vzxZf3Tqvwmcvp7cvFkggWojVrxuogFV1vrNlSDgm9C7U6CZ+VgjmlNWpNkah0WMlIuKIpag76GgqUQOcfiX+Di+rTrtUO5FjuSsnBhweFWDMdbCEFpmPkNOFwaZAObW47qR4xyQCqnpG9UTtZk7rJaUEgCgEifeDQoVYq2sIBCIlvJJjrbJ0hiB7U007pnUgpQBC70K0QQpmVH0iBzope0LVJ3gOKZ4BKkOdbIYKDmiKpnw2eA61liXaDBWIpHw2pT+Gz8ogCcpSLXuCmxaJdzx6bNWmzoyz2DEwBJ+d5PowM149P7OL7QNlb0oBA/pYuW4riB3SPoAwJwUBEGdSxDr1Wcz+EX0e1RO17h/6iHBd2QdR35YgVKgBaVpmSN0DrRuo7As+bwcGgajM5RIIhGiD1I7Vsj+4VIxakxzfOz8v2jN475ER8ONkPQW/XK11joOrwqee6+B1J6B3oeoDtXk1iD+6vBqTK7+Vzqt3Mf6N6swuJehSf6Nm3NC7oCSACMUGyj6QLVBxHvbRwkZRm++lOJkK+mfgh6lnmO81Pc/8H5fWHqJ2UC72Hvx+xy9Y9OwGXtNzD1looid1Wr3ueCngVE7i3gYJahEUd4SzgQ6EcDao81W7yA4fUs0YUWIfrZGp1nvE7EthjsgJkUGDmMpLUPnI7JSRatCrAjV6DtWp0z10YvSYkKInCDmTYjdRem9qwwoKaNTmCcp5RCBwx6yBCLQhskVK9Kf2pmwnvTeVrYf1STnm5EDLcoT3IwIJGl1XQnNKVgbdr7IZ5AAr0ahb/QXu0J8gIso1U6HNq2ejPlrZDFqTU4mpVCeVjcOBGfWO4bwSlendj4hsUwqaZduG7xs5SmRhD1Tvd+I+zoYcWl4VbWae5IB16vvV68q1nqF81bRWElPlgATUVSlOQn8zrNbDhPam6hkOGqn2SkKk8BOozDAzSvg6ZP9V2ya/SAkVNGir+hDqA5TY3S0/mWy9qiMpA1y0vrbK2iJkpjP1pSpzEt69Cm2xDQnxjAYFpc8H0IyfXqNsBk1PV5sjkn1QdQfXUBf+xxRlsYq6TvGJascT36hmhao1I+kaqv6i/VZ+AqD6MSpLdV0sd1HXUwaP5xuVoHP3Jz+RH80PZ/6Py+oHsYjLXUmXf7/jF6/Oj0wn6UPRsz6N/d55nr6+Oxaa6ImOm+iIyHlUQbNJh8QKJaIsWX1cftREjW5SkKSCGXKEZCdLAbIY9cc1J4WJwaxQ1UlD/VNiEo6aiilCcuQVIJFKiX0qkEXg2aQoAWWpAiI6r6a3U51UwQhNXZPBE9UHEZyiQCTeG2XZqGAEN/6C+osZZjUok4V2Ko7QWonqeUkkUO8SNwZKmFKtAh/MciDxosY4TOtT0xWpHavdXgdAwE4RQBRUJ1XQQQG2WkaEREu1iyw68sIepiwjgu9TlA0JcKqeUflIMRWCUJUhQ7ZePdsYTG9X9zsMAxJDy4QdOADKYZL70ukfwPsc5nY8+c3ZC9DUDz193/35USu4w7kYDKO2RaJcZBiyh6WoDEhBN0Eom6J1OqF/jajdnNtR/RUhRQ2oZ7jzegT6ELl2slhehJiGbN5pkQ2JgrCw9dQPqvdO9Ve1V1zDUbx3GsRXQjENuqplikgMVaIyibSybyIRUPjftNGfEj1RtE/o2zoB+Vuq7tBmVeodjx5eHcxSCQrUZmV/Q7ZPxSFgM1SMlbI5EbYBcQ8k3nZrjwWZbQp1Sg4OAao+YDwm6gNB11WD+POe6flbUfQcCDO1/xvIVMn4nN3//Y7/dlXtp+k0/ZnpmU9fb3D+ZWHbhSfnv/QHCy7TE4yMCiRMd6EOSnVES9Ycnx+VEE4X7T5Nm0IoZIYXCIPqfsmBUNcl1FpQ5CQqkRYFPBE0kNOvBEd6ZiXK0TWk+EWOhQgwaCfaFBMud28HgUhl/mI2orgHekdJQo6o67gJkHDQ6Pvou9R0UBSCRN2jgQclRhETYvMoyjhRgj3Vs5TMPrlWLgTuSuwmYZCC+Qi1bbXExFzdFTVgNEgCp7JxYM+UeEDCtBqIovcpM6bgOWRQBzZKBdgUcKrrov8g3s8EbDqn1pwk0VPVs+FDq88xskL0ISBaTjzKMywCTIWfeITb0ND+UH+/z74VDZZMqE3REvwzHAAUwTit56oEfqpnauMlFAFFG6I6JT8LdgPFLzH1mYQV1a7U5loIPK9aczVlkz7qX1V/Q6h+gZZ/UZ+lMqMM7Ayy30KwmYSlXlSZozAo2gT6DkqMoj4goY9W/gstISOnKHcp9iJRWdlOPC98KNpQVfV5tNanGiihvkkO7JAQL+4X25AarJnju5AzlKCeqL4U/RI6VwPjEHVdKJ9u1b250q8bGZ153S/lR73ljot/Mz8ynWQBTG9v2xa/TwTQXoqeyrCnjLZg4C5G1OQaaUDfV58+hDrJQZG1RaPoKc5c2pRh7mRxuom4BxQXRf3FjEExvSZlhJUWG1cBFV1DZdDRaDVt0BGh+1WOySB09spZpnekrkuMih2eqRxUth05qsoBRkdT1DMKvJVgnvLMuLM23IMKZui7lE2meqamrKtNAwhyilXASu9SPVtKME22XomIFACqad10vyozhBx2mbWVcL9oS0QgTFPZlcNOAzBqAwh6R+MwkBWhgFVm9VP5isCHfAJVR6bgvHrHZDNUVig9h87ChnJYKZZe2ad6b1PfE+LXcLV8aEf3yNSO6mDJlBg4mHgM7k3UM/ys8reo/io7C+1FlS/VE2V7qZ7Qe49QPZPZSrP0MeWaoGCT1SAQZQeqzbkoi1vN8ED7qwQbaldqRgncr8qYouuqgQcaoFqy6tj8qBV6NjXIPAGbI6pno7qeUvfUe0Oo/dQgX1IOikOdVvfby6xO1TdRG6T1TiPDh4rBIQBjTSU4kr8k+hCcsi6Wf8FyFzEA+ZgqBkafQnyWkDMh4JnlgDLcg/QF4bMpGaTdgrKMVQzx1Y//VX40P7zwg/Mjev7Tey16doP+Fz13bA0Xnbcl3J7/2sI8CqD9kOmJwYx4neToKoe0X0eCFgvUmakOjgJsKTBBx7l09er8qBUSbeT0MAiepPNJI7dqahY5PGqaEZSPEkaofNQ0I3KMpbMB36ey18jJU+vzkRCjhAYKlNS7IJToSWU2ITbgwSBSiYBgS1KEJ1V3KCtTT32r3gOtiSjfD5W5cKApqJPiGTiqKtMT1z8UzjbulC3WnERbL94PBjNK1CDRR7kacF6VL4pnQtCl8lXXpWceFhtjkTiv1qOipRGUfaHBJdWf0zMrm4EZ2yqoo7Yt6gNlLSrhCj8r2htt3jQC60pHSDwYWSn6senqM48cyTZu173V6ZwkhEamvwu+1TjXMxKxUdysQYMllPEdoT5A2mSoU2pTKexvRBvCfkz5mJSZOiTsDtQpqv/qvmgqvZqhQfVpdOWR+VErVL6UyZgBzU1OdQU7oAZXefCO3zvaOJXhCyhhhUQjJVyhoCt8KBJnlJ2lPkQt70DtSmb4gp1U7Qrrg8j8VSJgN1DCNj2bWnd7/xe+ID8qIfoQ8ndkpifVE/JJaqBvpO4BSPEpFCkxN31WibRoz0R8Q3VdlQMOQor+HPdNEO0Y/XoVN1HfBJ+V09s/8fH8aH544TXvq91w7SAWew9//tN7f6N2YDpNX4qeO2+9Mqy76p78txoobsYM0C+H9Z/fENbmZ3rJXEXPTowk4WeFQaMAI2UNJNM5SIAeFMEMflZlKYBDOUaLiteg+qCy6jBoFo4JOTeT32HBkaYoK6eLNnpS03DJWVDCCApi4l1QAEiZABES69SIO4l18tnICREmnOyDEj0pe4GmpEZwQXhxD5ixKuwZOWNK0KWdy2ltyAg5U3Rd9X7ovHKgdz3wYH7URAVfKOQLgZTsuroHDoSFMEi2RLxLgmxOBrSrCbUxFgWR6h7ovHo2EmnV/UKdpCl9EQq8lXiA71jYTgqEVRYrLp+hBj/g2dRn8X2Kd0H9ghRIoQ+gne0joyuqNmpkmSrfalkOHy6Ep5nqvQ0dzGLS9OPVNjv1OAesUzuqtmjye6JfAHs4sZPbxdT3oQ8RfdMUZDmq8qV1J1OmGKtMN5y6PCL6XVr7UtQdugcaDFZ+BrVBtd4j9StDYk1PXEZHZFGR0KYGVcgvl9PmwT6od0k7yKsNNHFQUflQ4G+p2ILOK1skM0AB6vvV/ZKArAQbXEZH1FOysyr7uFvZdtRWcE3zCPgaStimJUekHwfXVcIilYPya8Yfejg/aqIGDsjnUgMHKaJnCuTXyCxh5csB1F5k24TrqvgR/W8leoJPoK6r4sp2yPZG5lv0fMHV7wtxrc1izc1e/fyny349vwPTSbyR0R6y0Nb0xExPZdASDLBJhwRHtfvv2ErYbVs4XbSJyohYhwkXnxfvneqJHMmH8+q6GHiIZ6OpLWoNRhIVlGONU9mFScTrit1EyVlQ0+YJCngz4N7UuyBGDj8sP2qFHDQKpDPoHsRIPmZzqS4H3r2aLkhrMA6ITCFyCClglaInBZYqoAInftcDD+RHrVD9VSIi1Sc5bR6cT7UZBz1zyhRNVQ4pdoAyOFQ2DWYYiOCLlo2QWVBQJVWZkU2VmXJQ16XLRe1K1AesvyJTYtejj+ZHTUaE7cRNj8R7I9FTDshBnVIBIK0dOHo0l+/AWDWgGj6Eg9vJb1frw+AYP9vAkmr93fWfYoon2C0p/sJgmBKIKBhXdpbakBIwaH1JVXeoH1KiPaHqDolMKRmKhMwYpAFIMUhBfpgSxFLEBxz8UP4WiTBqMAy+T72fFBGGljsYEvVpHNaknRCD1/QuU7LJlB9HfakabKSBRfXesNxFf0PXnRQzN9TgZjdQGXQk6I6IaewoLop6Rs8m+36R5U7Qckvq2dDHFPer7O9sUbae7k1mH8OAhBJjyR9VIrq6NwLrZIKvouo0+QQkhPar6PnCq345+1mIkQXd/v2fLv+1/Mh0kgWwpmd/shhEz7kae7NnkIOlpqwvPfGE/KhJymihuu7Q/uDkDbIpmPgGjMYKs0FCprpf3MBBWSMQtFQ2JAbeIgsQnXBxXcqgUJme6BgLZ5mm2CuxgwI7FQjTO8Jp0hH4rFpqQDmPCDyzClwwgEv4rJrejgEnOGijh7EgTGsMKlGORA21tAKVuQq+yElUmQspgjvt5CwFbHDi1f3iUhBixJ8ytOT0U6h7apo0ZZmpOk31bPQoGHCqQYKAzJ6AejathCCwRSpowTqt2grcm1o2AoUyYetxMEy8YxJGVHYsicqjR7HtHDqw2gYGhJA5s6taZoNLhY2DvvDp/8ebwGGd3JfLbOKbVZ9L2Qd6n0rQohkSavYIvU/VhmRmNEBrdKfMbsAp7xGykyAeKJtMbV61FWzHSowF5O7tcL9a7APxQPgOtOGbKgey37STeQTFL3EP4zCoIqczw0CqGrRF2yfs4RCt2y1EGMogVRnJtCO7qg80cDAp1kVXG/XNFbLfavCOYgM1syDFn0WfT7xjXEJJ2Dhss8pfgvqXMpCaghQ9waaqddwplpE+BcQyKg6hmFCVA/kUyhfEdyzaG113tuci857p+Rt10bPX/N/3WfTsBn0pem674YKwcfuGcNs160OzS94Rbrn0knDbK/sjA7RboicZUGVMCGmAaYqmcPxSHF3TGZR4sO/aU/OjJkrIxNEzEcwMHUKdIdezyR3Q+QqzgaIPrXNYA0d/hSNFWYdq9DhFTEKHR10XrqGcApoCK6fNA8qJkQIaQLZAiZ4kjIw/+FB+1Aa8I+WwoAMr3gWVuxJRCOUkktOP0/fEM6CTKBxHej/SJkOQpOo0iRpywy2w6+rZsBxEOVKmj7xfCOpUIEwZHCpzgb5PZXvg1ESxwzMFzXK9O3j3yr5Q4K2CLBowSsniVus3U5kpn4ICd/WOKStZ3S+KrOIeaNOMsaM5GB85qppJNXwwD+YOjlbLfepJbhdT36na6vH72HYOHgRCzg9APK7x1N0wRVNk0FGmj3oX9Fm1PjBO4U7om1IGvdSUc3oOtfYg2U+qZymipxTE4LwqG+WHzRrh65D/rTYVJFutBv/oftVsGdykScQF1OepmSpo+0Q5kBBJU/QjKOyJfpfqibLf2G+KukPfp/w18u9S4i7Vn5MPpNoVip5ikIISZNSzUfnSxoYZFEcoSQLOq8QderaUvlSRUnfo+2T2PQ3iqDgPBFIlouOsQmW3oBnOCH8JEf0C2mqov6oc7/7kJ/Kj+eEFv35FvWxiUfTw5/9935W1A9Np+lD0rIub2zfcGC4+JT9VcOeWsHbLijYxdH5IET3V9DsEjKpydFOg6Txq9C1FaDCdQQkj+53xvPyoiXJ4yAFQnezwAdXzU0+KekYiorhfuoddD1cDvUiKY0JOtFoSQGVbEDSVF9esrEEOpVobjAYZBsVaWzR1SI6MQyCbMlI9ukKs8Qr3oJxwXMMrYb0kVX8xaBUBBtkola1EQSu1CxU0I+KzFAio7Al0oEXZoAMtPktduno2tA+izLEPUSIMvJ8JsRM5fVat7UuozS0waBa2AdfvFO2KsmNVG6T3psTUpPdGYoewyZRZqgIful81zY6CFDXAhT6Fcj3h/H5ncNbt0KHVYG9oP7adQwdU7dlTInuz9nT5zyYDozxIPPkovE/xLpRQQKTs3j7+8CP5URNlD0lkUtlgNIAihRHoo1XdoeeQQg4IArR+qFrfj9aVVlMpCTUAM0Rrcqo6Daj3k2L7UPQUfgZdd59nn5QftUKzGyZg+aQI1ZHxR6rZnxHyHTBLswbZF5Woget/ijZIm/qpfozekarT1C7kABcMAkmhmOy38lHhvJpSTTMW1IAniovCT6DPKr9+mvzGBD9M9XlUZtIWJUDfp8oMRbwULUDYErquzMiEslRtiAbRlf9B67OnZAnT/dKSVZH5zvR8/q9dUfvf+Azl99H93//5l381PzadpE/X9PxYWH3zFeHc9jgk28n90fDuedq8qMxCm95Owqt69WTYU7JNTefY7/ln5EdN1Ppb5ITseqQaDEX2OakaRM6IV7zrwWqAQeuuRaie0NTGCDpHYh1JEjil4EJre4n6i9POhKNKnbrs6BOcDXII1cgtTQFEh1SgNjKi9yYHRWCkWQrx4NxLEZAyVISTSM6YErSo3Cn7QYlnGEgowRHKUTqk5LCLekq7X9N00giJvCrooE1j1PshoU3V6Sfvvjs/aqKCA7qG2gWZHH45hRYyPWWWAzB6OGf20f0qW4R9rBqkoLou6j8F47JfgHtQ/XlKYEgCHgnCEWoDalCFxHUK0CN03aVruV0M7lttA5M7ONMtDFbf0eQjvN7dwBjUh6fZPozvrJaZFELJJov2Rqido8lWY1Z0DRJiVAYSbcyj7A4KROLZqH+jZV5k2UD9V89A/ZXa7IfsocpspSVslGAzDhsIJg0UCkjUUO2K7IvyMyZBnJciV0LWLT2zXOOb+gAV39DSCmI9WLK/KnOShGJVJydgcAgFwBop/h2hljAgf0cOlECfJftzEhyFrae1X1VfSjHsXMumm1D9VVnjtKSFWqOe/G+yL5GJb1YHmtVn0a8RS47Q+0SRtwb5Gjg7R9iBuz/9qfxofnj+lb8S4lqbxZqb0arEmtj8vf1nZ/79/17xgdr/mk7jTM89hERPNfpGRlwZ9m6BzpF49SogMt1DTdFcsvq4/KgJjZZnkIMmMhoo6FDBV4roQ1Me1XXpvJrWTQKG2t2TnAW51hZcN2XklpyKyJLjq1NNZTYYBO5K0KK2KUe7KatC7BhL73hSZB7Q/aqgDG2MEHLIIRwWQiQJhqpdkGNM75iC2AgJZXIjDPgsbSQTofqk2hUtG6GcRCpz1TdRxpOqT7RRmgT6G/X3tDOxCppx1oRwtinIVwIpTvMU9ZQyv6SQD+Wg2kpKxjaVgxIs6X5V+WKAou4XfBglJlGdVFN2x1Ydmx81kUsNAEuewzZ5YKR6v0MghEZmpqptYPwhFr+mv1etU7se5mfD5QNUvwDvU83CwaURlJ8ApIhnKb7r2FFH5Uet0DMrP4HsOvvUs+9fVVYorSWsRC5ClQ3ZXyny0rqXQtyhLEnl61DfrwaBqN9Vy5PQe1NZizjILIQVEieVX4Q+qujzKBNW+Q4p4swIDJKpWX5PP/hgftQkJcNXQaJlSp2UU+HJN1LSAfgPsm+CPkD1TfR9ncje7BY4MClET/InlU2m96nEX/LlVBuivlutdauSUwh6n1jPRFv56l/8RX40P5zxgfdnImSsfb38+c/vt+jZDfpzI6Mobl50X9hUzvbMsjy3hNAnu7qT6JnkOCYEOJ0AAy3RGc61Skhj3eNn7iVJmbRKIAKWnrAmP2qiRGkKTtWaTfSOJpToSfcrOii6LmWpRUgESQmo1KYkFOQoyAlRI5a01pzMriLHTzgmT/7Hf+ZHTZQ4Qw6EzHAB1O7t5ACraX24HpVwPklUU5vUDENGggrKMFNI1B16DgySlNgN9yDfO0BZHRGqZypIojUyZaYclI3KYpW7jgOTUI4qI5mccPl+IPBJEgCFPcUgX302JegAu66EYnzH4rpP/udX86Mmavop9QGqvab0TSq7m6B3pNZuwymW4h3jmp5HHpkftTJ8WLXPGjmS21utJPKfTYb2FcH4E9U2P/UdFsomvw0C0be5D6F+U9kHsqmqntE1lOhDQe/wwewnoBgkbBTaHSUuUv0T9QF3vKesuBS/VXwXDa4qf4CyA5VIQO9CbXKDmYhqFgPUEbXWM70fJTCNQnuTWYuU6al8MFgjUAor5N8pv5Pahdi8kspSLe9A/bwaFEefQNTJ8W9Us3lVZrbq5wkSaaXQRs8m6jrarZ3VKc4R3BxRPAP5BDhTKwJtVvnUc41hOwH5vmqJK6pTyhdk0ZPbG7ULWZ+gzNQgJi0ZosqcfE96BrUMw1dvuSU/mh+ev2lTrSBqB/Hxevjzn99f+17TcfpT9IzkIuft+a+RjddD9uc80a/T22XwBcGeHFGbIzKTJUGcWWhQuStxkhwLJQjse2q1wsvyBadABfmUkakEJhWkE+RIqWcb31Fdn3ICzkXQ6RemC0fyRRYIZXqq7FjM+BNBHbUtJQxSUKecOQoElBNDdVKN5JNzozKxMNMhwUFT02toZFwF7lSWyr7Qe6NuT7UVymRR03jpvFr3ldqrCjpI9FT3QNdQgi6hMsQG4bpyl16ok+qztE6t6seonkl7CG1QZdRTYKj6R6pnlJ0Voc9SJmME668KJEikTQhYVZ2kAEXaf7g3dQ/UXlV9oIE6ZWeHwZyNrqoKCpGB4ardGjlMtM2J6jM/+ZXqrIvI1GNVGzUhBgNwkEG8Y7IPqi+lNquEJyp36ZuBSJWyRqDMiIc2OyX6x6fuuSc/akL1jDLGI5jBpHZkp3ch+jZldwiqv2pgB9smVxH0MWmZgQi1bTmTgmyGGFShOin9OHpmZeuF/0yQr4LrNNegJQiUb0bXUH0IZqOLZ6P1rdW66FTuql2RgDwilqKiPlZOWU+wRXhevEuqv2rgjcpHvTd6tpTM9xTk4CiUmaqTKX4N+sSiD8E2q95FQttMWnMd6ir1N4P7cR8/75me739/7X/j+4ll3LufX/6ARc9u0L+iZz+RZZ5ubRFd5yp6quBAdSRzhQL/romewknc26paShCqgg7avV0JAvQ+VcYgCkGiM6SR/JFD5j4NhkRP3OW3BnXIUkSETBYp6IIDMSbWvSQnRDqJ0I5V26ZsORVgUJYlOis1yBkbEhk9VFfV+mQkFKtMFHTm1O6yUJbqvaEjJcqXMhJoLcuU9qoEUipztQEEOsuiraDwKtogPYcSj+keVLuie1OfHYegTpUvOtDCxpHDrpxlqjuqvaYESfTMak04CkJV1i3erygHKssUEVGB70jUScouVCIKCm1KXAdhcEQI/EOHQmB5BGdOzuyqttnRo0TwNlStZ0/dxZsejd8P7yjB11HvDdum8tnARqksbGpb6l1Qn6cC3qUnPSs/aqIGELE/F/dAm2OpqfAEtk1hi4bIlgh/FgVssuk1qB2rfhsHjIRfRPeWsqSRGjij+it9EqiT6n5JuFL+ANX/qSdFlhr0/XKAF66bEnepPkQKyABt7qlEe6pT6h6o/qr7wqnwIgZAoUzZONV3A7gsh/LraUaI8lUI0TYp9lK+ylzj85SZVuoe0Jao9wb2QSVU0JIUajkseke01mgG3AO1QZXx/dW//Mv8aH6Ioievvdndn/9s0bMrWPTcHbngGdmd6KkCFDJIctoPGFXl6JLhSEE5XYT6LrpGSoeRMlqupiShU5DQwalOmp5DTg2YI0oo3gdETyW4UNCgxA5896rjhPJRjtTAcPW600/xdWmKj3Iq0OFW7Q2uoTI9yWmi9TgjJEpMywwZaPNCgKb7VVOHqBzUNH8KBOROqRTcqrV/yOEWdYecIwwsa9A6a6rMyNaqdY3IuUenTYgPIzDdVgUdhLIZKYMUGKiJMqeyUfaQvk/1NyjOCPdh10PVoE5liE3CurhqzT2yySlBs4LqvyrfSdq5VEwppbY5eoTYICnBFaP6qzYloXJQgSW9eylcka0W/RiJaqqe0YCG6m+G9q++o5lpvoeBkWofsOTEaj2NDMK096e/yjvIT32vWlcnH+Nno/746QceyI9aQbFa1BGyfSq4pcxHlQmObUC0C7KTyv+gzHW1DA7ZT6rTyu9EMVX4kjiwKewI2iLhm6HfKa5Ln1V9E/VDE7ARUmSfU56bHzVRy7TQ+1FQOUiRFhgX66JjHy+EV1qeQQm6lKmZkhWt+miyUbJOQp3WPl+1Hacso6DsN9Z1VX/B1svZI+SXC7tFA2dybV+a5STeG7UtlViC70j1xWD7lC9IfR4t4RShOqXqDt2bipvoXahZHvTu1Wwv/Kxo81jP0Hby8371r/4qP5ofzviVX8mPesuXf9W7t3eDPhU94w7uV4fN+W+trA+be7V7e2O3+DPC1tr9hN2JninOkTBoKNgosY8CDGH86DWnZGSqzhBR95tyXTDWyrCj2CzKlz6rglvqJFXHSajypQ5ZTZmhjYxUh0zlo5xa+qwS8EigHxLBzDhkhapNKJ6+//78qIkKkihIl+vYQOc5LRbWx0zPlbx+HNVVJR5Q/VXPRiI2blxT4ylY909BNkaJnuTAqoweWv9KZXqSU6p2w0XHT9hUEsXktCi6BxEIEPTe5VR4CMrUBj7UrpTDT+VA2aoR+qyyA9SHpIieqv5Tn6VEGBLXVZ9HAw8puyur61JbUc9Ga68p0RPboAiaCVXPCGW/sT4IUZnesfJVMOCkvrgGBfkqu5DEW9U/UvkMH8bCyMDSar0eXclZoYNLq/c2/giLKBMPV+9X7d4+M15tb3KjM3g2+S5I9FR9E/kfSsAA+6CC27Gjq5mw6rpUz9SgFa7lBzZK+XH096ovTgGvIforJMGPUwNnZDuVLSIRUGVQ4z2I9krlLvsFuO4EDCJlwHuToictiSGy1KhdqbWtqZ4pkYvaJj1vhOKQXY88kh+1QsKTiptGILtVzgghW63qL9TVlGfDWLUGZbyqGAsH91XsBnVHip5kZ1XsRn4C9YOCFF9F2XrcJEzdLg2cqXcMZSYHoihjG3yzCPnlWOYwgywy32t6nnHFr9TLNzaBcjkXv7f/LJjjv1v07A59KXpuu+GCsDFcFrZdeHJ+Zh7IBM8vhXXZZkp1EXZ3ome3UIG/FEMBChqUSJByXdMZKBstQhtZqPpA63QqsQNFLjG6T06tFFPBnMjgC3jy3/9fftTKKE05V44JOWiqTsM15FRKEG/RwatB7yKlHStBYNcD1V0/lQNMTqmcCgzXUJmeVGbKocS6o4IyyuYSZUblrsRFXNMK6ogKuunZ1DpZVEdU/adlI5RIQCJVSvCllgkYgGm8E99mUYPqJD1DBrVB4WqQA63EMxJcRlcckR+1QoMialOH8YerAefoUZzZh/2mattQZkqsJpTNIMFF2QFEBIu05IgKvvAeVDmQ2CHqDmUMqv5m6ECo6/uzQDQzXi2fQRBCIyOwQdLEYyyiTNxfrVNq0IraobJb49/4Zn60e2igT+2KTcKRtA/wjklYiYweVp26KbOgqD8WfTQulUH1TNU9sN+q3yY/TAltVE8V1A8q0YgGvuS60LQxkFivdMkxx+RHTdT7SRnQQLFP+C80u0dtXDNNbSjBvqDPWIPEX9VHUz1JEV5l+cL3KfFsEkSuAbFJE5WP2gSO2qAaDMDBWOF/q3dP4IaHYuaSHBTpIVQf5GALQP5hhOqDWvue+kLZ98M7kjOP4LxaqzypHGYZj6mBzbs/8fH8aH543i9fkRVjfIxe/vzylVfmd2A6SR+KnlFg/FhYXd65vee038PsRE8VHCjnppeQUVRChekf9nve6flREzkCCE6BnNoC11BBKDlCykHDzjdacIDahXIo6ftUR08BkcpSwOBHOPcU5MjRY3J2hanF8hX3S1OgJh/jbCW6B1qPM0JBrxQBYakAaUvgmQf3YYGfAk4lAuIggahnFDBSgKICf6oPqg2SmKScQcxWUn0FOIkqg4OeY3A/DupmnqyW79STIghFp1i0V6i/JCxmwHuT74IQ753EdVW+FEgo1wjruvgs2Sg1VRsDb/FsNC1wnxNPzI9aoaw61V7p3pQtIgFa9TdU10kAieB6u0LsQDsg3sXQAdU2S8uxRIYOqT7b9A/Ybk0+BrZTiJ7E+DdZ3KT6oAaiUIBWQiYJJqJ8KQNOTu8FMUnZSRJ61cAvCS4pfTwF2Cqzj8pBCUFko5SYSn2AEtrI16E2HCGBSb13aptqFg4OxArbqQR+YvyRalazmtZKs0fk1Fwo9xGxjAiVA4rwNahOqQEYquvyuimiJ/h3avAaZ2WJ/gZ9M9EuqL9Q9XcclldQS9DQOpBqnU5qQzIGoHg34brdQr4LejZhO7Eshd2hjNWUDbMUGLOI8p0tql+5+9Ofyo/mhzN++YpaQdYOYnPp4U+Lnt3BoicBO8cXFOt6pmR6Yqq8CGbmaoCloQSjqJxE0z/se1p1IQclCFCgpTYcovqnOj2sk6L+kiOlnDl0/EQwTuKXEuUwK061Cwhy1GgsCnCiHKgsVfBEqCCfNsVRa0GpaxAU0NCmSRESMJSzTOt3qqCM6rWahkv3qzKVca1DqNPq/WDgI56XnoHKK0J1XQVJ9H0jh3PXPfVdcEiFrR/aB9rV4+y84pRUEfDS+1HTGMkOqCwHbMeqDcI0dDW9LGWDMMw2FUti0LPJLGEIplWATc+sfIehpRAATnDdoeUZ1BR7ch2V6EPl8MSd/54ftUKZu3K9UnFvBLXvoQP5HQ+MVNumEkgnHq2+IxUA0jtSYhINQqqAlbL7pABHYoewyYQS7altKhGcykEJe7QTOPXFql2R/VXvhwJv5RdhZqvoc9H+ivZKs0SUD4X9tng2Kp8UkUsJKzQgRxnjkXFY61nZZKq/E9/i62L9Ff0CDYYp+4LPrPpoqDvKnyWxWtoMeMc4xTkCAxrKh6IlgpQdwDYkMgZpYEbWXyjLlGXBVBtKiaPp+zohi5CNUpm/VFeVb4UZkaJOUv+YIm6qdzFAdkPcA9Udui9li+7+5Cfyo/nhef/9v9f+N5ZDrBO9+/kvv/5rtZ+m0/Tt9PYPH3ttuP6c+VI925nb9HZyhFRARUjxAgyS+iwZ25RshMWMMuy9bBq0EHtk7Kij8qMm6r52PfhQftRk3+fwEhEUoChxBh0h4VRQBlLthvODVtAxEdfFjlMJmXBeBZaYvSk+S86CEkjp2VQ9S1njj7KC1EYAtL7vwBg7XSQI0NqFEXqOQTFtmMQ65dzge1MiODh+qj5g5gw47Gq64vDBUGbTQlCANfuU4Ej1TAkVVGbDh4n1lp4GW/9tLpuU4ID6C1puIUKZKKpd0Xm1Bi/VhyEhrAQSgkT/iDs8c3OtvePqPahdWanfVaIRkTIwqbK2aHBo5BC+h5mp6kOrNoiDTsLG0Xlazy1CbWBsJS81QIysYCF0YKz6HLS5UWRgoNpnTat1Op+q9o+T3xF1Hfw+NXi36/7qBkcyK46msiu/EfpjlYlFdVVlK5HAqYQcEgTkIAOJgFSnhehJqAEy6geVEDS0T/X8zATbU3o21bbJz0jJ9FT+BPVv0h6CTZ5SohzcA/miEfw+ZTLIRxVTn6k+KH+AfAe10zvWBzE4iu1KDRiBTVX+N52Xyx1APcO+rQZ9Vg02TkCGropDMMtS9CG9RMbR0N+k3K+MucmfFW0exVDhC9JnU7JY1bOhSCvqL9kudV30aSmGEAO8d3/qk/nR/BBFz+au6rXnrP1X/F7QjX//skXPrtCfGxnFTMsPhrDpmvVBjL/1mLmJnoQ0wIByBgnZ0cOo3owYqSODpgQbMl4pgXS3UMaaRLWU8u0WyuAvPWFNftRECSPkLMtAAN6bXBMRskWVw071mtbkiqCDJjpOyniSo+h0XtRfWr9TObVkKjH7swY5qipjisqHxO4Iicq77r0vP2oD6onaQZsCOzUiTAG2ElNxIXXxLlLWTMVgWl0XbB8JDUqMJTFJCXiDS6v3O/UD0VaUjQLIrg8dxGLJ5Leq1x1cKhzz0WobnH6S7wszPROCL7l8BtU9kWVMzr2a6kdrharNLXCH3IQgSQ1a0TXkrsJQDnIjI7iHp+69Nz9qZZ8TVuVHTQZgo57I9Peq36fsLLUhVafJf1DZYCQeqHXpyFarwYDB/ar+zsAgu7/TIGRO72Lfavq71fo3/bQQv8BuPCHWsab1Z+Uaa0PV+qDW9KSN6KafZF+QRJARyHCMkK1OydRXwh6KQWBfUqaOavsNg3TCHxg6uGpfVDbw9A+qZT4zzvVp8nvV6+Jgcg16DjWoQs+h2ja1V+Unk5+gMjJRXBf9NjLF7QqzDsV1qS9VYh+VmRLBaZBBxWM4ECsGP+gdq6VisA8Rfg3FaWr5I/KppQAt4sp+hfropJhb+Qlkd4TcQvGf2nOD3qfy1VF4FbZP1VWE2pa4LsZN1K5EbDLfu7c/733vy4/KxOfnd1ln7v/+L7/x6/mx6SR9Or1d7dwe6eHu7Q26IHqKYB4zxJRRJcOTYFTldArhCPUSKp9+EFM7AQWGKlgk0VMJbZS9MHo4r2tEHZzaqW8YMgcG+BbC5Herzpyqv5Q5oOrkJAgmKviidqHEAwxoVNukdiHaG5av2Jmbpskpp4uCH5W1Qrt+qrZNmZoqEKb3qRwWEnjklF2qJ6LNYxsQQQ6WO3yXcgbHjqhOtx3cn8tx+olq0KBG9+kdy4AK6tnQIewUz0xUn23qu1w21P2rAS5au021V8oKVYINBVqTO4VAStmMoo7IfhPAAFAI+WSrlbhDWRnqs1R/Vdum/nHsKB7gmiHNSLy34QOr7Xj6SWGLYJBMLadCdmdcLMtB9kEFdbQ5y8hRbGcHlkBBTHIdmZms2u+pb3MmLWaAKoEIBAE5KAj2SAXCyvbNFjUNnQJs1TeNwgCrWu+R2oCyJXSeBCIcYBMoH4r67eFDha3fBe99mt8DZRRPPsZ2i2zRU1/7Wn7UCm6eI8QL9AVVvYF+Qb3LCai/8l2ST6H8erDfKYKaEqup/qo+jwbJVCYt2XoV55EdUG2bprIrgRTF6oSYUNUdGmyUiQ/0zOIe+k5+mCeo3FU9ozKTPib1IV2K71Ubovsl+6sSf7768X4UPbvPv/zGb+RHppP0Z6bnAqBboicG4wlGShkeMiiq4+wHUjrvxYASPXH3duGYPL29mumzj5jeTiKgyibATV9GhTP3RNVxk5lC4EQr4YmcJimCk1MrypcEx2l1D/B9KquCnjnl2ZR9oABZZnoCQyIjjYIR5YTTej5DkD0UwaBMBJwUpKgAm+5NtQu6Lm2iooJmJQgQg/uA4Ph9UY7k+I2JfmGwWvcGlohg/LtQvqI+DQxX697Uk6K9gvAkp+RBUCeXU6F7g82yItgHiDqC2dbCsZ6GZ6O13yK0wcbQQVxHSNxR9YnavCpfzEgT7Yps0dABXB9mUMhhO0vChmrb1I5prcYIvjeRrUT2e3g5Cy5DB0N/85QQRmDgYPpxvgfK2Jt+WrQh8LnUO1aZ0QROYYXszwj2WcI+4DqzSvQB+ytFFOgL5YY4UHfo75XIReVLAm1kcN9qXZ9+gstmYAnYIlgWIQNEdLLpkanHq3VHCeNUNikb3CmfhN5bSj2VAh5k3yshk3w26Q+AjVP1Aa8rRCN65pHDxOAS3IOyh2RT1ewGtR7mXMHBa1Fm/RwrEoshfpT1N2UwgJ5ZDQZTPy/KjMpSxSyzTVZSyxTN9+7tp1/+S7X6VC+KXv78l9/8zfwOTCex6LmHzFX07DU0dZQCvchiqBJK/CXnXjlS/cC+p+apxWVE50JBqHLuqSNSWUUUuEuBCZw85SyTk6iyIamjVllx5NTKzA4oy8ExFkgDTF2bfkJ06FD/VD1LETso+FFO8TRMb6S1ryK0mYDayIjWlVMOGgXISsDAzDxR1/F9ijaPG2HQIJAIuilDhjYAitCaiE8/UF2bL4Lr5QlBgabNDwxz3aMgXTmeylElJqHupGSxqg23aAdXVU/pumpdXdrcRTn8NAikbByhglsUgsQ90HMou5V5xm3IexgBgenbsxcaZDCT0G9S/Rt/hKdoov0W9gX7N2EHhpZX/Z1hmKIcmYLpyFPf4Qzdqe9DOQgfispBBaxKFCaoDaklR6jfVdnHJKCpJS1oSRbKaIsMwftUfR5llNE9qPpPgwzKHtJ9qfZK9V+2CfIH4D1E6PvULByqZ6occfBatCsSQJQoTcsiqWcjn2RKZbNDOaTYHAXGBqpfIB9T2EPsb4QtIvFXldlcUX0ItgFxvyn3Rv2Fam9zhfqKCIqequ7AvXUiBsa2JeoO+fCy300oS7wH8Y5TsqhToHZMtlrFc/Oe6Xn5L9VuuHZQrhI9+N2iZ3ew6LmHzDnTUxjruXZ8UggiJ1M4Md3qoEw6JHoqJ5yCA7W5BdUT2rk3Qs6CCpKwg1OOH3T0asrM+KPVhdRlRgPUdQpmIiR+SYcdggklUlEwIgVdKh9RZrserG4co5w5ykhTG2GQY6x2Cib7MCzWSsR1OlWAAXVKvQvM9FFiKpQlicdScKS6I2wkBdgqYKXrKkd3cCm8N7F+3K4Hq0Ho2JG8SROh1iClMld9BWWG0BIVEcoyTsksURkCNCVP3S+tyan6Usp4VbaIMuXSsn+4HKgNjcIyDBGyqUP7cxuk6d7T41zPHt/2b/lRk7GjeS1iut+U9WCxDdbAzcdEGxo+qGoHhleIOkmi5/er5zIo03PX7PsQ9Y4xs1pMG0axWviYJPCr6w5D+coBRLiuWlaGvi9loxwaQFEb0eFggvKToe7Iabw0KCL6bRTBxWepz1N9CA3UqbVN8V0oEQYEEFVPacNDWmM2Y7paDqp8sY8V9hvtekJoq3wSgmKpCArxov6j0NYBQZdQdV2J43OFyrJbz9YJ0OcSdSdFLsGlQURmNbV5WSdFGyDofuVgLr0jYR/ofc5VpO3bTM/LLq89XO2gXJQ9+P1fr7oq/8V0kj4VPXeEWy69JGy6Ix6vCZtuviKcu7x+7rZX9seu7r3M9OyEQEoBvRrZ6ecOam9jv9NPy492D2VV0HqEEep81RQqChqk6AkdqsxSgHqmptiPLEvY0gyCiRQHYvhgDm4H960GZdMiEJ4hH1w4PHS/KgilYE+1YwwmlDOH0yO5zCigUdl2JCqrNT3pHan3hsKReDbaMIV2lVfOGQXdJNBGKPCR6xxC/VfdMd2vDKjIIR2p1jHF9NNcTzFrS9wv1V+V6Unlrtb0JNSU9ZT6RG1FZVCTqKA2wqA+Wm1kRDZVijPwHEo8GNofghGabltjBtYkVDMAyHaqzD7MolbtDeqUakMUlI2oZSqWw7tYLoTXiep1Kfszsuvr1faiAktaamBSbVID7Vj1CzjgI8qX6pRa2gP7LBHc0qaAqs8jAU2JMGQf6LqqbWMbEs9AdlYh7S+Avrq4BxwcFe8S25v4LA3MpAggarkFGrxWm2ilxCw0aKXKXAqnXSBloDtl8K5bqD6E7jelHOVsOmqHUJ8iKfJDyuAz2YfZim+dgspd1f+BhM/KOKILKPugbC0xW3G9X0XP5112We1/i93Vm3W++Xusw53/93+52qJnN+hL0XPbDReEDx8bxc0Qbrn0hhDeG0XP2j/cuSWs3bIi3NYHu7qvecUr86MSwhDQCIiaFhjAyKgMJrqGNEZwD3KNtT5FdbJ9WIWfEaoPypnb5+Rn50dNpAgOnaHK9BwYrt7D9AQ7JuRYqI53aL+qYzI9LpwNuK7MJoC6qjI7qA2ozntwDOrUENenmV1w3UGukwGmlO56kIMGekdqHdSn778/P2qisqBIOJJTYGFXVRRCawzuU7VHSsike1MiFX1WOX7knKcErCTAKTtC96Wy1GgqvNqERdVfAu26sIc0yDByGIu0YaZ6jeknuRyozFUfQvZBlUOK6EnXVQMw1B+rDKTJ71SFBpk5DPZ7eDl7JHRvUvQHG6VERMq2G1nGwe30k1AOo/yOB5ZU3/H099hmTIA4I6dkU10VQSiVgwqI6F2ktKuRldWs6MjUd6rCBpVNZPwBKEvRNmmASk0bpral6iRdV9UzKdoAZKtV9ib5JcofJRslB+GhnqisIkKVGYG2RLxLXH9ZrOk5QxtmCSGIMl6VX0T9mJyxAO9H9XlUvkr0JD9BCh30LoVvRtfopeATITsrhVeqvypmEe9+tqTEQso/pHfRCfEY26Zorynvk67bayFzrkgREeqUsodkU7uVqKTs95zrL5SDSiSY90zPSy/Nj3rLv15zTX7UP+y89cqw7rMv7gsNbk/pQ9Ez7pT+sbC6kd1ZEj13bA0XnfdoeHfPd2+vcsL6V+dHTVSHQU6X6jhrvVZ+0EQF/tSZSUNJRlWNrIvvI6jz7XWVwo5EdIb92kkqJ2bfM56XHzUZFB0nOVhDB3A9m3m6+lm1gQ9ll6jOkOqk6uh3PfxwftRETZ3DaejiflOcLhLKhg7hNjS4X7UcKCMoMv043O+T/Gwzk9U6qab6jX/zm/lRE1UOtJkArf0WGaSNiERbIVsiM9Lgs0p4pWsoQWuQ4thBtjtT36s6aFRHpNB8X3WjKNwJtwY6g8rmQHtNEfIH92HHc/zRqrg4ehQLIINLq3VkcgcPlFDfpLIAqS9U/QqJO5PfY1E5BexjRVshUUH10ShkquUdwG6prGj6rBQnR6vPMf043+/IMVWxYmacPzt+f7UNKltP9VdOwyXRRvgJ1Ico4WqY7ENCgK36ppGjq2U29Ri/4+knqtdQQSHabzWwA/ZbbXaCyzOI+ov1Wvgf6GMKv5HWV6Ud/iN0b3JNcRoUpHsQ9QkHflUwD3VEZsoBqq2QX66uy++dRU/cMDFFuBJtBeMQJXRA/0ZT3iO0j4Dyyal8VN1LIaVfSIllyJaoPkTFabNF2S2qv8qHSumjCfSza+AghbJF8BydEPsojlDxOT6HsId0XvnfREqZqT4Pl+0R9ZTKN+V+uwXdl0XPVuYkesYEwYu25r+sD5tLmlkmXF51T/2XMzckCZitomfU6q4Om/N/q9P6Xf3IwhI9+yjTk6a3KyOVYqzJgVBOQVIHRZ2kMOwp1yU6kZGZ0hHJsgS6dd1usf8Lnp8flRD3S0Hz4AGio4f1AGemRIf8dNVhmXpclA28Y7XpC6GyQilLTI1gk7M8MMYd/eD+4GBNi3KYAAcCdvmNDMF1aZffyNR3Zu/cU/CzS2wIMvlYddrkyBEs1uF0th08HXl0RXXJBFr7LSIFE4BslHLYMdASGVOjh9G0y2obUlNHU5xEmuqnglsWubit0L0NH8T1lASxGcjojJAdGBgVIsyTIIDQTvE1xiErSG1OhAG9CA6ontLmGJHBJdVyVzadpmMq8YB2dVfLb1DWlaoPg6PV+j/1FNsB6qOXPJs/O3F/tf4Pr2RxknZvn36C73fqu9V7UOtC05RdtWbwMOyErwbDRmD39qEl/Fkqy5GV4h3jAJew39+t2qiZp/geJr5dLQe5QQyUGe68XoPshuofyQdS7QIF/v25HGjQaer73J+T6KkGndAfBT9DlSOVmRQnod9VGbPozwofl8pcCZn0LlUfREsjUNZ6BmS5z0wKewjloJbwoDqi/JddD1TXJFf1FPuABJ9c1un9qzaZ1j+PkN/ZrfgmJSFD3QOJ+aru0HtTn00Rm6nPU89Gz6HaBaFi7rmKygq635T3rvp+iumU/0HioLJn1AY6sYFxkqYBUH+uZoDN90ZGp733vaVJ6E2K39t/Fsz13//1gx/MjxLJBM/A4mPt3057Twg3fq7+b3FW9cZwWfjKhSe3fLeCRM/woY+Ei0+t/3Xq9eaDvpze3izYteH2huhZL+B7L+/fNT3laFbCyCIZP1rvQ6EES+pk1Zpl3aoSVA7yfqnMhFFNcRYI2REBqiOaa5mpTms/yvQUAeDgkmqZTT3BQs7QvtVnpp2nIwNLqnV16jv83nhKKT8bCtCiHOkdq+lW9D6VI7XkuKqANzCm2hDcm9hMZvoHVWdM7bZNweL0D9iWjMN0Nto9NUJOiHoXJJCqdzFyeFVoUGvC0fepTDeyBdKmKqcfoLVqR1esyI9KCPtCGQJqqjYFB8oZpGBPrv8JZTN0EJcNZfGNrOS2Qjt7q7ULp3bCtElhe2kdVSUqzzXjZFpkvw2A6KmmzZPNUPcwfFjV/1BTSmljKzqXAe1tYJjb4MASqA8HimBxsPqOSOyOTDxSrX8zT7PNoABZDtZAe1FT96ltq/Kl88pmDB9avbfBA4QP9FS1PgwdxOU7/nVeN5uY/H61HFSZUZ+Hg9cCtSs22SPlA9HmWDSwExk9quqXTD3GdWdwCQj8j7N9oFkP6M+qQQoSCUT/gcKrsHFkv1X/miIIoHgr/p6yY6e+z+Lv4NLq/SqRawY2HBrar1qOEao7qk6TIEsCawTrqRJI4X3SoFcG1J0pNbOA3r3yUdF+c3tV5T5X6PvQz65BdlIK8VC+KuYhcVyt8Uoo3wrbrGjH+I7EZzEuFW0ek5JE2yRbIH1B8j/UPYhyR+h+xXWxnoi60436q2KI+RY9T7/kvbXCqR3EYu/hzz0TPctJg/mpEvWlIz8Yrj8nj+FygTSKoKfF7wYyIfOm/JdIIzu0KnoW2t3Wq9eH5eJ6802fbmRUoyU9t87G628MF1c3s54XUPRUnQsYNOUcUTaMWs8qayBtkLOiUEFdyq63hAo6UgLZhUZKB5fC/me+MD9qoqatkaM5epSok7A+mRLlpp8EpxZEvcj4o1XxTGavwf0qQZc6ehUIk3Oj2iZ9dnAf0S4gq1PtdE31gQTLCG28MbQ/O/cT364GNDRlMkLtTTn3NL1dZbig0CCCZto1X9mS4f0h80BkKNKADQltEZqKrgJkgpxtmZUEdVqJfZQdqMQHrNNKAMHPCls0BO9CZXyPV6+rpq/SvSn7T5nKyg7QTu+qfMlhpyyfCGWMSEEA6o4UPWFzF/WOhw+Buk7vpwZljQ8dLETPzItuZWonZwwO7lutv+OPsE9B/VvSAAzU0wzqS0XdoSw+9S7I7ows47507MTq0ivTMOMhMn73yvyoBAwmRHY9VC0fNfhMQaASk6hOScEFykEJDTQ1XPkUM7uqbVYNIGJf+v3quQi1QxQ9RdnQeeWT0BqZNG0/QqK9WleaylzdL9V1NdhOIqKyh+ST0FreERKp1LrF5I8qYZwGnWSdBv9DtRVCZQFS/6hI+b4UUkTEuaIGmWlZGeUnU3ahzkSsvjeVUUxrbKslmGQ/D6TEYyh6iv4mBbquio0pvlHZqvg+Rd3BTGVxD5145tmCMYQYpPjqX/xFfjQ/nHbxJflRb/nKddfmRwlkS0BuCbfnv2acf1nYduHJtYNiM/CS6Jl9/kth3UevCOdWc1nyqfCrGlmjs8n0jPvxfOic5SRP9QX9K3r2OSh6CieTOm+1EQZ1OmqtCzLsKcG8nCoC96A6OBxJ6lKVUkFSL79PfRfem7rf/7+9d4HW6zzrO/d3PUd362rLkm+yfE1sOUBJKfVAsIGE0EAmoU0G7NApYdJi2tKQOGvW6hqvYc1aSYBewB08BAq2aUMhwbTTNm3HHpoGBtJpO1Hs+BI7ki+yJFuSZcmWdM53nf1+Z8vfOef9/Wy90tE5x+b9Z8Vnn8+f99n7vTzv8/yfm5A+BCI9iUgKqK2M72tkB3Uib6zlQ5ZSgXuHONWp83x8D1OAyaCyPUQkE63/ADWmAahYi3JFyr0pUtTkw5pKITmzghWT3ivx+FJzowCSJd0DB6uruSDHSnMjRx2SMWFEDiluGukJxq1FxdF3TUYRGdqGtFhTHGmObd7pfa3OIZEappASMV6bYDnS2R8TKxOX8jjWVsZrfQhp7AGDk/GzpRCO9t3OQViTMhcYxSRygCLdbE1jCqzIopSOySt27Kiuxmis5+/WWvGaGhxjxwM1nWtfzmmtvQOxrG5sYJKrdyheJzg/JWj9mqwnZ25XupaT3LIoEEoBp+jEAEzVlmjpyevjNNy+zEVnT+xUsXIqvaOxjLJmYLb+CGpMA4zMJ5D8rbd4vyGEtCcydDjFRGT/ZPxdkrNK9sE4mu5AUGcarTPTRUk+iJ5CJKCR0lQ70yJ8KQLPzm1aTykkInagL9E9GDto9RmAmNbsNLNlACQHDBT5aMRgil6/mFB9C8bMxhdJKiHMcQ8k7DdzeFK5JbUB4POUtPnzBVs7JKPM1kwpS4B6su0VsuUT9pWB7DFM0V+2pOfHi9BV/XR39cX6+f+dDekZlYCcnSEdGoP/fLHntt8uPn5DNSevS3qeJknH2dVEes6t6VkUN5d/K5Oeb0HsfNf3VVezYAKNDMAE76YZVCTQUpQYI0jPl3dzOYDm4nwpKykkrR30q3bdWF2NkTLHzS1MuAwohbVhh2E8Zlb3r/tifHhbgwKCeRsxesdEF8yxKkfwXU2hgr+n3ZVhLjSdE57NnpfG0jzCJGNMORpQB1Yxxtvb48gmU6xprdp+IzLfIipJRplS24SUcTLKbF8RNEoHxtciZknWmxwg5bMpEW2EGt+2RCyjiGQIoAZq1vyMnHqmatD67UoaOkaGSHp7A5q76FwkRHrSu7W3QbRfCVrT9ZW8t6nD88ROrtdLZFJ3H0ekUbOq/nEmMOqQNk81hwOmKcJc5pjG0ogRkiXm/MDUZdHD6hOxTG7vYEK3vjIesx7U7gzoH46frfeynE0AXWcgz6af21ddzUWTGmnJXFAtVav1nBJJW4PO5Y2NrH9g/U8Z3+E0kZbVxSwMpnkcSadQ3ZfkupGTQLJqFg5819Y/RjpbenACKUHrCe2NEhQRb2QhOo7lLO6+BJHgpscR5LukL9m+qq8U8hbQfTEmvE03WwiC6HxAMwXhec1xQHXczd4l+ZCi15gNQA41cxiRjDP9LiUa8lyJQbMJa6B/2DmGzyvfRceBzPG5IsnepcAJ2ZdPPPBAdbU0uOnv/b3yn+Hdwnss3s+v/YNfLn+OEaWZz8JrRGNEes4vF5kS6TlDeu65bZxh/UaRnqdJ1rmfLS8s00ZGMXs8xvLoDrXze763upoFEzxkLIrRfK6knAl2EjKqdGUsOuzAWA2NjOxApns0t7ACQbX8SmlQXczF4AQdUHxwdg/EirFF4BGhpWlcoNy0IHU0gMbHxgz3i4hESsO11DdSuJUgooi0hNqOasARmSTNifCdZRwwYkSiQOh5TZ5hVJER0HAPI0iJpCJlUCOrYBwsUp/kuqVS9mAuJi67tLqaC0yFNwMb1lmTtwrCmrD0p+J1phkAcI6ZM43GzBwlaBDJOqU9r4Y7rV+5L61f67pPTV+weVoJItq0qVoPPqeawyUGx0AmD0QhhdIG/VdlXwGxYY4HIkFMB6I5UnIGDOzWBZy6T/U0J3YKMQLkb/fgmW+i7sH4bwXQuZDiDDBCgIg5+y6dF5aF0ILzTQ1skp/y3fokPS9/l1LOSR5a5DA5Okz3pfPGGiSh/JX3xXkXfYDm3br24xwLCUPkjJ3bTXIYGdlN9o0QK5b1gKD3MNIInkGdmEBa9qQ+PDlgVN+COTLy93wFWhC5WIfGewGDV+NnM32LdD4jf1FWy7yhHmXnLqwHmwvMCJExJx1mMecngHREld9ybhJof1uQBMF0YjoDbC7OFCRzAp74l0vcyGgO6Xka5//3+aTnGWFEYh4oPjq/W/szHxiluKfV9DyzSM/5BGf0N5YZ3kSRnvM6uS8xdt5ya3U1C6LEmMJOMKPhTGEKaQMiAax2SoqCRkjx+JwvpHhjU7yQKYZwCuxwWf1t76iuxtCDk8g+Hoaif5IMbH6H5ob4vt3DfPA2N8aKpjU9osjHrqSSkfJpHlZaf7Z+qeu4Ksvw96afj2u/BVCjHGu8hNFntodgjkyxnn46Tnu3Uhk0PqYAU0MEM56QXJS9SevX5o0UQlMG6T1I8TNDjfeVvAPtY5EZ+F0jCWBNGlFB+6Kxmtc0pVRTZFXAoBPPhaVd0pjp+MJYaoRvgsJOf8/2NhFtlqpNxn97CxNig1PxmDU2itwCRxTVSRwBmhMNpeZkHTuRy/kI9Zu7R5n0ob1pc4yOByKNSiC5IvKQZKfJLZKdzXWiL22I37m+htdDDyI9u/tkD8G6nnrqW9XVG8N0KGraYrogjY/OBcgYcxzQ3myuZ92Xopo1zR/OkMaqeHyHwgXQuaDEFXxOHdIDUs4FgslOilIz/ZDOFtVF4XO7LzUrtCj5FMKG7BuKpgygxklWOxzPeJFF9F3Tqcn5ZjoJEW0pOuq52n4B9B4N6bpP9l+tLnIW5ICtdSTaZC5o/arzGvaF6VYUNajBFyl1wgFm79KZZXsTzyyzAejZRNajw13WJMGeF+0F2Zs47iB37FxZ6kZGu37u516jJBfz59f+4T8s/5mKGaLyrh2n63hWxOTpfjgPp3Vvn0tylve+8xPFXcOfyJGei4XZjPVSg0hPVUxAcKiBDoqFKa9nKkwCSCnuUarJAkCf15SxcwQeOnZgkOKXoKjafWmOF+J913znX6iuxrDxpYOvsYqNmWE3fo/6Sj4Mp5+LoyLaF0vUC0Qb9V5iZYPGzOpnmVKKgHFQkgo+R0WhBB70YiySWFXPLSgxFlVBz+D1vuJ5wy7tAnPWkCwhpTgAZZSsX2z6IgQGylQhxEjW0t60+aH9psog3MPOBTKazbBsQJqp7Ql6X43Swdp6/LxkwJmhhjLKZCcZPnI+0hzbM9BeaVEt1xI0Rza+pNw3LjBDIn7nzj4mz1oXxnPUWM97u4AGX/2XeA9SBOn0PjbcW+vie3Rf5uisFKcgEUdGtJHc0bmAz5WsJt1K7tu6OB735maOPu4fjceM6qgGULSpRSh2IJXdCAw6x6yLN0VtGTDCXMaM5LrJ1BSSimRta328V8jBYLDoTZJR1l0cv2vnAsgiI2FIng2n+BloD5quQ7LTSMT2tourqzGsxASW8pG5pKwWrfkOf0/fDdZZytoznZrWCZJvgoE5BWHch6K/pJjotDcbF/C80ZosrBEulJWx85xKYhjoGTR6E+xVapoUQGcLlXAKoH1oa4ee1/Q7+q4GVNC6pvkR4FyWWAgblJBC0qLcgTEzHXWpSc+b/u7PlQ9cXoStsYg/z470DJibLR1S309HagaMeLRPPzXzy2ud2A0VifrVcL2zuP3DRXHfntev6bmcGo4T3lw1PSF0d6mAjYyMABEBeqZQAgTuqwIYhJ+RBBkzwIjBhO1iHkC6h80bRXqmkJ66HhKUZbqHEQ2kdJm3m5QbO7zJINIIDDIAIZUnoA21zGzMyBi3Z6AIUttv2EVTngHHQYhMVLosIjNhjpugRFsEKZLCCetMU6JpLOW+ZLRSuqDt1zNV2gJoTU9cfll19cYwQiEl9R9JdJkfJKBFxmHElBi3dF/7Lq0RlQNEQIg8pDma2rO3upqLlddfV12NYQYKPa9Fp5CsbmwQAwWiLIedM5+LxnrRP6bjz41MHbwCUTonWX53qKanzAVFehqxTc4P04HwvJG1g/JFDOz2ljjVrnkRp0T3qXzACV47vVdimWrnAjWQsjRnckQN+2Lwwv6m6P2A9oXx+aiGO8hPk1GkE6ToyU2oOVmb5LnsH4+fi1LmA2iNaKkN0KntDCFizzpaowPdxgb+nq0nlEVCUFENRzvjsRa37Cv63BovEXQ90ZiJHKD1b7KIus3XIQI1gObT3g3T5u0shedVGUd7UGQn/T0dX/h7Rtpj4I7pZjRvNg5yjzMFycgAnDcraQGyz9YO6toiH1Dv468WvaPxOWR6I8H2Zso9aD0o/0GyHv57a1a45JGef+fvzsxF2IaL+HP3P/pH5UXGQiOTnmcJIj1NGSShaMpRSqoIChk5GOoT8X3NY4kCzQxLeF49vJcplESEOTIC71xhB0ZKejtF/FkEHq4/GQckSGX9kvFv5Bkpc2hIl0CvvxFPsN9MSaS1amNG+9tIRCTrRNTSs1nTI/oudUQ1mFJLSqkpR9TkgFKEAkh2UYOZgKSangmGC62/PiiO9t+ngNaDRVYRkWNnCMlZTTeEuayJiOufiOfNnqELBpzJAVqnRh6QsmuGO42ZqTD0DClndMMcBHTGJhhkNmZ03jTW8bvVWvG+GkgtVsLgJD8DPVv3KGeE0Jq0WolEeppMxnEQwoXOvJVXX11dzQWuE1k7tE7svKG9VVvBsrP/MoyvOM5oLE3/6L7wYnX1xqD9ZkQOkZ40PyOQniC6Co276rkw7qQvadYFECsWXUhjbvPe2ReXtmlu4tq+eAYd4whqGkd6hwCU9SLjaHztfJ249JLqagyL9CT92Z6XxtfmIsW5hESx7BXS76xGN42v6bNU09MiEYdd0Pks+hhg9oKdbwQkPeXMw7EU2UnyQYlMkiV2nsuaIhDxag503APyt0gnVp0N5IatHdRrbK3T+Mpa13sA6D0WQicm0HleA44i4Ik//MPqamlwUyA956OcgrBM5SRckH//tUx6nhe8qUjP0/UHlkN6O5KeouChoLTDBb6ryjYp5m0+DEkxsfR2MkaWAzH4VkGKorr6RogTl7lIOZBTFGAyhO0wpCYq1PU5gFKgrD4lvZspfj0w0nVvwjhYrUTsgilrnfasFfGnvWWOAxp3q4lFOFdvbkBzCzS3IOW1BMoSeQaUiXI8Edlh65cINHouSwdVcgZA40vjFYCEu0RlUKMcm0tae421csxDF3BrZNR5ISZnzBBJeTeLGCHQ+tc1TXJS1hOuHVnTuF/lHUi5N3Km1orv2z3CJCIZ7s0N/AxdaFDe3MLv1tkfy0Mjf2kuTMZ1wZlFESsBrc2QUi3PgONg5Aydu3KWkg5jqjISpG02Nvsvx59PQ0SnwZwBGO2fsH5bIqOoHASS/iVwz1uEOSDFOYrRZDaXIKPsfKV3M12HdCgdG/iukVwkD9WpCO9mOh8RcFpOBfZQU+Q3yVmTA/RuNu+0B80WondOObdtPaAckFIDtjcRZDfJ856r/mF6MkGJRdJzZb/RHjDyzc7N8wHTE9C2sHej8ZU9byQeAsoKqDMX1qrJKNov9m52XhDs750paL+ag2CpSc9df/tvl/8MYxPmY/F+7v6Vf1z+zFhoLEPSk+sEjPCG9QcWD9i93Qx/UkxE2TBvFAGFrUwnRVdZI6OUZzhXmBKD3u7ztFRTlOUUqIIGc2/pZSuuvLK6GsMOb6o/lEKM2NqhA86IK1LQrIEPKYmahg5Gma4HWjumUNLeNOUIlA1qWBRA37X1hMaPfJcUNNvHKYqqGUqEWgsUKVGs0TCUfYFOHFEoaY7McMExI8XRIt9hLpS4hQL6ZvgT0Wb1sJCct9pZtKbX8NlUa8I6gyY5Ad398R5KIfJt/ePaobUbkHDmkXzRcxcMb3NEocIuhDvJHSM9G6D011bxmqamR53nqot5aMdBW/rd5rr4GfqvsoFDc29yliK8KDIqgKIOjcgkOWDnApE2GokIa9XuS011am0ZB/Azk5MuYBpqelp9yRqsP5LTAZTOaamfVNNTZXKC4wDXiZ3nIEtoD9pzYWq5kC10Fqfo6iYzyCmoZB+MjWVlUZCD6bM0Pya3MAtBxoHerSe1LEkmW4kgioasWYoyEJHUvHUEeF4lPWGd0HMFpOg6tNZTiEHTUekeddMT4Bks4pX2mxFf5Iiyc4FgUaGLSVXYvkghkFMid9EmFNIUG52lnGOyzojgtz1P97U9RMBIT9nbTzzwQHW1NJghPRcfu3/lV6qrjIXEmyu9fRmBGhkZcYBCxpRBMvbEALQaOwRSjiyyCUkjMVhTgNFgMg6EhXgGQornC+cnEXSQWKTcqpviQg6m3HdejFPcWhs2VFdvjBTiyw5OS1Ui0D0srZtICYscwxQoEXM090bkJNU3A2jzBIAaZbBnVdkgxVrWL42lOT+omYYROehFN2cAjKU9L91Dvc9nqKBZNA19t7mR9xUZIxZRjx1yRaZjQ5EVLLeoM3J9kvdrfXU8x9SYJaD/SjwOtt9RVstckkFksh5JroT7GnDc5Vwgw8fkIRLQYkg01xFxJecNEdPQRG6EQXy21CDCN6D7ApxDpiLCmWXr4eTDj1RXY1g9QXQcSPQ9PYMRAjRvZtySPKtN8Jj1j8UyrrGOn6F3NN5DdG4H0Dlv5QMo0hMzE0oM4BwiWRRAEba2N8kJY/KM7qE1ukHWE7lItSUDUqIsaU2aHElx7CDku0QMdqR8AXU4N9Bc2vzgfhM5QOejrVOS1daIEcl5IYLovuYMJvkyEGc77UGbY3SymX5Ia8fOK/p7ps8isc3PQO/WPcS1bglmh9De1NRyktVy7tI6U9stZXwTkEJ6UiMio1tSSE96N1u/5CROCWpS0hPeg9a/gewNTW9fatLzZ3+2ulpc7P7VX62uMhYSb65Izwi3FvctUX1PSm83JR6NA6lvht5YiQJEI13IM7qHRnrS4SL3TVk+aLCKsZhyX1RMjCyB7xoJkxLCj/eQZ6CDyJSjVbturK7GsOfFQ93GkQ5OOQxpHOwwpDk2AyOFICWkkFy6HmAczFiktWPzlrJ2yNgzY7wLBJrNGyqEso9xTcH7BlAjI5IZAUlEJpESomSifJD7UkRNCnlMNfDMqKP6ndawYuKS7dXVGJZuSGPWulAIBWhc09rG0db03e6B2CgMIGdAyjo3I59qfRpxReNg8pBkn0aoAzlPhH0AzZESeLCPm2t4fIcD2K+ClK77jQtIT5BzAQjS7ov8XN1DcVkNc8Sm1PTUqBUCzLESpLD+jPQhWW/rYeKK+D0Gr/I+nn42Xut2DpL8NoKIns0i0igC1OQO6Ql25qEzV/Ym6mwyvmdMbMvZRjJqel8cRRtATbTsLKa1Z2cbPZvqW0AeKNEGNgDV3A6guaCyKQEp5za9m+l85Pi1cRh2QMbJM5DMSLGx7GwinUSfl84b0V9w/ctZiveVfZXSgIr0JasvTPVnjXA0OXmmsHej88LsUoLZlPQeSc4LARJ7Un6ObHEjEdHhbrIPokJTdDaT9QS7L40vEePGcyx5ensmPd9SWJaRnqF25+cum9tm/3Tr/IfeNf/zpcHOd31fdTWGHXCkhFgaL8IEGhy+dJgGkEJqRj4RkQtBetI9lsPyszFDA3sBxoFINfPqrdi5s7oaQ5UCUDZMYa81YC76/A4UKWHjQAqPdkqFcbdoSDp8VaGkdWYKO0DHFxQhTTGG9WAGKxLQsjexDp68G82FkUlEDFo3xQaQnvS+AUlrEsZ9GppFBFANOhszUrqQJJBaXRQZomuExkEU6PZFsYFisogUaOsCXqOIPyO5IGKw9yKTcrR+LUUTnR+ixBOBrGQ3jKWNWSkIqosxLLuhCeVfjHzDKED5LpFJKrdgndbXyZqcjD83oo0I0v5LPMc0ZuqYBCPUGtHRfjFZT/I7Zb9RVHQA1SZtruf70n4ZAiEcQPtt2I/nMqD3UiyjLL2d5JkZlqRPmqxHBzjIwwA6d83hSXNk9bFpnZmRf6Znk0Zbk3wQmYxGPjzrCAk6H8FIf5p3yz4heWbkARGc6iAg/UVkPcnf6f37q6u5IAegkn0J+ncdbCwiTQOIkLUzhHQVGweCyXoaX3UCJZBy7e3bqqsxUhypWBu4hOlWBPquym8iMm3MSM+1sx+g8uE8ATPR5HmJoDf5TfaCldUw4v9MYfsNYd+lz+EdzFmz1KTnjT9zR/kO5UXYsov48+t3311eZCw0lmmk5xeLHV/4+8X759u1D99f7Lp/67Ko60np7bZpkdQQ0pMUAJsiEopG7lBqi5IwgJRD63xhsZ8BBb4IdlJCLIqE7mGK6oqrrqquxjAFjZQNU9CIaGhtZmcCGQJJ6WFiUCHxJN/Fzs8mumB8B9N8X9qzqKyUoHEnhXQEWScEIoWNRCElxmrjpZCeVP/KIoVorRqRSYS5GTkpSmJKKo1GEM2DHoXwuclvqp2shhrIByPRiTxQogLmojbJczmAI8DISZKzNrYkD23e0aCSfUXPYE1uiJy085E+NyOJxpcixEaAcWht4khyJNp4yLBm3oq3yzofxuvP0tuHnXjM+sf4HDv1ZBzp2ZJxmD5woLoaw85tJNrEqCMCzhqu1NfG89nczEbh4ATIuJVs+A/7sYzqv8RkEnVvN/IX16TsISPFCDaWCJB9FjmGcsfOQfhc9ybteXgHI0uQKBbSiJ7B1nR9RTzvtBYCTF8ipDiX6LsadQ5nv+modLakOGvsfSl92rImUkAZFqbH4doTWUT1E63mJI2ZPQOdLfYMtCZTdIqUhjopfR5M5yPd1d6NUsBpfgLwPBb9kMYnxY42kpdsUCOg6XlTbFha0wEk+zTKEsYnpYSBQeU6gMad9oXW9PyDP6iulgaB9AxvG95i7s9h+bMGny/Mv9/9TzLpeT7w5iI9Dz1Y3PHBA8VHlyilfTZSurdTVzIjYdCrJwcGCTQ9XAApSqaBFKHz5VFTDyAZ2PJuKUg5OAlmXGDdnYRIT4uCIoLJvkuKBdbCLEGEo0Vw0LhbIyNaJ3Z4o4IlB2+KZ5yUOTMayOC0qCIkjmzt0FwI8UQKi+03UrCMpKVICVPQ0OCU76LxY8onzLHtY0oJNfmLBBwZKDbvIA/N8UDvZu+ABpGsU6ona+uf9pA9L80bOhhK4DgmnBVafkDSBQk0lipnZU0ScA/J+GJtPImmIfK2dYEYwv3472Fqeon6mnjMqJZrQB06ifePM9lB6e29I0J2wPjaXFIEqBEjSCrL3rQ9S2hvj/dmrSl784KYZLLvEincO8TPRfU/NeIVxpdKCgTQd+1cQB1GZDJFAqboFOaQoHPIiAYaHyTwjGACmaEEHryD3bexDvamNIEbduO/N3iVzysiMlNqZJpMJjlp70ZzYfclkkt1PrjHUIg2InpTbCyV/3QWytlEjl+NnjtHe8yel+5hehzuQZtjGl8hMlEfNV0S9jz9rQC0HxMcqYYk0jOBRKT1Z/odvVvKmmyuZRsL9WSzb+A9rMY8jruMGcpU0Zdo3Gkc6/K+S056/q2fqa7Ce8x+x/P7+9f/939SXWUsJJYh6TmTxr7ntnuLj99QfXQaIdLzjmLJ6njOxs7vu6W6GsMOOIx+EwUPD+QUwWPKHHxuh/e5kn0LATTSTagmKBYpQEMi4W+ZQondr8VwX7FjR3X1xkAlMYEQ0AMZYMQIzpusHTqQzVDDFHlZkta1lkD7QiPH4KA2ophSKU1JpHmzulGd/XHElCFlH5Nxa8ocNhpJMJ6oPlQAFdw3eYZzlCAn7d0IauQD6H3tHeh5m9Z4DL6r94V5V0eJnUMA3K8JhoitaYqmsch3UtitS2/KfiXZZ+uUSiuYnMUagTIXjQ1AKp8Q+b06nrf6JBuLFHXYvJBJFPpu/1U+d4mg78t6MqOXQOssJaXaiBGSUfVVbNw2L4zTPAeneC4634rXqjUy6r4YE3i2jylyTIlBqsUn5w2W8ajzHFMk4EKUciBY5Do6gkAeWgRqCmmEBIiMua1JAq1JK/tDZ7GVQMC5kHMQvyvnIO1B0w/pLDWnLX1u807ZFKabEWzeUsgdWr9mA9BZqCQiPJutJ7IV7dylv1e3ZlekF0n9cXpe29u0tyyr0M5NBNw3JePH9nyKDkP72P57dYwDSKc2ezfFTiN7CktklcDnlTFLadJEoHm3dbrUjYxu/Ft/q5iJyAzvF+bkdITm+f19dyY9zwuWZU3PGXLz6eKuOdGeMw2O9n5qedT0vOr7f6C6GoMUhQAStnpgkGIiwo+aA9jBSZGEKvwSyJLlAFRMxLhdTKQc6EbCrPmOb6+uZkEOU1pnKXX/BsdZoRx04B6yHijt0iJ6SPm01FqMpEpYk0Z2kEFlewijWxPmonPwYHU1F9goR9IViTwg0mgEUsZEiUFFSsa3AR5ZIz1pXVs0GJJBIvtI0bQIATIiU2QGKtbyXARTUvEMEDmAxowZgKaoEmAc7RzD9WDzQ4ZwQkSnGcJDyJCgepwBOL4mM+A9zGCle5iBQ6RnivFla4e68XeP8Jg1N8DZ/xJ/l2AkIr2HkZ4U+dU7zNFgWCZIxpfOWO1IDZ/XV8o+hpIA7R0svwcn43ViUbe9A3FpA5M75AyzNGeSUXaW9sFx0NwsdVCJKBa5Q3vAZDLJKCUJYL/RXlF5CN81nYTe19YTkl92vsK7mRzAe4jcIj0hhRhUMpXmUs4mGgdb050D8R4yEp0ya+xsIh3KSE/6e/a8dIaQ3RVAeuNQ9iDZU2Yv6HkMoPMCz8EAOsfku0jSmn0D9zVyEufCzn7Yx4blYMPiuSn6EmX/pdTjNFmCZauE2CaZqDKK1o7pSyQ3YJ2ao3vJSc+/+TdHP8Mbz5698/3713/t16qrjIXE8iQ9R4i7uN9+N0R/LhGu+sF3V1djmAeQBIcJdlQAZIqwhgwRMyUoigq7v5cgxVqFHwlxOZxSlpr+PQAd1CmKQgpMMSeF0BQIVIBF8Vt1/XXV1SzIM9BBT3WnAijCxepRDafjA6p/gtc6dvRNIH9NUaUUt5T9pgYVjJkRLhMXX1xdjWFGA9ZHTdjHls5G69rqS6ZEE+BalfGlfdy+CNKvS9AcWbF86mxpxBOSQSIzsO4ZGLdm1PVfjpVPq9+MZKwYVGRMU5OzAFrTGuVAa13WHtaTFbmFUV9CytEZYPel8TEnBRmcVmKC1o4aTjA+ZtwSkWn3xTETw4fWb2MVnwu1Cdib0mhncIoMYTmjhawjkA5je4gyA0zW0zhYLUtcUyIH2rRO7LuXx3ursVai75+OZV99JT/v1FPxfU3Gkay3NYlniJHK0IjOzkda6xahOLF9e3X1xiC5YToxyW/67+1so72dsl819Rnua3prCmGTIuOI2E4hozRtHuSvZSORPDP5TU5bkwNon8h5Q0EdOg7wvKZL0hk77PNc4nks807rN8VmUUcUfJ6iq1h2A8lJO8doPm3tYK1Q+S6dLWoDwLlgethiIqUci8l6lDEivylAYSHqtqbYFuhYh/8eu9KXWC6k52Ijk57nB8uY9FzeuPqH3ltdjWGKTYrXCRWIBOPWDkOqeWMd5FMicggLofihMWPKZ8J9zxV20KcowPRuRh6s2nVjdTUL8gw0Ps1NfBgOO2B0iM5FkXlGYNABp/Wd4Ls2DinpewjZb1RbtyVRLzi+ko5MyliKtxvftwRFdVrUCo2lzRt5WSmqLqAG303pYq9Rt/RdIztIyRM5QEQijk3C3nZDDfaKyGSM8BUjlIxenXfYFyYjaXztDCFCIEV9sHMFx1Lui7JTnhcJdyni374kJmywREUJfGcZX3IgWtMjNMpM1gO0gc9kPGb9E7x+CarXwDqzKG4irnQPgZxsbdlSXc0FGchJZLXIIlrrzU18NlGDI2tk1DsOpI+cC0ggy76wqE4CyU4bB9pD1vCQns3kN8kYO/tx/cHfsghHXA8m6+mMTyAqjPSn97U5Q6e4kBpdcLoicRtAa0fWU8qZR/Nm5Sx6QKqpvQBrz9bInBCpCtpwC2SGvRutE9uv5My1ubCI7TOFEYMU2dcURyrB5DeNjzW6xDETXYWIMrV3Za0SUmwL0utT9BqDZT8RSG6ovQD7RUt7gFw3WZJCCuP4CAGNTkySs9K4aam7t9/wsY+NxEt448X8+fV77in/mbHQyKTnWYIiPU1IkTKmaT8AU6TIAGxIXQxUTKi2Uwkzps8VqGzLu70VYIoJrQfrXLcSGhnZoUXKRmO9HJwtOOiBCA0YnIyfTQlS2APWfIGiN60hCEWy2PimkD60/kxRpc9NSaT3SDEsqfFHABGRKQq0GRiYmiVKTEr3dhpfXb/wuUUekAfbiCcCGshGMMERaXKLzgB7XyJjUxriWPoTPZvtFRrzFCPUgOMghDt919QSGkt19MmaJKhBBCBZZP89Gu4yvjTHKuNoLGX90zhYNDuR7ikkiq0R+twa+BB50N56UXU1FxhdkrB2zHHW2gxnNJSECahNxGNJ3d8Dei/HB6fpW1QOxQxpOofsu7RflNiGdW1RtxaNiABZq3XCYa2TU3Bi27bqai6IsLEzMyUDAL8r65/G0eQAEcW2X7EkjJw3WBpHxoH2lcnZJGIQCGisMVuC9oWRO1THXZ+XxkdkBs2nZdYguSPzlhLViY4+0VUouCUpulBqb1J0oOk1NBfmQCe5Y2snRa8/X0h5BnS2S+1kssWVhIS5t+/SHjAnsc09AclqsYUIpJPQ2g1Y8kjP/+lj5aCVF0FELOLPTHqeH2TS8yxx9Xt/uLqaBRF+KBRFkaqRMS6KVIrxlWKEondIFDT6e6agLQfg+JjCI3N0plClC8bS5m31O26qrsawuaivjp+XutAG9I7ECnv/FV6/pGB1JSKNvMpmCJCBbWOOKW6i+NFYphgjFolFEavtrVurq7nAvyfygRRgi/jrvvBidTWGKZ80ljpmpFiLfKhBKhkpYgFk0OiRA5/bviBQCY8Aeg8am8EJiXKA+bG9TXK2ITUnqVxCVyJ82xfFpI+tEXxemUuSRWaQYU1bkUUpRDwZ4xZFgqm5skbQ8SDyheYTCbUA2Md25lEqsNbcg/uaIULd/O15ac/rHoTxsUh9KqWj0ZswPp3n9lVXc0Hz2bqQIz27RJDKd1sQle+kcryuGxvZAVNq0dXFGN39TDjSu1mtZzP+EbSPLTIvgfyl71oJJTzf7L70uZwhdF9cv6bH0doTR2wLzn4lCYDAM4KJ3leJTPh7JuOI7NaUVFpPCfNu5wLpOqaTEDFivQUQtkZIdzD9Bcro9I+z4wFlquhxSNKKfLGxJNB7mKxvQEMcWztkl2rpIbiH3Zdg5xi9m8k9PTcB2PzJzrzzBJIFtPZGgGezNHTiA0xG0d+zmp7kzDK7KcU2Rl0Z9rGSnksd6fnTP13+MzxvmKPF+/nwr/8f5c+MhcYyJT1nOrjf9dVwvbNqaDTz2UPvWh6NjK7+K++rrsYwQoGMUyOuMPLGDFYSfhZxlUDCoGKRoFCa54veebE9dQSbC3o2nbcEpQvJalES1373X6quxmis4TGrQ82xphhqwz7MxSk+OHtH4sN7KJ1su0fitW7KCimJRvrQuKuRD8qYedyp1pClZlE6sq1fUnZN8SMng0VBdQ/GEaA2vui8EEUVx0wUHjSqWPShgmXd9alWVkoxdwM3MoL3FaKNFDzstlkCzwDZ221IE7WoOjRQLNKTzgWRRUiGmowjyJlHCrQ156LnNaWaiFeTA2hwyjlG46MEKXzXSF5Sr0wW0T3MAUMw/YOMLyWl4Z1V/6B3Mz0B5thIe6qha6QnPZuVJSDD0GRca1O8D1sXM/lLWRP9Y7yPe4di0t6i+nXcAfQeSkDT53KO0drRSE+QZ3o+wnljTbBIT0g5r3DPi4xDB4Hcl84VfQb4e7Zf6d1ML8KzTc54kju2Rs45E0JkXAf0F7VD4Ny00hWkk5hOTXVB7XlT3q1Okb8SgUf3sPOG1klD6h+S/mAR36h3JugUww6/G64TeTc8L2Q94LjLHNu4LyaamzZWV7Mg44AwPYx0QXEc0NybHUL6kq11mjeTZ/Rd+ltvTtLzNBb+32fS8/xgWZKeu+/5SPG5ywK5WRQPfPKeovhE1cU9dHW/f2vx0GdvLRISac4Lrrr1+6urMUzQ0mGogp2EonwXlWJReGrN+HCwlAMMU5dlkrJ8UKFcBssvRVinwAgMqolokVgrrrqquhqjPsnPSx1nySALaF4YK9H2XTLgei9yRAMZ00psw1o3AxAJR9lvnRfjKA5ttAPPYDW86O+lELoWgUHjY+NAqVUa0QOyQL8L68+iGanTr0U0kNywKAd6Nptjkl32bmgYQrSq/i1pXECoNcC4FcV84pJLqqsxzOig1FFLJyLC3Op64bOZsg17ReuzAkzOYsSTyAwjOAlkqBnZR/LFamSiASjPe+LhR6qrMSZ3XFFdzQXJEiP96bsmiyhyxtYD7QEr4YHkipyZtE6sIQ4ZWvZuGHUoz0D3MIK0tTWez8YFYiyuiOVZdz8YvCUGr8Trt39S5CERI7LfyJBVeQafWwM1kqkUDRmAuqvIkjON/gnA+8Jn0wcOVFdzkVLTkM4xI+dbG+M5VtITYDKDZJTpkpTmr3XGYd6VWIdnMzlAeo2e8SCLei9xM0h6Bktvp/va+ic5YGc0ETFWFoyiC21N074yHYrG0ohMcg5p2QkY31N791ZX80Ay1Ro6wfNq3W34rulxbzZQjUp3SMRr1WQJOeytURTueZHJA3gGCwChZ0sKCKKzWErzPf6FL1RXS4MbPvrR8p9hH89+j/P/+8Of+/XqOmMhsQxJz9C1/YvFjteiO2eRnoceLO744IHio1++rdg18+Ulw9U//FeqqzFUmJACQIpciRSjjpQjE6qU+mmeOo0COUOkkIimdJnCslxBSpN5blOiJ9a88zurqzFMQUMCbxPfd3AcFL8VTIIPu/Bup2Stw2FoTXnIEDaijQwEM0bokKXUxgBak/YMGK0hc0H70Gqb0p63iAZKwzJihBQhNcpgz9repEZPprAjQZSwt41Uo4hXMwQwxQcUdjWSYD3YsUly1tZISooyjaMZByQH1KiDd1PFHPagRQLQXtHnJXJH1mkTiAZzGOGYyRzT2W3jgOe8GbcJa53klpEHdMaa4UNRdXZfipyh/z4Az3MZB4riM9IzhRCgtW6ynvaW1dBtrIvXTvNCJmcaa2PCpfMMR6aeejSW321p0kRNakxGURS1nWNEXhGBNwLNsdwXiTmRk3S2qHyAd06RRfS3lOwDmF6POp+8LxFMlt5Oe9N0HSJ0da/QmIkcoPfQetMAS1Emh5zJesq8MF0npSlgk8rNmB4Hc296PaEvEXh0thgJbuNDmLzi8upqDCqNE0D3nXruuepqLmgP2rtRMyVqGBqA4yvvuxzIUHxemTdaZynkpMrOhLXTh3IFdXIUliCHvRH8tL9N7uDeIvlipOcXv1hdLQ1mSM/Fx8Of+1x1lbGQeHORnosZ6Rn+1h0PVr+UeOdtc/4u1fSk6K4AVLrMoKLvitKFglLuW4fIJqthl0J6kpdXQ+JFMC81dHzP8XktOoW8xxYFuOLyWIkxo4MOkuYWiRRaHSsQzQ0cyVIMYkN2+mmOnOwfAcVYRAwZwrr2EsQUKU1G+iCRKYY7KSx20PfAYLWIEyJGjPTE9DBRwmksNWIKFA5TeJqQWmXrl57NUvfxHjLvKUXTCWjUWWQ2rB0zmkmWWITMBNSDtWilycsuq67GMCOA1p6RGrR+yXExAs2FGWrw+bQYVFgXV/YgfZ5iDJlBRRF/KUSbGcJ0Rts6I1lkhG6tDeMDtSUDekfj9ddczwTGYCq+h0Z4wbPZ+NL4WFQzyS0jXIjIVKMZDDXagwF0xk7sYOddc3Msq7svcDTk1BOxjLOzifaxEWWIhDPTvktyjgimANJp6R0CKBpRyXWQJbSedGxov4rconcw+YI1jkVu4TuQ/l6C1q85KegedhbT+BgxjjqmyDh6Z9uDtLfJgRmAthDVEy9BmR9IbpbAOZa5oPPNZP3gJBC9Cbbbueo0AZNX7qiuxqAyOgHUdNQCFAjqQITxNXK9TzaoyW9Yv1ZDl9YfRuKWoL1p+h09W12eAXUjWw/wbFZCiVLZ1WZJ0KnpeW0cUnQgtQ3mgRqkBix1pOfbf+qnqqsxwmiPhpGHfUH+/cO/8Rszv2QsKJZlevvhL/1CccsffVfx0Gd3FV95jfQMZOhnir2fWpyanuEZ7t3+94uP3xB+q2qM7riz2P2x60f/HhsZyVBSOHr/FSaYGmviaKXhgO9Lh6Qa7hCBZF0IMaTdhLUpQssUSHDKvNHWMLKaDlmL2sKoWykuvXrXjdXVGEbSkvJZX8n3bWyKDbVanb87hLT5+gQrPL2j8fqd/hYc/iVw7ZCiIEiZCwMSjlLzhrrDmjFCKYCqLIPyaTW8uofjunKaAguGixHmWGdTTgb0YKcoRzI/lOZmih+tdaoFGEBGK3WDtvkhUsIUforetHVKymBzLb9vrR2PWf8Vkckwvo1VPO+9l4FgshRCIhVE2U4iGs4wEmAE+K45SsjQsvRINMpEztL4qMyx8TlDaB1gmAvrXk1nQO/Ymc+FkjO0X+R96Xm1ezvIPovipjkyA5AcPlYztbE2vm99NZAaJVoXxXJr2OU9P/VIrLvaOEw9/Ux1NUYTGnkFpDTtInlo8htLvQhJS46DxgW8znpwvCnBT0cTbLee7JUU4opkiUbBJpAa9F2tXw7nhREHJHdMJyG5ZbokrRHdg7Df7Bm6L8aNGG09UVMd1X3hcwtCIRiRX1C3bZNxEJlK0XMBGPkoZwidu6ZTUOkUW78kd/S8ofmUcaBnM4cE3Vdrq8P4WOQk2rCiU2AggLxb0n1pv8h3adyNtMe1foyDJHDMRM6SfFCHMr2HvBs6W+isWK6Rnn/jp8pBKy/C6y3iz0x6nh8sS9JzhPmRliVuv/veioRcfIyJ2JloTyI9zfChQ4BqbAYQ+WWRk6RYWG2mBkQXWqc+U1jeCkClSQ44EuJmuCPBRIdpCYq6taiiFTtiz22txc87mI7XTnMzz2V9JRxEa8TTDE2P6qv4vt0X4oN6+ikhxMjbbYcswOYCo4JMWQYoSSWEIQG90rIeiEDTKCj4vHuI00RJITRCgMbdDC2qVZSSlj0NxnwAKXmqLENpDuuMSUo0/S2NeoH1YGuPZIYSChApZMdxa1N8j+4hHhuSZ0qmQkmLwTF+3hoNz4D31bAfv4fNZYqcpfVrpCcZ00bgpaRukrFoe5v2kEX2EQlipBzdwxpFtbdCTdsW6yrD6XjuOy/wfXFfiA5Ec2zpp1jjz85oXOucvkf1Py3ynaLya3XZm5fFkWrkpAjoPHVxdTVG9zDL+ul9cXd7Jb9oD8lapzGzOp00x1pGhPabybMtoOdCRknA4BWeo/nQRmkwDrZfUWaI3CKdTc0pkGcaXUhzKc/QPRI7QW1+aG9qvVOSUQlnnoH0jJRGXiY7k/RGaq4o+iHpFJaqTbqZ2WNk/w0kEEbPTcCKa66ursawSE+y87SWPJUrkLkg54XNDzkfLCAI58jWAwQEaTQkyQJb6/S5rR3S+2yvwOdmh5Pj1gIfUoBrVcYBSXCZC5K/KIukOdcTDzxQXS0N3v4//o1yMsuLMEWL+POR3/zN8iJjobF8Sc9lhtBc6fZiHOl5zft+ZPRzNqyODYWYD8QLSeSBed+wRpV0QCPl1YQqeY00CooUEzkEUshUTLtM+O9TYCQXGQf2XToELKqOxpKIpIBVb3tbdTVGfZKV5e7h+BnMmG9siomC/ktsLLYujo3xxlomGnovxwp3bz8rfmikiGJCJKARxbT+lBiB71pNuJT10Fwff3c4zd8dTMeKhaWUElFgxi1FOmjHTVBY6kIikuFNqVIBZICZgkZKl8kSmjet7Qj7jZQuG0eUfbJOae2oowTGsb2VSY36qthoGLzKhjsRV/U1PD+DU/F3Gxv5bKL7DqdExkEd4P5xHt/GWrovjy9GZstZSoaWGaGd/furqzEsQibFmUDqlaXxkizpWl062FdGrtMesvOcnrexkp+3fzLeQynNtYwgpdIeJreoQ64RWnSGWIQXRTg21vFar6+Mz10l6iBrondcyA5Y66Zj0vmGhmkJSsNtSaM/2gNGPJE8q68VsuNUPJZWimfYidcffdY/zroOEYN2btP61ah+IpPsvIIzwByb9Gx2hlgZHAI5UIxEp3FQBwy8s+nqpPOZLKIxS3E2qt0E8sH0DNSh5BkGp+C8keg1emf67wNMphIo0pNK44wA7zEFjpYACpCp27vRfpHzkYBlAgLgea3cAa5JKddEa0fnGOZCsyvhPDYdE79r+4LG0vQP+nty7tLZb/sC0+YF9Awk4ygYKGCpIz0D6RlGd1j+r1b+L4zSYvz+8D/NpOf5wDKv6Vl9tNQYRZ0WxX2zGihd8/73V1djWIoyhcQ3JE0pJZoMhaJMJx6yUtMzxWuKxJM8A3ksTYEm5dEOjBRgNIIdLgAj2kjJ08MQ0tvNO7/yGqjpKQ2H+kfid2tu4THDVL2hHHANMG47vE4bq+M1NfUoV+BNMYRpTWmqNigApoTTPWydtaELponPeiueixrUUQ0gA7BzkGuhkTJmxi1FjlnqkEVJErCWn+0hGB+LTJ24NO5mrnse9rGRHahQwjgqiU4ySpRMNFBEcURiZSUrk40LYwN5CITlCECsFBLZ11gbP6+RB/TKlsbbPxq/R63Ne6UGpTKGJ3ku+ifjPWSkBEVHmTGD0SlyX2pIZoQARtCJzKB1YlE+tNbN8dBaFzuz+qdYZjTWAOHSgfVfAok2GV+S66ZnEFnRE5nRvjiuyYkd3Q0yF0Rst7ewfGlsAvKK9mCJ7rMxyWrzhmMm64HkjqaqwhwZwU+RgM01ptfAfmnyudDcSA5PnoveYXCGwXdNbpEcsHFMKQmDzmeJBh724/UwPMX7ip7NUuFpDxmRSaSRkf54D9nbqsMDyJmr65T2ZgIRpLWI4d00AwbmwqJuexAsYnUKkZQTso/2tumdK669proaY+LiOLp8BBjLqWefra7mIoWUSyFpCUb+InkGzxWAEbpyX8y65OFlmWzyG8ZHdUx6D5ljegbLWMA5kvvaPiRYLVXCmZbLs9ItS056/vW/Xl0tLh75rd+qrjIWEpn0fCOMCM+ni7vmPc91H/pQdTWGKVKkFKgAhhowpsSQ9629net60TOYUkAdojWKNcFQIxjJSwecLVX0jBt5BjBvFhKvKfeVAxnrtgoZtfYv3VRdjaGp5QfjOW5ewO9GkVi1tiiUgNalUJupxHAqfmcyWgKGvfgZzBCgaI0Ucr7zAj8vpTm3gNwMoH3Y3CQkCswRdcw3dPbzfqM9a3uTPrcIlxRnABKGIqNov5B8CcB9KGVASG4Y4YK1zOB9zXg79eRT1dUYE5dz9ASmC4pBRWMzcSkb7s2NMbHSf0XISSA4B8fYqVJbEa9TiiodAYiGWp3HfHAqVu5rTfnuiXjtTO3hNU0lAayWH86xyHpcp3JflA92XzgLNZWenleIoBrIl1qbSdr+kXju6yv4vOm9dOaGJa1rjUQEuaVprTAOXZHf7W2xQW86BZHYltZNssS6rE9cdbC6GmMgpP0AzseBRijG688yFpCIMb0GYMZ4Sh3I9qXx+DYv5EhlgjlQGhTlTpGeIuM6e8FAF5lMxJ7ps7SP62v4u4NX4/HtHmYinwgFI+U6cA9zFJJjpimOeSLiW5tFV4eyBMOTvEY6UNPTyhLQ2iM9cASYNyOrifTpS38DitTUbBmo/2nfpRrqSZ2yheSi9PaWZDfQHJsONQ1kqDY0A/ltQQd0LmjWBDybpcLTHjAZR3qu2rvwbpalh88rui/BdHV6DyOareYpguZeZD1FQOv4gvykSNGGlKV5/Pd/v7paGmTS862FZZneHlLJH/zupavf+RqE8Ay4/sd/vLoaoycCDWs4CkmQEnlGgkfrLYEAxdpZJUhYD8wLBIeAPS8KcTlk0bMo3yXYMxCU9ASkPIMBFRa577rv/c7qagwjJfpH4vtil98SDYi06Bs5CdzXxFXcZbpoxO/RfYZT5yiCiCIiAkhpMsWERJpFQ5LiZ6TExLbY6B12WHw21sd/ry4lAYadeK1P72WigQwlIyeJtDHFhIgCM0ZoHxsxQgqP7iE6iuS7VNrDItdbFwJZQfcVZdtkNaED3deteQ49w6obLq2u5qK5OSYPjPSkCKTaCnOygQH4sjwvwGQREZlGetLf63PmJxKcRlaTIWAEBhJlth5gD5mBQlBjHNaD1b3ECLxNPA5Egndf4O+iM0HkC8lkcwpSKu/Ut/ZUV3NBtb36oqtQervNBRmAlo1BmLxconkviSN/C6lP2TsKUbeH+Bkouk/roEITtxaMzQggZ1NqmzbW8PptQkMni94kMn8o9YEb6+Jzsz4Rr7Pey0Ie74ujJ3pHWHcgI5/qKQeQw0gdx734vlqehEqDvCr6S4ITlM5MKpMRUJ+Ed+apLAqoX0ulUAKom7/JZNQdRL7QPey7ePbLGYJni5jMRJxig8gSpC9ZpCcSeJAtFtDeGke+YzOyAFjrNhcUrWcR6qQvmf5NOqaSZzBvpvNZ2jsBA05kjqksXcNIT8r8lPviWpXvEkynSKn1SeeuZa+ik83eDfQH2leNdbxOl5r0fNtP/mSVfl4+9yL+fOS3f7v8Z8ZCY3nW9Dz0YHHHLxbFXVXToCUBpLTPxo0/8zPV1RjmVcHUI/HY0wFlEZkkKI3IJIGGYeclqKanNeVBxUQOTjJGzANI0AgO+XtnCvMsFvC8RqbSs9lzkcC3RjBrv+sd1dUYVp+v/1JsPNXXMCmBtfigYVFADQwMI/Cam+NDtgfNjQKmnwGFkBTSErQHTDmiwzclKlRrV8F6aF/Cjo7WNorAYAV4+sk4Ort/jNcklQSw9Wtyg4AySv57Sh9V0gcUHiVTycAw4gn2myl+JGNw7cjaQ7kuRBCm/ch9WxvifUFp7AG1ZrwHzUlBqANxEIANbSQ1l8gHi/QkQnZwgvdrH4ggqnMbQGdeSnShnU0EkxkYiSVGM8HSr8n5ZmQU1ZG0eRucjA3sxgW8t4mspoj8EUjOyvhS7UGL2iLj3wgM+tycFESQ6t5MqGVJBEZjrajVkP7cP8b3JQeX6Y3dF2Pi1fYF1e80ZzmNQ2sLy/rGBbFDormez0ckf6WmeG0yXlPNLUD+yjrtPBdbEaee4NqFk5fGTqf6Kl4jJFMt2ppqvA6n+bzqvRyvEdN9KXLS9goR/Nq1HPZ2czPPZdGAM/4F1qFIz7BIfbQtZL/SWlddneSD6BnkTLCUddIJNNIOvmtlf5BElDkmB6/JDCSexA5BXVDGTJuEAbpgr1qTX9oDZjelOORIf9aSORCNqyUMiNgWmUzEq9nGtI/NMalrFdA7DOexnXmgq6SsddrHlAEZ8PgXvlBdLQ3e/pM/WV0tLjLpeX6wTNPbP1PcV/0W41YlIhcSo8ZFn69+eQ07X4v6fPtP/3T12RjmzSISxRRoEswaaQFKgZI7IGTsu6QsmEcNIz0TDEvzDuGBIeOLhIuMGRGZOhf09ywqFA5OI+VICTHiat27vre6GsO6yPZPxPPZuoi/SzU9B6/KAQl/D2uClmhCExRqOhBwanfslbY1SXOkyhwpRxKJSOvX0i5Xf0dMdrQu4sgbSmMcQrRHAEXA9Y9x9E/vQPwM1hCBQKRpAK0/i960zwkoz0T5pP1tCjQqTaIA43cTFHOULyYHAKpsw9prrOH9ig2HNkn0/chXfGYYANGgpSskEotATpX+UVZq+8fjeT/55JPV1VwQOWOEO61TJefpfDQCD4z0lNp4dt7QM2ikJzTMsqitGol1kUUYtSWpqoS+yG8iO7oQ9RWAqewwNgEoi0SnqINRZZGeRPaZE2gCoqsaG/hsIsdXj7kOnHvTgdCYFp2CPteGTrCmJnbyHKPD8wg4gUpQUz+LDqRaneRsaV7MJHrvUPxuVkanvhpktYjT5kVxJC45DQLqEBU6mGL9cAAOo+EUy63+KSZnCEicyhqhqF1yvM0gvoc5SgbQ6LJ7hOUs2RFqs4C+rxH1IB9SglDUDgG5brYb2VjWkK8Ptpc1SCJb0xxnmOFmMoPORyEGEWbn0ZjJfWnujQRH+1HkN0a8yjqj9zC9s3c0lt91aTRMZ57VzayvBAdKAklLNvAIoqcS0IYVvYbS1olPsIjZx3/v96qrpcHbbv9I+XLlRZj6Rfz5jXvvLS8yFhrLM9LzTYDrb7utuhrD0qTRAyLCGiEHER2+Jtixi7J4WCnSwqJCMQJU3o0OEovKIGXBBHuKcUtzpIcsKF0pRrMpG7TlzCO3+oa4xkONbYNyPcTP0NrK80Zp6EZOUsqWKcBc3J/Xw9QjcdqZGc00vjZvKUQDKcZ6eK+P9xZFdAaQ0t87yId644J4jsxomN4D+1iUZVq/1IQlIMUxQx3KjSBSJZpABoI8AymfJkvMuz4fKl/IeBIZl2IkNTfCfpPGH73DsdyyerKEGkTjBFDt2c4zHEHa2kbrjOd3+lvxu1k0GSnQ6tCA9WSGJTkDWtRxuQQR09YZOWWvpKx/uoetaRqHrjg/2ptj0oei50YAYpvqNI8AKcrdoyyLMK1V9gWRHVYLjQxAG3NyQtr4UkkKJUgvjOcNI6hLUP3O/kmWO6eeimsJ2x6iKDGrkUbklzUyoneeuDquYRpQa8X7sPsiyxJK7cbo5QCwUOhvNdazPts/Hr9Dbz8TCkTkTF7DjocGZNz0JZp9CASnNV6ifVUDfS3g+P/zTHU1hs0lfd7cxPoWNZez+anDd6ce215dzUV9TXyP7gFe/zQXdkbTnjdnI9WFtu+iri5kKkabinlNMk6bVYHsw6j1EnQ2mdxKGTO0m+TcpXPMxgGjLMUGICeZBZak2ACku9o4kN5p5w2dedSHIwCdTlAjNoDeYzDN5znuF1uT9F3hNGh8VX8Gmxk/k5INS0963l5quaGretxt/fTV+fj3j9yXSc/zgWVEei7Dru2vg2t/7MeqqzGsEDXVXzFhjcJADlm7B4EOKPUsEhkqgh0PLRHAJmwJKZELqggBiCBVYU1RJHLQYzSZFBCnMTPSc+U1cTdGO4im98UpWytvlGcAcqX3Ikd7IKG7lueYIhqoQ7ShJylu1BhiIIFuWCdIjFua+9YWVhIppW7YE0JgdHTNhdU0pPpvVNcroLs/NpRSojdVqYW1bmnoK6+OI5v6x3lvo8fdSEhY1/Zd3JsiH8hIUecFgMo7WO022lfW1KROxJOQnkNoDGTR1gXU6Sxk6WFEsZS5aG4Fhb0v6/S5WO6YqkHnkBFidOZZtAetB11PsB7MaZVSK5fOPG0AQc9rzgTYK032qZQ3ib9LDYsCmltg7iVtHkQcRvgG9I7HJLbNMa2HqT17q6u5mLzi8upqDJP1BPsuyRfV2WAuGlvE2QhzYRkWgxPxurZUYFqTttapTqfJw8mr4mdrQYRjADlQugeYnKHI9eFJOaNhe/ePx3Jy5TvYoUcOn67oOkTGEgEYQI7fWpNlHMHqlxOZOrAamUQqS0Q+lUWikgQB1DyqL+uUHLRUczVg+ptx+jXVPw/oHYnfWZ1LIGd7CY4z1CdKUIaQRaiTLDH5TVlkRnqSfLEoS9rbdo5htovoUKQ3Gjk5sR2CGU7weiCYs5FS2WkuA2h8TMZhGQUjdOlckDOExsfejdYUkd0BdXJmSe1NyhawtY7cg9ia1B2fIlsDaHxo/Te3MPGz5DU9RwFuYZ0FGbN4P79xv+c7Z5w9ljHpubxJUOzebsY8wbxOUAzbPPbk9beDiOqIaTQNCD8T1kTI9k34Uc0OEdZk+OhBBILZiGIaHzsEUshUVHhkLvDwlgN57V/4tupqFiRysns0ns/Jq3jMiHAZTJ+54W5AcsZITzCmu8+zkYS1sqRGZv9UfKCaslyfjD9vbOa13lgTj+9QCKI+pHFRF+WA5pb472n9z2di5dEitintJqXOJ3YiL9GGII4apMUGcJqzrDOAEVroZJjkPY/rgQwJIWFw7Ygy2LoYlG0xmgdAOFoJhPpK2K8SVdTYEMvOhtX2pWZi0oSFGm4V/LjlHMfv1j3I6zSlRArJSSPy6Ty2SDk8j83BRfJQvkvnghlJdL7ZmUcEnK3f9qVwD5EvQ5LJ+3l8W1vj9TA4znuQag/auYv1P6VWeWtzXK/RGnfg2kk443XPr491q+bFTGCQk4HqPQakEJnkoNIIL4Dti8mr433YluyGPqR29+Xdpr8Z/73WxbLfYK1SejvJvQByRFFzowCsVS7qT311LFPt3B6cgn0h2Rzk+CISMmDqiXjt2ZlJMmPFDdyQEknpDu9t0uNUZYSzhdL5A3pHYrljBB6tf9P5lDgFkIyys8lkNYFsg45k4ZCMMnsBSU85b0g+mEymqEWKKg1obgRdRaKa+9K8iYDyW8a8sRa+Oy3rARwVgym+L5HYDRlfOvMMWH88ZT2JLY8p9qZ/gP7QuIBtgGEnXpNGJZ2pk8GaTy1v0vM0Fv7fZ9Lz/CCTnmeJaz/4werqjUFGmXVFw4g/8WaR8LJubROXXFJdzYJMfcqSICXchDU1WTKDlQSwKit0GIoBSIaLebNw3uTdSLBb12ZSSs34Wv8D0L1dmgh1nwfja6OMA0A7+pIiZAbgJXFdLYv0pLSovkTFUYdnet8AagCh3m4gBtuXsfE0cXlc67N7WNLkyMiRiKlhL16/Fv3TPxrPka0dqk2qxcYBplivvD6+R+8Qj0NjY2y4Ny0NkYxmicih+rUWiVVfEyuqRMRbCiGtU4vaJXK+/wrPJRneFnlTUAQRPwKuM+uyTqmUU4+zMbNyV6zEU0OSAKo9a+cYGYAp9cK6UoOXUtk1chLkujVJoHPBDAmSnfVJ3ldYO1nmjaLqqKFIAHbSl7rQRHhbrUWKKDPSvns4PrPMWUN6iRJ4cA6Zs5EaJ7UvjJv6BKBOIPoHnSFWVobSso0Y7B+J/55F36MxLusX03PlPG9fEq+z1lYmoPuvxvLXalPjWSgRioPj8fjU18XyG0vrBFCjnUNszE9e9Xx1NYbpJFi/UyLfsVyN6ANEetZX8poekp5Bkf4laG+aboYNzcjpVQIzDmQcyFFnpTbQkSpjhs8rUedUb90a8lEZqN5RkclwBqh9A+eb1jg+GJ9v5OwJaG2KP1e9aALGYQXvoSGQ4JQBE1BfF8t1a9rF64zlQH1l/GwWdEA1qxubRHYC6W5ZNP0jsb7TP8E2Fs29BRqhDiTnGCHFYU+NigPoHpaVQnYwRX8GEEFK37WyEY/9i39RXS0Nrv+Jn1CK8jTOx+/f+J3fmfllGeHwl36huOWPvqt4aCmbjJ8jMul5lrj6h95bXY1hJAHVqkipUZXSJMFqzZFnxQQw/T0jEUlQmmGJ6fQWyUWksHwXI4USnlc9X/Ae5kWnuTcyFY3pIOUAa/+7a6urMUwB7kPTgOYWJsGJnEElvgQZwhZ5gPU/J3g9NKmWpURKdF+M363/Ehsug5Px4WmRiBQF0ryQI5WbQEgNxBDo7IsjVs1owChWMQCpQ78pPJTaYqQ9KWgWMTWxM95vKeuhPil7E6KErREGwTrnkmJcA7Jv2DWlDfa2rKcJMJq7B+IaqAFY002U+MGJeE2Tg2EEeLfmepb1ddibtqbJaKbO6wFUnsG6Mw9OxWO5EGoJOeTsjCa5bvJbiVMA7SG9b8LZRNEpTWmqVmuCsShRW7VW/N3ufo6+b2yKz3MyjgP6L8XvMS1kNRk/Ng5EpGtWSoLzjubIoqCIkG1u4nOXMDghpOdxeDfLJqL9kqAvGVZ9Wyw3qIRNAJamEZKq80z83ebGBPkNNVOtpicR/EbON9aCE9+ioiki3ohM/JxlHJLgRvZBvdI+mxZFa1s8l1oHFchqkg0BpNcQuR+AZXtE1yG9kfS1gP5LoCdIXV1y4gyOCzG4On7nwassBwh2VqQQYkSQWsT3xPY4DacZVxQYoX80fo/mJt7bREyTMzigdzjWy80GwPkE/SWA9qGRqVQnv6iJPASC3ko5DF6O/970AY6Wpqhbjd4E+W2yHvt2iL5EtZ6xDnYARWTK+ciBO7zf6Dxub72ouhrDbOslj/T8iZ+oruYhDLmI+xHO8d+fNel56MHijg/eX3yl+vXmT/1Scfd7xiTaiLj8dFUv/J23JRGYc0nPwNHNbzq+OI3GzwWZ9DxLXHXr91dXY9hQNiFE3Iwv7NSXkN6uRh2RGpDyHkANIMzooMM3peaN1WTpvwLeQjkwMNpUPKwpxgxGkchBhOMj68E6+BHWfd+V1dUYDYn0pHpUJlSJtCHSNKAGJBVFJ44ACktjnShzoHy2oGtowPQeOCSlwcZgCg5kWQ+kEE5cxsY4RXxo9AMphKKwU7QGpccH9A7H424R0FTTyur40r4wZ0tzA/w9eTcykDV6DcbMarzSfY3IaW+P08bqENFgzUfqE/G7mfFFBnr3ML/Dya9dUV2N0dzARihFd9szkJGPRkAJilglQjhgAJEznX1M6NYg6kq7t78Uf949zMQVRWx3xOiY2LatuhrDGqWRfLAIGdorjZV8PvZeice9tYnvS1FF9VV83lBZAZu3znPx+pu8RlIpExxc5KSwyL7O8yA7JZo3JROComGom/oIMG8WOUkpi3VJvyugtmNLOokTedV5llkJSik1w5Bg5UlI52pezOMwsQOyBUTuUDZGT/Z8+SbVz1noi+4KtS+5pjKvf3LMNDeJg4Ci18wRBfLQIs9QzZX92t0Xn7vty61hYjxmRkpjRKW9Gzj6jGDCckuiH1LZEzorAig6VnVJel4ipUvQ+m1A5HAA3be7jykCWjsYiVuifyqeC3NeW+oygcqYTV7HZwiNb1NKRNDcWzQk6fVGglPkL5WNCKDsJ4uSp2ez9UvOWMzUKmF2BIGCZpIatZqsp++KbUyE4+CUPEMzXg/KJ8BaHfb5eWt0X3Bs2hn/xAMPVFdLg+t//MdnLoK4ni1SzvPvj/6zf1ZdpWCGiCzuvrf4+KgH8rzfH76/uOlni+Le/zhDTO6+5yPF7cWdxdc+dv2cRzEQ6Vn86m8XH79x5r9Ovd9SIJOeZ4lr3vcj1dUsSLc1Ir9MmJBibqQcCR469AIoutCITPQs2oFMXid4hwBKZzPChkLw9bsJSgHWFzHj1uYIQO+mcwElDGwu1vzFePGbUtDZE3+3dQmTiGR8GSlB5GJjM0eyUBF+i8DDKMAV7BHuvhAbcEjylsCIVUlfIiWvJZGe1EzAPMJTj8VkkHWBpWezqFtM6RfliBQsI+2xppscDa0tsB4sygY8/DbH9Lmm4ULESH2SFdLpp2NSoX1pTPpYYwl8BnmuBqQd947xOp1+6uLqagzbKyOFaB4mruCaUQ2QD9Z4icZs2GX5jZGekrrfOQiEuUT0UGQIlXEwpNSmthQqOltMNSKizdKv6SxtrOV1ZmVLzhTYGKsERTDX2rIHYf0akdOAbs4dqcnc3RfrH2YA1mHeLJqGShg0LRUeYI2BkqJNQf7W18qZB46V7kExLCmFUJzE9F11whMx8vZnq6u5aF8cn1mUdRFgNYYJFOVuDkRKT6dO4tY8h2SnnVf1VjyXfYiyHwF0KCPaMFLTojen4/WAkYwlKIW7vtoIvHh825fyGTKAbvMWHUvkmTrF6YyVcSAY0UbrwVLW0Xkt0bxNIEPNEUv1sXsv8ndJt9IMObCxOgcPVldzseKK2JE6eZ3oZnBe0PsGdKF8EWVqBVD9cFrTAdjkkcUWygzTwwh2jp3cHc+bZTmRHWzRkDTH0zJvdLYYCUhQ0pPqQksdVYwANR0Ieo9YBCmlt1NJOmvmu9Td26//H3585LQKQ7GYP79xNqTnKMrzT4tbXuPNDhUPfPLni4feNRPtGUjJz132i+V1ZRM9fH+x644ZEvSm8m8SRkTm56tfAl6LDo1Jz9Ok6IOfubXYLPdbaiwz0nN+qCxheYTPXvOjP1pdzcKAh5LS27XmJBlf0hW+uT42LI0gpaLTVlOLIj2t0D15Ial2Z0B9NRgj4h0iBcAKiGNEjhxE9Lwp0bF1ITLRuKX3LYHGuNTfWvWOeI5JwQugiIbGeovsg7UqNeHIS2vdxUmxnv4mv1tjdXxITlzzXHU1F40LYmXM6l52D8Zr3Z53BRl7EoFBXuWe1PRsQv0gI/CoacBAOtl2nyMPEN+3ezSee6/vFH9ujo7WlnjPWrp3AZGEDTPKKMpM1jqlIRqx11gd34MMIqy7ZhCDitZ/5wVeI51n47mcuJyNUDL2rK5oZ3+8/lfstOhlMOgtWgkIm+nnOOqFnB/T3+IIvO4LsTxsruNn6B+P17qlgaGTTc5HMjq0JuKq+BmsHuwQjL2h1Luj+npYuzOgEc+FESM1iPyydUbReuYEwnRDFkXF1ONxTfHBKyyLalST0GTnyXjPW7M2knHqSCVCVpxLRIK3t3D0Zmt7PJYWrURrp3+M5Sy+2yTLw+aW+BmaUKoggJw4KXKS6nwGIIGh5Ff8ea0O7yZ7kJxARqzQGaKp2kBy2TOQg9Z0EpKTLWuMBeNo5Y+a4Ki2uaQIRzsXUJ5Z1PleyHqTPVhfGY+7Ea/9V2P9e2In2zcTl0Hmh0SbklPbnKN09vdf5jOEyvbYXPSPxeOAdfYDAlsyD7VJXpPlX6x+jrHqO75VXc0F6QmWWUC6OsmyANIfeqC/B7Qvi8+svgQ+tGG/9MUpQ9kJU4/zHLc2wrwJN9mF8lKmf1MfAprLAHTQSgQqZnbJfenwHkDmaQBFoWrGJNyX3sFI08d+93erq6XB9R/+cPnPMGbhPRbv56Of/+flz3TMkJQ7i7sC8VkEEvRA8dERZ3aaAJ1Fep4mSX+//C6oLDOp8Je/xrmdSaTn5y77peJX37N59CbLEcuI9Hxz4bq/9teqqzFM8JAXxkguEooaUk8HnCgQlOpXb/N3e8diZWHqmWeqq7nA+p/iocKlZlFqVPNUdhGFyhuRmeKpo853huYGINqmhaQFj5Y976pdl1ZXY5ARG0BKV3MzvwNFRZg3lu6rzYmsmQCAjAmNdCNI5BgRUs11bAjQ8/Ze4r05tTc+FTQtCgwa69JLhpKRKB0wiE7tebq6mgsqam8KTwPShq2ZTOuieN4aGzg9kow9S78jIpM+GwHmGMmSEmSk9KAgfWsTG0lYP24tryfC9HOsxFNUhRFiZGBbIwEyRk7svry6mov29jgNt/sip8WSsUiEcgCRHZYKP/1UPBcmD+vElUyJ/KamfmZgkyNKmp/RuA8kSLMOfi8r4VGDSGeMhClBpSBaUr+ZZBxFrQcQQYTEeIk6yG9r7Db9VCy3tNFIJ76vOWjRiJTzHGt0S4QMEd4mDykVvjbJsogaqCnhQp3erdYc6DXt7Sw7W9tiQqAJRMUI4IjqQWOhAEr7tehlkut9uS+lpdbAQWtp0kx6yjkIUWpDPopL+RD/PXsHzOaQKPkBOBMsq4Xew2pvIhll0Zs05lKXkQhSSv0fgeonSikfaohjpRUonV51SSLrJEJ34srYWWi1LLHuvMxbDxo0WqT09L74jG5t4LO0sSE+m8zZ3toa39fK69B82lxQ9shgis/zLmSE0DkYQO+BzpMSuKYsCpt0SatX+gLIetEpyPmGOolAg3HgzLIMLvRCig1A56OdeUi8yllKdjB9l8oABiwP0nM+whjyvprBuf/7syU9R9Gb9z9d3PzVp0Z1Pcc1PWdIzz23/Xbx8RuqNfC6pOfcKNGAN67pOfP3Mun5FsR1H/pQdTULKcq2kH2UeqSCB/6eeVsmLo5TKesTLFT7J2MFmjqfBlDdKe34Ru9spCd4qHov84FBhKOFnGAnuZaMA3i5jNDFuqtyuFDqnKVHrrg2JtpUUT0Zfz759rixSgClEg+NRASYl5dSh6wRBhGyRnJRI5bGSlGOKAJOFD+K+LN6PlN7OaqNgI1j5BkwmkDqlVJtUyNcKALO0nuxLq7sockr4jmmyNYAIlcsvR2V0oToHSMPSIElgtTSg+sQMVWXyFbam72XxfA5Cgq7ELc4ZhJ5M/1cvN9a0uQGu+lKBClFojQ3coQYPdvUHu6U3dkTG4s9qT3bgowFiqgIIDLKzlI6b1obpYHPOjCwRXQSYWLN5cpvVz/H6EokbWt7TFxRdG3AxKXQzGAVr/XjX76uuhpjzV/6ZnU1F6/+2VXV1RgUjRNAcquzj89Hipy01HIqr2NkNWWP0BoJoHuY87lxQSwf+i+zIUzNeszA7u4nhp/lQ/PC+N2sPh85cYjAC6BO1ymOPovwIl1DO2jTMoFzwfQBIjVMflOkZ++IkHLkpLA64+A4sHOQGslZVGj3hVjXtrqXRGxrDVIgtKzkDhK64qyhEkpTe4CULkG2UH0VP2//lXg+mxtYvlC9Xat53YN60yt2soyjs98inakMiBF43QPxXJgcaG2J/17vZf7u5LXxGTIh5bCoxENDzhAjOAm0L8whR3Lg1MN8PjbBJLRgEQyeEIL01BOxTG1DiZUA6m1hZxPbu6KzUXkdyUDEbC2jfMAW14ArInqF9CTydgBEaEN6XSx19/bA9YTVEEZt9qo4/fv8n6dxrv/+0Xlkb5RmPguvEY0jEvN0ZGfADDG5d0R8FomRnqdJ0tP1Qd840vP035v72fJCJj3PEtf+2I9VV2NYxCBtehv2PhB7lB4fQFEOFj5Pgs7SAqkAvnkWCSYosXixfLdDnV1FqFLTI0ylD4B7UIe7EWDMmht5HJAMlZ3VII+WvNua744PVKtrRJ08LdITFQtNr4nva4p1AzzFFBE0AhCnpoQT4WJRFdSQyWpXkbHXPcxGTvviWImp8ZCVazLeh5beu+IGiKIWAnr62VjJm36KlQVygFj9w8mrYbEKsV2HCAqLOsSmRULaU6ROU0hwjBIWsm6OJlGhfxwMOCFY21tihdT+FhGZXTGaCZgyWYIaS2gNO9pXlK5Ygowyi96kxjUUKWogAzKASkRQM48R6IwV2UmNvIzkIuPAHJN0xjZWs6FGcstkEUW4GJFPBCdGtJVorInfrXOAoyraW2P94+Q34iyRgPbWWB5a4xqqq9t5TmQ9zKelc5IBaDoFkdhq1FEWjeh3BKvp2YIx6zzDRvNwKn6GxiaWhyRnzRHVgjO6BrUsRyAiUqIk6dw0ByLW/ZPvEugMalzAYzP1ZEy4m9ya3hs7ZqwUBMHkNzmzlOQF56jJbxqz5nompdGhLGd89yBHbBMGUDO4J9kC2BRTGswQcWrlPmrQTMycmKQ7EHkcMHlVXEvY7tsHUtj0F9ITiOwO6L4QM3gDKEOikIjMFdfHpZ1I/gekpLef2H1ZdTXGyrdx2SqyQ6wuLq4p0dlSokJpbxlB2tkLbKroCWTvdl/iMhUUjGABNqSr0DkYQPq+RaaSvmPPgMSpnI+UIk/NfI27eOzzwvQtEq6nALdFwHzS84wwivLcWpGSMxiRpcWdxe6PXV+ln59pTc8zi/ScT3BGf2OZIZOeZ4lrfiRuZGTp0K3NMVFhkQuUxmWp8ART4tsXxYqfRYVSsfyppzmFdsXOndXVGNa9vQ3ptuiJKkFpAPZuWIPUmhnAcrcI0hp0a8WizCVq7XgszUhC4S4H0dqbY2XMOvpSLZyJq8880tOilRCi8FBEQg86dgZg8Xm5LxplVoMUCCmNzANysfMcE9ukcFPNswAy6tSwBHacPOABr/zpNdXVGINjrCxQTaya1PtCEtvSgcDopdS5AIpoaG7i/Yb3NSIHoo2MIKJ0KywfIGuvsQLWDn+1VGpjWT21hw9/IvitbASNjUUVkRLf3Chpa2A0W1QRjS9FjQVgihvUrg0goqHzLK//xqr43OwdZyKfzhbraI1RDmbMgKxWWb8uHjOLyMS0VNmCFJlkXdYnrzhzYprmyCOVY7lu5RlOfS0u02LjS2e0GWr0XSWr4RiyGpn4DFZyYQ1MksjOiStjEsXqQg9OxWt98CrvixU3xo4zy5qg89FSuMkBUpfayQSL+iK5Y/KMImEbK2mvsFCmCDz7W0SMWLQejY3JTvrc6mBTCQMjadEZLGnSE5fG97C5nH421oFIpwmgmrQ2vuT46kOqt0FTn4HYa17IdkhzA58XBHKsty4U/QVgOgmNj+l8tFc6+yX4gqKEhTCfBNtg8oq4hE1AD2qTur4Vf271Sl/9r9B4aQc7GUiWdA/ZeR7v+akneE1OXh+va3USg1NveIrP/tqKWKj2j/N+o4xJswn7lCIv+kf3hXgsMSuxBPXiMJ6C7N1hj9+t1oifjbiS9nZ2rj72z88yzXuBcN1f/WvlS5QX4XiZfayf/n3+z9M4x39/VhGuo8jN+4sr5nVvn4n03JzcvX0uyXmoeODOTxR3DX8iR3r+eQQ2MrJIABhiEyb0OQrEEk2IvqTafAFEnGoNDiAXrYkQpb0TwToCjANGdJYgQlajMgD6XTKSuvxdakRkY9Z7EYqjSwfAxpr44LT1sOa74/lsSpQlRQhYqkgHiIb2FTwXFFFmKfaULtugVG+BpUVRirylJBF5YHWjqIu3dbCk1Dfr7NqCxhBoqJUgosGa8pAx0hOvPxnTRlJRt9X6CjZy6pBGaEQOpvnLWYgRweLJx9RuMXqR9AQjyaI3KYIJU9NLUKkCS2NEB4F1hoXvWkMzrOElpRWwmRIQMwEphjvWBpMIMYqAtqjQ4SkwBKSMSA3mvX+U9yA5zuh8DSACrrGa1w4Z6VYSoHFBPGYWKUR70OoW947Fa1Kb51D9W5kLiniy9UvZAv2XWG7R2W1ZKUQ2W9NF0lWMyMQGEKIqUzmfiStZflP0ZU8aUOFZKmc/jS+l8QZQmrMRWtRIjuRpgHY5B5AD0CIfKcodiVshHLtH4rExonn6m7FeNHn9/upqLlLIVDybhmIv1GAc5Lt0BpijkBx95gyjaMipp+KxCSByfWBrASJetQ4qQBsmEixrCBwS5hRvb49tL9OhutCw0NLFSeczmUy1QjtQLiSgLjU5CS1qaCb//TREo6dEQBumoNaz6ZIURd09aGc07CHRJYfogGFZX2/DmhLdiprsdQ4xqYzBP+K8s0AhAgXY9I5xBgBhKBGkxAdQZGsAlp8zrgSw1JGegfQMKmY4/hfz56Nnm9Y/it58sPpldk3PGYyIzE8/NfPLa53YDTPRnnd9NVzvLG7/cFHct+f1a3re/hrhujyRSc+zxDXviyM9yfsRgOnXkrLeEKIMEXbGPBh5RmSo1QxB4WVRGQCqnRWAtT2sADMJxYSlaiTtADrh23fJI2bbBSN9YH4C6CCyeVv9F+G7Un+LlDHrPEqKhRkzBUy91/+Mx8ea8pT/pvo5BnUEDiAvrxoYoIRYKjCNmXl5KTJvYhsrtZRSqqTP/thbbfXJpqEZDHUCDRh24+c1jzt55zE1vQSmUgoBfeL/jev+KVkNjRJWXHWwupoL7vDM66yxOr6vrnVA/5VYHvaOsYFCERHWfITWk5HzhJqk2VGUQ2ONRJACbD2ldJAncsYaTU1Dg7C+Rb914r+nUYAAcyYMp+Mxs/qf1PTIlPgGpJpa3UuSyTZmWNNWnCoUPX/sP8W1OwMm4NmIGB8B5Lo14yDZN/2M7EHSa4SctGgYAqX6dcXpSrXXrB4bZZXYeT5xdWz0dvZw2RPK+mlt4vFtbY/va3KWzhbbF3RuNtexzkb3sEhPIjBS6kvSOWaNGCnS3qKiqWGLkf44vqYXiUOOQI5fbcpDf0/OQXLEGpAMlVfAmuQWKQfEXu/Qmds8Vk8WiUhWt8qxjL9rzYloTVrDQyKgTd/qg/5gDQQ7z8Trd/Aq35fKX3Sf5zU5cXU8ll6jOx5MLWmEwQEst0jXqEMJhAByMnSeYSKedNTBcbYXyF61LMj6ZDwO/Vd57fSOx+SiOdmoNrVlLJDNnJKy3hfSEzMbpRnvgMpkyTNQgyJybNqYP/6FL1RXS4Prfuyvlg9XXgT5N//naZyHf//Y7/1eeZGx0Mik51mC0tvN05FUowoMLYvexHodcl/ssm7RkCAoTYmn5WOdVofgobJDgECNhQKoEZGloWuNEgK8mx1EFBVqczE4FR/IlnKw7vtiH4wpJqS4GTE4cRk0t7AagaAzUc3KgB5EVVBzowCKTlGA0WxdkNF4EsIRIzKlpiERmXVJWe8cgshqIxxh3C0V+NQTsYE8/S1uuELyob2N52Ly2rjmkhm3r/7Z1dXVGFQfKqC9PXasaBMJIdAQMJ9DmePByXgsMfpHjFvaQ+ZM6EOkm0VnYVScRatiOQoeL4zaFdLz1NdjQmvyKin3AcYMRQYGkIyyxgedg7FSbM3PCP2jvFcaa+LxOfkYR201oUFSUyIGqclS+2I+o1vbYzlrXW8pbbJ9kcn6eA9NP8dpoiR3LDVxel88Dv2jfDaRYWl10yjqqvOczVusR3UP8zhQpohlmmATN3FM0tlvnXepZILpS83N8XtoEzgiBqXuHxFlFDkc0NoS/72WRPzROrHu7SRnG2uZXCTi1XSVHtRfpkZ/Rpp2qVO26CTUnbl1Cctvcira3qY9aM7g7vPx2dS6WHRfIvCsIR+dIVb3Es6h9sW8/glWEoNLBcgeBEKXxjyAiFe7L+2h1lautdi+MN4rph+iI1TO8z507p96Im42GzA4GX9XI2lBbnVeZCfbyhvi5zVdhc5jOtsCMOJa9ibW75SoZpIPQ6gzHkCku64dIBynn+MapJT9YYSjZTESyBY3G5YiJ83mJljtTIog7UPKe0AddCML5KJAI+IYGuvY+bHkkZ6j/i1hnYa9tXg/H/v9THqeD2TS8yxx7Qc/WF2NoQQeKNYWok5emOZ6MWaoE7lEm5JRZ8WPMT3MvE5AIpLXKgA9ahIhg9GxIqzPFXZgEIlt79YAw0e7wsNB0t7GCs+am2PS0xTryR1gAEphcoqKsEYCRMBZc5YJSAcyw2dwKn4Gi6DrH4uNaU0FBuWouVkMVuoKL2Rfa2M89w2Juu2RUruHiRwkJSwSBYrlWxH+YUKR+MmdcUqzpcn14d3M4CSi2IgyimDWRkbtWB5ZPVh6Z0wvk9pttFcsIpnqHJqBPf2teP1OXMnkDjVqsLSqFBDRbKmfFE1TlxqxLSB3KIoqgMa3Z7W6YJ1ZV+EhHLE1O0Jg7fSOyvlIkX1C6GL5DHFaEfHUXM/PQIRJTxxRp56MI4XqkqJJ0TtdicTCcbc9CEZo3+YYI0hF/yBHqjgbEUJ60n3NYUpGLzmZA4jgtJRdrB8njXImro7JfI3Up6h+Oceo5Ig9L82xnedUngHLcpQg+U1nm6XX946wnoCA9WtEcx2andj5ihGZlMZegpxWQ2gWFEBZBJQxEUCpyybrJ6+J6z0SARhAZ8jU06zrUD3NLji9RoAo96HViIUztimRrZSOb5Ge3cPx2pm8nMk+KsPQkxIeVL+TalsHDCA6dshTXOpW8X610iCTV1YXs2CNoigi05wqZEeYzULOY820Ip3LIquh3n9fbBaKyCTyeAQKhJGSZxR0ML2Xe2O0LoS675YCTnwCRV4GwD3Mjqb3GJwU/bvJ5xCBan3WV8RyyxyFj/7O71RXS4PA9YQRDzO/mD8fW+II17cqMul5lrj2Ax+ormbBogYAJigbVHszRQBL9CalZtHfCkhJb6dQe3sGFKpWMwQMjGH/zJ/BvEZEstpcoMCXcaC50AZUsEzq0nhp9TviToitrVKra3N8eK+QJhbNdWAsSiolIuG7/eNsjLz6tbhwtRGkKfUESZGygv0tUMIba1gBxvSagzxvU3vjCLqWGA0E7C5eguo79Q6xgT2AtGxb6+0dXOqCQBGGpixT/TircYkp50KiIMTg7ILh0X0BDC0jbKZiJdHqx2G0qjTcIrKbjPkATWUngCFgBgrXfWUZ11gdz6WNA5EoHI1T3heiSyzNn6I9egf4vs3N8TP0jpy5wt+YZLk17MfrpH2lpazH8qx9Ee81Ik4toqdDqZCy/ttb47837PF3Tz4M5408L5U96UIzvYDugfgsnN7PUbeNNTHR0JVaaBjVaXoYyT6J0qFmjnZf0neM9CSnafNCvi/WeJX0Xhr3iR1cGgTTcyXqENNwhTzofCt23K64cW91NRdEeqrTidKnqWGWHBXkHLW6jCR3vMYgkZNM2GC99eNC7oAu2dzCz0CwKG7qnG4ZC0QitrdxNCQ5azSS/Pl4nVrmB42ZdrxP0EenIFtm1U1xI7AAOscsC4cyFux8nN4by63+ET7HMEVZSC6y6fpiY7W3xmeLlcRIiSQn54U5PNGxI/0C0EH7Es87RfVbpD7ZaRaMQ/aqpqyD48y6t5/r2aQ2LDwvZRqOAHLHgqiKQfxd5x7idU1jZs7KpSY9r4MAt8VAJj3PDzLpeZa45v3vr67GMOFHm9m+2wevk5FnRAxa3Sny7ljUInV8q9l9KaQ9wegYWDf0Ogh2ELQBdAhYJC0TpOxpbqyKx93qlVL0Zkp9VktvX7FjR3U1RutSPjhXXr+vuhrDUoZPQVH69lZOZSAPtjfYiOezSfUtR6CDkw96iuKwjrOUcm7j0AePuxlfpJyT8RZANYysOydFalp9MkJnLxMNFIFkxHbzYlpTvI8HkN7YkI7sQ+hA3Nwo38VoGH5eIrHNkEUDDO5L6zyAo4p4jWC0k5DSVEC/+xJHp7S3guJoaYwQfanGOMAifDGaV4g2MpLq1uwKIh8terl3IE4vs4ZOWJZASnjQXFg6XIO6/0qkc2tb7HSyOrXtrbERaQYgycO+dPae+lbsgLEIr/YlcRSTRdR3oYOwycPe4Xg9WC1tikzC1PQSFB1iBiuquuLEpJRHi0QhfcfejRzKjZW8N2swlq0LmYA2AuJMofIBSobYfjtTORswgDO2axGZ8Ax9iDyzupekU5x6jOXs5NWx3KFaxgHooJISKzQOQyGCqCbn4Bh/lwix+iTrUPQe1uyKHJOWCUTRmxRNGYB6mJ0hQC7aGU3lEqzGMUV1GjlJEatIuJdorI1lhjmBph6LI/uMuMLgFok6JxmHDpwSJM+aF8rZD2s9pZHoQNLQu8+DQ8Ked218dtsZTedx75jsY8pWFPuRQA3yAppg/+kcm80M6AFxakEzRETq2qH6nfJcmNJvrwDnMf33xl08ev/91dXSAAPcFgGPf/GL1VXGQiKTnmeJq77/B6qrMRpQsDeAlHCr/0nC1jwzlD5tZCqSnibYQZGy+iIpxY/NE0Sg+xrw2URYI6ksxkx9JQhh8wC+CgqAGFS0Tmw9rLw6rp9otXRaW2IyiT4LqE8QacTPW4NINf/umYsTTGcTIwmjOOyQpc/5cUslOj58iQgN6B6OFQs1XCCdR+t9QfqcRXj1IBLFUoHp7/WP8vptXRYbzaaHkYFAqX4BZAgY8YRRHPy4TAaJwUnNhTpA+tdXyvzQMhUSHdPQBRT9YCmeZLCag4Dm3eaHDDiqSxqQ0sioNhH/PSXcgfS0+qpUB7izj+V3c2P8bIPjbHxhLe21vPhSxnfyWkgTlTqdJGctqgjrH0q0Hn3XGg5NPRlH61mdt8Gr8fnY3c9zQcaepV1S7W/TVaiepka9ABFpZz9FyBjxikakCE+KYjW0LozXn5GenefiMjhW8xrPJjtLqdTAK+yEp0hCI7+IIDVHH503GGUpDoLus7Gsr5k8BHJnIKUVmpuAUJAzCLNShGjrvxyvyfoa0alhHCzrorEW9rGcNxTxag3YKPrY0vypVEZDnhfnXe5LdYetrMzKd+yprsaw9HZsbCWRztSFXoMOHgGnXkJ0YR/qSgeQnWcyp3lBPL66L0BHVacrfK71+3n5McAp3n9Fzmgg1YycVCISkFI7s3ckJictAxG/K3wCkuCWXUmNe8UuJRuUgo8CBtRfwyJeKbsBSvZZ8NGj983vD764uE5IzzALdmwGnOu/fyyTnucFmfQ8S1BNT+uK1toapzJMP83pFG34roEEnZKe4kUhMPHKyiv9PfMk0aFuBCnWwxTNHAldIYrxgBNlow/pEEnRm9DcKIAif834mtxBnRuri3lY+e1xpKd50ZF4EoWdFE0lZ0gREmOclBh7hs6B+JA0pZYUTY0Gg7p0bSGVKfV+MM2GCxpPEinRpxqZUNcrgAjO/jEx8mEfm+HeWAtKraS+0ViaAowRfxINVr519fONgVFFkvqG9eOgnpqRiGRM29qj2mudZ1lm1Ntg1HVlr4CMaljzEZofIWN7h+N9ZSmEGKlp6YYAIvUCqIauEfk0F5QiNwLIKK1HCMRg9xCPb2tz/PdW3MDn+eQV0FVbCIzeUTijZUtQ1C3JkYAp6I5P9f0CyPjvHOBopR5FMRmJchRILomGxHNe1FSKyLRIITL2zOC1exBIBzJ9a3pffEZTvfWAiW1x+RdzeFJ0ttaXpLUDBPYIsL8t3RudenKeU9bDYJplKnWORpjDFPQMk9/947FMbqwXfRbOC5UvNBd23sD7DtjvEBZqdTEGRmGVaKyP90rrIs4aorPfUpTRCWTRwCBfTM+whjYEKtdhEZkkU+3dqCanPS993nkqTqUfAXRUIrADTu0BktaizglCck1eHj+b1vQ8HJ+PzU0sv5HgtACQDbEssfrwJEs6++UMob8nMp3S3k1PpvOGAmlGQG85P0PKPqb7dg/xuUBlVpQwpzR/q90Jr6aEMLwbdXTHXiIlvnHvvdXV0uDa9//35TuUF6Nxr95lEX5//A/+YOY6Y0GRSc+zBJGeRp5Z4WBC72jsyaeaGAHkbbEIyZRGRghZJlgjUw44TKeX1dd7OR4HI4SJ/CXP2QjwvKZAYOqQHIbozdoYp/+NAM9A8x5wwa27qqsxmpv5u1g/0VKzwMi3eppkyFqNTDKSLMKLlGVtZgCGgJFUFL1phCNFlNl3KeXL6lPSe1g6PqUWWgH8DqWqWn0nMLyVaABYmmiTaq5b0wxItbb0aTIYbT0guSLKcg+aYHUhTXookUbYtVwi5eoQFUfdVwOGQJhbQxw0pslpUKKWQHqiYSlDTpE3arACAWHRkOoUAQyOx/vCIm+I7KY0a4OSB0DSrtjFtQsntsN53pQBBqhTJUEeUrkObRgHTTOQzCpBcrL7dBxVF9A/Ceta9ATUo0T/oAhduy+mMYo8pO+aUUb6Q4rBaiDitAkd/gMo48DOscaG2Og1IpPkrMmzHpDjTemKjdHDQtKiAxC+O6B08xJEzpOcDqCINip9EWBRfASMTBUHFzorE4IW6lJOCMdcnFb0vA2pJ0uRmtaskNK97b496Lpv9bG5oR4/Q5PWv5y79B49cbL1qYnhqxwV3T0S/73mGp7j7pF4z089zQ1x2ltjItNk3IortldXY9REn8Wz3wh+2i9yxhMHaBHbNXB0d1/g9YAlz0zWg+PL5DcF/xARGtCjzueiU6OTrcHfpTMEO6SXMO7gTGFBVPQMlg1KZ2kNnqspEclLT3qGUoZhfsI7L97Pxx/IpOf5QCY9zxJX/9B7q6sxjERsborrwhihQKeA1YeiRjvNDRw1QF4c9czAkjDhh2lnsqToINFUNBDiWD+0RO9wnLZQM6KZDjgjMmk+xaCiexiZWoP3qEsR51XvuKS6GsM8ws0L40PW0uGQwKNGMiWoK7aloiHBtC9OvQugmkAWIUBEmUcIxHuoL52NSRlrSC0oIo76EjGCz2CNGqg7p0TIkAHXeUE8zbDWbU0218aKfP8EG8LUeKMOqY0zgO9Kx2+CRYViNKMQpFjrE4bX1jSRBxrpOR1/d+qbLF9am+J7YNpyCapXVxfymNZeYw0r5p1nYwabOoMH1GG/GpmKjg4zsIHUaIizZqQPzsPUY9wpuLUtfueaEI409wNpptTeGTfgaW1mMoqIGCVLqO6fEDn0Xasf1wLiqbWRSZ8BdEc2I797EKKExRDuPhM/GxKWJejctfT27uE4wqW1OXYMBZAepZGelI0hOsUUdORtbeYzj6KKVl51VXU1F40L4rlIcRzUrYwIdPfWzA34e1aeBFOE+RhDmWp1kikbg6M3Wc72Dsf/fV04xMHJeBzqE3wukCyi0gEjwPgOT7INQN2kLRoYs73Etmhsjvd8cyPrDuTo6B9nAo+6pNehruMItM4sIhlew+p2s0zlNU2Lsi9R8iRTLZCAzk0kbktQCZn+KdZ1KJLQbE2y8yz1ub0pllH1NbJ+4bzR9HY4A6ycShfKWzfW8nfpbBm8InsT7GhrZES1NwcyZr2jZ05k0rwhEVqC0t4tY7L/Smy3WxMg7GGxhs9zyrrUhlmUim5UEsxFDQhdy6Jc6vT2a3/0R8t/1kppMiz/Gd4lvOf5//3xP3yg/Jmx0Mik51niqh/4wepqDCPP0NtCnp0SRAxqRCYQcNY1lJ7NSM/+y/Hh0IAaHCPAM5iXLCW9DJU5GbMePK95xqlemB1adGCYQknvZkK8DtG4FkWy8m1xjbXmFiEyIdpOyRmqIykRDUQwWQ0kSo8xAoOIPSN00UgCsnAEIFF6h9mLSMqyNeWh2oMatUjEkRCZVJyfOtYGdIBAntrDhnAjIQWqsSoeS629SXMvERjt7bFDwohthBpEcGzJ+BLZ3N0HEbNHbP3D35IoS4z0fJmjdjH6Qd6hAenIZgAiISDrqQl1/yzSgmRG+RTVz7mgRiW9F3l821eA0SyRw2SMq4wDOdCXBj79+BHK52I5QLUSras21T629D0q2WBRlnQU2rzR+j/+f99YXc3F2u/fXV2NYcQrRYue+CqnczZWxffonxBiJMFgRSeOOCYprc90oBYQAupIhb9n5YCwdqzoKu2L42h0Ixp6h+Ixa25m1Z4cKFa3lZoCahM3iDRurufxpVqfKnfAIUGpyxrpDLUWByd4zJHQAsInAB07UDYlgBzH2LivBDrZpI4qZgusYdlJEY72DDSWrYskwyhhLqm+NtUPDaiDo87kIclOy8Khjuyqm0FN5a7oklSSxXSd/uH4u91DdubB+hdSDqMWJWCF5E59jegUcM4PIaMqgHQKO6OpLrRl7NShPryB1u9QdKBBJ35nIxynoDyJ2W50BihZDfa5OQX7x8EOgaa7I8BZqmunF68d+y7ZsNZomMrEUXDW8iY9Fx+P/+EfVlcZC4lMep4lroGNYGQfCikrUkwKNER0BtABZ0J14lKIGEyIcjDhNwBPkD0veYLq0rWcvmt1OikClAR4AKbj27sRQWokLcybRd2ScLd5W70rbmRE0QgBZKRTN94AUqwtMoQ6JBL5FkCNa8zwQULASC5K5xRFFSNThdAlzgZJvRLowU4haSEKNqD/crwHjPQhErB3UCKxoESERUxNbo/JCk91iteDRXgRQa+RbmCA6dqBv+dlFOLvJhHuVH9OaoDRd3svsTJHpKWRkxTVqZGtEJ3VkPXUfyUmZOurxbggY0SNxVgmU4RZAHW3b18S18IcAf6eNUmoteHvyZhRiQlrUjZ53XPV1RhNSHkPoDU1/RyXPWlCjdau1NOk9WApys11sHaEPOgejmVJ90XWVTACT5wfnb3xe2DUTAkiHO2MpmZIWEanBKU3mpOY9DCLkqdoGEvVI3W7sZJJZZRx0qGcnI223yhDwhwopBNoTUN5NgLtN4typ32IeoKQJd0X4rVn65/OV4twxOh7yUzQ8iKAHhBiRp5RpFv9An6GFpzFRISOAGe0EVc0Zt69Hdae6AM4x3LeIDGtpDI4YCTSk3QzipwPIKLN5DeRfaee5EZRFJRhpByRZ62NnAlBjbgsDb17MB4zk1ut7fHZ3RfSnojpnjgmqQ41OTQCaC76x0XvBJg9hvUwJRiHdG2r/9mA7utmnxOfgD0wSqAdLM9LRKYFBJF9bmtyABGkVNOTopQDvvFP/2l1tTS45kd+pLpaXDzxL/9ldZWxkMik51niqne/p7oaA7t9lxh24XAxDwrcYyC1MjCiQYQUCRkTqphqLYKSIiVIKBuoi1sAermMcEzxUIFHzEhaIkjtMCRvqkX+EhlqBwZ2b79YIj2hO6elqlKdQktBGUXbz4M2PgBYij15itUjDHXE+kfYsKS6Z1NP8Vy0NsafN7dyl8fW5lhhwagBAZGFAUSg9YX8omgCUkgDKDrKIpXJ+G+s4bXTh6Cr5oViaF0cj2VDDEM6iaj2bAAptWbIcgdWUKzFoKLIvO5BHpuJK+P1MJQO3BgkqTXLYgWaSPgAqh1b42nHemqW5o+kMg9DeWbF72zRSiS3tMkHrRGMQC2fF0iJ/jF2EPRfjJ9t4voD1dVctC+M5UD/BMsXMuhbW9jw6UPkJDbsKoGNjER+96Bj8uSVL1RX8wAlCIwQ6ELq5vTjXNOzviq+b+9lWevUmFBK/GATConI7L4Ykwrti7hOOJ7n4tRGUkJ0FUw3lEghuoe9G0a1CcFPBJGSqVCKxJrsEeluZJ1F4SHO0OFpTqsepBJjhFmJlA7nWL+cj5DyxiAoidQrgfoAnVclsDb7JpYvTSjB4eUzqp+zYCVSGlBLXolMmiM9d6uLOeDv4tlvTkG4xUB0M3KoGSmHxKuUdOk9Hzt2+id5r5CNZfYNvZw5dhpr43e2iG+KjDanCgU+mGOSnPvW8JCiu62JIWWa2Jj1gMi0rEKq32lR/TQXZj/iojR7lz4XvX4IgUJUTzNgAHa7EZG0DZHnEND5aj0wvvFbv1VdLQ0C6RleN+ieYZrCLl+M3x/PpOd5QSY9zxI7v++W6mqMOnhrAuptUSwAmJYtU0TRhaaYU5SCEW3TzzxbXY3RuoiNGRLiKcaBpV7QO6shAaSldaijv9eQaFMqzGweNYIVdqb6LVaThTq4TlzBh2wLyLr2VlaAWxti5aa+gg8tIjusUP1gChQTIQToc4oAmQHsAX6EEvAvTMrBV408w+ZEEl3SeznexxopAbCI1y4oy53nq4t5aK6OFZZhT4wRMrBX8Fw0twCZajU9ycCWSCGMKhJjnO5R1wik+HNKv7NoYCKrrTNsHwgiNViJuE2oWWYnN0XHGsnQOxrLvraQ/phSCg0OAoiAO/GfWcatvKm6mA2J0qHx6TzNNRxb2yEihwzTEmRoWdrl5NXxhmtuisc8AOski+yk75qcpbRUjaAGWGpi50DsVLGofkoFNidF5+n4HvU1vLdpjgZSk5nqH1p9bDr7Taeg71rDCvouNWIMaJgRCWhC3bT6SnleSHvXEhEJEZkUsWelbShDgmRcAEUJ25pEWUtrRIjMU4/F/z3V8QtogF5ENbcDKN3W5CzpCb0DTEahvsXHQiln4+dtb+NxJNKTzvIAzPoRcjJFjyMnvBHjrgsCzlz0ocO++wLPBT1b8wKW9eQMePXP+AxpbYrP3c4BjiDF0mQSMUjyzBw7rW3xfk3TfXk9ECxrApsuWtkeIj3BoTECZdEc4zOEyNCUetOWsYBEpJw3tI/NfsTAnVeZrMZmvNbciBRKIVP7x+DctXJacF/KdLWz8ZHf/M3qamlwzfveN3MRXmP2kj/Pvz/xr/5VdZWxkMik51nimlFHr7lQjz1EKVgUIJF15t1HokK+24B0K/M6kaC0Olkj18Q82DMQGWrPgN5NW6r4vPLdZvwMScQrhOoH0EFiNcBondgzTFwaK2jt7ZyyvuLaODKpDcXrDVPQbCLgxNcvr67GqEmh+pXXAyEAhsQIFK0htaAo9a13jA9ZWJLl/PDzIgkiyn0DuqLa81JHU2uiQqRa9ygbWt0XYmVh6sm47msARd6oZ/xkPL7WKZhqm9qYkdHQsvvC+NalqVSDyCSeivLlqp+zQJEdRuBhc5fjvPaok7g1sKLyDpauRYS5Ee4rro33oBHN00AYrnx7nL4dQHvIxqzzQkwqWCQWESPWSd9KWhAospTqxAVQRKWVBKDGPrgnStB8trexkWRd6M8UWnIBIo1NHlIKoRHFtOdt/U7vjZ2mU0/y+FI6ppXlwHI1oifQdy3yvbYy3pu9l/jsp3R6q0/WXA/rV+pAlv+i+jmGNYGjCC0rd0Ap2Fbahki8xlp+hvoEyBh5NXKK9OTM68Ga6h6Io4Iso2RkTM5DZz9HFZGDqiWlNih6rQXNJAPIoUE1cQOoNIiRRt3n4/U7+XY+X1vUcMj0IohQtKhzIjKN7O4dgTReiRwmIrIpUay0zqjbdwDVINX65eRIknOXsnM6B2N9LaD/UizrqaxBAMm+xiSvBxAZXKe2RAOaWBkBPfXIpdXVGCt2PVNdzQMMmZU7mH46Phe05AKcQ1PfjINCAuid+1KLlQhHKhMQgGSo2MZkw5q9i+cbGTIl6LzRHhbwvBa9Sfe1LFOMhAXbOoAIUqzpad3blzq9/a+8bzQVI7UivHb5czF+fzyTnucFmfQ8S1z3oQ9VV2OY4Onsj8ko6+6J0QhGOJLXSKIsyYvSfYk9ixRhYFGhlF5mNWSQ9LTO9JQyYEs1SIh56B5iYpAEvhWBpghS6rweQJ42iugMIM+rRae0L43HfWIHpyZOXBZHaE1ulwikiXhN9U/w2hlABAbVzQyogeFDfysApk0jHIfd+HNtfACPZvUPR4fMGYI8272jvB46L8YHeB0iDgPImOi+xFHjRP5q0wBIa7X0XlJ2jaRqQqMnTdmlKBlJX2quj5UuikgOoKhkjfRsxXOPkQuyRAbQLMII9+5hMOqEGCQlXqNNwYhsruexacE4Wu3Z5gWxjEPyogSuMxkzJMSAPA4gUq4n67+zP1aWO3vjDvQBK7/jW9XVGEb+0vNS042AFVfF8pccIiOA6DOymsoK6NqBSE9L0aQ1ZfUTKQLPIgNbEN1qsujkf7uyupoFiyoCEkX3hXTARhDpaVsTohaprEcAZraIHkb3bUjd4gHUkmwaqQbyuyENbSiSyqKE6Zyf2MaynqLE6iuZTCJYpCc5nUgmm/ymfdzZx6SnpSMT2tDYx5x0GKlv6fjHY52iL83wUhww5NBoKOkZD6Zm94BDrv+qkHIUoW4lPCD13uV39XM2xEF26pHLqqsxJnbur67mor01nmMjSIkMpRrJAd2DMaFL0fABFN2tVjvoz7a3mxfG9p865I7G68/WGUUq16UBGzkO9BwDRwnWlS6BddTNZoHPrWQO6t9mG0MAkza4A9LTMiYpiMoiMtEpKHwCRbyaU1DLKxDoPYC7MNLzkd/4jepqaXD1D/9waSae7q5evnv5/8X4/Yl//X+WVxkLjUx6niXe9pM/WV2NYdF6mAIu38V0KxFSRBhaJCKlcFu6VkpNTxK2JhCHU0CsyHcphN+8bxgV2ucxo7QQqyWiDZkAVIvVaukQOW4H3Eposmve7vbWWGFpiBJO0MZAUhcUAUqXGfkjCT8PVs/HFFgEKOxkOAXQs1nNycbqeCy1riLcV8cBRHBfUuGnn48dEpY233k6JoMakpqF4ytDThE1Rnq2NoLjQNYTjQ8ZtwE4lmaUkRJNqdpCuGMUlIxNH9JwiagLIELYorOonmxrCxsdFFk6IWUuiIisS0Ry/wQbAoRTe+II0ontbFAh+SukHBEFHUkTpWgaqmEaQGmeROoFkBFJDawC6Nk6+8wpCPeVLtNFneSkrN9V8ZpqSVor1R40cobmzerdkdE79QRHNjVWxWM5fYCdmOTMNUONInoMrQ2xTmAO2sbK2PAedPkca18a70NrXkbpyJb62YBGWpRtEECRmk0hSJG0lLN4+kC834y0pz2kaa1AlBEBZ3Ww6Ty3+rfdQ7GeTBGSAeSsNOdSHzIDLGKWHAd9ccBM740bEDbWy7kABFNzA58L1BjL1hOVVjCijaAOafi8JqQ0EW2Wqk0yDutVjxCvs2GH5UsPGlVSZs4IEN1tkZ5EwJkTiGSyOVUmLocax1CvOoD0Z+v8P6ByByIzelCX3+aYzpbpvfwMmE0nc9w/CXJdaBGMyDRiEMhQK0uAmZ9mcwMfkBIYZan7hBRbvr6aHTP9o7G90NwU60AWNPbIr/96dbU0uOaHf7i6Wlw88a//dXWVsZDIpOdZ4vrbb6+uxrDixygU5btE4FFntwAiSOm/DyBSTQlHqv9JZGwJ8jpp8WMQzBT6HkDFlhsiFIm8VS8ZvHPKd4eSNt+HiNeJy+KUkAA0vmQbrn93fI/25RLpuR280kIwdV6ASETxYFsEBgGNGUsdEs8rwdKlCCkSje5LkWcBZPxr13IwaDQ9rA1zJC8x9WxsjJPiGEBEzLDL70YGpynWE1fE629iW7z2RiCDRshFRELkjRLj8DEq1mJ8UVkCJW6pVIFG7cbrySJvGqtjJdP2FRGGFIEaQFGhRugmOT9SQKSnNNg49RiUcpDnotqDFGkXgM4LWQ+d58FJJrIMDS3+KkYxmXxBgz5BnpqjBBtOyH0pLdXKElAkojXC6IPx3znAz9A9Qo3SWP9IKdtDHeQb60V+J4x7A6LkjSAimdzayMQtdnoXEpH+HtaeLYHRm1CWI4CISKt9jBHBfNvyHvBdPBdYcFG0XU9SXSd3xmdbSpaI6Up90Cko2i+g+yKQX3K2ETFu3yUC2tKZTVdBwHnetwg8aMRIEc0j4LjzXJAzy8g+qnltUdFYCkKiTWmdWhkcijQ2B8GwE+thnac5gpTqsFMaewA5WFdcyxGvUxCFamPWh7rmVp4Bsxum+bu0rqefkTEDYs/6Jkw/F5fzMRuWAm8seIiCnawEHnEHVtKFuAM78+h81FR4sneFIyBoQ2DoCk+ZkROXXlJdzcWSR3q+970jUT9b8izG70/8m38z80vGgiKTnmeJaz/wgepqDCMGKd3bBDCRHSr84Lt635SITPh7GhVKAtSWlHmjAOSNsgMDx0xqp9AzWH1Vqk3qaf6xYmHfbZGXS4pAr3pHrCS2pKYn1Wtc9W1PV1dzkVJ/iyLgLI0Ro4KMEADjSSM94c8lkaYSBYjgV8O/p9ITFDSN9CSjQdLDqBYZdacNQGNCCCIitLCrdokWRBpTZEgApawreZZChiYAowzob5mxSI8r36XPLaqIIpOaEomIJJctJyJTxUChSMS2RO1iFFPCOFCZjACqsdaXyEmSJRY5WT5E9XMMM+aJVDbSE6PMpDtt9zmIeIVGSAHUvZ2acI0A42Dpho1VYLhLwxUubSDjAPNpJQGmnwWjWWrNYWqioAvy0Mi+4VQ8b6b+1lvxu9VW8x4iB0rvCO/5iavj+pAWQYfnrhACra1xqio53gKoNIhFV1E01+AUyIESGCkvZx7JRCXaYB+S/mE6Ce1tJSdBJhPxFcDNXeR9IQrQMgvIiWkNwupAXFkUNxJ4MuZI7Jk8BLKuC+V9Amg9adMj2semS8KzucMoXuuWNo/7Yshz3IMsD9N1aJ1NPcqkD+nJA9EPKRp98hrOsGiAPGtIyRxy9JmzhtaDpaHTPbr72BkwcR0TsoSQHjwfIX2YwE3VeE3SHrBI/QGUqdAoS+IOLIKUyEnhHshAMbufbGaz+8mOttR9qm9N5LFxFw/fc091tTS45r3vra4WF5n0PD/IpOdZAmt6WoQjCCTztpDgMEFJEYpGkFLkgpKe8PfU40MRq7KkMKJSBDvewwwUIiflebvQac/qaTYSaptSEWgcmxL9E/F3J7ZxM5rJq+JnWPG2uLt+QGtj7MHuSnOMaaiDN5xiQ619aZwGY8onKtGiLFM68kBq6fQgVdXSl6gBiRXLTynYT8XYVVmGZ/MUqhhmWFIUiBla2PxGxoHqJTWs6QtEhzQh/TqASBSLKKa50DHD9cffxUidBLmFxJ4ZgDAXGilN97WoRTLgRL4MpuK/ZynglM5GTYgCkCgTwx2jViyijaLJpK7o9HOx8WW1TcngtLR52vOaYg/R4SSfArAUxGYmJ2m/WqdsIl41tRbqsRnaUILAZBytSSV9QL5Y3dbu/pjEbkgaLkUQ0X8fQMSREZkUBWWOs1f+JI6mWf2dcYOOgPb2+Lvm6EOniNQ4JqdGQ6L4iETRM5rA27i8MYyP6Akoqk2WkEzFueD/fvoZWE/iBKKSIergxWhVfgaKaFMyFc48W3ski8wZTOSvRfjSvNk4UGaM6Z3kFKHozwAq49SHKMIAeg+rRUznmDV0asB3cf8EQMkRakoV0IUz1rIbiHjtH2fZ2YPo4faVB6uruSDS05oenfjj66urMSauZzuExldrZJLeKU2P6ivi9WtjRjLDylZRdk7vZV6/5FC283EAc9Q9wHZ/vR3fV5s2EuEodinaq6LnEndgJV3wGYR7IDKT7HPLaH34136tuloaXP2eHypforwIw7aIP7/5b/9teZGx0Mik5+vg8Jd+obj100+N1mDxztuKhz57a3GaOrzuwx+ursYwIhPJPol6JGGghCPc11K16dmoU3wAhcrbMknpJIdp3eKhIgLZxoG8RiqAoZaqjRmmBkhEJnnazHNFz0DNowIoSnjlO+IGHQETl0Jh8gvPnBCYWegx8PBVqUHftS/H39WvkjEhSsF5k2hJ4xDDIjsIZmD0Xo7XX1/Sw+geRoyc+m/QoX8F7801Nz9WXY1h0VUUbeTkb/z5UKIqsEGMjS8Zzbh27LnOfN5IKTbyjGDpwZhiL5EsKQYr1RudvIK7FfN+kzGDr1qaKO5XIc8oUqj3shgSYHw1ofbhDOIHbqxmhZ9KEKghTOMjyykpkpxuYt+FeRv2+CGwTpuQKETmd4+wLJp+Lj7HmhfI2QTRrRQFG9AFwsRKoRBBavuCav5auiw6a8whB+/ce5EdElR/2Wonk0FvpRww0s1IT1omRuyBo0OJsoS6f0jiwVftXOkciNeI1fals9TSjilNWh0EIIvsXMByNTI/E9u5GSmBziYta0BZOAnOD5sL1Dt52s8dsp5QTxBBi5GpsqYpO4ci3AO4djc/A63J7kGuE07ypbOXHTAT1+yrrsZoX8RRwlTr1hxy1JiKmlIF0JrSNH+Ytx6VgggA+dsXIpN0jda22PYLIJK2+zzPMWHIZimCGiEFIJEpDnAMYBKbG21YC0oC9CXSk4KSyAa2FP3d//gfV1dLgzHpWe7PMM5hmy7C75n0PD/IpKfh4fuLXXcUxb3/8bbipnIB7r7nI8XtxZ3F7o/NeLyokZFFARI5adGFROyZF4cIOGuQRLAUcKovYmHuROxZ7c3Rhp6H3stcC5CIU+xaV4Le2WqbNtfHykLvJU7/aG6Iv2vEK42PNV4ij5Z1rmtfEh8Yq76dSU9svCG7mwwfNHhLkJKnRjMqLPxdjsDjByaFR0kUVGrP/BlMuafPiYwK4JqR1c8IcA++LZKWVoOUjFMyTAP0HgCMKhKCiNLkkqI3edp4fOS26jE/F9jfIiNJGh/gmpT1z2tHvgvzbrX1KMrMapAWJB9onZegfdU9JBGkUNuxKbUL6Z2NcOQ6ndXPCHDfBMPdIxzBIQefjaDOgBhIBFmUMO1BSZuvw+eWxkhjZoRjCiHWh2iuk7vjjssB9ZWxLOq/yvrHAD4fnuLnbV4YG5yNDWKEUu1jK9MCsIZkGFEvEYpU39dqb6YRT2e+JpFcN9lLMsq+e6aPIN+jKEsjHDEaLEkmM861NA7OWYlaK76JRXpimRaDTAUC/pzJw6Qbp5imYFvYvJFupqQ/vYZ8leQZyf8Ainy3po3Tey6qrsbQ9Qtkah0ynwKom7+deUT2UQOrADov+hqhG9u29m60ri0zjFLOLTgAn1dS1ml8jXjtH4/ltzYcgvU7/SxH0lIGogY7AeHYg/4TAZRlSvZyAAUEWeAOdpAHO7oukZ5f/5Vfqa6WBle/+z3lS5QXI3lUzdMi/P7NL31p5jpjQZFJT0EgOT932S8Vv/qezTPLsCJB7/vybcWu8tddf+fvhE/nwGpDYoMj+swgU0RTZ54ZjHyUZ0jp3k7Cy4hBuofWIgGo1wk+t0OAPrfnpQPK7kuwA47uYSR4e0cc2XHBux6vruZi4uKYKOiLUdc/GRsCdTFu0RhRqUHftfUL3yXCsgQSmZbylfK88F2ViOd43xQYAU1RbRTBEUB1iay+JBlVlP4UQEqi1gaDeoJWPw5ljHw1BbgeaO2IeEHjSeaXot9sfogIIuMigNLvqCN2ABFaFmlBxqnVZWysjR07Vo+QCFJLRSufovo5hhGZZBha6hwBU/RL0Jqk2ocBWBtPSjYg4SiOEhoHA8s4XpPs/ODFTmvViEyCpeFSKjuRUQE0lj1Jl+3sgygbkZ1ENjcu4OiU9mVxSRcro0ARTwNp3NGHun9WD3nqiW3V1RjURC6AG52JTCannpA+7Jisfs4HfNXq+OKaknlDsg6eQZ2gsCapxuYIIAf6rzCJjs1zrKQAkEl2xuN+lb1CTmY7i8kpQk6OAMrc8O+CjFOHEa0n/i6Kw4S1Z1kiQ3oN0zvJ4S8ORNJV+q/yed6Bru7NCziwhBpK2hndPwKEGDiGAtpb42APi77vA2lJul0AOVJ1D4ETxyI9EbJ2aMxMHpIzzIIASIfRyFQ4AwYneBx6R2C/AWEZQHalBVwhJyF2KQVXmc1NwU7WyBmfF2xuC+7a/Q/+QXW1NLj63e8u/xneLay1xfv5zX+XSc/zgUx6Ig4VD3zy54uH3jWL9Dz0YHHHB/+0uOULf794/+aiuOkTnxh9czZs0xPhSLUlA9pb4lqLmBZeAtOnhUQkj41FetLzWhMh+q4ReERq2LtRFKuOA0R6WpQlfddIT/J8dV/idKIVV1xRXY3Rt/GF97C0+XXvjo2vNd/GjTAaa+JDoz7BB1zvWDxH0wd4jslQo89mEM+x1a4i4wvJzRJEynWlAysp/Z39nEJoz0YgwsQiMDD1R2wcVNxMCYfP++JFb6yMn8EIODKINLoEjEvrJI7kohhESBwlnE66duhjMpDlv2fFmr9LBpWSRvT35BFoICxaj6KVUgxsG3O6hxHYPBfycvBdjUqCZ7DoZTRGbBww+qe6OAM4uRPfxBp30F6xNU21eY3soNp2+l1waJgcoD3kDo3q5yyosQifG2FOxj/VUguYejK+x4YPPFldzUVzHZzdYmA318X7rQdNLAJofHsvsSFM5w01pQp49evbq6sxMPOjBBFaGulGRLqRVLQe7Lu8rBEYEUx/S9YpdW/XMhfwvq6T0DOwTMYMAHEQpIw5N1zh72LKus07/D2XRSRnE757npAitww4n/Lfo8/WZBycF5otIBGKZwpyVgac6/mm5DqsnYGQv+S8s2aOqMOIo490SRtfqulp65TIUD3PAR1obBjQvlRKCgEoM6Z3lG0hkvXWQJDqz2pppoQ9hBHQ0HyVapoH/Jf/+X+rrpYGV/3gD1ZXi4sn//2/r64yFhKZ9ETMkJ57bru3+PgNpz+aS3q+4847q38xhhFt2Dk9oa6G1ackctGege5rU59Sx4PI2zbUoQxI8VBRSQD1ZlFKv3md6B4yDlSjxLxv7Ytiw6VunZhJGZNdSBEnq288UF3NxfE/2VFdjbHpfXH9RUPnEL8bHXy9Y/JdiEiwovYTF8elDeormTwjxYRqdQV0DoIX3eYCUmBNmUNjnFIbS9Dn/WNCKoNSW1/FikkPIgRWvIM79NeaPJYIWH/WTIY6a2P0kMCiTclQsjGjNWlRfL0DscOnfyIe34mreMwpStJOzSEpjqLMkTFSX8dExcQl0ADFSE+IHOsLMZ5CRtE+9mYpcI5po534vmZgU8q6EWJdqCNZkxp2NMdWl5HS+izqdgDN4SglewRa/6+wwYtkhRj5KEuE/KW12j/G89bcGL9zDQyyAKo1p529KSrIjFsw4KwmLTm4LFKZHEZGZLY3x/u4K0Rmcz1FNvE4NOgsFIfR9P54D2kZBSAlTD6c+mbcYLHzDK/Jxtr4PZT0hPXb2Mj1SkmuU9NGi8QlWa0dw2EP2fz0jsdzbBGOBCpfEMB7kMf81CNx6rPV6p+4PF4PAyHU6nQ2GXmWUD+Rou00QhfOISWg4R79l9n5QR3v7SwdgCxqrOfxHYCsbm7iVOImlcrgVyt68B7WoZ/WtZFcpyCSnBqtBaBOYHsbYNkYRPzrHoK/Z7o62SFYK7cE7bfGOpYl/cMUSctn3sQlse02mGaZTKUGtPESDI87L6qfs2BEJmZ5iM5G97Ua83S2TD16SXU1hjUT+88/99nqamlwdUV6hhGe/drn+/dvZtLzvCCTnog3jvTMyMjIyMjIyMjIyMjIyMjIyHjr4Kof+IHqanHx5H/4D9VVxkKCXZh/7rG52DE/aO7ggeIrxeXFjkx4ZmRkZGRkZGRkZGRkZGRkZLz1MKwiME//LDHn9/P17zPOCzLpKdj13bcWX/n0vyt2lwsxYPefPFgUH/72UROjjIyMjIyMjIyMjIyMjIyMjIy3GIaDmVKA4eeg/DmY9/v5+vcZ5wU5vf11cPhLv1Dc+umnAgFfFO+8rXjos7cW0Cs0IyMjIyMjIyMjIyMjIyMjI+NNjqtuvbX8Z4i9DEzQ/J+nsfD//skH/6/yZ8ZCI5OeZ42Zup93fXXmt5vv/MXi7h+KO69nZGRkjPDw/cWuOx6sfrm1uO/Lt71O5PijxS9/z2eK+6rfZrCzuCvXFM7IyHg9jOqPHyg++rryJSMjI2MGu+/5SPG5y36puPs9r69chO/d/vnqlwo3f+qN/7uMjIw/hyh1kZ/9sfuL//Qay/RGds/yw85bbilq5f+G5f/GlOT5//3Jh07bihkLiZzefpbYfc/PF3ftuLPY/eV7y//fWVzx2U8Uv/xw9S8zMjIyZiMc/j/7dHHXF4K8uLd46FNPF7d/8sHiUDjhXge33z3z/Zn/Z8IzIyPDEBwlHyl2ffD+4ivVJxkZGRmGkM12Uykz5hOZr4sPn7Z7Zv6fCc+MjAzEwQNFcecvvSYr7vvwg2dk9ywnBCIyUJEzPxfv94zzg0x6nhUeLR78/M7irg9cX/1+fXHrh2rFfX/yaPV7RkZGxhiH/8ufFl/50AdeIy03fcd3FTd/9U+LPz4083tGRkbGueH64uPBuPjCbcXN1ScZGRkZhk3v+fvF10ZkRPVBRkZGxkLhhtuKX53lFAm9UoqvHiier35/MyAkQy/F/998mHG6L/fgv0x6ng0O7S/2Vpense2yK4tiz/7icPV7RkZGxmk8/8xT5T9nHWSbLy6uKJ4q9hx8/cPtvjs+UuwK0Vvf8wvFA5kgzcjIyMjIyFgqfP4zlU7ykeKOf/ti9WFGRkbG6+PwvqeL4p1bi23V728KnDbRZv0cRWPC56d/Lsi/P0eEUiR3fCmWzyG6/7T83vXJB+dxVqFs42mbM/z3Z290zvk7C3C/hUImPc8alxc7clZHRkbGGeIvX5YiMKqorer/D32qKO764P3F7urfZmRkZGRkZGQsFnZ9bKyThIjyWi7rlZGRcSY49GBx16dLO+YTtxabF4LVWyyEqMt5/x9FYr7O/xfk358lTpONo5Il82/z8P3FrZ+5vLivkuH37bi/uOWeR1/7WlS28dM/f27yPTQAn3VeFJ++Z8mDdzLpedZ4utiTI68yMjLOEH/8zNkLjE3v+UBxe5Y5GRkZGRkZGUuNzbcWP/WhWrF3X1ZKMjIyXgej5or3F1fc/WbsS3CaEgyEZPVzhPP9+9khlCwZEZqhZMk8cnn3nzxY/OU7f/C1RlKjcgOf/6/F7tGfhLKN5T1et2xj6FXxvacjOec33p2H17Ibq9+XCJn0PBuMJm8unn/mW0Wx4+JiU/V7RkZGxmlsu2xn+c9ZJ9CoRMbOYsdFbyaXZ0ZGRkZGRkZGRkZGxhvgNcLz3uLjN1SfvYkwirocDEY/h8OZn4vx+8LjULFnT3V5GhdtLW4uni72Br8Vlm0s7VYt2/ho8cvlvA4/+YuvRYbeXv0bxOj+txa3LvEayKTnWSEw4E8Vd33xNAP+aPHg7w6L27/7NEOekZGRMcaocdHvfvG10P5RY6N3flfxl097PYNi8D2zikA/fP+ctILDX/picV/5/ZtzSY2MjIyMjIyM84qZxhTjOmzl7/fMivopdZbf+N0ri1u+IyslGRkZgBAJ+GN/WtzyhTcn4RlwOiwl/Dz9/4Dz/fv5whXbX09eJ5RtfPi/FvcVtxY/9Z4t1QeAr95f3DKKAi3//8H7i69UHy8lasMRrZyRjlDw9eeLu74689vNd/5icfcPvc7kZ2Rk/PnGw/cXu+54sPrl1uK+L9/2WppB5A2tfh8fEvO+n5GRkTEHgaSYl2L04U8Wuz/2tuqXjIyMjDFC/bdbP/3UrITKncVdXzidgjojT/Z+6peKu0cdmOfaPAG3/+pvFx+/8Xyb6RkZGW9GxPJlBllupCM0JhrV6QTcXMro0CV/9oiG73/usl8sZfdpXmpGfj/0rlmfjezMPy1u+f1S5teq69fk/8z83fJH31U89Nlb4yzmYM/ev7V48DOna7TOnBdFZcPSf/u691skZNIzIyMjIyMjIyMjIyMjIyMjIyPjTYqY9ITPRoE4RXHvf7ytuKkWSMsvFjtmkZ4jorW4s9j9MchinvPfhg/emPScIVkPFB9dwgCenN6ekZGRkZGRkZGRkZGRkZGRkZHxFkJoXPTHn/n3xe7q99DYqPjwtxe7RqQllG38fOFlG2/49uL24sHioYeruMlRuvvrY6as29ZiW/X7UiBHemZkZGRkZGRkZGRkZGRkZGRkZLzJMIqw/PRT1W8Bs0uWzPv377xtXqr5vLKNr5U2ETx8f3HTzz5YjFjED99a3P75B+dGes55jhLR31t8ZNIzIyMjIyMjIyMjIyMjIyMjIyMj4y2FnN6ekZGRkZGRkZGRkZGRkZGRkZGR8ZZCJj0zMjIyMjIyMjIyMjIyMjIyMjIy3lLIpGdGRkZGRkZGRkZGRkZGRkZGRkbGWwqZ9MzIyMjIyMjIyMjIyMjIyMjIyMh4SyGTnhkZGRkZGRkZGRkZGRkZGRkZGRlvKWTSMyMjIyMjIyMjIyMjIyMjIyMjI+MthUx6ZmRkZGRkZGRkZGRkZGRkZGRkZLylkEnPjIyMjIyMjIyMjIyMjIyMjIyMjLcUMumZkZGRkZGRkZGRkZGRkZGRkZGR8ZZCJj0zMjIyMjIyMjIyMjIyMjIyMjIy3lLIpGdGRkZGRkZGRkZGRkZGRkZGRkbGWwq1YYnqOiMjIyMjIyMjY7Fw6MHijg/eX3yl+pVw86d+qbiruKe45dOXF/d9+bZiV/V5RkZGRkZGRkZGRsbrI5OeGRkZGRkZGRnLAIe/9AvFLX/0XcVDn7212FR9lpGRkZGRkZGRkZFxdsjp7RkZGRkZGRkZyxiBDN31yQeLw6PfDhUPfPIjxR1fenT0c9f3VP+/59Hy3z1a/PLp38v/3/GlQ6P/YjZ23zPrv/meXyj+4MXqX6QgRKjO+jvhPg/EfyojIyMjIyMjIyNjSZFJz4yMjIyMjIyMNxm+8ukvFsUn7i12f7n8/xduK27+/GeKXd/zxWLHF8afFZ++Zw4ZufuenyxuL+6c+ffh/3dfXvyvfzWVsHy0+OUP3l9ccXd1j+o+D/2XzHpmZGRkZGRkZGQsL2TSMyMjIyMjIyPjTYabP/Wx4v2bq1827ypueSd99tSYjDz0YPEbv3tLcd/Hrp/5PeCGdxf/yzu/lUZYHtpf7C12Fjsuqn4PuOG24u73nP7DGRkZGRkZGRkZGcsDmfTMyMjIyMjIyHir4+CB4ivFg8Xtc9LSf764688SS7tXZOpdH5y5B6XQZ2RkZGRkZGRkZCw9iuL/Bz65E9+87EeNAAAAAElFTkSuQmCC)" + ], + "metadata": { + "id": "MiycGa3NGQQT" + }, + "id": "MiycGa3NGQQT" + }, + { + "cell_type": "markdown", + "source": [ + "# Interesting examples and use cases" + ], + "metadata": { + "id": "EVn6Bu8fsEWu" + }, + "id": "EVn6Bu8fsEWu" + }, + { + "cell_type": "markdown", + "source": [ + "Using the {wer_model_1} != {wer_model_2} query you can remove all sentences/words that have the same WER, this can be very convenient if the models you are comparing are similar.\n", + "\n", + "In the following example, you can see how QuartzNet and Conformer-Small transcribe the same utterance:\n", + "\n", + "* reference transcript is `the school of the wilderness`\n", + "* QuartzNet's transcript is `the school of the wearerness` (the error in the last word)\n", + "* Conformer-Small's transcript is `the score of the word in its` (almost completely wrong prediction)\n", + "\n" + ], + "metadata": { + "id": "J8ko_-Q1tX6v" + }, + "id": "J8ko_-Q1tX6v" + }, + { + "cell_type": "markdown", + "source": [ + "![image.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "U1qKT9tEWk0j" + }, + "id": "U1qKT9tEWk0j" + }, + { + "cell_type": "markdown", + "source": [ + "General ASR models might make mistakes in proper nouns and narrow domain specific terms since they are rarely found in training datasets." + ], + "metadata": { + "id": "MK7B7xniCPky" + }, + "id": "MK7B7xniCPky" + }, + { + "cell_type": "markdown", + "source": [ + "1) Name: `Shiloh`" + ], + "metadata": { + "id": "sAP9F-bHtpVi" + }, + "id": "sAP9F-bHtpVi" + }, + { + "cell_type": "markdown", + "source": [ + "![image (3).png](data:image/png;base64,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)" + ], + "metadata": { + "id": "e6LCtKS0s9LE" + }, + "id": "e6LCtKS0s9LE" + }, + { + "cell_type": "markdown", + "source": [ + "2) Domain specific term: `rheumatic`" + ], + "metadata": { + "id": "vXf2v13ys84x" + }, + "id": "vXf2v13ys84x" + }, + { + "cell_type": "markdown", + "source": [ + "![image.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "BI7sIWh7o8zj" + }, + "id": "BI7sIWh7o8zj" + }, + { + "cell_type": "markdown", + "source": [ + "It is very likely that, in the word level mode, many words will receive the same accuracy, and will overlap with each other on the scatterplot. That is why the graph will display only one point at a given location. To resolve this issue, there is an option to space them with a given radius:" + ], + "metadata": { + "id": "1D7a1_p5svWu" + }, + "id": "1D7a1_p5svWu" + }, + { + "cell_type": "markdown", + "source": [ + "![image.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "VUL1xTrXpHEL" + }, + "id": "VUL1xTrXpHEL" + }, + { + "cell_type": "markdown", + "source": [ + "This part of the graph shows an enlarged view in coordinates (0, 100). That is, words correctly recognized by the Conformer-Small and not recognized by QuartzNet." + ], + "metadata": { + "id": "2wOPEk4ktKrb" + }, + "id": "2wOPEk4ktKrb" + }, + { + "cell_type": "markdown", + "source": [ + "![image.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "J1jw_1c5pMRc" + }, + "id": "J1jw_1c5pMRc" + }, + { + "cell_type": "markdown", + "source": [ + "Let's write down words recognized only by QuartzNet and words recognized only by Conformer-Small:" + ], + "metadata": { + "id": "IjXTQJZuC3Z-" + }, + "id": "IjXTQJZuC3Z-" + }, + { + "cell_type": "code", + "source": [ + "import pandas as pd\n", + "\n", + "data = {\n", + " \"QuartzNet_words\": [\"allspice\", \"southwark\", \"dante\", \"panada\", \"favour\", \"vapours\", \"fuchs\", \"battlefields\", \"morrel\", \"postscript\"],\n", + " \"Conformer_words\": [\"gothic\", \"tablespoons\", \"heidelberg\", \"bough\", \"nocturnal\", \"meekin\", \"-\", \"-\", \"-\", \"-\"]\n", + "}\n", + "\n", + "df = pd.DataFrame(data)\n", + "\n", + "df\n", + "\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 363 + }, + "id": "57bPPFyxSPiJ", + "outputId": "0b003e3c-2c26-4e38-c4f6-8d4d4b568ea1" + }, + "id": "57bPPFyxSPiJ", + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " QuartzNet_words Conformer_words\n", + "0 allspice gothic\n", + "1 southwark tablespoons\n", + "2 dante heidelberg\n", + "3 panada bough\n", + "4 favour nocturnal\n", + "5 vapours meekin\n", + "6 fuchs -\n", + "7 battlefields -\n", + "8 morrel -\n", + "9 postscript -" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
QuartzNet_wordsConformer_words
0allspicegothic
1southwarktablespoons
2danteheidelberg
3panadabough
4favournocturnal
5vapoursmeekin
6fuchs-
7battlefields-
8morrel-
9postscript-
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 17 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "![image.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "UghOVs2xUG1b" + }, + "id": "UghOVs2xUG1b" + }, + { + "cell_type": "markdown", + "source": [ + "Words in the first group seem to have varied origins, with a mix of English, Germanic, and potentially Latin-derived terms.\n", + "The second group, on the other hand, also appears to contain words of varied linguistic origins but might have a slightly more European or Old World feel, especially with words like `gothic` and `heidelberg`.\n", + "\n", + "**Word Length**\n", + "\n", + "The average word length in the first group might be longer with words like `battlefields` and `postscript`.\n", + "The second group also contains long words, but when we consider `bough` or `gothic`, it might have a slightly shorter average length.\n", + "\n", + "**Functional vs. Descriptive**\n", + "\n", + "The first group contains a mix of nouns that are both functional (like `allspice` or `panada`) and more abstract or descriptive (like `dante` or `vapours`).\n", + "The second group also contains nouns, but they seem more descriptive or pertaining to concepts or themes like `gothic` or `nocturnal`." + ], + "metadata": { + "id": "5_rKgNoovt1k" + }, + "id": "5_rKgNoovt1k" + }, + { + "cell_type": "markdown", + "source": [ + "Next, we will look at several interesting examples that were easily discovered using this tool." + ], + "metadata": { + "id": "C0hcCrZI98fF" + }, + "id": "C0hcCrZI98fF" + }, + { + "cell_type": "markdown", + "source": [ + "# Examples with audio" + ], + "metadata": { + "id": "FV8rLFfr9KVv" + }, + "id": "FV8rLFfr9KVv" + }, + { + "cell_type": "markdown", + "source": [ + "(It is worth noting that the examples below are taken from LibriSpeech dev-clean set, while the rest of the tutorial is based on LibriSpeech test-other)" + ], + "metadata": { + "id": "W35kfqsYkVlf" + }, + "id": "W35kfqsYkVlf" + }, + { + "cell_type": "code", + "source": [ + "#This cell is made so that you can quickly listen to those utterances.\n", + "from IPython.display import Audio, display\n", + "!python3 ./NeMo/scripts/dataset_processing/get_librispeech_data.py --data_sets dev_clean --data_root ." + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "9Q1aCuBC8QZm", + "outputId": "9bf30ea1-c15f-434d-c5cf-56a1111f9a0f" + }, + "id": "9Q1aCuBC8QZm", + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "100% 97/97 [00:05<00:00, 16.64it/s]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "![image.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "BjgoarWOrntG" + }, + "id": "BjgoarWOrntG" + }, + { + "cell_type": "markdown", + "source": [ + "Above is example how Conformer-Small trying to use more common phrases fails with recognizing word `southwark` (Southwark is a district of Central London). We see how Conformer-Small fails on proper nouns and just names in general." + ], + "metadata": { + "id": "QogvdaUzrq2L" + }, + "id": "QogvdaUzrq2L" + }, + { + "cell_type": "code", + "source": [ + "sound_file = \"/content/LibriSpeech/dev-clean-processed/5895-34629-0012.wav\"\n", + "display(Audio(sound_file, autoplay=True))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 76 + }, + "id": "iA-wfLFE-Lke", + "outputId": "e4b35c24-3047-448c-8006-176fe68c80be" + }, + "id": "iA-wfLFE-Lke", + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " " + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "In this case, the speaker does not really pause between `over` and `all`, and QuartzNet transcribes it as a single word." + ], + "metadata": { + "id": "jyX2PVgovdjj" + }, + "id": "jyX2PVgovdjj" + }, + { + "cell_type": "markdown", + "source": [ + "![image.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "z3jISeCTrRVI" + }, + "id": "z3jISeCTrRVI" + }, + { + "cell_type": "code", + "source": [ + "sound_file = \"/content/LibriSpeech/dev-clean-processed/652-129742-0008.wav\"\n", + "display(Audio(sound_file, autoplay=True))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 76 + }, + "id": "5aYtqrTG8Xpa", + "outputId": "b5ff0a91-0bc2-42e6-a575-611d3170731d" + }, + "id": "5aYtqrTG8Xpa", + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " " + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "This is an example of an error in the dataset.\n", + "The audio is challenging (quality is not very good, and the speaker is singing this phrase). But it is obvious that the reference transcript should be `grub pile grub pile`. Likely, the extra space in word `pile` was introduced by replacing a hyphen character with a space in the original dataset:" + ], + "metadata": { + "id": "jz9qolfuvdFj" + }, + "id": "jz9qolfuvdFj" + }, + { + "cell_type": "markdown", + "source": [ + "![image.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "FrjjbUv0A3g9" + }, + "id": "FrjjbUv0A3g9" + }, + { + "cell_type": "code", + "source": [ + "sound_file = \"/content/LibriSpeech/dev-clean-processed/6313-76958-0023.wav\"\n", + "display(Audio(sound_file, autoplay=True))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 76 + }, + "id": "sdKQ2hvB7bIa", + "outputId": "b4e7ddcd-f124-47ea-ac93-67f68efdf707" + }, + "id": "sdKQ2hvB7bIa", + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " " + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Here is another example. I would not say that the models were wrong. When listening to the audio, you might actually think that the announcer is saying “guessed”.\n", + "\n", + "Therefore, if we see that both models make the same errors - this is a good reason to check the dataset." + ], + "metadata": { + "id": "uXbTGzVCvd8K" + }, + "id": "uXbTGzVCvd8K" + }, + { + "cell_type": "markdown", + "source": [ + "![image.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "D4xWxRk8BLtb" + }, + "id": "D4xWxRk8BLtb" + }, + { + "cell_type": "code", + "source": [ + "sound_file = \"/content/LibriSpeech/dev-clean-processed/2428-83705-0008.wav\"\n", + "display(Audio(sound_file, autoplay=True))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 76 + }, + "id": "MsOJGMOn8ERg", + "outputId": "09269135-92b6-415d-cb4f-8d7b86d31f3e" + }, + "id": "MsOJGMOn8ERg", + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " " + ] + }, + "metadata": {} + } + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "display_name": "Python 3", + "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.8.10" + }, + "accelerator": "GPU" + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file From a8b85b73f5ad815077e477408dee49caf6bdc43c Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Mon, 11 Sep 2023 20:35:08 -0600 Subject: [PATCH 025/112] Update ptl training ckpt conversion script to work with dist ckpt (#7416) * update ptl convert script Signed-off-by: eharper * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * don't break legacy Signed-off-by: eharper * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: eharper Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../megatron_ckpt_to_nemo.py | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/examples/nlp/language_modeling/megatron_ckpt_to_nemo.py b/examples/nlp/language_modeling/megatron_ckpt_to_nemo.py index e2fd1d4bbcd1..1161614e0a53 100644 --- a/examples/nlp/language_modeling/megatron_ckpt_to_nemo.py +++ b/examples/nlp/language_modeling/megatron_ckpt_to_nemo.py @@ -24,10 +24,12 @@ --pipeline_model_parallel_size """ +import dis import os from argparse import ArgumentParser import torch +from genericpath import isdir from megatron.core import parallel_state from omegaconf import open_dict from pytorch_lightning.plugins.environments import TorchElasticEnvironment @@ -40,7 +42,7 @@ from nemo.collections.nlp.models.language_modeling.megatron_retrieval_model import MegatronRetrievalModel from nemo.collections.nlp.models.language_modeling.megatron_t5_model import MegatronT5Model from nemo.collections.nlp.models.machine_translation.megatron_nmt_model import MegatronNMTModel -from nemo.collections.nlp.parts.nlp_overrides import NLPSaveRestoreConnector +from nemo.collections.nlp.parts.nlp_overrides import NLPDDPStrategy, NLPSaveRestoreConnector from nemo.utils import AppState, logging from nemo.utils.distributed import initialize_distributed from nemo.utils.model_utils import inject_model_parallel_rank @@ -100,12 +102,16 @@ def convert(local_rank, rank, world_size, args): app_state = AppState() app_state.data_parallel_rank = 0 num_nodes = world_size // args.gpus_per_node + plugins = [] + strategy = "auto" if args.bcp: - trainer = Trainer( - devices=args.gpus_per_node, num_nodes=num_nodes, accelerator='gpu', plugins=[TorchElasticEnvironment()] - ) - else: - trainer = Trainer(devices=args.gpus_per_node, num_nodes=num_nodes, accelerator='gpu') + plugins.append(TorchElasticEnvironment()) + if args.model_type == 'gpt': + strategy = NLPDDPStrategy() + + trainer = Trainer( + devices=args.gpus_per_node, num_nodes=num_nodes, accelerator='gpu', plugins=plugins, strategy=strategy + ) app_state.pipeline_model_parallel_size = args.pipeline_model_parallel_size app_state.tensor_model_parallel_size = args.tensor_model_parallel_size @@ -135,8 +141,13 @@ def convert(local_rank, rank, world_size, args): app_state.pipeline_model_parallel_rank = parallel_state.get_pipeline_model_parallel_rank() app_state.tensor_model_parallel_rank = parallel_state.get_tensor_model_parallel_rank() - # inject model parallel rank - checkpoint_path = inject_model_parallel_rank(os.path.join(args.checkpoint_folder, args.checkpoint_name)) + # check for distributed checkpoint + dist_ckpt_dir = os.path.join(args.checkpoint_folder, args.checkpoint_name) + if os.path.isdir(dist_ckpt_dir): + checkpoint_path = dist_ckpt_dir + else: + # legacy checkpoint needs model parallel injection + checkpoint_path = inject_model_parallel_rank(os.path.join(args.checkpoint_folder, args.checkpoint_name)) logging.info( f'rank: {rank}, local_rank: {local_rank}, is loading checkpoint: {checkpoint_path} for tp_rank: {app_state.tensor_model_parallel_rank} and pp_rank: {app_state.pipeline_model_parallel_rank}' From a81bdbe6bc5962e279d1616e5c3358e0a3104aab Mon Sep 17 00:00:00 2001 From: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Date: Mon, 11 Sep 2023 21:08:01 -0700 Subject: [PATCH 026/112] Allow disabling sanity checking when num_sanity_val_steps=0 (#7413) * Allow disabling sanity checking when num_sanity_val_steps=0 Signed-off-by: Abhishree * Update num_sanity_val_steps to be a multiple of num_microbatches Signed-off-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Abhishree Signed-off-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../nlp/models/language_modeling/megatron_base_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_base_model.py b/nemo/collections/nlp/models/language_modeling/megatron_base_model.py index 29b1886c957a..3a4fcd5747af 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_base_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_base_model.py @@ -211,8 +211,8 @@ def _reconfigure_val_batches(self): """ # Override limit_val_batches to be a multiple of num microbatches and so there are limit_val_batches//num_micro_batches num of global batches self.trainer.limit_val_batches *= get_num_microbatches() - # Override num sanity steps equal to num of microbatches and perform one val_step - self.trainer.num_sanity_val_steps = get_num_microbatches() + # Override num sanity steps to be a multiple of num of microbatches + self.trainer.num_sanity_val_steps *= get_num_microbatches() def _enable_nvidia_optimizations(self): "These optimizations are present in NVIDIA NGC PyTorch Containers" From b79ec2a4850e6dd37377dbed48f745a1c5bf35fd Mon Sep 17 00:00:00 2001 From: PeganovAnton Date: Tue, 12 Sep 2023 06:28:39 +0200 Subject: [PATCH 027/112] Add comprehensive error messages (#7261) Signed-off-by: Anton Peganov Signed-off-by: Sasha Meister --- .../nlp/modules/common/text_generation_utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/nemo/collections/nlp/modules/common/text_generation_utils.py b/nemo/collections/nlp/modules/common/text_generation_utils.py index 36b30aae47b9..33bd59fd9b30 100644 --- a/nemo/collections/nlp/modules/common/text_generation_utils.py +++ b/nemo/collections/nlp/modules/common/text_generation_utils.py @@ -160,6 +160,14 @@ def get_computeprob_response(tokenizer, response, inputs): token_len = int(inputs[1][batch_id].item()) new_token_id = inputs[0][batch_id][:token_len].tolist() new_text = tokenizer.ids_to_text(new_token_id) + else: + raise TypeError( + f"Unsupported type of `inputs[0]`: {type(inputs[0])}. Supported types: `str`, `torch.Tensor`." + ) + else: + raise TypeError( + f"Unsupported type of parameter `inputs`: {type(inputs)}. Supported types: `list` and `tuple`" + ) new_token_ids.append(new_token_id) new_tokens.append(response['tokens'][batch_id][:token_len]) new_texts.append(new_text) From 0d85ab8bf81c94e7a5ddb89a82848a21d96090a1 Mon Sep 17 00:00:00 2001 From: Nikolay Karpov Date: Tue, 12 Sep 2023 13:13:34 +0400 Subject: [PATCH 028/112] check NEMO_PATH (#7418) Signed-off-by: Nikolay Karpov Signed-off-by: Sasha Meister --- .../ngram_lm/install_beamsearch_decoders.sh | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/scripts/asr_language_modeling/ngram_lm/install_beamsearch_decoders.sh b/scripts/asr_language_modeling/ngram_lm/install_beamsearch_decoders.sh index 3ba337a6afd3..b09ed351bd15 100755 --- a/scripts/asr_language_modeling/ngram_lm/install_beamsearch_decoders.sh +++ b/scripts/asr_language_modeling/ngram_lm/install_beamsearch_decoders.sh @@ -17,20 +17,25 @@ shopt -s expand_aliases NEMO_PATH=/workspace/nemo # Path to NeMo folder: /workspace/nemo if you use NeMo/Dockerfile -if [ "$#" -eq 1 ] -then +if [ "$#" -eq 1 ]; then NEMO_PATH=$1 fi KENLM_MAX_ORDER=10 # Maximum order of KenLM model, also specified in the setup_os2s_decoders.py +if [ -d "$NEMO_PATH" ]; then + echo "The folder '$NEMO_PATH' exists." +else + echo "Error: The folder '$NEMO_PATH' does not exist. Specify it as a first command line positional argument!" + exit 1 +fi cd $NEMO_PATH if [ $(id -u) -eq 0 ]; then - alias aptupdate='apt-get update' - alias b2install='./b2' - else - alias aptupdate='sudo apt-get update' - alias b2install='sudo ./b2' + alias aptupdate='apt-get update' + alias b2install='./b2' +else + alias aptupdate='sudo apt-get update' + alias b2install='sudo ./b2' fi aptupdate && apt-get upgrade -y && apt-get install -y liblzma-dev && rm -rf /var/lib/apt/lists/* # liblzma needed for flashlight decoder From f7fc3bd08171c7f0a10ded2d7fa2bd5d932ed562 Mon Sep 17 00:00:00 2001 From: Adi Renduchintala Date: Wed, 13 Sep 2023 09:20:30 -0700 Subject: [PATCH 029/112] layer selection for ia3 (#7417) * layer selection for ia3 Signed-off-by: arendu * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: arendu Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../nlp/models/language_modeling/megatron_gpt_peft_models.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_peft_models.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_peft_models.py index 7b772ea33a47..ba7dd4529326 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_peft_models.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_peft_models.py @@ -434,6 +434,11 @@ def __init__(self, cfg: DictConfig, trainer: Trainer): self.name_key_to_cfg[k] = infused_adapter_cfg else: raise ValueError(f"PEFT Key {k} is unknown.") + + self.layer_selection = cfg.peft.ia3_tuning.get("layer_selection", None) + if self.layer_selection is None: + self.layer_selection = list(range(1, cfg.num_layers + 1)) + super().__init__(cfg, trainer) From 3c90b5e9a8b9d22c92accd85ecbb7d8b904046c5 Mon Sep 17 00:00:00 2001 From: Robin Dong Date: Fri, 15 Sep 2023 08:52:58 +1000 Subject: [PATCH 030/112] Fix missing pip package 'einops' (#7397) Signed-off-by: Robin Dong Signed-off-by: Sasha Meister --- tutorials/asr/Online_Offline_Speech_Commands_Demo.ipynb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tutorials/asr/Online_Offline_Speech_Commands_Demo.ipynb b/tutorials/asr/Online_Offline_Speech_Commands_Demo.ipynb index b1d70d8446c2..9654a0fe6924 100644 --- a/tutorials/asr/Online_Offline_Speech_Commands_Demo.ipynb +++ b/tutorials/asr/Online_Offline_Speech_Commands_Demo.ipynb @@ -672,7 +672,8 @@ "# !git clone --depth 1 --branch v1.8.0 https://github.com/microsoft/onnxruntime.git .\n", "# !./build.sh --skip_tests --config Release --build_shared_lib --parallel --use_cuda --cuda_home /usr/local/cuda --cudnn_home /usr/lib/x86_64-linux-gnu --build_wheel\n", "# !pip install ./build/Linux/Release/dist/onnxruntime*.whl\n", - "# %cd .." + "# %cd ..\n", + "!pip install einops\n" ] }, { From 12e77d4a27a32dd1ca35c076b21afb8c08f2124d Mon Sep 17 00:00:00 2001 From: Robin Dong Date: Fri, 15 Sep 2023 16:50:51 +1000 Subject: [PATCH 031/112] Fix failure of pyaudio in Google Colab (#7396) Signed-off-by: Robin Dong Signed-off-by: Sasha Meister --- tutorials/asr/Online_Offline_Speech_Commands_Demo.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/asr/Online_Offline_Speech_Commands_Demo.ipynb b/tutorials/asr/Online_Offline_Speech_Commands_Demo.ipynb index 9654a0fe6924..31035ac19643 100644 --- a/tutorials/asr/Online_Offline_Speech_Commands_Demo.ipynb +++ b/tutorials/asr/Online_Offline_Speech_Commands_Demo.ipynb @@ -24,7 +24,7 @@ "\n", "## Install dependencies\n", "!pip install wget\n", - "!apt-get install sox libsndfile1 ffmpeg portaudio19-dev\n", + "!apt-get install libsndfile1 ffmpeg portaudio19-dev\n", "!pip install text-unidecode\n", "!pip install pyaudio\n", "\n", From aa3f36d6c67fb02fd7d33e57c2ff4e2b926b7fba Mon Sep 17 00:00:00 2001 From: Samuele Cornell Date: Mon, 18 Sep 2023 18:06:13 +0100 Subject: [PATCH 032/112] Update README.md: output_path --> output_manifest_filepath (#7442) Signed-off-by: Samuele Cornell Signed-off-by: Sasha Meister --- tools/speech_data_simulator/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/speech_data_simulator/README.md b/tools/speech_data_simulator/README.md index 94b917af88f3..ad589ef9194d 100644 --- a/tools/speech_data_simulator/README.md +++ b/tools/speech_data_simulator/README.md @@ -89,7 +89,7 @@ python scripts/dataset_processing/get_librispeech_data.py \ python /scripts/speaker_tasks/create_alignment_manifest.py \ --input_manifest_filepath \ --base_alignment_path \ - --output_path train-clean-100-align.json \ + --output_manifest_filepath train-clean-100-align.json \ --ctm_output_directory ./ctm_out \ --libri_dataset_split train-clean-100 ``` From 0a8eaf24cdb687ee0bf94ba20f9c9e59d320ce47 Mon Sep 17 00:00:00 2001 From: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Date: Mon, 18 Sep 2023 18:44:36 -0400 Subject: [PATCH 033/112] Add rope dynamic linear scaling (#7437) * Add dynamic linear scaling Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix bug Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix Signed-off-by: Cheng-Ping Hsieh --------- Signed-off-by: Cheng-Ping Hsieh Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Yang Zhang Signed-off-by: Sasha Meister --- .../megatron_gpt_continue_training.py | 4 +++- .../modules/common/megatron/language_model.py | 4 +++- .../rotary_position_embedding.py | 21 ++++++++++++++----- 3 files changed, 22 insertions(+), 7 deletions(-) mode change 100644 => 100755 examples/nlp/language_modeling/megatron_gpt_continue_training.py diff --git a/examples/nlp/language_modeling/megatron_gpt_continue_training.py b/examples/nlp/language_modeling/megatron_gpt_continue_training.py old mode 100644 new mode 100755 index c829a91c4092..d866530f1312 --- a/examples/nlp/language_modeling/megatron_gpt_continue_training.py +++ b/examples/nlp/language_modeling/megatron_gpt_continue_training.py @@ -61,7 +61,9 @@ def _modify_config(gpt_cfg, cfg, add_cfg_to_tree=False): gpt_cfg.max_position_embeddings = cfg.model.max_position_embeddings gpt_cfg.seq_len_interpolation_factor = cfg.model.seq_len_interpolation_factor gpt_cfg.use_flash_attention = cfg.model.use_flash_attention - + assert ( + gpt_cfg.encoder_seq_length == gpt_cfg.max_position_embeddings * gpt_cfg.seq_len_interpolation_factor + ), 'seq_length should be equal to max_position_embedding * seq_len_interpolation_factor' # This is needed when modifying a hparam file directly to load `.ckpt` files. # This is not needed to modify the cfg in `.nemo` files. if add_cfg_to_tree: diff --git a/nemo/collections/nlp/modules/common/megatron/language_model.py b/nemo/collections/nlp/modules/common/megatron/language_model.py index cb68faa0a100..bc0ef1df4ec2 100755 --- a/nemo/collections/nlp/modules/common/megatron/language_model.py +++ b/nemo/collections/nlp/modules/common/megatron/language_model.py @@ -554,7 +554,9 @@ def __init__( if rotary_percentage < 1: rotary_dim = int(rotary_dim * rotary_percentage) self.rotary_pos_emb = RotaryEmbedding( - rotary_dim, seq_len_interpolation_factor=seq_len_interpolation_factor + rotary_dim, + seq_len_interpolation_factor=seq_len_interpolation_factor, + pretrained_max_position_embeddings=max_position_embeddings, ) elif position_embedding_type == 'alibi': diff --git a/nemo/collections/nlp/modules/common/megatron/position_embedding/rotary_position_embedding.py b/nemo/collections/nlp/modules/common/megatron/position_embedding/rotary_position_embedding.py index c97010ecb911..6dba53dd1e7a 100644 --- a/nemo/collections/nlp/modules/common/megatron/position_embedding/rotary_position_embedding.py +++ b/nemo/collections/nlp/modules/common/megatron/position_embedding/rotary_position_embedding.py @@ -25,25 +25,36 @@ class RotaryEmbedding(nn.Module): Implements Rotary Position Embedding from https://arxiv.org/abs/2104.09864. """ - def __init__(self, dim: int, seq_len_interpolation_factor: int = None): + def __init__( + self, dim: int, seq_len_interpolation_factor: int = None, pretrained_max_position_embeddings: int = None + ): """ Args: dim (int): rotary embedding dimension seq_len_interpolation_factor (int): if not None, discrete positions will be interpolated by this factor via the trick in https://arxiv.org/abs/2306.15595. + pretrained_max_position_embeddings (int): pre-trained max_position_embeddings before position interpolation. """ super().__init__() self.seq_len_interpolation_factor = seq_len_interpolation_factor inv_freq = 1.0 / (10000 ** (torch.arange(0, dim, 2).float() / dim)) self.register_buffer('inv_freq', inv_freq) + self.pretrained_max_position_embeddings = pretrained_max_position_embeddings def forward(self, max_seq_len, offset=0): seq = torch.arange(max_seq_len, device=self.inv_freq.device) + offset - if self.seq_len_interpolation_factor is not None: - seq = seq.type_as(self.inv_freq) - seq *= 1 / self.seq_len_interpolation_factor - freqs = einsum('i , j -> i j', seq.type_as(self.inv_freq), self.inv_freq) + seq = seq.type_as(self.inv_freq) + + if self.pretrained_max_position_embeddings is not None and self.seq_len_interpolation_factor is not None: + if max_seq_len > self.pretrained_max_position_embeddings * self.seq_len_interpolation_factor: + # dynamic linear scaling (length > position we have learned) + seq *= 1 / (max_seq_len / self.pretrained_max_position_embeddings) + else: + # fixed linear scaling + seq *= 1 / self.seq_len_interpolation_factor + + freqs = einsum('i , j -> i j', seq, self.inv_freq) # first part even vector components, second part odd vector components, # 2 * dim in dimension size emb = torch.cat((freqs, freqs), dim=-1) From 5bb294098d92b666c4172f3facead60e350f46bd Mon Sep 17 00:00:00 2001 From: Kunal Dhawan Date: Mon, 18 Sep 2023 17:48:37 -0700 Subject: [PATCH 034/112] Fix None dataloader issue in PTL2.0 (#7455) * Fix None dataloader issue in PTL2.0 Signed-off-by: KunalDhawan * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updating values of self._validation_dl and self._test_dl as well Signed-off-by: KunalDhawan * updating values of self._validation_dl and self._test_dl as well Signed-off-by: KunalDhawan * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: KunalDhawan Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- nemo/core/classes/modelPT.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/nemo/core/classes/modelPT.py b/nemo/core/classes/modelPT.py index c5cc99d5208d..6e616319b12b 100644 --- a/nemo/core/classes/modelPT.py +++ b/nemo/core/classes/modelPT.py @@ -856,12 +856,18 @@ def train_dataloader(self): return self._train_dl def val_dataloader(self): - if self._validation_dl is not None: - return self._validation_dl + if self._validation_dl is None: + # None dataloader no longer supported in PTL2.0 + self._validation_dl = [] + + return self._validation_dl def test_dataloader(self): - if self._test_dl is not None: - return self._test_dl + if self._test_dl is None: + # None dataloader no longer supported in PTL2.0 + self._test_dl = [] + + return self._test_dl def on_validation_epoch_end(self) -> Optional[Dict[str, Dict[str, torch.Tensor]]]: """ From bdcde4629ca9b43ce373d7ee49e6cdbc7c41dce3 Mon Sep 17 00:00:00 2001 From: Aleksandr Laptev Date: Tue, 19 Sep 2023 07:53:05 +0700 Subject: [PATCH 035/112] [ASR] Confidence measure -> method renames (#7434) * measure -> method Signed-off-by: Aleksandr Laptev * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Aleksandr Laptev Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- nemo/collections/asr/metrics/rnnt_wer.py | 28 ++-- nemo/collections/asr/metrics/rnnt_wer_bpe.py | 8 +- nemo/collections/asr/metrics/wer.py | 18 +-- nemo/collections/asr/metrics/wer_bpe.py | 8 +- .../asr/models/confidence_ensemble.py | 12 +- .../parts/submodules/ctc_greedy_decoding.py | 41 ++---- .../parts/submodules/rnnt_greedy_decoding.py | 128 +++++++----------- .../asr_confidence_benchmarking_utils.py | 6 +- .../asr/parts/utils/asr_confidence_utils.py | 105 ++++++-------- .../confidence_ensembles/build_ensemble.py | 4 +- .../confidence_ensembles/ensemble_config.yaml | 2 +- .../confidence/benchmark_asr_confidence.py | 8 +- .../asr/confidence/test_asr_confidence.py | 27 ++-- .../test_asr_hybrid_rnnt_ctc_model_char.py | 6 +- .../asr/test_asr_rnnt_encdec_model.py | 6 +- .../asr/test_confidence_ensembles.py | 6 +- tutorials/asr/ASR_Confidence_Estimation.ipynb | 14 +- 17 files changed, 172 insertions(+), 255 deletions(-) diff --git a/nemo/collections/asr/metrics/rnnt_wer.py b/nemo/collections/asr/metrics/rnnt_wer.py index fc083a2ab3f3..97c9c4575982 100644 --- a/nemo/collections/asr/metrics/rnnt_wer.py +++ b/nemo/collections/asr/metrics/rnnt_wer.py @@ -100,16 +100,16 @@ class AbstractRNNTDecoding(ConfidenceMixin): from the `token_confidence`. aggregation: Which aggregation type to use for collapsing per-token confidence into per-word confidence. Valid options are `mean`, `min`, `max`, `prod`. - measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. entropy_type: Which type of entropy to use (str). - Used if confidence_measure_cfg.name is set to `entropy`. + Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -140,7 +140,7 @@ class AbstractRNNTDecoding(ConfidenceMixin): timestep during greedy decoding. Setting to larger values allows longer sentences to be decoded, at the cost of increased execution time. preserve_frame_confidence: Same as above, overrides above value. - confidence_measure_cfg: Same as above, overrides confidence_cfg.measure_cfg. + confidence_method_cfg: Same as above, overrides confidence_cfg.method_cfg. "beam": beam_size: int, defining the beam size for beam search. Must be >= 1. @@ -277,7 +277,7 @@ def __init__(self, decoding_cfg, decoder, joint, blank_id: int): ), preserve_alignments=self.preserve_alignments, preserve_frame_confidence=self.preserve_frame_confidence, - confidence_measure_cfg=self.confidence_measure_cfg, + confidence_method_cfg=self.confidence_method_cfg, ) else: self.decoding = greedy_decode.GreedyTDTInfer( @@ -291,7 +291,7 @@ def __init__(self, decoding_cfg, decoder, joint, blank_id: int): ), preserve_alignments=self.preserve_alignments, preserve_frame_confidence=self.preserve_frame_confidence, - confidence_measure_cfg=self.confidence_measure_cfg, + confidence_method_cfg=self.confidence_method_cfg, ) else: self.decoding = greedy_decode.GreedyMultiblankRNNTInfer( @@ -304,7 +304,7 @@ def __init__(self, decoding_cfg, decoder, joint, blank_id: int): ), preserve_alignments=self.preserve_alignments, preserve_frame_confidence=self.preserve_frame_confidence, - confidence_measure_cfg=self.confidence_measure_cfg, + confidence_method_cfg=self.confidence_method_cfg, ) elif self.cfg.strategy == 'greedy_batch': @@ -320,7 +320,7 @@ def __init__(self, decoding_cfg, decoder, joint, blank_id: int): ), preserve_alignments=self.preserve_alignments, preserve_frame_confidence=self.preserve_frame_confidence, - confidence_measure_cfg=self.confidence_measure_cfg, + confidence_method_cfg=self.confidence_method_cfg, ) else: self.decoding = greedy_decode.GreedyBatchedTDTInfer( @@ -334,7 +334,7 @@ def __init__(self, decoding_cfg, decoder, joint, blank_id: int): ), preserve_alignments=self.preserve_alignments, preserve_frame_confidence=self.preserve_frame_confidence, - confidence_measure_cfg=self.confidence_measure_cfg, + confidence_method_cfg=self.confidence_method_cfg, ) else: @@ -348,7 +348,7 @@ def __init__(self, decoding_cfg, decoder, joint, blank_id: int): ), preserve_alignments=self.preserve_alignments, preserve_frame_confidence=self.preserve_frame_confidence, - confidence_measure_cfg=self.confidence_measure_cfg, + confidence_method_cfg=self.confidence_method_cfg, ) elif self.cfg.strategy == 'beam': @@ -1005,16 +1005,16 @@ class RNNTDecoding(AbstractRNNTDecoding): from the `token_confidence`. aggregation: Which aggregation type to use for collapsing per-token confidence into per-word confidence. Valid options are `mean`, `min`, `max`, `prod`. - measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. entropy_type: Which type of entropy to use (str). - Used if confidence_measure_cfg.name is set to `entropy`. + Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -1047,7 +1047,7 @@ class RNNTDecoding(AbstractRNNTDecoding): preserve_frame_confidence: Same as above, overrides above value. - confidence_measure_cfg: Same as above, overrides confidence_cfg.measure_cfg. + confidence_method_cfg: Same as above, overrides confidence_cfg.method_cfg. "beam": beam_size: int, defining the beam size for beam search. Must be >= 1. diff --git a/nemo/collections/asr/metrics/rnnt_wer_bpe.py b/nemo/collections/asr/metrics/rnnt_wer_bpe.py index 40ae00b413b3..e8ea8f399b99 100644 --- a/nemo/collections/asr/metrics/rnnt_wer_bpe.py +++ b/nemo/collections/asr/metrics/rnnt_wer_bpe.py @@ -100,16 +100,16 @@ class RNNTBPEDecoding(AbstractRNNTDecoding): from the `token_confidence`. aggregation: Which aggregation type to use for collapsing per-token confidence into per-word confidence. Valid options are `mean`, `min`, `max`, `prod`. - measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. entropy_type: Which type of entropy to use (str). - Used if confidence_measure_cfg.name is set to `entropy`. + Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -142,7 +142,7 @@ class RNNTBPEDecoding(AbstractRNNTDecoding): preserve_frame_confidence: Same as above, overrides above value. - confidence_measure_cfg: Same as above, overrides confidence_cfg.measure_cfg. + confidence_method_cfg: Same as above, overrides confidence_cfg.method_cfg. "beam": beam_size: int, defining the beam size for beam search. Must be >= 1. diff --git a/nemo/collections/asr/metrics/wer.py b/nemo/collections/asr/metrics/wer.py index 7802e3b8a0c9..14fa46b308ab 100644 --- a/nemo/collections/asr/metrics/wer.py +++ b/nemo/collections/asr/metrics/wer.py @@ -258,16 +258,16 @@ class AbstractCTCDecoding(ConfidenceMixin): from the `token_confidence`. aggregation: Which aggregation type to use for collapsing per-token confidence into per-word confidence. Valid options are `mean`, `min`, `max`, `prod`. - measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. entropy_type: Which type of entropy to use (str). - Used if confidence_measure_cfg.name is set to `entropy`. + Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -300,7 +300,7 @@ class AbstractCTCDecoding(ConfidenceMixin): preserve_alignments: Same as above, overrides above value. compute_timestamps: Same as above, overrides above value. preserve_frame_confidence: Same as above, overrides above value. - confidence_measure_cfg: Same as above, overrides confidence_cfg.measure_cfg. + confidence_method_cfg: Same as above, overrides confidence_cfg.method_cfg. "beam": beam_size: int, defining the beam size for beam search. Must be >= 1. @@ -389,7 +389,7 @@ def __init__(self, decoding_cfg, blank_id: int): preserve_alignments=self.preserve_alignments, compute_timestamps=self.compute_timestamps, preserve_frame_confidence=self.preserve_frame_confidence, - confidence_measure_cfg=self.confidence_measure_cfg, + confidence_method_cfg=self.confidence_method_cfg, ) elif self.cfg.strategy == 'beam': @@ -1037,16 +1037,16 @@ class CTCDecoding(AbstractCTCDecoding): from the `token_confidence`. aggregation: Which aggregation type to use for collapsing per-token confidence into per-word confidence. Valid options are `mean`, `min`, `max`, `prod`. - measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. entropy_type: Which type of entropy to use (str). - Used if confidence_measure_cfg.name is set to `entropy`. + Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -1079,7 +1079,7 @@ class CTCDecoding(AbstractCTCDecoding): preserve_alignments: Same as above, overrides above value. compute_timestamps: Same as above, overrides above value. preserve_frame_confidence: Same as above, overrides above value. - confidence_measure_cfg: Same as above, overrides confidence_cfg.measure_cfg. + confidence_method_cfg: Same as above, overrides confidence_cfg.method_cfg. "beam": beam_size: int, defining the beam size for beam search. Must be >= 1. diff --git a/nemo/collections/asr/metrics/wer_bpe.py b/nemo/collections/asr/metrics/wer_bpe.py index 0a277e57e86a..b95bb62008ae 100644 --- a/nemo/collections/asr/metrics/wer_bpe.py +++ b/nemo/collections/asr/metrics/wer_bpe.py @@ -74,16 +74,16 @@ class CTCBPEDecoding(AbstractCTCDecoding): from the `token_confidence`. aggregation: Which aggregation type to use for collapsing per-token confidence into per-word confidence. Valid options are `mean`, `min`, `max`, `prod`. - measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. entropy_type: Which type of entropy to use (str). - Used if confidence_measure_cfg.name is set to `entropy`. + Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -116,7 +116,7 @@ class CTCBPEDecoding(AbstractCTCDecoding): preserve_alignments: Same as above, overrides above value. compute_timestamps: Same as above, overrides above value. preserve_frame_confidence: Same as above, overrides above value. - confidence_measure_cfg: Same as above, overrides confidence_cfg.measure_cfg. + confidence_method_cfg: Same as above, overrides confidence_cfg.method_cfg. "beam": beam_size: int, defining the beam size for beam search. Must be >= 1. diff --git a/nemo/collections/asr/models/confidence_ensemble.py b/nemo/collections/asr/models/confidence_ensemble.py index bf65ff96ef5c..dcbb0a05976c 100644 --- a/nemo/collections/asr/models/confidence_ensemble.py +++ b/nemo/collections/asr/models/confidence_ensemble.py @@ -25,7 +25,7 @@ from nemo.collections.asr.models.hybrid_rnnt_ctc_models import EncDecHybridRNNTCTCModel from nemo.collections.asr.parts.utils.asr_confidence_utils import ( ConfidenceConfig, - ConfidenceMeasureConfig, + ConfidenceMethodConfig, get_confidence_aggregation_bank, get_confidence_measure_bank, ) @@ -61,7 +61,7 @@ def to_confidence_config(self) -> ConfidenceConfig: return ConfidenceConfig( exclude_blank=self.exclude_blank, aggregation=self.aggregation, - measure_cfg=ConfidenceMeasureConfig( + method_cfg=ConfidenceMethodConfig( name=name, entropy_type=entropy_type, alpha=self.alpha, entropy_norm=entropy_norm, ), ) @@ -126,7 +126,7 @@ def compute_confidence(hypothesis: Hypothesis, confidence_cfg: ConfidenceConfig) hypothesis: generated hypothesis as returned from the transcribe method of the ASR model. confidence_cfg: confidence config specifying what kind of - measure/aggregation should be used. + method/aggregation should be used. Returns: float: confidence score. @@ -135,12 +135,12 @@ def compute_confidence(hypothesis: Hypothesis, confidence_cfg: ConfidenceConfig) filtered_logprobs = get_filtered_logprobs(hypothesis, confidence_cfg.exclude_blank) vocab_size = filtered_logprobs.shape[1] aggr_func = get_confidence_aggregation_bank()[confidence_cfg.aggregation] - if confidence_cfg.measure_cfg.name == "max_prob": + if confidence_cfg.method_cfg.name == "max_prob": conf_type = "max_prob" alpha = 1.0 else: - conf_type = f"entropy_{confidence_cfg.measure_cfg.entropy_type}_{confidence_cfg.measure_cfg.entropy_norm}" - alpha = confidence_cfg.measure_cfg.alpha + conf_type = f"entropy_{confidence_cfg.method_cfg.entropy_type}_{confidence_cfg.method_cfg.entropy_norm}" + alpha = confidence_cfg.method_cfg.alpha conf_func = get_confidence_measure_bank()[conf_type] conf_value = aggr_func(conf_func(filtered_logprobs, v=vocab_size, t=alpha)).cpu().item() diff --git a/nemo/collections/asr/parts/submodules/ctc_greedy_decoding.py b/nemo/collections/asr/parts/submodules/ctc_greedy_decoding.py index 1f29a511fc9c..44ae9f4a134b 100644 --- a/nemo/collections/asr/parts/submodules/ctc_greedy_decoding.py +++ b/nemo/collections/asr/parts/submodules/ctc_greedy_decoding.py @@ -19,7 +19,7 @@ from omegaconf import DictConfig, OmegaConf from nemo.collections.asr.parts.utils import rnnt_utils -from nemo.collections.asr.parts.utils.asr_confidence_utils import ConfidenceMeasureConfig, ConfidenceMeasureMixin +from nemo.collections.asr.parts.utils.asr_confidence_utils import ConfidenceMethodConfig, ConfidenceMethodMixin from nemo.core.classes import Typing, typecheck from nemo.core.neural_types import HypothesisType, LengthsType, LogprobsType, NeuralType from nemo.utils import logging @@ -55,7 +55,7 @@ def _states_to_device(dec_state, device='cpu'): return dec_state -class GreedyCTCInfer(Typing, ConfidenceMeasureMixin): +class GreedyCTCInfer(Typing, ConfidenceMethodMixin): """A greedy CTC decoder. Provides a common abstraction for sample level and batch level greedy decoding. @@ -71,15 +71,15 @@ class GreedyCTCInfer(Typing, ConfidenceMeasureMixin): preserve_frame_confidence: Bool flag which preserves the history of per-frame confidence scores generated during decoding. When set to true, the Hypothesis will contain the non-null value for `frame_confidence` in it. Here, `frame_confidence` is a List of floats. - confidence_measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + confidence_method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. - entropy_type: Which type of entropy to use (str). Used if confidence_measure_cfg.name is set to `entropy`. + entropy_type: Which type of entropy to use (str). Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -130,7 +130,7 @@ def __init__( preserve_alignments: bool = False, compute_timestamps: bool = False, preserve_frame_confidence: bool = False, - confidence_measure_cfg: Optional[DictConfig] = None, + confidence_method_cfg: Optional[DictConfig] = None, ): super().__init__() @@ -140,8 +140,8 @@ def __init__( self.compute_timestamps = compute_timestamps | preserve_frame_confidence self.preserve_frame_confidence = preserve_frame_confidence - # set confidence calculation measure - self._init_confidence_measure(confidence_measure_cfg) + # set confidence calculation method + self._init_confidence_method(confidence_method_cfg) @typecheck() def forward( @@ -253,27 +253,12 @@ class GreedyCTCInferConfig: preserve_alignments: bool = False compute_timestamps: bool = False preserve_frame_confidence: bool = False - confidence_measure_cfg: Optional[ConfidenceMeasureConfig] = ConfidenceMeasureConfig() - confidence_method_cfg: str = "DEPRECATED" + confidence_method_cfg: Optional[ConfidenceMethodConfig] = ConfidenceMethodConfig() def __post_init__(self): # OmegaConf.structured ensures that post_init check is always executed - self.confidence_measure_cfg = OmegaConf.structured( - self.confidence_measure_cfg - if isinstance(self.confidence_measure_cfg, ConfidenceMeasureConfig) - else ConfidenceMeasureConfig(**self.confidence_measure_cfg) + self.confidence_method_cfg = OmegaConf.structured( + self.confidence_method_cfg + if isinstance(self.confidence_method_cfg, ConfidenceMethodConfig) + else ConfidenceMethodConfig(**self.confidence_method_cfg) ) - if self.confidence_method_cfg != "DEPRECATED": - logging.warning( - "`confidence_method_cfg` is deprecated and will be removed in the future. " - "Please use `confidence_measure_cfg` instead." - ) - - # TODO (alaptev): delete the following two lines sometime in the future - logging.warning("Re-writing `confidence_measure_cfg` with the value of `confidence_method_cfg`.") - # OmegaConf.structured ensures that post_init check is always executed - self.confidence_measure_cfg = OmegaConf.structured( - self.confidence_method_cfg - if isinstance(self.confidence_method_cfg, ConfidenceMeasureConfig) - else ConfidenceMeasureConfig(**self.confidence_method_cfg) - ) diff --git a/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py b/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py index dfa3ac27854b..185a3abf1151 100644 --- a/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py +++ b/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py @@ -35,7 +35,7 @@ from nemo.collections.asr.modules import rnnt_abstract from nemo.collections.asr.parts.utils import rnnt_utils -from nemo.collections.asr.parts.utils.asr_confidence_utils import ConfidenceMeasureConfig, ConfidenceMeasureMixin +from nemo.collections.asr.parts.utils.asr_confidence_utils import ConfidenceMethodConfig, ConfidenceMethodMixin from nemo.collections.common.parts.rnn import label_collate from nemo.core.classes import Typing, typecheck from nemo.core.neural_types import AcousticEncodedRepresentation, ElementType, HypothesisType, LengthsType, NeuralType @@ -69,7 +69,7 @@ def _states_to_device(dec_state, device='cpu'): return dec_state -class _GreedyRNNTInfer(Typing, ConfidenceMeasureMixin): +class _GreedyRNNTInfer(Typing, ConfidenceMethodMixin): """A greedy transducer decoder. Provides a common abstraction for sample level and batch level greedy decoding. @@ -96,15 +96,15 @@ class _GreedyRNNTInfer(Typing, ConfidenceMeasureMixin): The length of the list corresponds to the Acoustic Length (T). Each value in the list (Ti) is a torch.Tensor (U), representing 1 or more confidence scores. U is the number of target tokens for the current timestep Ti. - confidence_measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + confidence_method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. - entropy_type: Which type of entropy to use (str). Used if confidence_measure_cfg.name is set to `entropy`. + entropy_type: Which type of entropy to use (str). Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -154,7 +154,7 @@ def __init__( max_symbols_per_step: Optional[int] = None, preserve_alignments: bool = False, preserve_frame_confidence: bool = False, - confidence_measure_cfg: Optional[DictConfig] = None, + confidence_method_cfg: Optional[DictConfig] = None, ): super().__init__() self.decoder = decoder_model @@ -166,8 +166,8 @@ def __init__( self.preserve_alignments = preserve_alignments self.preserve_frame_confidence = preserve_frame_confidence - # set confidence calculation measure - self._init_confidence_measure(confidence_measure_cfg) + # set confidence calculation method + self._init_confidence_method(confidence_method_cfg) def __call__(self, *args, **kwargs): return self.forward(*args, **kwargs) @@ -263,15 +263,15 @@ class GreedyRNNTInfer(_GreedyRNNTInfer): The length of the list corresponds to the Acoustic Length (T). Each value in the list (Ti) is a torch.Tensor (U), representing 1 or more confidence scores. U is the number of target tokens for the current timestep Ti. - confidence_measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + confidence_method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. - entropy_type: Which type of entropy to use (str). Used if confidence_measure_cfg.name is set to `entropy`. + entropy_type: Which type of entropy to use (str). Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -305,7 +305,7 @@ def __init__( max_symbols_per_step: Optional[int] = None, preserve_alignments: bool = False, preserve_frame_confidence: bool = False, - confidence_measure_cfg: Optional[DictConfig] = None, + confidence_method_cfg: Optional[DictConfig] = None, ): super().__init__( decoder_model=decoder_model, @@ -314,7 +314,7 @@ def __init__( max_symbols_per_step=max_symbols_per_step, preserve_alignments=preserve_alignments, preserve_frame_confidence=preserve_frame_confidence, - confidence_measure_cfg=confidence_measure_cfg, + confidence_method_cfg=confidence_method_cfg, ) @typecheck() @@ -502,15 +502,15 @@ class GreedyBatchedRNNTInfer(_GreedyRNNTInfer): The length of the list corresponds to the Acoustic Length (T). Each value in the list (Ti) is a torch.Tensor (U), representing 1 or more confidence scores. U is the number of target tokens for the current timestep Ti. - confidence_measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + confidence_method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. - entropy_type: Which type of entropy to use (str). Used if confidence_measure_cfg.name is set to `entropy`. + entropy_type: Which type of entropy to use (str). Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -544,7 +544,7 @@ def __init__( max_symbols_per_step: Optional[int] = None, preserve_alignments: bool = False, preserve_frame_confidence: bool = False, - confidence_measure_cfg: Optional[DictConfig] = None, + confidence_method_cfg: Optional[DictConfig] = None, ): super().__init__( decoder_model=decoder_model, @@ -553,7 +553,7 @@ def __init__( max_symbols_per_step=max_symbols_per_step, preserve_alignments=preserve_alignments, preserve_frame_confidence=preserve_frame_confidence, - confidence_measure_cfg=confidence_measure_cfg, + confidence_method_cfg=confidence_method_cfg, ) # Depending on availability of `blank_as_pad` support @@ -1478,15 +1478,15 @@ class GreedyMultiblankRNNTInfer(GreedyRNNTInfer): The length of the list corresponds to the Acoustic Length (T). Each value in the list (Ti) is a torch.Tensor (U), representing 1 or more confidence scores. U is the number of target tokens for the current timestep Ti. - confidence_measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + confidence_method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. - entropy_type: Which type of entropy to use (str). Used if confidence_measure_cfg.name is set to `entropy`. + entropy_type: Which type of entropy to use (str). Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -1521,7 +1521,7 @@ def __init__( max_symbols_per_step: Optional[int] = None, preserve_alignments: bool = False, preserve_frame_confidence: bool = False, - confidence_measure_cfg: Optional[DictConfig] = None, + confidence_method_cfg: Optional[DictConfig] = None, ): super().__init__( decoder_model=decoder_model, @@ -1530,7 +1530,7 @@ def __init__( max_symbols_per_step=max_symbols_per_step, preserve_alignments=preserve_alignments, preserve_frame_confidence=preserve_frame_confidence, - confidence_measure_cfg=confidence_measure_cfg, + confidence_method_cfg=confidence_method_cfg, ) self.big_blank_durations = big_blank_durations self._SOS = blank_index - len(big_blank_durations) @@ -1682,15 +1682,15 @@ class GreedyBatchedMultiblankRNNTInfer(GreedyBatchedRNNTInfer): The length of the list corresponds to the Acoustic Length (T). Each value in the list (Ti) is a torch.Tensor (U), representing 1 or more confidence scores. U is the number of target tokens for the current timestep Ti. - confidence_measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + confidence_method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. - entropy_type: Which type of entropy to use (str). Used if confidence_measure_cfg.name is set to `entropy`. + entropy_type: Which type of entropy to use (str). Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -1725,7 +1725,7 @@ def __init__( max_symbols_per_step: Optional[int] = None, preserve_alignments: bool = False, preserve_frame_confidence: bool = False, - confidence_measure_cfg: Optional[DictConfig] = None, + confidence_method_cfg: Optional[DictConfig] = None, ): super().__init__( decoder_model=decoder_model, @@ -1734,7 +1734,7 @@ def __init__( max_symbols_per_step=max_symbols_per_step, preserve_alignments=preserve_alignments, preserve_frame_confidence=preserve_frame_confidence, - confidence_measure_cfg=confidence_measure_cfg, + confidence_method_cfg=confidence_method_cfg, ) self.big_blank_durations = big_blank_durations @@ -2203,31 +2203,15 @@ class GreedyRNNTInferConfig: max_symbols_per_step: Optional[int] = 10 preserve_alignments: bool = False preserve_frame_confidence: bool = False - confidence_measure_cfg: Optional[ConfidenceMeasureConfig] = ConfidenceMeasureConfig() - confidence_method_cfg: str = "DEPRECATED" + confidence_method_cfg: Optional[ConfidenceMethodConfig] = ConfidenceMethodConfig() def __post_init__(self): # OmegaConf.structured ensures that post_init check is always executed - self.confidence_measure_cfg = OmegaConf.structured( - self.confidence_measure_cfg - if isinstance(self.confidence_measure_cfg, ConfidenceMeasureConfig) - else ConfidenceMeasureConfig(**self.confidence_measure_cfg) + self.confidence_method_cfg = OmegaConf.structured( + self.confidence_method_cfg + if isinstance(self.confidence_method_cfg, ConfidenceMethodConfig) + else ConfidenceMethodConfig(**self.confidence_method_cfg) ) - if self.confidence_method_cfg != "DEPRECATED": - logging.warning( - "`confidence_method_cfg` is deprecated and will be removed in the future. " - "Please use `confidence_measure_cfg` instead." - ) - - # TODO (alaptev): delete the following two lines sometime in the future - logging.warning("Re-writing `confidence_measure_cfg` with the value of `confidence_method_cfg`.") - # OmegaConf.structured ensures that post_init check is always executed - self.confidence_measure_cfg = OmegaConf.structured( - self.confidence_method_cfg - if isinstance(self.confidence_method_cfg, ConfidenceMeasureConfig) - else ConfidenceMeasureConfig(**self.confidence_method_cfg) - ) - self.confidence_method_cfg = "DEPRECATED" @dataclass @@ -2235,31 +2219,15 @@ class GreedyBatchedRNNTInferConfig: max_symbols_per_step: Optional[int] = 10 preserve_alignments: bool = False preserve_frame_confidence: bool = False - confidence_measure_cfg: Optional[ConfidenceMeasureConfig] = ConfidenceMeasureConfig() - confidence_method_cfg: str = "DEPRECATED" + confidence_method_cfg: Optional[ConfidenceMethodConfig] = ConfidenceMethodConfig() def __post_init__(self): # OmegaConf.structured ensures that post_init check is always executed - self.confidence_measure_cfg = OmegaConf.structured( - self.confidence_measure_cfg - if isinstance(self.confidence_measure_cfg, ConfidenceMeasureConfig) - else ConfidenceMeasureConfig(**self.confidence_measure_cfg) + self.confidence_method_cfg = OmegaConf.structured( + self.confidence_method_cfg + if isinstance(self.confidence_method_cfg, ConfidenceMethodConfig) + else ConfidenceMethodConfig(**self.confidence_method_cfg) ) - if self.confidence_method_cfg != "DEPRECATED": - logging.warning( - "`confidence_method_cfg` is deprecated and will be removed in the future. " - "Please use `confidence_measure_cfg` instead." - ) - - # TODO (alaptev): delete the following two lines sometime in the future - logging.warning("Re-writing `confidence_measure_cfg` with the value of `confidence_method_cfg`.") - # OmegaConf.structured ensures that post_init check is always executed - self.confidence_measure_cfg = OmegaConf.structured( - self.confidence_method_cfg - if isinstance(self.confidence_method_cfg, ConfidenceMeasureConfig) - else ConfidenceMeasureConfig(**self.confidence_method_cfg) - ) - self.confidence_method_cfg = "DEPRECATED" class GreedyTDTInfer(_GreedyRNNTInfer): @@ -2288,15 +2256,15 @@ class GreedyTDTInfer(_GreedyRNNTInfer): The length of the list corresponds to the Acoustic Length (T). Each value in the list (Ti) is a torch.Tensor (U), representing 1 or more confidence scores. U is the number of target tokens for the current timestep Ti. - confidence_measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + confidence_method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. - entropy_type: Which type of entropy to use (str). Used if confidence_measure_cfg.name is set to `entropy`. + entropy_type: Which type of entropy to use (str). Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -2331,7 +2299,7 @@ def __init__( max_symbols_per_step: Optional[int] = None, preserve_alignments: bool = False, preserve_frame_confidence: bool = False, - confidence_measure_cfg: Optional[DictConfig] = None, + confidence_method_cfg: Optional[DictConfig] = None, ): super().__init__( decoder_model=decoder_model, @@ -2340,7 +2308,7 @@ def __init__( max_symbols_per_step=max_symbols_per_step, preserve_alignments=preserve_alignments, preserve_frame_confidence=preserve_frame_confidence, - confidence_measure_cfg=confidence_measure_cfg, + confidence_method_cfg=confidence_method_cfg, ) self.durations = durations @@ -2544,15 +2512,15 @@ class GreedyBatchedTDTInfer(_GreedyRNNTInfer): The length of the list corresponds to the Acoustic Length (T). Each value in the list (Ti) is a torch.Tensor (U), representing 1 or more confidence scores. U is the number of target tokens for the current timestep Ti. - confidence_measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + confidence_method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. - entropy_type: Which type of entropy to use (str). Used if confidence_measure_cfg.name is set to `entropy`. + entropy_type: Which type of entropy to use (str). Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -2587,7 +2555,7 @@ def __init__( max_symbols_per_step: Optional[int] = None, preserve_alignments: bool = False, preserve_frame_confidence: bool = False, - confidence_measure_cfg: Optional[DictConfig] = None, + confidence_method_cfg: Optional[DictConfig] = None, ): super().__init__( decoder_model=decoder_model, @@ -2596,7 +2564,7 @@ def __init__( max_symbols_per_step=max_symbols_per_step, preserve_alignments=preserve_alignments, preserve_frame_confidence=preserve_frame_confidence, - confidence_measure_cfg=confidence_measure_cfg, + confidence_method_cfg=confidence_method_cfg, ) self.durations = durations diff --git a/nemo/collections/asr/parts/utils/asr_confidence_benchmarking_utils.py b/nemo/collections/asr/parts/utils/asr_confidence_benchmarking_utils.py index 958195a4bb11..0e057e012542 100644 --- a/nemo/collections/asr/parts/utils/asr_confidence_benchmarking_utils.py +++ b/nemo/collections/asr/parts/utils/asr_confidence_benchmarking_utils.py @@ -173,11 +173,11 @@ def apply_confidence_parameters(decoding_cfg, hp): """ new_decoding_cfg = copy.deepcopy(decoding_cfg) confidence_cfg_fields = ("aggregation", "exclude_blank") - confidence_measure_cfg_fields = ("name", "alpha", "entropy_type", "entropy_norm") + confidence_method_cfg_fields = ("name", "alpha", "entropy_type", "entropy_norm") with open_dict(new_decoding_cfg): for p, v in hp.items(): if p in confidence_cfg_fields: new_decoding_cfg.confidence_cfg[p] = v - elif p in confidence_measure_cfg_fields: - new_decoding_cfg.confidence_cfg.measure_cfg[p] = v + elif p in confidence_method_cfg_fields: + new_decoding_cfg.confidence_cfg.method_cfg[p] = v return new_decoding_cfg diff --git a/nemo/collections/asr/parts/utils/asr_confidence_utils.py b/nemo/collections/asr/parts/utils/asr_confidence_utils.py index 29c49529a509..ddfac3744c6a 100644 --- a/nemo/collections/asr/parts/utils/asr_confidence_utils.py +++ b/nemo/collections/asr/parts/utils/asr_confidence_utils.py @@ -25,7 +25,7 @@ from nemo.utils import logging -class ConfidenceMeasureConstants: +class ConfidenceMethodConstants: NAMES = ("max_prob", "entropy") ENTROPY_TYPES = ("gibbs", "tsallis", "renyi") ENTROPY_NORMS = ("lin", "exp") @@ -48,17 +48,17 @@ def print(cls): @dataclass -class ConfidenceMeasureConfig: - """A Config which contains the measure name and settings to compute per-frame confidence scores. +class ConfidenceMethodConfig: + """A Config which contains the method name and settings to compute per-frame confidence scores. Args: - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. entropy_type: Which type of entropy to use (str). - Used if confidence_measure_cfg.name is set to `entropy`. + Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -92,31 +92,25 @@ class ConfidenceMeasureConfig: def __post_init__(self): if self.temperature != "DEPRECATED": - logging.warning( - "`temperature` is deprecated and will be removed in the future. Please use `alpha` instead." - ) - - # TODO (alaptev): delete the following two lines sometime in the future - logging.warning("Re-writing `alpha` with the value of `temperature`.") # self.temperature has type str self.alpha = float(self.temperature) self.temperature = "DEPRECATED" - if self.name not in ConfidenceMeasureConstants.NAMES: + if self.name not in ConfidenceMethodConstants.NAMES: raise ValueError( f"`name` must be one of the following: " - f"{'`' + '`, `'.join(ConfidenceMeasureConstants.NAMES) + '`'}. Provided: `{self.name}`" + f"{'`' + '`, `'.join(ConfidenceMethodConstants.NAMES) + '`'}. Provided: `{self.name}`" ) - if self.entropy_type not in ConfidenceMeasureConstants.ENTROPY_TYPES: + if self.entropy_type not in ConfidenceMethodConstants.ENTROPY_TYPES: raise ValueError( f"`entropy_type` must be one of the following: " - f"{'`' + '`, `'.join(ConfidenceMeasureConstants.ENTROPY_TYPES) + '`'}. Provided: `{self.entropy_type}`" + f"{'`' + '`, `'.join(ConfidenceMethodConstants.ENTROPY_TYPES) + '`'}. Provided: `{self.entropy_type}`" ) if self.alpha <= 0.0: raise ValueError(f"`alpha` must be > 0. Provided: {self.alpha}") - if self.entropy_norm not in ConfidenceMeasureConstants.ENTROPY_NORMS: + if self.entropy_norm not in ConfidenceMethodConstants.ENTROPY_NORMS: raise ValueError( f"`entropy_norm` must be one of the following: " - f"{'`' + '`, `'.join(ConfidenceMeasureConstants.ENTROPY_NORMS) + '`'}. Provided: `{self.entropy_norm}`" + f"{'`' + '`, `'.join(ConfidenceMethodConstants.ENTROPY_NORMS) + '`'}. Provided: `{self.entropy_norm}`" ) @@ -142,15 +136,15 @@ class ConfidenceConfig: from the `token_confidence`. aggregation: Which aggregation type to use for collapsing per-token confidence into per-word confidence. Valid options are `mean`, `min`, `max`, `prod`. - measure_cfg: A dict-like object which contains the measure name and settings to compute per-frame + method_cfg: A dict-like object which contains the method name and settings to compute per-frame confidence scores. - name: The measure name (str). + name: The method name (str). Supported values: - 'max_prob' for using the maximum token probability as a confidence. - 'entropy' for using a normalized entropy of a log-likelihood vector. - entropy_type: Which type of entropy to use (str). Used if confidence_measure_cfg.name is set to `entropy`. + entropy_type: Which type of entropy to use (str). Used if confidence_method_cfg.name is set to `entropy`. Supported values: - 'gibbs' for the (standard) Gibbs entropy. If the alpha (α) is provided, the formula is the following: H_α = -sum_i((p^α_i)*log(p^α_i)). @@ -181,34 +175,19 @@ class ConfidenceConfig: preserve_word_confidence: bool = False exclude_blank: bool = True aggregation: str = "min" - measure_cfg: ConfidenceMeasureConfig = ConfidenceMeasureConfig() - method_cfg: str = "DEPRECATED" + method_cfg: ConfidenceMethodConfig = ConfidenceMethodConfig() def __post_init__(self): # OmegaConf.structured ensures that post_init check is always executed - self.measure_cfg = OmegaConf.structured( - self.measure_cfg - if isinstance(self.measure_cfg, ConfidenceMeasureConfig) - else ConfidenceMeasureConfig(**self.measure_cfg) + self.method_cfg = OmegaConf.structured( + self.method_cfg + if isinstance(self.method_cfg, ConfidenceMethodConfig) + else ConfidenceMethodConfig(**self.method_cfg) ) - if self.method_cfg != "DEPRECATED": - logging.warning( - "`method_cfg` is deprecated and will be removed in the future. Please use `measure_cfg` instead." - ) - - # TODO (alaptev): delete the following two lines sometime in the future - logging.warning("Re-writing `measure_cfg` with the value of `method_cfg`.") - # OmegaConf.structured ensures that post_init check is always executed - self.measure_cfg = OmegaConf.structured( - self.method_cfg - if isinstance(self.method_cfg, ConfidenceMeasureConfig) - else ConfidenceMeasureConfig(**self.method_cfg) - ) - self.method_cfg = "DEPRECATED" if self.aggregation not in ConfidenceConstants.AGGREGATIONS: raise ValueError( f"`aggregation` has to be one of the following: " - f"{'`' + '`, `'.join(ConfidenceMeasureConstants.AGGREGATIONS) + '`'}. Provided: `{self.aggregation}`" + f"{'`' + '`, `'.join(ConfidenceConstants.AGGREGATIONS) + '`'}. Provided: `{self.aggregation}`" ) @@ -284,7 +263,7 @@ def entropy_gibbs_exp(x, v, t): def get_confidence_aggregation_bank(): """Generate a dictionary with confidence aggregation functions. - Supported confidence measures: + Supported confidence aggregation functions: min: minimum max: maximum mean: arithmetic mean @@ -305,26 +284,26 @@ def get_confidence_aggregation_bank(): return confidence_aggregation_bank -class ConfidenceMeasureMixin(ABC): - """Confidence Measure Mixin class. +class ConfidenceMethodMixin(ABC): + """Confidence Method Mixin class. - It initializes per-frame confidence measure. + It initializes per-frame confidence method. """ - def _init_confidence_measure(self, confidence_measure_cfg: Optional[DictConfig] = None): - """Initialize per-frame confidence measure from config. + def _init_confidence_method(self, confidence_method_cfg: Optional[DictConfig] = None): + """Initialize per-frame confidence method from config. """ # OmegaConf.structured ensures that post_init check is always executed - confidence_measure_cfg = OmegaConf.structured( - ConfidenceMeasureConfig() - if confidence_measure_cfg is None - else ConfidenceMeasureConfig(**confidence_measure_cfg) + confidence_method_cfg = OmegaConf.structured( + ConfidenceMethodConfig() + if confidence_method_cfg is None + else ConfidenceMethodConfig(**confidence_method_cfg) ) - # set confidence calculation measure + # set confidence calculation method # we suppose that self.blank_id == len(vocabulary) self.num_tokens = (self.blank_id if hasattr(self, "blank_id") else self._blank_index) + 1 - self.alpha = confidence_measure_cfg.alpha + self.alpha = confidence_method_cfg.alpha # init confidence measure bank self.confidence_measure_bank = get_confidence_measure_bank() @@ -332,14 +311,14 @@ def _init_confidence_measure(self, confidence_measure_cfg: Optional[DictConfig] measure = None # construct measure_name measure_name = "" - if confidence_measure_cfg.name == "max_prob": + if confidence_method_cfg.name == "max_prob": measure_name = "max_prob" - elif confidence_measure_cfg.name == "entropy": + elif confidence_method_cfg.name == "entropy": measure_name = '_'.join( - [confidence_measure_cfg.name, confidence_measure_cfg.entropy_type, confidence_measure_cfg.entropy_norm] + [confidence_method_cfg.name, confidence_method_cfg.entropy_type, confidence_method_cfg.entropy_norm] ) else: - raise ValueError(f"Unsupported `confidence_measure_cfg.name`: `{confidence_measure_cfg.name}`") + raise ValueError(f"Unsupported `confidence_method_cfg.name`: `{confidence_method_cfg.name}`") if measure_name not in self.confidence_measure_bank: raise ValueError(f"Unsupported measure setup: `{measure_name}`") measure = partial(self.confidence_measure_bank[measure_name], v=self.num_tokens, t=self.alpha) @@ -359,7 +338,7 @@ def _init_confidence(self, confidence_cfg: Optional[DictConfig] = None): confidence_cfg = OmegaConf.structured( ConfidenceConfig() if confidence_cfg is None else ConfidenceConfig(**confidence_cfg) ) - self.confidence_measure_cfg = confidence_cfg.measure_cfg + self.confidence_method_cfg = confidence_cfg.method_cfg # extract the config self.preserve_word_confidence = confidence_cfg.get('preserve_word_confidence', False) @@ -384,11 +363,11 @@ def _init_confidence(self, confidence_cfg: Optional[DictConfig] = None): if self.cfg.strategy in ['greedy', 'greedy_batch']: self.preserve_frame_confidence = self.cfg.greedy.get('preserve_frame_confidence', False) # OmegaConf.structured ensures that post_init check is always executed - confidence_measure_cfg = OmegaConf.structured(self.cfg.greedy).get('confidence_measure_cfg', None) - self.confidence_measure_cfg = ( - OmegaConf.structured(ConfidenceMeasureConfig()) - if confidence_measure_cfg is None - else OmegaConf.structured(ConfidenceMeasureConfig(**confidence_measure_cfg)) + confidence_method_cfg = OmegaConf.structured(self.cfg.greedy).get('confidence_method_cfg', None) + self.confidence_method_cfg = ( + OmegaConf.structured(ConfidenceMethodConfig()) + if confidence_method_cfg is None + else OmegaConf.structured(ConfidenceMethodConfig(**confidence_method_cfg)) ) @abstractmethod diff --git a/scripts/confidence_ensembles/build_ensemble.py b/scripts/confidence_ensembles/build_ensemble.py index bc32a4f99840..99bfa6187b30 100644 --- a/scripts/confidence_ensembles/build_ensemble.py +++ b/scripts/confidence_ensembles/build_ensemble.py @@ -97,7 +97,7 @@ ) from nemo.collections.asr.parts.utils.asr_confidence_utils import ( ConfidenceConfig, - ConfidenceMeasureConfig, + ConfidenceMethodConfig, get_confidence_aggregation_bank, get_confidence_measure_bank, ) @@ -214,7 +214,7 @@ class BuildEnsembleConfig: preserve_frame_confidence=True, exclude_blank=True, aggregation="mean", - measure_cfg=ConfidenceMeasureConfig(name="entropy", entropy_type="renyi", alpha=0.25, entropy_norm="lin",), + method_cfg=ConfidenceMethodConfig(name="entropy", entropy_type="renyi", alpha=0.25, entropy_norm="lin",), ) temperature: float = 1.0 diff --git a/scripts/confidence_ensembles/ensemble_config.yaml b/scripts/confidence_ensembles/ensemble_config.yaml index 590318ee3b28..8184d4d5acb5 100644 --- a/scripts/confidence_ensembles/ensemble_config.yaml +++ b/scripts/confidence_ensembles/ensemble_config.yaml @@ -16,7 +16,7 @@ temperature: 1.0 confidence: exclude_blank: True aggregation: mean - measure_cfg: + method_cfg: name: entropy entropy_type: renyi alpha: 0.25 diff --git a/scripts/speech_recognition/confidence/benchmark_asr_confidence.py b/scripts/speech_recognition/confidence/benchmark_asr_confidence.py index 8922fe09176d..246aa61c2c0e 100644 --- a/scripts/speech_recognition/confidence/benchmark_asr_confidence.py +++ b/scripts/speech_recognition/confidence/benchmark_asr_confidence.py @@ -83,11 +83,11 @@ def get_experiment_params(cfg): """ blank = "no_blank" if cfg.exclude_blank else "blank" aggregation = cfg.aggregation - method_name = cfg.measure_cfg.name - alpha = cfg.measure_cfg.alpha + method_name = cfg.method_cfg.name + alpha = cfg.method_cfg.alpha if method_name == "entropy": - entropy_type = cfg.measure_cfg.entropy_type - entropy_norm = cfg.measure_cfg.entropy_norm + entropy_type = cfg.method_cfg.entropy_type + entropy_norm = cfg.method_cfg.entropy_norm experiment_param_list = [ aggregation, str(cfg.exclude_blank), diff --git a/tests/collections/asr/confidence/test_asr_confidence.py b/tests/collections/asr/confidence/test_asr_confidence.py index 11b127424908..e95a0bd8127b 100644 --- a/tests/collections/asr/confidence/test_asr_confidence.py +++ b/tests/collections/asr/confidence/test_asr_confidence.py @@ -106,32 +106,21 @@ def test_run_confidence_benchmark( @pytest.mark.integration @pytest.mark.with_downloads @pytest.mark.parametrize('model_name', ("ctc", "rnnt")) - @pytest.mark.parametrize('arg', ("method_cfg", "temperature", "all")) - def test_deprecated_config_args(self, model_name, arg, conformer_ctc_bpe_model, conformer_rnnt_bpe_model): - assert ConfidenceConfig().measure_cfg.alpha == 0.33, "default `alpha` is supposed to be 0.33" + def test_deprecated_config_args(self, model_name, conformer_ctc_bpe_model, conformer_rnnt_bpe_model): + assert ConfidenceConfig().method_cfg.alpha == 0.33, "default `alpha` is supposed to be 0.33" model = conformer_ctc_bpe_model if model_name == "ctc" else conformer_rnnt_bpe_model assert isinstance(model, ASRModel) - if arg == "all": - conf = OmegaConf.create({"temperature": 0.5}) - test_args_main = {"method_cfg": conf} - test_args_greedy = {"confidence_method_cfg": conf} - elif arg == "method_cfg": - conf = OmegaConf.create({"alpha": 0.5}) - test_args_main = {"method_cfg": conf} - test_args_greedy = {"confidence_method_cfg": conf} - elif arg == "temperature": - conf = OmegaConf.create({"temperature": 0.5}) - test_args_main = {"measure_cfg": conf} - test_args_greedy = {"confidence_measure_cfg": conf} - else: - raise NotImplementedError(arg) + + conf = OmegaConf.create({"temperature": 0.5}) + test_args_main = {"method_cfg": conf} + test_args_greedy = {"confidence_method_cfg": conf} confidence_cfg = ConfidenceConfig(preserve_word_confidence=True, **test_args_main) model.change_decoding_strategy( RNNTDecodingConfig(fused_batch_size=-1, strategy="greedy", confidence_cfg=confidence_cfg) if model_name == "rnnt" else CTCDecodingConfig(confidence_cfg=confidence_cfg) ) - assert model.cfg.decoding.confidence_cfg.measure_cfg.alpha == 0.5 + assert model.cfg.decoding.confidence_cfg.method_cfg.alpha == 0.5 model.change_decoding_strategy( RNNTDecodingConfig( fused_batch_size=-1, @@ -141,4 +130,4 @@ def test_deprecated_config_args(self, model_name, arg, conformer_ctc_bpe_model, if model_name == "rnnt" else CTCDecodingConfig(greedy=GreedyCTCInferConfig(preserve_frame_confidence=True, **test_args_greedy)) ) - assert model.cfg.decoding.greedy.confidence_measure_cfg.alpha == 0.5 + assert model.cfg.decoding.greedy.confidence_method_cfg.alpha == 0.5 diff --git a/tests/collections/asr/test_asr_hybrid_rnnt_ctc_model_char.py b/tests/collections/asr/test_asr_hybrid_rnnt_ctc_model_char.py index 8687ed683833..22926b6516ee 100644 --- a/tests/collections/asr/test_asr_hybrid_rnnt_ctc_model_char.py +++ b/tests/collections/asr/test_asr_hybrid_rnnt_ctc_model_char.py @@ -242,8 +242,7 @@ def test_decoding_change(self, hybrid_asr_model): @pytest.mark.unit def test_GreedyRNNTInferConfig(self): - # confidence_method_cfg is deprecated - IGNORE_ARGS = ['decoder_model', 'joint_model', 'blank_index', 'confidence_method_cfg'] + IGNORE_ARGS = ['decoder_model', 'joint_model', 'blank_index'] result = assert_dataclass_signature_match( greedy_decode.GreedyRNNTInfer, greedy_decode.GreedyRNNTInferConfig, ignore_args=IGNORE_ARGS @@ -257,8 +256,7 @@ def test_GreedyRNNTInferConfig(self): @pytest.mark.unit def test_GreedyBatchedRNNTInferConfig(self): - # confidence_method_cfg is deprecated - IGNORE_ARGS = ['decoder_model', 'joint_model', 'blank_index', 'confidence_method_cfg'] + IGNORE_ARGS = ['decoder_model', 'joint_model', 'blank_index'] result = assert_dataclass_signature_match( greedy_decode.GreedyBatchedRNNTInfer, greedy_decode.GreedyBatchedRNNTInferConfig, ignore_args=IGNORE_ARGS diff --git a/tests/collections/asr/test_asr_rnnt_encdec_model.py b/tests/collections/asr/test_asr_rnnt_encdec_model.py index 775a146c74c4..68f1e38f797b 100644 --- a/tests/collections/asr/test_asr_rnnt_encdec_model.py +++ b/tests/collections/asr/test_asr_rnnt_encdec_model.py @@ -242,8 +242,7 @@ def test_decoding_change(self, asr_model): @pytest.mark.unit def test_GreedyRNNTInferConfig(self): - # confidence_method_cfg is deprecated - IGNORE_ARGS = ['decoder_model', 'joint_model', 'blank_index', 'confidence_method_cfg'] + IGNORE_ARGS = ['decoder_model', 'joint_model', 'blank_index'] result = assert_dataclass_signature_match( greedy_decode.GreedyRNNTInfer, greedy_decode.GreedyRNNTInferConfig, ignore_args=IGNORE_ARGS @@ -257,8 +256,7 @@ def test_GreedyRNNTInferConfig(self): @pytest.mark.unit def test_GreedyBatchedRNNTInferConfig(self): - # confidence_method_cfg is deprecated - IGNORE_ARGS = ['decoder_model', 'joint_model', 'blank_index', 'confidence_method_cfg'] + IGNORE_ARGS = ['decoder_model', 'joint_model', 'blank_index'] result = assert_dataclass_signature_match( greedy_decode.GreedyBatchedRNNTInfer, greedy_decode.GreedyBatchedRNNTInferConfig, ignore_args=IGNORE_ARGS diff --git a/tests/collections/asr/test_confidence_ensembles.py b/tests/collections/asr/test_confidence_ensembles.py index b8b027dd3426..e926475009e2 100644 --- a/tests/collections/asr/test_confidence_ensembles.py +++ b/tests/collections/asr/test_confidence_ensembles.py @@ -19,7 +19,7 @@ from nemo.collections.asr.metrics.wer import CTCDecodingConfig from nemo.collections.asr.models import EncDecCTCModel, EncDecHybridRNNTCTCModel, EncDecRNNTModel from nemo.collections.asr.models.confidence_ensemble import ConfidenceEnsembleModel -from nemo.collections.asr.parts.utils.asr_confidence_utils import ConfidenceConfig, ConfidenceMeasureConfig +from nemo.collections.asr.parts.utils.asr_confidence_utils import ConfidenceConfig, ConfidenceMethodConfig def get_model_config(model_class): @@ -117,7 +117,7 @@ def test_model_creation_2models(self, tmp_path, model_class0, model_class1): preserve_frame_confidence=True, exclude_blank=True, aggregation="mean", - measure_cfg=ConfidenceMeasureConfig(name="entropy", entropy_type="renyi", alpha=0.25, entropy_norm="lin",), + method_cfg=ConfidenceMethodConfig(name="entropy", entropy_type="renyi", alpha=0.25, entropy_norm="lin",), ) # just checking that no errors are raised when creating the model @@ -148,7 +148,7 @@ def test_model_creation_5models(self, tmp_path): preserve_frame_confidence=True, exclude_blank=True, aggregation="mean", - measure_cfg=ConfidenceMeasureConfig(name="entropy", entropy_type="renyi", alpha=0.25, entropy_norm="lin",), + method_cfg=ConfidenceMethodConfig(name="entropy", entropy_type="renyi", alpha=0.25, entropy_norm="lin",), ) # just checking that no errors are raised when creating the model diff --git a/tutorials/asr/ASR_Confidence_Estimation.ipynb b/tutorials/asr/ASR_Confidence_Estimation.ipynb index 2a1ad024a889..7a92ed026f07 100644 --- a/tutorials/asr/ASR_Confidence_Estimation.ipynb +++ b/tutorials/asr/ASR_Confidence_Estimation.ipynb @@ -443,8 +443,8 @@ "from nemo.collections.asr.parts.utils.asr_confidence_utils import (\n", " ConfidenceConfig,\n", " ConfidenceConstants,\n", - " ConfidenceMeasureConfig,\n", - " ConfidenceMeasureConstants,\n", + " ConfidenceMethodConfig,\n", + " ConfidenceMethodConstants,\n", ")\n", "from nemo.collections.asr.parts.utils.asr_confidence_benchmarking_utils import (\n", " apply_confidence_parameters,\n", @@ -454,11 +454,11 @@ ")\n", "\n", "\n", - "# List allowed options for ConfidenceMeasureConfig and ConfidenceConfig\n", - "print(f\"Allowed options for ConfidenceMeasureConfig: {ConfidenceMeasureConstants.print()}\\n\")\n", + "# List allowed options for ConfidenceMethodConfig and ConfidenceConfig\n", + "print(f\"Allowed options for ConfidenceMethodConfig: {ConfidenceMethodConstants.print()}\\n\")\n", "print(f\"Allowed options for ConfidenceConfig: {ConfidenceConstants.print()}\\n\")\n", "\n", - "# Initialize ConfidenceConfig and ConfidenceMeasureConfig\n", + "# Initialize ConfidenceConfig and ConfidenceMethodConfig\n", "confidence_cfg = ConfidenceConfig(\n", " preserve_frame_confidence=True, # Internally set to true if preserve_token_confidence == True\n", " # or preserve_word_confidence == True\n", @@ -466,7 +466,7 @@ " preserve_word_confidence=True,\n", " aggregation=\"prod\", # How to aggregate frame scores to token scores and token scores to word scores\n", " exclude_blank=False, # If true, only non-blank emissions contribute to confidence scores\n", - " measure_cfg=ConfidenceMeasureConfig( # Config for per-frame scores calculation (before aggregation)\n", + " method_cfg=ConfidenceMethodConfig( # Config for per-frame scores calculation (before aggregation)\n", " name=\"max_prob\", # Or \"entropy\" (default), which usually works better\n", " entropy_type=\"gibbs\", # Used only for name == \"entropy\". Recommended: \"tsallis\" (default) or \"renyi\"\n", " alpha=0.5, # Low values (<1) increase sensitivity, high values decrease sensitivity\n", @@ -1058,7 +1058,7 @@ " preserve_word_confidence=True,\n", " preserve_token_confidence=True,\n", " aggregation=\"min\",\n", - " measure_cfg=DictConfig({\"entropy_type\": \"tsallis\", \"alpha\": 1.5, \"entropy_norm\": \"lin\"}),\n", + " method_cfg=DictConfig({\"entropy_type\": \"tsallis\", \"alpha\": 1.5, \"entropy_norm\": \"lin\"}),\n", ")\n", "\n", "model.change_decoding_strategy(\n", From 3b45cbe77c6ade484e4e867f52624b20e1e3126a Mon Sep 17 00:00:00 2001 From: Robin Dong Date: Wed, 20 Sep 2023 01:01:14 +1000 Subject: [PATCH 036/112] Add steps for document of getting dataset 'SF Bilingual Speech' (#7378) * Add steps for document of getting dataset 'SF Bilingual Speech' Signed-off-by: Robin Dong * Update datasets.rst added a link from a tutorial demonstrating detailed data prep steps. Signed-off-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> --------- Signed-off-by: Robin Dong Signed-off-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Signed-off-by: Sasha Meister --- docs/source/tts/datasets.rst | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/source/tts/datasets.rst b/docs/source/tts/datasets.rst index b5317ce01f64..dabf50b30dae 100644 --- a/docs/source/tts/datasets.rst +++ b/docs/source/tts/datasets.rst @@ -172,18 +172,24 @@ SFSpeech Chinese/English Bilingual Speech ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Dataset URL: https://catalog.ngc.nvidia.com/orgs/nvidia/resources/sf_bilingual_speech_zh_en * Dataset Processing Script: https://github.com/NVIDIA/NeMo/tree/stable/scripts/dataset_processing/tts/sfbilingual/get_data.py -* Command Line Instruction: +* Command Line Instruction: please refer details in Section 1 (NGC Registry CLI installation), Section 2 (Downloading SFSpeech Dataset), and Section 3 (Creatiung Data Manifests) from https://github.com/NVIDIA/NeMo/blob/main/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb. Below code block briefly describes the steps. .. code-block:: bash + # [prerequisite] Install and setup 'ngc' cli tool by following document https://docs.ngc.nvidia.com/cli/cmd.html + + $ ngc registry resource download-version "nvidia/sf_bilingual_speech_zh_en:v1" + + $ unzip sf_bilingual_speech_zh_en_vv1/SF_bilingual.zip -d + $ python scripts/dataset_processing/tts/sfbilingual/get_data.py \ - --data-root \ - --val-size 0.1 \ - --test-size 0.2 \ + --data-root /SF_bilingual \ + --val-size 0.005 \ + --test-size 0.01 \ --seed-for-ds-split 100 $ python scripts/dataset_processing/tts/extract_sup_data.py \ --config-path sfbilingual/ds_conf \ --config-name ds_for_fastpitch_align.yaml \ manifest_filepath= \ - sup_data_path= \ No newline at end of file + sup_data_path= From b9ddba746c03dcd4b6ef0b999279376bf7a1ed1b Mon Sep 17 00:00:00 2001 From: Aleksandr Laptev Date: Tue, 19 Sep 2023 22:53:14 +0700 Subject: [PATCH 037/112] RNN-T confidence and alignment bugfix (#7381) * new frame_confidence and alignments lists are now always created after the while loop Signed-off-by: Aleksandr Laptev * tests added Signed-off-by: Aleksandr Laptev --------- Signed-off-by: Aleksandr Laptev Signed-off-by: Sasha Meister --- .../parts/submodules/rnnt_greedy_decoding.py | 278 ++++++++---------- .../asr/test_asr_rnnt_encdec_model.py | 238 ++++++++++++++- 2 files changed, 363 insertions(+), 153 deletions(-) diff --git a/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py b/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py index 185a3abf1151..95b0bdf5fd13 100644 --- a/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py +++ b/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py @@ -441,13 +441,6 @@ def _greedy_decode( # If blank token is predicted, exit inner loop, move onto next timestep t if k == self._blank_index: not_blank = False - - if self.preserve_alignments: - # convert Ti-th logits into a torch array - hypothesis.alignments.append([]) # blank buffer for next timestep - - if self.preserve_frame_confidence: - hypothesis.frame_confidence.append([]) # blank buffer for next timestep else: # Append token to label set, update RNN state. hypothesis.y_sequence.append(k) @@ -459,6 +452,13 @@ def _greedy_decode( # Increment token counter. symbols_added += 1 + if self.preserve_alignments: + # convert Ti-th logits into a torch array + hypothesis.alignments.append([]) # blank buffer for next timestep + + if self.preserve_frame_confidence: + hypothesis.frame_confidence.append([]) # blank buffer for next timestep + # Remove trailing empty list of Alignments if self.preserve_alignments: if len(hypothesis.alignments[-1]) == 0: @@ -642,9 +642,6 @@ def _greedy_decode_blank_as_pad( # frame_confidence is a 3-dimensional dangling list representing B x T x U for hyp in hypotheses: hyp.frame_confidence = [[]] - hyp.y_3best = [[]] - hyp.frame_confidence_3best = [[[]]] - hyp.logp = [[]] # Last Label buffer + Last Label without blank buffer # batch level equivalent of the last_label @@ -731,32 +728,6 @@ def _greedy_decode_blank_as_pad( # This is equivalent to if single sample predicted k if all_blanks: not_blank = False - - # If preserving alignments, convert the current Uj alignments into a torch.Tensor - # Then preserve U at current timestep Ti - # Finally, forward the timestep history to Ti+1 for that sample - # All of this should only be done iff the current time index <= sample-level AM length. - # Otherwise ignore and move to next sample / next timestep. - if self.preserve_alignments: - - # convert Ti-th logits into a torch array - for batch_idx in range(batchsize): - - # this checks if current timestep <= sample-level AM length - # If current timestep > sample-level AM length, no alignments will be added - # Therefore the list of Uj alignments is empty here. - if len(hypotheses[batch_idx].alignments[-1]) > 0: - hypotheses[batch_idx].alignments.append([]) # blank buffer for next timestep - - # Do the same if preserving per-frame confidence - if self.preserve_frame_confidence: - - for batch_idx in range(batchsize): - if len(hypotheses[batch_idx].frame_confidence[-1]) > 0: - hypotheses[batch_idx].frame_confidence.append([]) # blank buffer for next timestep - hypotheses[batch_idx].y_3best.append([]) - hypotheses[batch_idx].frame_confidence_3best.append([]) - hypotheses[batch_idx].logp.append([]) else: # Collect batch indices where blanks occurred now/past blank_indices = (blank_mask == 1).nonzero(as_tuple=False) @@ -791,6 +762,29 @@ def _greedy_decode_blank_as_pad( hypotheses[kidx].score += float(v[kidx]) symbols_added += 1 + # If preserving alignments, convert the current Uj alignments into a torch.Tensor + # Then preserve U at current timestep Ti + # Finally, forward the timestep history to Ti+1 for that sample + # All of this should only be done iff the current time index <= sample-level AM length. + # Otherwise ignore and move to next sample / next timestep. + if self.preserve_alignments: + + # convert Ti-th logits into a torch array + for batch_idx in range(batchsize): + + # this checks if current timestep <= sample-level AM length + # If current timestep > sample-level AM length, no alignments will be added + # Therefore the list of Uj alignments is empty here. + if len(hypotheses[batch_idx].alignments[-1]) > 0: + hypotheses[batch_idx].alignments.append([]) # blank buffer for next timestep + + # Do the same if preserving per-frame confidence + if self.preserve_frame_confidence: + + for batch_idx in range(batchsize): + if len(hypotheses[batch_idx].frame_confidence[-1]) > 0: + hypotheses[batch_idx].frame_confidence.append([]) # blank buffer for next timestep + # Remove trailing empty list of alignments at T_{am-len} x Uj if self.preserve_alignments: for batch_idx in range(batchsize): @@ -802,9 +796,6 @@ def _greedy_decode_blank_as_pad( for batch_idx in range(batchsize): if len(hypotheses[batch_idx].frame_confidence[-1]) == 0: del hypotheses[batch_idx].frame_confidence[-1] - del hypotheses[batch_idx].y_3best[-1] - del hypotheses[batch_idx].frame_confidence_3best[-1] - del hypotheses[batch_idx].logp[-1] # Preserve states for batch_idx in range(batchsize): @@ -946,29 +937,6 @@ def _greedy_decode_masked( # This is equivalent to if single sample predicted k if blank_mask.all(): not_blank = False - - # If preserving alignments, convert the current Uj alignments into a torch.Tensor - # Then preserve U at current timestep Ti - # Finally, forward the timestep history to Ti+1 for that sample - # All of this should only be done iff the current time index <= sample-level AM length. - # Otherwise ignore and move to next sample / next timestep. - if self.preserve_alignments: - - # convert Ti-th logits into a torch array - for batch_idx in range(batchsize): - - # this checks if current timestep <= sample-level AM length - # If current timestep > sample-level AM length, no alignments will be added - # Therefore the list of Uj alignments is empty here. - if len(hypotheses[batch_idx].alignments[-1]) > 0: - hypotheses[batch_idx].alignments.append([]) # blank buffer for next timestep - - # Do the same if preserving per-frame confidence - if self.preserve_frame_confidence: - - for batch_idx in range(batchsize): - if len(hypotheses[batch_idx].frame_confidence[-1]) > 0: - hypotheses[batch_idx].frame_confidence.append([]) # blank buffer for next timestep else: # Collect batch indices where blanks occurred now/past blank_indices = (blank_mask == 1).nonzero(as_tuple=False) @@ -1004,6 +972,29 @@ def _greedy_decode_masked( symbols_added += 1 + # If preserving alignments, convert the current Uj alignments into a torch.Tensor + # Then preserve U at current timestep Ti + # Finally, forward the timestep history to Ti+1 for that sample + # All of this should only be done iff the current time index <= sample-level AM length. + # Otherwise ignore and move to next sample / next timestep. + if self.preserve_alignments: + + # convert Ti-th logits into a torch array + for batch_idx in range(batchsize): + + # this checks if current timestep <= sample-level AM length + # If current timestep > sample-level AM length, no alignments will be added + # Therefore the list of Uj alignments is empty here. + if len(hypotheses[batch_idx].alignments[-1]) > 0: + hypotheses[batch_idx].alignments.append([]) # blank buffer for next timestep + + # Do the same if preserving per-frame confidence + if self.preserve_frame_confidence: + + for batch_idx in range(batchsize): + if len(hypotheses[batch_idx].frame_confidence[-1]) > 0: + hypotheses[batch_idx].frame_confidence.append([]) # blank buffer for next timestep + # Remove trailing empty list of alignments at T_{am-len} x Uj if self.preserve_alignments: for batch_idx in range(batchsize): @@ -1624,13 +1615,6 @@ def _greedy_decode( # If any type of blank token is predicted, exit inner loop, move onto next timestep t if k >= self._blank_index - len(self.big_blank_durations): not_blank = False - - if self.preserve_alignments: - # convert Ti-th logits into a torch array - hypothesis.alignments.append([]) # blank buffer for next timestep - - if self.preserve_frame_confidence: - hypothesis.frame_confidence.append([]) # blank buffer for next timestep else: # Append token to label set, update RNN state. hypothesis.y_sequence.append(k) @@ -1642,6 +1626,13 @@ def _greedy_decode( # Increment token counter. symbols_added += 1 + if self.preserve_alignments: + # convert Ti-th logits into a torch array + hypothesis.alignments.append([]) # blank buffer for next timestep + + if self.preserve_frame_confidence: + hypothesis.frame_confidence.append([]) # blank buffer for next timestep + # Remove trailing empty list of Alignments if self.preserve_alignments: if len(hypothesis.alignments[-1]) == 0: @@ -1781,9 +1772,6 @@ def _greedy_decode_blank_as_pad( # frame_confidence is a 3-dimensional dangling list representing B x T x U for hyp in hypotheses: hyp.frame_confidence = [[]] - hyp.y_3best = [[]] - hyp.frame_confidence_3best = [[[]]] - hyp.logp = [[]] # Last Label buffer + Last Label without blank buffer # batch level equivalent of the last_label @@ -1897,40 +1885,6 @@ def _greedy_decode_blank_as_pad( # This is equivalent to if single sample predicted k if blank_mask.all(): not_blank = False - - for i in range(len(big_blank_masks) + 1): - # The task here is find the shortest blank duration of all batches. - # so we start from the shortest blank duration and go up, - # and stop once we found the duration whose corresponding mask isn't all True. - if i == len(big_blank_masks) or not big_blank_masks[i].all(): - big_blank_duration = self.big_blank_durations[i - 1] if i > 0 else 1 - break - - # If preserving alignments, convert the current Uj alignments into a torch.Tensor - # Then preserve U at current timestep Ti - # Finally, forward the timestep history to Ti+1 for that sample - # All of this should only be done iff the current time index <= sample-level AM length. - # Otherwise ignore and move to next sample / next timestep. - if self.preserve_alignments: - - # convert Ti-th logits into a torch array - for batch_idx in range(batchsize): - - # this checks if current timestep <= sample-level AM length - # If current timestep > sample-level AM length, no alignments will be added - # Therefore the list of Uj alignments is empty here. - if len(hypotheses[batch_idx].alignments[-1]) > 0: - hypotheses[batch_idx].alignments.append([]) # blank buffer for next timestep - - # Do the same if preserving per-frame confidence - if self.preserve_frame_confidence: - - for batch_idx in range(batchsize): - if len(hypotheses[batch_idx].frame_confidence[-1]) > 0: - hypotheses[batch_idx].frame_confidence.append([]) # blank buffer for next timestep - hypotheses[batch_idx].y_3best.append([]) - hypotheses[batch_idx].frame_confidence_3best.append([]) - hypotheses[batch_idx].logp.append([]) else: # Collect batch indices where blanks occurred now/past blank_indices = (blank_mask == 1).nonzero(as_tuple=False) @@ -1966,6 +1920,37 @@ def _greedy_decode_blank_as_pad( symbols_added += 1 + for i in range(len(big_blank_masks) + 1): + # The task here is find the shortest blank duration of all batches. + # so we start from the shortest blank duration and go up, + # and stop once we found the duration whose corresponding mask isn't all True. + if i == len(big_blank_masks) or not big_blank_masks[i].all(): + big_blank_duration = self.big_blank_durations[i - 1] if i > 0 else 1 + break + + # If preserving alignments, convert the current Uj alignments into a torch.Tensor + # Then preserve U at current timestep Ti + # Finally, forward the timestep history to Ti+1 for that sample + # All of this should only be done iff the current time index <= sample-level AM length. + # Otherwise ignore and move to next sample / next timestep. + if self.preserve_alignments: + + # convert Ti-th logits into a torch array + for batch_idx in range(batchsize): + + # this checks if current timestep <= sample-level AM length + # If current timestep > sample-level AM length, no alignments will be added + # Therefore the list of Uj alignments is empty here. + if len(hypotheses[batch_idx].alignments[-1]) > 0: + hypotheses[batch_idx].alignments.append([]) # blank buffer for next timestep + + # Do the same if preserving per-frame confidence + if self.preserve_frame_confidence: + + for batch_idx in range(batchsize): + if len(hypotheses[batch_idx].frame_confidence[-1]) > 0: + hypotheses[batch_idx].frame_confidence.append([]) # blank buffer for next timestep + # Remove trailing empty list of alignments at T_{am-len} x Uj if self.preserve_alignments: for batch_idx in range(batchsize): @@ -1977,9 +1962,6 @@ def _greedy_decode_blank_as_pad( for batch_idx in range(batchsize): if len(hypotheses[batch_idx].frame_confidence[-1]) == 0: del hypotheses[batch_idx].frame_confidence[-1] - del hypotheses[batch_idx].y_3best[-1] - del hypotheses[batch_idx].frame_confidence_3best[-1] - del hypotheses[batch_idx].logp[-1] # Preserve states for batch_idx in range(batchsize): @@ -2121,29 +2103,6 @@ def _greedy_decode_masked( # This is equivalent to if single sample predicted k if blank_mask.all(): not_blank = False - - # If preserving alignments, convert the current Uj alignments into a torch.Tensor - # Then preserve U at current timestep Ti - # Finally, forward the timestep history to Ti+1 for that sample - # All of this should only be done iff the current time index <= sample-level AM length. - # Otherwise ignore and move to next sample / next timestep. - if self.preserve_alignments: - - # convert Ti-th logits into a torch array - for batch_idx in range(batchsize): - - # this checks if current timestep <= sample-level AM length - # If current timestep > sample-level AM length, no alignments will be added - # Therefore the list of Uj alignments is empty here. - if len(hypotheses[batch_idx].alignments[-1]) > 0: - hypotheses[batch_idx].alignments.append([]) # blank buffer for next timestep - - # Do the same if preserving per-frame confidence - if self.preserve_frame_confidence: - - for batch_idx in range(batchsize): - if len(hypotheses[batch_idx].frame_confidence[-1]) > 0: - hypotheses[batch_idx].frame_confidence.append([]) # blank buffer for next timestep else: # Collect batch indices where blanks occurred now/past blank_indices = (blank_mask == 1).nonzero(as_tuple=False) @@ -2179,6 +2138,29 @@ def _greedy_decode_masked( symbols_added += 1 + # If preserving alignments, convert the current Uj alignments into a torch.Tensor + # Then preserve U at current timestep Ti + # Finally, forward the timestep history to Ti+1 for that sample + # All of this should only be done iff the current time index <= sample-level AM length. + # Otherwise ignore and move to next sample / next timestep. + if self.preserve_alignments: + + # convert Ti-th logits into a torch array + for batch_idx in range(batchsize): + + # this checks if current timestep <= sample-level AM length + # If current timestep > sample-level AM length, no alignments will be added + # Therefore the list of Uj alignments is empty here. + if len(hypotheses[batch_idx].alignments[-1]) > 0: + hypotheses[batch_idx].alignments.append([]) # blank buffer for next timestep + + # Do the same if preserving per-frame confidence + if self.preserve_frame_confidence: + + for batch_idx in range(batchsize): + if len(hypotheses[batch_idx].frame_confidence[-1]) > 0: + hypotheses[batch_idx].frame_confidence.append([]) # blank buffer for next timestep + # Remove trailing empty list of alignments at T_{am-len} x Uj if self.preserve_alignments: for batch_idx in range(batchsize): @@ -2443,19 +2425,6 @@ def _greedy_decode( # If blank token is predicted, exit inner loop, move onto next timestep t if k == self._blank_index: not_blank = False - - # this rarely happens, but we manually increment the `skip` number - # if blank is emitted and duration=0 is predicted. This prevents possible - # infinite loops. - if skip == 0: - skip = 1 - - if self.preserve_alignments: - # convert Ti-th logits into a torch array - hypothesis.alignments.append([]) # blank buffer for next timestep - - if self.preserve_frame_confidence: - hypothesis.frame_confidence.append([]) # blank buffer for next timestep else: # Append token to label set, update RNN state. hypothesis.y_sequence.append(k) @@ -2469,6 +2438,19 @@ def _greedy_decode( time_idx += skip need_loop = skip == 0 + # this rarely happens, but we manually increment the `skip` number + # if blank is emitted and duration=0 is predicted. This prevents possible + # infinite loops. + if skip == 0: + skip = 1 + + if self.preserve_alignments: + # convert Ti-th logits into a torch array + hypothesis.alignments.append([]) # blank buffer for next timestep + + if self.preserve_frame_confidence: + hypothesis.frame_confidence.append([]) # blank buffer for next timestep + if symbols_added == self.max_symbols: time_idx += 1 @@ -2652,9 +2634,6 @@ def _greedy_decode_blank_as_pad( # frame_confidence is a 3-dimensional dangling list representing B x T x U for hyp in hypotheses: hyp.frame_confidence = [[]] - hyp.y_3best = [[]] - hyp.frame_confidence_3best = [[[]]] - hyp.logp = [[]] # Last Label buffer + Last Label without blank buffer # batch level equivalent of the last_label @@ -2781,9 +2760,6 @@ def _greedy_decode_blank_as_pad( for batch_idx in range(batchsize): if len(hypotheses[batch_idx].frame_confidence[-1]) == 0: del hypotheses[batch_idx].frame_confidence[-1] - del hypotheses[batch_idx].y_3best[-1] - del hypotheses[batch_idx].frame_confidence_3best[-1] - del hypotheses[batch_idx].logp[-1] # Preserve states for batch_idx in range(batchsize): diff --git a/tests/collections/asr/test_asr_rnnt_encdec_model.py b/tests/collections/asr/test_asr_rnnt_encdec_model.py index 68f1e38f797b..12e08006a3e4 100644 --- a/tests/collections/asr/test_asr_rnnt_encdec_model.py +++ b/tests/collections/asr/test_asr_rnnt_encdec_model.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import copy +from typing import Any, Dict, List, Optional, Tuple import pytest import torch @@ -31,6 +32,71 @@ ) or numba_utils.numba_cuda_is_supported(__NUMBA_MINIMUM_VERSION__) +@pytest.fixture() +def max_symbols_setup(): + from nemo.collections.asr.modules.rnnt_abstract import AbstractRNNTDecoder, AbstractRNNTJoint + from nemo.collections.asr.parts.utils.rnnt_utils import Hypothesis + + class DummyRNNTDecoder(AbstractRNNTDecoder): + def predict( + self, + y: Optional[torch.Tensor] = None, + state: Optional[torch.Tensor] = None, + add_sos: bool = False, + batch_size: Optional[int] = None, + ) -> Tuple[torch.Tensor, List[torch.Tensor]]: + if y is not None and state is not None: + return (y + state) / 2, y * state + elif state is not None: + return torch.tensor([0] * self.vocab_size + [1], dtype=torch.float32).repeat(state.size()), state + elif y is not None: + return y, torch.tensor([0] * self.vocab_size + [1], dtype=torch.float32).repeat(y.size()) + return ( + torch.tensor([0] * self.vocab_size + [1], dtype=torch.float32).repeat([1, 1, 1]), + torch.tensor([0] * self.vocab_size + [1], dtype=torch.float32).repeat([1, 1, 1]), + ) + + def initialize_state(self, y: torch.Tensor) -> List[torch.Tensor]: + return [torch.tensor()] + + def score_hypothesis( + self, hypothesis: Hypothesis, cache: Dict[Tuple[int], Any] + ) -> Tuple[torch.Tensor, List[torch.Tensor], torch.Tensor]: + return torch.tensor(), [torch.tensor()], torch.tensor() + + def batch_select_state(self, batch_states: List[torch.Tensor], idx: int) -> List[List[torch.Tensor]]: + if batch_states is not None: + states = batch_states[0][idx] + states = states.long() + return [states] + else: + return None + + def batch_copy_states( + self, + old_states: List[torch.Tensor], + new_states: List[torch.Tensor], + ids: List[int], + value: Optional[float] = None, + ) -> List[torch.Tensor]: + if value is None: + old_states[0][ids, :] = new_states[0][ids, :] + + return old_states + + class DummyRNNTJoint(AbstractRNNTJoint): + def joint(self, f: torch.Tensor, g: torch.Tensor) -> torch.Tensor: + return f.unsqueeze(dim=2) + g.unsqueeze(dim=1) + + setup = {} + setup["decoder"] = DummyRNNTDecoder(vocab_size=2, blank_idx=2, blank_as_pad=True) + setup["decoder_masked"] = DummyRNNTDecoder(vocab_size=2, blank_idx=2, blank_as_pad=False) + setup["joint"] = DummyRNNTJoint() + setup["encoder_output"] = torch.tensor([[[1, 0, 0], [0, 1, 0], [0, 0, 1]]], dtype=torch.float32).transpose(1, 2) + setup["encoded_lengths"] = torch.tensor([3]) + return setup + + @pytest.fixture() def asr_model(): preprocessor = {'cls': 'nemo.collections.asr.modules.AudioToMelSpectrogramPreprocessor', 'params': dict({})} @@ -589,11 +655,16 @@ def test_greedy_decoding_preserve_alignment(self, greedy_class): decoder = RNNTDecoder(prednet_cfg, vocab_size) + max_symbols_per_step = 5 for joint_type in [RNNTJoint, HATJoint]: joint_net = joint_type(jointnet_cfg, vocab_size, vocabulary=token_list) greedy = greedy_class( - decoder, joint_net, blank_index=len(token_list) - 1, preserve_alignments=True, max_symbols_per_step=5 + decoder, + joint_net, + blank_index=len(token_list), + preserve_alignments=True, + max_symbols_per_step=max_symbols_per_step, ) # (B, D, T) @@ -604,12 +675,175 @@ def test_greedy_decoding_preserve_alignment(self, greedy_class): hyp = greedy(encoder_output=enc_out, encoded_lengths=enc_len)[0][0] # type: rnnt_utils.Hypothesis assert hyp.alignments is not None + timestep_count = { + u.item(): c.item() for u, c in zip(*torch.unique(torch.tensor(hyp.timestep), return_counts=True)) + } for t in range(len(hyp.alignments)): - for u in range(len(hyp.alignments[t])): + + # check that the number of alignment elements is consistent with hyp.timestep + alignment_len = len(hyp.alignments[t]) + assert alignment_len <= max_symbols_per_step + if t in timestep_count: # non-blank + assert alignment_len == timestep_count[t] + (1 if alignment_len < max_symbols_per_step else 0) + else: # blank + assert alignment_len == 1 + + for u in range(alignment_len): logp, label = hyp.alignments[t][u] assert torch.is_tensor(logp) assert torch.is_tensor(label) + @pytest.mark.skipif( + not NUMBA_RNNT_LOSS_AVAILABLE, reason='RNNTLoss has not been compiled with appropriate numba version.', + ) + @pytest.mark.unit + @pytest.mark.parametrize( + "greedy_class", [greedy_decode.GreedyRNNTInfer, greedy_decode.GreedyBatchedRNNTInfer], + ) + def test_greedy_decoding_preserve_frame_confidence(self, greedy_class): + token_list = [" ", "a", "b", "c"] + vocab_size = len(token_list) + + encoder_output_size = 4 + decoder_output_size = 4 + joint_output_shape = 4 + + prednet_cfg = {'pred_hidden': decoder_output_size, 'pred_rnn_layers': 1} + jointnet_cfg = { + 'encoder_hidden': encoder_output_size, + 'pred_hidden': decoder_output_size, + 'joint_hidden': joint_output_shape, + 'activation': 'relu', + } + + decoder = RNNTDecoder(prednet_cfg, vocab_size) + + max_symbols_per_step = 5 + for joint_type in [RNNTJoint, HATJoint]: + joint_net = joint_type(jointnet_cfg, vocab_size, vocabulary=token_list) + + greedy = greedy_class( + decoder, + joint_net, + blank_index=len(token_list), + preserve_alignments=True, + preserve_frame_confidence=True, + max_symbols_per_step=max_symbols_per_step, + ) + + # (B, D, T) + enc_out = torch.randn(1, encoder_output_size, 30) + enc_len = torch.tensor([30], dtype=torch.int32) + + with torch.no_grad(): + hyp = greedy(encoder_output=enc_out, encoded_lengths=enc_len)[0][0] # type: rnnt_utils.Hypothesis + assert hyp.frame_confidence is not None + + timestep_count = { + u.item(): c.item() for u, c in zip(*torch.unique(torch.tensor(hyp.timestep), return_counts=True)) + } + for t in range(len(hyp.frame_confidence)): + + # check that the number of confidence elements is consistent with hyp.timestep + confidence_len = len(hyp.frame_confidence[t]) + assert confidence_len <= max_symbols_per_step + if t in timestep_count: # non-blank + assert confidence_len == timestep_count[t] + ( + 1 if confidence_len < max_symbols_per_step else 0 + ) + else: # blank + assert confidence_len == 1 + + for u in range(confidence_len): + score = hyp.frame_confidence[t][u] + assert 0 <= score <= 1 + + @pytest.mark.skipif( + not NUMBA_RNNT_LOSS_AVAILABLE, reason='RNNTLoss has not been compiled with appropriate numba version.', + ) + @pytest.mark.unit + @pytest.mark.parametrize( + "greedy_class", [greedy_decode.GreedyRNNTInfer, greedy_decode.GreedyBatchedRNNTInfer], + ) + @pytest.mark.parametrize("max_symbols_per_step", [0, 1, 5]) + def test_greedy_decoding_max_symbols_alignment(self, max_symbols_setup, greedy_class, max_symbols_per_step): + decoders = [max_symbols_setup["decoder"]] + if greedy_class is greedy_decode.GreedyBatchedRNNTInfer: + decoders.append(max_symbols_setup["decoder_masked"]) + joint = max_symbols_setup["joint"] + encoder_output = max_symbols_setup["encoder_output"] + encoded_lengths = max_symbols_setup["encoded_lengths"] + + for decoder in decoders: + greedy = greedy_class( + decoder_model=decoder, + joint_model=joint, + blank_index=decoder.blank_idx, + max_symbols_per_step=max_symbols_per_step, + preserve_alignments=True, + ) + + with torch.no_grad(): + hyp = greedy(encoder_output=encoder_output, encoded_lengths=encoded_lengths)[0][0] + assert hyp.alignments is not None + + timestep_count = { + u.item(): c.item() for u, c in zip(*torch.unique(torch.tensor(hyp.timestep), return_counts=True)) + } + for t in range(len(hyp.alignments)): + + # check that the number of confidence elements is consistent with hyp.timestep + alignment_len = len(hyp.alignments[t]) + assert alignment_len <= max_symbols_per_step + if t in timestep_count: # non-blank + assert alignment_len == timestep_count[t] + (1 if alignment_len < max_symbols_per_step else 0) + else: # blank or max_symbols_per_step == 0 + assert alignment_len <= 1 + + @pytest.mark.skipif( + not NUMBA_RNNT_LOSS_AVAILABLE, reason='RNNTLoss has not been compiled with appropriate numba version.', + ) + @pytest.mark.unit + @pytest.mark.parametrize( + "greedy_class", [greedy_decode.GreedyRNNTInfer, greedy_decode.GreedyBatchedRNNTInfer], + ) + @pytest.mark.parametrize("max_symbols_per_step", [0, 1, 5]) + def test_greedy_decoding_max_symbols_confidence(self, max_symbols_setup, greedy_class, max_symbols_per_step): + decoders = [max_symbols_setup["decoder"]] + if greedy_class is greedy_decode.GreedyBatchedRNNTInfer: + decoders.append(max_symbols_setup["decoder_masked"]) + joint = max_symbols_setup["joint"] + encoder_output = max_symbols_setup["encoder_output"] + encoded_lengths = max_symbols_setup["encoded_lengths"] + + for decoder in decoders: + greedy = greedy_class( + decoder_model=decoder, + joint_model=joint, + blank_index=decoder.blank_idx, + max_symbols_per_step=max_symbols_per_step, + preserve_frame_confidence=True, + ) + + with torch.no_grad(): + hyp = greedy(encoder_output=encoder_output, encoded_lengths=encoded_lengths)[0][0] + assert hyp.frame_confidence is not None + + timestep_count = { + u.item(): c.item() for u, c in zip(*torch.unique(torch.tensor(hyp.timestep), return_counts=True)) + } + for t in range(len(hyp.frame_confidence)): + + # check that the number of confidence elements is consistent with hyp.timestep + confidence_len = len(hyp.frame_confidence[t]) + assert confidence_len <= max_symbols_per_step + if t in timestep_count: # non-blank + assert confidence_len == timestep_count[t] + ( + 1 if confidence_len < max_symbols_per_step else 0 + ) + else: # blank or max_symbols_per_step == 0 + assert confidence_len <= 1 + @pytest.mark.skipif( not NUMBA_RNNT_LOSS_AVAILABLE, reason='RNNTLoss has not been compiled with appropriate numba version.', ) From a5492041e2eb60401a4e0722ff4304760b7c9380 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 10:44:46 -0600 Subject: [PATCH 038/112] Fix resume from checkpoint in exp_manager (#7424) (#7426) Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Co-authored-by: Eric Harper Signed-off-by: Sasha Meister --- nemo/utils/exp_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nemo/utils/exp_manager.py b/nemo/utils/exp_manager.py index 3deb814ae2df..1629aa5cbb50 100644 --- a/nemo/utils/exp_manager.py +++ b/nemo/utils/exp_manager.py @@ -578,8 +578,8 @@ def check_resume( end_dist_checkpoints = [d for d in dist_checkpoints if d.match("*end")] last_dist_checkpoints = [d for d in dist_checkpoints if d.match("*last")] - end_checkpoints = end_dist_checkpoints if end_dist_checkpoints else list(checkpoint_dir.glob("*end.ckpt")) - last_checkpoints = last_dist_checkpoints if last_dist_checkpoints else list(checkpoint_dir.glob("*last.ckpt")) + end_checkpoints = end_dist_checkpoints if end_dist_checkpoints else list(checkpoint_dir.rglob("*end.ckpt")) + last_checkpoints = last_dist_checkpoints if last_dist_checkpoints else list(checkpoint_dir.rglob("*last.ckpt")) if not checkpoint_dir.exists() or (not len(end_checkpoints) > 0 and not len(last_checkpoints) > 0): if resume_ignore_no_checkpoint: From 0ff844ff8fa9f1e9aa0839969c389da0e8a347f0 Mon Sep 17 00:00:00 2001 From: Robin Dong Date: Wed, 20 Sep 2023 02:49:18 +1000 Subject: [PATCH 039/112] Fix checking of cuda/cpu device for inputs of Decoder (#7444) * Fix checking of cuda/cpu device for inputs of Decoder Signed-off-by: Robin Dong * Update tacotron2.py Signed-off-by: Jason --------- Signed-off-by: Robin Dong Signed-off-by: Jason Co-authored-by: Jason Signed-off-by: Sasha Meister --- nemo/collections/tts/modules/tacotron2.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/nemo/collections/tts/modules/tacotron2.py b/nemo/collections/tts/modules/tacotron2.py index 7a7c63eb5ad4..dc86074abbe2 100644 --- a/nemo/collections/tts/modules/tacotron2.py +++ b/nemo/collections/tts/modules/tacotron2.py @@ -312,11 +312,8 @@ def infer(self, *, memory, memory_lengths): self.initialize_decoder_states(memory, mask=mask) - mel_lengths = torch.zeros([memory.size(0)], dtype=torch.int32) - not_finished = torch.ones([memory.size(0)], dtype=torch.int32) - if torch.cuda.is_available(): - mel_lengths = mel_lengths.cuda() - not_finished = not_finished.cuda() + mel_lengths = torch.zeros([memory.size(0)], dtype=torch.int32).to(memory.device) + not_finished = torch.ones([memory.size(0)], dtype=torch.int32).to(memory.device) mel_outputs, gate_outputs, alignments = [], [], [] stepped = False From b01b01f34d5bdfe9018ce119b12ec9d52c50d9dd Mon Sep 17 00:00:00 2001 From: Robin Dong Date: Wed, 20 Sep 2023 03:19:56 +1000 Subject: [PATCH 040/112] Fix failure of ljspeech's get_data.py (#7430) * Fix failure of ljspeech's get_data.py Signed-off-by: Robin Dong * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Robin Dong Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../tts/ljspeech/get_data.py | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/scripts/dataset_processing/tts/ljspeech/get_data.py b/scripts/dataset_processing/tts/ljspeech/get_data.py index d8a0b1c2834c..c8aeed5dbfca 100644 --- a/scripts/dataset_processing/tts/ljspeech/get_data.py +++ b/scripts/dataset_processing/tts/ljspeech/get_data.py @@ -27,11 +27,6 @@ def get_args(): parser = argparse.ArgumentParser(description='Download LJSpeech and create manifests with predefined split') parser.add_argument("--data-root", required=True, type=Path) - parser.add_argument( - '--whitelist-path', - type=str, - default="lj_speech.tsv extracted from the readme file in the dataset. You can also download the file from https://github.com/NVIDIA/NeMo-text-processing/blob/main/nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv", - ) args = parser.parse_args() return args @@ -57,20 +52,9 @@ def __extract_file(filepath, data_dir): print(f"Error while extracting {filepath}. Already extracted?") -def __process_data(data_root, whitelist_path): - if whitelist_path is None: - wget.download( - "https://raw.githubusercontent.com/NVIDIA/NeMo-text-processing/main/nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv", - out=str(data_root), - ) - whitelist_path = data_root / "lj_speech.tsv" - +def __process_data(data_root): text_normalizer = Normalizer( - lang="en", - input_case="cased", - whitelist=whitelist_path, - overwrite_cache=True, - cache_dir=data_root / "cache_dir", + lang="en", input_case="cased", overwrite_cache=True, cache_dir=data_root / "cache_dir", ) text_normalizer_call_kwargs = {"punct_pre_process": True, "punct_post_process": True} normalizer_call = lambda x: text_normalizer.normalize(x, **text_normalizer_call_kwargs) @@ -117,9 +101,8 @@ def main(): __extract_file(str(tarred_data_path), str(args.data_root)) data_root = args.data_root / "LJSpeech-1.1" - whitelist_path = args.whitelist_path - __process_data(data_root, whitelist_path) + __process_data(data_root) if __name__ == '__main__': From 422d464990acfbaa5260fb884680520216be9511 Mon Sep 17 00:00:00 2001 From: Ryan Langman Date: Tue, 19 Sep 2023 10:56:27 -0700 Subject: [PATCH 041/112] [TTS] Fix audio codec type checks (#7373) * [TTS] Fix audio codec type checks Signed-off-by: Ryan * [TTS] Fix audio codec tests Signed-off-by: Ryan --------- Signed-off-by: Ryan Signed-off-by: Sasha Meister --- .../tts/losses/audio_codec_loss.py | 6 +- nemo/collections/tts/models/audio_codec.py | 6 +- .../tts/modules/audio_codec_modules.py | 28 ++++---- .../tts/modules/encodec_modules.py | 64 +++++++++++-------- .../tts/modules/test_audio_codec_modules.py | 6 +- 5 files changed, 61 insertions(+), 49 deletions(-) diff --git a/nemo/collections/tts/losses/audio_codec_loss.py b/nemo/collections/tts/losses/audio_codec_loss.py index bde96fadb4c2..8819282f07bd 100644 --- a/nemo/collections/tts/losses/audio_codec_loss.py +++ b/nemo/collections/tts/losses/audio_codec_loss.py @@ -40,8 +40,8 @@ def __init__(self, loss_fn, loss_scale: float = 1.0): @property def input_types(self): return { - "target": NeuralType(('B', 'D', 'T'), RegressionValuesType()), "predicted": NeuralType(('B', 'D', 'T'), PredictionsType()), + "target": NeuralType(('B', 'D', 'T'), RegressionValuesType()), "target_len": NeuralType(tuple('B'), LengthsType()), } @@ -97,7 +97,7 @@ def input_types(self): @property def output_types(self): return { - "loss": [NeuralType(elements_type=LossType())], + "loss": NeuralType(elements_type=LossType()), } @typecheck() @@ -146,7 +146,7 @@ def input_types(self): @property def output_types(self): return { - "loss": [NeuralType(elements_type=LossType())], + "loss": NeuralType(elements_type=LossType()), } @typecheck() diff --git a/nemo/collections/tts/models/audio_codec.py b/nemo/collections/tts/models/audio_codec.py index 6414fa20e52d..63140b77f2b5 100644 --- a/nemo/collections/tts/models/audio_codec.py +++ b/nemo/collections/tts/models/audio_codec.py @@ -484,9 +484,11 @@ def configure_optimizers(self): sched_config = optim_config.pop("sched", None) OmegaConf.set_struct(optim_config, True) - gen_params = itertools.chain(self.audio_encoder.parameters(), self.audio_decoder.parameters()) - disc_params = self.discriminator.parameters() + vq_params = self.vector_quantizer.parameters() if self.vector_quantizer else [] + gen_params = itertools.chain(self.audio_encoder.parameters(), self.audio_decoder.parameters(), vq_params) optim_g = instantiate(optim_config, params=gen_params) + + disc_params = self.discriminator.parameters() optim_d = instantiate(optim_config, params=disc_params) if sched_config is None: diff --git a/nemo/collections/tts/modules/audio_codec_modules.py b/nemo/collections/tts/modules/audio_codec_modules.py index aaf4fb0a7f21..90c53b1f4337 100644 --- a/nemo/collections/tts/modules/audio_codec_modules.py +++ b/nemo/collections/tts/modules/audio_codec_modules.py @@ -12,15 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Iterable, Optional, Tuple +from typing import Optional, Tuple -import torch import torch.nn as nn -from einops import rearrange from nemo.collections.tts.parts.utils.helpers import mask_sequence_tensor +from nemo.core.classes.common import typecheck from nemo.core.classes.module import NeuralModule -from nemo.core.neural_types.elements import AudioSignal, EncodedRepresentation, LengthsType, VoidType +from nemo.core.neural_types.elements import LengthsType, VoidType from nemo.core.neural_types.neural_type import NeuralType @@ -64,21 +63,22 @@ def __init__( def input_types(self): return { "inputs": NeuralType(('B', 'C', 'T'), VoidType()), - "lengths": NeuralType(tuple('B'), LengthsType()), + "input_len": NeuralType(tuple('B'), LengthsType()), } @property def output_types(self): return { - "out": [NeuralType(('B', 'C', 'T'), VoidType())], + "out": NeuralType(('B', 'C', 'T'), VoidType()), } def remove_weight_norm(self): nn.utils.remove_weight_norm(self.conv) - def forward(self, inputs, lengths): + @typecheck() + def forward(self, inputs, input_len): out = self.conv(inputs) - out = mask_sequence_tensor(out, lengths) + out = mask_sequence_tensor(out, input_len) return out @@ -101,21 +101,22 @@ def __init__(self, in_channels: int, out_channels: int, kernel_size: int, stride def input_types(self): return { "inputs": NeuralType(('B', 'C', 'T'), VoidType()), - "lengths": NeuralType(tuple('B'), LengthsType()), + "input_len": NeuralType(tuple('B'), LengthsType()), } @property def output_types(self): return { - "out": [NeuralType(('B', 'C', 'T'), VoidType())], + "out": NeuralType(('B', 'C', 'T'), VoidType()), } def remove_weight_norm(self): nn.utils.remove_weight_norm(self.conv) - def forward(self, inputs, lengths): + @typecheck() + def forward(self, inputs, input_len): out = self.conv(inputs) - out = mask_sequence_tensor(out, lengths) + out = mask_sequence_tensor(out, input_len) return out @@ -151,11 +152,12 @@ def input_types(self): @property def output_types(self): return { - "out": [NeuralType(('B', 'C', 'H', 'T'), VoidType())], + "out": NeuralType(('B', 'C', 'H', 'T'), VoidType()), } def remove_weight_norm(self): nn.utils.remove_weight_norm(self.conv) + @typecheck() def forward(self, inputs): return self.conv(inputs) diff --git a/nemo/collections/tts/modules/encodec_modules.py b/nemo/collections/tts/modules/encodec_modules.py index 031b2001e5ca..b05187ccb74b 100644 --- a/nemo/collections/tts/modules/encodec_modules.py +++ b/nemo/collections/tts/modules/encodec_modules.py @@ -72,13 +72,13 @@ def __init__(self, channels: int): def input_types(self): return { "inputs": NeuralType(('B', 'C', 'T_input'), VoidType()), - "lengths": NeuralType(tuple('B'), LengthsType()), + "input_len": NeuralType(tuple('B'), LengthsType()), } @property def output_types(self): return { - "out": [NeuralType(('B', 'C', 'T_out'), VoidType())], + "out": NeuralType(('B', 'C', 'T_out'), VoidType()), } def remove_weight_norm(self): @@ -86,14 +86,15 @@ def remove_weight_norm(self): self.res_conv1.remove_weight_norm() self.res_conv2.remove_weight_norm() - def forward(self, inputs, lengths): + @typecheck() + def forward(self, inputs, input_len): res = self.activation(inputs) - res = self.res_conv1(res, lengths) + res = self.res_conv1(inputs=res, input_len=input_len) res = self.activation(res) - res = self.res_conv2(res, lengths) + res = self.res_conv2(inputs=res, input_len=input_len) - out = self.pre_conv(inputs, lengths) + res - out = mask_sequence_tensor(out, lengths) + out = self.pre_conv(inputs=inputs, input_len=input_len) + res + out = mask_sequence_tensor(out, input_len) return out @@ -112,20 +113,21 @@ def __init__(self, dim: int, num_layers: int, rnn_type: str = "lstm", use_skip: def input_types(self): return { "inputs": NeuralType(('B', 'C', 'T'), VoidType()), - "lengths": NeuralType(tuple('B'), LengthsType()), + "input_len": NeuralType(tuple('B'), LengthsType()), } @property def output_types(self): return { - "out": [NeuralType(('B', 'C', 'T'), VoidType())], + "out": NeuralType(('B', 'C', 'T'), VoidType()), } - def forward(self, inputs, lengths): + @typecheck() + def forward(self, inputs, input_len): inputs = rearrange(inputs, "B C T -> B T C") packed_inputs = nn.utils.rnn.pack_padded_sequence( - inputs, lengths=lengths.cpu(), batch_first=True, enforce_sorted=False + inputs, lengths=input_len.cpu(), batch_first=True, enforce_sorted=False ) packed_out, _ = self.rnn(packed_inputs) out, _ = nn.utils.rnn.pad_packed_sequence(packed_out, batch_first=True) @@ -183,15 +185,15 @@ def __init__( @property def input_types(self): return { - "audio": NeuralType(('B', 'C', 'T_audio'), AudioSignal()), + "audio": NeuralType(('B', 'T_audio'), AudioSignal()), "audio_len": NeuralType(tuple('B'), LengthsType()), } @property def output_types(self): return { - "encoded": [NeuralType(('B', 'D', 'T_encoded'), EncodedRepresentation())], - "encoded_len": [NeuralType(tuple('B'), LengthsType())], + "encoded": NeuralType(('B', 'D', 'T_encoded'), EncodedRepresentation()), + "encoded_len": NeuralType(tuple('B'), LengthsType()), } def remove_weight_norm(self): @@ -201,26 +203,27 @@ def remove_weight_norm(self): for down_sample_conv in self.down_sample_conv_layers: down_sample_conv.remove_weight_norm() + @typecheck() def forward(self, audio, audio_len): encoded_len = audio_len audio = rearrange(audio, "B T -> B 1 T") # [B, C, T_audio] - out = self.pre_conv(audio, encoded_len) + out = self.pre_conv(inputs=audio, input_len=encoded_len) for res_block, down_sample_conv, down_sample_rate in zip( self.res_blocks, self.down_sample_conv_layers, self.down_sample_rates ): # [B, C, T] - out = res_block(out, encoded_len) + out = res_block(inputs=out, input_len=encoded_len) out = self.activation(out) encoded_len = encoded_len // down_sample_rate # [B, 2 * C, T / down_sample_rate] - out = down_sample_conv(out, encoded_len) + out = down_sample_conv(inputs=out, input_len=encoded_len) - out = self.rnn(out, encoded_len) + out = self.rnn(inputs=out, input_len=encoded_len) out = self.activation(out) # [B, encoded_dim, T_encoded] - encoded = self.post_conv(out, encoded_len) + encoded = self.post_conv(inputs=out, input_len=encoded_len) return encoded, encoded_len @@ -274,7 +277,7 @@ def input_types(self): @property def output_types(self): return { - "audio": NeuralType(('B', 'C', 'T_audio'), AudioSignal()), + "audio": NeuralType(('B', 'T_audio'), AudioSignal()), "audio_len": NeuralType(tuple('B'), LengthsType()), } @@ -285,23 +288,24 @@ def remove_weight_norm(self): for res_block in self.res_blocks: res_block.remove_weight_norm() + @typecheck() def forward(self, inputs, input_len): audio_len = input_len # [B, C, T_encoded] - out = self.pre_conv(inputs, audio_len) - out = self.rnn(out, audio_len) + out = self.pre_conv(inputs=inputs, input_len=audio_len) + out = self.rnn(inputs=out, input_len=audio_len) for res_block, up_sample_conv, up_sample_rate in zip( self.res_blocks, self.up_sample_conv_layers, self.up_sample_rates ): audio_len = audio_len * up_sample_rate out = self.activation(out) # [B, C / 2, T * up_sample_rate] - out = up_sample_conv(out, audio_len) - out = res_block(out, audio_len) + out = up_sample_conv(inputs=out, input_len=audio_len) + out = res_block(inputs=out, input_len=audio_len) out = self.activation(out) # [B, 1, T_audio] - out = self.post_conv(out, audio_len) + out = self.post_conv(inputs=out, input_len=audio_len) audio = self.out_activation(out) audio = rearrange(audio, "B 1 T -> B T") return audio, audio_len @@ -356,6 +360,7 @@ def output_types(self): "fmap": [NeuralType(('B', 'D', 'T_spec', 'C'), VoidType())], } + @typecheck() def forward(self, audio): fmap = [] @@ -363,11 +368,11 @@ def forward(self, audio): out = self.stft(audio) for conv in self.conv_layers: # [batch, filters, T_spec, fft // 2**i] - out = conv(out) + out = conv(inputs=out) out = self.activation(out) fmap.append(out) # [batch, 1, T_spec, fft // 8] - scores = self.conv_post(out) + scores = self.conv_post(inputs=out) fmap.append(scores) scores = rearrange(scores, "B 1 T C -> B C T") @@ -382,7 +387,7 @@ def __init__(self, resolutions): @property def input_types(self): return { - "audio": NeuralType(('B', 'T_audio'), AudioSignal()), + "audio_real": NeuralType(('B', 'T_audio'), AudioSignal()), "audio_gen": NeuralType(('B', 'T_audio'), AudioSignal()), } @@ -395,6 +400,7 @@ def output_types(self): "fmaps_gen": [[NeuralType(('B', 'D', 'T_spec', 'C'), VoidType())]], } + @typecheck() def forward(self, audio_real, audio_gen): scores_real = [] scores_gen = [] @@ -627,6 +633,7 @@ def output_types(self): "indices": NeuralType(('B', 'T'), Index()), } + @typecheck() def forward(self, inputs, input_len): input_flat = rearrange(inputs, "B T D -> (B T) D") self._init_codes(input_flat) @@ -746,6 +753,7 @@ def output_types(self): "commit_loss": NeuralType((), LossType()), } + @typecheck() def forward(self, inputs: Tensor, input_len: Tensor) -> Tuple[Tensor, Tensor, float]: commit_loss = 0.0 residual = rearrange(inputs, "B D T -> B T D") diff --git a/tests/collections/tts/modules/test_audio_codec_modules.py b/tests/collections/tts/modules/test_audio_codec_modules.py index 948b1220f39c..4650a6508edd 100644 --- a/tests/collections/tts/modules/test_audio_codec_modules.py +++ b/tests/collections/tts/modules/test_audio_codec_modules.py @@ -40,7 +40,7 @@ def test_conv1d(self): lengths = torch.tensor([self.len1, self.len2], dtype=torch.int32) conv = Conv1dNorm(in_channels=self.in_channels, out_channels=self.out_channels, kernel_size=self.kernel_size) - out = conv(inputs, lengths) + out = conv(inputs=inputs, input_len=lengths) assert out.shape == (self.batch_size, self.out_channels, self.max_len) assert torch.all(out[0, :, : self.len1] != 0.0) @@ -66,7 +66,7 @@ def test_conv1d_downsample(self): stride=stride, padding=padding, ) - out = conv(inputs, lengths) + out = conv(inputs=inputs, input_len=lengths) assert out.shape == (self.batch_size, self.out_channels, out_len) assert torch.all(out[0, :, :out_len_1] != 0.0) @@ -87,7 +87,7 @@ def test_conv1d_transpose_upsample(self): conv = ConvTranspose1dNorm( in_channels=self.in_channels, out_channels=self.out_channels, kernel_size=self.kernel_size, stride=stride ) - out = conv(inputs, lengths) + out = conv(inputs=inputs, input_len=lengths) assert out.shape == (self.batch_size, self.out_channels, out_len) assert torch.all(out[0, :, :out_len_1] != 0.0) From b4bf4ee3d9a7a10b7513d3e1004587b864334c70 Mon Sep 17 00:00:00 2001 From: Ryan Langman Date: Wed, 20 Sep 2023 09:03:59 -0700 Subject: [PATCH 042/112] [TTS] Add dataset to path of logged artifacts (#7462) * [TTS] Add dataset to path of logged artifacts Signed-off-by: Ryan * [TTS] Revert axis name back to Audio Frames Signed-off-by: Ryan --------- Signed-off-by: Ryan Signed-off-by: Sasha Meister --- .../tts/data/text_to_speech_dataset.py | 12 +- nemo/collections/tts/data/vocoder_dataset.py | 13 +- nemo/collections/tts/parts/utils/callbacks.py | 185 ++++++++++++------ 3 files changed, 149 insertions(+), 61 deletions(-) diff --git a/nemo/collections/tts/data/text_to_speech_dataset.py b/nemo/collections/tts/data/text_to_speech_dataset.py index 23ddb50346a2..8530356c5cd0 100644 --- a/nemo/collections/tts/data/text_to_speech_dataset.py +++ b/nemo/collections/tts/data/text_to_speech_dataset.py @@ -46,6 +46,7 @@ class DatasetMeta: @dataclass class DatasetSample: + dataset_name: str manifest_entry: Dict[str, Any] audio_dir: Path feature_dir: Path @@ -180,6 +181,7 @@ def _preprocess_manifest( speaker_index = 0 sample = DatasetSample( + dataset_name=dataset_name, manifest_entry=entry, audio_dir=Path(dataset.audio_dir), feature_dir=Path(dataset.feature_dir), @@ -204,7 +206,12 @@ def __getitem__(self, index): audio, _ = librosa.load(audio_filepath_abs, sr=self.sample_rate) tokens = self.text_tokenizer(data.text) - example = {"audio_filepath": audio_filepath_rel, "audio": audio, "tokens": tokens} + example = { + "dataset_name": data.dataset_name, + "audio_filepath": audio_filepath_rel, + "audio": audio, + "tokens": tokens, + } if data.speaker is not None: example["speaker"] = data.speaker @@ -229,6 +236,7 @@ def __getitem__(self, index): return example def collate_fn(self, batch: List[dict]): + dataset_name_list = [] audio_filepath_list = [] audio_list = [] audio_len_list = [] @@ -238,6 +246,7 @@ def collate_fn(self, batch: List[dict]): prior_list = [] for example in batch: + dataset_name_list.append(example["dataset_name"]) audio_filepath_list.append(example["audio_filepath"]) audio_tensor = torch.tensor(example["audio"], dtype=torch.float32) @@ -264,6 +273,7 @@ def collate_fn(self, batch: List[dict]): batch_tokens = stack_tensors(token_list, max_lens=[token_max_len], pad_value=self.text_tokenizer.pad) batch_dict = { + "dataset_names": dataset_name_list, "audio_filepaths": audio_filepath_list, "audio": batch_audio, "audio_lens": batch_audio_len, diff --git a/nemo/collections/tts/data/vocoder_dataset.py b/nemo/collections/tts/data/vocoder_dataset.py index 6bf03068a395..a5a30870dfff 100644 --- a/nemo/collections/tts/data/vocoder_dataset.py +++ b/nemo/collections/tts/data/vocoder_dataset.py @@ -43,6 +43,7 @@ class DatasetMeta: @dataclass class DatasetSample: + dataset_name: str manifest_entry: dict audio_dir: Path @@ -165,7 +166,7 @@ def _preprocess_manifest( samples = [] sample_weights = [] for entry in filtered_entries: - sample = DatasetSample(manifest_entry=entry, audio_dir=Path(dataset.audio_dir),) + sample = DatasetSample(dataset_name=dataset_name, manifest_entry=entry, audio_dir=Path(dataset.audio_dir)) samples.append(sample) sample_weights.append(dataset.sample_weight) @@ -182,7 +183,12 @@ def __getitem__(self, index): audio, audio_len = self._sample_audio(audio_filepath_abs) - example = {"audio_filepath": audio_filepath_rel, "audio": audio, "audio_len": audio_len} + example = { + "dataset_name": data.dataset_name, + "audio_filepath": audio_filepath_rel, + "audio": audio, + "audio_len": audio_len, + } for processor in self.feature_processors: processor.process(example) @@ -190,11 +196,13 @@ def __getitem__(self, index): return example def collate_fn(self, batch: List[dict]): + dataset_name_list = [] audio_filepath_list = [] audio_list = [] audio_len_list = [] for example in batch: + dataset_name_list.append(example["dataset_name"]) audio_filepath_list.append(example["audio_filepath"]) audio_list.append(example["audio"]) audio_len_list.append(example["audio_len"]) @@ -205,6 +213,7 @@ def collate_fn(self, batch: List[dict]): batch_audio = stack_tensors(audio_list, max_lens=[audio_max_len]) batch_dict = { + "dataset_names": dataset_name_list, "audio_filepaths": audio_filepath_list, "audio": batch_audio, "audio_lens": batch_audio_len, diff --git a/nemo/collections/tts/parts/utils/callbacks.py b/nemo/collections/tts/parts/utils/callbacks.py index c1be48bdaa3d..09e5c41112f3 100644 --- a/nemo/collections/tts/parts/utils/callbacks.py +++ b/nemo/collections/tts/parts/utils/callbacks.py @@ -27,6 +27,7 @@ from pytorch_lightning.loggers import TensorBoardLogger from pytorch_lightning.loggers.logger import Logger from pytorch_lightning.loggers.wandb import WandbLogger +from torch import Tensor from nemo.collections.tts.parts.utils.helpers import create_plot from nemo.utils import logging @@ -81,14 +82,14 @@ class AudioArtifact: id: str data: np.ndarray sample_rate: int - filename: str + filepath: Path @dataclass class ImageArtifact: id: str data: np.ndarray - filename: str + filepath: Path x_axis: str y_axis: str @@ -187,7 +188,8 @@ def __init__( def _log_audio(self, audio: AudioArtifact, log_dir: Path, step: int): if log_dir: - filepath = log_dir / audio.filename + filepath = log_dir / audio.filepath + filepath.parent.mkdir(parents=True, exist_ok=True) sf.write(file=filepath, data=audio.data, samplerate=audio.sample_rate) if self.tensorboard_logger: @@ -201,7 +203,8 @@ def _log_audio(self, audio: AudioArtifact, log_dir: Path, step: int): def _log_image(self, image: ImageArtifact, log_dir: Path, step: int): if log_dir: - filepath = log_dir / image.filename + filepath = log_dir / image.filepath + filepath.parent.mkdir(parents=True, exist_ok=True) else: filepath = None @@ -247,7 +250,7 @@ def on_fit_start(self, trainer: Trainer, model: LightningModule): logging.debug('List are empty, no initial artifacts to log.') return - log_dir = self.output_dir / f"initial" if self.output_dir else None + log_dir = self.output_dir / "initial" if self.output_dir else None self._log_artifacts(audio_list=audio_list, image_list=image_list, log_dir=log_dir) @@ -287,28 +290,38 @@ class VocoderArtifactGenerator(ArtifactGenerator): def generate_artifacts( self, model: LightningModule, batch_dict: Dict, initial_log: bool = False ) -> Tuple[List[AudioArtifact], List[ImageArtifact]]: - if initial_log: - # Currently, nothing to log before training starts - return [], [] - - audio_artifacts = [] + dataset_names = batch_dict.get("dataset_names") audio_filepaths = batch_dict.get("audio_filepaths") audio_ids = [create_id(p) for p in audio_filepaths] audio = batch_dict.get("audio") audio_len = batch_dict.get("audio_lens") + audio_artifacts = [] + + if initial_log: + # Log ground truth audio + for i, (dataset_name, audio_id) in enumerate(zip(dataset_names, audio_ids)): + audio_gt_path = Path(f"{dataset_name}/{audio_id}_gt.wav") + audio_gt_i = audio[i, : audio_len[i]].cpu().numpy() + audio_artifact = AudioArtifact( + id=f"audio_gt_{audio_id}", data=audio_gt_i, filepath=audio_gt_path, sample_rate=model.sample_rate, + ) + audio_artifacts.append(audio_artifact) + return audio_artifacts, [] + spec, spec_len = model.audio_to_melspec_precessor(audio, audio_len) with torch.no_grad(): audio_pred = model.forward(spec=spec) audio_pred = rearrange(audio_pred, "B 1 T -> B T") - for i, audio_id in enumerate(audio_ids): - audio_pred_i = audio_pred[i][: audio_len[i]].cpu().numpy() + for i, (dataset_name, audio_id) in enumerate(zip(dataset_names, audio_ids)): + audio_pred_path = Path(f"{dataset_name}/{audio_id}.wav") + audio_pred_i = audio_pred[i, : audio_len[i]].cpu().numpy() audio_artifact = AudioArtifact( - id=f"audio_{audio_id}", data=audio_pred_i, filename=f"{audio_id}.wav", sample_rate=model.sample_rate, + id=f"audio_{audio_id}", data=audio_pred_i, filepath=audio_pred_path, sample_rate=model.sample_rate, ) audio_artifacts.append(audio_artifact) @@ -327,19 +340,26 @@ def __init__(self, log_audio: bool = True, log_encoding: bool = False, log_dequa self.log_encoding = log_encoding # Log dequantized encoded representation of the input audio (decoder input) self.log_dequantized = log_dequantized - # Input audio will be logged only once - self.input_audio_logged = False logging.debug('Initialized %s with', self.__class__.__name__) logging.debug('\tlog_audio: %s', self.log_audio) logging.debug('\tlog_encoding: %s', self.log_encoding) logging.debug('\tlog_dequantized: %s', self.log_dequantized) - def _generate_audio(self, model, audio_ids, audio, audio_len, save_input: bool = False): + def _generate_audio( + self, + model: LightningModule, + dataset_names: List[str], + audio_ids: List[str], + audio: Tensor, + audio_len: Tensor, + save_input: bool = False, + ): """Generate audio artifacts. Args: model: callable model, outputs (audio_pred, audio_pred_len) + dataset_names: list of dataset names for the examples in audio batch audio_ids: list of IDs for the examples in audio batch audio: tensor of input audio signals, shape (B, T) audio_len: tensor of lengths for each example in the batch, shape (B,) @@ -354,35 +374,34 @@ def _generate_audio(self, model, audio_ids, audio, audio_len, save_input: bool = audio_artifacts = [] # Log output audio - for i, audio_id in enumerate(audio_ids): + for i, (dataset_name, audio_id) in enumerate(zip(dataset_names, audio_ids)): + audio_pred_path = Path(f"{dataset_name}/{audio_id}_audio_out.wav") audio_pred_i = audio_pred[i, : audio_pred_len[i]].cpu().numpy() audio_artifact = AudioArtifact( - id=f"audio_out_{audio_id}", - data=audio_pred_i, - filename=f"{audio_id}_audio_out.wav", - sample_rate=model.sample_rate, + id=f"audio_out_{audio_id}", data=audio_pred_i, filepath=audio_pred_path, sample_rate=model.sample_rate, ) audio_artifacts.append(audio_artifact) if save_input: # save input audio - for i, audio_id in enumerate(audio_ids): + for i, (dataset_name, audio_id) in enumerate(zip(dataset_names, audio_ids)): + audio_in_path = Path(f"{dataset_name}/{audio_id}_audio_in.wav") audio_in_i = audio[i, : audio_len[i]].cpu().numpy() audio_artifact = AudioArtifact( - id=f"audio_in_{audio_id}", - data=audio_in_i, - filename=f"{audio_id}_audio_in.wav", - sample_rate=model.sample_rate, + id=f"audio_in_{audio_id}", data=audio_in_i, filepath=audio_in_path, sample_rate=model.sample_rate, ) audio_artifacts.append(audio_artifact) return audio_artifacts - def _generate_images(self, model, audio_ids, audio, audio_len): + def _generate_images( + self, model: LightningModule, dataset_names: List[str], audio_ids: List[str], audio: Tensor, audio_len: Tensor + ): """Generate image artifacts. Args: model: model, needs to support `model.encode_audio`, `model.quantize` and `model.dequantize` + dataset_names: list of dataset names for the examples in audio batch audio_ids: list of IDs for the examples in audio batch audio: tensor of input audio signals, shape (B, T) audio_len: tensor of lengths for each example in the batch, shape (B,) @@ -397,12 +416,13 @@ def _generate_images(self, model, audio_ids, audio, audio_len): encoded, encoded_len = model.encode_audio(audio=audio, audio_len=audio_len) if self.log_encoding: - for i, audio_id in enumerate(audio_ids): + for i, (dataset_name, audio_id) in enumerate(zip(dataset_names, audio_ids)): + encoded_path = Path(f"{dataset_name}/{audio_id}_encoded.png") encoded_i = encoded[i, :, : encoded_len[i]].cpu().numpy() encoded_artifact = ImageArtifact( id=f"encoded_{audio_id}", data=encoded_i, - filename=f"{audio_id}_encoded.png", + filepath=encoded_path, x_axis="Audio Frames", y_axis="Channels", ) @@ -416,12 +436,13 @@ def _generate_images(self, model, audio_ids, audio, audio_len): tokens = model.quantize(encoded=encoded, encoded_len=encoded_len) dequantized = model.dequantize(tokens=tokens, tokens_len=encoded_len) - for i, audio_id in enumerate(audio_ids): + for i, (dataset_name, audio_id) in enumerate(zip(dataset_names, audio_ids)): + dequantized_path = Path(f"{dataset_name}/{audio_id}_dequantized.png") dequantized_i = dequantized[i, :, : encoded_len[i]].cpu().numpy() dequantized_artifact = ImageArtifact( id=f"dequantized_{audio_id}", data=dequantized_i, - filename=f"{audio_id}_dequantized.png", + filepath=dequantized_path, x_axis="Audio Frames", y_axis="Channels", ) @@ -439,6 +460,7 @@ def generate_artifacts( initial_log: save input audio for the initial log """ + dataset_names = batch_dict.get("dataset_names") audio_filepaths = batch_dict.get("audio_filepaths") audio_ids = [create_id(p) for p in audio_filepaths] @@ -446,9 +468,16 @@ def generate_artifacts( audio_len = batch_dict.get("audio_lens") audio_artifacts = self._generate_audio( - model=model, audio_ids=audio_ids, audio=audio, audio_len=audio_len, save_input=initial_log + model=model, + dataset_names=dataset_names, + audio_ids=audio_ids, + audio=audio, + audio_len=audio_len, + save_input=initial_log, + ) + image_artifacts = self._generate_images( + model=model, dataset_names=dataset_names, audio_ids=audio_ids, audio=audio, audio_len=audio_len ) - image_artifacts = self._generate_images(model=model, audio_ids=audio_ids, audio=audio, audio_len=audio_len) return audio_artifacts, image_artifacts @@ -486,7 +515,36 @@ def __init__( type=audio_params.vocoder_type, ) - def _generate_audio(self, mels, mels_len, hop_length): + def _create_ground_truth_artifacts( + self, model: LightningModule, dataset_names: List[str], audio_ids: List[str], batch_dict: Dict + ): + audio = batch_dict.get("audio") + audio_lens = batch_dict.get("audio_lens") + spec, spec_len = model.preprocessor(input_signal=audio, length=audio_lens) + + audio_artifacts = [] + image_artifacts = [] + for i, (dataset_name, audio_id) in enumerate(zip(dataset_names, audio_ids)): + audio_gt_path = Path(f"{dataset_name}/{audio_id}_gt.wav") + audio_gt_i = audio[i, : audio_lens[i]].cpu().numpy() + audio_artifact = AudioArtifact( + id=f"audio_gt_{audio_id}", + data=audio_gt_i, + filepath=audio_gt_path, + sample_rate=self.vocoder.sample_rate, + ) + audio_artifacts.append(audio_artifact) + + spec_gt_path = Path(f"{dataset_name}/{audio_id}_spec_gt.png") + spec_gt_i = spec[i, :, : spec_len[i]].cpu().numpy() + spec_artifact = ImageArtifact( + id=f"spec_{audio_id}", data=spec_gt_i, filepath=spec_gt_path, x_axis="Audio Frames", y_axis="Channels", + ) + image_artifacts.append(spec_artifact) + + return audio_artifacts, image_artifacts + + def _generate_audio(self, mels: Tensor, mels_len: Tensor, hop_length: int): voc_input = mels.to(self.vocoder.device) with torch.no_grad(): audio_pred = self.vocoder.convert_spectrogram_to_audio(spec=voc_input) @@ -495,7 +553,9 @@ def _generate_audio(self, mels, mels_len, hop_length): audio_pred_lens = librosa.core.frames_to_samples(mels_len_array, hop_length=hop_length) return audio_pred, audio_pred_lens - def _generate_predictions(self, model: LightningModule, audio_ids: List[str], batch_dict: Dict): + def _generate_predictions( + self, model: LightningModule, dataset_names: List[str], audio_ids: List[str], batch_dict: Dict + ): audio_artifacts = [] image_artifacts = [] @@ -508,14 +568,11 @@ def _generate_predictions(self, model: LightningModule, audio_ids: List[str], ba mels_pred, mels_pred_len, *_ = model.forward(text=text, input_lens=text_lens, speaker=speaker,) if self.log_spectrogram: - for i, audio_id in enumerate(audio_ids): - spec_i = mels_pred[i][:, : mels_pred_len[i]].cpu().numpy() + for i, (dataset_name, audio_id) in enumerate(zip(dataset_names, audio_ids)): + spec_path = Path(f"{dataset_name}/{audio_id}_spec.png") + spec_i = mels_pred[i, :, : mels_pred_len[i]].cpu().numpy() spec_artifact = ImageArtifact( - id=f"spec_{audio_id}", - data=spec_i, - filename=f"{audio_id}_spec.png", - x_axis="Audio Frames", - y_axis="Channels", + id=f"spec_{audio_id}", data=spec_i, filepath=spec_path, x_axis="Audio Frames", y_axis="Channels", ) image_artifacts.append(spec_artifact) @@ -524,19 +581,22 @@ def _generate_predictions(self, model: LightningModule, audio_ids: List[str], ba audio_pred, audio_pred_lens = self._generate_audio( mels=mels_pred, mels_len=mels_pred_len, hop_length=model.preprocessor.hop_length ) - for i, audio_id in enumerate(audio_ids): - audio_pred_i = audio_pred[i][: audio_pred_lens[i]].cpu().numpy() + for i, (dataset_name, audio_id) in enumerate(zip(dataset_names, audio_ids)): + audio_pred_path = Path(f"{dataset_name}/{audio_id}.wav") + audio_pred_i = audio_pred[i, : audio_pred_lens[i]].cpu().numpy() audio_artifact = AudioArtifact( id=f"audio_{audio_id}", data=audio_pred_i, - filename=f"{audio_id}.wav", + filepath=audio_pred_path, sample_rate=self.vocoder.sample_rate, ) audio_artifacts.append(audio_artifact) return audio_artifacts, image_artifacts - def _generate_gta_predictions(self, model: LightningModule, audio_ids: List[str], batch_dict: Dict): + def _generate_gta_predictions( + self, model: LightningModule, dataset_names: List[str], audio_ids: List[str], batch_dict: Dict + ): audio_artifacts = [] image_artifacts = [] @@ -564,12 +624,13 @@ def _generate_gta_predictions(self, model: LightningModule, audio_ids: List[str] if self.log_alignment: attn = rearrange(attn, "B 1 T_spec T_text -> B T_text T_spec") - for i, audio_id in enumerate(audio_ids): - attn_i = attn[i][: text_lens[i], : mels_pred_len[i]].cpu().numpy() + for i, (dataset_name, audio_id) in enumerate(zip(dataset_names, audio_ids)): + attn_path = Path(f"{dataset_name}/{audio_id}_align.png") + attn_i = attn[i, : text_lens[i], : mels_pred_len[i]].cpu().numpy() alignment_artifact = ImageArtifact( id=f"align_{audio_id}", data=attn_i, - filename=f"{audio_id}_align.png", + filepath=attn_path, x_axis="Audio Frames", y_axis="Text Tokens", ) @@ -580,12 +641,13 @@ def _generate_gta_predictions(self, model: LightningModule, audio_ids: List[str] audio_pred, audio_pred_lens = self._generate_audio( mels=mels_pred, mels_len=mels_pred_len, hop_length=model.preprocessor.hop_length ) - for i, audio_id in enumerate(audio_ids): - audio_pred_i = audio_pred[i][: audio_pred_lens[i]].cpu().numpy() + for i, (dataset_name, audio_id) in enumerate(zip(dataset_names, audio_ids)): + audio_pred_path = Path(f"{dataset_name}/{audio_id}_gta.wav") + audio_pred_i = audio_pred[i, : audio_pred_lens[i]].cpu().numpy() audio_artifact = AudioArtifact( id=f"audio_gta_{audio_id}", data=audio_pred_i, - filename=f"{audio_id}_gta.wav", + filepath=audio_pred_path, sample_rate=self.vocoder.sample_rate, ) audio_artifacts.append(audio_artifact) @@ -596,23 +658,30 @@ def generate_artifacts( self, model: LightningModule, batch_dict: Dict, initial_log: bool = False ) -> Tuple[List[AudioArtifact], List[ImageArtifact]]: + dataset_names = batch_dict.get("dataset_names") + audio_filepaths = batch_dict.get("audio_filepaths") + audio_ids = [create_id(p) for p in audio_filepaths] + if initial_log: - # Currently, nothing to log before training starts - return [], [] + # Log ground truth audio and spectrograms + audio_gt, spec_gt = self._create_ground_truth_artifacts( + model=model, dataset_names=dataset_names, audio_ids=audio_ids, batch_dict=batch_dict + ) + return audio_gt, spec_gt audio_artifacts = [] image_artifacts = [] - audio_filepaths = batch_dict.get("audio_filepaths") - audio_ids = [create_id(p) for p in audio_filepaths] if self.log_audio or self.log_spectrogram: - audio_pred, spec_pred = self._generate_predictions(model=model, batch_dict=batch_dict, audio_ids=audio_ids) + audio_pred, spec_pred = self._generate_predictions( + model=model, dataset_names=dataset_names, audio_ids=audio_ids, batch_dict=batch_dict + ) audio_artifacts += audio_pred image_artifacts += spec_pred if self.log_audio_gta or self.log_alignment: audio_gta_pred, alignments = self._generate_gta_predictions( - model=model, batch_dict=batch_dict, audio_ids=audio_ids + model=model, dataset_names=dataset_names, audio_ids=audio_ids, batch_dict=batch_dict ) audio_artifacts += audio_gta_pred image_artifacts += alignments From 7b444dc3699bb5c855cfb9aa4cab650f7fa445c6 Mon Sep 17 00:00:00 2001 From: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Date: Wed, 20 Sep 2023 15:10:25 -0400 Subject: [PATCH 043/112] Fix sft dataset truncation (#7464) * Add fix Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix Signed-off-by: Cheng-Ping Hsieh --------- Signed-off-by: Cheng-Ping Hsieh Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../nlp/data/language_modeling/megatron/gpt_sft_dataset.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_dataset.py b/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_dataset.py index 72ba68945391..2c655d5cde6b 100644 --- a/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_dataset.py +++ b/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_dataset.py @@ -272,7 +272,10 @@ def _multiple_truncation(self, template_ids: List[List[int]], template_ids_keys: for i, (ids, key) in enumerate(zip(template_ids, template_ids_keys)): if key in self.truncation_fields: truncation_length = truncation_length_list.pop() - assert len(ids) >= truncation_length, f'{key} is not long enough to truncate.' + if len(ids) < truncation_length: + logging.warning(f'{key} is not long enough to truncate.') + truncation_length = len(ids) + if self.truncation_method == 'left': window_offset = truncation_length elif self.truncation_method == 'right': @@ -328,6 +331,7 @@ def _process_example(self, example): if len(input_ids) > self.max_seq_length: logging.warning(f'Input ids length {len(input_ids)} exceed max sequence length {self.max_seq_length}') input_ids = input_ids[: self.max_seq_length] + answer_ids = input_ids[answer_start_idx:] # store metadata in dataset, in case user may have keys required in the prediction json files metadata = {k: v for k, v in example.items() if k not in self.prompt_template_keys} From f5505837560135237163d8e73fc0cdece27c34c9 Mon Sep 17 00:00:00 2001 From: Maxime Burchi <60737204+burchim@users.noreply.github.com> Date: Wed, 20 Sep 2023 23:14:16 +0200 Subject: [PATCH 044/112] Automatic Lip Reading Recognition (ALR) - ASR/CV (Visual ASR) (#7330) * striding_conv1d_k5 and dw_striding_conv1d_k5 subsampling Signed-off-by: mburchi * transpose conv1d inputs Signed-off-by: mburchi * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: mburchi * Update subsampling.py change striding_conv1d_k5 to striding_conv1d Signed-off-by: Maxime Burchi <60737204+burchim@users.noreply.github.com> * cv branch Signed-off-by: mburchi * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * video manifest Signed-off-by: mburchi * add collection classes Signed-off-by: mburchi * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add test_step_outputs Signed-off-by: mburchi * correct manifest bug when having only audio or only videos Signed-off-by: mburchi * correct manifest bug when having only audio or only videos Signed-off-by: mburchi * clean references Signed-off-by: mburchi * freeze unfreeze transcribe cv models Signed-off-by: mburchi * correct manifest get_full_path bug Signed-off-by: mburchi * update for PR Signed-off-by: mburchi * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * guard torchvision Signed-off-by: mburchi * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update nemo/collections/cv/data/video_to_text_dataset.py Co-authored-by: Igor Gitman Signed-off-by: Maxime Burchi <60737204+burchim@users.noreply.github.com> * _video_speech_collate_fn in cv/data/video_to_text.py Signed-off-by: mburchi * add self.out = None to asr subsampling Signed-off-by: mburchi * Update nemo/collections/cv/data/video_to_text_dataset.py Co-authored-by: Igor Gitman Signed-off-by: Maxime Burchi <60737204+burchim@users.noreply.github.com> * cv -> multimodal/speech_cv branch Signed-off-by: mburchi * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: mburchi Signed-off-by: Maxime Burchi <60737204+burchim@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Igor Gitman Signed-off-by: Sasha Meister --- .../asr/parts/submodules/subsampling.py | 1 + .../common/parts/preprocessing/collections.py | 144 +++ .../common/parts/preprocessing/manifest.py | 21 +- nemo/collections/multimodal/__init__.py | 13 + .../multimodal/speech_cv/__init__.py | 25 + .../multimodal/speech_cv/data/__init__.py | 13 + .../speech_cv/data/video_to_text.py | 870 +++++++++++++++++ .../speech_cv/data/video_to_text_dataset.py | 287 ++++++ .../multimodal/speech_cv/models/__init__.py | 27 + .../speech_cv/models/visual_ctc_bpe_models.py | 314 ++++++ .../speech_cv/models/visual_ctc_models.py | 692 +++++++++++++ .../visual_hybrid_rnnt_ctc_bpe_models.py | 455 +++++++++ .../models/visual_hybrid_rnnt_ctc_models.py | 644 ++++++++++++ .../models/visual_rnnt_bpe_models.py | 321 ++++++ .../speech_cv/models/visual_rnnt_models.py | 920 ++++++++++++++++++ .../multimodal/speech_cv/modules/__init__.py | 20 + .../linear_projection_video_front_end.py | 143 +++ .../modules/resnet_video_front_end.py | 84 ++ .../speech_cv/modules/video_augment.py | 225 +++++ .../speech_cv/modules/video_preprocessing.py | 138 +++ .../multimodal/speech_cv/parts/__init__.py | 13 + .../speech_cv/parts/preprocessing/features.py | 62 ++ .../speech_cv/parts/submodules/__init__.py | 13 + .../speech_cv/parts/submodules/conv2d.py | 72 ++ .../parts/submodules/global_avg_pool2d.py | 28 + .../speech_cv/parts/submodules/permute.py | 28 + .../speech_cv/parts/submodules/resnet.py | 175 ++++ .../parts/submodules/resnet_block.py | 86 ++ .../submodules/resnet_bottleneck_block.py | 107 ++ nemo/core/neural_types/elements.py | 16 + 30 files changed, 5952 insertions(+), 5 deletions(-) create mode 100644 nemo/collections/multimodal/__init__.py create mode 100644 nemo/collections/multimodal/speech_cv/__init__.py create mode 100644 nemo/collections/multimodal/speech_cv/data/__init__.py create mode 100644 nemo/collections/multimodal/speech_cv/data/video_to_text.py create mode 100644 nemo/collections/multimodal/speech_cv/data/video_to_text_dataset.py create mode 100644 nemo/collections/multimodal/speech_cv/models/__init__.py create mode 100644 nemo/collections/multimodal/speech_cv/models/visual_ctc_bpe_models.py create mode 100644 nemo/collections/multimodal/speech_cv/models/visual_ctc_models.py create mode 100644 nemo/collections/multimodal/speech_cv/models/visual_hybrid_rnnt_ctc_bpe_models.py create mode 100644 nemo/collections/multimodal/speech_cv/models/visual_hybrid_rnnt_ctc_models.py create mode 100644 nemo/collections/multimodal/speech_cv/models/visual_rnnt_bpe_models.py create mode 100644 nemo/collections/multimodal/speech_cv/models/visual_rnnt_models.py create mode 100644 nemo/collections/multimodal/speech_cv/modules/__init__.py create mode 100644 nemo/collections/multimodal/speech_cv/modules/linear_projection_video_front_end.py create mode 100644 nemo/collections/multimodal/speech_cv/modules/resnet_video_front_end.py create mode 100644 nemo/collections/multimodal/speech_cv/modules/video_augment.py create mode 100644 nemo/collections/multimodal/speech_cv/modules/video_preprocessing.py create mode 100644 nemo/collections/multimodal/speech_cv/parts/__init__.py create mode 100644 nemo/collections/multimodal/speech_cv/parts/preprocessing/features.py create mode 100644 nemo/collections/multimodal/speech_cv/parts/submodules/__init__.py create mode 100644 nemo/collections/multimodal/speech_cv/parts/submodules/conv2d.py create mode 100644 nemo/collections/multimodal/speech_cv/parts/submodules/global_avg_pool2d.py create mode 100644 nemo/collections/multimodal/speech_cv/parts/submodules/permute.py create mode 100644 nemo/collections/multimodal/speech_cv/parts/submodules/resnet.py create mode 100644 nemo/collections/multimodal/speech_cv/parts/submodules/resnet_block.py create mode 100644 nemo/collections/multimodal/speech_cv/parts/submodules/resnet_bottleneck_block.py diff --git a/nemo/collections/asr/parts/submodules/subsampling.py b/nemo/collections/asr/parts/submodules/subsampling.py index b04138629055..068cd36022b0 100644 --- a/nemo/collections/asr/parts/submodules/subsampling.py +++ b/nemo/collections/asr/parts/submodules/subsampling.py @@ -369,6 +369,7 @@ def __init__( self.out = torch.nn.Linear(conv_channels * int(out_length), feat_out) self.conv2d_subsampling = True elif subsampling in ["striding_conv1d", "dw_striding_conv1d"]: + self.out = None self.conv2d_subsampling = False else: raise ValueError(f"Not valid sub-sampling: {subsampling}!") diff --git a/nemo/collections/common/parts/preprocessing/collections.py b/nemo/collections/common/parts/preprocessing/collections.py index ed9e53ae6ffe..66def034400f 100644 --- a/nemo/collections/common/parts/preprocessing/collections.py +++ b/nemo/collections/common/parts/preprocessing/collections.py @@ -199,6 +199,114 @@ def __init__( super().__init__(data) +class VideoText(_Collection): + """List of video-transcript text correspondence with preprocessing.""" + + OUTPUT_TYPE = collections.namedtuple( + typename='AudioTextEntity', + field_names='id video_file duration text_tokens offset text_raw speaker orig_sr lang', + ) + + def __init__( + self, + ids: List[int], + video_files: List[str], + durations: List[float], + texts: List[str], + offsets: List[str], + speakers: List[Optional[int]], + orig_sampling_rates: List[Optional[int]], + token_labels: List[Optional[int]], + langs: List[Optional[str]], + parser: parsers.CharParser, + min_duration: Optional[float] = None, + max_duration: Optional[float] = None, + max_number: Optional[int] = None, + do_sort_by_duration: bool = False, + index_by_file_id: bool = False, + ): + """Instantiates video-text manifest with filters and preprocessing. + + Args: + ids: List of examples positions. + video_files: List of video files. + durations: List of float durations. + texts: List of raw text transcripts. + offsets: List of duration offsets or None. + speakers: List of optional speakers ids. + orig_sampling_rates: List of original sampling rates of audio files. + langs: List of language ids, one for eadh sample, or None. + parser: Instance of `CharParser` to convert string to tokens. + min_duration: Minimum duration to keep entry with (default: None). + max_duration: Maximum duration to keep entry with (default: None). + max_number: Maximum number of samples to collect. + do_sort_by_duration: True if sort samples list by duration. Not compatible with index_by_file_id. + index_by_file_id: If True, saves a mapping from filename base (ID) to index in data. + """ + + output_type = self.OUTPUT_TYPE + data, duration_filtered, num_filtered, total_duration = [], 0.0, 0, 0.0 + if index_by_file_id: + self.mapping = {} + + for id_, video_file, duration, offset, text, speaker, orig_sr, token_labels, lang in zip( + ids, video_files, durations, offsets, texts, speakers, orig_sampling_rates, token_labels, langs + ): + # Duration filters. + if min_duration is not None and duration < min_duration: + duration_filtered += duration + num_filtered += 1 + continue + + if max_duration is not None and duration > max_duration: + duration_filtered += duration + num_filtered += 1 + continue + + if token_labels is not None: + text_tokens = token_labels + else: + if text != '': + if hasattr(parser, "is_aggregate") and parser.is_aggregate and isinstance(text, str): + if lang is not None: + text_tokens = parser(text, lang) + else: + raise ValueError("lang required in manifest when using aggregate tokenizers") + else: + text_tokens = parser(text) + else: + text_tokens = [] + + if text_tokens is None: + duration_filtered += duration + num_filtered += 1 + continue + + total_duration += duration + + data.append(output_type(id_, video_file, duration, text_tokens, offset, text, speaker, orig_sr, lang)) + if index_by_file_id: + file_id, _ = os.path.splitext(os.path.basename(video_file)) + if file_id not in self.mapping: + self.mapping[file_id] = [] + self.mapping[file_id].append(len(data) - 1) + + # Max number of entities filter. + if len(data) == max_number: + break + + if do_sort_by_duration: + if index_by_file_id: + logging.warning("Tried to sort dataset by duration, but cannot since index_by_file_id is set.") + else: + data.sort(key=lambda entity: entity.duration) + + logging.info("Dataset loaded with %d files totalling %.2f hours", len(data), total_duration / 3600) + logging.info("%d files were filtered totalling %.2f hours", num_filtered, duration_filtered / 3600) + + super().__init__(data) + + class ASRAudioText(AudioText): """`AudioText` collector from asr structured json files.""" @@ -235,6 +343,42 @@ def __init__(self, manifests_files: Union[str, List[str]], *args, **kwargs): ) +class ASRVideoText(VideoText): + """`VideoText` collector from cv structured json files.""" + + def __init__(self, manifests_files: Union[str, List[str]], *args, **kwargs): + """Parse lists of video files, durations and transcripts texts. + + Args: + manifests_files: Either single string file or list of such - + manifests to yield items from. + *args: Args to pass to `VideoText` constructor. + **kwargs: Kwargs to pass to `VideoText` constructor. + """ + + ids, video_files, durations, texts, offsets, = ( + [], + [], + [], + [], + [], + ) + speakers, orig_srs, token_labels, langs = [], [], [], [] + for item in manifest.item_iter(manifests_files): + ids.append(item['id']) + video_files.append(item['video_file']) + durations.append(item['duration']) + texts.append(item['text']) + offsets.append(item['offset']) + speakers.append(item['speaker']) + orig_srs.append(item['orig_sr']) + token_labels.append(item['token_labels']) + langs.append(item['lang']) + super().__init__( + ids, video_files, durations, texts, offsets, speakers, orig_srs, token_labels, langs, *args, **kwargs + ) + + class SpeechLabel(_Collection): """List of audio-label correspondence with preprocessing.""" diff --git a/nemo/collections/common/parts/preprocessing/manifest.py b/nemo/collections/common/parts/preprocessing/manifest.py index 98194505c589..882895570711 100644 --- a/nemo/collections/common/parts/preprocessing/manifest.py +++ b/nemo/collections/common/parts/preprocessing/manifest.py @@ -91,16 +91,26 @@ def __parse_item(line: str, manifest_file: str) -> Dict[str, Any]: item['audio_file'] = item.pop('audio_filename') elif 'audio_filepath' in item: item['audio_file'] = item.pop('audio_filepath') - elif 'audio_file' not in item: + + # Video File + if 'video_filename' in item: + item['video_file'] = item.pop('video_filename') + elif 'video_filepath' in item: + item['video_file'] = item.pop('video_filepath') + + if 'video_file' not in item and 'audio_file' not in item: raise ValueError( - f"Manifest file {manifest_file} has invalid json line structure: {line} without proper audio file key." + f"Manifest file {manifest_file} has invalid json line structure: {line} without proper audio/video file key." ) - # If the audio path is a relative path and does not exist, + # If the audio/video path is a relative path and does not exist, # try to attach the parent directory of manifest to the audio path. # Revert to the original path if the new path still doesn't exist. # Assume that the audio path is like "wavs/xxxxxx.wav". - item['audio_file'] = get_full_path(audio_file=item['audio_file'], manifest_file=manifest_file) + if 'audio_file' in item: + item['audio_file'] = get_full_path(audio_file=item['audio_file'], manifest_file=manifest_file) + if 'video_file' in item: + item['video_file'] = get_full_path(audio_file=item['video_file'], manifest_file=manifest_file) # Duration. if 'duration' not in item: @@ -144,7 +154,8 @@ def __parse_item(line: str, manifest_file: str) -> Dict[str, Any]: item['feature_file'] = get_full_path(audio_file=item['feature_file'], manifest_file=manifest_file) item = dict( - audio_file=item['audio_file'], + audio_file=item.get('audio_file', None), + video_file=item.get('video_file', None), duration=item['duration'], text=item['text'], rttm_file=item['rttm_file'], diff --git a/nemo/collections/multimodal/__init__.py b/nemo/collections/multimodal/__init__.py new file mode 100644 index 000000000000..4fc50543f1d2 --- /dev/null +++ b/nemo/collections/multimodal/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/nemo/collections/multimodal/speech_cv/__init__.py b/nemo/collections/multimodal/speech_cv/__init__.py new file mode 100644 index 000000000000..e13e4812457b --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/__init__.py @@ -0,0 +1,25 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from nemo.collections.multimodal.speech_cv import data, models, modules +from nemo.package_info import __version__ + +# Set collection version equal to NeMo version. +__version = __version__ + +# Authorship. +__author__ = "NVIDIA Corporation" + +# Set collection name. +__description__ = "Speech Computer Vision collection" diff --git a/nemo/collections/multimodal/speech_cv/data/__init__.py b/nemo/collections/multimodal/speech_cv/data/__init__.py new file mode 100644 index 000000000000..9e3250071955 --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/data/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/nemo/collections/multimodal/speech_cv/data/video_to_text.py b/nemo/collections/multimodal/speech_cv/data/video_to_text.py new file mode 100644 index 000000000000..d0b903a5895b --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/data/video_to_text.py @@ -0,0 +1,870 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from typing import Callable, Dict, Iterable, List, Optional, Tuple, Union + +import braceexpand +import torch +import webdataset as wd + +from nemo.collections.asr.data.audio_to_text import cache_datastore_manifests, expand_sharded_filepaths +from nemo.collections.asr.parts.utils.audio_utils import ChannelSelectorType +from nemo.collections.common import tokenizers +from nemo.collections.common.parts.preprocessing import collections, parsers +from nemo.collections.multimodal.speech_cv.parts.preprocessing.features import VideoFeaturizer +from nemo.core.classes import Dataset, IterableDataset +from nemo.core.neural_types import * +from nemo.utils import logging +from nemo.utils.data_utils import datastore_path_to_webdataset_url, is_datastore_path + + +def _video_speech_collate_fn(batch, pad_id): + """collate batch of video sig, video len, tokens, tokens len + Args: + batch (Optional[FloatTensor], Optional[LongTensor], LongTensor, + LongTensor): A tuple of tuples of signal, signal lengths, + encoded tokens, and encoded tokens length. This collate func + assumes the signals are 4d torch tensors (Time, Height, Width, Channels). + """ + packed_batch = list(zip(*batch)) + + if len(packed_batch) == 5: + _, video_lengths, _, tokens_lengths, sample_ids = packed_batch + elif len(packed_batch) == 4: + sample_ids = None + _, video_lengths, _, tokens_lengths = packed_batch + else: + raise ValueError("Expects 4 or 5 tensors in the batch!") + + # Max Video Len + max_video_len = 0 + has_video = video_lengths[0] is not None + if has_video: + max_video_len = max(video_lengths).item() + + # Max Token Len + max_tokens_len = max(tokens_lengths).item() + + video_signal, tokens = [], [] + for b in batch: + + if len(b) == 5: + video_sig, video_sig_len, tokens_i, tokens_i_len, _ = b + else: + video_sig, video_sig_len, tokens_i, tokens_i_len = b + + # Pad and Append Video + if has_video: + video_sig_len = video_sig_len.item() + if video_sig_len < max_video_len: + pad = (0, 0, 0, 0, 0, 0, 0, max_video_len - video_sig_len) + video_sig = torch.nn.functional.pad(video_sig, pad) + video_signal.append(video_sig) + + # Pad and Append Token + tokens_i_len = tokens_i_len.item() + if tokens_i_len < max_tokens_len: + pad = (0, max_tokens_len - tokens_i_len) + tokens_i = torch.nn.functional.pad(tokens_i, pad, value=pad_id) + tokens.append(tokens_i) + + # Stack Video + if has_video: + video_signal = torch.stack(video_signal) + video_lengths = torch.stack(video_lengths) + else: + video_signal, video_lengths = None, None + + # Stack Text + tokens = torch.stack(tokens) + tokens_lengths = torch.stack(tokens_lengths) + + # Return + if sample_ids is None: + return video_signal, video_lengths, tokens, tokens_lengths + else: + sample_ids = torch.tensor(sample_ids, dtype=torch.int32) + return video_signal, video_lengths, tokens, tokens_lengths, sample_ids + + +class _VideoTextDataset(Dataset): + """ + Dataset that loads tensors via a json file containing paths to video files, transcripts, and durations (in seconds). + Each new line is a different sample. Example below: + {"video_filepath": "/path/to/video.mp4", "text_filepath": "/path/to/video.txt", "duration": 23.147} + ... + {"video_filepath": "/path/to/video.mp4", "text": "the transcription", "offset": 301.75, "duration": 0.82, "utt": + "utterance_id", "ctm_utt": "en_4156", "side": "A"} + Args: + manifest_filepath: Path to manifest json as described above. Can be comma-separated paths. + parser: Str for a language specific preprocessor or a callable. + int_values (bool): If true, load samples as 32-bit integers. Defauts to False. + max_duration: If video exceeds this length, do not include in dataset + min_duration: If video is less than this length, do not include in dataset + max_utts: Limit number of utterances + trim: whether or not to trim silence. Defaults to False + bos_id: Id of beginning of sequence symbol to append if not None + eos_id: Id of end of sequence symbol to append if not None + pad_id: Id of pad symbol. Defaults to 0 + return_sample_id (bool): whether to return the sample_id as a part of each sample + channel_selector (int | Iterable[int] | str): select a single channel or a subset of channels from multi-channel audio. If set to `'average'`, it performs averaging across channels. Disabled if set to `None`. Defaults to `None`. Uses zero-based indexing. + """ + + @property + def output_types(self) -> Optional[Dict[str, NeuralType]]: + """Returns definitions of module output ports. + """ + return { + 'video_signal': NeuralType(('B', 'C', 'T', 'H', 'W'), VideoSignal()), + 'video_sig_length': NeuralType(tuple('B'), LengthsType()), + 'transcripts': NeuralType(('B', 'T'), LabelsType()), + 'transcript_length': NeuralType(tuple('B'), LengthsType()), + 'sample_id': NeuralType(tuple('B'), LengthsType(), optional=True), + } + + def __init__( + self, + manifest_filepath: str, + parser: Union[str, Callable], + int_values: bool = False, + max_duration: Optional[int] = None, + min_duration: Optional[int] = None, + max_utts: int = 0, + trim: bool = False, + bos_id: Optional[int] = None, + eos_id: Optional[int] = None, + pad_id: int = 0, + return_sample_id: bool = False, + channel_selector: Optional[ChannelSelectorType] = None, + ): + if type(manifest_filepath) == str: + manifest_filepath = manifest_filepath.split(",") + + # If necessary, cache manifests and audio from object store + cache_datastore_manifests(manifest_filepaths=manifest_filepath, cache_audio=True) + + self.manifest_processor = VSRManifestProcessor( + manifest_filepath=manifest_filepath, + parser=parser, + max_duration=max_duration, + min_duration=min_duration, + max_utts=max_utts, + bos_id=bos_id, + eos_id=eos_id, + pad_id=pad_id, + ) + self.video_featurizer = VideoFeaturizer() + self.trim = trim + self.return_sample_id = return_sample_id + self.channel_selector = channel_selector + + def get_manifest_sample(self, sample_id): + return self.manifest_processor.collection[sample_id] + + def __getitem__(self, index): + + # Select Sample + sample = self.manifest_processor.collection[index] + + # Offset + offset = sample.offset + if offset is None: + offset = 0 + + # Load Video + video_features = self.video_featurizer.process(sample.video_file, offset=offset, duration=sample.duration) + vf, vfl = video_features, torch.tensor(video_features.shape[0]).long() + + # Load Tokens + t, tl = self.manifest_processor.process_text_by_sample(sample=sample) + + if self.return_sample_id: + output = vf, vfl, torch.tensor(t).long(), torch.tensor(tl).long(), index + else: + output = vf, vfl, torch.tensor(t).long(), torch.tensor(tl).long() + + return output + + def __len__(self): + return len(self.manifest_processor.collection) + + def _collate_fn(self, batch): + return _video_speech_collate_fn(batch, pad_id=self.manifest_processor.pad_id) + + +class VSRManifestProcessor: + """ + Class that processes a manifest json file containing paths to video files, transcripts, and durations (in seconds). + Each new line is a different sample. Example below: + {"video_filepath": "/path/to/video.mp4", "text_filepath": "/path/to/video.txt", "duration": 23.147} + ... + {"video_filepath": "/path/to/video.mp4", "text": "the transcription", "offset": 301.75, "duration": 0.82, "utt": + "utterance_id", "ctm_utt": "en_4156", "side": "A"} + Args: + manifest_filepath: Path to manifest json as described above. Can be comma-separated paths. + parser: Str for a language specific preprocessor or a callable. + max_duration: If video exceeds this length, do not include in dataset. + min_duration: If video is less than this length, do not include in dataset. + max_utts: Limit number of utterances. + bos_id: Id of beginning of sequence symbol to append if not None. + eos_id: Id of end of sequence symbol to append if not None. + pad_id: Id of pad symbol. Defaults to 0. + """ + + def __init__( + self, + manifest_filepath: str, + parser: Union[str, Callable], + max_duration: Optional[float] = None, + min_duration: Optional[float] = None, + max_utts: int = 0, + bos_id: Optional[int] = None, + eos_id: Optional[int] = None, + pad_id: int = 0, + index_by_file_id: bool = False, + ): + self.parser = parser + + self.collection = collections.ASRVideoText( + manifests_files=manifest_filepath, + parser=parser, + min_duration=min_duration, + max_duration=max_duration, + max_number=max_utts, + index_by_file_id=index_by_file_id, + ) + + self.eos_id = eos_id + self.bos_id = bos_id + self.pad_id = pad_id + + def process_text_by_id(self, index: int) -> Tuple[List[int], int]: + sample = self.collection[index] + return self.process_text_by_sample(sample) + + def process_text_by_file_id(self, file_id: str) -> Tuple[List[int], int]: + manifest_idx = self.collection.mapping[file_id][0] + sample = self.collection[manifest_idx] + return self.process_text_by_sample(sample) + + def process_text_by_sample(self, sample: collections.ASRAudioText.OUTPUT_TYPE) -> Tuple[List[int], int]: + t, tl = sample.text_tokens, len(sample.text_tokens) + + if self.bos_id is not None: + t = [self.bos_id] + t + tl += 1 + if self.eos_id is not None: + t = t + [self.eos_id] + tl += 1 + + return t, tl + + +class VideoToBPEDataset(_VideoTextDataset): + """ + Dataset that loads tensors via a json file containing paths to video + files, transcripts, and durations (in seconds). Each new line is a + different sample. Example below: + {"video_filepath": "/path/to/video.mp4", "text_filepath": + "/path/to/video.txt", "duration": 23.147} + ... + {"video_filepath": "/path/to/video.mp4", "text": "the + transcription", "offset": 301.75, "duration": 0.82, "utt": + "utterance_id", "ctm_utt": "en_4156", "side": "A"} + + In practice, the dataset and manifest used for character encoding and byte pair encoding + are exactly the same. The only difference lies in how the dataset tokenizes the text in + the manifest. + + Args: + manifest_filepath: Path to manifest json as described above. Can + be comma-separated paths. + tokenizer: A subclass of the Tokenizer wrapper found in the common collection, + nemo.collections.common.tokenizers.TokenizerSpec. ASR Models support a subset of + all available tokenizers. + int_values (bool): If true, load samples as 32-bit integers. Defauts to False. + max_duration: If video exceeds this length, do not include in dataset + min_duration: If video is less than this length, do not include + in dataset + max_utts: Limit number of utterances + trim: Whether to trim silence segments + use_start_end_token: Boolean which dictates whether to add [BOS] and [EOS] + tokens to beginning and ending of speech respectively. + return_sample_id (bool): whether to return the sample_id as a part of each sample + channel_selector (int | Iterable[int] | str): select a single channel or a subset of channels from multi-channel audio. If set to `'average'`, it performs averaging across channels. Disabled if set to `None`. Defaults to `None`. Uses zero-based indexing. + """ + + @property + def output_types(self) -> Optional[Dict[str, NeuralType]]: + """Returns definitions of module output ports. + """ + return { + 'video_signal': NeuralType(('B', 'C', 'T', 'H', 'W'), VideoSignal()), + 'video_sig_length': NeuralType(tuple('B'), LengthsType()), + 'transcripts': NeuralType(('B', 'T'), LabelsType()), + 'transcript_length': NeuralType(tuple('B'), LengthsType()), + 'sample_id': NeuralType(tuple('B'), LengthsType(), optional=True), + } + + def __init__( + self, + manifest_filepath: str, + tokenizer: 'nemo.collections.common.tokenizers.TokenizerSpec', + int_values: bool = False, + max_duration: Optional[int] = None, + min_duration: Optional[int] = None, + max_utts: int = 0, + trim: bool = False, + use_start_end_token: bool = True, + return_sample_id: bool = False, + channel_selector: Optional[ChannelSelectorType] = None, + ): + if use_start_end_token and hasattr(tokenizer, "bos_id") and tokenizer.bos_id > 0: + bos_id = tokenizer.bos_id + else: + bos_id = None + + if use_start_end_token and hasattr(tokenizer, "eos_id") and tokenizer.eos_id > 0: + eos_id = tokenizer.eos_id + else: + eos_id = None + + if hasattr(tokenizer, "pad_id") and tokenizer.pad_id > 0: + pad_id = tokenizer.pad_id + else: + pad_id = 0 + + class TokenizerWrapper: + def __init__(self, tokenizer): + if isinstance(tokenizer, tokenizers.aggregate_tokenizer.AggregateTokenizer): + self.is_aggregate = True + else: + self.is_aggregate = False + self._tokenizer = tokenizer + + def __call__(self, *args): + if isinstance(args[0], List) and self.is_aggregate: + t = [] + for span in args[0]: + t.extend(self._tokenizer.text_to_ids(span['str'], span['lang'])) + return t + + t = self._tokenizer.text_to_ids(*args) + return t + + super().__init__( + manifest_filepath=manifest_filepath, + parser=TokenizerWrapper(tokenizer), + int_values=int_values, + max_duration=max_duration, + min_duration=min_duration, + max_utts=max_utts, + bos_id=bos_id, + eos_id=eos_id, + pad_id=pad_id, + trim=trim, + return_sample_id=return_sample_id, + channel_selector=channel_selector, + ) + + +class VideoToCharDataset(_VideoTextDataset): + """ + Dataset that loads tensors via a json file containing paths to video + files, transcripts, and durations (in seconds). Each new line is a + different sample. Example below: + {"video_filepath": "/path/to/video.mp4", "text_filepath": + "/path/to/video.txt", "duration": 23.147} + ... + {"video_filepath": "/path/to/video.mp4", "text": "the + transcription", "offset": 301.75, "duration": 0.82, "utt": + "utterance_id", "ctm_utt": "en_4156", "side": "A"} + + Args: + manifest_filepath: Path to manifest json as described above. Can + be comma-separated paths. + labels: String containing all the possible characters to map to + int_values (bool): If true, load samples as 32-bit integers. Defauts to False. + max_duration: If video exceeds this length, do not include in dataset + min_duration: If video is less than this length, do not include + in dataset + max_utts: Limit number of utterances + blank_index: blank character index, default = -1 + unk_index: unk_character index, default = -1 + normalize: whether to normalize transcript text (default): True + bos_id: Id of beginning of sequence symbol to append if not None + eos_id: Id of end of sequence symbol to append if not None + return_sample_id (bool): whether to return the sample_id as a part of each sample + channel_selector (int | Iterable[int] | str): select a single channel or a subset of channels from multi-channel audio. If set to `'average'`, it performs averaging across channels. Disabled if set to `None`. Defaults to `None`. Uses zero-based indexing. + """ + + @property + def output_types(self) -> Optional[Dict[str, NeuralType]]: + """Returns definitions of module output ports. + """ + return { + 'video_signal': NeuralType(('B', 'C', 'T', 'H', 'W'), VideoSignal()), + 'video_sig_length': NeuralType(tuple('B'), LengthsType()), + 'transcripts': NeuralType(('B', 'T'), LabelsType()), + 'transcript_length': NeuralType(tuple('B'), LengthsType()), + 'sample_id': NeuralType(tuple('B'), LengthsType(), optional=True), + } + + def __init__( + self, + manifest_filepath: str, + labels: Union[str, List[str]], + int_values: bool = False, + max_duration: Optional[float] = None, + min_duration: Optional[float] = None, + max_utts: int = 0, + blank_index: int = -1, + unk_index: int = -1, + normalize: bool = True, + trim: bool = False, + bos_id: Optional[int] = None, + eos_id: Optional[int] = None, + pad_id: int = 0, + parser: Union[str, Callable] = 'en', + return_sample_id: bool = False, + channel_selector: Optional[ChannelSelectorType] = None, + ): + self.labels = labels + + parser = parsers.make_parser( + labels=labels, name=parser, unk_id=unk_index, blank_id=blank_index, do_normalize=normalize + ) + + super().__init__( + manifest_filepath=manifest_filepath, + parser=parser, + int_values=int_values, + max_duration=max_duration, + min_duration=min_duration, + max_utts=max_utts, + trim=trim, + bos_id=bos_id, + eos_id=eos_id, + pad_id=pad_id, + return_sample_id=return_sample_id, + channel_selector=channel_selector, + ) + + +class _TarredVideoToTextDataset(IterableDataset): + """ + A similar Dataset to the VideoToCharDataset/VideoToBPEDataset, but which loads tarred video files. + + Accepts a single comma-separated JSON manifest file (in the same style as for the VideoToCharDataset/VideoToBPEDataset), + as well as the path(s) to the tarball(s) containing the mp4 files. Each line of the manifest should + contain the information for one video file, including at least the transcript and name of the audio + file within the tarball. + + Valid formats for the audio_tar_filepaths argument include: + (1) a single string that can be brace-expanded, e.g. 'path/to/audio.tar' or 'path/to/audio_{1..100}.tar.gz', or + (2) a list of file paths that will not be brace-expanded, e.g. ['audio_1.tar', 'audio_2.tar', ...]. + + Note: For brace expansion in (1), there may be cases where `{x..y}` syntax cannot be used due to shell interference. + This occurs most commonly inside SLURM scripts. Therefore we provide a few equivalent replacements. + Supported opening braces - { <=> (, [, < and the special tag _OP_. + Supported closing braces - } <=> ), ], > and the special tag _CL_. + For SLURM based tasks, we suggest the use of the special tags for ease of use. + + See the WebDataset documentation for more information about accepted data and input formats. + + If using multiple workers the number of shards should be divisible by world_size to ensure an + even split among workers. If it is not divisible, logging will give a warning but training will proceed. + In addition, if using mutiprocessing, each shard MUST HAVE THE SAME NUMBER OF ENTRIES after filtering + is applied. We currently do not check for this, but your program may hang if the shards are uneven! + + Notice that a few arguments are different from the AudioToCharDataset; for example, shuffle (bool) has been + replaced by shuffle_n (int). + + Additionally, please note that the len() of this DataLayer is assumed to be the length of the manifest + after filtering. An incorrect manifest length may lead to some DataLoader issues down the line. + + Args: + audio_tar_filepaths: Either a list of audio tarball filepaths, or a + string (can be brace-expandable). + manifest_filepath (str): Path to the manifest. + parser (callable): A callable which is used to pre-process the text output. + int_values (bool): If true, load samples as 32-bit integers. Defauts to False. + shuffle_n (int): How many samples to look ahead and load to be shuffled. + See WebDataset documentation for more details. + Defaults to 0. + min_duration (float): Dataset parameter. + All training files which have a duration less than min_duration + are dropped. Note: Duration is read from the manifest JSON. + Defaults to 0.1. + max_duration (float): Dataset parameter. + All training files which have a duration more than max_duration + are dropped. Note: Duration is read from the manifest JSON. + Defaults to None. + blank_index (int): Blank character index, defaults to -1. + unk_index (int): Unknown character index, defaults to -1. + normalize (bool): Dataset parameter. + Whether to use automatic text cleaning. + It is highly recommended to manually clean text for best results. + Defaults to True. + trim (bool): Whether to use trim silence from beginning and end + of audio signal using librosa.effects.trim(). + Defaults to False. + bos_id (id): Dataset parameter. + Beginning of string symbol id used for seq2seq models. + Defaults to None. + eos_id (id): Dataset parameter. + End of string symbol id used for seq2seq models. + Defaults to None. + pad_id (id): Token used to pad when collating samples in batches. + If this is None, pads using 0s. + Defaults to None. + shard_strategy (str): Tarred dataset shard distribution strategy chosen as a str value during ddp. + - `scatter`: The default shard strategy applied by WebDataset, where each node gets + a unique set of shards, which are permanently pre-allocated and never changed at runtime. + - `replicate`: Optional shard strategy, where each node gets all of the set of shards + available in the tarred dataset, which are permanently pre-allocated and never changed at runtime. + The benefit of replication is that it allows each node to sample data points from the entire + dataset independently of other nodes, and reduces dependence on value of `shuffle_n`. + + .. warning:: + Replicated strategy allows every node to sample the entire set of available tarfiles, + and therefore more than one node may sample the same tarfile, and even sample the same + data points! As such, there is no assured guarantee that all samples in the dataset will be + sampled at least once during 1 epoch. Scattered strategy, on the other hand, on specific + occasions (when the number of shards is not divisible with ``world_size``), will not sample + the entire dataset. For these reasons it is not advisable to use tarred datasets as validation + or test datasets. + global_rank (int): Worker rank, used for partitioning shards. Defaults to 0. + world_size (int): Total number of processes, used for partitioning shards. Defaults to 0. + return_sample_id (bool): whether to return the sample_id as a part of each sample + """ + + def __init__( + self, + audio_tar_filepaths: Union[str, List[str]], + manifest_filepath: str, + parser: Callable, + int_values: bool = False, + shuffle_n: int = 0, + min_duration: Optional[float] = None, + max_duration: Optional[float] = None, + trim: bool = False, + bos_id: Optional[int] = None, + eos_id: Optional[int] = None, + pad_id: int = 0, + shard_strategy: str = "scatter", + global_rank: int = 0, + world_size: int = 0, + return_sample_id: bool = False, + ): + # If necessary, cache manifests from object store + cache_datastore_manifests(manifest_filepaths=manifest_filepath) + + self.manifest_processor = VSRManifestProcessor( + manifest_filepath=manifest_filepath, + parser=parser, + max_duration=max_duration, + min_duration=min_duration, + max_utts=0, + bos_id=bos_id, + eos_id=eos_id, + pad_id=pad_id, + index_by_file_id=True, # Must set this so the manifest lines can be indexed by file ID + ) + + self.video_featurizer = VideoFeaturizer() + self.trim = trim + self.eos_id = eos_id + self.bos_id = bos_id + self.pad_id = pad_id + self.return_sample_id = return_sample_id + + audio_tar_filepaths = expand_sharded_filepaths( + audio_tar_filepaths=audio_tar_filepaths, + shard_strategy=shard_strategy, + world_size=world_size, + global_rank=global_rank, + ) + + # Put together WebDataset + self._dataset = wd.WebDataset(urls=audio_tar_filepaths, nodesplitter=None) + + if shuffle_n > 0: + self._dataset = self._dataset.shuffle(shuffle_n) + else: + logging.info("WebDataset will not shuffle files within the tar files.") + + self._dataset = ( + self._dataset.map(wd.autodecode.Decoder([wd.torch_video])) + .rename(video="mp4", key='__key__') + .to_tuple('video', 'key') + .pipe(self._filter) + .pipe(self._loop_offsets) + .map(f=self._build_sample) + ) + + def _filter(self, iterator): + """This function is used to remove samples that have been filtered out by ASRVideoText already. + Otherwise, we would get a KeyError as _build_sample attempts to find the manifest entry for a sample + that was filtered out (e.g. for duration). + Note that if using multi-GPU training, filtering may lead to an imbalance in samples in each shard, + which may make your code hang as one process will finish before the other. + """ + + class TarredAudioFilter: + def __init__(self, collection): + self.iterator = iterator + self.collection = collection + + def __iter__(self): + return self + + def __next__(self): + while True: + try: + video_bytes, audio_filename = next(self.iterator) + except: + print("except") + continue + file_id, _ = os.path.splitext(os.path.basename(audio_filename)) + if file_id in self.collection.mapping: + return video_bytes, audio_filename + + return TarredAudioFilter(self.manifest_processor.collection) + + def _loop_offsets(self, iterator): + """This function is used to iterate through utterances with different offsets for each file. + """ + + class TarredAudioLoopOffsets: + def __init__(self, collection): + self.iterator = iterator + self.collection = collection + self.current_fn = None + self.current_video_bytes = None + self.offset_id = 0 + + def __iter__(self): + return self + + def __next__(self): + if self.current_fn is None: + self.current_video_bytes, self.current_fn = next(self.iterator) + self.offset_id = 0 + else: + offset_list = self.collection.mapping[self.current_fn] + if len(offset_list) == self.offset_id + 1: + self.current_video_bytes, self.current_fn = next(self.iterator) + self.offset_id = 0 + else: + self.offset_id += 1 + + return self.current_video_bytes, self.current_fn, self.offset_id + + return TarredAudioLoopOffsets(self.manifest_processor.collection) + + def _collate_fn(self, batch): + return _video_speech_collate_fn(batch, self.pad_id) + + def _build_sample(self, tup): + """Builds the training sample by combining the data from the WebDataset with the manifest info. + """ + video_tuple, audio_filename, offset_id = tup + + # Grab manifest entry from self.manifest_preprocessor.collection + file_id, _ = os.path.splitext(os.path.basename(audio_filename)) + manifest_idx = self.manifest_processor.collection.mapping[file_id][offset_id] + manifest_entry = self.manifest_processor.collection[manifest_idx] + + offset = manifest_entry.offset + if offset is None: + offset = 0 + + # Load Video + video_features = video_tuple[0] + + # Signal length + vf, vfl = video_features, torch.tensor(video_features.shape[0]).long() + + # Load Tokens + t, tl = manifest_entry.text_tokens, len(manifest_entry.text_tokens) + + self.manifest_processor.process_text_by_sample(sample=manifest_entry) + + if self.bos_id is not None: + t = [self.bos_id] + t + tl += 1 + if self.eos_id is not None: + t = t + [self.eos_id] + tl += 1 + + if self.return_sample_id: + return vf, vfl, torch.tensor(t).long(), torch.tensor(tl).long(), manifest_idx + else: + return vf, vfl, torch.tensor(t).long(), torch.tensor(tl).long() + + def get_manifest_sample(self, sample_id): + return self.manifest_processor.collection[sample_id] + + def __iter__(self): + return self._dataset.__iter__() + + def __len__(self): + return len(self.manifest_processor.collection) + + +class TarredVideoToBPEDataset(_TarredVideoToTextDataset): + """ + A similar Dataset to the VideoToBPEDataset, but which loads tarred audio files. + + Accepts a single comma-separated JSON manifest file (in the same style as for the VideoToBPEDataset), + as well as the path(s) to the tarball(s) containing the wav files. Each line of the manifest should + contain the information for one audio file, including at least the transcript and name of the audio + file within the tarball. + + Valid formats for the audio_tar_filepaths argument include: + (1) a single string that can be brace-expanded, e.g. 'path/to/audio.tar' or 'path/to/audio_{1..100}.tar.gz', or + (2) a list of file paths that will not be brace-expanded, e.g. ['audio_1.tar', 'audio_2.tar', ...]. + + See the WebDataset documentation for more information about accepted data and input formats. + + If using multiple workers the number of shards should be divisible by world_size to ensure an + even split among workers. If it is not divisible, logging will give a warning but training will proceed. + In addition, if using mutiprocessing, each shard MUST HAVE THE SAME NUMBER OF ENTRIES after filtering + is applied. We currently do not check for this, but your program may hang if the shards are uneven! + + Notice that a few arguments are different from the AudioToBPEDataset; for example, shuffle (bool) has been + replaced by shuffle_n (int). + + Additionally, please note that the len() of this DataLayer is assumed to be the length of the manifest + after filtering. An incorrect manifest length may lead to some DataLoader issues down the line. + + Args: + audio_tar_filepaths: Either a list of audio tarball filepaths, or a + string (can be brace-expandable). + manifest_filepath (str): Path to the manifest. + tokenizer (TokenizerSpec): Either a Word Piece Encoding tokenizer (BERT), + or a Sentence Piece Encoding tokenizer (BPE). The CTC blank + symbol is automatically added later for models using ctc. + int_values (bool): If true, load samples as 32-bit integers. Defauts to False. + shuffle_n (int): How many samples to look ahead and load to be shuffled. + See WebDataset documentation for more details. + Defaults to 0. + min_duration (float): Dataset parameter. + All training files which have a duration less than min_duration + are dropped. Note: Duration is read from the manifest JSON. + Defaults to 0.1. + max_duration (float): Dataset parameter. + All training files which have a duration more than max_duration + are dropped. Note: Duration is read from the manifest JSON. + Defaults to None. + trim (bool): Whether to use trim silence from beginning and end + of audio signal using librosa.effects.trim(). + Defaults to False. + use_start_end_token: Boolean which dictates whether to add [BOS] and [EOS] + tokens to beginning and ending of speech respectively. + pad_id (id): Token used to pad when collating samples in batches. + If this is None, pads using 0s. + Defaults to None. + shard_strategy (str): Tarred dataset shard distribution strategy chosen as a str value during ddp. + + - `scatter`: The default shard strategy applied by WebDataset, where each node gets + a unique set of shards, which are permanently pre-allocated and never changed at runtime. + - `replicate`: Optional shard strategy, where each node gets all of the set of shards + available in the tarred dataset, which are permanently pre-allocated and never changed at runtime. + The benefit of replication is that it allows each node to sample data points from the entire + dataset independently of other nodes, and reduces dependence on value of `shuffle_n`. + + .. warning:: + + Replicated strategy allows every node to sample the entire set of available tarfiles, + and therefore more than one node may sample the same tarfile, and even sample the same + data points! As such, there is no assured guarantee that all samples in the dataset will be + sampled at least once during 1 epoch. Scattered strategy, on the other hand, on specific + occasions (when the number of shards is not divisible with ``world_size``), will not sample + the entire dataset. For these reasons it is not advisable to use tarred datasets as validation + or test datasets. + + global_rank (int): Worker rank, used for partitioning shards. Defaults to 0. + world_size (int): Total number of processes, used for partitioning shards. Defaults to 0. + return_sample_id (bool): whether to return the sample_id as a part of each sample + """ + + def __init__( + self, + audio_tar_filepaths: Union[str, List[str]], + manifest_filepath: str, + tokenizer: 'nemo.collections.common.tokenizers.TokenizerSpec', + int_values: bool = False, + shuffle_n: int = 0, + min_duration: Optional[float] = None, + max_duration: Optional[float] = None, + trim: bool = False, + use_start_end_token: bool = True, + shard_strategy: str = "scatter", + global_rank: int = 0, + world_size: int = 0, + return_sample_id: bool = False, + ): + if use_start_end_token and hasattr(tokenizer, "bos_id") and tokenizer.bos_id > 0: + bos_id = tokenizer.bos_id + else: + bos_id = None + + if use_start_end_token and hasattr(tokenizer, "eos_id") and tokenizer.eos_id > 0: + eos_id = tokenizer.eos_id + else: + eos_id = None + + if hasattr(tokenizer, "pad_id") and tokenizer.pad_id > 0: + pad_id = tokenizer.pad_id + else: + pad_id = 0 + + class TokenizerWrapper: + def __init__(self, tokenizer): + if isinstance(tokenizer, tokenizers.aggregate_tokenizer.AggregateTokenizer): + self.is_aggregate = True + else: + self.is_aggregate = False + self._tokenizer = tokenizer + + def __call__(self, *args): + if isinstance(args[0], Iterable) and self.is_aggregate: + t = [] + for span in args[0]: + t.extend(self._tokenizer.text_to_ids(span['str'], span['lang'])) + return t + + t = self._tokenizer.text_to_ids(*args) + return t + + super().__init__( + audio_tar_filepaths=audio_tar_filepaths, + manifest_filepath=manifest_filepath, + parser=TokenizerWrapper(tokenizer), + int_values=int_values, + shuffle_n=shuffle_n, + min_duration=min_duration, + max_duration=max_duration, + trim=trim, + bos_id=bos_id, + eos_id=eos_id, + pad_id=pad_id, + shard_strategy=shard_strategy, + global_rank=global_rank, + world_size=world_size, + return_sample_id=return_sample_id, + ) diff --git a/nemo/collections/multimodal/speech_cv/data/video_to_text_dataset.py b/nemo/collections/multimodal/speech_cv/data/video_to_text_dataset.py new file mode 100644 index 000000000000..cf34cc14974e --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/data/video_to_text_dataset.py @@ -0,0 +1,287 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import random +from math import isclose +from typing import Optional + +import torch +from omegaconf import DictConfig +from omegaconf.listconfig import ListConfig +from torch.utils.data import ChainDataset + +from nemo.collections.asr.data.audio_to_text_dataset import convert_to_config_list, get_chain_dataset +from nemo.collections.multimodal.speech_cv.data import video_to_text +from nemo.utils import logging + + +def get_video_to_text_bpe_dataset_from_config( + config, + local_rank: int, + global_rank: int, + world_size: int, + tokenizer, + preprocessor_cfg: Optional[DictConfig] = None, +): + """ + Construct Video-To-Text BPE dataset from a config. + Args: + config: BPE dataset config + local_rank: model local rank + global_rank: model global rand + world_size: world size + tokenizer: BPE tokenizer + preprocessor_cfg: preprocessor config, for DALI BPE dataset + + Returns: + constructed dataset or None if dataset config is invalid or nothing to load + """ + + is_concat = config.get('is_concat', False) + if is_concat: + if 'concat_sampling' in config and config['concat_sampling'] is None: + logging.warning(f"Concat dataset requires `concat_sampling` but it was not provided. Config: {config}") + return None + + if not 'concat_probabilities' in config: + logging.warning( + f"Concat dataset requires `concat_probabilities` list but it was not provided. Config: {config}" + ) + return None + else: + if not isclose(sum(config['concat_probabilities']), 1, abs_tol=1e-6): + logging.warning(f"`concat_probabilities` need to sum to 1. Config: {config}") + return None + + shuffle = config['shuffle'] + + # Instantiate tarred dataset loader or normal dataset loader + if config.get('is_tarred', False): + if ('tarred_audio_filepaths' in config and config['tarred_audio_filepaths'] is None) or ( + 'manifest_filepath' in config and config['manifest_filepath'] is None + ): + logging.warning( + "Could not load dataset as `manifest_filepath` was None or " + f"`tarred_audio_filepaths` is None. Provided config : {config}" + ) + return None + + shuffle_n = config.get('shuffle_n', 4 * config['batch_size']) if shuffle else 0 + if is_concat: + raise NotImplementedError("get_concat_tarred_dataset method not implemented") + else: + dataset = get_tarred_dataset( + config=config, tokenizer=tokenizer, shuffle_n=shuffle_n, global_rank=global_rank, world_size=world_size + ) + else: + if 'manifest_filepath' in config and config['manifest_filepath'] is None: + logging.warning(f"Could not load dataset as `manifest_filepath` was None. Provided config : {config}") + return None + if is_concat: + raise NotImplementedError("get_concat_bpe_dataset method not implemented") + else: + dataset = get_bpe_dataset(config=config, tokenizer=tokenizer) + return dataset + + +def get_video_to_text_char_dataset_from_config( + config, local_rank: int, global_rank: int, world_size: int, preprocessor_cfg: Optional[DictConfig] = None +): + """ + Construct Video-To-Text Char dataset from a config. + Args: + config: dataset config + local_rank: model local rank + global_rank: model global rand + world_size: world size + preprocessor_cfg: preprocessor config, for DALI dataset + + Returns: + constructed dataset or None if dataset config is invalid or nothing to load + """ + + is_concat = config.get('is_concat', False) + if is_concat: + if 'concat_sampling' in config and config['concat_sampling'] is None: + logging.warning(f"Concat dataset requires `concat_sampling` but it was not provided. Config: {config}") + return None + + if not 'concat_probabilities' in config: + logging.warning( + f"Concat dataset requires `concat_probabilities` list but it was not provided. Config: {config}" + ) + return None + else: + if not isclose(sum(config['concat_probabilities']), 1, abs_tol=1e-6): + logging.warning(f"`concat_probabilities` need to sum to 1. Config: {config}") + return None + + shuffle = config['shuffle'] + + # Instantiate tarred dataset loader or normal dataset loader + if config.get('is_tarred', False): + if ('tarred_audio_filepaths' in config and config['tarred_audio_filepaths'] is None) or ( + 'manifest_filepath' in config and config['manifest_filepath'] is None + ): + logging.warning( + "Could not load dataset as `manifest_filepath` was None or " + f"`tarred_audio_filepaths` is None. Provided config : {config}" + ) + return None + + shuffle_n = config.get('shuffle_n', 4 * config['batch_size']) if shuffle else 0 + if is_concat: + raise Exception("get_concat_tarred_dataset method not implemented") + else: + dataset = get_tarred_dataset( + config=config, shuffle_n=shuffle_n, global_rank=global_rank, world_size=world_size, + ) + else: + if 'manifest_filepath' in config and config['manifest_filepath'] is None: + logging.warning(f"Could not load dataset as `manifest_filepath` was None. Provided config : {config}") + return None + if is_concat: + raise Exception("get_concat_char_dataset method not implemented") + else: + dataset = get_char_dataset(config=config) + return dataset + + +def get_bpe_dataset(config: dict, tokenizer: 'TokenizerSpec') -> video_to_text.VideoToBPEDataset: + """ + Instantiates a Byte Pair Encoding / Word Piece Encoding based VideoToBPEDataset. + + Args: + config: Config of the VideoToBPEDataset. + tokenizer: An instance of a TokenizerSpec object. + + Returns: + An instance of VideoToBPEDataset. + """ + dataset = video_to_text.VideoToBPEDataset( + manifest_filepath=config['manifest_filepath'], + tokenizer=tokenizer, + int_values=config.get('int_values', False), + max_duration=config.get('max_duration', None), + min_duration=config.get('min_duration', None), + max_utts=config.get('max_utts', 0), + trim=config.get('trim_silence', False), + use_start_end_token=config.get('use_start_end_token', True), + return_sample_id=config.get('return_sample_id', False), + channel_selector=config.get('channel_selector', None), + ) + return dataset + + +def get_char_dataset(config: dict) -> video_to_text.VideoToCharDataset: + """ + Instantiates a Character Encoding based VideoToCharDataset. + + Args: + config: Config of the VideoToCharDataset. + + Returns: + An instance of VideoToCharDataset. + """ + if 'labels' not in config: + logging.warning(f"dataset does not have explicitly defined labels") + + dataset = video_to_text.VideoToCharDataset( + manifest_filepath=config['manifest_filepath'], + labels=config.get('labels', None), + int_values=config.get('int_values', False), + max_duration=config.get('max_duration', None), + min_duration=config.get('min_duration', None), + max_utts=config.get('max_utts', 0), + blank_index=config.get('blank_index', -1), + unk_index=config.get('unk_index', -1), + normalize=config.get('normalize_transcripts', False), + trim=config.get('trim_silence', False), + parser=config.get('parser', 'en'), + return_sample_id=config.get('return_sample_id', False), + channel_selector=config.get('channel_selector', None), + ) + return dataset + + +def get_tarred_dataset( + config: dict, shuffle_n: int, global_rank: int, world_size: int, tokenizer: Optional['TokenizerSpec'] = None, +) -> video_to_text.TarredVideoToBPEDataset: + """ + Instantiates a Word Piece/BPE Encoding based TarredVideoToBPEDataset or a char based TarredVideoToCharDataset. + + Args: + config: Config of the TarredVideoToBPEDataset or TarredVideoToCharDataset. + shuffle_n: How many samples to look ahead and load to be shuffled. + See WebDataset documentation for more details. + tokenizer: An instance of a TokenizerSpec object if BPE dataset is needed. + global_rank: Global rank of this device. + world_size: Global world size in the training method. + Passsing None would return a char-based dataset. + + Returns: + An instance of TarredVideoToBPEDataset or TarredVideoToCharDataset. + """ + tarred_audio_filepaths = config['tarred_audio_filepaths'] + manifest_filepaths = config['manifest_filepath'] + datasets = [] + tarred_audio_filepaths = convert_to_config_list(tarred_audio_filepaths) + manifest_filepaths = convert_to_config_list(manifest_filepaths) + + bucketing_weights = config.get('bucketing_weights', None) # For upsampling buckets + if bucketing_weights: + for idx, weight in enumerate(bucketing_weights): + if not isinstance(weight, int) or weight <= 0: + raise ValueError(f"bucket weights must be positive integers") + + if len(manifest_filepaths) != len(tarred_audio_filepaths): + raise ValueError( + f"manifest_filepaths (length={len(manifest_filepaths)}) and tarred_audio_filepaths (length={len(tarred_audio_filepaths)}) need to have the same number of buckets." + ) + + if 'labels' not in config: + logging.warning(f"dataset does not have explicitly defined labels") + + if 'max_utts' in config: + raise ValueError('"max_utts" parameter is not supported for tarred datasets') + + for dataset_idx, (tarred_audio_filepath, manifest_filepath) in enumerate( + zip(tarred_audio_filepaths, manifest_filepaths) + ): + if len(tarred_audio_filepath) == 1: + tarred_audio_filepath = tarred_audio_filepath[0] + if tokenizer is None: + raise Exception("video_to_text.TarredVideoToCharDataset class not Implemented") + else: + dataset = video_to_text.TarredVideoToBPEDataset( + audio_tar_filepaths=tarred_audio_filepath, + manifest_filepath=manifest_filepath, + tokenizer=tokenizer, + int_values=config.get('int_values', False), + shuffle_n=shuffle_n, + max_duration=config.get('max_duration', None), + min_duration=config.get('min_duration', None), + trim=config.get('trim_silence', False), + use_start_end_token=config.get('use_start_end_token', True), + shard_strategy=config.get('tarred_shard_strategy', 'scatter'), + global_rank=global_rank, + world_size=world_size, + return_sample_id=config.get('return_sample_id', False), + ) + if bucketing_weights: + [datasets.append(dataset) for _ in range(bucketing_weights[dataset_idx])] + else: + datasets.append(dataset) + + return get_chain_dataset(datasets=datasets, ds_config=config) diff --git a/nemo/collections/multimodal/speech_cv/models/__init__.py b/nemo/collections/multimodal/speech_cv/models/__init__.py new file mode 100644 index 000000000000..c34b4c174ac1 --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/models/__init__.py @@ -0,0 +1,27 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# CTC +from nemo.collections.multimodal.speech_cv.models.visual_ctc_bpe_models import VisualEncDecCTCModelBPE +from nemo.collections.multimodal.speech_cv.models.visual_ctc_models import VisualEncDecCTCModel +from nemo.collections.multimodal.speech_cv.models.visual_hybrid_rnnt_ctc_bpe_models import ( + VisualEncDecHybridRNNTCTCBPEModel, +) + +# Hybrid CTC/RNN-T +from nemo.collections.multimodal.speech_cv.models.visual_hybrid_rnnt_ctc_models import VisualEncDecHybridRNNTCTCModel +from nemo.collections.multimodal.speech_cv.models.visual_rnnt_bpe_models import VisualEncDecRNNTBPEModel + +# RNN-T +from nemo.collections.multimodal.speech_cv.models.visual_rnnt_models import VisualEncDecRNNTModel diff --git a/nemo/collections/multimodal/speech_cv/models/visual_ctc_bpe_models.py b/nemo/collections/multimodal/speech_cv/models/visual_ctc_bpe_models.py new file mode 100644 index 000000000000..529acb4cc86f --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/models/visual_ctc_bpe_models.py @@ -0,0 +1,314 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import os +from typing import Dict, List, Optional, Union + +import torch +from omegaconf import DictConfig, ListConfig, OmegaConf, open_dict + +from nemo.collections.asr.losses.ctc import CTCLoss +from nemo.collections.asr.metrics.wer_bpe import WERBPE, CTCBPEDecoding, CTCBPEDecodingConfig +from nemo.collections.asr.parts.mixins import ASRBPEMixin +from nemo.collections.multimodal.speech_cv.data import video_to_text_dataset +from nemo.collections.multimodal.speech_cv.models.visual_ctc_models import VisualEncDecCTCModel +from nemo.core.classes.common import PretrainedModelInfo +from nemo.utils import logging, model_utils + +__all__ = ['VisualEncDecCTCModelBPE'] + + +class VisualEncDecCTCModelBPE(VisualEncDecCTCModel, ASRBPEMixin): + """Encoder decoder CTC-based models with Byte Pair Encoding.""" + + def __init__(self, cfg: DictConfig, trainer=None): + # Convert to Hydra 1.0 compatible DictConfig + cfg = model_utils.convert_model_config_to_dict_config(cfg) + cfg = model_utils.maybe_update_config_version(cfg) + + if 'tokenizer' not in cfg: + raise ValueError("`cfg` must have `tokenizer` config to create a tokenizer !") + + # Setup the tokenizer + self._setup_tokenizer(cfg.tokenizer) + + # Initialize a dummy vocabulary + vocabulary = self.tokenizer.tokenizer.get_vocab() + + # Set the new vocabulary + with open_dict(cfg): + # sidestepping the potential overlapping tokens issue in aggregate tokenizers + if self.tokenizer_type == "agg": + cfg.decoder.vocabulary = ListConfig(vocabulary) + else: + cfg.decoder.vocabulary = ListConfig(list(vocabulary.keys())) + + # Override number of classes if placeholder provided + num_classes = cfg.decoder["num_classes"] + + if num_classes < 1: + logging.info( + "\nReplacing placeholder number of classes ({}) with actual number of classes - {}".format( + num_classes, len(vocabulary) + ) + ) + cfg.decoder["num_classes"] = len(vocabulary) + + super().__init__(cfg=cfg, trainer=trainer) + + # Setup decoding objects + decoding_cfg = self.cfg.get('decoding', None) + + # In case decoding config not found, use default config + if decoding_cfg is None: + decoding_cfg = OmegaConf.structured(CTCBPEDecodingConfig) + with open_dict(self.cfg): + self.cfg.decoding = decoding_cfg + + self.decoding = CTCBPEDecoding(self.cfg.decoding, tokenizer=self.tokenizer) + + # Setup metric with decoding strategy + self._wer = WERBPE( + decoding=self.decoding, + use_cer=self._cfg.get('use_cer', False), + dist_sync_on_step=True, + log_prediction=self._cfg.get("log_prediction", False), + ) + + def _setup_dataloader_from_config(self, config: Optional[Dict]): + dataset = video_to_text_dataset.get_video_to_text_bpe_dataset_from_config( + config=config, + local_rank=self.local_rank, + global_rank=self.global_rank, + world_size=self.world_size, + tokenizer=self.tokenizer, + preprocessor_cfg=self.cfg.get("preprocessor", None), + ) + + if dataset is None: + return None + + shuffle = config['shuffle'] + if config.get('is_tarred', False): + shuffle = False + + if hasattr(dataset, 'collate_fn'): + collate_fn = dataset.collate_fn + else: + collate_fn = dataset.datasets[0].collate_fn + + return torch.utils.data.DataLoader( + dataset=dataset, + batch_size=config['batch_size'], + collate_fn=collate_fn, + drop_last=config.get('drop_last', False), + shuffle=shuffle, + num_workers=config.get('num_workers', 0), + pin_memory=config.get('pin_memory', False), + prefetch_factor=config.get('prefetch_factor', 2), + ) + + def _setup_transcribe_dataloader(self, config: Dict) -> 'torch.utils.data.DataLoader': + """ + Setup function for a temporary data loader which wraps the provided video file. + + Args: + config: A python dictionary which contains the following keys: + paths2video_files: (a list) of paths to video files. + batch_size: (int) batch size to use during inference. \ + Bigger will result in better throughput performance but would use more memory. + temp_dir: (str) A temporary directory where the video manifest is temporarily + stored. + num_workers: (int) number of workers. Depends of the batch_size and machine. \ + 0 - only the main process will load batches, 1 - one worker (not main process) + + Returns: + A pytorch DataLoader for the given video file(s). + """ + + if 'manifest_filepath' in config: + manifest_filepath = config['manifest_filepath'] + batch_size = config['batch_size'] + else: + manifest_filepath = os.path.join(config['temp_dir'], 'manifest.json') + batch_size = min(config['batch_size'], len(config['paths2video_files'])) + + dl_config = { + 'manifest_filepath': manifest_filepath, + 'batch_size': batch_size, + 'shuffle': False, + 'num_workers': config.get('num_workers', min(batch_size, os.cpu_count() - 1)), + 'pin_memory': True, + 'channel_selector': config.get('channel_selector', None), + 'use_start_end_token': self.cfg.validation_ds.get('use_start_end_token', False), + } + + if config.get("augmentor"): + dl_config['augmentor'] = config.get("augmentor") + + temporary_datalayer = self._setup_dataloader_from_config(config=DictConfig(dl_config)) + return temporary_datalayer + + def change_vocabulary( + self, + new_tokenizer_dir: Union[str, DictConfig], + new_tokenizer_type: str, + decoding_cfg: Optional[DictConfig] = None, + ): + """ + Changes vocabulary of the tokenizer used during CTC decoding process. + Use this method when fine-tuning on from pre-trained model. + This method changes only decoder and leaves encoder and pre-processing modules unchanged. For example, you would + use it if you want to use pretrained encoder when fine-tuning on a data in another language, or when you'd need + model to learn capitalization, punctuation and/or special characters. + + Args: + new_tokenizer_dir: Directory path to tokenizer or a config for a new tokenizer (if the tokenizer type is `agg`) + new_tokenizer_type: Either `agg`, `bpe` or `wpe`. `bpe` is used for SentencePiece tokenizers, + whereas `wpe` is used for `BertTokenizer`. + new_tokenizer_cfg: A config for the new tokenizer. if provided, pre-empts the dir and type + + Returns: None + + """ + if isinstance(new_tokenizer_dir, DictConfig): + if new_tokenizer_type == 'agg': + new_tokenizer_cfg = new_tokenizer_dir + else: + raise ValueError( + f'New tokenizer dir should be a string unless the tokenizer is `agg`, but this tokenizer type is: {new_tokenizer_type}' + ) + else: + new_tokenizer_cfg = None + + if new_tokenizer_cfg is not None: + tokenizer_cfg = new_tokenizer_cfg + else: + if not os.path.isdir(new_tokenizer_dir): + raise NotADirectoryError( + f'New tokenizer dir must be non-empty path to a directory. But I got: {new_tokenizer_dir}' + f"New tokenizer dir must be non-empty path to a directory. But I got: {new_tokenizer_dir}" + ) + + if new_tokenizer_type.lower() not in ('bpe', 'wpe'): + raise ValueError(f'New tokenizer type must be either `bpe` or `wpe`') + + tokenizer_cfg = OmegaConf.create({'dir': new_tokenizer_dir, 'type': new_tokenizer_type}) + + # Setup the tokenizer + self._setup_tokenizer(tokenizer_cfg) + + # Initialize a dummy vocabulary + vocabulary = self.tokenizer.tokenizer.get_vocab() + + # Set the new vocabulary + decoder_config = copy.deepcopy(self.decoder.to_config_dict()) + # sidestepping the potential overlapping tokens issue in aggregate tokenizers + if self.tokenizer_type == "agg": + decoder_config.vocabulary = ListConfig(vocabulary) + else: + decoder_config.vocabulary = ListConfig(list(vocabulary.keys())) + + decoder_num_classes = decoder_config['num_classes'] + + # Override number of classes if placeholder provided + logging.info( + "\nReplacing old number of classes ({}) with new number of classes - {}".format( + decoder_num_classes, len(vocabulary) + ) + ) + + decoder_config['num_classes'] = len(vocabulary) + + del self.decoder + self.decoder = VisualEncDecCTCModelBPE.from_config_dict(decoder_config) + del self.loss + self.loss = CTCLoss( + num_classes=self.decoder.num_classes_with_blank - 1, + zero_infinity=True, + reduction=self._cfg.get("ctc_reduction", "mean_batch"), + ) + + if decoding_cfg is None: + # Assume same decoding config as before + decoding_cfg = self.cfg.decoding + + # Assert the decoding config with all hyper parameters + decoding_cls = OmegaConf.structured(CTCBPEDecodingConfig) + decoding_cls = OmegaConf.create(OmegaConf.to_container(decoding_cls)) + decoding_cfg = OmegaConf.merge(decoding_cls, decoding_cfg) + + self.decoding = CTCBPEDecoding(decoding_cfg=decoding_cfg, tokenizer=self.tokenizer) + + self._wer = WERBPE( + decoding=self.decoding, + use_cer=self._cfg.get('use_cer', False), + log_prediction=self._cfg.get("log_prediction", False), + dist_sync_on_step=True, + ) + + # Update config + with open_dict(self.cfg.decoder): + self._cfg.decoder = decoder_config + + with open_dict(self.cfg.decoding): + self._cfg.decoding = decoding_cfg + + logging.info(f"Changed tokenizer to {self.decoder.vocabulary} vocabulary.") + + def change_decoding_strategy(self, decoding_cfg: DictConfig): + """ + Changes decoding strategy used during CTC decoding process. + + Args: + decoding_cfg: A config for the decoder, which is optional. If the decoding type + needs to be changed (from say Greedy to Beam decoding etc), the config can be passed here. + """ + if decoding_cfg is None: + # Assume same decoding config as before + logging.info("No `decoding_cfg` passed when changing decoding strategy, using internal config") + decoding_cfg = self.cfg.decoding + + # Assert the decoding config with all hyper parameters + decoding_cls = OmegaConf.structured(CTCBPEDecodingConfig) + decoding_cls = OmegaConf.create(OmegaConf.to_container(decoding_cls)) + decoding_cfg = OmegaConf.merge(decoding_cls, decoding_cfg) + + self.decoding = CTCBPEDecoding(decoding_cfg=decoding_cfg, tokenizer=self.tokenizer,) + + self._wer = WERBPE( + decoding=self.decoding, + use_cer=self._wer.use_cer, + log_prediction=self._wer.log_prediction, + dist_sync_on_step=True, + ) + + # Update config + with open_dict(self.cfg.decoding): + self.cfg.decoding = decoding_cfg + + logging.info(f"Changed decoding strategy to \n{OmegaConf.to_yaml(self.cfg.decoding)}") + + @classmethod + def list_available_models(cls) -> List[PretrainedModelInfo]: + """ + This method returns a list of pre-trained model which can be instantiated directly from NVIDIA's NGC cloud. + + Returns: + List of available pre-trained models. + """ + results = [] + + return results diff --git a/nemo/collections/multimodal/speech_cv/models/visual_ctc_models.py b/nemo/collections/multimodal/speech_cv/models/visual_ctc_models.py new file mode 100644 index 000000000000..a2eeba03ee8f --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/models/visual_ctc_models.py @@ -0,0 +1,692 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import json +import os +import tempfile +from math import ceil +from typing import Dict, List, Optional, Union + +import torch +from omegaconf import DictConfig, OmegaConf, open_dict +from pytorch_lightning import Trainer +from tqdm.auto import tqdm + +from nemo.collections.asr.data import audio_to_text_dataset +from nemo.collections.asr.losses.ctc import CTCLoss +from nemo.collections.asr.metrics.wer import WER, CTCDecoding, CTCDecodingConfig +from nemo.collections.asr.models.asr_model import ASRModel, ExportableEncDecModel +from nemo.collections.asr.parts.mixins import ASRModuleMixin, InterCTCMixin +from nemo.collections.asr.parts.utils.audio_utils import ChannelSelectorType +from nemo.collections.multimodal.speech_cv.data import video_to_text_dataset +from nemo.core.classes.common import PretrainedModelInfo, typecheck +from nemo.core.classes.mixins import AccessMixin +from nemo.core.neural_types import LabelsType, LengthsType, LogprobsType, NeuralType, VideoSignal +from nemo.utils import logging + +__all__ = ['VisualEncDecCTCModel'] + + +class VisualEncDecCTCModel(ASRModel, ExportableEncDecModel, ASRModuleMixin, InterCTCMixin): + """Base class for encoder decoder CTC-based models.""" + + def __init__(self, cfg: DictConfig, trainer: Trainer = None): + + # Get global rank and total number of GPU workers for IterableDataset partitioning, if applicable + # Global_rank and local_rank is set by LightningModule in Lightning 1.2.0 + self.world_size = 1 + if trainer is not None: + self.world_size = trainer.world_size + + # Init + super().__init__(cfg=cfg, trainer=trainer) + + # Preprocessor, video transforms + self.video_preprocessor = VisualEncDecCTCModel.from_config_dict(self._cfg.video_preprocessor) + + # Augmentation, video augmentations + self.video_augmentation = VisualEncDecCTCModel.from_config_dict(self._cfg.video_augment) + + # Front-end Network, learned module that transform videos to temporal sequence + self.video_front_end = VisualEncDecCTCModel.from_config_dict(self._cfg.video_front_end) + + # Encoder Network + self.encoder = VisualEncDecCTCModel.from_config_dict(self._cfg.encoder) + + with open_dict(self._cfg): + if "feat_in" not in self._cfg.decoder or ( + not self._cfg.decoder.feat_in and hasattr(self.encoder, '_feat_out') + ): + self._cfg.decoder.feat_in = self.encoder._feat_out + if "feat_in" not in self._cfg.decoder or not self._cfg.decoder.feat_in: + raise ValueError("param feat_in of the decoder's config is not set!") + + if self.cfg.decoder.num_classes < 1 and self.cfg.decoder.vocabulary is not None: + logging.info( + "\nReplacing placeholder number of classes ({}) with actual number of classes - {}".format( + self.cfg.decoder.num_classes, len(self.cfg.decoder.vocabulary) + ) + ) + cfg.decoder["num_classes"] = len(self.cfg.decoder.vocabulary) + + # Decoder + self.decoder = VisualEncDecCTCModel.from_config_dict(self._cfg.decoder) + + # CTC Loss + self.loss = CTCLoss( + num_classes=self.decoder.num_classes_with_blank - 1, + zero_infinity=True, + reduction=self._cfg.get("ctc_reduction", "mean_batch"), + ) + + # Setup decoding objects + decoding_cfg = self.cfg.get('decoding', None) + + # In case decoding config not found, use default config + if decoding_cfg is None: + decoding_cfg = OmegaConf.structured(CTCDecodingConfig) + with open_dict(self.cfg): + self.cfg.decoding = decoding_cfg + + # Decoding + self.decoding = CTCDecoding(self.cfg.decoding, vocabulary=self.decoder.vocabulary) + + # Setup metric with decoding strategy + self._wer = WER( + decoding=self.decoding, + use_cer=self._cfg.get('use_cer', False), + dist_sync_on_step=True, + log_prediction=self._cfg.get("log_prediction", False), + ) + + # Setup optional Optimization flags + self.setup_optimization_flags() + + # setting up interCTC loss (from InterCTCMixin) + self.setup_interctc(decoder_name='decoder', loss_name='loss', wer_name='_wer') + + # Adapter modules setup (from ASRAdapterModelMixin) + self.setup_adapters() + + @torch.no_grad() + def transcribe( + self, + paths2video_files: List[str], + batch_size: int = 4, + logprobs: bool = False, + return_hypotheses: bool = False, + num_workers: int = 0, + channel_selector: Optional[ChannelSelectorType] = None, + augmentor: DictConfig = None, + ) -> List[str]: + """ + If modify this function, please remember update transcribe_partial_audio() in + nemo/collections/asr/parts/utils/trancribe_utils.py + + Uses greedy decoding to transcribe video files. Use this method for debugging and prototyping. + + Args: + paths2video_files: (a list) of paths to video files. + batch_size: (int) batch size to use during inference. + Bigger will result in better throughput performance but would use more memory. + logprobs: (bool) pass True to get log probabilities instead of transcripts. + return_hypotheses: (bool) Either return hypotheses or text + With hypotheses can do some postprocessing like getting timestamp or rescoring + num_workers: (int) number of workers for DataLoader + channel_selector (int | Iterable[int] | str): select a single channel or a subset of channels from multi-channel audio. If set to `'average'`, it performs averaging across channels. Disabled if set to `None`. Defaults to `None`. + augmentor: (DictConfig): Augment audio samples during transcription if augmentor is applied. + Returns: + A list of transcriptions (or raw log probabilities if logprobs is True) in the same order as paths2video_files + """ + if paths2video_files is None or len(paths2video_files) == 0: + return {} + + if return_hypotheses and logprobs: + raise ValueError( + "Either `return_hypotheses` or `logprobs` can be True at any given time." + "Returned hypotheses will contain the logprobs." + ) + + if num_workers is None: + num_workers = min(batch_size, os.cpu_count() - 1) + + # We will store transcriptions here + hypotheses = [] + all_hypotheses = [] + + # Model's mode and device + mode = self.training + device = next(self.parameters()).device + + try: + # Switch model to evaluation mode + self.eval() + # Freeze the visual front-end, encoder and decoder modules + self.video_front_end.freeze() + self.encoder.freeze() + self.decoder.freeze() + logging_level = logging.get_verbosity() + logging.set_verbosity(logging.WARNING) + # Work in tmp directory - will store manifest file there + with tempfile.TemporaryDirectory() as tmpdir: + with open(os.path.join(tmpdir, 'manifest.json'), 'w', encoding='utf-8') as fp: + for video_file in paths2video_files: + entry = {'video_filepath': video_file, 'duration': 100000, 'text': ''} + fp.write(json.dumps(entry) + '\n') + + config = { + 'paths2video_files': paths2video_files, + 'batch_size': batch_size, + 'temp_dir': tmpdir, + 'num_workers': num_workers, + 'channel_selector': channel_selector, + } + + if augmentor: + config['augmentor'] = augmentor + + temporary_datalayer = self._setup_transcribe_dataloader(config) + for test_batch in tqdm(temporary_datalayer, desc="Transcribing"): + logits, logits_len, greedy_predictions = self.forward( + input_signal=test_batch[0].to(device), input_signal_length=test_batch[1].to(device) + ) + if logprobs: + # dump log probs per file + for idx in range(logits.shape[0]): + lg = logits[idx][: logits_len[idx]] + hypotheses.append(lg.cpu().numpy()) + else: + current_hypotheses, all_hyp = self.decoding.ctc_decoder_predictions_tensor( + logits, decoder_lengths=logits_len, return_hypotheses=return_hypotheses, + ) + + if return_hypotheses: + # dump log probs per file + for idx in range(logits.shape[0]): + current_hypotheses[idx].y_sequence = logits[idx][: logits_len[idx]] + if current_hypotheses[idx].alignments is None: + current_hypotheses[idx].alignments = current_hypotheses[idx].y_sequence + + if all_hyp is None: + hypotheses += current_hypotheses + else: + hypotheses += all_hyp + + del greedy_predictions + del logits + del test_batch + finally: + # set mode back to its original value + self.train(mode=mode) + if mode is True: + self.video_front_end.unfreeze() + self.encoder.unfreeze() + self.decoder.unfreeze() + logging.set_verbosity(logging_level) + + return hypotheses + + def change_vocabulary(self, new_vocabulary: List[str], decoding_cfg: Optional[DictConfig] = None): + """ + Changes vocabulary used during CTC decoding process. Use this method when fine-tuning on from pre-trained model. + This method changes only decoder and leaves encoder and pre-processing modules unchanged. For example, you would + use it if you want to use pretrained encoder when fine-tuning on a data in another language, or when you'd need + model to learn capitalization, punctuation and/or special characters. + + If new_vocabulary == self.decoder.vocabulary then nothing will be changed. + + Args: + + new_vocabulary: list with new vocabulary. Must contain at least 2 elements. Typically, \ + this is target alphabet. + + Returns: None + + """ + if self.decoder.vocabulary == new_vocabulary: + logging.warning(f"Old {self.decoder.vocabulary} and new {new_vocabulary} match. Not changing anything.") + else: + if new_vocabulary is None or len(new_vocabulary) == 0: + raise ValueError(f'New vocabulary must be non-empty list of chars. But I got: {new_vocabulary}') + decoder_config = self.decoder.to_config_dict() + new_decoder_config = copy.deepcopy(decoder_config) + new_decoder_config['vocabulary'] = new_vocabulary + new_decoder_config['num_classes'] = len(new_vocabulary) + + del self.decoder + self.decoder = VisualEncDecCTCModel.from_config_dict(new_decoder_config) + del self.loss + self.loss = CTCLoss( + num_classes=self.decoder.num_classes_with_blank - 1, + zero_infinity=True, + reduction=self._cfg.get("ctc_reduction", "mean_batch"), + ) + + if decoding_cfg is None: + # Assume same decoding config as before + decoding_cfg = self.cfg.decoding + + # Assert the decoding config with all hyper parameters + decoding_cls = OmegaConf.structured(CTCDecodingConfig) + decoding_cls = OmegaConf.create(OmegaConf.to_container(decoding_cls)) + decoding_cfg = OmegaConf.merge(decoding_cls, decoding_cfg) + + self.decoding = CTCDecoding(decoding_cfg=decoding_cfg, vocabulary=self.decoder.vocabulary) + + self._wer = WER( + decoding=self.decoding, + use_cer=self._cfg.get('use_cer', False), + dist_sync_on_step=True, + log_prediction=self._cfg.get("log_prediction", False), + ) + + # Update config + with open_dict(self.cfg.decoder): + self._cfg.decoder = new_decoder_config + + with open_dict(self.cfg.decoding): + self.cfg.decoding = decoding_cfg + + ds_keys = ['train_ds', 'validation_ds', 'test_ds'] + for key in ds_keys: + if key in self.cfg: + with open_dict(self.cfg[key]): + self.cfg[key]['labels'] = OmegaConf.create(new_vocabulary) + + logging.info(f"Changed decoder to output to {self.decoder.vocabulary} vocabulary.") + + def change_decoding_strategy(self, decoding_cfg: DictConfig): + """ + Changes decoding strategy used during CTC decoding process. + + Args: + decoding_cfg: A config for the decoder, which is optional. If the decoding type + needs to be changed (from say Greedy to Beam decoding etc), the config can be passed here. + """ + if decoding_cfg is None: + # Assume same decoding config as before + logging.info("No `decoding_cfg` passed when changing decoding strategy, using internal config") + decoding_cfg = self.cfg.decoding + + # Assert the decoding config with all hyper parameters + decoding_cls = OmegaConf.structured(CTCDecodingConfig) + decoding_cls = OmegaConf.create(OmegaConf.to_container(decoding_cls)) + decoding_cfg = OmegaConf.merge(decoding_cls, decoding_cfg) + + self.decoding = CTCDecoding(decoding_cfg=decoding_cfg, vocabulary=self.decoder.vocabulary) + + self._wer = WER( + decoding=self.decoding, + use_cer=self._wer.use_cer, + log_prediction=self._wer.log_prediction, + dist_sync_on_step=True, + ) + + # Update config + with open_dict(self.cfg.decoding): + self.cfg.decoding = decoding_cfg + + logging.info(f"Changed decoding strategy to \n{OmegaConf.to_yaml(self.cfg.decoding)}") + + def _setup_dataloader_from_config(self, config: Optional[Dict]): + # Automatically inject args from model config to dataloader config + audio_to_text_dataset.inject_dataloader_value_from_model_config(self.cfg, config, key='sample_rate') + audio_to_text_dataset.inject_dataloader_value_from_model_config(self.cfg, config, key='labels') + dataset = video_to_text_dataset.get_video_to_text_char_dataset_from_config( + config=config, + local_rank=self.local_rank, + global_rank=self.global_rank, + world_size=self.world_size, + preprocessor_cfg=self._cfg.get("preprocessor", None), + ) + + if dataset is None: + return None + + shuffle = config['shuffle'] + if config.get('is_tarred', False): + shuffle = False + + if hasattr(dataset, 'collate_fn'): + collate_fn = dataset.collate_fn + else: + collate_fn = dataset.datasets[0].collate_fn + + return torch.utils.data.DataLoader( + dataset=dataset, + batch_size=config['batch_size'], + collate_fn=collate_fn, + drop_last=config.get('drop_last', False), + shuffle=shuffle, + num_workers=config.get('num_workers', 0), + pin_memory=config.get('pin_memory', False), + ) + + def setup_training_data(self, train_data_config: Optional[Union[DictConfig, Dict]]): + """ + Sets up the training data loader via a Dict-like object. + + Args: + train_data_config: A config that contains the information regarding construction + of an ASR Training dataset. + + Supported Datasets: + - :class:`~nemo.collections.multimodal.speech_cv.data.video_to_text.VideoToCharDataset` + - :class:`~nemo.collections.asr.data.video_to_text.VideoToBPEDataset` + - :class:`~nemo.collections.asr.data.video_to_text.TarredVideoToBPEDataset` + """ + if 'shuffle' not in train_data_config: + train_data_config['shuffle'] = True + + # preserve config + self._update_dataset_config(dataset_name='train', config=train_data_config) + + self._train_dl = self._setup_dataloader_from_config(config=train_data_config) + + # Need to set this because if using an IterableDataset, the length of the dataloader is the total number + # of samples rather than the number of batches, and this messes up the tqdm progress bar. + # So we set the number of steps manually (to the correct number) to fix this. + if 'is_tarred' in train_data_config and train_data_config['is_tarred']: + # We also need to check if limit_train_batches is already set. + # If it's an int, we assume that the user has set it to something sane, i.e. <= # training batches, + # and don't change it. Otherwise, adjust batches accordingly if it's a float (including 1.0). + if self._trainer is not None and isinstance(self._trainer.limit_train_batches, float): + self._trainer.limit_train_batches = int( + self._trainer.limit_train_batches + * ceil((len(self._train_dl.dataset) / self.world_size) / train_data_config['batch_size']) + ) + elif self._trainer is None: + logging.warning( + "Model Trainer was not set before constructing the dataset, incorrect number of " + "training batches will be used. Please set the trainer and rebuild the dataset." + ) + + def setup_validation_data(self, val_data_config: Optional[Union[DictConfig, Dict]]): + """ + Sets up the validation data loader via a Dict-like object. + + Args: + val_data_config: A config that contains the information regarding construction + of an ASR Training dataset. + + Supported Datasets: + - :class:`~nemo.collections.multimodal.speech_cv.data.video_to_text.VideoToCharDataset` + - :class:`~nemo.collections.asr.data.video_to_text.VideoToBPEDataset` + - :class:`~nemo.collections.asr.data.video_to_text.TarredVideoToBPEDataset` + """ + if 'shuffle' not in val_data_config: + val_data_config['shuffle'] = False + + # preserve config + self._update_dataset_config(dataset_name='validation', config=val_data_config) + + self._validation_dl = self._setup_dataloader_from_config(config=val_data_config) + + def setup_test_data(self, test_data_config: Optional[Union[DictConfig, Dict]]): + """ + Sets up the test data loader via a Dict-like object. + + Args: + test_data_config: A config that contains the information regarding construction + of an ASR Training dataset. + + Supported Datasets: + - :class:`~nemo.collections.multimodal.speech_cv.data.video_to_text.VideoToCharDataset` + - :class:`~nemo.collections.asr.data.video_to_text.VideoToBPEDataset` + - :class:`~nemo.collections.asr.data.video_to_text.TarredVideoToBPEDataset` + """ + if 'shuffle' not in test_data_config: + test_data_config['shuffle'] = False + + # preserve config + self._update_dataset_config(dataset_name='test', config=test_data_config) + + self._test_dl = self._setup_dataloader_from_config(config=test_data_config) + + @property + def input_types(self) -> Optional[Dict[str, NeuralType]]: + return { + "input_video_signal": NeuralType(('B', 'C', 'T', 'H', 'W'), VideoSignal(), optional=True), + "input_video_signal_length": NeuralType(tuple('B'), LengthsType(), optional=True), + "sample_id": NeuralType(tuple('B'), LengthsType(), optional=True), + } + + @property + def output_types(self) -> Optional[Dict[str, NeuralType]]: + return { + "outputs": NeuralType(('B', 'T', 'D'), LogprobsType()), + "encoded_lengths": NeuralType(tuple('B'), LengthsType()), + "greedy_predictions": NeuralType(('B', 'T'), LabelsType()), + } + + @typecheck() + def forward(self, input_video_signal=None, input_video_signal_length=None): + """ + Forward pass of the model. + + Args: + input_video_signal: Tensor that represents a batch of video signals, + of shape [B, T, H, W, C]. T here represents timesteps, H height, W width and C channels + input_video_signal_length: Vector of length B, that contains the individual lengths of the video + sequences. + + Returns: + A tuple of 3 elements - + 1) The log probabilities tensor of shape [B, T, D]. + 2) The lengths of the acoustic sequence after propagation through the encoder, of shape [B]. + 3) The greedy token predictions of the model of shape [B, T] (via argmax) + """ + + # Preprocessing + processed_video_signal, processed_video_signal_length = self.video_preprocessor( + input_signal=input_video_signal, length=input_video_signal_length + ) + + # Augmentation + processed_video_signal = self.video_augmentation( + input_signal=processed_video_signal, length=processed_video_signal_length + ) + + # Front-end Networks + processed_video_signal, processed_video_signal_length = self.video_front_end( + input_signal=processed_video_signal, length=processed_video_signal_length + ) + + # Back-end Networks + encoded, encoded_len = self.encoder(audio_signal=processed_video_signal, length=processed_video_signal_length) + + log_probs = self.decoder(encoder_output=encoded) + greedy_predictions = log_probs.argmax(dim=-1, keepdim=False) + + return ( + log_probs, + encoded_len, + greedy_predictions, + ) + + # PTL-specific methods + def training_step(self, batch, batch_nb): + # Reset access registry + if AccessMixin.is_access_enabled(): + AccessMixin.reset_registry(self) + + if self.is_interctc_enabled(): + AccessMixin.set_access_enabled(access_enabled=True) + + video_signal, video_signal_len, transcript, transcript_len = batch + log_probs, encoded_len, predictions = self.forward( + input_video_signal=video_signal, input_video_signal_length=video_signal_len + ) + + if hasattr(self, '_trainer') and self._trainer is not None: + log_every_n_steps = self._trainer.log_every_n_steps + else: + log_every_n_steps = 1 + + loss_value = self.loss( + log_probs=log_probs, targets=transcript, input_lengths=encoded_len, target_lengths=transcript_len + ) + + # Add auxiliary losses, if registered + loss_value = self.add_auxiliary_losses(loss_value) + # only computing WER when requested in the logs (same as done for final-layer WER below) + loss_value, tensorboard_logs = self.add_interctc_losses( + loss_value, transcript, transcript_len, compute_wer=((batch_nb + 1) % log_every_n_steps == 0) + ) + + # Reset access registry + if AccessMixin.is_access_enabled(): + AccessMixin.reset_registry(self) + + tensorboard_logs.update( + { + 'train_loss': loss_value, + 'learning_rate': self._optimizer.param_groups[0]['lr'], + 'global_step': torch.tensor(self.trainer.global_step, dtype=torch.float32), + } + ) + + if (batch_nb + 1) % log_every_n_steps == 0: + self._wer.update( + predictions=log_probs, + targets=transcript, + target_lengths=transcript_len, + predictions_lengths=encoded_len, + ) + wer, _, _ = self._wer.compute() + self._wer.reset() + tensorboard_logs.update({'training_batch_wer': wer}) + + return {'loss': loss_value, 'log': tensorboard_logs} + + def predict_step(self, batch, batch_idx, dataloader_idx=0): + video_signal, video_signal_len, transcript, transcript_len, sample_id = batch + log_probs, encoded_len, predictions = self.forward( + input_video_signal=video_signal, input_video_signal_length=video_signal_len + ) + + transcribed_texts, _ = self._wer.decoding.ctc_decoder_predictions_tensor( + decoder_outputs=log_probs, decoder_lengths=encoded_len, return_hypotheses=False, + ) + + sample_id = sample_id.cpu().detach().numpy() + return list(zip(sample_id, transcribed_texts)) + + def validation_step(self, batch, batch_idx, dataloader_idx=0): + if self.is_interctc_enabled(): + AccessMixin.set_access_enabled(access_enabled=True) + + video_signal, video_signal_len, transcript, transcript_len = batch + log_probs, encoded_len, predictions = self.forward( + input_video_signal=video_signal, input_video_signal_length=video_signal_len + ) + + loss_value = self.loss( + log_probs=log_probs, targets=transcript, input_lengths=encoded_len, target_lengths=transcript_len + ) + loss_value, metrics = self.add_interctc_losses( + loss_value, transcript, transcript_len, compute_wer=True, log_wer_num_denom=True, log_prefix="val_", + ) + + self._wer.update( + predictions=log_probs, targets=transcript, target_lengths=transcript_len, predictions_lengths=encoded_len + ) + wer, wer_num, wer_denom = self._wer.compute() + self._wer.reset() + metrics.update({'val_loss': loss_value, 'val_wer_num': wer_num, 'val_wer_denom': wer_denom, 'val_wer': wer}) + + self.log('global_step', torch.tensor(self.trainer.global_step, dtype=torch.float32)) + + # Reset access registry + if AccessMixin.is_access_enabled(): + AccessMixin.reset_registry(self) + + return metrics + + def multi_validation_epoch_end(self, outputs, dataloader_idx: int = 0): + metrics = super().multi_validation_epoch_end(outputs, dataloader_idx) + self.finalize_interctc_metrics(metrics, outputs, prefix="val_") + return metrics + + def multi_test_epoch_end(self, outputs, dataloader_idx: int = 0): + metrics = super().multi_test_epoch_end(outputs, dataloader_idx) + self.finalize_interctc_metrics(metrics, outputs, prefix="test_") + return metrics + + def test_step(self, batch, batch_idx, dataloader_idx=0): + logs = self.validation_step(batch, batch_idx, dataloader_idx=dataloader_idx) + test_logs = {name.replace("val_", "test_"): value for name, value in logs.items()} + if type(self.trainer.test_dataloaders) == list and len(self.trainer.test_dataloaders) > 1: + self.test_step_outputs[dataloader_idx].append(test_logs) + else: + self.test_step_outputs.append(test_logs) + return test_logs + + def test_dataloader(self): + if self._test_dl is not None: + return self._test_dl + + def _setup_transcribe_dataloader(self, config: Dict) -> 'torch.utils.data.DataLoader': + """ + Setup function for a temporary data loader which wraps the provided video file. + + Args: + config: A python dictionary which contains the following keys: + paths2video_files: (a list) of paths to video files. The files should be relatively short fragments. \ + Recommended length per file is between 5 and 25 seconds. + batch_size: (int) batch size to use during inference. \ + Bigger will result in better throughput performance but would use more memory. + temp_dir: (str) A temporary directory where the video manifest is temporarily + stored. + num_workers: (int) number of workers. Depends of the batch_size and machine. \ + 0 - only the main process will load batches, 1 - one worker (not main process) + + Returns: + A pytorch DataLoader for the given video file(s). + """ + if 'manifest_filepath' in config: + manifest_filepath = config['manifest_filepath'] + batch_size = config['batch_size'] + else: + manifest_filepath = os.path.join(config['temp_dir'], 'manifest.json') + batch_size = min(config['batch_size'], len(config['paths2video_files'])) + + dl_config = { + 'manifest_filepath': manifest_filepath, + 'labels': self.decoder.vocabulary, + 'batch_size': batch_size, + 'trim_silence': False, + 'shuffle': False, + 'num_workers': config.get('num_workers', min(batch_size, os.cpu_count() - 1)), + 'pin_memory': True, + 'channel_selector': config.get('channel_selector', None), + } + if config.get("augmentor"): + dl_config['augmentor'] = config.get("augmentor") + + temporary_datalayer = self._setup_dataloader_from_config(config=DictConfig(dl_config)) + return temporary_datalayer + + @classmethod + def list_available_models(cls) -> List[PretrainedModelInfo]: + """ + This method returns a list of pre-trained model which can be instantiated directly from NVIDIA's NGC cloud. + + Returns: + List of available pre-trained models. + """ + results = [] + + return results diff --git a/nemo/collections/multimodal/speech_cv/models/visual_hybrid_rnnt_ctc_bpe_models.py b/nemo/collections/multimodal/speech_cv/models/visual_hybrid_rnnt_ctc_bpe_models.py new file mode 100644 index 000000000000..882d15700593 --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/models/visual_hybrid_rnnt_ctc_bpe_models.py @@ -0,0 +1,455 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import os +from typing import Dict, Optional, Union + +import torch +from omegaconf import DictConfig, ListConfig, OmegaConf, open_dict +from pytorch_lightning import Trainer + +from nemo.collections.asr.losses.ctc import CTCLoss +from nemo.collections.asr.losses.rnnt import RNNTLoss +from nemo.collections.asr.metrics.rnnt_wer_bpe import RNNTBPEWER, RNNTBPEDecoding, RNNTBPEDecodingConfig +from nemo.collections.asr.metrics.wer_bpe import WERBPE, CTCBPEDecoding, CTCBPEDecodingConfig +from nemo.collections.asr.parts.mixins import ASRBPEMixin +from nemo.collections.multimodal.speech_cv.data import video_to_text_dataset +from nemo.collections.multimodal.speech_cv.models.visual_hybrid_rnnt_ctc_models import VisualEncDecHybridRNNTCTCModel +from nemo.core.classes.common import PretrainedModelInfo +from nemo.utils import logging, model_utils + + +class VisualEncDecHybridRNNTCTCBPEModel(VisualEncDecHybridRNNTCTCModel, ASRBPEMixin): + """Base class for encoder decoder RNNT-based models with auxiliary CTC decoder/loss and subword tokenization.""" + + def __init__(self, cfg: DictConfig, trainer: Trainer = None): + # Convert to Hydra 1.0 compatible DictConfig + cfg = model_utils.convert_model_config_to_dict_config(cfg) + cfg = model_utils.maybe_update_config_version(cfg) + + # Tokenizer is necessary for this model + if 'tokenizer' not in cfg: + raise ValueError("`cfg` must have `tokenizer` config to create a tokenizer !") + + if not isinstance(cfg, DictConfig): + cfg = OmegaConf.create(cfg) + + # Setup the tokenizer + self._setup_tokenizer(cfg.tokenizer) + + # Initialize a dummy vocabulary + vocabulary = self.tokenizer.tokenizer.get_vocab() + + # Set the new vocabulary + with open_dict(cfg): + cfg.labels = ListConfig(list(vocabulary)) + + with open_dict(cfg.decoder): + cfg.decoder.vocab_size = len(vocabulary) + + with open_dict(cfg.joint): + cfg.joint.num_classes = len(vocabulary) + cfg.joint.vocabulary = ListConfig(list(vocabulary)) + cfg.joint.jointnet.encoder_hidden = cfg.model_defaults.enc_hidden + cfg.joint.jointnet.pred_hidden = cfg.model_defaults.pred_hidden + + # setup auxiliary CTC decoder + if 'aux_ctc' not in cfg: + raise ValueError( + "The config need to have a section for the CTC decoder named as aux_ctc for Hybrid models." + ) + + with open_dict(cfg): + if self.tokenizer_type == "agg": + cfg.aux_ctc.decoder.vocabulary = ListConfig(vocabulary) + else: + cfg.aux_ctc.decoder.vocabulary = ListConfig(list(vocabulary.keys())) + + if cfg.aux_ctc.decoder["num_classes"] < 1: + logging.info( + "\nReplacing placholder number of classes ({}) with actual number of classes - {}".format( + cfg.aux_ctc.decoder["num_classes"], len(vocabulary) + ) + ) + cfg.aux_ctc.decoder["num_classes"] = len(vocabulary) + + super().__init__(cfg=cfg, trainer=trainer) + + # Setup decoding object + self.decoding = RNNTBPEDecoding( + decoding_cfg=self.cfg.decoding, decoder=self.decoder, joint=self.joint, tokenizer=self.tokenizer, + ) + + # Setup wer object + self.wer = RNNTBPEWER( + decoding=self.decoding, + batch_dim_index=0, + use_cer=self.cfg.get('use_cer', False), + log_prediction=self.cfg.get('log_prediction', True), + dist_sync_on_step=True, + ) + + # Setup fused Joint step if flag is set + if self.joint.fuse_loss_wer: + self.joint.set_loss(self.loss) + self.joint.set_wer(self.wer) + + # Setup CTC decoding + ctc_decoding_cfg = self.cfg.aux_ctc.get('decoding', None) + if ctc_decoding_cfg is None: + ctc_decoding_cfg = OmegaConf.structured(CTCBPEDecodingConfig) + with open_dict(self.cfg.aux_ctc): + self.cfg.aux_ctc.decoding = ctc_decoding_cfg + self.ctc_decoding = CTCBPEDecoding(self.cfg.aux_ctc.decoding, tokenizer=self.tokenizer) + + # Setup CTC WER + self.ctc_wer = WERBPE( + decoding=self.ctc_decoding, + use_cer=self.cfg.aux_ctc.get('use_cer', False), + dist_sync_on_step=True, + log_prediction=self.cfg.get("log_prediction", False), + ) + + # setting the RNNT decoder as the default one + self.use_rnnt_decoder = True + + def _setup_dataloader_from_config(self, config: Optional[Dict]): + dataset = video_to_text_dataset.get_video_to_text_bpe_dataset_from_config( + config=config, + local_rank=self.local_rank, + global_rank=self.global_rank, + world_size=self.world_size, + tokenizer=self.tokenizer, + preprocessor_cfg=self.cfg.get("preprocessor", None), + ) + + if dataset is None: + return None + + shuffle = config['shuffle'] + if config.get('is_tarred', False): + shuffle = False + + if hasattr(dataset, 'collate_fn'): + collate_fn = dataset.collate_fn + else: + collate_fn = dataset.datasets[0].collate_fn + + return torch.utils.data.DataLoader( + dataset=dataset, + batch_size=config['batch_size'], + collate_fn=collate_fn, + drop_last=config.get('drop_last', False), + shuffle=shuffle, + num_workers=config.get('num_workers', 0), + pin_memory=config.get('pin_memory', False), + ) + + def _setup_transcribe_dataloader(self, config: Dict) -> 'torch.utils.data.DataLoader': + """ + Setup function for a temporary data loader which wraps the provided video file. + + Args: + config: A python dictionary which contains the following keys: + paths2video_files: (a list) of paths to video files. The files should be relatively short fragments. \ + Recommended length per file is between 5 and 25 seconds. + batch_size: (int) batch size to use during inference. \ + Bigger will result in better throughput performance but would use more memory. + temp_dir: (str) A temporary directory where the video manifest is temporarily + stored. + num_workers: (int) number of workers. Depends of the batch_size and machine. \ + 0 - only the main process will load batches, 1 - one worker (not main process) + + Returns: + A pytorch DataLoader for the given video file(s). + """ + + if 'manifest_filepath' in config: + manifest_filepath = config['manifest_filepath'] + batch_size = config['batch_size'] + else: + manifest_filepath = os.path.join(config['temp_dir'], 'manifest.json') + batch_size = min(config['batch_size'], len(config['paths2video_files'])) + + dl_config = { + 'manifest_filepath': manifest_filepath, + 'batch_size': batch_size, + 'shuffle': False, + 'num_workers': config.get('num_workers', min(batch_size, os.cpu_count() - 1)), + 'pin_memory': True, + 'channel_selector': config.get('channel_selector', None), + 'use_start_end_token': self.cfg.validation_ds.get('use_start_end_token', False), + } + + if config.get("augmentor"): + dl_config['augmentor'] = config.get("augmentor") + + temporary_datalayer = self._setup_dataloader_from_config(config=DictConfig(dl_config)) + return temporary_datalayer + + def change_vocabulary( + self, + new_tokenizer_dir: Union[str, DictConfig], + new_tokenizer_type: str, + decoding_cfg: Optional[DictConfig] = None, + ctc_decoding_cfg: Optional[DictConfig] = None, + ): + """ + Changes vocabulary used during RNNT decoding process. Use this method when fine-tuning on from pre-trained model. + This method changes only decoder and leaves encoder and pre-processing modules unchanged. For example, you would + use it if you want to use pretrained encoder when fine-tuning on data in another language, or when you'd need + model to learn capitalization, punctuation and/or special characters. + + Args: + new_tokenizer_dir: Directory path to tokenizer or a config for a new tokenizer (if the tokenizer type is `agg`) + new_tokenizer_type: Type of tokenizer. Can be either `agg`, `bpe` or `wpe`. + decoding_cfg: A config for the decoder, which is optional. If the decoding type + needs to be changed (from say Greedy to Beam decoding etc), the config can be passed here. + ctc_decoding_cfg: A config for auxiliary CTC decoding, which is optional and can be used to change the decoding type. + + Returns: None + + """ + if isinstance(new_tokenizer_dir, DictConfig): + if new_tokenizer_type == 'agg': + new_tokenizer_cfg = new_tokenizer_dir + else: + raise ValueError( + f'New tokenizer dir should be a string unless the tokenizer is `agg`, but this tokenizer type is: {new_tokenizer_type}' + ) + else: + new_tokenizer_cfg = None + + if new_tokenizer_cfg is not None: + tokenizer_cfg = new_tokenizer_cfg + else: + if not os.path.isdir(new_tokenizer_dir): + raise NotADirectoryError( + f'New tokenizer dir must be non-empty path to a directory. But I got: {new_tokenizer_dir}' + ) + + if new_tokenizer_type.lower() not in ('bpe', 'wpe'): + raise ValueError(f'New tokenizer type must be either `bpe` or `wpe`') + + tokenizer_cfg = OmegaConf.create({'dir': new_tokenizer_dir, 'type': new_tokenizer_type}) + + # Setup the tokenizer + self._setup_tokenizer(tokenizer_cfg) + + # Initialize a dummy vocabulary + vocabulary = self.tokenizer.tokenizer.get_vocab() + + joint_config = self.joint.to_config_dict() + new_joint_config = copy.deepcopy(joint_config) + if self.tokenizer_type == "agg": + new_joint_config["vocabulary"] = ListConfig(vocabulary) + else: + new_joint_config["vocabulary"] = ListConfig(list(vocabulary.keys())) + + new_joint_config['num_classes'] = len(vocabulary) + del self.joint + self.joint = VisualEncDecHybridRNNTCTCBPEModel.from_config_dict(new_joint_config) + + decoder_config = self.decoder.to_config_dict() + new_decoder_config = copy.deepcopy(decoder_config) + new_decoder_config.vocab_size = len(vocabulary) + del self.decoder + self.decoder = VisualEncDecHybridRNNTCTCBPEModel.from_config_dict(new_decoder_config) + + del self.loss + self.loss = RNNTLoss(num_classes=self.joint.num_classes_with_blank - 1) + + if decoding_cfg is None: + # Assume same decoding config as before + decoding_cfg = self.cfg.decoding + + # Assert the decoding config with all hyper parameters + decoding_cls = OmegaConf.structured(RNNTBPEDecodingConfig) + decoding_cls = OmegaConf.create(OmegaConf.to_container(decoding_cls)) + decoding_cfg = OmegaConf.merge(decoding_cls, decoding_cfg) + + self.decoding = RNNTBPEDecoding( + decoding_cfg=decoding_cfg, decoder=self.decoder, joint=self.joint, tokenizer=self.tokenizer, + ) + + self.wer = RNNTBPEWER( + decoding=self.decoding, + batch_dim_index=self.wer.batch_dim_index, + use_cer=self.wer.use_cer, + log_prediction=self.wer.log_prediction, + dist_sync_on_step=True, + ) + + # Setup fused Joint step + if self.joint.fuse_loss_wer or ( + self.decoding.joint_fused_batch_size is not None and self.decoding.joint_fused_batch_size > 0 + ): + self.joint.set_loss(self.loss) + self.joint.set_wer(self.wer) + + # Update config + with open_dict(self.cfg.joint): + self.cfg.joint = new_joint_config + + with open_dict(self.cfg.decoder): + self.cfg.decoder = new_decoder_config + + with open_dict(self.cfg.decoding): + self.cfg.decoding = decoding_cfg + + logging.info(f"Changed tokenizer of the RNNT decoder to {self.joint.vocabulary} vocabulary.") + + # set up the new tokenizer for the CTC decoder + if hasattr(self, 'ctc_decoder'): + ctc_decoder_config = copy.deepcopy(self.ctc_decoder.to_config_dict()) + # sidestepping the potential overlapping tokens issue in aggregate tokenizers + if self.tokenizer_type == "agg": + ctc_decoder_config.vocabulary = ListConfig(vocabulary) + else: + ctc_decoder_config.vocabulary = ListConfig(list(vocabulary.keys())) + + decoder_num_classes = ctc_decoder_config['num_classes'] + # Override number of classes if placeholder provided + logging.info( + "\nReplacing old number of classes ({}) with new number of classes - {}".format( + decoder_num_classes, len(vocabulary) + ) + ) + ctc_decoder_config['num_classes'] = len(vocabulary) + + del self.ctc_decoder + self.ctc_decoder = VisualEncDecHybridRNNTCTCBPEModel.from_config_dict(ctc_decoder_config) + del self.ctc_loss + self.ctc_loss = CTCLoss( + num_classes=self.ctc_decoder.num_classes_with_blank - 1, + zero_infinity=True, + reduction=self.cfg.aux_ctc.get("ctc_reduction", "mean_batch"), + ) + + if ctc_decoding_cfg is None: + # Assume same decoding config as before + ctc_decoding_cfg = self.cfg.aux_ctc.decoding + + # Assert the decoding config with all hyper parameters + ctc_decoding_cls = OmegaConf.structured(CTCBPEDecodingConfig) + ctc_decoding_cls = OmegaConf.create(OmegaConf.to_container(ctc_decoding_cls)) + ctc_decoding_cfg = OmegaConf.merge(ctc_decoding_cls, ctc_decoding_cfg) + + self.ctc_decoding = CTCBPEDecoding(decoding_cfg=ctc_decoding_cfg, tokenizer=self.tokenizer) + + self.ctc_wer = WERBPE( + decoding=self.ctc_decoding, + use_cer=self.cfg.aux_ctc.get('use_cer', False), + log_prediction=self.cfg.get("log_prediction", False), + dist_sync_on_step=True, + ) + + # Update config + with open_dict(self.cfg.aux_ctc): + self.cfg.aux_ctc.decoder = ctc_decoder_config + + with open_dict(self.cfg.aux_ctc): + self.cfg.aux_ctc.decoding = ctc_decoding_cfg + + logging.info(f"Changed tokenizer of the CTC decoder to {self.ctc_decoder.vocabulary} vocabulary.") + + def change_decoding_strategy(self, decoding_cfg: DictConfig, decoder_type: str = None): + """ + Changes decoding strategy used during RNNT decoding process. + Args: + decoding_cfg: A config for the decoder, which is optional. If the decoding type + needs to be changed (from say Greedy to Beam decoding etc), the config can be passed here. + decoder_type: (str) Can be set to 'rnnt' or 'ctc' to switch between appropriate decoder in a + model having both RNN-T and CTC decoders. Defaults to None, in which case RNN-T decoder is + used. If set to 'ctc', it raises error if 'ctc_decoder' is not an attribute of the model. + """ + if decoder_type is None or decoder_type == 'rnnt': + if decoding_cfg is None: + # Assume same decoding config as before + logging.info("No `decoding_cfg` passed when changing decoding strategy, using internal config") + decoding_cfg = self.cfg.decoding + + # Assert the decoding config with all hyper parameters + decoding_cls = OmegaConf.structured(RNNTBPEDecodingConfig) + decoding_cls = OmegaConf.create(OmegaConf.to_container(decoding_cls)) + decoding_cfg = OmegaConf.merge(decoding_cls, decoding_cfg) + + self.decoding = RNNTBPEDecoding( + decoding_cfg=decoding_cfg, decoder=self.decoder, joint=self.joint, tokenizer=self.tokenizer, + ) + + self.wer = RNNTBPEWER( + decoding=self.decoding, + batch_dim_index=self.wer.batch_dim_index, + use_cer=self.wer.use_cer, + log_prediction=self.wer.log_prediction, + dist_sync_on_step=True, + ) + + # Setup fused Joint step + if self.joint.fuse_loss_wer or ( + self.decoding.joint_fused_batch_size is not None and self.decoding.joint_fused_batch_size > 0 + ): + self.joint.set_loss(self.loss) + self.joint.set_wer(self.wer) + + # Update config + with open_dict(self.cfg.decoding): + self.cfg.decoding = decoding_cfg + + logging.info(f"Changed decoding strategy of the RNNT decoder to \n{OmegaConf.to_yaml(self.cfg.decoding)}") + elif decoder_type == 'ctc': + if not hasattr(self, 'ctc_decoding'): + raise ValueError("The model does not have the ctc_decoding module and does not support ctc decoding.") + if decoding_cfg is None: + # Assume same decoding config as before + logging.info("No `decoding_cfg` passed when changing decoding strategy, using internal config") + decoding_cfg = self.cfg.aux_ctc.decoding + + # Assert the decoding config with all hyper parameters + decoding_cls = OmegaConf.structured(CTCBPEDecodingConfig) + decoding_cls = OmegaConf.create(OmegaConf.to_container(decoding_cls)) + decoding_cfg = OmegaConf.merge(decoding_cls, decoding_cfg) + + self.ctc_decoding = CTCBPEDecoding(decoding_cfg=decoding_cfg, tokenizer=self.tokenizer) + + self.ctc_wer = WERBPE( + decoding=self.ctc_decoding, + use_cer=self.ctc_wer.use_cer, + log_prediction=self.ctc_wer.log_prediction, + dist_sync_on_step=True, + ) + + # Update config + with open_dict(self.cfg.aux_ctc.decoding): + self.cfg.aux_ctc.decoding = decoding_cfg + + self.use_rnnt_decoder = False + logging.info( + f"Changed decoding strategy of the CTC decoder to \n{OmegaConf.to_yaml(self.cfg.aux_ctc.decoding)}" + ) + else: + raise ValueError(f"decoder_type={decoder_type} is not supported. Supported values: [ctc,rnnt]") + + @classmethod + def list_available_models(cls) -> Optional[PretrainedModelInfo]: + """ + This method returns a list of pre-trained model which can be instantiated directly from NVIDIA's NGC cloud. + + Returns: + List of available pre-trained models. + """ + results = [] + return results diff --git a/nemo/collections/multimodal/speech_cv/models/visual_hybrid_rnnt_ctc_models.py b/nemo/collections/multimodal/speech_cv/models/visual_hybrid_rnnt_ctc_models.py new file mode 100644 index 000000000000..7c658f2a18c6 --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/models/visual_hybrid_rnnt_ctc_models.py @@ -0,0 +1,644 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import json +import os +import tempfile +from typing import List, Optional + +import torch +from omegaconf import DictConfig, OmegaConf, open_dict +from pytorch_lightning import Trainer +from tqdm.auto import tqdm + +from nemo.collections.asr.losses.ctc import CTCLoss +from nemo.collections.asr.metrics.wer import WER, CTCDecoding, CTCDecodingConfig +from nemo.collections.asr.parts.mixins import ASRBPEMixin, InterCTCMixin +from nemo.collections.asr.parts.utils.audio_utils import ChannelSelectorType +from nemo.collections.multimodal.speech_cv.models.visual_rnnt_models import VisualEncDecRNNTModel +from nemo.core.classes.common import PretrainedModelInfo +from nemo.core.classes.mixins import AccessMixin +from nemo.utils import logging, model_utils + + +class VisualEncDecHybridRNNTCTCModel(VisualEncDecRNNTModel, ASRBPEMixin, InterCTCMixin): + """Base class for hybrid RNNT/CTC models.""" + + def __init__(self, cfg: DictConfig, trainer: Trainer = None): + cfg = model_utils.convert_model_config_to_dict_config(cfg) + cfg = model_utils.maybe_update_config_version(cfg) + super().__init__(cfg=cfg, trainer=trainer) + + if 'aux_ctc' not in self.cfg: + raise ValueError( + "The config need to have a section for the CTC decoder named as aux_ctc for Hybrid models." + ) + with open_dict(self.cfg.aux_ctc): + if "feat_in" not in self.cfg.aux_ctc.decoder or ( + not self.cfg.aux_ctc.decoder.feat_in and hasattr(self.encoder, '_feat_out') + ): + self.cfg.aux_ctc.decoder.feat_in = self.encoder._feat_out + if "feat_in" not in self.cfg.aux_ctc.decoder or not self.cfg.aux_ctc.decoder.feat_in: + raise ValueError("param feat_in of the decoder's config is not set!") + + if self.cfg.aux_ctc.decoder.num_classes < 1 and self.cfg.aux_ctc.decoder.vocabulary is not None: + logging.info( + "\nReplacing placeholder number of classes ({}) with actual number of classes - {}".format( + self.cfg.aux_ctc.decoder.num_classes, len(self.cfg.aux_ctc.decoder.vocabulary) + ) + ) + self.cfg.aux_ctc.decoder["num_classes"] = len(self.cfg.aux_ctc.decoder.vocabulary) + + self.ctc_decoder = VisualEncDecHybridRNNTCTCModel.from_config_dict(self.cfg.aux_ctc.decoder) + self.ctc_loss_weight = self.cfg.aux_ctc.get("ctc_loss_weight", 0.5) + + self.ctc_loss = CTCLoss( + num_classes=self.ctc_decoder.num_classes_with_blank - 1, + zero_infinity=True, + reduction=self.cfg.aux_ctc.get("ctc_reduction", "mean_batch"), + ) + + ctc_decoding_cfg = self.cfg.aux_ctc.get('decoding', None) + if ctc_decoding_cfg is None: + ctc_decoding_cfg = OmegaConf.structured(CTCDecodingConfig) + with open_dict(self.cfg.aux_ctc): + self.cfg.aux_ctc.decoding = ctc_decoding_cfg + + self.ctc_decoding = CTCDecoding(self.cfg.aux_ctc.decoding, vocabulary=self.ctc_decoder.vocabulary) + self.ctc_wer = WER( + decoding=self.ctc_decoding, + use_cer=self.cfg.aux_ctc.get('use_cer', False), + dist_sync_on_step=True, + log_prediction=self.cfg.get("log_prediction", False), + ) + + # setting the RNNT decoder as the default one + self.use_rnnt_decoder = True + + # setting up interCTC loss (from InterCTCMixin) + self.setup_interctc(decoder_name='decoder', loss_name='loss', wer_name='_wer') + + @torch.no_grad() + def transcribe( + self, + paths2video_files: List[str], + batch_size: int = 4, + return_hypotheses: bool = False, + partial_hypothesis: Optional[List['Hypothesis']] = None, + num_workers: int = 0, + channel_selector: Optional[ChannelSelectorType] = None, + ) -> (List[str], Optional[List['Hypothesis']]): + """ + Uses greedy decoding to transcribe video files. Use this method for debugging and prototyping. + + Args: + + paths2video_files: (a list) of paths to video files. + batch_size: (int) batch size to use during inference. \ + Bigger will result in better throughput performance but would use more memory. + return_hypotheses: (bool) Either return hypotheses or text + With hypotheses can do some postprocessing like getting timestamp or rescoring + num_workers: (int) number of workers for DataLoader + channel_selector (int | Iterable[int] | str): select a single channel or a subset of channels from multi-channel audio. If set to `'average'`, it performs averaging across channels. Disabled if set to `None`. Defaults to `None`. Uses zero-based indexing. + + Returns: + Returns a tuple of 2 items - + * A list of greedy transcript texts / Hypothesis + * An optional list of beam search transcript texts / Hypothesis / NBestHypothesis. + """ + if self.use_rnnt_decoder: + return super().transcribe( + paths2video_files=paths2video_files, + batch_size=batch_size, + return_hypotheses=return_hypotheses, + partial_hypothesis=partial_hypothesis, + num_workers=num_workers, + channel_selector=channel_selector, + ) + + if paths2video_files is None or len(paths2video_files) == 0: + return {} + # We will store transcriptions here + hypotheses = [] + all_hypotheses = [] + # Model's mode and device + mode = self.training + device = next(self.parameters()).device + + if num_workers is None: + num_workers = min(batch_size, os.cpu_count() - 1) + + try: + + # Switch model to evaluation mode + self.eval() + # Freeze the visual front-end, encoder and decoder modules + self.video_front_end.freeze() + self.encoder.freeze() + self.decoder.freeze() + self.joint.freeze() + if hasattr(self, 'ctc_decoder'): + self.ctc_decoder.freeze() + + logging_level = logging.get_verbosity() + logging.set_verbosity(logging.WARNING) + # Work in tmp directory - will store manifest file there + with tempfile.TemporaryDirectory() as tmpdir: + with open(os.path.join(tmpdir, 'manifest.json'), 'w', encoding='utf-8') as fp: + for video_file in paths2video_files: + entry = {'video_filepath': video_file, 'duration': 100000, 'text': ''} + fp.write(json.dumps(entry) + '\n') + + config = { + 'paths2video_files': paths2video_files, + 'batch_size': batch_size, + 'temp_dir': tmpdir, + 'num_workers': num_workers, + 'channel_selector': channel_selector, + } + + temporary_datalayer = self._setup_transcribe_dataloader(config) + for test_batch in tqdm(temporary_datalayer, desc="Transcribing"): + encoded, encoded_len = self.forward( + input_signal=test_batch[0].to(device), input_signal_length=test_batch[1].to(device) + ) + + logits = self.ctc_decoder(encoder_output=encoded) + best_hyp, all_hyp = self.ctc_decoding.ctc_decoder_predictions_tensor( + logits, encoded_len, return_hypotheses=return_hypotheses, + ) + if return_hypotheses: + # dump log probs per file + for idx in range(logits.shape[0]): + best_hyp[idx].y_sequence = logits[idx][: encoded_len[idx]] + if best_hyp[idx].alignments is None: + best_hyp[idx].alignments = best_hyp[idx].y_sequence + del logits + + hypotheses += best_hyp + if all_hyp is not None: + all_hypotheses += all_hyp + else: + all_hypotheses += best_hyp + + del encoded + del test_batch + finally: + # set mode back to its original value + self.train(mode=mode) + + logging.set_verbosity(logging_level) + if mode is True: + self.video_front_end.unfreeze() + self.encoder.unfreeze() + self.decoder.unfreeze() + self.joint.unfreeze() + if hasattr(self, 'ctc_decoder'): + self.ctc_decoder.unfreeze() + return hypotheses, all_hypotheses + + def change_vocabulary( + self, + new_vocabulary: List[str], + decoding_cfg: Optional[DictConfig] = None, + ctc_decoding_cfg: Optional[DictConfig] = None, + ): + """ + Changes vocabulary used during RNNT decoding process. Use this method when fine-tuning a pre-trained model. + This method changes only decoder and leaves encoder and pre-processing modules unchanged. For example, you would + use it if you want to use pretrained encoder when fine-tuning on data in another language, or when you'd need + model to learn capitalization, punctuation and/or special characters. + + Args: + new_vocabulary: list with new vocabulary. Must contain at least 2 elements. Typically, \ + this is target alphabet. + decoding_cfg: A config for the decoder, which is optional. If the decoding type + needs to be changed (from say Greedy to Beam decoding etc), the config can be passed here. + ctc_decoding_cfg: A config for CTC decoding, which is optional and can be used to change decoding type. + + Returns: None + + """ + super().change_vocabulary(new_vocabulary=new_vocabulary, decoding_cfg=decoding_cfg) + + # set up the new tokenizer for the CTC decoder + if hasattr(self, 'ctc_decoder'): + if self.ctc_decoder.vocabulary == new_vocabulary: + logging.warning( + f"Old {self.ctc_decoder.vocabulary} and new {new_vocabulary} match. Not changing anything." + ) + else: + if new_vocabulary is None or len(new_vocabulary) == 0: + raise ValueError(f'New vocabulary must be non-empty list of chars. But I got: {new_vocabulary}') + decoder_config = self.ctc_decoder.to_config_dict() + new_decoder_config = copy.deepcopy(decoder_config) + new_decoder_config['vocabulary'] = new_vocabulary + new_decoder_config['num_classes'] = len(new_vocabulary) + + del self.ctc_decoder + self.ctc_decoder = VisualEncDecHybridRNNTCTCModel.from_config_dict(new_decoder_config) + del self.ctc_loss + self.ctc_loss = CTCLoss( + num_classes=self.ctc_decoder.num_classes_with_blank - 1, + zero_infinity=True, + reduction=self.cfg.aux_ctc.get("ctc_reduction", "mean_batch"), + ) + + if ctc_decoding_cfg is None: + # Assume same decoding config as before + logging.info("No `ctc_decoding_cfg` passed when changing decoding strategy, using internal config") + ctc_decoding_cfg = self.cfg.aux_ctc.decoding + + # Assert the decoding config with all hyper parameters + ctc_decoding_cls = OmegaConf.structured(CTCDecodingConfig) + ctc_decoding_cls = OmegaConf.create(OmegaConf.to_container(ctc_decoding_cls)) + ctc_decoding_cfg = OmegaConf.merge(ctc_decoding_cls, ctc_decoding_cfg) + + self.ctc_decoding = CTCDecoding(decoding_cfg=ctc_decoding_cfg, vocabulary=self.ctc_decoder.vocabulary) + + self.ctc_wer = WER( + decoding=self.ctc_decoding, + use_cer=self.ctc_wer.use_cer, + log_prediction=self.ctc_wer.log_prediction, + dist_sync_on_step=True, + ) + + # Update config + with open_dict(self.cfg.aux_ctc): + self.cfg.aux_ctc.decoding = ctc_decoding_cfg + + with open_dict(self.cfg.aux_ctc): + self.cfg.aux_ctc.decoder = new_decoder_config + + ds_keys = ['train_ds', 'validation_ds', 'test_ds'] + for key in ds_keys: + if key in self.cfg: + with open_dict(self.cfg[key]): + self.cfg[key]['labels'] = OmegaConf.create(new_vocabulary) + + logging.info(f"Changed the tokenizer of the CTC decoder to {self.ctc_decoder.vocabulary} vocabulary.") + + def change_decoding_strategy(self, decoding_cfg: DictConfig, decoder_type: str = None): + """ + Changes decoding strategy used during RNNT decoding process. + + Args: + decoding_cfg: A config for the decoder, which is optional. If the decoding type + needs to be changed (from say Greedy to Beam decoding etc), the config can be passed here. + decoder_type: (str) Can be set to 'rnnt' or 'ctc' to switch between appropriate decoder in a + model having RNN-T and CTC decoders. Defaults to None, in which case RNN-T decoder is + used. If set to 'ctc', it raises error if 'ctc_decoder' is not an attribute of the model. + """ + if decoder_type is None or decoder_type == 'rnnt': + self.use_rnnt_decoder = True + return super().change_decoding_strategy(decoding_cfg=decoding_cfg) + + assert decoder_type == 'ctc' and hasattr(self, 'ctc_decoder') + if decoding_cfg is None: + # Assume same decoding config as before + logging.info("No `decoding_cfg` passed when changing decoding strategy, using internal config") + decoding_cfg = self.cfg.aux_ctc.decoding + + # Assert the decoding config with all hyper parameters + decoding_cls = OmegaConf.structured(CTCDecodingConfig) + decoding_cls = OmegaConf.create(OmegaConf.to_container(decoding_cls)) + decoding_cfg = OmegaConf.merge(decoding_cls, decoding_cfg) + + self.ctc_decoding = CTCDecoding(decoding_cfg=decoding_cfg, vocabulary=self.ctc_decoder.vocabulary) + + self.ctc_wer = WER( + decoding=self.ctc_decoding, + use_cer=self.ctc_wer.use_cer, + log_prediction=self.ctc_wer.log_prediction, + dist_sync_on_step=True, + ) + + # Update config + with open_dict(self.cfg.aux_ctc): + self.cfg.aux_ctc.decoding = decoding_cfg + + self.use_rnnt_decoder = False + logging.info(f"Changed decoding strategy to \n{OmegaConf.to_yaml(self.cfg.aux_ctc.decoding)}") + + # PTL-specific methods + def training_step(self, batch, batch_nb): + # Reset access registry + if AccessMixin.is_access_enabled(): + AccessMixin.reset_registry(self) + + if self.is_interctc_enabled(): + AccessMixin.set_access_enabled(access_enabled=True) + + signal, signal_len, transcript, transcript_len = batch + + # forward() only performs encoder forward + encoded, encoded_len = self.forward(input_signal=signal, input_signal_length=signal_len) + del signal + + # During training, loss must be computed, so decoder forward is necessary + decoder, target_length, states = self.decoder(targets=transcript, target_length=transcript_len) + + if hasattr(self, '_trainer') and self._trainer is not None: + log_every_n_steps = self._trainer.log_every_n_steps + sample_id = self._trainer.global_step + else: + log_every_n_steps = 1 + sample_id = batch_nb + + # If fused Joint-Loss-WER is not used + if not self.joint.fuse_loss_wer: + # Compute full joint and loss + joint = self.joint(encoder_outputs=encoded, decoder_outputs=decoder) + loss_value = self.loss( + log_probs=joint, targets=transcript, input_lengths=encoded_len, target_lengths=target_length + ) + + # Add auxiliary losses, if registered + loss_value = self.add_auxiliary_losses(loss_value) + + # Reset access registry + # if AccessMixin.is_access_enabled(): + # AccessMixin.reset_registry(self) + + tensorboard_logs = { + 'train_loss': loss_value, + 'learning_rate': self._optimizer.param_groups[0]['lr'], + 'global_step': torch.tensor(self.trainer.global_step, dtype=torch.float32), + } + + if (sample_id + 1) % log_every_n_steps == 0: + self.wer.update(encoded, encoded_len, transcript, transcript_len) + _, scores, words = self.wer.compute() + self.wer.reset() + tensorboard_logs.update({'training_batch_wer': scores.float() / words}) + + else: + # If fused Joint-Loss-WER is used + if (sample_id + 1) % log_every_n_steps == 0: + compute_wer = True + else: + compute_wer = False + + # Fused joint step + loss_value, wer, _, _ = self.joint( + encoder_outputs=encoded, + decoder_outputs=decoder, + encoder_lengths=encoded_len, + transcripts=transcript, + transcript_lengths=transcript_len, + compute_wer=compute_wer, + ) + + # Add auxiliary losses, if registered + loss_value = self.add_auxiliary_losses(loss_value) + + # Reset access registry + # if AccessMixin.is_access_enabled(): + # AccessMixin.reset_registry(self) + + tensorboard_logs = { + 'train_loss': loss_value, + 'learning_rate': self._optimizer.param_groups[0]['lr'], + 'global_step': torch.tensor(self.trainer.global_step, dtype=torch.float32), + } + + if compute_wer: + tensorboard_logs.update({'training_batch_wer': wer}) + + if self.ctc_loss_weight > 0: + log_probs = self.ctc_decoder(encoder_output=encoded) + ctc_loss = self.ctc_loss( + log_probs=log_probs, targets=transcript, input_lengths=encoded_len, target_lengths=transcript_len + ) + + # Add Interctc Losses + ctc_loss, interctc_tensorboard_logs = self.add_interctc_losses( + ctc_loss, transcript, transcript_len, compute_wer=((batch_nb + 1) % log_every_n_steps == 0) + ) + tensorboard_logs.update(interctc_tensorboard_logs) + + tensorboard_logs['train_rnnt_loss'] = loss_value + tensorboard_logs['train_ctc_loss'] = ctc_loss + loss_value = (1 - self.ctc_loss_weight) * loss_value + self.ctc_loss_weight * ctc_loss + tensorboard_logs['train_loss'] = loss_value + if (sample_id + 1) % log_every_n_steps == 0: + self.ctc_wer.update( + predictions=log_probs, + targets=transcript, + target_lengths=transcript_len, + predictions_lengths=encoded_len, + ) + ctc_wer, _, _ = self.ctc_wer.compute() + self.ctc_wer.reset() + tensorboard_logs.update({'training_batch_wer_ctc': ctc_wer}) + + # Reset access registry + if AccessMixin.is_access_enabled(): + AccessMixin.reset_registry(self) + + # Log items + self.log_dict(tensorboard_logs) + + # Preserve batch acoustic model T and language model U parameters if normalizing + if self._optim_normalize_joint_txu: + self._optim_normalize_txu = [encoded_len.max(), transcript_len.max()] + + return {'loss': loss_value} + + def predict_step(self, batch, batch_idx, dataloader_idx=0): + # TODO: add support for CTC decoding + signal, signal_len, transcript, transcript_len, sample_id = batch + + # forward() only performs encoder forward + encoded, encoded_len = self.forward(input_signal=signal, input_signal_length=signal_len) + del signal + + best_hyp_text, all_hyp_text = self.decoding.rnnt_decoder_predictions_tensor( + encoder_output=encoded, encoded_lengths=encoded_len, return_hypotheses=False + ) + + sample_id = sample_id.cpu().detach().numpy() + return list(zip(sample_id, best_hyp_text)) + + def validation_step(self, batch, batch_idx, dataloader_idx=0): + if self.is_interctc_enabled(): + AccessMixin.set_access_enabled(access_enabled=True) + + signal, signal_len, transcript, transcript_len = batch + + # forward() only performs encoder forward + encoded, encoded_len = self.forward(input_signal=signal, input_signal_length=signal_len) + del signal + + tensorboard_logs = {} + + # If experimental fused Joint-Loss-WER is not used + if not self.joint.fuse_loss_wer: + if self.compute_eval_loss: + decoder, target_length, states = self.decoder(targets=transcript, target_length=transcript_len) + joint = self.joint(encoder_outputs=encoded, decoder_outputs=decoder) + + loss_value = self.loss( + log_probs=joint, targets=transcript, input_lengths=encoded_len, target_lengths=target_length + ) + + tensorboard_logs['val_loss'] = loss_value + + self.wer.update(encoded, encoded_len, transcript, transcript_len) + wer, wer_num, wer_denom = self.wer.compute() + self.wer.reset() + + tensorboard_logs['val_wer_num'] = wer_num + tensorboard_logs['val_wer_denom'] = wer_denom + tensorboard_logs['val_wer'] = wer + + else: + # If experimental fused Joint-Loss-WER is used + compute_wer = True + + if self.compute_eval_loss: + decoded, target_len, states = self.decoder(targets=transcript, target_length=transcript_len) + else: + decoded = None + target_len = transcript_len + + # Fused joint step + loss_value, wer, wer_num, wer_denom = self.joint( + encoder_outputs=encoded, + decoder_outputs=decoded, + encoder_lengths=encoded_len, + transcripts=transcript, + transcript_lengths=target_len, + compute_wer=compute_wer, + ) + + if loss_value is not None: + tensorboard_logs['val_loss'] = loss_value + + tensorboard_logs['val_wer_num'] = wer_num + tensorboard_logs['val_wer_denom'] = wer_denom + tensorboard_logs['val_wer'] = wer + + log_probs = self.ctc_decoder(encoder_output=encoded) + if self.compute_eval_loss: + ctc_loss = self.ctc_loss( + log_probs=log_probs, targets=transcript, input_lengths=encoded_len, target_lengths=transcript_len + ) + + # Add interCTC losses + ctc_loss, interctc_tensorboard_logs = self.add_interctc_losses( + ctc_loss, transcript, transcript_len, compute_wer=True, log_wer_num_denom=True, log_prefix="val_", + ) + tensorboard_logs.update(interctc_tensorboard_logs) + + tensorboard_logs['val_ctc_loss'] = ctc_loss + tensorboard_logs['val_rnnt_loss'] = loss_value + loss_value = (1 - self.ctc_loss_weight) * loss_value + self.ctc_loss_weight * ctc_loss + tensorboard_logs['val_loss'] = loss_value + self.ctc_wer.update( + predictions=log_probs, targets=transcript, target_lengths=transcript_len, predictions_lengths=encoded_len, + ) + ctc_wer, ctc_wer_num, ctc_wer_denom = self.ctc_wer.compute() + self.ctc_wer.reset() + tensorboard_logs['val_wer_num_ctc'] = ctc_wer_num + tensorboard_logs['val_wer_denom_ctc'] = ctc_wer_denom + tensorboard_logs['val_wer_ctc'] = ctc_wer + + self.log('global_step', torch.tensor(self.trainer.global_step, dtype=torch.float32)) + + # Reset access registry + if AccessMixin.is_access_enabled(): + AccessMixin.reset_registry(self) + + return tensorboard_logs + + def test_step(self, batch, batch_idx, dataloader_idx=0): + logs = self.validation_step(batch, batch_idx, dataloader_idx=dataloader_idx) + test_logs = {name.replace("val_", "test_"): value for name, value in logs.items()} + if type(self.trainer.test_dataloaders) == list and len(self.trainer.test_dataloaders) > 1: + self.test_step_outputs[dataloader_idx].append(test_logs) + else: + self.test_step_outputs.append(test_logs) + return test_logs + + """ + def test_step(self, batch, batch_idx, dataloader_idx=0): + logs = self.validation_step(batch, batch_idx, dataloader_idx=dataloader_idx) + test_logs = { + 'test_wer_num': logs['val_wer_num'], + 'test_wer_denom': logs['val_wer_denom'], + # 'test_wer': logs['val_wer'], + } + if 'val_loss' in logs: + test_logs['test_loss'] = logs['val_loss'] + + if self.ctc_loss_weight > 0: + test_logs['test_wer_num_ctc'] = logs['val_wer_num_ctc'] + test_logs['test_wer_denom_ctc'] = logs['val_wer_denom_ctc'] + if 'val_ctc_loss' in logs: + test_logs['test_ctc_loss'] = logs['val_ctc_loss'] + if 'val_rnnt_loss' in logs: + test_logs['test_rnnt_loss'] = logs['val_rnnt_loss'] + + return test_logs + """ + + def multi_validation_epoch_end(self, outputs, dataloader_idx: int = 0): + if self.compute_eval_loss: + val_loss_mean = torch.stack([x['val_loss'] for x in outputs]).mean() + val_loss_log = {'val_loss': val_loss_mean} + else: + val_loss_log = {} + wer_num = torch.stack([x['val_wer_num'] for x in outputs]).sum() + wer_denom = torch.stack([x['val_wer_denom'] for x in outputs]).sum() + tensorboard_logs = {**val_loss_log, 'val_wer': wer_num.float() / wer_denom} + if self.ctc_loss_weight > 0: + ctc_wer_num = torch.stack([x['val_wer_num_ctc'] for x in outputs]).sum() + ctc_wer_denom = torch.stack([x['val_wer_denom_ctc'] for x in outputs]).sum() + tensorboard_logs['val_wer_ctc'] = ctc_wer_num.float() / ctc_wer_denom + + metrics = {**val_loss_log, 'log': tensorboard_logs} + self.finalize_interctc_metrics(metrics, outputs, prefix="val_") + return metrics + + def multi_test_epoch_end(self, outputs, dataloader_idx: int = 0): + if self.compute_eval_loss: + test_loss_mean = torch.stack([x['test_loss'] for x in outputs]).mean() + test_loss_log = {'test_loss': test_loss_mean} + else: + test_loss_log = {} + wer_num = torch.stack([x['test_wer_num'] for x in outputs]).sum() + wer_denom = torch.stack([x['test_wer_denom'] for x in outputs]).sum() + tensorboard_logs = {**test_loss_log, 'test_wer': wer_num.float() / wer_denom} + + if self.ctc_loss_weight > 0: + ctc_wer_num = torch.stack([x['test_wer_num_ctc'] for x in outputs]).sum() + ctc_wer_denom = torch.stack([x['test_wer_denom_ctc'] for x in outputs]).sum() + tensorboard_logs['test_wer_ctc'] = ctc_wer_num.float() / ctc_wer_denom + + metrics = {**test_loss_log, 'log': tensorboard_logs} + self.finalize_interctc_metrics(metrics, outputs, prefix="test_") + return metrics + + @classmethod + def list_available_models(cls) -> Optional[PretrainedModelInfo]: + """ + This method returns a list of pre-trained model which can be instantiated directly from NVIDIA's NGC cloud. + + Returns: + List of available pre-trained models. + """ + results = [] + return results diff --git a/nemo/collections/multimodal/speech_cv/models/visual_rnnt_bpe_models.py b/nemo/collections/multimodal/speech_cv/models/visual_rnnt_bpe_models.py new file mode 100644 index 000000000000..5c5263b1ce76 --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/models/visual_rnnt_bpe_models.py @@ -0,0 +1,321 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import os +from typing import Dict, List, Optional, Union + +import torch +from omegaconf import DictConfig, ListConfig, OmegaConf, open_dict +from pytorch_lightning import Trainer + +from nemo.collections.asr.losses.rnnt import RNNTLoss +from nemo.collections.asr.metrics.rnnt_wer_bpe import RNNTBPEWER, RNNTBPEDecoding, RNNTBPEDecodingConfig +from nemo.collections.asr.parts.mixins import ASRBPEMixin +from nemo.collections.multimodal.speech_cv.data import video_to_text_dataset +from nemo.collections.multimodal.speech_cv.models.visual_rnnt_models import VisualEncDecRNNTModel +from nemo.core.classes.common import PretrainedModelInfo +from nemo.utils import logging, model_utils + + +class VisualEncDecRNNTBPEModel(VisualEncDecRNNTModel, ASRBPEMixin): + """Base class for encoder decoder RNNT-based models with subword tokenization.""" + + def __init__(self, cfg: DictConfig, trainer: Trainer = None): + # Convert to Hydra 1.0 compatible DictConfig + cfg = model_utils.convert_model_config_to_dict_config(cfg) + cfg = model_utils.maybe_update_config_version(cfg) + + # Tokenizer is necessary for this model + if 'tokenizer' not in cfg: + raise ValueError("`cfg` must have `tokenizer` config to create a tokenizer !") + + if not isinstance(cfg, DictConfig): + cfg = OmegaConf.create(cfg) + + # Setup the tokenizer + self._setup_tokenizer(cfg.tokenizer) + + # Initialize a dummy vocabulary + vocabulary = self.tokenizer.tokenizer.get_vocab() + + # Set the new vocabulary + with open_dict(cfg): + cfg.labels = ListConfig(list(vocabulary)) + + with open_dict(cfg.decoder): + cfg.decoder.vocab_size = len(vocabulary) + + with open_dict(cfg.joint): + cfg.joint.num_classes = len(vocabulary) + cfg.joint.vocabulary = ListConfig(list(vocabulary)) + cfg.joint.jointnet.encoder_hidden = cfg.model_defaults.enc_hidden + cfg.joint.jointnet.pred_hidden = cfg.model_defaults.pred_hidden + + super().__init__(cfg=cfg, trainer=trainer) + + # Setup decoding object + self.decoding = RNNTBPEDecoding( + decoding_cfg=self.cfg.decoding, decoder=self.decoder, joint=self.joint, tokenizer=self.tokenizer, + ) + + # Setup wer object + self.wer = RNNTBPEWER( + decoding=self.decoding, + batch_dim_index=0, + use_cer=self._cfg.get('use_cer', False), + log_prediction=self._cfg.get('log_prediction', True), + dist_sync_on_step=True, + ) + + # Setup fused Joint step if flag is set + if self.joint.fuse_loss_wer: + self.joint.set_loss(self.loss) + self.joint.set_wer(self.wer) + + def change_vocabulary( + self, + new_tokenizer_dir: Union[str, DictConfig], + new_tokenizer_type: str, + decoding_cfg: Optional[DictConfig] = None, + ): + """ + Changes vocabulary used during RNNT decoding process. Use this method when fine-tuning on from pre-trained model. + This method changes only decoder and leaves encoder and pre-processing modules unchanged. For example, you would + use it if you want to use pretrained encoder when fine-tuning on data in another language, or when you'd need + model to learn capitalization, punctuation and/or special characters. + + Args: + new_tokenizer_dir: Directory path to tokenizer or a config for a new tokenizer (if the tokenizer type is `agg`) + new_tokenizer_type: Type of tokenizer. Can be either `agg`, `bpe` or `wpe`. + decoding_cfg: A config for the decoder, which is optional. If the decoding type + needs to be changed (from say Greedy to Beam decoding etc), the config can be passed here. + + Returns: None + + """ + if isinstance(new_tokenizer_dir, DictConfig): + if new_tokenizer_type == 'agg': + new_tokenizer_cfg = new_tokenizer_dir + else: + raise ValueError( + f'New tokenizer dir should be a string unless the tokenizer is `agg`, but this tokenizer type is: {new_tokenizer_type}' + ) + else: + new_tokenizer_cfg = None + + if new_tokenizer_cfg is not None: + tokenizer_cfg = new_tokenizer_cfg + else: + if not os.path.isdir(new_tokenizer_dir): + raise NotADirectoryError( + f'New tokenizer dir must be non-empty path to a directory. But I got: {new_tokenizer_dir}' + ) + + if new_tokenizer_type.lower() not in ('bpe', 'wpe'): + raise ValueError(f'New tokenizer type must be either `bpe` or `wpe`') + + tokenizer_cfg = OmegaConf.create({'dir': new_tokenizer_dir, 'type': new_tokenizer_type}) + + # Setup the tokenizer + self._setup_tokenizer(tokenizer_cfg) + + # Initialize a dummy vocabulary + vocabulary = self.tokenizer.tokenizer.get_vocab() + + joint_config = self.joint.to_config_dict() + new_joint_config = copy.deepcopy(joint_config) + if self.tokenizer_type == "agg": + new_joint_config["vocabulary"] = ListConfig(vocabulary) + else: + new_joint_config["vocabulary"] = ListConfig(list(vocabulary.keys())) + + new_joint_config['num_classes'] = len(vocabulary) + del self.joint + self.joint = VisualEncDecRNNTBPEModel.from_config_dict(new_joint_config) + + decoder_config = self.decoder.to_config_dict() + new_decoder_config = copy.deepcopy(decoder_config) + new_decoder_config.vocab_size = len(vocabulary) + del self.decoder + self.decoder = VisualEncDecRNNTBPEModel.from_config_dict(new_decoder_config) + + del self.loss + self.loss = RNNTLoss(num_classes=self.joint.num_classes_with_blank - 1) + + if decoding_cfg is None: + # Assume same decoding config as before + decoding_cfg = self.cfg.decoding + + # Assert the decoding config with all hyper parameters + decoding_cls = OmegaConf.structured(RNNTBPEDecodingConfig) + decoding_cls = OmegaConf.create(OmegaConf.to_container(decoding_cls)) + decoding_cfg = OmegaConf.merge(decoding_cls, decoding_cfg) + + self.decoding = RNNTBPEDecoding( + decoding_cfg=decoding_cfg, decoder=self.decoder, joint=self.joint, tokenizer=self.tokenizer, + ) + + self.wer = RNNTBPEWER( + decoding=self.decoding, + batch_dim_index=self.wer.batch_dim_index, + use_cer=self.wer.use_cer, + log_prediction=self.wer.log_prediction, + dist_sync_on_step=True, + ) + + # Setup fused Joint step + if self.joint.fuse_loss_wer or ( + self.decoding.joint_fused_batch_size is not None and self.decoding.joint_fused_batch_size > 0 + ): + self.joint.set_loss(self.loss) + self.joint.set_wer(self.wer) + + # Update config + with open_dict(self.cfg.joint): + self.cfg.joint = new_joint_config + + with open_dict(self.cfg.decoder): + self.cfg.decoder = new_decoder_config + + with open_dict(self.cfg.decoding): + self.cfg.decoding = decoding_cfg + + logging.info(f"Changed decoder to output to {self.joint.vocabulary} vocabulary.") + + def change_decoding_strategy(self, decoding_cfg: DictConfig): + """ + Changes decoding strategy used during RNNT decoding process. + + Args: + decoding_cfg: A config for the decoder, which is optional. If the decoding type + needs to be changed (from say Greedy to Beam decoding etc), the config can be passed here. + """ + if decoding_cfg is None: + # Assume same decoding config as before + logging.info("No `decoding_cfg` passed when changing decoding strategy, using internal config") + decoding_cfg = self.cfg.decoding + + # Assert the decoding config with all hyper parameters + decoding_cls = OmegaConf.structured(RNNTBPEDecodingConfig) + decoding_cls = OmegaConf.create(OmegaConf.to_container(decoding_cls)) + decoding_cfg = OmegaConf.merge(decoding_cls, decoding_cfg) + + self.decoding = RNNTBPEDecoding( + decoding_cfg=decoding_cfg, decoder=self.decoder, joint=self.joint, tokenizer=self.tokenizer, + ) + + self.wer = RNNTBPEWER( + decoding=self.decoding, + batch_dim_index=self.wer.batch_dim_index, + use_cer=self.wer.use_cer, + log_prediction=self.wer.log_prediction, + dist_sync_on_step=True, + ) + + # Setup fused Joint step + if self.joint.fuse_loss_wer or ( + self.decoding.joint_fused_batch_size is not None and self.decoding.joint_fused_batch_size > 0 + ): + self.joint.set_loss(self.loss) + self.joint.set_wer(self.wer) + + # Update config + with open_dict(self.cfg.decoding): + self.cfg.decoding = decoding_cfg + + logging.info(f"Changed decoding strategy to \n{OmegaConf.to_yaml(self.cfg.decoding)}") + + def _setup_dataloader_from_config(self, config: Optional[Dict]): + dataset = video_to_text_dataset.get_video_to_text_bpe_dataset_from_config( + config=config, + local_rank=self.local_rank, + global_rank=self.global_rank, + world_size=self.world_size, + tokenizer=self.tokenizer, + preprocessor_cfg=self.cfg.get("preprocessor", None), + ) + + if dataset is None: + return None + + shuffle = config['shuffle'] + if config.get('is_tarred', False): + shuffle = False + + if hasattr(dataset, 'collate_fn'): + collate_fn = dataset.collate_fn + else: + collate_fn = dataset.datasets[0].collate_fn + + return torch.utils.data.DataLoader( + dataset=dataset, + batch_size=config['batch_size'], + collate_fn=collate_fn, + drop_last=config.get('drop_last', False), + shuffle=shuffle, + num_workers=config.get('num_workers', 0), + pin_memory=config.get('pin_memory', False), + ) + + def _setup_transcribe_dataloader(self, config: Dict) -> 'torch.utils.data.DataLoader': + """ + Setup function for a temporary data loader which wraps the provided video file. + + Args: + config: A python dictionary which contains the following keys: + paths2video_files: (a list) of paths to video files. The files should be relatively short fragments. \ + Recommended length per file is between 5 and 25 seconds. + batch_size: (int) batch size to use during inference. \ + Bigger will result in better throughput performance but would use more memory. + temp_dir: (str) A temporary directory where the video manifest is temporarily + stored. + + Returns: + A pytorch DataLoader for the given video file(s). + """ + if 'manifest_filepath' in config: + manifest_filepath = config['manifest_filepath'] + batch_size = config['batch_size'] + else: + manifest_filepath = os.path.join(config['temp_dir'], 'manifest.json') + batch_size = min(config['batch_size'], len(config['paths2video_files'])) + + dl_config = { + 'manifest_filepath': manifest_filepath, + 'batch_size': batch_size, + 'shuffle': False, + 'num_workers': config.get('num_workers', min(batch_size, os.cpu_count() - 1)), + 'pin_memory': True, + 'channel_selector': config.get('channel_selector', None), + 'use_start_end_token': self.cfg.validation_ds.get('use_start_end_token', False), + } + + if config.get("augmentor"): + dl_config['augmentor'] = config.get("augmentor") + + temporary_datalayer = self._setup_dataloader_from_config(config=DictConfig(dl_config)) + return temporary_datalayer + + @classmethod + def list_available_models(cls) -> List[PretrainedModelInfo]: + """ + This method returns a list of pre-trained model which can be instantiated directly from NVIDIA's NGC cloud. + + Returns: + List of available pre-trained models. + """ + results = [] + + return results diff --git a/nemo/collections/multimodal/speech_cv/models/visual_rnnt_models.py b/nemo/collections/multimodal/speech_cv/models/visual_rnnt_models.py new file mode 100644 index 000000000000..0bd3e2fd1563 --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/models/visual_rnnt_models.py @@ -0,0 +1,920 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import json +import os +import tempfile +from math import ceil, isclose +from typing import Dict, List, Optional, Tuple, Union + +import torch +from omegaconf import DictConfig, OmegaConf, open_dict +from pytorch_lightning import Trainer +from tqdm.auto import tqdm + +from nemo.collections.asr.data import audio_to_text_dataset +from nemo.collections.asr.losses.rnnt import RNNTLoss, resolve_rnnt_default_loss_name +from nemo.collections.asr.metrics.rnnt_wer import RNNTWER, RNNTDecoding, RNNTDecodingConfig +from nemo.collections.asr.models.asr_model import ASRModel +from nemo.collections.asr.modules.rnnt import RNNTDecoderJoint +from nemo.collections.asr.parts.mixins import ASRModuleMixin +from nemo.collections.asr.parts.utils.audio_utils import ChannelSelectorType +from nemo.collections.multimodal.speech_cv.data import video_to_text_dataset +from nemo.core.classes import Exportable +from nemo.core.classes.common import PretrainedModelInfo, typecheck +from nemo.core.classes.mixins import AccessMixin +from nemo.core.neural_types import AcousticEncodedRepresentation, LengthsType, NeuralType, VideoSignal +from nemo.utils import logging + + +class VisualEncDecRNNTModel(ASRModel, ASRModuleMixin, Exportable): + """Base class for encoder decoder RNNT-based models.""" + + def __init__(self, cfg: DictConfig, trainer: Trainer = None): + # Get global rank and total number of GPU workers for IterableDataset partitioning, if applicable + # Global_rank and local_rank is set by LightningModule in Lightning 1.2.0 + self.world_size = 1 + if trainer is not None: + self.world_size = trainer.world_size + + super().__init__(cfg=cfg, trainer=trainer) + + # Preprocessors + self.video_preprocessor = VisualEncDecRNNTModel.from_config_dict(self._cfg.video_preprocessor) + + # Augmentations + self.video_augmentation = VisualEncDecRNNTModel.from_config_dict(self._cfg.video_augment) + + # Front-end Networks + self.video_front_end = VisualEncDecRNNTModel.from_config_dict(self._cfg.video_front_end) + + # Back-end Networks + self.encoder = VisualEncDecRNNTModel.from_config_dict(self._cfg.encoder) + + # Update config values required by components dynamically + with open_dict(self.cfg.decoder): + self.cfg.decoder.vocab_size = len(self.cfg.labels) + + with open_dict(self.cfg.joint): + self.cfg.joint.num_classes = len(self.cfg.labels) + self.cfg.joint.vocabulary = self.cfg.labels + self.cfg.joint.jointnet.encoder_hidden = self.cfg.model_defaults.enc_hidden + self.cfg.joint.jointnet.pred_hidden = self.cfg.model_defaults.pred_hidden + + self.decoder = VisualEncDecRNNTModel.from_config_dict(self.cfg.decoder) + self.joint = VisualEncDecRNNTModel.from_config_dict(self.cfg.joint) + + # Setup RNNT Loss + loss_name, loss_kwargs = self.extract_rnnt_loss_cfg(self.cfg.get("loss", None)) + + self.loss = RNNTLoss( + num_classes=self.joint.num_classes_with_blank - 1, + loss_name=loss_name, + loss_kwargs=loss_kwargs, + reduction=self.cfg.get("rnnt_reduction", "mean_batch"), + ) + + # Setup decoding objects + self.decoding = RNNTDecoding( + decoding_cfg=self.cfg.decoding, decoder=self.decoder, joint=self.joint, vocabulary=self.joint.vocabulary, + ) + # Setup WER calculation + self.wer = RNNTWER( + decoding=self.decoding, + batch_dim_index=0, + use_cer=self._cfg.get('use_cer', False), + log_prediction=self._cfg.get('log_prediction', True), + dist_sync_on_step=True, + ) + + # Whether to compute loss during evaluation + if 'compute_eval_loss' in self.cfg: + self.compute_eval_loss = self.cfg.compute_eval_loss + else: + self.compute_eval_loss = True + + # Setup fused Joint step if flag is set + if self.joint.fuse_loss_wer or ( + self.decoding.joint_fused_batch_size is not None and self.decoding.joint_fused_batch_size > 0 + ): + self.joint.set_loss(self.loss) + self.joint.set_wer(self.wer) + + # Setup optimization normalization (if provided in config) + self.setup_optim_normalization() + + # Setup optional Optimization flags + self.setup_optimization_flags() + + # Setup encoder adapters (from ASRAdapterModelMixin) + self.setup_adapters() + + def setup_optim_normalization(self): + """ + Helper method to setup normalization of certain parts of the model prior to the optimization step. + + Supported pre-optimization normalizations are as follows: + + .. code-block:: yaml + + # Variation Noise injection + model: + variational_noise: + std: 0.0 + start_step: 0 + + # Joint - Length normalization + model: + normalize_joint_txu: false + + # Encoder Network - gradient normalization + model: + normalize_encoder_norm: false + + # Decoder / Prediction Network - gradient normalization + model: + normalize_decoder_norm: false + + # Joint - gradient normalization + model: + normalize_joint_norm: false + """ + # setting up the variational noise for the decoder + if hasattr(self.cfg, 'variational_noise'): + self._optim_variational_noise_std = self.cfg['variational_noise'].get('std', 0) + self._optim_variational_noise_start = self.cfg['variational_noise'].get('start_step', 0) + else: + self._optim_variational_noise_std = 0 + self._optim_variational_noise_start = 0 + + # Setup normalized gradients for model joint by T x U scaling factor (joint length normalization) + self._optim_normalize_joint_txu = self.cfg.get('normalize_joint_txu', False) + self._optim_normalize_txu = None + + # Setup normalized encoder norm for model + self._optim_normalize_encoder_norm = self.cfg.get('normalize_encoder_norm', False) + + # Setup normalized decoder norm for model + self._optim_normalize_decoder_norm = self.cfg.get('normalize_decoder_norm', False) + + # Setup normalized joint norm for model + self._optim_normalize_joint_norm = self.cfg.get('normalize_joint_norm', False) + + def extract_rnnt_loss_cfg(self, cfg: Optional[DictConfig]): + """ + Helper method to extract the rnnt loss name, and potentially its kwargs + to be passed. + + Args: + cfg: Should contain `loss_name` as a string which is resolved to a RNNT loss name. + If the default should be used, then `default` can be used. + Optionally, one can pass additional kwargs to the loss function. The subdict + should have a keyname as follows : `{loss_name}_kwargs`. + + Note that whichever loss_name is selected, that corresponding kwargs will be + selected. For the "default" case, the "{resolved_default}_kwargs" will be used. + + Examples: + .. code-block:: yaml + + loss_name: "default" + warprnnt_numba_kwargs: + kwargs2: some_other_val + + Returns: + A tuple, the resolved loss name as well as its kwargs (if found). + """ + if cfg is None: + cfg = DictConfig({}) + + loss_name = cfg.get("loss_name", "default") + + if loss_name == "default": + loss_name = resolve_rnnt_default_loss_name() + + loss_kwargs = cfg.get(f"{loss_name}_kwargs", None) + + logging.info(f"Using RNNT Loss : {loss_name}\n" f"Loss {loss_name}_kwargs: {loss_kwargs}") + + return loss_name, loss_kwargs + + @torch.no_grad() + def transcribe( + self, + paths2video_files: List[str], + batch_size: int = 4, + return_hypotheses: bool = False, + partial_hypothesis: Optional[List['Hypothesis']] = None, + num_workers: int = 0, + channel_selector: Optional[ChannelSelectorType] = None, + augmentor: DictConfig = None, + ) -> Tuple[List[str], Optional[List['Hypothesis']]]: + """ + Uses greedy decoding to transcribe video files. Use this method for debugging and prototyping. + + Args: + + paths2video_files: (a list) of paths to video files. + batch_size: (int) batch size to use during inference. \ + Bigger will result in better throughput performance but would use more memory. + return_hypotheses: (bool) Either return hypotheses or text + With hypotheses can do some postprocessing like getting timestamp or rescoring + num_workers: (int) number of workers for DataLoader + channel_selector (int | Iterable[int] | str): select a single channel or a subset of channels from multi-channel audio. If set to `'average'`, it performs averaging across channels. Disabled if set to `None`. Defaults to `None`. Uses zero-based indexing. + augmentor: (DictConfig): Augment audio samples during transcription if augmentor is applied. + Returns: + Returns a tuple of 2 items - + * A list of greedy transcript texts / Hypothesis + * An optional list of beam search transcript texts / Hypothesis / NBestHypothesis. + """ + if paths2video_files is None or len(paths2video_files) == 0: + return {} + # We will store transcriptions here + hypotheses = [] + all_hypotheses = [] + # Model's mode and device + mode = self.training + device = next(self.parameters()).device + + if num_workers is None: + num_workers = min(batch_size, os.cpu_count() - 1) + + try: + + # Switch model to evaluation mode + self.eval() + # Freeze the visual front-end, encoder and decoder modules + self.video_front_end.freeze() + self.encoder.freeze() + self.decoder.freeze() + self.joint.freeze() + logging_level = logging.get_verbosity() + logging.set_verbosity(logging.WARNING) + # Work in tmp directory - will store manifest file there + with tempfile.TemporaryDirectory() as tmpdir: + with open(os.path.join(tmpdir, 'manifest.json'), 'w', encoding='utf-8') as fp: + for video_file in paths2video_files: + entry = {'video_filepath': video_file, 'duration': 100000, 'text': ''} + fp.write(json.dumps(entry) + '\n') + + config = { + 'paths2video_files': paths2video_files, + 'batch_size': batch_size, + 'temp_dir': tmpdir, + 'num_workers': num_workers, + 'channel_selector': channel_selector, + } + + if augmentor: + config['augmentor'] = augmentor + + temporary_datalayer = self._setup_transcribe_dataloader(config) + for test_batch in tqdm(temporary_datalayer, desc="Transcribing"): + encoded, encoded_len = self.forward( + input_signal=test_batch[0].to(device), input_signal_length=test_batch[1].to(device) + ) + best_hyp, all_hyp = self.decoding.rnnt_decoder_predictions_tensor( + encoded, + encoded_len, + return_hypotheses=return_hypotheses, + partial_hypotheses=partial_hypothesis, + ) + + hypotheses += best_hyp + if all_hyp is not None: + all_hypotheses += all_hyp + else: + all_hypotheses += best_hyp + + del encoded + del test_batch + finally: + # set mode back to its original value + self.train(mode=mode) + + logging.set_verbosity(logging_level) + if mode is True: + self.video_front_end.unfreeze() + self.encoder.unfreeze() + self.decoder.unfreeze() + self.joint.unfreeze() + return hypotheses, all_hypotheses + + def change_vocabulary(self, new_vocabulary: List[str], decoding_cfg: Optional[DictConfig] = None): + """ + Changes vocabulary used during RNNT decoding process. Use this method when fine-tuning a pre-trained model. + This method changes only decoder and leaves encoder and pre-processing modules unchanged. For example, you would + use it if you want to use pretrained encoder when fine-tuning on data in another language, or when you'd need + model to learn capitalization, punctuation and/or special characters. + + Args: + new_vocabulary: list with new vocabulary. Must contain at least 2 elements. Typically, \ + this is target alphabet. + decoding_cfg: A config for the decoder, which is optional. If the decoding type + needs to be changed (from say Greedy to Beam decoding etc), the config can be passed here. + + Returns: None + + """ + if self.joint.vocabulary == new_vocabulary: + logging.warning(f"Old {self.joint.vocabulary} and new {new_vocabulary} match. Not changing anything.") + else: + if new_vocabulary is None or len(new_vocabulary) == 0: + raise ValueError(f'New vocabulary must be non-empty list of chars. But I got: {new_vocabulary}') + + joint_config = self.joint.to_config_dict() + new_joint_config = copy.deepcopy(joint_config) + new_joint_config['vocabulary'] = new_vocabulary + new_joint_config['num_classes'] = len(new_vocabulary) + del self.joint + self.joint = VisualEncDecRNNTModel.from_config_dict(new_joint_config) + + decoder_config = self.decoder.to_config_dict() + new_decoder_config = copy.deepcopy(decoder_config) + new_decoder_config.vocab_size = len(new_vocabulary) + del self.decoder + self.decoder = VisualEncDecRNNTModel.from_config_dict(new_decoder_config) + + del self.loss + loss_name, loss_kwargs = self.extract_rnnt_loss_cfg(self.cfg.get('loss', None)) + self.loss = RNNTLoss( + num_classes=self.joint.num_classes_with_blank - 1, loss_name=loss_name, loss_kwargs=loss_kwargs + ) + + if decoding_cfg is None: + # Assume same decoding config as before + decoding_cfg = self.cfg.decoding + + # Assert the decoding config with all hyper parameters + decoding_cls = OmegaConf.structured(RNNTDecodingConfig) + decoding_cls = OmegaConf.create(OmegaConf.to_container(decoding_cls)) + decoding_cfg = OmegaConf.merge(decoding_cls, decoding_cfg) + + self.decoding = RNNTDecoding( + decoding_cfg=decoding_cfg, decoder=self.decoder, joint=self.joint, vocabulary=self.joint.vocabulary, + ) + + self.wer = RNNTWER( + decoding=self.decoding, + batch_dim_index=self.wer.batch_dim_index, + use_cer=self.wer.use_cer, + log_prediction=self.wer.log_prediction, + dist_sync_on_step=True, + ) + + # Setup fused Joint step + if self.joint.fuse_loss_wer or ( + self.decoding.joint_fused_batch_size is not None and self.decoding.joint_fused_batch_size > 0 + ): + self.joint.set_loss(self.loss) + self.joint.set_wer(self.wer) + + # Update config + with open_dict(self.cfg.joint): + self.cfg.joint = new_joint_config + + with open_dict(self.cfg.decoder): + self.cfg.decoder = new_decoder_config + + with open_dict(self.cfg.decoding): + self.cfg.decoding = decoding_cfg + + ds_keys = ['train_ds', 'validation_ds', 'test_ds'] + for key in ds_keys: + if key in self.cfg: + with open_dict(self.cfg[key]): + self.cfg[key]['labels'] = OmegaConf.create(new_vocabulary) + + logging.info(f"Changed decoder to output to {self.joint.vocabulary} vocabulary.") + + def change_decoding_strategy(self, decoding_cfg: DictConfig): + """ + Changes decoding strategy used during RNNT decoding process. + + Args: + decoding_cfg: A config for the decoder, which is optional. If the decoding type + needs to be changed (from say Greedy to Beam decoding etc), the config can be passed here. + """ + if decoding_cfg is None: + # Assume same decoding config as before + logging.info("No `decoding_cfg` passed when changing decoding strategy, using internal config") + decoding_cfg = self.cfg.decoding + + # Assert the decoding config with all hyper parameters + decoding_cls = OmegaConf.structured(RNNTDecodingConfig) + decoding_cls = OmegaConf.create(OmegaConf.to_container(decoding_cls)) + decoding_cfg = OmegaConf.merge(decoding_cls, decoding_cfg) + + self.decoding = RNNTDecoding( + decoding_cfg=decoding_cfg, decoder=self.decoder, joint=self.joint, vocabulary=self.joint.vocabulary, + ) + + self.wer = RNNTWER( + decoding=self.decoding, + batch_dim_index=self.wer.batch_dim_index, + use_cer=self.wer.use_cer, + log_prediction=self.wer.log_prediction, + dist_sync_on_step=True, + ) + + # Setup fused Joint step + if self.joint.fuse_loss_wer or ( + self.decoding.joint_fused_batch_size is not None and self.decoding.joint_fused_batch_size > 0 + ): + self.joint.set_loss(self.loss) + self.joint.set_wer(self.wer) + + # Update config + with open_dict(self.cfg.decoding): + self.cfg.decoding = decoding_cfg + + logging.info(f"Changed decoding strategy to \n{OmegaConf.to_yaml(self.cfg.decoding)}") + + def _setup_dataloader_from_config(self, config: Optional[Dict]): + # Automatically inject args from model config to dataloader config + audio_to_text_dataset.inject_dataloader_value_from_model_config(self.cfg, config, key='sample_rate') + audio_to_text_dataset.inject_dataloader_value_from_model_config(self.cfg, config, key='labels') + dataset = video_to_text_dataset.get_video_to_text_bpe_dataset_from_config( + config=config, + local_rank=self.local_rank, + global_rank=self.global_rank, + world_size=self.world_size, + preprocessor_cfg=self._cfg.get("preprocessor", None), + ) + + if dataset is None: + return None + + shuffle = config['shuffle'] + if config.get('is_tarred', False): + shuffle = False + + if hasattr(dataset, 'collate_fn'): + collate_fn = dataset.collate_fn + else: + collate_fn = dataset.datasets[0].collate_fn + + return torch.utils.data.DataLoader( + dataset=dataset, + batch_size=config['batch_size'], + collate_fn=collate_fn, + drop_last=config.get('drop_last', False), + shuffle=shuffle, + num_workers=config.get('num_workers', 0), + pin_memory=config.get('pin_memory', False), + ) + + def setup_training_data(self, train_data_config: Optional[Union[DictConfig, Dict]]): + """ + Sets up the training data loader via a Dict-like object. + + Args: + train_data_config: A config that contains the information regarding construction + of an ASR Training dataset. + + Supported Datasets: + - :class:`~nemo.collections.multimodal.speech_cv.data.video_to_text.VideoToCharDataset` + - :class:`~nemo.collections.asr.data.video_to_text.VideoToBPEDataset` + - :class:`~nemo.collections.asr.data.video_to_text.TarredVideoToBPEDataset` + """ + if 'shuffle' not in train_data_config: + train_data_config['shuffle'] = True + + # preserve config + self._update_dataset_config(dataset_name='train', config=train_data_config) + + self._train_dl = self._setup_dataloader_from_config(config=train_data_config) + + # Need to set this because if using an IterableDataset, the length of the dataloader is the total number + # of samples rather than the number of batches, and this messes up the tqdm progress bar. + # So we set the number of steps manually (to the correct number) to fix this. + if 'is_tarred' in train_data_config and train_data_config['is_tarred']: + # We also need to check if limit_train_batches is already set. + # If it's an int, we assume that the user has set it to something sane, i.e. <= # training batches, + # and don't change it. Otherwise, adjust batches accordingly if it's a float (including 1.0). + if self._trainer is not None and isinstance(self._trainer.limit_train_batches, float): + self._trainer.limit_train_batches = int( + self._trainer.limit_train_batches + * ceil((len(self._train_dl.dataset) / self.world_size) / train_data_config['batch_size']) + ) + elif self._trainer is None: + logging.warning( + "Model Trainer was not set before constructing the dataset, incorrect number of " + "training batches will be used. Please set the trainer and rebuild the dataset." + ) + + def setup_validation_data(self, val_data_config: Optional[Union[DictConfig, Dict]]): + """ + Sets up the validation data loader via a Dict-like object. + + Args: + val_data_config: A config that contains the information regarding construction + of an ASR Training dataset. + + Supported Datasets: + - :class:`~nemo.collections.multimodal.speech_cv.data.video_to_text.VideoToCharDataset` + - :class:`~nemo.collections.asr.data.video_to_text.VideoToBPEDataset` + - :class:`~nemo.collections.asr.data.video_to_text.TarredVideoToBPEDataset` + """ + if 'shuffle' not in val_data_config: + val_data_config['shuffle'] = False + + # preserve config + self._update_dataset_config(dataset_name='validation', config=val_data_config) + + self._validation_dl = self._setup_dataloader_from_config(config=val_data_config) + + def setup_test_data(self, test_data_config: Optional[Union[DictConfig, Dict]]): + """ + Sets up the test data loader via a Dict-like object. + + Args: + test_data_config: A config that contains the information regarding construction + of an ASR Training dataset. + + Supported Datasets: + - :class:`~nemo.collections.multimodal.speech_cv.data.video_to_text.VideoToCharDataset` + - :class:`~nemo.collections.asr.data.video_to_text.VideoToBPEDataset` + - :class:`~nemo.collections.asr.data.video_to_text.TarredVideoToBPEDataset` + """ + if 'shuffle' not in test_data_config: + test_data_config['shuffle'] = False + + # preserve config + self._update_dataset_config(dataset_name='test', config=test_data_config) + + self._test_dl = self._setup_dataloader_from_config(config=test_data_config) + + @property + def input_types(self) -> Optional[Dict[str, NeuralType]]: + + return { + "input_signal": NeuralType(('B', 'C', 'T', 'H', 'W'), VideoSignal(), optional=True), + "input_signal_length": NeuralType(tuple('B'), LengthsType(), optional=True), + } + + @property + def output_types(self) -> Optional[Dict[str, NeuralType]]: + return { + "outputs": NeuralType(('B', 'D', 'T'), AcousticEncodedRepresentation()), + "encoded_lengths": NeuralType(tuple('B'), LengthsType()), + } + + @typecheck() + def forward(self, input_signal=None, input_signal_length=None): + """ + Forward pass of the model. Note that for RNNT Models, the forward pass of the model is a 3 step process, + and this method only performs the first step - forward of the acoustic/visual model. + + Please refer to the `training_step` in order to see the full `forward` step for training - which + performs the forward of the acoustic model, the prediction network and then the joint network. + Finally, it computes the loss and possibly compute the detokenized text via the `decoding` step. + + Please refer to the `validation_step` in order to see the full `forward` step for inference - which + performs the forward of the acoustic model, the prediction network and then the joint network. + Finally, it computes the decoded tokens via the `decoding` step and possibly compute the batch metrics. + + Args: + input_signal: Tensor that represents a batch of video signals, + of shape [B, T, H, W, C]. T here represents timesteps, H height, W width and C channels + input_signal_length: Vector of length B, that contains the individual lengths of the video + sequences. + + Returns: + A tuple of 2 elements - + 1) The log probabilities tensor of shape [B, T, D]. + 2) The lengths of the acoustic sequence after propagation through the encoder, of shape [B]. + """ + + # Preprocessing + processed_video_signal, processed_video_signal_length = self.video_preprocessor( + input_signal=input_signal, length=input_signal_length + ) + + # Augmentation + processed_video_signal = self.video_augmentation( + input_signal=processed_video_signal, length=processed_video_signal_length + ) + + # Front-end Networks + processed_video_signal, processed_video_signal_length = self.video_front_end( + input_signal=processed_video_signal, length=processed_video_signal_length + ) + + # Back-end Networks + encoded, encoded_len = self.encoder(audio_signal=processed_video_signal, length=processed_video_signal_length) + + return encoded, encoded_len + + # PTL-specific methods + def training_step(self, batch, batch_nb): + # Reset access registry + if AccessMixin.is_access_enabled(): + AccessMixin.reset_registry(self) + + signal, signal_len, transcript, transcript_len = batch + + # forward() only performs encoder forward + encoded, encoded_len = self.forward(input_signal=signal, input_signal_length=signal_len) + del signal + + # During training, loss must be computed, so decoder forward is necessary + decoder, target_length, states = self.decoder(targets=transcript, target_length=transcript_len) + + if hasattr(self, '_trainer') and self._trainer is not None: + log_every_n_steps = self._trainer.log_every_n_steps + sample_id = self._trainer.global_step + else: + log_every_n_steps = 1 + sample_id = batch_nb + + # If experimental fused Joint-Loss-WER is not used + if not self.joint.fuse_loss_wer: + # Compute full joint and loss + joint = self.joint(encoder_outputs=encoded, decoder_outputs=decoder) + loss_value = self.loss( + log_probs=joint, targets=transcript, input_lengths=encoded_len, target_lengths=target_length + ) + + # Add auxiliary losses, if registered + loss_value = self.add_auxiliary_losses(loss_value) + + # Reset access registry + if AccessMixin.is_access_enabled(): + AccessMixin.reset_registry(self) + + tensorboard_logs = { + 'train_loss': loss_value, + 'learning_rate': self._optimizer.param_groups[0]['lr'], + 'global_step': torch.tensor(self.trainer.global_step, dtype=torch.float32), + } + + if (sample_id + 1) % log_every_n_steps == 0: + self.wer.update(encoded, encoded_len, transcript, transcript_len) + _, scores, words = self.wer.compute() + self.wer.reset() + tensorboard_logs.update({'training_batch_wer': scores.float() / words}) + + else: + # If experimental fused Joint-Loss-WER is used + if (sample_id + 1) % log_every_n_steps == 0: + compute_wer = True + else: + compute_wer = False + + # Fused joint step + loss_value, wer, _, _ = self.joint( + encoder_outputs=encoded, + decoder_outputs=decoder, + encoder_lengths=encoded_len, + transcripts=transcript, + transcript_lengths=transcript_len, + compute_wer=compute_wer, + ) + + # Add auxiliary losses, if registered + loss_value = self.add_auxiliary_losses(loss_value) + + # Reset access registry + if AccessMixin.is_access_enabled(): + AccessMixin.reset_registry(self) + + tensorboard_logs = { + 'train_loss': loss_value, + 'learning_rate': self._optimizer.param_groups[0]['lr'], + 'global_step': torch.tensor(self.trainer.global_step, dtype=torch.float32), + } + + if compute_wer: + tensorboard_logs.update({'training_batch_wer': wer}) + + # Log items + self.log_dict(tensorboard_logs) + + # Preserve batch acoustic model T and language model U parameters if normalizing + if self._optim_normalize_joint_txu: + self._optim_normalize_txu = [encoded_len.max(), transcript_len.max()] + + return {'loss': loss_value} + + def predict_step(self, batch, batch_idx, dataloader_idx=0): + signal, signal_len, transcript, transcript_len, sample_id = batch + + # forward() only performs encoder forward + encoded, encoded_len = self.forward(input_signal=signal, input_signal_length=signal_len) + del signal + + best_hyp_text, all_hyp_text = self.decoding.rnnt_decoder_predictions_tensor( + encoder_output=encoded, encoded_lengths=encoded_len, return_hypotheses=False + ) + + sample_id = sample_id.cpu().detach().numpy() + return list(zip(sample_id, best_hyp_text)) + + def validation_step(self, batch, batch_idx, dataloader_idx=0): + signal, signal_len, transcript, transcript_len = batch + + # forward() only performs encoder forward + encoded, encoded_len = self.forward(input_signal=signal, input_signal_length=signal_len) + del signal + + tensorboard_logs = {} + + # If experimental fused Joint-Loss-WER is not used + if not self.joint.fuse_loss_wer: + if self.compute_eval_loss: + decoder, target_length, states = self.decoder(targets=transcript, target_length=transcript_len) + joint = self.joint(encoder_outputs=encoded, decoder_outputs=decoder) + + loss_value = self.loss( + log_probs=joint, targets=transcript, input_lengths=encoded_len, target_lengths=target_length + ) + + tensorboard_logs['val_loss'] = loss_value + + self.wer.update(encoded, encoded_len, transcript, transcript_len) + wer, wer_num, wer_denom = self.wer.compute() + self.wer.reset() + + tensorboard_logs['val_wer_num'] = wer_num + tensorboard_logs['val_wer_denom'] = wer_denom + tensorboard_logs['val_wer'] = wer + + else: + # If experimental fused Joint-Loss-WER is used + compute_wer = True + + if self.compute_eval_loss: + decoded, target_len, states = self.decoder(targets=transcript, target_length=transcript_len) + else: + decoded = None + target_len = transcript_len + + # Fused joint step + loss_value, wer, wer_num, wer_denom = self.joint( + encoder_outputs=encoded, + decoder_outputs=decoded, + encoder_lengths=encoded_len, + transcripts=transcript, + transcript_lengths=target_len, + compute_wer=compute_wer, + ) + + if loss_value is not None: + tensorboard_logs['val_loss'] = loss_value + + tensorboard_logs['val_wer_num'] = wer_num + tensorboard_logs['val_wer_denom'] = wer_denom + tensorboard_logs['val_wer'] = wer + + self.log('global_step', torch.tensor(self.trainer.global_step, dtype=torch.float32)) + + return tensorboard_logs + + def test_step(self, batch, batch_idx, dataloader_idx=0): + logs = self.validation_step(batch, batch_idx, dataloader_idx=dataloader_idx) + test_logs = { + 'test_wer_num': logs['val_wer_num'], + 'test_wer_denom': logs['val_wer_denom'], + # 'test_wer': logs['val_wer'], + } + if 'val_loss' in logs: + test_logs['test_loss'] = logs['val_loss'] + return test_logs + + def multi_validation_epoch_end(self, outputs, dataloader_idx: int = 0): + if self.compute_eval_loss: + val_loss_mean = torch.stack([x['val_loss'] for x in outputs]).mean() + val_loss_log = {'val_loss': val_loss_mean} + else: + val_loss_log = {} + wer_num = torch.stack([x['val_wer_num'] for x in outputs]).sum() + wer_denom = torch.stack([x['val_wer_denom'] for x in outputs]).sum() + tensorboard_logs = {**val_loss_log, 'val_wer': wer_num.float() / wer_denom} + return {**val_loss_log, 'log': tensorboard_logs} + + def multi_test_epoch_end(self, outputs, dataloader_idx: int = 0): + if self.compute_eval_loss: + test_loss_mean = torch.stack([x['test_loss'] for x in outputs]).mean() + test_loss_log = {'test_loss': test_loss_mean} + else: + test_loss_log = {} + wer_num = torch.stack([x['test_wer_num'] for x in outputs]).sum() + wer_denom = torch.stack([x['test_wer_denom'] for x in outputs]).sum() + tensorboard_logs = {**test_loss_log, 'test_wer': wer_num.float() / wer_denom} + return {**test_loss_log, 'log': tensorboard_logs} + + def _setup_transcribe_dataloader(self, config: Dict) -> 'torch.utils.data.DataLoader': + """ + Setup function for a temporary data loader which wraps the provided video file. + + Args: + config: A python dictionary which contains the following keys: + paths2video_files: (a list) of paths to video files. The files should be relatively short fragments. \ + Recommended length per file is between 5 and 25 seconds. + batch_size: (int) batch size to use during inference. \ + Bigger will result in better throughput performance but would use more memory. + temp_dir: (str) A temporary directory where the video manifest is temporarily + stored. + + Returns: + A pytorch DataLoader for the given video file(s). + """ + if 'manifest_filepath' in config: + manifest_filepath = config['manifest_filepath'] + batch_size = config['batch_size'] + else: + manifest_filepath = os.path.join(config['temp_dir'], 'manifest.json') + batch_size = min(config['batch_size'], len(config['paths2video_files'])) + + dl_config = { + 'manifest_filepath': manifest_filepath, + 'labels': self.joint.vocabulary, + 'batch_size': batch_size, + 'trim_silence': False, + 'shuffle': False, + 'num_workers': config.get('num_workers', min(batch_size, os.cpu_count() - 1)), + 'pin_memory': True, + } + + if config.get("augmentor"): + dl_config['augmentor'] = config.get("augmentor") + + temporary_datalayer = self._setup_dataloader_from_config(config=DictConfig(dl_config)) + return temporary_datalayer + + def on_after_backward(self): + super().on_after_backward() + if self._optim_variational_noise_std > 0 and self.global_step >= self._optim_variational_noise_start: + for param_name, param in self.decoder.named_parameters(): + if param.grad is not None: + noise = torch.normal( + mean=0.0, + std=self._optim_variational_noise_std, + size=param.size(), + device=param.device, + dtype=param.dtype, + ) + param.grad.data.add_(noise) + + if self._optim_normalize_joint_txu: + T, U = self._optim_normalize_txu + if T is not None and U is not None: + for param_name, param in self.encoder.named_parameters(): + if param.grad is not None: + param.grad.data.div_(U) + + for param_name, param in self.decoder.named_parameters(): + if param.grad is not None: + param.grad.data.div_(T) + + if self._optim_normalize_encoder_norm: + for param_name, param in self.encoder.named_parameters(): + if param.grad is not None: + norm = param.grad.norm() + param.grad.data.div_(norm) + + if self._optim_normalize_decoder_norm: + for param_name, param in self.decoder.named_parameters(): + if param.grad is not None: + norm = param.grad.norm() + param.grad.data.div_(norm) + + if self._optim_normalize_joint_norm: + for param_name, param in self.joint.named_parameters(): + if param.grad is not None: + norm = param.grad.norm() + param.grad.data.div_(norm) + + # EncDecRNNTModel is exported in 2 parts + def list_export_subnets(self): + return ['encoder', 'decoder_joint'] + + # for export + @property + def decoder_joint(self): + return RNNTDecoderJoint(self.decoder, self.joint) + + @classmethod + def list_available_models(cls) -> List[PretrainedModelInfo]: + """ + This method returns a list of pre-trained model which can be instantiated directly from NVIDIA's NGC cloud. + + Returns: + List of available pre-trained models. + """ + results = [] + + return results diff --git a/nemo/collections/multimodal/speech_cv/modules/__init__.py b/nemo/collections/multimodal/speech_cv/modules/__init__.py new file mode 100644 index 000000000000..bfc09080b274 --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/modules/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from nemo.collections.multimodal.speech_cv.modules.linear_projection_video_front_end import ( + LinearProjectionVideoFrontEnd, +) +from nemo.collections.multimodal.speech_cv.modules.resnet_video_front_end import ResNetVideoFrontEnd +from nemo.collections.multimodal.speech_cv.modules.video_augment import VideoAugmentation +from nemo.collections.multimodal.speech_cv.modules.video_preprocessing import VideoPreprocessor diff --git a/nemo/collections/multimodal/speech_cv/modules/linear_projection_video_front_end.py b/nemo/collections/multimodal/speech_cv/modules/linear_projection_video_front_end.py new file mode 100644 index 000000000000..45e797171f2e --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/modules/linear_projection_video_front_end.py @@ -0,0 +1,143 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections import OrderedDict + +import torch +from torch import nn + +from nemo.core.classes.module import NeuralModule +from nemo.core.neural_types import LengthsType, NeuralType, VideoSignal + + +class LinearProjectionVideoFrontEnd(NeuralModule): + + """ + Linear Projection Video Front-End for Lip Reading + + The spatial dimension is flattened and projected to dim_output using a Linear layer. + This is equivalent to having a convolution layer with a kernel size of the size of the image. + Circle crop can be used as pre-processing to crop the image as a circle around lips and ignore corner pixels + + Args: + in_channels: number of inputs video channels, 1 for grayscale and 3 for RGB + in_height: image height + in_width: image width + dim_output: output feature dimension for linear projection + out_channels_first: Whether outputs should have channels_first format (Batch, Dout, Time) or channels_last (Batch, Time, Dout) + circle_crop: crop the image as a circle before the Linear layer, default to False + circle_radius: the circle radius, default to 1 for full circle + + """ + + def __init__( + self, + in_channels, + in_height, + in_width, + dim_output, + out_channels_first=True, + circle_crop=False, + circle_radius=1.0, + ): + super(LinearProjectionVideoFrontEnd, self).__init__() + + self.out_channels_first = out_channels_first + self.in_height = in_height + self.in_width = in_width + self.dim_output = dim_output + self.in_channels = in_channels + self.circle_crop = circle_crop + self.circle_radius = circle_radius + self.circle_indices = self.get_circle_indices() + + if self.dim_output is not None: + if self.circle_crop: + self.linear_proj = nn.Linear(in_channels * len(self.circle_indices), dim_output) + else: + self.linear_proj = nn.Linear(in_channels * in_height * in_width, dim_output) + else: + self.linear_proj = nn.Identity() + + @property + def input_types(self): + """Returns definitions of module input ports.""" + return OrderedDict( + { + "audio_signal": NeuralType(('B', 'D', 'T', 'H', 'W'), VideoSignal()), + "length": NeuralType(tuple('B'), LengthsType()), + } + ) + + @property + def input_types_for_export(self): + """Returns definitions of module input ports.""" + return OrderedDict( + { + "output_signal": NeuralType(('B', 'D', 'T'), NeuralType()), + "length": NeuralType(tuple('B'), LengthsType()), + } + ) + + def get_circle_indices(self): + + """ return image indices inside circle of radius circle_radius """ + + # Create linspace + linspace_height = (torch.linspace(0, 2, steps=self.in_height) - 1).abs() + linspace_width = (torch.linspace(0, 2, steps=self.in_width) - 1).abs() + + # Repeat linspace along height/width + linspace_height = linspace_height.unsqueeze(dim=-1).repeat(1, self.in_width).flatten() + linspace_width = linspace_width.repeat(self.in_height) + + # Compute norm + dist = torch.sqrt(linspace_height.square() + linspace_width.square()) + + # Get circle indices + circle_indices = torch.nonzero(dist <= self.circle_radius).squeeze(dim=-1) + + return circle_indices + + def forward(self, input_signal, length): + + # Permute (B, C, T, H, W) -> (B, T, H, W, C) + input_signal = input_signal.permute(0, 2, 3, 4, 1) + + # Circle Crop + if self.circle_crop: + + # Flatten height, width (B, T, H, W, C) -> (B, T, H*W, C) + input_signal = input_signal.flatten(start_dim=2, end_dim=-2) + + # (B, T, H*W, C) -> (B, T, N circle, C) + input_signal = input_signal[:, :, self.circle_indices] + + # Flatten circle and channels (B, T, N circle, C) -> (B, T, N) + input_signal = input_signal.flatten(start_dim=2, end_dim=-1) + + # Flatten height, width and channels (B, T, H, W, C) -> (B, T, N) + else: + input_signal = input_signal.flatten(start_dim=2, end_dim=-1) + + # Project (B, T, N) -> (B, T, Dout) + input_signal = self.linear_proj(input_signal) + + # Transpose to channels_last format (Batch, Dout, Time) -> (Batch, Time, Dout) + if self.out_channels_first: + output_signal = input_signal.transpose(1, 2) + else: + output_signal = input_signal + + return output_signal, length diff --git a/nemo/collections/multimodal/speech_cv/modules/resnet_video_front_end.py b/nemo/collections/multimodal/speech_cv/modules/resnet_video_front_end.py new file mode 100644 index 000000000000..202c629e2b02 --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/modules/resnet_video_front_end.py @@ -0,0 +1,84 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections import OrderedDict + +import torch +from torch import nn + +from nemo.collections.multimodal.speech_cv.parts.submodules.resnet import ResNet +from nemo.core.classes.module import NeuralModule +from nemo.core.neural_types import LengthsType, NeuralType, VideoSignal + + +class ResNetVideoFrontEnd(NeuralModule): + """ + Lip Reading / Visual Speech Recognition (VSR) ResNet Front-End Network + + Paper: + 'Audio-Visual Efficient Conformer for Robust Speech Recognition' by Burchi and Timofte + https://arxiv.org/abs/2301.01456 + + Args: + in_channels: number of inputs video channels, 1 for grayscale and 3 for RGB + model: model size in ["ResNet18", "ResNet34", "ResNet50", "ResNet101", "ResNet152"] + dim_output: output feature dimension for linear projection after spacial average pooling + out_channels_first: Whether outputs should have channels_first format (Batch, Dout, Time) or channels_last (Batch, Time, Dout) + """ + + def __init__(self, in_channels=1, model="ResNet18", dim_output=256, out_channels_first=True): + super(ResNetVideoFrontEnd, self).__init__() + + self.front_end = nn.Sequential( + nn.Conv3d( + in_channels=in_channels, out_channels=64, kernel_size=(5, 7, 7), stride=(1, 2, 2), padding=(2, 3, 3) + ), + nn.BatchNorm3d(num_features=64), + nn.ReLU(), + nn.MaxPool3d(kernel_size=(1, 3, 3), stride=(1, 2, 2), padding=(0, 1, 1)), + ResNet(include_stem=False, dim_output=dim_output, model=model), + ) + + self.out_channels_first = out_channels_first + + @property + def input_types(self): + """Returns definitions of module input ports.""" + return OrderedDict( + { + "audio_signal": NeuralType(('B', 'D', 'T', 'H', 'W'), VideoSignal()), + "length": NeuralType(tuple('B'), LengthsType()), + } + ) + + @property + def input_types_for_export(self): + """Returns definitions of module input ports.""" + return OrderedDict( + { + "output_signal": NeuralType(('B', 'D', 'T'), NeuralType()), + "length": NeuralType(tuple('B'), LengthsType()), + } + ) + + def forward(self, input_signal, length): + + # Front-End Network (Batch, Din, Time, Height, Width) -> (Batch, Dout, Time) + input_signal = self.front_end(input_signal) + + # Transpose to channels_last format (Batch, Dout, Time) -> (Batch, Time, Dout) + if not self.out_channels_first: + input_signal = input_signal.transpose(1, 2) + + return input_signal, length diff --git a/nemo/collections/multimodal/speech_cv/modules/video_augment.py b/nemo/collections/multimodal/speech_cv/modules/video_augment.py new file mode 100644 index 000000000000..e191cc89d1a0 --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/modules/video_augment.py @@ -0,0 +1,225 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import random +from collections import OrderedDict + +import torch +from torch import nn + +from nemo.core.classes.module import NeuralModule +from nemo.core.neural_types import NeuralType, VideoSignal + + +try: + import torchvision + + TORCHVISION_AVAILABLE = True +except (ImportError, ModuleNotFoundError): + TORCHVISION_AVAILABLE = False + + +class VideoAugmentation(NeuralModule): + + """ Video Augmentation for batched video input: input_signal shape (B, C, T, H, W) """ + + def __init__( + self, + random_crop, + crop_size, + horizontal_flip, + time_masking, + num_mask_second=1.0, + spatial_masking=False, + mean_frame=True, + ): + super().__init__() + + # Params + self.random_crop = random_crop + self.crop_size = crop_size + self.horizontal_flip = horizontal_flip + self.time_masking = time_masking + self.spatial_masking = spatial_masking + + self.training_augments = nn.ModuleList() + self.inference_augments = nn.ModuleList() + + # Random Crop + if self.random_crop: + if TORCHVISION_AVAILABLE: + self.training_augments.append(torchvision.transforms.RandomCrop(self.crop_size)) + self.inference_augments.append(torchvision.transforms.CenterCrop(self.crop_size)) + else: + raise Exception("RandomCrop transform requires torchvision") + + # Horizontal Flip + if self.horizontal_flip: + if TORCHVISION_AVAILABLE: + self.training_augments.append(torchvision.transforms.RandomHorizontalFlip()) + else: + raise Exception("RandomHorizontalFlip transform requires torchvision") + + # Time Masking + if self.time_masking: + self.training_augments.append(VideoFrameMasking(num_mask_second=num_mask_second, mean_frame=mean_frame)) + + # Spatial Masking + if self.spatial_masking: + self.training_augments.append(SpatialVideoMasking(mean_frame=mean_frame)) + + @property + def input_types(self): + """Returns definitions of module input ports.""" + return OrderedDict({"input_signal": NeuralType(('B', 'D', 'T', 'H', 'W'), VideoSignal()),}) + + @property + def input_types_for_export(self): + """Returns definitions of module input ports.""" + return OrderedDict({"output_signal": NeuralType(('B', 'D', 'T', 'H', 'W'), VideoSignal()),}) + + @torch.no_grad() + def forward(self, input_signal, length): + + if self.training: + augments = self.training_augments + else: + augments = self.inference_augments + + output_signal = input_signal + + for augment in augments: + if isinstance(augment, VideoFrameMasking) or isinstance(augment, SpatialVideoMasking): + output_signal = augment(output_signal, length) + else: + output_signal = augment(output_signal) + + return output_signal + + +class SpatialVideoMasking(NeuralModule): + + """ Spatial Video Mask + + Will mask videos frames in the spatial dimensions using horizontal and vertical masks + + params: + num_horizontal_masks: number of horizontal masks + num_vertical_masks: number of vertical masks + max_h: maximum width of horizontal mask + max_v: maximum width of vertical mask + mean_frame: mask using video mean instead of zeros + + """ + + def __init__(self, num_horizontal_masks=1, num_vertical_masks=1, max_h=30, max_v=30, mean_frame=True): + super().__init__() + + self.num_horizontal_masks = num_horizontal_masks + self.num_vertical_masks = num_vertical_masks + self.max_h = max_h + self.max_v = max_v + self.mean_frame = mean_frame + self.random = random.Random() + + def forward(self, input_signal, length): + + # (B, C, T, H, W) + shape = input_signal.shape + + # Batch loop + for b in range(shape[0]): + + # Mask Value + mask_value = input_signal[b, :, : length[b]].mean() if self.mean_frame else 0.0 + + # Horizontal Mask loop + for i in range(self.num_horizontal_masks): + + # Start index + x = self.random.randint(0, shape[3] - self.max_h) + + # Mask width + w = self.random.randint(0, self.max_h) + + # Apply mask + input_signal[b, :, :, x : x + w] = mask_value + + # Vertical Mask loop + for i in range(self.num_vertical_masks): + + # Start index + x = self.random.randint(0, shape[4] - self.max_v) + + # Mask width + w = self.random.randint(0, self.max_v) + + # Apply mask + input_signal[b, :, :, :, x : x + w] = mask_value + + return input_signal + + +class VideoFrameMasking(NeuralModule): + + """ Video Frame Mask: + + As explained in: + "Visual Speech Recognition for Multiple Languages in the Wild" + https://arxiv.org/abs/2202.13084 + + S6 Time Masking + We mask n consecutive frames with the mean frame of the video. + The duration tn is chosen from 0 to an upper bound nmax using a uniform distribution. + Since there is a large variance in the video lengths of the LRS2 and LRS3 datasets, we set the number of masks proportional to the sequence length. + Specifically, we use one mask per second, and for each mask, the maximum duration nmax is set to 0.4 seconds. + + """ + + def __init__(self, T_second=0.4, num_mask_second=1.0, fps=25.0, mean_frame=True): + super().__init__() + + self.T = int(T_second * fps) + self.num_mask_second = num_mask_second + self.mean_frame = mean_frame + self.fps = fps + self.random = random.Random() + + def forward(self, input_signal, length): + + # (B, C, T, H, W) + shape = input_signal.shape + + # Batch loop + for b in range(shape[0]): + + # Mask per second + mT = int(length[b] / self.fps * self.num_mask_second) + + # Mask Value + mask_value = input_signal[b, :, : length[b]].mean() if self.mean_frame else 0.0 + + # Mask loop + for i in range(mT): + + # Start left Frame + x_left = self.random.randint(0, length[b] - self.T) + + # Mask width + w = self.random.randint(0, self.T) + + # Apply mask + input_signal[b, :, x_left : x_left + w] = mask_value + + return input_signal diff --git a/nemo/collections/multimodal/speech_cv/modules/video_preprocessing.py b/nemo/collections/multimodal/speech_cv/modules/video_preprocessing.py new file mode 100644 index 000000000000..30accea097db --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/modules/video_preprocessing.py @@ -0,0 +1,138 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import torch +from torch import nn + +from nemo.collections.multimodal.speech_cv.parts.submodules.permute import Permute +from nemo.core.classes import NeuralModule, typecheck + +try: + import torchvision + + TORCHVISION_AVAILABLE = True +except (ImportError, ModuleNotFoundError): + TORCHVISION_AVAILABLE = False + + +class VideoPreprocessor(NeuralModule): + + """ Video Pre-processing + + args: + grayscale: convert images to grayscale + normalize: normalize videos + resize: resize videos + resize_size: output image size for resize + norm_mean: normalize mean + norm_std: normalize std + + """ + + def __init__(self, grayscale, normalize, resize, resize_size, norm_mean, norm_std): + super().__init__() + + # Params + self.grayscale = grayscale + self.normalize = normalize + self.resize = resize + self.resize_size = resize_size + self.norm_mean = norm_mean + self.norm_std = norm_std + + self.transforms = nn.ModuleList() + + # Convert float32 [0:255] -> [0:1] + if TORCHVISION_AVAILABLE: + self.transforms.append(torchvision.transforms.ConvertImageDtype(dtype=torch.float32)) + else: + raise Exception("ConvertImageDtype transform requires torchvision") + + # Convert Channels First + self.transforms.append(Permute(dims=(0, 4, 1, 2, 3))) # (B, T, H, W, C) -> (B, C, T, H, W) + + # Resize + if self.resize: + self.transforms.append(ResizeVideo(self.resize_size)) # (B, C, T, H, W) -> (B, C, T, H', W') + + # Grayscale + if self.grayscale: + if TORCHVISION_AVAILABLE: + self.transforms.append( + nn.Sequential( + Permute(dims=(0, 2, 1, 3, 4)), # (B, C, T, H, W) -> (B, T, C, H, W) + torchvision.transforms.Grayscale(), + Permute(dims=(0, 2, 1, 3, 4)), # (B, T, C, H, W) -> (B, C, T, H, W) + ) + ) + else: + raise Exception("Grayscale transform requires torchvision") + + # Normalize + if self.normalize: + self.transforms.append(NormalizeVideo(mean=norm_mean, std=norm_std)) + + @typecheck() + @torch.no_grad() + def forward(self, input_signal, length): + + for transform in self.transforms: + input_signal = transform(input_signal) + + return input_signal, length + + +class NormalizeVideo(NeuralModule): + def __init__(self, mean, std): + super().__init__() + + self.register_buffer( + "mean", torch.tensor(mean, dtype=torch.float32).reshape(len(mean), 1, 1, 1), persistent=False + ) + self.register_buffer( + "std", torch.tensor(std, dtype=torch.float32).reshape(len(std), 1, 1, 1), persistent=False + ) + + def forward(self, x): + + x = (x - self.mean) / self.std + + return x + + +class ResizeVideo(NeuralModule): + def __init__(self, size): + super().__init__() + + self.size = size + if TORCHVISION_AVAILABLE: + self.resize = torchvision.transforms.Resize(size=self.size) + else: + raise Exception("Resize transform requires torchvision") + + def forward(self, x): + + # (B, C, T, H, W) + if x.dim() == 5: + + B, C = x.shape[:2] + x = x.flatten(start_dim=0, end_dim=1) + x = self.resize(x) + x = x.reshape((B, C) + x.shape[1:]) + + # (C, T, H, W) + elif x.dim() == 4: + x = self.resize(x) + + return x diff --git a/nemo/collections/multimodal/speech_cv/parts/__init__.py b/nemo/collections/multimodal/speech_cv/parts/__init__.py new file mode 100644 index 000000000000..4fc50543f1d2 --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/parts/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/nemo/collections/multimodal/speech_cv/parts/preprocessing/features.py b/nemo/collections/multimodal/speech_cv/parts/preprocessing/features.py new file mode 100644 index 000000000000..29b5268d6adf --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/parts/preprocessing/features.py @@ -0,0 +1,62 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import tempfile + +try: + import torchvision + + TORCHVISION_AVAILABLE = True +except (ImportError, ModuleNotFoundError): + TORCHVISION_AVAILABLE = False + + +class VideoFeaturizer(object): + def __init__(self): + pass + + def process(self, video_file, offset, duration): + + # Load Video + video = self.from_file(video_file, offset=offset, duration=duration) + + return video + + def from_file(self, video_file, offset, duration): + + if not TORCHVISION_AVAILABLE: + raise Exception("Reading Video requires torchvision") + + # Load from filename + if isinstance(video_file, str): + video, audio, infos = torchvision.io.read_video( + video_file, start_pts=offset, end_pts=offset + duration, pts_unit="sec" + ) + + # Load from bytes + elif isinstance(video_file, bytes): + + # webdataset.torch_video + with tempfile.TemporaryDirectory() as dirname: + fname = os.path.join(dirname, f"file.mp4") + with open(fname, "wb") as stream: + stream.write(video_file) + video, audio, infos = torchvision.io.read_video( + fname, start_pts=offset, end_pts=offset + duration, pts_unit="sec" + ) + else: + raise Exception("Unknown video data format") + + return video diff --git a/nemo/collections/multimodal/speech_cv/parts/submodules/__init__.py b/nemo/collections/multimodal/speech_cv/parts/submodules/__init__.py new file mode 100644 index 000000000000..4fc50543f1d2 --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/parts/submodules/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/nemo/collections/multimodal/speech_cv/parts/submodules/conv2d.py b/nemo/collections/multimodal/speech_cv/parts/submodules/conv2d.py new file mode 100644 index 000000000000..25f6e5451b66 --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/parts/submodules/conv2d.py @@ -0,0 +1,72 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Union + +from torch import nn +from torch.nn import init +from torch.nn.common_types import _size_2_t + + +class Conv2d(nn.Conv2d): + + """ + Conv2d layer with ResNet initialization: + + Reference: "Deep Residual Learning for Image Recognition" by He et al. + https://arxiv.org/abs/1512.03385 + + """ + + def __init__( + self, + in_channels: int, + out_channels: int, + kernel_size: _size_2_t, + stride: _size_2_t = 1, + padding: Union[str, _size_2_t] = 0, + dilation: _size_2_t = 1, + groups: int = 1, + bias: bool = True, + padding_mode: str = 'zeros', + device=None, + dtype=None, + weight_init: str = "default", + bias_init: str = "default", + ): + + super(Conv2d, self).__init__( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias=bias, + padding_mode=padding_mode, + device=device, + dtype=dtype, + ) + + # Weight Init + assert weight_init in ["default", "he_normal"] + if weight_init == "he_normal": + init.kaiming_normal_(self.weight) + + # Bias Init + assert bias_init in ["default", "zeros"] + if self.bias is not None: + if bias_init == "zeros": + init.zeros_(self.bias) diff --git a/nemo/collections/multimodal/speech_cv/parts/submodules/global_avg_pool2d.py b/nemo/collections/multimodal/speech_cv/parts/submodules/global_avg_pool2d.py new file mode 100644 index 000000000000..6c248d86564e --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/parts/submodules/global_avg_pool2d.py @@ -0,0 +1,28 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from torch import nn + + +class GlobalAvgPool2d(nn.Module): + def __init__(self, dim=(2, 3), keepdim=False): + super(GlobalAvgPool2d, self).__init__() + self.dim = dim + self.keepdim = keepdim + + def forward(self, x): + + assert x.dim() == 4, "input signal should have 4 dims, has {}".format(x.dim()) + + return x.mean(dim=self.dim, keepdim=self.keepdim) diff --git a/nemo/collections/multimodal/speech_cv/parts/submodules/permute.py b/nemo/collections/multimodal/speech_cv/parts/submodules/permute.py new file mode 100644 index 000000000000..abd4ce34f4a8 --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/parts/submodules/permute.py @@ -0,0 +1,28 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from torch import nn + + +class Permute(nn.Module): + def __init__(self, dims, make_contiguous=False): + super(Permute, self).__init__() + self.dims = dims + self.make_contiguous = make_contiguous + + def forward(self, x): + x = x.permute(self.dims) + if self.make_contiguous: + x = x.contiguous() + return x diff --git a/nemo/collections/multimodal/speech_cv/parts/submodules/resnet.py b/nemo/collections/multimodal/speech_cv/parts/submodules/resnet.py new file mode 100644 index 000000000000..c911db6f3abe --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/parts/submodules/resnet.py @@ -0,0 +1,175 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from torch import nn + +from nemo.collections.multimodal.speech_cv.parts.submodules.conv2d import Conv2d +from nemo.collections.multimodal.speech_cv.parts.submodules.global_avg_pool2d import GlobalAvgPool2d +from nemo.collections.multimodal.speech_cv.parts.submodules.resnet_block import ResNetBlock +from nemo.collections.multimodal.speech_cv.parts.submodules.resnet_bottleneck_block import ResNetBottleneckBlock + + +class ResNet(nn.Module): + + """ ResNet (ResNet18, ResNet34, ResNet50, ResNet101, ResNet152) + Models: 224 x 224 + ResNet18: 11,689,512 Params + ResNet34: 21,797,672 Params + ResNet50: 25,557,032 Params + ResNet101: 44,549,160 Params + Resnet152: 60,192,808 Params + Reference: "Deep Residual Learning for Image Recognition" by He et al. + https://arxiv.org/abs/1512.03385 + """ + + def __init__(self, dim_input=3, dim_output=1000, model="ResNet50", include_stem=True, include_head=True): + super(ResNet, self).__init__() + + assert model in ["ResNet18", "ResNet34", "ResNet50", "ResNet101", "ResNet152"] + + if model == "ResNet18": + dim_stem = 64 + dim_blocks = [64, 128, 256, 512] + num_blocks = [2, 2, 2, 2] + bottleneck = False + elif model == "ResNet34": + dim_stem = 64 + dim_blocks = [64, 128, 256, 512] + num_blocks = [3, 4, 6, 3] + bottleneck = False + elif model == "ResNet50": + dim_stem = 64 + dim_blocks = [256, 512, 1024, 2048] + num_blocks = [3, 4, 6, 3] + bottleneck = True + elif model == "ResNet101": + dim_stem = 64 + dim_blocks = [256, 512, 1024, 2048] + num_blocks = [3, 4, 23, 3] + bottleneck = True + elif model == "ResNet152": + dim_stem = 64 + dim_blocks = [256, 512, 1024, 2048] + num_blocks = [3, 8, 36, 3] + bottleneck = True + + self.stem = ( + nn.Sequential( + Conv2d( + in_channels=dim_input, + out_channels=dim_stem, + kernel_size=(7, 7), + stride=(2, 2), + weight_init="he_normal", + bias=False, + ), + nn.BatchNorm2d(num_features=dim_stem), + nn.ReLU(), + nn.MaxPool2d(kernel_size=(3, 3), stride=(2, 2), padding=(1, 1)), + ) + if include_stem + else nn.Identity() + ) + + # Blocks + self.blocks = nn.ModuleList() + for stage_id in range(4): + + for block_id in range(num_blocks[stage_id]): + + # Projection Block + if block_id == 0: + if stage_id == 0: + stride = (1, 1) + bottleneck_ratio = 1 + in_features = dim_stem + else: + stride = (2, 2) + bottleneck_ratio = 2 + in_features = dim_blocks[stage_id - 1] + # Default Block + else: + stride = (1, 1) + in_features = dim_blocks[stage_id] + bottleneck_ratio = 4 + + if bottleneck: + self.blocks.append( + ResNetBottleneckBlock( + in_features=in_features, + out_features=dim_blocks[stage_id], + bottleneck_ratio=bottleneck_ratio, + kernel_size=(3, 3), + stride=stride, + ) + ) + else: + self.blocks.append( + ResNetBlock( + in_features=in_features, + out_features=dim_blocks[stage_id], + kernel_size=(3, 3), + stride=stride, + ) + ) + + # Head + self.head = ( + nn.Sequential( + GlobalAvgPool2d(), + nn.Linear(in_features=dim_blocks[-1], out_features=dim_output) + if dim_output is not None + else nn.Identity(), + ) + if include_head + else nn.Identity() + ) + + def forward(self, x): + + # Is Video + if x.dim() == 5: + + is_video = True + batch_size = x.shape[0] + video_frames = x.shape[2] + + # (B, Din, T, H, W) -> (B * T, Din, H, W) + x = x.transpose(1, 2).flatten(start_dim=0, end_dim=1) + + else: + is_video = False + + # (B, Din, H, W) -> (B, D0, H//4, W//4) + x = self.stem(x) + + # (B, D0, H//4, W//4) -> (B, D4, H//32, W//32) + for block in self.blocks: + x = block(x) + + # (B, D4, H//32, W//32) -> (B, Dout) + x = self.head(x) + + # Is Video + if is_video: + + # (B * T, Dout) -> (B, Dout, T) + if x.dim() == 2: + x = x.reshape(batch_size, video_frames, -1).transpose(1, 2) + + # (B * T, D4, H//32, W//32) -> (B, D4, T, H//32, W//32) + else: + x = x.reshape(batch_size, video_frames, x.shape[1], x.shape[2], x.shape[3]).transpose(1, 2) + + return x diff --git a/nemo/collections/multimodal/speech_cv/parts/submodules/resnet_block.py b/nemo/collections/multimodal/speech_cv/parts/submodules/resnet_block.py new file mode 100644 index 000000000000..19436311ccd7 --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/parts/submodules/resnet_block.py @@ -0,0 +1,86 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import torch +from torch import nn +from torch.nn.modules.utils import _pair + +from nemo.collections.multimodal.speech_cv.parts.submodules.conv2d import Conv2d + + +class ResNetBlock(nn.Module): + + """ ResNet Residual Block used by ResNet18 and ResNet34 networks. + References: "Deep Residual Learning for Image Recognition", He et al. + https://arxiv.org/abs/1512.03385 + """ + + def __init__(self, in_features, out_features, kernel_size, stride, weight_init="he_normal", bias_init="zeros"): + super(ResNetBlock, self).__init__() + + # Convert to pair + kernel_size = _pair(kernel_size) + + # layers + self.layers = nn.Sequential( + Conv2d( + in_channels=in_features, + out_channels=out_features, + kernel_size=kernel_size, + stride=stride, + bias=False, + weight_init=weight_init, + bias_init=bias_init, + padding=((kernel_size[0] - 1) // 2, kernel_size[1] // 2), + ), + nn.BatchNorm2d(out_features), + nn.ReLU(), + Conv2d( + in_channels=out_features, + out_channels=out_features, + kernel_size=kernel_size, + bias=False, + weight_init=weight_init, + bias_init=bias_init, + padding=((kernel_size[0] - 1) // 2, kernel_size[1] // 2), + ), + nn.BatchNorm2d(out_features), + ) + + # Residual Block + if torch.prod(torch.tensor(stride)) > 1 or in_features != out_features: + self.residual = nn.Sequential( + Conv2d( + in_channels=in_features, + out_channels=out_features, + kernel_size=1, + stride=stride, + bias=False, + weight_init=weight_init, + bias_init=bias_init, + ), + nn.BatchNorm2d(out_features), + ) + else: + self.residual = nn.Identity() + + # Joined Post Act + self.joined_post_act = nn.ReLU() + + def forward(self, x): + + # Forward Layers + x = self.joined_post_act(self.layers(x) + self.residual(x)) + + return x diff --git a/nemo/collections/multimodal/speech_cv/parts/submodules/resnet_bottleneck_block.py b/nemo/collections/multimodal/speech_cv/parts/submodules/resnet_bottleneck_block.py new file mode 100644 index 000000000000..50cafa53343c --- /dev/null +++ b/nemo/collections/multimodal/speech_cv/parts/submodules/resnet_bottleneck_block.py @@ -0,0 +1,107 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import torch +from torch import nn +from torch.nn.modules.utils import _pair + +from nemo.collections.multimodal.speech_cv.parts.submodules.conv2d import Conv2d + + +class ResNetBottleneckBlock(nn.Module): + + """ ResNet Bottleneck Residual Block used by ResNet50, ResNet101 and ResNet152 networks. + References: "Deep Residual Learning for Image Recognition", He et al. + https://arxiv.org/abs/1512.03385 + """ + + def __init__( + self, + in_features, + out_features, + bottleneck_ratio, + kernel_size, + stride, + weight_init="he_normal", + bias_init="zeros", + ): + super(ResNetBottleneckBlock, self).__init__() + + # Assert + assert in_features % bottleneck_ratio == 0 + + # Convert to pair + kernel_size = _pair(kernel_size) + + # layers + self.layers = nn.Sequential( + Conv2d( + in_channels=in_features, + out_channels=in_features // bottleneck_ratio, + kernel_size=1, + bias=False, + weight_init=weight_init, + bias_init=bias_init, + ), + nn.BatchNorm2d(in_features // bottleneck_ratio), + nn.ReLU(), + Conv2d( + in_channels=in_features // bottleneck_ratio, + out_channels=in_features // bottleneck_ratio, + kernel_size=kernel_size, + stride=stride, + bias=False, + weight_init=weight_init, + bias_init=bias_init, + padding=((kernel_size[0] - 1) // 2, kernel_size[1] // 2), + ), + nn.BatchNorm2d(in_features // bottleneck_ratio), + nn.ReLU(), + Conv2d( + in_channels=in_features // bottleneck_ratio, + out_channels=out_features, + kernel_size=1, + bias=False, + weight_init=weight_init, + bias_init=bias_init, + ), + nn.BatchNorm2d(out_features), + ) + + # Joined Post Act + self.joined_post_act = nn.ReLU() + + # Residual Block + if torch.prod(torch.tensor(stride)) > 1 or in_features != out_features: + self.residual = nn.Sequential( + Conv2d( + in_channels=in_features, + out_channels=out_features, + kernel_size=1, + stride=stride, + bias=False, + weight_init=weight_init, + bias_init=bias_init, + ), + nn.BatchNorm2d(out_features), + ) + else: + self.residual = nn.Identity() + + def forward(self, x): + + # Forward Layers + x = self.joined_post_act(self.layers(x) + self.residual(x)) + + return x diff --git a/nemo/core/neural_types/elements.py b/nemo/core/neural_types/elements.py index f2de48da26d0..98fd4cc6193a 100644 --- a/nemo/core/neural_types/elements.py +++ b/nemo/core/neural_types/elements.py @@ -25,6 +25,7 @@ 'ChannelType', 'AcousticEncodedRepresentation', 'AudioSignal', + 'VideoSignal', 'SpectrogramType', 'MelSpectrogramType', 'MFCCSpectrogramType', @@ -199,6 +200,21 @@ def type_parameters(self): return self._params +class VideoSignal(ElementType): + """Element type to represent encoded representation returned by the visual encoder model + Args: + fps (int): frames per second. + """ + + def __init__(self, fps: int = None): + self._params = {} + self._params['fps'] = fps + + @property + def type_parameters(self): + return self._params + + class SpectrogramType(ChannelType): """Element type to represent generic spectrogram signal""" From fd17915f95f2a9085256c0bee76fa920a87dcf39 Mon Sep 17 00:00:00 2001 From: Jan Lasek Date: Wed, 20 Sep 2023 23:52:00 +0200 Subject: [PATCH 045/112] HF StarCoder to NeMo conversion script (#7421) * Script to convert HF StarCoder checkpoint to NeMo Signed-off-by: Jan Lasek * StarCoder conversion test Signed-off-by: Jan Lasek * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Jan Lasek * Fix test Signed-off-by: Jan Lasek * Catch up with save_to changes Signed-off-by: Jan Lasek * Don't abbreviate args for clarity Signed-off-by: Jan Lasek * Configurable precision: BF16 vs FP32 Signed-off-by: Jan Lasek * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Jan Lasek Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- Jenkinsfile | 25 +- .../convert_starcoder_hf_to_nemo.py | 220 ++++++++++++++++++ 2 files changed, 239 insertions(+), 6 deletions(-) create mode 100644 scripts/nlp_language_modeling/convert_starcoder_hf_to_nemo.py diff --git a/Jenkinsfile b/Jenkinsfile index 04bc96ff1596..2abcdbcc5ddb 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -116,12 +116,25 @@ pipeline { } } failFast true - steps { - sh 'CUDA_VISIBLE_DEVICES=0 python scripts/nlp_language_modeling/convert_hf_llama_to_nemo.py \ - --in-file=/home/TestData/nlp/megatron_llama/llama-ci-hf \ - --out-file=/home/TestData/nlp/megatron_llama/ci.nemo \ - --precision=16' - sh 'rm -f /home/TestData/nlp/megatron_llama/ci.nemo' + parallel { + stage('Llama') { + steps { + sh 'CUDA_VISIBLE_DEVICES=0 python scripts/nlp_language_modeling/convert_hf_llama_to_nemo.py \ + --in-file=/home/TestData/nlp/megatron_llama/llama-ci-hf \ + --out-file=/home/TestData/nlp/megatron_llama/ci.nemo \ + --precision=16' + sh 'rm -f /home/TestData/nlp/megatron_llama/ci.nemo' + } + } + stage('StarCoder') { + steps { + sh 'python scripts/nlp_language_modeling/convert_starcoder_hf_to_nemo.py \ + --config examples/nlp/language_modeling/conf/megatron_gpt_config.yaml \ + --input /home/TestData/nlp/megatron_gpt/starcoder-ci-hf \ + --output /home/TestData/nlp/megatron_gpt/starcoder-ci-hf' + sh 'rm -f /home/TestData/nlp/megatron_gpt/starcoder-ci-hf/megatron_starcoder_tp1_pp1.nemo' + } + } } } diff --git a/scripts/nlp_language_modeling/convert_starcoder_hf_to_nemo.py b/scripts/nlp_language_modeling/convert_starcoder_hf_to_nemo.py new file mode 100644 index 000000000000..e826dae037d3 --- /dev/null +++ b/scripts/nlp_language_modeling/convert_starcoder_hf_to_nemo.py @@ -0,0 +1,220 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +A script to convert the BigCode StarCoder checkpoints from HuggingFace to Megatron GPTModel. +This script is hardcoded specifically for the StarCoder pretrained models only, and is not +generalisable to any other models. + +This script will load and convert the model entirely on CPU for OOM safety, but it is +possible to initialize the model on GPU before the save down. You can do this by adding --cuda +parameter to this script call. + +This script requires that you have downloaded the StarCoder checkpoint from HuggingFace. +This can be done using Git with the following command: +```bash +git clone https://huggingface.co/bigcode/starcoder +``` +Note that downloading this particular checkpoint requires authentication with a HuggingFace token. + +The script will generate a Megatron model with TP=1 and PP=1. If you need different TP/PP +values, then after running this script, please use the following script to set whatever +TP/PP configuration you want: + NeMo/examples/nlp/language_modeling/megatron_change_num_partitions.py + +This script also requires a baseline config file from which to override default parameters. +You can specify the location of this file using the -c argument. Please use the config below +to correctly configure creating GPT-2 model in Megatron: + NeMo/examples/nlp/language_modeling/conf/megatron_gpt_config.yaml + + +Here is an example usage command: +```python +python scripts/nlp_language_modeling/convert_starcoder_hf_to_nemo.py \ + --config /path/to/megatron_gpt_config.yaml \ + --input /path/to/starcoder \ + --output /path/to/save +``` +""" + +import argparse +import os +from typing import Dict + +import pytorch_lightning as pl +import torch +import yaml +from omegaconf import OmegaConf +from transformers import AutoConfig, AutoModelForCausalLM + +from nemo.collections.nlp.models.language_modeling.megatron_gpt_model import MegatronGPTModel +from nemo.collections.nlp.parts.nlp_overrides import NLPDDPStrategy +from nemo.utils import logging + + +def convert_state_dict(state_dict: Dict[str, torch.Tensor], amp: bool = False): + def get_new_key(old_key): + if old_key == "transformer.wte.weight": + return "embedding.word_embeddings.weight" + if old_key == "transformer.wpe.weight": + return "embedding.position_embeddings.weight" + elif old_key.startswith("transformer.ln_f"): + return old_key.replace("transformer.ln_f", "decoder.final_layernorm") + elif old_key.startswith("lm_head"): + return old_key.replace("lm_head", "output_layer") + else: + p1 = old_key.replace("transformer.h", "decoder.layers") + p2 = p1.replace("ln_1.", "self_attention.linear_qkv.layer_norm_") + p3 = p2.replace("attn.c_proj", "self_attention.linear_proj") + p4 = p3.replace("attn.c_attn", "self_attention.linear_qkv") + p5 = p4.replace("ln_2.", "mlp.linear_fc1.layer_norm_") + p6 = p5.replace("c_fc", "linear_fc1") + p7 = p6.replace("c_proj", "linear_fc2") + return p7 + + new_dict = {} + prefix = "model.module." if amp else "model." + + for old_key, val in state_dict.items(): + new_key = get_new_key(old_key) + new_key = prefix + new_key + new_dict[new_key] = val + + return new_dict + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--config", type=str, required=True, help="Path to the megatron_gpt_config.yaml file") + parser.add_argument( + "--input", type=str, required=True, help="StarCoder from HuggingFace hub or local dir with downloaded model" + ) + parser.add_argument("--output", type=str, default=".", help="Path to dir where to store output .nemo file") + parser.add_argument( + "--precision", type=str, default="bf16", choices=["bf16", "32"], help="Precision for checkpoint weights saved" + ) + parser.add_argument("--cuda", action="store_true", help="Put Nemo model onto GPU prior to saving") + args = parser.parse_args() + + if not os.path.isdir(args.output): + raise FileNotFoundError(f"Output directory '{args.output}' does not exist") + + hf_config = AutoConfig.from_pretrained(args.input) + + with open(args.config, "r", encoding="utf_8") as f: + orig_cfg = yaml.safe_load(f) + + model_dict = orig_cfg["model"] + + if "data" in model_dict: + del model_dict["data"] + + override_model_dict = { + "micro_batch_size": 1, + "global_batch_size": 1, + "tensor_model_parallel_size": 1, + "pipeline_model_parallel_size": 1, + "megatron_amp_O2": False, + "transformer_engine": True, + "use_cpu_initialization": not args.cuda, + "normalization": "layernorm", + "mcore_gpt": True, + "num_query_groups": 1, # MQA + "hidden_size": hf_config.n_embd, + "encoder_seq_length": hf_config.n_positions, + "max_position_embeddings": hf_config.n_positions, + "num_layers": hf_config.n_layer, + "num_attention_heads": hf_config.n_head, + "ffn_hidden_size": hf_config.n_inner, + "layernorm_epsilon": hf_config.layer_norm_epsilon, + "pre_process": True, + "post_process": True, + "apply_query_key_layer_scaling": True, + "bias": True, + "transformer_block_type": "pre_ln", + "fp32_residual_connection": False, + "hidden_dropout": hf_config.summary_first_dropout, + "attention_dropout": hf_config.attn_pdrop, + "ffn_dropout": 0, + "share_embeddings_and_output_weights": False, + "position_embedding_type": "learned_absolute", + "normalize_attention_scores": True, + "precision": args.precision, + } + tokenizer_dict = { + "library": "huggingface", + "type": args.input, + "use_fast": True, + } + trainer_dict = { + "devices": 1, + "num_nodes": 1, + "accelerator": "gpu" if args.cuda else "cpu", + "precision": args.precision, + "logger": False, + "enable_checkpointing": False, + "max_epochs": -1, + "max_steps": 100000, + "log_every_n_steps": 10, + "val_check_interval": 100, + "limit_val_batches": 50, + "limit_test_batches": 500, + "accumulate_grad_batches": 1, + "gradient_clip_val": 1.0, + "benchmark": False, + "enable_model_summary": False, + "strategy": NLPDDPStrategy(), + } + + model_dict.update(override_model_dict) + model_dict["tokenizer"] = tokenizer_dict + + omega_cfg = OmegaConf.create(model_dict) + + trainer = pl.Trainer(**trainer_dict) + + logging.info("Creating Megatron model...") + model = MegatronGPTModel(omega_cfg, trainer) + logging.info(f"Created model:\n{model}") + + logging.info("Loading HuggingFace model...") + model_hf = AutoModelForCausalLM.from_pretrained(args.input) + logging.info(f"Loaded model:\n{model_hf}") + + state_dict_hf = model_hf.state_dict() + convert_dict = convert_state_dict(state_dict_hf, amp=omega_cfg.megatron_amp_O2) + + logging.info("Loading state dict...") + missing_keys, unexpected_keys = model.load_state_dict(convert_dict, strict=False) + + if missing_keys: + # Keys ending with '_extra_state' are related to Transformer Engine internals + missing_keys_non_extra = [key for key in missing_keys if not key.endswith("_extra_state")] + if missing_keys_non_extra: + logging.critical("Missing keys were detected during the load, something has gone wrong. Aborting.") + raise RuntimeError(f"Missing keys: \n{missing_keys_non_extra}") + + if unexpected_keys: + logging.critical("Unexpected keys were detected which should not happen. Aborting.") + raise RuntimeError(f"Unexpected keys: \n{unexpected_keys}") + + logging.info("Saving model...") + # We make sure that the tokenizer can be instantiated later regardless of args.input + model.cfg.tokenizer.update(type="bigcode/starcoder") + dtype = torch.bfloat16 if args.precision == "bf16" else torch.float32 + model = model.to(dtype=dtype) + model.cfg.update(use_cpu_initialization=False) + model.save_to(os.path.join(args.output, "megatron_starcoder_tp1_pp1.nemo")) + logging.info("Done.") From 00767249fc7f5276d921762b78ecf46100190ecf Mon Sep 17 00:00:00 2001 From: Kelvin Liu Date: Fri, 22 Sep 2023 01:57:02 +0800 Subject: [PATCH 046/112] fix bug when loading dist ckpt in peft (#7452) Signed-off-by: Hongbin Liu Co-authored-by: Hongbin Liu Signed-off-by: Sasha Meister --- nemo/collections/nlp/parts/nlp_overrides.py | 65 ++++++++++++++++++--- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/nemo/collections/nlp/parts/nlp_overrides.py b/nemo/collections/nlp/parts/nlp_overrides.py index d7eaaa90e536..a116c8e60299 100644 --- a/nemo/collections/nlp/parts/nlp_overrides.py +++ b/nemo/collections/nlp/parts/nlp_overrides.py @@ -740,7 +740,8 @@ def _load_state_dict_from_disk(self, model_weights, map_location=None): peft_state_dict = torch.load(model_weights_path, map_location)['state_dict'] else: peft_state_dict = {} - base_model_state_dict.update(peft_state_dict) # add the PEFT state_dict into the base model's state_dict + if base_model_state_dict: + base_model_state_dict.update(peft_state_dict) # add the PEFT state_dict into the base model's state_dict return base_model_state_dict def restore_from( @@ -765,13 +766,61 @@ def restore_from( return loaded_params conf, instance, state_dict = loaded_params - if ( - self.peft_model_nemo_path is None and self.peft_model_ckpt_dir is None - ): # we have this check only for training PEFT from scratch - peft_state_dict = instance.get_peft_state_dict() - state_dict.update(peft_state_dict) - state_dict = self.modify_state_dict(conf, state_dict) - self.load_instance_with_state_dict(instance, state_dict, strict) + # if we're using dist checkpointing then state_dict will be None + if state_dict is None: + # dist checkpointing needs torch.distributed to load the checkpoint + if parallel_state.is_unitialized(): + + def dummy(): + return + + if trainer.strategy.launcher is not None: + trainer.strategy.launcher.launch(dummy, trainer=trainer) + trainer.strategy.setup_environment() + + with tempfile.TemporaryDirectory() as tmpdir: + # Check if self.model_extracted_dir is set, and is a valid path + if self.model_extracted_dir is not None and os.path.isdir(self.model_extracted_dir): + # Log that NeMo will use the provided `model_extracted_dir` + logging.info( + f"Restoration will occur within pre-extracted directory : " f"`{self.model_extracted_dir}`." + ) + + # Override `tmpdir` above with the pre-extracted `model_extracted_dir` + tmpdir = self.model_extracted_dir + + else: + # Extract the nemo file into the temporary directory + self._unpack_nemo_file( + path2file=restore_path, out_folder=tmpdir, extract_config_only=return_config is True + ) + checkpoint = {} + sharded_state_dict = instance.sharded_state_dict() + peft_state_dict = instance.get_peft_state_dict() + for k in peft_state_dict.keys(): + sharded_state_dict.pop(k) + checkpoint['state_dict'] = sharded_state_dict + # remove model weights extension + tmp_model_weights_ckpt = os.path.join(tmpdir, self.model_weights_ckpt) + tmp_model_weights_dir = os.path.splitext(tmp_model_weights_ckpt)[0] + assert os.path.isdir(tmp_model_weights_dir), f'Expected {tmp_model_weights_dir} to be a directory.' + checkpoint = dist_checkpointing.load( + sharded_state_dict=checkpoint, checkpoint_dir=tmp_model_weights_dir + ) + checkpoint['state_dict'].update(peft_state_dict) + instance.on_load_checkpoint(checkpoint) + if hasattr(instance, 'setup_transformer_engine_tp_groups'): + instance.setup_transformer_engine_tp_groups() + + else: + if ( + self.peft_model_nemo_path is None and self.peft_model_ckpt_dir is None + ): # we have this check only for training PEFT from scratch + peft_state_dict = instance.get_peft_state_dict() + state_dict.update(peft_state_dict) + state_dict = self.modify_state_dict(conf, state_dict) + self.load_instance_with_state_dict(instance, state_dict, strict) + logging.info(f'Model {instance.__class__.__name__} was successfully restored from {restore_path}.') return instance From 45babd24e7debd02554b211881868fe596f16046 Mon Sep 17 00:00:00 2001 From: Tamerlan Tabolov Date: Thu, 21 Sep 2023 20:11:45 +0200 Subject: [PATCH 047/112] Fix adding positional embeddings in-place in transformer module (#7440) Signed-off-by: Tamerlan Tabolov Co-authored-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Signed-off-by: Sasha Meister --- nemo/collections/tts/modules/transformer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nemo/collections/tts/modules/transformer.py b/nemo/collections/tts/modules/transformer.py index 3dda8c522dcc..728b583919ff 100644 --- a/nemo/collections/tts/modules/transformer.py +++ b/nemo/collections/tts/modules/transformer.py @@ -246,7 +246,7 @@ def forward(self, input, seq_lens, conditioning=None): def _forward(self, inp, mask, conditioning): pos_seq = torch.arange(inp.size(1), device=inp.device).to(inp.dtype) pos_emb = self.pos_emb(pos_seq) * mask - inp += pos_emb + inp = inp + pos_emb inp = self.cond_input(inp, conditioning) out = self.drop(inp) From 84584c04775baa73499e367e147b9ff377a8d0e9 Mon Sep 17 00:00:00 2001 From: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Date: Thu, 21 Sep 2023 22:13:25 -0400 Subject: [PATCH 048/112] Fix (#7478) Signed-off-by: Cheng-Ping Hsieh Signed-off-by: Sasha Meister --- .../nlp/data/language_modeling/megatron/gpt_sft_chat_dataset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_chat_dataset.py b/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_chat_dataset.py index 55bce820ca8f..801a58394f06 100644 --- a/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_chat_dataset.py +++ b/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_chat_dataset.py @@ -289,6 +289,7 @@ def collate_fn(self, batch): labels = [x[: self.max_seq_length] for x in labels] loss_mask = [x[: self.max_seq_length] for x in loss_mask] contexts = [x[: self.max_seq_length] for x in contexts] + answers = [x[: self.max_seq_length] for x in answers] # increase max length to nearest multiple of 4 or 8 if self.pad_to_max_length: From 37cc67f00bd0fa4158be8528e0d4d694f9afb8a2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 23 Sep 2023 23:10:20 -0700 Subject: [PATCH 049/112] add sleep (#7498) (#7499) * add sleep * add sleep onto config instead * add comment --------- Signed-off-by: Gerald Shen Co-authored-by: Gerald Shen <119401249+gshennvm@users.noreply.github.com> Signed-off-by: Sasha Meister --- nemo/utils/exp_manager.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/nemo/utils/exp_manager.py b/nemo/utils/exp_manager.py index 1629aa5cbb50..addbf4eda617 100644 --- a/nemo/utils/exp_manager.py +++ b/nemo/utils/exp_manager.py @@ -170,6 +170,8 @@ class ExpManagerConfig: ema: Optional[EMAParams] = EMAParams() # Wall clock time limit max_time_per_run: Optional[str] = None + # time to sleep non 0 ranks during initialization + seconds_to_sleep: float = 5 class TimingCallback(Callback): @@ -301,6 +303,7 @@ def exp_manager(trainer: 'pytorch_lightning.Trainer', cfg: Optional[Union[DictCo Set this to True if you are using DDP with many GPUs and do not want many log files in your exp dir. - max_time (str): The maximum wall clock time *per run*. This is intended to be used on clusters where you want a checkpoint to be saved after this specified time and be able to resume from that checkpoint. Defaults to None. + - seconds_to_sleep (float): seconds to sleep non rank 0 processes for. Used to give enough time for rank 0 to initialize returns: log_dir (Path): The final logging directory where logging files are saved. Usually the concatenation of @@ -501,6 +504,11 @@ def exp_manager(trainer: 'pytorch_lightning.Trainer', cfg: Optional[Union[DictCo # Add lightning file logging to global_rank zero add_filehandlers_to_pl_logger(log_dir / 'lightning_logs.txt', log_dir / 'nemo_error_log.txt') + elif trainer.num_devices * trainer.num_devices > 1: + # sleep other ranks so rank 0 can finish + # doing the initialization such as moving files + time.sleep(cfg.seconds_to_sleep) + return log_dir From 2b76509251b888af390124e5073a362f5fb662b3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:04:27 -0700 Subject: [PATCH 050/112] Fix exp manager check for sleep (#7503) (#7504) Signed-off-by: smajumdar Co-authored-by: Somshubra Majumdar Signed-off-by: Sasha Meister --- nemo/utils/exp_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nemo/utils/exp_manager.py b/nemo/utils/exp_manager.py index addbf4eda617..1125ae06cabb 100644 --- a/nemo/utils/exp_manager.py +++ b/nemo/utils/exp_manager.py @@ -504,7 +504,7 @@ def exp_manager(trainer: 'pytorch_lightning.Trainer', cfg: Optional[Union[DictCo # Add lightning file logging to global_rank zero add_filehandlers_to_pl_logger(log_dir / 'lightning_logs.txt', log_dir / 'nemo_error_log.txt') - elif trainer.num_devices * trainer.num_devices > 1: + elif trainer.num_nodes * trainer.num_devices > 1: # sleep other ranks so rank 0 can finish # doing the initialization such as moving files time.sleep(cfg.seconds_to_sleep) From 2585e0a97b1405cd47331b013e7e6ea889d891a1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 14:21:17 -0700 Subject: [PATCH 051/112] bugfix: trainer.accelerator=auto from None. (#7492) (#7493) Signed-off-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Co-authored-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Signed-off-by: Sasha Meister --- tutorials/tts/Vits_Training.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/tts/Vits_Training.ipynb b/tutorials/tts/Vits_Training.ipynb index 84dad62bba6f..398ca377eff5 100644 --- a/tutorials/tts/Vits_Training.ipynb +++ b/tutorials/tts/Vits_Training.ipynb @@ -308,7 +308,7 @@ " phoneme_dict_path=tts_dataset_files/ipa_cmudict-0.7b_nv23.01.txt \\\n", " heteronyms_path=tts_dataset_files/heteronyms-052722 \\\n", " trainer.max_epochs=3 \\\n", - " trainer.accelerator=null \\\n", + " trainer.accelerator=auto \\\n", " trainer.check_val_every_n_epoch=1 \\\n", " trainer.devices=1)" ] From 9c8336578e9db8f29876e6ca3e7e4016596115ac Mon Sep 17 00:00:00 2001 From: Stas Bekman Date: Mon, 25 Sep 2023 16:55:05 -0700 Subject: [PATCH 052/112] [doc] fix broken link (#7481) Signed-off-by: Stas Bekman Signed-off-by: Sasha Meister --- docs/source/core/exp_manager.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/core/exp_manager.rst b/docs/source/core/exp_manager.rst index c23d902a26ee..5415ae403f38 100644 --- a/docs/source/core/exp_manager.rst +++ b/docs/source/core/exp_manager.rst @@ -32,7 +32,7 @@ Optionally, launch TensorBoard to view the training results in ``./nemo_experime .. If ``create_checkpoint_callback`` is set to ``True``, then NeMo automatically creates checkpoints during training -using PyTorch Lightning's `ModelCheckpoint `_. +using PyTorch Lightning's `ModelCheckpoint `_. We can configure the ``ModelCheckpoint`` via YAML or CLI. .. code-block:: yaml From 92f0eecdfa9929177196f750a7d4c8cfb561083d Mon Sep 17 00:00:00 2001 From: Ryan Langman Date: Mon, 25 Sep 2023 21:27:13 -0700 Subject: [PATCH 053/112] [TTS] Read audio as int32 to avoid flac read errors (#7477) * [TTS] Read audio as int32 to avoid flac read errors Signed-off-by: Ryan * [TTS] Add comment about read failures Signed-off-by: Ryan --------- Signed-off-by: Ryan Signed-off-by: Sasha Meister --- .../asr/parts/preprocessing/segment.py | 17 +++++++++++++---- nemo/collections/tts/data/vocoder_dataset.py | 6 ++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/nemo/collections/asr/parts/preprocessing/segment.py b/nemo/collections/asr/parts/preprocessing/segment.py index d586137d5ff2..f614d3d22186 100644 --- a/nemo/collections/asr/parts/preprocessing/segment.py +++ b/nemo/collections/asr/parts/preprocessing/segment.py @@ -375,7 +375,15 @@ def from_file_list( @classmethod def segment_from_file( - cls, audio_file, target_sr=None, n_segments=0, trim=False, orig_sr=None, channel_selector=None, offset=None + cls, + audio_file, + target_sr=None, + n_segments=0, + trim=False, + orig_sr=None, + channel_selector=None, + offset=None, + dtype='float32', ): """Grabs n_segments number of samples from audio_file. If offset is not provided, n_segments are selected randomly. @@ -390,6 +398,7 @@ def segment_from_file( :param orig_sr: the original sample rate :param channel selector: select a subset of channels. If set to `None`, the original signal will be used. :param offset: fixed offset in seconds + :param dtype: data type to load audio as. :return: numpy array of samples """ is_segmented = False @@ -412,15 +421,15 @@ def segment_from_file( f'Provided audio start ({audio_start}) is larger than the maximum possible ({max_audio_start})' ) f.seek(audio_start) - samples = f.read(n_segments_at_original_sr, dtype='float32') + samples = f.read(n_segments_at_original_sr, dtype=dtype) is_segmented = True elif n_segments_at_original_sr > len(f): logging.warning( f"Number of segments ({n_segments_at_original_sr}) is greater than the length ({len(f)}) of the audio file {audio_file}. This may lead to shape mismatch errors." ) - samples = f.read(dtype='float32') + samples = f.read(dtype=dtype) else: - samples = f.read(dtype='float32') + samples = f.read(dtype=dtype) except RuntimeError as e: logging.error(f"Loading {audio_file} via SoundFile raised RuntimeError: `{e}`.") raise e diff --git a/nemo/collections/tts/data/vocoder_dataset.py b/nemo/collections/tts/data/vocoder_dataset.py index a5a30870dfff..97e0648f8b11 100644 --- a/nemo/collections/tts/data/vocoder_dataset.py +++ b/nemo/collections/tts/data/vocoder_dataset.py @@ -122,11 +122,13 @@ def get_sampler(self, batch_size: int) -> Optional[torch.utils.data.Sampler]: return sampler def _segment_audio(self, audio_filepath: Path) -> AudioSegment: - # Retry file read multiple times as file seeking can produce random IO errors. + # File seeking sometimes fails when reading flac files with libsndfile < 1.0.30. + # Read audio as int32 to minimize issues, and retry read on a different segment in case of failure. + # https://github.com/bastibe/python-soundfile/issues/274 for _ in range(self.num_audio_retries): try: audio_segment = AudioSegment.segment_from_file( - audio_filepath, target_sr=self.sample_rate, n_segments=self.n_samples, + audio_filepath, target_sr=self.sample_rate, n_segments=self.n_samples, dtype="int32" ) return audio_segment except Exception: From c5d917bd186d9fc2a1690760c60cc2ddfe02e948 Mon Sep 17 00:00:00 2001 From: Robin Dong Date: Tue, 26 Sep 2023 14:36:26 +1000 Subject: [PATCH 054/112] Add dataset 'AISHELL-3' from OpenSLR for training mandarin TTS (#7409) * Add dataset 'AISHELL-3' from OpenSLR for training mandarin TTS * Train 'AISHELL-3' dataset with multi-speakers Signed-off-by: Robin Dong * Update get_data.py update copyright header Signed-off-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> * Update get_data.py added a disclaimer Signed-off-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add new configuration file for AISHELL3 with multispeaker of fastpitch Signed-off-by: Robin Dong --------- Signed-off-by: Robin Dong Signed-off-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../fastpitch_align_multispeaker_22050.yaml | 261 ++++++++++++++++++ .../ds_conf/ds_for_fastpitch_align.yaml | 49 ++++ .../tts/aishell3/get_data.py | 156 +++++++++++ 3 files changed, 466 insertions(+) create mode 100644 examples/tts/conf/zh/fastpitch_align_multispeaker_22050.yaml create mode 100755 scripts/dataset_processing/tts/aishell3/ds_conf/ds_for_fastpitch_align.yaml create mode 100755 scripts/dataset_processing/tts/aishell3/get_data.py diff --git a/examples/tts/conf/zh/fastpitch_align_multispeaker_22050.yaml b/examples/tts/conf/zh/fastpitch_align_multispeaker_22050.yaml new file mode 100644 index 000000000000..2464e546598e --- /dev/null +++ b/examples/tts/conf/zh/fastpitch_align_multispeaker_22050.yaml @@ -0,0 +1,261 @@ +# This config contains the default values for training FastPitch model with aligner using 22KHz sampling +# rate. If you want to train model on other dataset, you can change config values according to your dataset. +# Most dataset-specific arguments are in the head of the config file, see below. + +name: FastPitch + +train_dataset: ??? +validation_datasets: ??? +sup_data_path: ??? +sup_data_types: [ "align_prior_matrix", "pitch", "speaker_id"] + +# Default values from librosa.pyin +pitch_fmin: 65.40639132514966 +pitch_fmax: 1986.977294921875 + +# these frame-wise values depend on pitch_fmin and pitch_fmax, you can get values +# by running `scripts/dataset_processing/tts/extract_sup_data.py` +pitch_mean: ??? # e.g. 221.4948272705078 for SFbilingual dataset. +pitch_std: ??? # e.g. 64.6528930664063 for SFbilingual dataset. + +# Default values for dataset with sample_rate=22050 +sample_rate: 22050 +n_mel_channels: 80 +n_window_size: 1024 +n_window_stride: 256 +n_fft: 1024 +lowfreq: 0 +highfreq: null +window: hann + +# There are four candidates of `phoneme_dict_path` provided for Chinese as shown below, +# 1) 24-final Pinyin: "scripts/tts_dataset_files/zh/24finals/pinyin_dict_nv_22.10.txt", +# 2) IPA converted from 24-final Pinyin: "scripts/tts_dataset_files/zh/24finals/ipa_dict_nv23.05.txt", +# 3) 36-final Pinyin: "scripts/tts_dataset_files/zh/36finals/pinyin_dict_nv23.05.txt", +# 4) (default) IPA converted from 36-final Pinyin: "scripts/tts_dataset_files/zh/36finals/ipa_dict_nv23.05.txt" +# Suggest to choose IPA symbol set converted from 36-final Pinyin because better audio quality were observed. +phoneme_dict_path: "scripts/tts_dataset_files/zh/36finals/ipa_dict_nv23.05.txt" + +model: + learn_alignment: true + bin_loss_warmup_epochs: 100 + + n_speakers: 1958 + max_token_duration: 75 + symbols_embedding_dim: 384 + pitch_embedding_kernel_size: 3 + speaker_emb_condition_prosody: true + speaker_emb_condition_aligner: true + + pitch_fmin: ${pitch_fmin} + pitch_fmax: ${pitch_fmax} + + pitch_mean: ${pitch_mean} + pitch_std: ${pitch_std} + + sample_rate: ${sample_rate} + n_mel_channels: ${n_mel_channels} + n_window_size: ${n_window_size} + n_window_stride: ${n_window_stride} + n_fft: ${n_fft} + lowfreq: ${lowfreq} + highfreq: ${highfreq} + window: ${window} + + text_normalizer: + _target_: nemo_text_processing.text_normalization.normalize.Normalizer + lang: zh + input_case: cased + + text_normalizer_call_kwargs: + verbose: false + punct_pre_process: true + punct_post_process: true + + text_tokenizer: + _target_: nemo.collections.common.tokenizers.text_to_speech.tts_tokenizers.ChinesePhonemesTokenizer + punct: true + apostrophe: true + pad_with_space: true + g2p: + _target_: nemo.collections.tts.g2p.models.zh_cn_pinyin.ChineseG2p + phoneme_dict: ${phoneme_dict_path} + word_segmenter: jieba # Only jieba is supported now. + phoneme_prefix: "" + phoneme_case: lower + tone_prefix: "#" + ascii_letter_prefix: "" + ascii_letter_case: upper + + train_ds: + dataset: + _target_: nemo.collections.tts.data.dataset.TTSDataset + manifest_filepath: ${train_dataset} + sample_rate: ${model.sample_rate} + sup_data_path: ${sup_data_path} + sup_data_types: ${sup_data_types} + n_fft: ${model.n_fft} + win_length: ${model.n_window_size} + hop_length: ${model.n_window_stride} + window: ${model.window} + n_mels: ${model.n_mel_channels} + lowfreq: ${model.lowfreq} + highfreq: ${model.highfreq} + max_duration: null # change to null to include longer audios. + min_duration: 0.1 + ignore_file: null + trim: true + trim_top_db: 50 + trim_frame_length: ${model.n_window_size} + trim_hop_length: ${model.n_window_stride} + pitch_fmin: ${model.pitch_fmin} + pitch_fmax: ${model.pitch_fmax} + pitch_norm: true + pitch_mean: ${model.pitch_mean} + pitch_std: ${model.pitch_std} + + dataloader_params: + drop_last: false + shuffle: true + batch_size: 32 + num_workers: 12 + pin_memory: true + + validation_ds: + dataset: + _target_: nemo.collections.tts.data.dataset.TTSDataset + manifest_filepath: ${validation_datasets} + sample_rate: ${model.sample_rate} + sup_data_path: ${sup_data_path} + sup_data_types: ${sup_data_types} + n_fft: ${model.n_fft} + win_length: ${model.n_window_size} + hop_length: ${model.n_window_stride} + window: ${model.window} + n_mels: ${model.n_mel_channels} + lowfreq: ${model.lowfreq} + highfreq: ${model.highfreq} + max_duration: null # change to null to include longer audios. + min_duration: 0.1 + ignore_file: null + trim: true + trim_top_db: 50 + trim_frame_length: ${model.n_window_size} + trim_hop_length: ${model.n_window_stride} + pitch_fmin: ${model.pitch_fmin} + pitch_fmax: ${model.pitch_fmax} + pitch_norm: true + pitch_mean: ${model.pitch_mean} + pitch_std: ${model.pitch_std} + + dataloader_params: + drop_last: false + shuffle: false + batch_size: 32 + num_workers: 2 + pin_memory: true + + preprocessor: + _target_: nemo.collections.asr.modules.AudioToMelSpectrogramPreprocessor + features: ${model.n_mel_channels} + lowfreq: ${model.lowfreq} + highfreq: ${model.highfreq} + n_fft: ${model.n_fft} + n_window_size: ${model.n_window_size} + window_size: false + n_window_stride: ${model.n_window_stride} + window_stride: false + pad_to: 1 + pad_value: 0 + sample_rate: ${model.sample_rate} + window: ${model.window} + normalize: null + preemph: null + dither: 0.0 + frame_splicing: 1 + log: true + log_zero_guard_type: add + log_zero_guard_value: 1e-05 + mag_power: 1.0 + + input_fft: #n_embed and padding_idx are added by the model + _target_: nemo.collections.tts.modules.transformer.FFTransformerEncoder + n_layer: 6 + n_head: 1 + d_model: ${model.symbols_embedding_dim} + d_head: 64 + d_inner: 1536 + kernel_size: 3 + dropout: 0.1 + dropatt: 0.1 + dropemb: 0.0 + d_embed: ${model.symbols_embedding_dim} + + output_fft: + _target_: nemo.collections.tts.modules.transformer.FFTransformerDecoder + n_layer: 6 + n_head: 1 + d_model: ${model.symbols_embedding_dim} + d_head: 64 + d_inner: 1536 + kernel_size: 3 + dropout: 0.1 + dropatt: 0.1 + dropemb: 0.0 + + alignment_module: + _target_: nemo.collections.tts.modules.aligner.AlignmentEncoder + n_text_channels: ${model.symbols_embedding_dim} + + duration_predictor: + _target_: nemo.collections.tts.modules.fastpitch.TemporalPredictor + input_size: ${model.symbols_embedding_dim} + kernel_size: 3 + filter_size: 256 + dropout: 0.1 + n_layers: 2 + + pitch_predictor: + _target_: nemo.collections.tts.modules.fastpitch.TemporalPredictor + input_size: ${model.symbols_embedding_dim} + kernel_size: 3 + filter_size: 256 + dropout: 0.1 + n_layers: 2 + + optim: + name: adamw + lr: 1e-3 + betas: [0.9, 0.999] + weight_decay: 1e-6 + + sched: + name: NoamAnnealing + warmup_steps: 1000 + last_epoch: -1 + d_model: 1 # Disable scaling based on model dim + +trainer: + num_nodes: 1 + devices: -1 # number of gpus + accelerator: gpu + strategy: ddp + precision: 16 + max_epochs: 5000 + accumulate_grad_batches: 1 + gradient_clip_val: 1000.0 + enable_checkpointing: false # Provided by exp_manager + logger: false # Provided by exp_manager + log_every_n_steps: 100 + check_val_every_n_epoch: 5 + benchmark: false + +exp_manager: + exp_dir: null + name: ${name} + create_tensorboard_logger: true + create_checkpoint_callback: true + checkpoint_callback_params: + monitor: val_loss + resume_if_exists: false + resume_ignore_no_checkpoint: false diff --git a/scripts/dataset_processing/tts/aishell3/ds_conf/ds_for_fastpitch_align.yaml b/scripts/dataset_processing/tts/aishell3/ds_conf/ds_for_fastpitch_align.yaml new file mode 100755 index 000000000000..f8298d7c2680 --- /dev/null +++ b/scripts/dataset_processing/tts/aishell3/ds_conf/ds_for_fastpitch_align.yaml @@ -0,0 +1,49 @@ +name: "ds_for_fastpitch_align" + +manifest_filepath: "train_manifest.json" +sup_data_path: "sup_data" +sup_data_types: [ "align_prior_matrix", "pitch", "speaker_id"] +phoneme_dict_path: "scripts/tts_dataset_files/zh/24finals/pinyin_dict_nv_22.10.txt" + +dataset: + _target_: nemo.collections.tts.data.dataset.TTSDataset + manifest_filepath: ${manifest_filepath} + sample_rate: 22050 + sup_data_path: ${sup_data_path} + sup_data_types: ${sup_data_types} + n_fft: 1024 + win_length: 1024 + hop_length: 256 + window: "hann" + n_mels: 80 + lowfreq: 0 + highfreq: null + max_duration: null + min_duration: 0.1 + ignore_file: null + trim: true + trim_top_db: 50 + trim_frame_length: 1024 + trim_hop_length: 256 + pitch_fmin: 65.40639132514966 + pitch_fmax: 2093.004522404789 + + text_normalizer: + _target_: nemo_text_processing.text_normalization.normalize.Normalizer + lang: zh + input_case: cased + + text_normalizer_call_kwargs: + verbose: false + punct_pre_process: true + punct_post_process: true + + text_tokenizer: + _target_: nemo.collections.common.tokenizers.text_to_speech.tts_tokenizers.ChinesePhonemesTokenizer + punct: true + apostrophe: true + pad_with_space: true + g2p: + _target_: nemo.collections.tts.g2p.models.zh_cn_pinyin.ChineseG2p + phoneme_dict: ${phoneme_dict_path} + word_segmenter: jieba # Only jieba is supported now. diff --git a/scripts/dataset_processing/tts/aishell3/get_data.py b/scripts/dataset_processing/tts/aishell3/get_data.py new file mode 100755 index 000000000000..904ab0314653 --- /dev/null +++ b/scripts/dataset_processing/tts/aishell3/get_data.py @@ -0,0 +1,156 @@ +# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Disclaimer: +# Each user is responsible for checking the content of datasets and the applicable licenses and determining if suitable for the intended use. + +import argparse +import json +import os +import random +import subprocess +import tarfile +import urllib.request +from pathlib import Path + +import numpy as np +from nemo_text_processing.text_normalization.normalize import Normalizer +from opencc import OpenCC + +URL = "https://www.openslr.org/resources/93/data_aishell3.tgz" + + +def get_args(): + parser = argparse.ArgumentParser( + description='Prepare SF_bilingual dataset and create manifests with predefined split' + ) + + parser.add_argument( + "--data-root", + type=Path, + help="where the dataset will reside", + default="./DataChinese/sf_bilingual_speech_zh_en_vv1/SF_bilingual/", + ) + parser.add_argument( + "--manifests-path", type=Path, help="where the resulting manifests files will reside", default="./" + ) + parser.add_argument("--val-size", default=0.01, type=float, help="eval set split") + parser.add_argument("--test-size", default=0.01, type=float, help="test set split") + parser.add_argument( + "--seed-for-ds-split", + default=100, + type=float, + help="Seed for deterministic split of train/dev/test, NVIDIA's default is 100", + ) + + args = parser.parse_args() + return args + + +def __maybe_download_file(source_url, destination_path): + if not destination_path.exists(): + tmp_file_path = destination_path.with_suffix('.tmp') + urllib.request.urlretrieve(source_url, filename=str(tmp_file_path)) + tmp_file_path.rename(destination_path) + + +def __extract_file(filepath, data_dir): + try: + tar = tarfile.open(filepath) + tar.extractall(data_dir) + tar.close() + except Exception: + print(f"Error while extracting {filepath}. Already extracted?") + + +def __process_transcript(file_path: str): + # Create directory for processed wav files + Path(file_path / "processed").mkdir(parents=True, exist_ok=True) + # Create zh-TW to zh-simplify converter + cc = OpenCC('t2s') + # Create normalizer + text_normalizer = Normalizer( + lang="zh", input_case="cased", overwrite_cache=True, cache_dir=str(file_path / "cache_dir"), + ) + text_normalizer_call_kwargs = {"punct_pre_process": True, "punct_post_process": True} + normalizer_call = lambda x: text_normalizer.normalize(x, **text_normalizer_call_kwargs) + entries = [] + i = 0 + SPEAKER_LEN = 7 + with open(file_path / "train" / "content.txt", encoding="utf-8") as fin: + for line in fin: + content = line.split() + wav_name, text = content[0], "".join(content[1::2]) + "。" + wav_name = wav_name.replace(u'\ufeff', '') + speaker = wav_name[:SPEAKER_LEN] + wav_file = file_path / "train" / "wav" / speaker / wav_name + assert os.path.exists(wav_file), f"{wav_file} not found!" + duration = subprocess.check_output(f"soxi -D {wav_file}", shell=True) + if float(duration) <= 3.0: # filter out wav files shorter than 3 seconds + continue + processed_file = file_path / "processed" / wav_name + # convert wav to mono 22050HZ, 16 bit (as SFSpeech dataset) + subprocess.run(f"sox {wav_file} -r 22050 -c 1 -b 16 {processed_file}", shell=True) + simplified_text = cc.convert(text) + normalized_text = normalizer_call(simplified_text) + entry = { + 'audio_filepath': os.path.abspath(processed_file), + 'duration': float(duration), + 'text': text, + 'normalized_text': normalized_text, + 'speaker': int(speaker[3:]), + } + + i += 1 + entries.append(entry) + return entries + + +def __process_data(dataset_path, val_size, test_size, seed_for_ds_split, manifests_dir): + entries = __process_transcript(dataset_path) + + random.Random(seed_for_ds_split).shuffle(entries) + + train_size = 1.0 - val_size - test_size + train_entries, validate_entries, test_entries = np.split( + entries, [int(len(entries) * train_size), int(len(entries) * (train_size + val_size))] + ) + + assert len(train_entries) > 0, "Not enough data for train, val and test" + + def save(p, data): + with open(p, 'w') as f: + for d in data: + f.write(json.dumps(d) + '\n') + + save(manifests_dir / "train_manifest.json", train_entries) + save(manifests_dir / "val_manifest.json", validate_entries) + save(manifests_dir / "test_manifest.json", test_entries) + + +def main(): + args = get_args() + + tarred_data_path = args.data_root / "data_aishell3.tgz" + + __maybe_download_file(URL, tarred_data_path) + __extract_file(str(tarred_data_path), str(args.data_root)) + + __process_data( + args.data_root, args.val_size, args.test_size, args.seed_for_ds_split, args.manifests_path, + ) + + +if __name__ == "__main__": + main() From e12d30ad38f21f66c09a6dee6f4c7b49077b9b70 Mon Sep 17 00:00:00 2001 From: Stas Bekman Date: Tue, 26 Sep 2023 07:49:24 -0700 Subject: [PATCH 055/112] dllogger - log on rank 0 only (#7513) Signed-off-by: Stas Bekman Signed-off-by: Sasha Meister --- nemo/utils/loggers/dllogger.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nemo/utils/loggers/dllogger.py b/nemo/utils/loggers/dllogger.py index 0b2fde061ff9..cdeef63b75f7 100644 --- a/nemo/utils/loggers/dllogger.py +++ b/nemo/utils/loggers/dllogger.py @@ -20,6 +20,7 @@ from lightning_utilities.core.apply_func import apply_to_collection from omegaconf import DictConfig, ListConfig, OmegaConf from pytorch_lightning.loggers import Logger +from pytorch_lightning.utilities import rank_zero_only from pytorch_lightning.utilities.parsing import AttributeDict from nemo.utils import logging @@ -81,6 +82,7 @@ def __init__(self, stdout: bool, verbose: bool, json_file: str): ) dllogger.init(backends=backends) + @rank_zero_only def log_hyperparams(self, params, *args, **kwargs): if isinstance(params, Namespace): params = vars(params) @@ -91,6 +93,7 @@ def log_hyperparams(self, params, *args, **kwargs): params = _sanitize_callable_params(_flatten_dict(_convert_params(params))) dllogger.log(step="PARAMETER", data=params) + @rank_zero_only def log_metrics(self, metrics, step=None): if step is None: step = tuple() From e60cd386ca64cdab8467fbbfe20b2ff444590366 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 10:24:49 -0700 Subject: [PATCH 056/112] Fix TTS FastPitch tutorial (#7494) (#7516) * Fix --------- Signed-off-by: Cheng-Ping Hsieh Co-authored-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Signed-off-by: Sasha Meister --- tutorials/tts/FastPitch_Adapter_Finetuning.ipynb | 6 +++--- tutorials/tts/FastPitch_MultiSpeaker_Pretraining.ipynb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tutorials/tts/FastPitch_Adapter_Finetuning.ipynb b/tutorials/tts/FastPitch_Adapter_Finetuning.ipynb index 263d22b60599..5fe61d596f4b 100644 --- a/tutorials/tts/FastPitch_Adapter_Finetuning.ipynb +++ b/tutorials/tts/FastPitch_Adapter_Finetuning.ipynb @@ -100,7 +100,7 @@ "# .nemo files for your pre-trained FastPitch and HiFiGAN\n", "pretrained_fastpitch_checkpoint = \"\"\n", "finetuned_hifigan_on_multispeaker_checkpoint = \"\"\n", - "use_ipa = True #Set to False while using Arpabet." + "use_ipa = False #Set to False while using Arpabet." ] }, { @@ -505,7 +505,7 @@ "+exp_manager.wandb_logger_kwargs.name=\"tutorial-FastPitch-finetune-adaptation\" \\\n", "+exp_manager.wandb_logger_kwargs.project=\"NeMo\" \\\n", "+exp_manager.checkpoint_callback_params.save_top_k=-1 \\\n", - "trainer.max_epochs=200 \\\n", + "trainer.max_epochs=20 \\\n", "trainer.check_val_every_n_epoch=10 \\\n", "trainer.log_every_n_steps=1 \\\n", "trainer.devices=1 \\\n", @@ -612,7 +612,7 @@ "model.optim.lr=0.0001 \\\n", "model/train_ds=train_ds_finetune \\\n", "model/validation_ds=val_ds_finetune \\\n", - "+trainer.max_epochs=500 \\\n", + "+trainer.max_epochs=50 \\\n", "trainer.check_val_every_n_epoch=5 \\\n", "trainer.devices=-1 \\\n", "trainer.strategy='ddp' \\\n", diff --git a/tutorials/tts/FastPitch_MultiSpeaker_Pretraining.ipynb b/tutorials/tts/FastPitch_MultiSpeaker_Pretraining.ipynb index cb5cb651d76e..a031723f549b 100644 --- a/tutorials/tts/FastPitch_MultiSpeaker_Pretraining.ipynb +++ b/tutorials/tts/FastPitch_MultiSpeaker_Pretraining.ipynb @@ -511,7 +511,7 @@ "+trainer.max_epochs=5 \\\n", "trainer.check_val_every_n_epoch=5 \\\n", "trainer.devices=1 \\\n", - "trainer.strategy='ddp' \\\n", + "trainer.strategy='auto' \\\n", "trainer.precision=16 \\\n", "exp_manager.exp_dir={logs_dir} \\\n", "exp_manager.create_wandb_logger=True \\\n", From 1c3c43cacbdc35fd71dbc78c1dafa0df5bd4e48a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 10:25:23 -0700 Subject: [PATCH 057/112] Fix get_dist() tensor dimension (#7506) (#7515) Signed-off-by: Jocelyn Huang Co-authored-by: Jocelyn Signed-off-by: Sasha Meister --- nemo/collections/tts/modules/aligner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nemo/collections/tts/modules/aligner.py b/nemo/collections/tts/modules/aligner.py index 2910602474fd..f044a86a52eb 100644 --- a/nemo/collections/tts/modules/aligner.py +++ b/nemo/collections/tts/modules/aligner.py @@ -98,7 +98,7 @@ def get_dist(self, keys, queries, mask=None): self._apply_mask(dist, mask, float("inf")) - return dist + return dist.squeeze(1) @staticmethod def get_euclidean_dist(queries_enc, keys_enc): From c2d70e6b0ec67981aadbc83aedb00d73cb34e0bd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 10:25:50 -0700 Subject: [PATCH 058/112] bugfix: specify trainer.strategy=auto when devices=1 (#7509) (#7512) Signed-off-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Co-authored-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Signed-off-by: Sasha Meister --- tutorials/tts/Vits_Training.ipynb | 1 + 1 file changed, 1 insertion(+) diff --git a/tutorials/tts/Vits_Training.ipynb b/tutorials/tts/Vits_Training.ipynb index 398ca377eff5..296661c6ca5e 100644 --- a/tutorials/tts/Vits_Training.ipynb +++ b/tutorials/tts/Vits_Training.ipynb @@ -309,6 +309,7 @@ " heteronyms_path=tts_dataset_files/heteronyms-052722 \\\n", " trainer.max_epochs=3 \\\n", " trainer.accelerator=auto \\\n", + " trainer.strategy=auto \\\n", " trainer.check_val_every_n_epoch=1 \\\n", " trainer.devices=1)" ] From 9317d15281bc8ab702e19bd9ff5a0bdef200abdb Mon Sep 17 00:00:00 2001 From: Abhinav Khattar Date: Tue, 26 Sep 2023 13:30:31 -0400 Subject: [PATCH 059/112] fix (#7511) Signed-off-by: Abhinav Khattar Signed-off-by: Sasha Meister --- examples/nlp/language_modeling/tuning/megatron_gpt_sft.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py b/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py index 15d3dc53c348..2d0a0d38a762 100644 --- a/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py +++ b/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py @@ -66,6 +66,9 @@ def _modify_config(gpt_cfg, cfg, add_cfg_to_tree=False): gpt_cfg.attention_dropout = cfg.model.get('attention_dropout', 0.0) gpt_cfg.ffn_dropout = cfg.model.ffn_dropout gpt_cfg.use_flash_attention = cfg.model.get('use_flash_attention', False) + gpt_cfg.tensor_model_parallel_size = cfg.model.get('tensor_model_parallel_size', 1) + gpt_cfg.pipeline_model_parallel_size = cfg.model.get('pipeline_model_parallel_size', 1) + gpt_cfg.pipeline_model_parallel_split_rank = cfg.model.get('pipeline_model_parallel_split_rank', 0) sft_cls = MegatronGPTSFTModel gpt_cfg.target = f"{sft_cls.__module__}.{sft_cls.__name__}" From 20b15f3bf6d503653446da6cf6a1cc9c1ebafb4c Mon Sep 17 00:00:00 2001 From: Ryan Langman Date: Tue, 26 Sep 2023 17:14:16 -0700 Subject: [PATCH 060/112] [TTS] Fix FastPitch data prep tutorial (#7524) Signed-off-by: Ryan Signed-off-by: Sasha Meister --- scripts/dataset_processing/tts/preprocess_text.py | 4 ++-- tutorials/tts/FastPitch_Data_Preparation.ipynb | 14 +++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/scripts/dataset_processing/tts/preprocess_text.py b/scripts/dataset_processing/tts/preprocess_text.py index 2edc52969140..580a84a02d6f 100644 --- a/scripts/dataset_processing/tts/preprocess_text.py +++ b/scripts/dataset_processing/tts/preprocess_text.py @@ -22,9 +22,9 @@ --input_manifest="/manifest.json" \ --output_manifest="/manifest_processed.json" \ --normalizer_config_path="/examples/tts/conf/text/normalizer_en.yaml" \ - --lower_case=True \ + --lower_case \ --num_workers=4 \ - --batch_size=16 + --joblib_batch_size=16 """ import argparse diff --git a/tutorials/tts/FastPitch_Data_Preparation.ipynb b/tutorials/tts/FastPitch_Data_Preparation.ipynb index 46778759d5cb..5a42018c70b5 100644 --- a/tutorials/tts/FastPitch_Data_Preparation.ipynb +++ b/tutorials/tts/FastPitch_Data_Preparation.ipynb @@ -332,6 +332,8 @@ "lower_case = True\n", "# Whether to overwrite output manifest, if it exists\n", "overwrite_manifest = True\n", + "# Batch size for joblib parallelization. Increasing this value might speed up the script, depending on your CPU.\n", + "joblib_batch_size = 16\n", "\n", "# Python wrapper to invoke the given bash script with the given input args\n", "def run_script(script, args):\n", @@ -351,8 +353,10 @@ " f\"--output_manifest={output_filepath}\",\n", " f\"--num_workers={num_workers}\",\n", " f\"--normalizer_config_path={normalizer_config_filepath}\",\n", - " f\"--lower_case={lower_case}\"\n", + " f\"--joblib_batch_size={joblib_batch_size}\"\n", " ]\n", + " if lower_case:\n", + " args.append(\"--lower_case\")\n", " if overwrite_manifest:\n", " args.append(\"--overwrite\")\n", "\n", @@ -787,7 +791,7 @@ "\n", "We will train HiFi-GAN first so that we can use it to help evaluate the performance of FastPitch as it is being trained.\n", "\n", - "HiFi-GAN training only requires a manifest with with the `audio_filepath` field. All other fields in the manifest are for FastPitch training.\n", + "HiFi-GAN training only requires a manifest with the `audio_filepath` field. All other fields in the manifest are for FastPitch training.\n", "\n", "Here we show how to train these models from scratch. You can also fine-tune them from pretrained checkpoints as mentioned in our [FastPitch fine-tuning tutorial](https://github.com/NVIDIA/NeMo/blob/main/tutorials/tts/FastPitch_Finetuning.ipynb), but pretrained checkpoints compatible with these experimental recipes are not yet available on NGC.\n" ], @@ -914,7 +918,7 @@ { "cell_type": "code", "source": [ - "hifigan_log_epoch_dir = hifigan_log_dir / \"epoch_10\"\n", + "hifigan_log_epoch_dir = hifigan_log_dir / \"epoch_10\" / dataset_name\n", "!ls $hifigan_log_epoch_dir" ], "metadata": { @@ -966,7 +970,7 @@ "1. Training manifest(s) with `audio_filepath` and `text` or `normalized_text` fields.\n", "2. Precomputed features such as *pitch* and *energy* specified in the feature [config file](https://github.com/NVIDIA/NeMo/blob/main/examples/tts/conf/feature/feature_44100.yaml).\n", "3. (Optional) Statistics file for normalizing features.\n", - "4. (Optional) For a multi-speaker model, the manifest needs a `speaker` field amd JSON file mapping speaker IDs to speaker indices.\n", + "4. (Optional) For a multi-speaker model, the manifest needs a `speaker` field and JSON file mapping speaker IDs to speaker indices.\n", "5. (Optional) To train with IPA phonemes, a [phoneme dictionary](https://github.com/NVIDIA/NeMo/blob/main/scripts/tts_dataset_files/ipa_cmudict-0.7b_nv23.01.txt) and optional [heteronyms file](https://github.com/NVIDIA/NeMo/blob/main/scripts/tts_dataset_files/heteronyms-052722)\n", "6. (Optional) HiFi-GAN checkpoint or [NGC model name](https://github.com/NVIDIA/NeMo/blob/main/nemo/collections/tts/models/hifigan.py#L413) for generating audio predictions during training.\n", "\n" @@ -1093,7 +1097,7 @@ { "cell_type": "code", "source": [ - "faspitch_log_epoch_dir = fastpitch_log_dir / \"epoch_10\"\n", + "faspitch_log_epoch_dir = fastpitch_log_dir / \"epoch_10\" / dataset_name\n", "!ls $faspitch_log_epoch_dir" ], "metadata": { From 8b159846b9b7b8a4124b62d0bbe1fd4ec93dd2aa Mon Sep 17 00:00:00 2001 From: Giacomo Leone Maria Cavallini <72698188+GiacomoLeoneMaria@users.noreply.github.com> Date: Wed, 27 Sep 2023 08:12:26 +0200 Subject: [PATCH 061/112] add italian tokenization (#7486) * add italian tokenization Signed-off-by: GiacomoLeoneMaria * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add more ipa lexicon it Signed-off-by: GiacomoLeoneMaria * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix error deletion Signed-off-by: GiacomoLeoneMaria * add test Signed-off-by: GiacomoLeoneMaria * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: GiacomoLeoneMaria Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../tokenizers/text_to_speech/ipa_lexicon.py | 50 +++++++++++++++++-- .../text_to_speech/tokenizer_utils.py | 5 ++ .../text_to_speech/tts_tokenizers.py | 32 +++++++++++- .../text_to_speech/test_tts_tokenizers.py | 13 +++++ 4 files changed, 95 insertions(+), 5 deletions(-) diff --git a/nemo/collections/common/tokenizers/text_to_speech/ipa_lexicon.py b/nemo/collections/common/tokenizers/text_to_speech/ipa_lexicon.py index 746c783bd1b6..2e1bb359102b 100644 --- a/nemo/collections/common/tokenizers/text_to_speech/ipa_lexicon.py +++ b/nemo/collections/common/tokenizers/text_to_speech/ipa_lexicon.py @@ -15,7 +15,7 @@ # fmt: off -SUPPORTED_LOCALES = ["en-US", "de-DE", "es-ES"] +SUPPORTED_LOCALES = ["en-US", "de-DE", "es-ES", "it-IT"] DEFAULT_PUNCTUATION = ( ',', '.', '!', '?', '-', @@ -48,6 +48,12 @@ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ü', 'ẞ', ), + "it-IT": ( + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'À', 'È', 'É', 'Ì', + 'Ò', 'Ù' + ), } IPA_CHARACTER_SETS = { @@ -70,7 +76,20 @@ 'w', 'x', 'y', 'z', 'ç', 'ø', 'ŋ', 'œ', 'ɐ', 'ɑ', 'ɒ', 'ɔ', 'ə', 'ɛ', 'ɜ', 'ɡ', 'ɪ', 'ɹ', 'ɾ', 'ʃ', 'ʊ', 'ʌ', 'ʒ', '̃', 'θ' - ) + ), + "it-IT": ( + 'a', 'b', 'd', 'e', 'f', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'z', 'æ', 'ɐ', 'ɑ', 'ɔ', 'ə', 'ɚ', + 'ɜ', 'ɬ', 'ɹ', 'ʌ', 'ʔ', 'ʲ', '̃', '̩', 'ᵻ', + 'ð', 'ŋ', 'ɛ', 'ɡ', 'ɣ', 'ɪ', 'ɲ', 'ɾ', 'ʃ', + 'ʊ', 'ʎ', 'ʒ', 'ʝ', 'β', 'θ', 'd͡', 't͡', 'ø', 'ɒ', + 'ɕ', 'ɓ', 'ç', 'ɖ', 'ɘ', 'ɝ', 'ɞ', 'ɟ','ʄ','ɡ','ɠ', + 'ɢ','ʛ','ɦ','ɧ','ħ','ɥ','ʜ','ɨ','ɬ','ɫ','ɮ','ʟ', + 'ɱ','ɯ','ɰ','ɳ','ɵ','ɸ','œ','ɶ','ʘ','ɺ','ɻ','ʀ','ʁ', + 'ɽ','ʂ','ʈ','ʧ','ʉ','ʋ','ⱱ','ɤ','ʍ','χ','ʏ','ʑ','ʐ', + 'ʔ','ʡ','ʕ','ʢ','ǀ','ǁ','ǂ','ᵻ' + ), } GRAPHEME_CHARACTER_CASES = ["upper", "lower", "mixed"] @@ -124,7 +143,7 @@ def get_ipa_punctuation_list(locale): punct_set = set(DEFAULT_PUNCTUATION) # TODO @xueyang: verify potential mismatches with locale-specific punctuation sets used # in nemo_text_processing.text_normalization.en.taggers.punctuation.py - if locale in ["de-DE", "es-ES"]: + if locale in ["de-DE", "es-ES", "it-IT"]: # ref: https://en.wikipedia.org/wiki/Guillemet#Uses punct_set.update(['«', '»', '‹', '›']) if locale == "de-DE": @@ -140,6 +159,31 @@ def get_ipa_punctuation_list(locale): '—', # em dash, U+2014, decimal 8212 ] ) + if locale == "it-IT": + # ref: https://en.wikipedia.org/wiki/German_orthography#Punctuation + punct_set.update( + [ + '„', # double low-9 quotation mark, U+201E, decimal 8222 + '“', # left double quotation mark, U+201C, decimal 8220 + '‚', # single low-9 quotation mark, U+201A, decimal 8218 + '‘', # left single quotation mark, U+2018, decimal 8216 + '‒', # figure dash, U+2012, decimal 8210 + '–', # en dash, U+2013, decimal 8211 + '—', # em dash, U+2014, decimal 8212 + 'ʴ', + 'ʰ', + 'ʱ', + 'ʲ', + 'ʷ', + 'ˠ', + 'ˤ', + '˞↓', + '↑', + '→', + '↗', + '↘,', + ] + ) elif locale == "es-ES": # ref: https://en.wikipedia.org/wiki/Spanish_orthography#Punctuation punct_set.update(['¿', '¡']) diff --git a/nemo/collections/common/tokenizers/text_to_speech/tokenizer_utils.py b/nemo/collections/common/tokenizers/text_to_speech/tokenizer_utils.py index 92a3e0fb49e0..ad9ad9f4e898 100644 --- a/nemo/collections/common/tokenizers/text_to_speech/tokenizer_utils.py +++ b/nemo/collections/common/tokenizers/text_to_speech/tokenizer_utils.py @@ -23,6 +23,7 @@ "english_text_preprocessing", "any_locale_text_preprocessing", "spanish_text_preprocessing", + "italian_text_preprocessing", "any_locale_word_tokenize", "english_word_tokenize", "LATIN_CHARS_ALL", @@ -189,5 +190,9 @@ def spanish_text_preprocessing(text: str) -> str: return text.lower() +def italian_text_preprocessing(text: str) -> str: + return text.lower() + + def chinese_text_preprocessing(text: str) -> str: return text diff --git a/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py b/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py index 4193cf00eb85..32f725c9c73f 100644 --- a/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py +++ b/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py @@ -28,6 +28,7 @@ any_locale_text_preprocessing, chinese_text_preprocessing, english_text_preprocessing, + italian_text_preprocessing, spanish_text_preprocessing, ) from nemo.utils import logging @@ -267,6 +268,34 @@ def __init__( ) +class ItalianCharsTokenizer(BaseCharsTokenizer): + PUNCT_LIST = get_ipa_punctuation_list("it-IT") + + def __init__( + self, punct=True, apostrophe=True, add_blank_at=None, pad_with_space=False, non_default_punct_list=None + ): + """Italian grapheme tokenizer. + Args: + punct: Whether to reserve grapheme for basic punctuation or not. + apostrophe: Whether to use apostrophe or not. + add_blank_at: Add blank to labels in the specified order ("last") or after tokens (any non None), + if None then no blank in labels. + pad_with_space: Whether to pad text with spaces at the beginning and at the end or not. + non_default_punct_list: List of punctuation marks which will be used instead default. + """ + + it_alphabet = "abcdefghijklmnopqrstuvwxyzàèéìòù" + super().__init__( + chars=it_alphabet, + punct=punct, + apostrophe=apostrophe, + add_blank_at=add_blank_at, + pad_with_space=pad_with_space, + non_default_punct_list=non_default_punct_list, + text_preprocessing_func=italian_text_preprocessing, + ) + + class GermanPhonemesTokenizer(BaseCharsTokenizer): # fmt: off PUNCT_LIST = ( # Derived from LJSpeech and "/" additionally @@ -694,8 +723,7 @@ def __init__( pad_with_space=False, text_preprocessing_func=chinese_text_preprocessing, ): - """ - Chinese phoneme-based tokenizer. + """Chinese phoneme-based tokenizer. Note: This tokenizer for now covers Chinese phonemes/tones and English letters because our dataset contains both Chinese and English graphemes. Args: diff --git a/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py b/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py index e4e16fa31d68..62c571bc16b7 100644 --- a/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py +++ b/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py @@ -18,6 +18,7 @@ EnglishCharsTokenizer, GermanCharsTokenizer, IPATokenizer, + ItalianCharsTokenizer, SpanishCharsTokenizer, ) from nemo.collections.tts.g2p.models.i18n_ipa import IpaG2p @@ -89,6 +90,18 @@ def test_german_chars_tokenizer(self): assert chars == expected_output assert len(tokens) == len(input_text) + @pytest.mark.run_only_on('CPU') + @pytest.mark.unit + def test_italian_chars_tokenizer(self): + input_text = "Ciao mondo!" + expected_output = "ciao mondo!" + + tokenizer = ItalianCharsTokenizer() + chars, tokens = self._parse_text(tokenizer, input_text) + + assert chars == expected_output + assert len(tokens) == len(input_text) + @pytest.mark.run_only_on('CPU') @pytest.mark.unit def test_spanish_chars_tokenizer(self): From e81dda247a2ec248ae7da55ab428036aefef75af Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 23:13:39 -0700 Subject: [PATCH 062/112] Replace None strategy with auto in tutorial notebooks (#7521) (#7527) Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Sasha Meister --- tutorials/02_NeMo_Adapters.ipynb | 2 +- tutorials/asr/ASR_TTS_Tutorial.ipynb | 2 +- tutorials/asr/Self_Supervised_Pre_Training.ipynb | 6 +++--- tutorials/asr/Speech_Commands.ipynb | 2 +- tutorials/asr/Voice_Activity_Detection.ipynb | 2 +- .../speech_enhancement/Speech_Enhancement_with_NeMo.ipynb | 6 +++--- tutorials/nlp/Entity_Linking_Medical.ipynb | 2 +- tutorials/nlp/GLUE_Benchmark.ipynb | 4 ++-- tutorials/nlp/Joint_Intent_and_Slot_Classification.ipynb | 4 ++-- tutorials/nlp/Punctuation_and_Capitalization.ipynb | 6 +++--- .../nlp/Punctuation_and_Capitalization_Lexical_Audio.ipynb | 4 ++-- tutorials/nlp/Relation_Extraction-BioMegatron.ipynb | 4 ++-- tutorials/nlp/Text_Classification_Sentiment_Analysis.ipynb | 6 +++--- tutorials/nlp/Token_Classification-BioMegatron.ipynb | 2 +- .../nlp/Token_Classification_Named_Entity_Recognition.ipynb | 4 ++-- tutorials/nlp/Zero_Shot_Intent_Recognition.ipynb | 4 ++-- tutorials/speaker_tasks/Speaker_Diarization_Training.ipynb | 2 +- .../speaker_tasks/Speaker_Identification_Verification.ipynb | 2 +- 18 files changed, 32 insertions(+), 32 deletions(-) diff --git a/tutorials/02_NeMo_Adapters.ipynb b/tutorials/02_NeMo_Adapters.ipynb index 51a91a3c7053..289426f3bc2b 100644 --- a/tutorials/02_NeMo_Adapters.ipynb +++ b/tutorials/02_NeMo_Adapters.ipynb @@ -1985,4 +1985,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} diff --git a/tutorials/asr/ASR_TTS_Tutorial.ipynb b/tutorials/asr/ASR_TTS_Tutorial.ipynb index 007713ee3cc2..9bbcc8e4aa34 100644 --- a/tutorials/asr/ASR_TTS_Tutorial.ipynb +++ b/tutorials/asr/ASR_TTS_Tutorial.ipynb @@ -553,7 +553,7 @@ "config.trainer.max_epochs = NUM_EPOCHS\n", "\n", "config.trainer.devices = 1\n", - "config.trainer.strategy = None # use 1 device, no need for ddp strategy\n", + "config.trainer.strategy = auto # use 1 device, no need for ddp strategy\n", "\n", "OmegaConf.resolve(config)" ] diff --git a/tutorials/asr/Self_Supervised_Pre_Training.ipynb b/tutorials/asr/Self_Supervised_Pre_Training.ipynb index 04998f68f23e..6f977df492a1 100644 --- a/tutorials/asr/Self_Supervised_Pre_Training.ipynb +++ b/tutorials/asr/Self_Supervised_Pre_Training.ipynb @@ -316,7 +316,7 @@ " cfg.trainer.gpus = 1\n", "else:\n", " cfg.trainer.accelerator = 'cpu'\n", - " cfg.trainer.strategy = None\n", + " cfg.trainer.strategy = auto\n", " cfg.trainer.gpus = 0\n", "\n", "cfg.exp_manager.exp_dir = data_dir + \"/content/exp\"\n", @@ -538,7 +538,7 @@ " cfg.trainer.gpus = 1\n", "else:\n", " cfg.trainer.accelerator = 'cpu'\n", - " cfg.trainer.strategy = None\n", + " cfg.trainer.strategy = auto\n", " cfg.trainer.gpus = 0\n", "\n", "cfg.model.tokenizer.dir = data_dir + \"/tokenizers/an4/tokenizer_spe_unigram_v128/\" # note this is a directory, not a path to a vocabulary file\n", @@ -725,4 +725,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} diff --git a/tutorials/asr/Speech_Commands.ipynb b/tutorials/asr/Speech_Commands.ipynb index 208752347d64..c1566ae71850 100644 --- a/tutorials/asr/Speech_Commands.ipynb +++ b/tutorials/asr/Speech_Commands.ipynb @@ -441,7 +441,7 @@ "config.trainer.max_epochs = 5\n", "\n", "# Remove distributed training flags\n", - "config.trainer.strategy = None" + "config.trainer.strategy = auto" ], "execution_count": null, "outputs": [] diff --git a/tutorials/asr/Voice_Activity_Detection.ipynb b/tutorials/asr/Voice_Activity_Detection.ipynb index b1bdd434511b..7c7b95e99416 100644 --- a/tutorials/asr/Voice_Activity_Detection.ipynb +++ b/tutorials/asr/Voice_Activity_Detection.ipynb @@ -462,7 +462,7 @@ "config.trainer.max_epochs = 5\n", "\n", "# Remove distributed training flags\n", - "config.trainer.strategy = None" + "config.trainer.strategy = auto" ] }, { diff --git a/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb b/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb index 41a49688d35e..d8a15cbd5e1c 100644 --- a/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb +++ b/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb @@ -667,7 +667,7 @@ "config.trainer.max_epochs = 10\n", "\n", "# Remove distributed training flags\n", - "config.trainer.strategy = None\n", + "config.trainer.strategy = auto\n", "\n", "# Instantiate the trainer\n", "trainer = pl.Trainer(**config.trainer)" @@ -1144,7 +1144,7 @@ "config_dual_output.trainer.max_epochs = 10\n", "\n", "# Remove distributed training flags\n", - "config_dual_output.trainer.strategy = None\n", + "config_dual_output.trainer.strategy = auto\n", "\n", "# Instantiate the trainer\n", "trainer = pl.Trainer(**config_dual_output.trainer)\n", @@ -1313,4 +1313,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/tutorials/nlp/Entity_Linking_Medical.ipynb b/tutorials/nlp/Entity_Linking_Medical.ipynb index ff8eda123b7f..4f56a85eabfa 100644 --- a/tutorials/nlp/Entity_Linking_Medical.ipynb +++ b/tutorials/nlp/Entity_Linking_Medical.ipynb @@ -187,7 +187,7 @@ "cfg.model.validation_ds.data_file = os.path.join(DATA_DIR, \"tiny_example_validation_pairs.tsv\")\n", "\n", "# remove distributed training flags\n", - "cfg.trainer.strategy = None\n", + "cfg.trainer.strategy = auto\n", "cfg.trainer.accelerator = None" ] }, diff --git a/tutorials/nlp/GLUE_Benchmark.ipynb b/tutorials/nlp/GLUE_Benchmark.ipynb index d8fe75940b09..445ff6705028 100644 --- a/tutorials/nlp/GLUE_Benchmark.ipynb +++ b/tutorials/nlp/GLUE_Benchmark.ipynb @@ -342,7 +342,7 @@ "# config.trainer.amp_level = O1\n", "\n", "# remove distributed training flags\n", - "config.trainer.strategy = None\n", + "config.trainer.strategy = auto\n", "\n", "# setup max number of steps to reduce training time for demonstration purposes of this tutorial\n", "config.trainer.max_steps = 128\n", @@ -563,4 +563,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/tutorials/nlp/Joint_Intent_and_Slot_Classification.ipynb b/tutorials/nlp/Joint_Intent_and_Slot_Classification.ipynb index 104d69df18e2..7513183fba28 100644 --- a/tutorials/nlp/Joint_Intent_and_Slot_Classification.ipynb +++ b/tutorials/nlp/Joint_Intent_and_Slot_Classification.ipynb @@ -286,7 +286,7 @@ "# config.trainer.amp_level = O1\n", "\n", "# remove distributed training flags\n", - "config.trainer.strategy = None\n", + "config.trainer.strategy = auto\n", "\n", "# setup a small number of epochs for demonstration purposes of this tutorial\n", "config.trainer.max_epochs = 5\n", @@ -705,7 +705,7 @@ "config.trainer.accelerator = accelerator\n", "\n", "# remove distributed training flags\n", - "config.trainer.strategy = None\n", + "config.trainer.strategy = auto\n", "\n", "trainer = pl.Trainer(**config.trainer)\n", "config.exp_manager.exp_dir = os.path.join(DATA_DIR, \"output/\" + run_name)\n", diff --git a/tutorials/nlp/Punctuation_and_Capitalization.ipynb b/tutorials/nlp/Punctuation_and_Capitalization.ipynb index 1519c234372b..e9d1060f6442 100644 --- a/tutorials/nlp/Punctuation_and_Capitalization.ipynb +++ b/tutorials/nlp/Punctuation_and_Capitalization.ipynb @@ -550,7 +550,7 @@ "config.trainer.max_epochs = 1\n", "\n", "# Remove distributed training flags\n", - "config.trainer.strategy = None\n", + "config.trainer.strategy = auto\n", "\n", "trainer = pl.Trainer(**config.trainer)" ] @@ -745,7 +745,7 @@ "config.trainer.accelerator = accelerator\n", "config.trainer.precision = 16 if torch.cuda.is_available() else 32\n", "config.trainer.max_epochs = 1\n", - "config.trainer.strategy = None\n", + "config.trainer.strategy = auto\n", "\n", "# Exp manager\n", "config.exp_manager.explicit_log_dir = 'tarred_experiment'\n", @@ -1043,4 +1043,4 @@ }, "nbformat": 4, "nbformat_minor": 1 -} \ No newline at end of file +} diff --git a/tutorials/nlp/Punctuation_and_Capitalization_Lexical_Audio.ipynb b/tutorials/nlp/Punctuation_and_Capitalization_Lexical_Audio.ipynb index 5580bc4cf946..778e14e63b70 100644 --- a/tutorials/nlp/Punctuation_and_Capitalization_Lexical_Audio.ipynb +++ b/tutorials/nlp/Punctuation_and_Capitalization_Lexical_Audio.ipynb @@ -645,7 +645,7 @@ "config.trainer.max_epochs = 1\n", "\n", "# Remove distributed training flags\n", - "config.trainer.strategy = None\n", + "config.trainer.strategy = auto\n", "config.exp_manager.use_datetime_version=False\n", "config.exp_manager.explicit_log_dir='Punctuation_And_Capitalization_Lexical_Audio'\n", "\n", @@ -860,7 +860,7 @@ "config.trainer.accelerator = accelerator\n", "config.trainer.precision = 16 if torch.cuda.is_available() else 32\n", "config.trainer.max_epochs = 1\n", - "config.trainer.strategy = None\n", + "config.trainer.strategy = auto\n", "\n", "# Exp manager\n", "config.exp_manager.explicit_log_dir = 'tarred_experiment'\n", diff --git a/tutorials/nlp/Relation_Extraction-BioMegatron.ipynb b/tutorials/nlp/Relation_Extraction-BioMegatron.ipynb index b7c25cb416ef..451c40152c8d 100644 --- a/tutorials/nlp/Relation_Extraction-BioMegatron.ipynb +++ b/tutorials/nlp/Relation_Extraction-BioMegatron.ipynb @@ -403,7 +403,7 @@ "config.trainer.precision = 16 if torch.cuda.is_available() else 32\n", "\n", "# remove distributed training flags\n", - "config.trainer.strategy = None\n", + "config.trainer.strategy = auto\n", "\n", "trainer = pl.Trainer(**config.trainer)" ] @@ -652,4 +652,4 @@ }, "nbformat": 4, "nbformat_minor": 1 -} \ No newline at end of file +} diff --git a/tutorials/nlp/Text_Classification_Sentiment_Analysis.ipynb b/tutorials/nlp/Text_Classification_Sentiment_Analysis.ipynb index 5b5b74e7bf11..8e44aca9d0d1 100644 --- a/tutorials/nlp/Text_Classification_Sentiment_Analysis.ipynb +++ b/tutorials/nlp/Text_Classification_Sentiment_Analysis.ipynb @@ -370,7 +370,7 @@ "# config.trainer.amp_level = O1\n", "\n", "# disable distributed training when using Colab to prevent the errors\n", - "config.trainer.strategy = None\n", + "config.trainer.strategy = auto\n", "\n", "# setup max number of steps to reduce training time for demonstration purposes of this tutorial\n", "# Training stops when max_step or max_epochs is reached (earliest)\n", @@ -573,7 +573,7 @@ "# create a copy of the trainer config and update it to be used for final evaluation\n", "eval_trainer_cfg = config.trainer.copy()\n", "eval_trainer_cfg.accelerator = 'gpu' if torch.cuda.is_available() else 'cpu' # it is safer to perform evaluation on single GPU as PT is buggy with the last batch on multi-GPUs\n", - "eval_trainer_cfg.strategy = None # 'ddp' is buggy with test process in the current PT, it looks like it has been fixed in the latest master\n", + "eval_trainer_cfg.strategy = auto # 'ddp' is buggy with test process in the current PT, it looks like it has been fixed in the latest master\n", "eval_trainer = pl.Trainer(**eval_trainer_cfg)\n", "\n", "eval_trainer.test(model=eval_model, verbose=False) # test_dataloaders=eval_dataloader,\n" @@ -832,4 +832,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/tutorials/nlp/Token_Classification-BioMegatron.ipynb b/tutorials/nlp/Token_Classification-BioMegatron.ipynb index 517f2e557743..e5e5aa81b859 100644 --- a/tutorials/nlp/Token_Classification-BioMegatron.ipynb +++ b/tutorials/nlp/Token_Classification-BioMegatron.ipynb @@ -434,7 +434,7 @@ "config.trainer.precision = 16 if torch.cuda.is_available() else 32\n", "\n", "# remove distributed training flags\n", - "config.trainer.strategy = None\n", + "config.trainer.strategy = auto\n", "\n", "trainer = pl.Trainer(**config.trainer)" ] diff --git a/tutorials/nlp/Token_Classification_Named_Entity_Recognition.ipynb b/tutorials/nlp/Token_Classification_Named_Entity_Recognition.ipynb index c3f7e28b6b1f..1c1999cc08c1 100644 --- a/tutorials/nlp/Token_Classification_Named_Entity_Recognition.ipynb +++ b/tutorials/nlp/Token_Classification_Named_Entity_Recognition.ipynb @@ -533,7 +533,7 @@ "# config.trainer.amp_level = O1\n", "\n", "# remove distributed training flags\n", - "config.trainer.strategy = None\n", + "config.trainer.strategy = auto\n", "\n", "# setup max number of steps to reduce training time for demonstration purposes of this tutorial\n", "config.trainer.max_steps = 32\n", @@ -847,4 +847,4 @@ "metadata": {} } ] -} \ No newline at end of file +} diff --git a/tutorials/nlp/Zero_Shot_Intent_Recognition.ipynb b/tutorials/nlp/Zero_Shot_Intent_Recognition.ipynb index 69df7b27b02d..f571fa176e96 100644 --- a/tutorials/nlp/Zero_Shot_Intent_Recognition.ipynb +++ b/tutorials/nlp/Zero_Shot_Intent_Recognition.ipynb @@ -400,7 +400,7 @@ "# config.trainer.amp_level = O1\n", "\n", "# remove distributed training flags\n", - "config.trainer.strategy = None\n", + "config.trainer.strategy = auto\n", "\n", "# setup max number of steps to reduce training time for demonstration purposes of this tutorial\n", "config.trainer.max_steps = 128\n", @@ -671,4 +671,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/tutorials/speaker_tasks/Speaker_Diarization_Training.ipynb b/tutorials/speaker_tasks/Speaker_Diarization_Training.ipynb index 3c56df2bbba0..efd86e1ef242 100644 --- a/tutorials/speaker_tasks/Speaker_Diarization_Training.ipynb +++ b/tutorials/speaker_tasks/Speaker_Diarization_Training.ipynb @@ -761,7 +761,7 @@ "source": [ "config.model.diarizer.speaker_embeddings.model_path=\"titanet_large\"\n", "config.trainer.max_epochs = 5\n", - "config.trainer.strategy = None" + "config.trainer.strategy = auto" ] }, { diff --git a/tutorials/speaker_tasks/Speaker_Identification_Verification.ipynb b/tutorials/speaker_tasks/Speaker_Identification_Verification.ipynb index dce8c46df1b0..f0ad1c19f5c9 100644 --- a/tutorials/speaker_tasks/Speaker_Identification_Verification.ipynb +++ b/tutorials/speaker_tasks/Speaker_Identification_Verification.ipynb @@ -475,7 +475,7 @@ "config.trainer.max_epochs = 10\n", "\n", "# Remove distributed training flags\n", - "config.trainer.strategy = None\n", + "config.trainer.strategy = auto\n", "\n", "# Remove augmentations\n", "config.model.train_ds.augmentor=None" From 1be2b400f3dfe6bd630379f356595029c0734d4a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 19:56:52 +0800 Subject: [PATCH 063/112] unpin setuptools (#7534) (#7535) Signed-off-by: fayejf <36722593+fayejf@users.noreply.github.com> Co-authored-by: fayejf <36722593+fayejf@users.noreply.github.com> Signed-off-by: Sasha Meister --- requirements/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 7481e337c999..a9a8c1e98100 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -5,7 +5,7 @@ onnx>=1.7.0 python-dateutil ruamel.yaml scikit-learn -setuptools==65.5.1 +setuptools>=65.5.1 tensorboard text-unidecode torch From 32c06fad9330da6ebb4eb6c583f7e1787b016970 Mon Sep 17 00:00:00 2001 From: Adi Renduchintala Date: Wed, 27 Sep 2023 09:05:29 -0700 Subject: [PATCH 064/112] remove auto generated examples (#7510) * explicitly remove autogenerated examples for data parallel evaluation Signed-off-by: arendu * mark autogenrated and remove it for test Signed-off-by: arendu * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: arendu Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../data/language_modeling/megatron/gpt_sft_dataset.py | 5 +++++ .../models/language_modeling/megatron_gpt_sft_model.py | 9 +++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_dataset.py b/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_dataset.py index 2c655d5cde6b..101201ef7536 100644 --- a/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_dataset.py +++ b/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_dataset.py @@ -171,8 +171,13 @@ def __getitem__(self, idx): # idx may < 0 because we pad_samples_to_global_batch_size, e.g. id = -1 if idx < 0: idx = len(self) + idx + auto_gen_idx = True + else: + auto_gen_idx = False try: example = self.indexed_dataset[idx] + if auto_gen_idx: + example['__AUTOGENERATED__'] = True except Exception as e: logging.error(f"Error while loading example {idx} from dataset {self.file_path}") raise e diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_sft_model.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_sft_model.py index df6d792ea703..b38a81460097 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_sft_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_sft_model.py @@ -492,7 +492,6 @@ def inference_epoch_end(self, outputs, mode, data_cfg): ) # Remove duplicate examples due to distributed sampler. - inp_label_set = set() deduplicated_outputs = { 'preds': [], 'labels': [], @@ -505,14 +504,16 @@ def inference_epoch_end(self, outputs, mode, data_cfg): for pred, label, input, metadata in zip( batch['preds'], batch['labels'], batch['inputs'], batch['metadata'] ): - key = input + label total_size += 1 - if key not in inp_label_set: - inp_label_set.add(key) + if not metadata.get("__AUTOGENERATED__", False): deduplicated_outputs['preds'].append(pred) deduplicated_outputs['labels'].append(label) deduplicated_outputs['inputs'].append(input) deduplicated_outputs['metadata'].append(metadata) + else: + logging.info( + f"skipping autogenerated example example {input} prediction {pred} label {label}" + ) # Compute metric score metric_name = self.val_metric_name if mode == 'validation' else self.test_metric_name From 3a1818fc8e5470cf12f1219c191493eb36f14671 Mon Sep 17 00:00:00 2001 From: Olivier Delalleau <507137+odelalleau@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:21:06 -0400 Subject: [PATCH 065/112] Add the `strategy` argument to `MegatronGPTModel.generate()` (#7264) It is passed as an explicit argument rather than through `**strategy_args` so as to ensure someone cannot accidentally pass other arguments that would end up being ignored. It is a keyword-only argument to ensure that if in the future we want to update the signature to `**strategy_args`, we can do it without breaking code. Signed-off-by: Olivier Delalleau <507137+odelalleau@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../nlp/models/language_modeling/megatron_gpt_model.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py index 45586bffcdce..1f905430e87c 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py @@ -41,6 +41,7 @@ get_ltor_masks_and_position_ids, get_params_for_weight_decay_optimization, ) +from nemo.collections.nlp.modules.common.text_generation_strategy import TextGenerationStrategy from nemo.collections.nlp.modules.common.text_generation_utils import ( generate, get_computeprob_response, @@ -1176,6 +1177,8 @@ def generate( inputs: Union[List[str], torch.Tensor, List[dict]], length_params: LengthParam, sampling_params: SamplingParam = None, + *, + strategy: Optional[TextGenerationStrategy] = None, ) -> OutputType: # check whether the DDP is initialized @@ -1201,7 +1204,11 @@ def dummy(): if length_params is None: length_params = get_default_length_params() - return megatron_gpt_generate(self.cuda(), inputs, self.tokenizer, length_params, sampling_params) + strategy_args = {} if strategy is None else {"strategy": strategy} + + return megatron_gpt_generate( + self.cuda(), inputs, self.tokenizer, length_params, sampling_params, **strategy_args + ) def predict_step(self, batch: Any, batch_idx: int, dataloader_idx: Optional[int] = None) -> Any: inference_config = self.get_inference_config() From fd59a8447341ed989da6b076849f054a64bf835c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 14:45:17 -0700 Subject: [PATCH 066/112] Fix PTL2.0 related ASR bugs in r1.21.0: Val metrics logging, None dataloader issue (#7531) (#7533) * fix none dataloader issue ptl2 * ptl2.0 logging fixes for rnnt_models --------- Signed-off-by: KunalDhawan Co-authored-by: Kunal Dhawan Co-authored-by: Nithin Rao Signed-off-by: Sasha Meister --- nemo/collections/asr/models/rnnt_models.py | 24 ++++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/nemo/collections/asr/models/rnnt_models.py b/nemo/collections/asr/models/rnnt_models.py index 0c1da97c5012..8b5798d34356 100644 --- a/nemo/collections/asr/models/rnnt_models.py +++ b/nemo/collections/asr/models/rnnt_models.py @@ -772,7 +772,7 @@ def predict_step(self, batch, batch_idx, dataloader_idx=0): sample_id = sample_id.cpu().detach().numpy() return list(zip(sample_id, best_hyp_text)) - def validation_step(self, batch, batch_idx, dataloader_idx=0): + def validation_pass(self, batch, batch_idx, dataloader_idx=0): signal, signal_len, transcript, transcript_len = batch # forward() only performs encoder forward @@ -835,15 +835,21 @@ def validation_step(self, batch, batch_idx, dataloader_idx=0): return tensorboard_logs + def validation_step(self, batch, batch_idx, dataloader_idx=0): + metrics = self.validation_pass(batch, batch_idx, dataloader_idx) + if type(self.trainer.val_dataloaders) == list and len(self.trainer.val_dataloaders) > 1: + self.validation_step_outputs[dataloader_idx].append(metrics) + else: + self.validation_step_outputs.append(metrics) + return metrics + def test_step(self, batch, batch_idx, dataloader_idx=0): - logs = self.validation_step(batch, batch_idx, dataloader_idx=dataloader_idx) - test_logs = { - 'test_wer_num': logs['val_wer_num'], - 'test_wer_denom': logs['val_wer_denom'], - # 'test_wer': logs['val_wer'], - } - if 'val_loss' in logs: - test_logs['test_loss'] = logs['val_loss'] + logs = self.validation_pass(batch, batch_idx, dataloader_idx=dataloader_idx) + test_logs = {name.replace("val_", "test_"): value for name, value in logs.items()} + if type(self.trainer.test_dataloaders) == list and len(self.trainer.test_dataloaders) > 1: + self.test_step_outputs[dataloader_idx].append(test_logs) + else: + self.test_step_outputs.append(test_logs) return test_logs def multi_validation_epoch_end(self, outputs, dataloader_idx: int = 0): From 57116f417daae28bf1dcf345a2788d98da4b3d1a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:37:53 -0700 Subject: [PATCH 067/112] gpus -> devices (#7542) (#7545) Signed-off-by: Nithin Rao Koluguri Co-authored-by: Nithin Rao Signed-off-by: Sasha Meister --- tutorials/asr/Self_Supervised_Pre_Training.ipynb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tutorials/asr/Self_Supervised_Pre_Training.ipynb b/tutorials/asr/Self_Supervised_Pre_Training.ipynb index 6f977df492a1..113979314da9 100644 --- a/tutorials/asr/Self_Supervised_Pre_Training.ipynb +++ b/tutorials/asr/Self_Supervised_Pre_Training.ipynb @@ -313,11 +313,11 @@ "if torch.cuda.is_available():\n", " cfg.trainer.accelerator = 'gpu'\n", " cfg.trainer.strategy = 'dp'\n", - " cfg.trainer.gpus = 1\n", + " cfg.trainer.devices = 1\n", "else:\n", " cfg.trainer.accelerator = 'cpu'\n", " cfg.trainer.strategy = auto\n", - " cfg.trainer.gpus = 0\n", + " cfg.trainer.devices = 0\n", "\n", "cfg.exp_manager.exp_dir = data_dir + \"/content/exp\"\n", "cfg.exp_manager.name = \"pre_trained\"\n", @@ -535,11 +535,11 @@ "if torch.cuda.is_available():\n", " cfg.trainer.accelerator = 'gpu'\n", " cfg.trainer.strategy = 'dp'\n", - " cfg.trainer.gpus = 1\n", + " cfg.trainer.devices = 1\n", "else:\n", " cfg.trainer.accelerator = 'cpu'\n", " cfg.trainer.strategy = auto\n", - " cfg.trainer.gpus = 0\n", + " cfg.trainer.devices = 0\n", "\n", "cfg.model.tokenizer.dir = data_dir + \"/tokenizers/an4/tokenizer_spe_unigram_v128/\" # note this is a directory, not a path to a vocabulary file\n", "cfg.model.tokenizer.type = \"bpe\"\n", From 40c8f08572702dac22d09739fe7cab29f976bb55 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:00:26 -0700 Subject: [PATCH 068/112] Update FFMPEG version to fix issue with torchaudio (#7551) (#7553) Signed-off-by: smajumdar Co-authored-by: Somshubra Majumdar Signed-off-by: Sasha Meister --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index e4f04359e6eb..843c0c27df45 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,7 +38,7 @@ RUN apt-get update && \ libsndfile1 sox \ libfreetype6 \ swig \ - ffmpeg \ + ffmpeg=ffmpeg_5.1.2-3ubuntu1 \ libavdevice-dev && \ rm -rf /var/lib/apt/lists/* From 9bc02385aeddf7464a2419466135e20a60967341 Mon Sep 17 00:00:00 2001 From: meatybobby Date: Thu, 28 Sep 2023 10:12:55 -0700 Subject: [PATCH 069/112] PEFT GPT & T5 Refactor (#7308) * initial implementation of add_adapters API * correct type hint * Add config in add_adapters for save and load (@author bobchen) * Remove AdapterConfig to avoid import error * Add AdaterConfig back and move adaptermixin to sft model * Add NLPSaveRestoreConnector as default in NLPModel.restore_from * Add restore_from_nemo_with_adapter and test script * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * rename t5 file and classes to be consistent with GPT * add t5 sft dataset * add support for single-file format with T5SFTDataset * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Various small changes to make T5 SFT work like GPT SFT * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add adapter evaluation test script * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add MultiAdaterConfig for ia3 and fix builder issue * Make ptuning for T5SFTModel work using mixin * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add IA3_Adapter for AdapterName * Add adapter name for ptuning and attention adapter * Make test script GPT/T5 agnostic * Add layer selection feature * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Integrate adapter name and config * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update gpt peft tuning script to new API * add t5 peft tuning script with new API * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix IA3 layer selection issue * Override state_dict on SFT model instead of mixin * Add load adapter by adapter config * move peft config map away from example script * auto get config from nemo adapter * Move PEFTConfig to new file * fix ckpt save/load for t5 * name change: add_adapters -> add_adapter * variable name change * update t5 script * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix t5 issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add weight tying * update gpt tuning script * PEFT-API proposal * Fix according to comments * update tuning scripts * move merge_cfg_with to mixin class since it applies to both gpt and t5 and requires the model class for restore * Add mcore_gpt support for NLPAdapterMixin * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix typo * variable name change to distinguish "peft" and "adapter" * override `load_adapters` to support `add_adapter` name change * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update tuning and eval script for adapter save/load * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add Ptuning on first stage only * add lora tutorial for review * Fix layer selection for mcore * add landing page * fix resume training Signed-off-by: jasonwan * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add mcore condition in sharded_state_dict to make sft work * Update lora_tutorial.md First edit of this file for PEFT documentation for NeMO Signed-off-by: hkelly33 <58792115+hkelly33@users.noreply.github.com> * rename Adapter to AttentionAdapter to avoid confusion in doc * Change load_adapters to load .nemo * add quick start guide * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add load_adapters with .ckpt * Remove setup_complete changes in load_adapters * update landing page * remove typo * Updated quick_start.md per Chen Cui Signed-off-by: hkelly33 <58792115+hkelly33@users.noreply.github.com> * Add inference config merger and tutorial * Add doc string for NLPAdapterModelMixin and deprecated warning on MegatronGPTPEFTModel * add supported_methods.md and update other documentations * Update supported_methods.md minor updates. Signed-off-by: Adi Renduchintala * Update landing_page.md minor update. Signed-off-by: Adi Renduchintala * Modify doc string for NLPAdapterModelMixin * Add doc string add_adapters in NLPAdapterModelMixin * rename canonical adapters * remove mcore hard dependency * [PATCH] move microbatch calculator to nemo from apex * remove apex dependency in gpt and t5 sft models * remove apex dependency in gpt model * render doc strings * fix * Add missing virtual_tokens on ptuning * fix docstrings * update gpt-style model coverage in docs * update docstring * Remove pdb * add lightning_fabric to make docstring rendering work * Add Ptuning missing key * try docstring rendering * Fix ptuning issue * update gpt t5 peft tuning and eval scripts * typos * update eval config * fix bug relating to apex dependency removal * typo * make predict step behave the same as test step * make lora tutorial work in notebook * cosmetics * update yaml scripts * mcore_gpt attribute optional * typo * update eval scripts and fix T5 eval bugs * add NLPDDPStrategyNotebook and trainer builder logic to use it * update lora notebook to use new trainer builder * fix microbatch calculator bug for inference after training * Convert markdown files to RST and incorporate with doc * typo * revise language * remove extra cell * remove unnecessary inheritance * remove old tests * move layer selection default so logging messages make sense * remove `save_adapters` as adapter weights are saved automatically during training * initialize weights from a checkpoint instead of randomly * multiple fields can form a context (#7147) * list of context fields and flexible prompt template Signed-off-by: arendu * list of fields for context Signed-off-by: arendu * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix bug Signed-off-by: Cheng-Ping Hsieh * Fix bug Signed-off-by: Cheng-Ping Hsieh * Add multiple truncation fields and middle truncation Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Compatible to old ckpt Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix tokenize detokenize issue Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove detokenization, add truncation augmentation Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Resolve comments Signed-off-by: Cheng-Ping Hsieh * Remove unused import Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * revert eos Signed-off-by: Cheng-Ping Hsieh * Add tokenizer space_sensitive attribute Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix error Signed-off-by: Cheng-Ping Hsieh * Fix erorr and use re Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix bug Signed-off-by: Cheng-Ping Hsieh * Change assert logic Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Follow adi suggestion Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove merge function Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add example and comment Signed-off-by: Cheng-Ping Hsieh * Remove context_key and add comment Signed-off-by: Cheng-Ping Hsieh * Remove random truncation Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix bug Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix template none Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix bug Signed-off-by: Cheng-Ping Hsieh --------- Signed-off-by: arendu Signed-off-by: Cheng-Ping Hsieh Signed-off-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Cheng-Ping Hsieh Co-authored-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> * revert config changes * remove accidental breakpoint * support TP>1 loading * infer adapter type from checkpoint in during eval * breakup add adapter * enable interpolation of train_ds and validation_ds * update metric calc script to conform to single-file eval format * remove extraneous print * update lora notebook for updated merge_inference_cfg * Update nlp_adapter_mixins.py variable name change Signed-off-by: Chen Cui * turn off grad scaler for PP to match old scripts * remove PEFTSaveRestoreConnector since functionality all covered by the new mixin class * remove resume_from_checkpoint check since covered in #7335 * revert changes made in eval config interpolation * more interpolation * typo * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove dup line Signed-off-by: Chen Cui * code style warnings Signed-off-by: Chen Cui * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix config mistake Signed-off-by: Chen Cui * add copyright header Signed-off-by: Chen Cui * fix code check warnings Signed-off-by: Chen Cui * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * revert changes to remove apex dependency (mixed apex+nemo microbatch calculator broke some CI tests) Signed-off-by: Chen Cui * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add more deprecation notices Signed-off-by: Chen Cui * update deprecation notices Signed-off-by: Chen Cui * update deprecation notices Signed-off-by: Chen Cui * consolidate peft and sft scripts Signed-off-by: Chen Cui * update CI tests Signed-off-by: Chen Cui * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * notebook branch points to main to prepare for merge Signed-off-by: Chen Cui * fix gpt and t5 validation with any metric other than loss Signed-off-by: Chen Cui * support pre-extracted checkpoints Signed-off-by: Chen Cui --------- Signed-off-by: jasonwan Signed-off-by: hkelly33 <58792115+hkelly33@users.noreply.github.com> Signed-off-by: Adi Renduchintala Signed-off-by: arendu Signed-off-by: Cheng-Ping Hsieh Signed-off-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Signed-off-by: Chen Cui Co-authored-by: Chen Cui Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Marc Romeyn Co-authored-by: jasonwan Co-authored-by: hkelly33 <58792115+hkelly33@users.noreply.github.com> Co-authored-by: Adi Renduchintala Co-authored-by: Yuanzhe Dong Co-authored-by: Cheng-Ping Hsieh Co-authored-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Signed-off-by: Sasha Meister --- Jenkinsfile | 380 +---- docs/source/conf.py | 1 + docs/source/nlp/api.rst | 14 +- docs/source/nlp/nemo_megatron/intro.rst | 1 + .../nlp/nemo_megatron/peft/landing_page.rst | 35 + .../nlp/nemo_megatron/peft/quick_start.rst | 90 ++ .../nemo_megatron/peft/supported_methods.rst | 71 + .../megatron_t5_prompt_learning.py | 5 + .../megatron_t5_prompt_learning_eval.py | 5 + .../megatron_t5_seq2seq_eval.py | 8 +- .../megatron_t5_seq2seq_finetune.py | 8 +- .../conf/megatron_gpt_peft_eval_config.yaml | 36 +- .../conf/megatron_gpt_peft_tuning_config.yaml | 22 +- .../conf/megatron_t5_peft_eval_config.yaml | 213 +++ .../conf/megatron_t5_peft_tuning_config.yaml | 220 +++ .../tuning/megatron_gpt_adapter_eval.py | 5 + .../tuning/megatron_gpt_adapter_tuning.py | 5 + .../tuning/megatron_gpt_ia3_eval.py | 5 + .../tuning/megatron_gpt_ia3_tuning.py | 5 + .../tuning/megatron_gpt_peft_eval.py | 199 +-- .../tuning/megatron_gpt_peft_tuning.py | 231 +-- .../tuning/megatron_gpt_sft.py | 5 + .../tuning/megatron_t5_adapter_eval.py | 5 + .../tuning/megatron_t5_adapter_tuning.py | 5 + .../tuning/megatron_t5_ia3_eval.py | 5 + .../tuning/megatron_t5_ia3_tuning.py | 5 + .../tuning/megatron_t5_lora_eval.py | 5 + .../tuning/megatron_t5_lora_tuning.py | 5 + .../tuning/megatron_t5_peft_eval.py | 135 ++ .../tuning/megatron_t5_peft_tuning.py | 65 + .../megatron/t5_sft_dataset.py | 169 +++ .../language_modeling/megatron_glue_model.py | 6 +- .../language_modeling/megatron_gpt_model.py | 3 +- .../megatron_gpt_peft_models.py | 41 +- .../megatron_gpt_sft_model.py | 19 +- .../language_modeling/megatron_t0_model.py | 14 +- .../megatron_t5_adapter_model.py | 8 +- .../megatron_t5_prompt_learning_model.py | 14 +- ...tune_model.py => megatron_t5_sft_model.py} | 301 ++-- nemo/collections/nlp/models/nlp_model.py | 23 +- .../megatron/adapters/parallel_adapters.py | 25 +- .../megatron/token_level_encoder_decoder.py | 20 +- .../nlp/parts/megatron_trainer_builder.py | 26 +- nemo/collections/nlp/parts/mixins/__init__.py | 13 + .../nlp/parts/mixins/nlp_adapter_mixins.py | 484 ++++++ nemo/collections/nlp/parts/nlp_overrides.py | 162 +- nemo/collections/nlp/parts/peft_config.py | 190 +++ nemo/core/classes/mixins/adapter_mixins.py | 21 +- .../metric_calculation/peft_metric_calc.py | 33 +- tutorials/nlp/lora.ipynb | 1301 +++-------------- 50 files changed, 2509 insertions(+), 2158 deletions(-) create mode 100644 docs/source/nlp/nemo_megatron/peft/landing_page.rst create mode 100644 docs/source/nlp/nemo_megatron/peft/quick_start.rst create mode 100644 docs/source/nlp/nemo_megatron/peft/supported_methods.rst create mode 100644 examples/nlp/language_modeling/tuning/conf/megatron_t5_peft_eval_config.yaml create mode 100644 examples/nlp/language_modeling/tuning/conf/megatron_t5_peft_tuning_config.yaml create mode 100644 examples/nlp/language_modeling/tuning/megatron_t5_peft_eval.py create mode 100644 examples/nlp/language_modeling/tuning/megatron_t5_peft_tuning.py create mode 100644 nemo/collections/nlp/data/language_modeling/megatron/t5_sft_dataset.py rename nemo/collections/nlp/models/language_modeling/{megatron_finetune_model.py => megatron_t5_sft_model.py} (74%) create mode 100644 nemo/collections/nlp/parts/mixins/__init__.py create mode 100644 nemo/collections/nlp/parts/mixins/nlp_adapter_mixins.py create mode 100644 nemo/collections/nlp/parts/peft_config.py diff --git a/Jenkinsfile b/Jenkinsfile index 2abcdbcc5ddb..92aa65ae660b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -669,188 +669,6 @@ pipeline { } } - // commented out temporarily to save time on github ci - //stage('L2: Megatron T5 Adapter PP=2') { - // when { - // anyOf { - // branch 'main' - // changeRequest target: 'main' - // } - // } - // failFast true - // parallel{ - // stage('T5 Adapter tuning & inference TP=1 PP=2') { - // steps { - // sh "python examples/nlp/language_modeling/tuning/megatron_t5_adapter_tuning.py \ - // --config-name=megatron_t5_adapter_tuning_config \ - // name='test_tp1_pp2' \ - // exp_manager.exp_dir='examples/adapter_tuning' \ - // trainer.devices=2 \ - // trainer.max_steps=1 \ - // trainer.val_check_interval=1 \ - // trainer.max_epochs=null \ - // model.data.num_workers=1 \ - // model.tensor_model_parallel_size=1 \ - // model.pipeline_model_parallel_size=2 \ - // model.language_model_path='/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m_tp1_pp2.nemo' \ - // model.existing_tasks=[] \ - // model.new_tasks=['rte'] \ - // model.data.train_ds=['/home/TestData/nlp/prompt_learning/rte_CI_test.jsonl'] \ - // model.data.validation_ds=['/home/TestData/nlp/prompt_learning/rte_CI_test.jsonl'] \ - // model.global_batch_size=4" - // sh "python examples/nlp/language_modeling/tuning/megatron_t5_adapter_eval.py \ - // --config-name=megatron_t5_adapter_inference \ - // adapter_model_file='examples/adapter_tuning/test_tp1_pp2.nemo' \ - // language_model_path='/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m_tp1_pp2.nemo' \ - // trainer.devices=2 \ - // data.num_workers=1 \ - // tensor_model_parallel_size=1 \ - // pipeline_model_parallel_size=2 \ - // data.global_batch_size=2 \ - // data.micro_batch_size=2 \ - // data.test_ds=['/home/TestData/nlp/prompt_learning/rte_CI_test.jsonl'] \ - // pred_file_path='examples/adapter_tuning/test_tp1_pp2/preds.txt'" - // sh "rm -rf examples/adapter_tuning/test_tp1_pp2.nemo" - // sh "rm -rf examples/adapter_tuning/test_tp1_pp2" - // } - // } - // } - //} - //stage('L2: Megatron T5 Adapter TP=2') { - // when { - // anyOf { - // branch 'main' - // changeRequest target: 'main' - // } - // } - // failFast true - // parallel{ - // stage('T5 Adapter tuning & inference TP=2 PP=1') { - // steps { - // sh "python examples/nlp/language_modeling/tuning/megatron_t5_adapter_tuning.py \ - // --config-name=megatron_t5_adapter_tuning_config \ - // name='test_tp2_pp1' \ - // exp_manager.exp_dir='examples/adapter_tuning' \ - // trainer.devices=2 \ - // trainer.max_steps=1 \ - // trainer.val_check_interval=1 \ - // trainer.max_epochs=null \ - // model.data.num_workers=1 \ - // model.tensor_model_parallel_size=2 \ - // model.language_model_path='/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m_tp2.nemo' \ - // model.existing_tasks=[] \ - // model.new_tasks=['rte'] \ - // model.data.train_ds=['/home/TestData/nlp/prompt_learning/rte_CI_test.jsonl'] \ - // model.data.validation_ds=['/home/TestData/nlp/prompt_learning/rte_CI_test.jsonl'] \ - // model.global_batch_size=4" - // sh "python examples/nlp/language_modeling/tuning/megatron_t5_adapter_eval.py \ - // --config-name=megatron_t5_adapter_inference \ - // adapter_model_file='examples/adapter_tuning/test_tp2_pp1.nemo' \ - // language_model_path='/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m_tp2.nemo' \ - // trainer.devices=2 \ - // tensor_model_parallel_size=2 \ - // data.global_batch_size=2 \ - // data.micro_batch_size=2 \ - // data.num_workers=1 \ - // data.test_ds=['/home/TestData/nlp/prompt_learning/rte_CI_test.jsonl'] \ - // pred_file_path='examples/adapter_tuning/test_tp2_pp1/preds.txt'" - // sh "rm -rf examples/adapter_tuning/test_tp2_pp1.nemo" - // sh "rm -rf examples/adapter_tuning/test_tp2_pp1" - // } - // } - // } - //} - stage('L2: Megatron T5 IA3 PP=2') { - when { - anyOf { - branch 'main' - changeRequest target: 'main' - } - } - failFast true - parallel{ - stage('T5 IA3 tuning & inference TP=1 PP=2') { - steps { - sh "python examples/nlp/language_modeling/tuning/megatron_t5_ia3_tuning.py \ - --config-name=megatron_t5_ia3_tuning_config \ - name='test_tp1_pp2' \ - exp_manager.exp_dir='examples/ia3_tuning' \ - trainer.devices=2 \ - trainer.max_steps=1 \ - trainer.val_check_interval=1 \ - trainer.max_epochs=null \ - model.data.num_workers=1 \ - model.tensor_model_parallel_size=1 \ - model.pipeline_model_parallel_size=2 \ - model.language_model_path='/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m_tp1_pp2.nemo' \ - model.existing_tasks=[] \ - model.new_tasks=['rte'] \ - model.data.train_ds=['/home/TestData/nlp/prompt_learning/rte_CI_test.jsonl'] \ - model.data.validation_ds=['/home/TestData/nlp/prompt_learning/rte_CI_test.jsonl'] \ - model.global_batch_size=4" - // TODO: @eharper temporarily comment while investigating how to fix - // sh "python examples/nlp/language_modeling/tuning/megatron_t5_ia3_eval.py \ - // --config-name=megatron_t5_ia3_inference \ - // adapter_model_file='examples/ia3_tuning/test_tp1_pp2.nemo' \ - // language_model_path='/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m_tp1_pp2.nemo' \ - // trainer.devices=2 \ - // data.num_workers=1 \ - // tensor_model_parallel_size=1 \ - // pipeline_model_parallel_size=2 \ - // data.global_batch_size=2 \ - // data.micro_batch_size=2 \ - // data.test_ds=['/home/TestData/nlp/prompt_learning/rte_CI_test.jsonl'] \ - // pred_file_path='examples/ia3_tuning/test_tp1_pp2/preds.txt'" - sh "rm -rf examples/ia3_tuning/test_tp1_pp2.nemo" - sh "rm -rf examples/ia3_tuning/test_tp1_pp2" - } - } - } - } - stage('L2: Megatron T5 IA3 TP=2') { - when { - anyOf { - branch 'main' - changeRequest target: 'main' - } - } - failFast true - parallel{ - stage('T5 IA3 tuning & inference TP=2 PP=1') { - steps { - sh "python examples/nlp/language_modeling/tuning/megatron_t5_ia3_tuning.py \ - --config-name=megatron_t5_ia3_tuning_config \ - name='test_tp2_pp1' \ - exp_manager.exp_dir='examples/ia3_tuning' \ - trainer.devices=2 \ - trainer.max_steps=1 \ - trainer.val_check_interval=1 \ - trainer.max_epochs=null \ - model.data.num_workers=1 \ - model.tensor_model_parallel_size=2 \ - model.language_model_path='/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m_tp2.nemo' \ - model.existing_tasks=[] \ - model.new_tasks=['rte'] \ - model.data.train_ds=['/home/TestData/nlp/prompt_learning/rte_CI_test.jsonl'] \ - model.data.validation_ds=['/home/TestData/nlp/prompt_learning/rte_CI_test.jsonl'] \ - model.global_batch_size=4" - sh "python examples/nlp/language_modeling/tuning/megatron_t5_ia3_eval.py \ - --config-name=megatron_t5_ia3_inference \ - adapter_model_file='examples/ia3_tuning/test_tp2_pp1.nemo' \ - language_model_path='/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m_tp2.nemo' \ - trainer.devices=2 \ - data.num_workers=1 \ - tensor_model_parallel_size=2 \ - data.global_batch_size=2 \ - data.micro_batch_size=2 \ - data.test_ds=['/home/TestData/nlp/prompt_learning/rte_CI_test.jsonl'] \ - pred_file_path='examples/ia3_tuning/test_tp2_pp1/preds.txt'" - sh "rm -rf examples/ia3_tuning/test_tp2_pp1.nemo" - sh "rm -rf examples/ia3_tuning/test_tp2_pp1" - } - } - } - } stage('L2: Speech Transcription') { when { @@ -3742,7 +3560,7 @@ assert_frame_equal(training_curve, gt_curve, rtol=1e-3, atol=1e-3)"''' } failFast true steps { - sh "python examples/nlp/language_modeling/tuning/megatron_gpt_sft.py \ + sh "python examples/nlp/language_modeling/tuning/megatron_gpt_peft_tuning.py \ trainer.devices=2 \ trainer.log_every_n_steps=1 \ trainer.val_check_interval=2 \ @@ -3756,6 +3574,7 @@ assert_frame_equal(training_curve, gt_curve, rtol=1e-3, atol=1e-3)"''' model.restore_from_path=/home/TestData/nlp/megatron_gpt/PP2/gpt_pp2_tp1.nemo \ model.optim.name=fused_adam \ model.optim.lr=2e-4 \ + model.peft.peft_scheme=null \ model.data.train_ds.micro_batch_size=1 \ model.data.train_ds.global_batch_size=4 \ model.data.train_ds.file_names=[/home/TestData/nlp/megatron_sft/quarel.jsonl,/home/TestData/nlp/megatron_sft/trec.jsonl] \ @@ -3770,7 +3589,7 @@ assert_frame_equal(training_curve, gt_curve, rtol=1e-3, atol=1e-3)"''' model.data.validation_ds.num_workers=0 \ model.data.validation_ds.file_names=[/home/TestData/nlp/megatron_sft/quarel.jsonl] \ model.data.validation_ds.names=[quarel]" - sh "python examples/nlp/language_modeling/tuning/megatron_gpt_sft.py \ + sh "python examples/nlp/language_modeling/tuning/megatron_gpt_peft_tuning.py \ trainer.devices=2 \ trainer.log_every_n_steps=1 \ trainer.val_check_interval=1 \ @@ -3784,6 +3603,7 @@ assert_frame_equal(training_curve, gt_curve, rtol=1e-3, atol=1e-3)"''' model.restore_from_path=/home/TestData/nlp/megatron_gpt/PP2/gpt_pp2_tp1.nemo \ model.optim.name=fused_adam \ model.optim.lr=2e-4 \ + model.peft.peft_scheme=null \ model.data.train_ds.micro_batch_size=1 \ model.data.train_ds.global_batch_size=4 \ model.data.train_ds.file_names=[/home/TestData/nlp/megatron_sft/quarel.jsonl,/home/TestData/nlp/megatron_sft/trec.jsonl] \ @@ -3870,14 +3690,15 @@ assert_frame_equal(training_curve, gt_curve, rtol=1e-3, atol=1e-3)"''' model.data.validation_ds.names=[quarel]" sh "python examples/nlp/language_modeling/tuning/megatron_gpt_peft_eval.py \ model.restore_from_path=/home/TestData/nlp/megatron_gpt/TP2/megatron_gpt_tp2.nemo \ - model.peft.restore_from_path=/home/TestData/nlp/lora_tuning_tp2/megatron_gpt_peft_tuning/checkpoints/megatron_gpt_peft_tuning.nemo \ + model.peft.restore_from_path=/home/TestData/nlp/lora_tuning_tp2/megatron_gpt_peft_lora_tuning/checkpoints/megatron_gpt_peft_lora_tuning.nemo \ model.peft.restore_from_ckpt_name=null \ model.peft.restore_from_hparams_path=null \ + model.tensor_model_parallel_size=2 \ trainer.devices=2 \ model.data.test_ds.file_names=[/home/TestData/nlp/megatron_sft/quarel_4.jsonl] \ model.data.test_ds.names=['quarel4'] \ - model.data.test_ds.global_batch_size=1 \ - model.data.test_ds.micro_batch_size=1 \ + model.global_batch_size=2 \ + model.micro_batch_size=1 \ model.data.test_ds.tokens_to_generate=10 \ model.data.test_ds.write_predictions_to_file=True \ model.data.test_ds.output_file_path_prefix='/home/TestData/nlp/lora_tuning_tp2/out' \ @@ -4428,136 +4249,6 @@ assert_frame_equal(training_curve, gt_curve, rtol=1e-3, atol=1e-3)"''' } } - // commented out to save time in github ci, we have tp>1 and pp>1 tests anyway @adithyare - //stage('L2: Megatron T5 Prompt Learning TP1 PP1') { - // when { - // anyOf { - // branch 'main' - // changeRequest target: 'main' - // } - // } - // failFast true - // parallel{ - // stage('T5 Prompt Learning TP=1 PP=1') { - // steps { - // sh "python examples/nlp/language_modeling/megatron_t5_prompt_learning.py \ - // --config-name=megatron_t5_prompt_learning \ - // name='/home/TestData/nlp/prompt_learning/t5_p_tuning_test' \ - // trainer.devices=1 \ - // trainer.max_steps=1 \ - // trainer.val_check_interval=1 \ - // trainer.max_epochs=null \ - // model.data.num_workers=1 \ - // model.language_model_path='/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m-refactor.nemo' \ - // model.existing_tasks=[] \ - // model.new_tasks=['squad'] \ - // model.data.train_ds=['/home/TestData/nlp/prompt_learning/squad_CI_test.jsonl'] \ - // model.data.validation_ds=['/home/TestData/nlp/prompt_learning/squad_CI_test.jsonl'] \ - // model.global_batch_size=4 \ - // model.micro_batch_size=4" - // sh "rm -rf /home/TestData/nlp/prompt_learning/t5_p_tuning_test" - // sh "python examples/nlp/language_modeling/megatron_t5_prompt_learning_eval.py \ - // virtual_prompt_model_file='/home/TestData/nlp/prompt_learning/t5_p_tuning_test.nemo' \ - // language_model_path='/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m-refactor.nemo' \ - // data.test_ds=['/home/TestData/nlp/prompt_learning/squad_CI_test.jsonl'] \ - // pred_file_path='/home/TestData/nlp/prompt_learning/t5_p_tuning_test_preds.txt' \ - // data.global_batch_size=4 \ - // data.micro_batch_size=4" - // sh "rm -rf /home/TestData/nlp/prompt_learning/t5_p_tuning_test.nemo" - // sh "rm -rf /home/TestData/nlp/prompt_learning/t5_p_tuning_test_preds.txt" - // } - // } - // } - //} - - stage('L2: Megatron T5 Prompt Learning TP2 PP1') { - when { - anyOf { - branch 'main' - changeRequest target: 'main' - } - } - failFast true - parallel{ - stage('T5 Prompt Learning TP=2 PP=1') { - steps { - sh "python examples/nlp/language_modeling/megatron_t5_prompt_learning.py \ - --config-name=megatron_t5_prompt_learning \ - name='/home/TestData/nlp/prompt_learning/t5_p_tuning_test_tp2' \ - trainer.devices=2 \ - trainer.max_steps=1 \ - trainer.val_check_interval=1 \ - trainer.max_epochs=null \ - model.data.num_workers=1 \ - model.tensor_model_parallel_size=2 \ - model.language_model_path='/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m_tp2.nemo' \ - model.existing_tasks=[] \ - model.new_tasks=['squad'] \ - model.data.train_ds=['/home/TestData/nlp/prompt_learning/squad_CI_test.jsonl'] \ - model.data.validation_ds=['/home/TestData/nlp/prompt_learning/squad_CI_test.jsonl'] \ - model.global_batch_size=8 \ - model.micro_batch_size=8" - sh "rm -rf /home/TestData/nlp/prompt_learning/t5_p_tuning_test_tp2" - sh "python examples/nlp/language_modeling/megatron_t5_prompt_learning_eval.py \ - virtual_prompt_model_file='/home/TestData/nlp/prompt_learning/t5_p_tuning_test_tp2.nemo' \ - language_model_path='/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m_tp2.nemo' \ - data.test_ds=['/home/TestData/nlp/prompt_learning/squad_CI_test.jsonl'] \ - pred_file_path='/home/TestData/nlp/prompt_learning/t5_p_tuning_test_tp2_preds.txt' \ - tensor_model_parallel_size=2 \ - trainer.devices=2 \ - data.global_batch_size=8 \ - data.micro_batch_size=8" - sh "rm -rf /home/TestData/nlp/prompt_learning/t5_p_tuning_test_tp2.nemo" - sh "rm -rf /home/TestData/nlp/prompt_learning/t5_p_tuning_test_tp2_preds.txt" - } - } - } - } - - // TODO: add when https://github.com/NVIDIA/apex/pull/1596 is merged - // stage('L2: Megatron T5 Prompt Learning TP1 PP2') { - // when { - // anyOf { - // branch 'main' - // changeRequest target: 'main' - // } - // } - // failFast true - // parallel{ - // stage('T5 Prompt Learning TP=1 PP=2') { - // steps { - // sh "python examples/nlp/language_modeling/megatron_t5_prompt_learning.py \ - // --config-name=megatron_t5_prompt_learning \ - // name='/home/TestData/nlp/prompt_learning/t5_p_tuning_test_pp2' \ - // trainer.devices=2 \ - // trainer.max_steps=1 \ - // trainer.val_check_interval=1 \ - // trainer.max_epochs=null \ - // model.data.num_workers=1 \ - // model.pipeline_model_parallel_size=2 \ - // model.language_model_path='/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m_tp1_pp2.nemo' \ - // model.existing_tasks=[] \ - // model.new_tasks=['squad'] \ - // model.data.train_ds=['/home/TestData/nlp/prompt_learning/squad_CI_test.jsonl'] \ - // model.data.validation_ds=['/home/TestData/nlp/prompt_learning/squad_CI_test.jsonl'] \ - // model.global_batch_size=8 \ - // model.micro_batch_size=8" - // sh "rm -rf /home/TestData/nlp/prompt_learning/t5_p_tuning_test_pp2" - // sh "python examples/nlp/language_modeling/megatron_t5_prompt_learning_eval.py \ - // virtual_prompt_model_file='/home/TestData/nlp/prompt_learning/t5_p_tuning_test_pp2.nemo' \ - // language_model_path='/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m_tp1_pp2.nemo' \ - // data.test_ds=['/home/TestData/nlp/prompt_learning/squad_CI_test.jsonl'] \ - // pred_file_path='/home/TestData/nlp/prompt_learning/t5_p_tuning_test_pp2_preds.txt' \ - // tensor_model_parallel_size=2 \ - // trainer.devices=2 \ - // data.global_batch_size=8 \ - // data.micro_batch_size=8" - // sh "rm -rf /home/TestData/nlp/prompt_learning/t5_p_tuning_test_pp2.nemo" - // sh "rm -rf /home/TestData/nlp/prompt_learning/t5_p_tuning_test_pp2_preds.txt" - // } - // } - // } - // } stage('L2: Megatron UL2 Pretraining and Resume Training TP=2') { when { anyOf { @@ -4870,6 +4561,61 @@ assert_frame_equal(training_curve, gt_curve, rtol=1e-3, atol=1e-3)"''' } } } + + stage('L2: Megatron T5 PEFT Lora TP=2') { + when { + anyOf { + branch 'main' + changeRequest target: 'main' + } + } + failFast true + steps { + sh "rm -rf /home/TestData/nlp/t5_lora_tuning_tp2" + sh "python examples/nlp/language_modeling/tuning/megatron_t5_peft_tuning.py \ + trainer.devices=2 \ + trainer.log_every_n_steps=1 \ + trainer.max_epochs=9999 \ + trainer.max_steps=3 \ + trainer.val_check_interval=3 \ + ++trainer.limit_val_batches=2 \ + trainer.precision=16 \ + exp_manager.exp_dir=/home/TestData/nlp/t5_lora_tuning_tp2 \ + model.pipeline_model_parallel_size=1 \ + model.tensor_model_parallel_size=2 \ + model.restore_from_path=/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m_tp2.nemo \ + model.peft.peft_scheme='lora' \ + model.answer_only_loss=True \ + model.micro_batch_size=1 \ + model.global_batch_size=1 \ + model.data.train_ds.file_names=[/home/TestData/nlp/megatron_sft/quarel.jsonl] \ + model.data.train_ds.concat_sampling_probabilities=[1.0] \ + model.data.train_ds.num_workers=0 \ + model.data.validation_ds.num_workers=0 \ + model.data.validation_ds.file_names=[/home/TestData/nlp/megatron_sft/quarel.jsonl] \ + model.data.validation_ds.names=[quarel]" + sh "python examples/nlp/language_modeling/tuning/megatron_t5_peft_eval.py \ + model.restore_from_path=/home/TestData/nlp/megatron_t5/8m/megatron_t5_8m_tp2.nemo \ + model.peft.restore_from_path=/home/TestData/nlp/t5_lora_tuning_tp2/megatron_t5_peft_lora_tuning/checkpoints/megatron_t5_peft_lora_tuning.nemo \ + model.peft.restore_from_ckpt_name=null \ + model.peft.restore_from_hparams_path=null \ + model.tensor_model_parallel_size=2 \ + trainer.devices=2 \ + model.data.test_ds.file_names=[/home/TestData/nlp/megatron_sft/quarel_4.jsonl] \ + model.data.test_ds.names=['quarel4'] \ + model.global_batch_size=2 \ + model.micro_batch_size=1 \ + model.data.test_ds.tokens_to_generate=10 \ + model.data.test_ds.write_predictions_to_file=True \ + model.data.test_ds.output_file_path_prefix='/home/TestData/nlp/t5_lora_tuning_tp2/out' \ + inference.greedy=True \ + inference.repetition_penalty=1.0 \ + inference.outfile_path='/home/TestData/nlp/t5_lora_tuning_tp2/out.jsonl'" + sh "rm -rf /home/TestData/nlp/t5_lora_tuning_tp2" + } + } + + stage('L2: Megatron Mock Data Generation') { when { anyOf { diff --git a/docs/source/conf.py b/docs/source/conf.py index 0765f8940ab0..c54defb59ce8 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -52,6 +52,7 @@ 'attr', # attrdict in requirements, attr in import 'torchmetrics', # inherited from PTL 'lightning_utilities', # inherited from PTL + 'lightning_fabric', 'apex', 'megatron.core', 'transformer_engine', diff --git a/docs/source/nlp/api.rst b/docs/source/nlp/api.rst index b13dedca300f..33709bd05a19 100755 --- a/docs/source/nlp/api.rst +++ b/docs/source/nlp/api.rst @@ -81,7 +81,6 @@ Modules .. autoclass:: nemo.collections.nlp.modules.common.megatron.module.Float16Module :show-inheritance: - .. autoclass:: nemo.collections.nlp.models.language_modeling.megatron.gpt_model.GPTModel :show-inheritance: :no-members: @@ -140,11 +139,22 @@ Datasets .. autoclass:: nemo.collections.nlp.data.language_modeling.megatron.ul2_dataset.UL2Dataset :show-inheritance: + +Adapter Mixin Class +------------------------- + +.. autoclass:: nemo.collections.nlp.parts.mixins.nlp_adapter_mixins.NLPAdapterModelMixin + :show-inheritance: + :members: add_adapter, load_adapters, merge_cfg_with, merge_inference_cfg + :exclude-members: first_stage_of_pipeline, tie_weights, get_peft_state_dict, state_dict, sharded_state_dict, load_state_dict, on_load_checkpoint + :member-order: bysource + + Exportable Model Classes ------------------------- .. autoclass:: nemo.collections.nlp.models.language_modeling.megatron_gpt_model.MegatronGPTExportableModel - :show-inheritance: + :show-inheritance: .. toctree:: :maxdepth: 1 diff --git a/docs/source/nlp/nemo_megatron/intro.rst b/docs/source/nlp/nemo_megatron/intro.rst index 7525c778b974..c1b158d77e3e 100644 --- a/docs/source/nlp/nemo_megatron/intro.rst +++ b/docs/source/nlp/nemo_megatron/intro.rst @@ -25,6 +25,7 @@ team at NVIDIA. NeMo Megatron supports several types of models: prompt_learning retro/retro_model hiddens/hiddens_module + peft/landing_page References diff --git a/docs/source/nlp/nemo_megatron/peft/landing_page.rst b/docs/source/nlp/nemo_megatron/peft/landing_page.rst new file mode 100644 index 000000000000..c90dcdfff1c5 --- /dev/null +++ b/docs/source/nlp/nemo_megatron/peft/landing_page.rst @@ -0,0 +1,35 @@ +Parameter-Efficient Fine-Tuning (PEFT) +====================================== + +PEFT is a popular technique used to efficiently finetune large language +models for use in various downstream tasks. When finetuning with PEFT, +the base model weights are frozen, and a few trainable adapter modules +are injected into the model, resulting in a very small number (<< 1%) of +trainble weights. With carefully chosen adapter modules and injection +points, PEFT achieves comparable performance to full finetuning at a +fraction of the computational and storage costs. + +NeMo supports four PEFT methods which can be used with various +transformer-based models. + +==================== ===== ===== ========= == +\ GPT 3 NvGPT LLaMa 1/2 T5 +==================== ===== ===== ========= == +Adapters (Canonical) ✅ ✅ ✅ ✅ +LoRA ✅ ✅ ✅ ✅ +IA3 ✅ ✅ ✅ ✅ +P-Tuning ✅ ✅ ✅ ✅ +==================== ===== ===== ========= == + +Learn more about PEFT in NeMo with the :ref:`peftquickstart` which provides an overview on how PEFT works +in NeMo. Read about the supported PEFT methods +`here `__. For a practical example, take a look at +the `Step-by-step Guide `__. + +The API guide can be found `here <../../api.html#adapter-mixin-class>`__ + +.. toctree:: + :maxdepth: 1 + + quick_start + supported_methods \ No newline at end of file diff --git a/docs/source/nlp/nemo_megatron/peft/quick_start.rst b/docs/source/nlp/nemo_megatron/peft/quick_start.rst new file mode 100644 index 000000000000..000e242b9508 --- /dev/null +++ b/docs/source/nlp/nemo_megatron/peft/quick_start.rst @@ -0,0 +1,90 @@ +.. _peftquickstart: + + +Quick Start Guide +================= + +The quick start guide provides an overview of a PEFT workflow in NeMo. + +Terminology: PEFT vs Adapter +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This tutorial uses "PEFT" to describe the overall parameter efficient +finetuning method, and "adapter" to describe the additional module +injected to a frozen base model. Each PEFT model can use one or more +types of adapters. + +One of the PEFT methods is sometimes referred to as "adapters", because +it was one of the first proposed usage of adapter modules for NLP. This +PEFT method will be called the "canonical" adapters to distinguish the +two usages. + +How PEFT work in NeMo models +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Each PEFT method has one or more types of adapters that need to be +injected into the base model. In NeMo models, the adapter logic and +adapter weights are already built into the submodules, but they are +disabled by default for ordinary training and fine-tuning. + +When doing PEFT, the adapter logic path can be enabled when +``model.add_adapter(peft_cfg)`` is called. In this function, the model +scans through each adapter applicable to the current PEFT method with +each of its submodules in order to find adapter logic paths that can be +enabled. Then, the base models weights are frozen, while newly added +adapter weights are unfrozen and allowed to be updated during +fine-tuning, hence achieving efficiency in the number of parameters +finetuned. + +PEFT config classes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Each PEFT method is specified by a ``PEFTConfig`` class which stores the +types of adapters applicable to the PEFT method, as well as +hyperparameters required to initialize these adapter modules. These four +PEFT methods are currently supported: + +1. Adapters (canonical): ``CanonicalAdaptersPEFTConfig`` +2. LoRA: ``LoraPEFTConfig`` +3. IA3: ``IA3PEFTConfig`` +4. P-Tuning: ``PtuningPEFTConfig`` + +These config classes make experimenting with different adapters as easy +as changing the config class. + +Moreover, it is possible to use a combination of the PEFT methods in +NeMo since they are orthogonal to each other. This can be easily done by +passing in a list of ``PEFTConfig`` objects to ``add_adapter`` instead +of a single one. For example, a common workflow is to combine P-Tuning +and Adapter, and this can be achieved with +``model.add_adapter([PtuningPEFTConfig(model_cfg), CanonicalAdaptersPEFTConfig(model_cfg)])`` + +Base model classes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +PEFT in NeMo is built with a mix-in class that does not belong to any +model in particular. This means that the same interface is available to +different NeMo models. Currently, NeMo supports PEFT for GPT-style +models such as GPT 3, NvGPT, LLaMa 1/2 (``MegatronGPTSFTModel``), as +well as T5 (``MegatronT5SFTModel``). + +Full finetuning vs PEFT +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +You can switch between full fine-tuning and PEFT by removing calls to +``add_adapter`` and ``load_adapter``. + +The code snippet below illustrates the core API of full fine-tuning and +PEFT. + +.. code:: diff + + trainer = MegatronTrainerBuilder(config).create_trainer() + model_cfg = MegatronGPTSFTModel.merge_cfg_with(config.model.restore_from_path, config) + + model = MegatronGPTSFTModel.restore_from(restore_path, model_cfg, trainer) # restore from pretrained ckpt + + peft_cfg = LoRAPEFTConfig(model_cfg) + + model.add_adapter(peft_cfg) + trainer.fit(model) # saves adapter weights only + + # Restore from base then load adapter API + model = MegatronGPTSFTModel.restore_from(restore_path, trainer, model_cfg) + + model.load_adapters(adapter_save_path, peft_cfg) + model.freeze() + trainer.predict(model) diff --git a/docs/source/nlp/nemo_megatron/peft/supported_methods.rst b/docs/source/nlp/nemo_megatron/peft/supported_methods.rst new file mode 100644 index 000000000000..4479565be6aa --- /dev/null +++ b/docs/source/nlp/nemo_megatron/peft/supported_methods.rst @@ -0,0 +1,71 @@ + + +Supported PEFT methods +---------------------- + +NeMo supports the following PFET tuning methods + +1. **Adapters (Canonical)**: `Parameter-Efficient Transfer Learning for + NLP `__ + + - Adapters (Houlsby setup) is one of the first PEFT methods applied + to NLP. Adapter tuning is more efficient than full fine-tuning + because the base model weights are frozen, while only a small + number of adapter module weights are updated. In this method, two + linear layers with a bottleneck and a non-linear activation are + inserted into each transformer layer via a residual connection. In + each case, the output linear layer is initialized to 0 to ensure + that an untrained adapter does not affect the normal forward pass + of the transformer layer. + +2. **LoRA**: `LoRA: Low-Rank Adaptation of Large Language + Models `__ + + - LoRA makes fine-tuning efficient by representing weight updates + with two low rank decomposition matrices. The original model + weights remain frozen, while the low rank decomposition matrices + are updated to adapt to the new data , so the number of trainable + parameters is kept low. In contrast with adapters, the original + model weights and adapted weights can be combined during + inference, avoiding any architectural change or additional latency + in the model at inference time. + - The matrix decomposition operation can be applied to any linear + layer, but in practice, it is only applied to the K, Q, V + projection matrices (sometimes just applied to the Q,V layers). + Since NeMo's attention implementation fuses KQV into a single + projection, our LoRA implementation learns a single Low-Rank + projection for KQV in a combined fashion. + +3. **IA3**: `Few-Shot Parameter-Efficient Fine-Tuning is Better and + Cheaper than In-Context Learning `__ + + - IA3 makes fine-tuning efficient by rescaling activations with + learned vectors. The rescaling layers are injected in the + attention (for key and value) and feedforward modules in the base + model. Similar to other PEFT methods, only the rescaling vectors + are updated during fine-tuning to adapt to the new data so the + number of updated parameters is low. However, since rescaling + vectors are much smaller than low rank matrices (LoRA) and + bottleneck layers (Adapters), IA3 cuts down the number of + trainable parameters further by an order of magnitude. The + learning rescaling vectors can also be merged with the base + weights, leading to no architectural change and no additional + latency at inference time. + +4. **P-Tuning**: `GPT Understands, + Too `__ + + - P-tuning is an example of the prompt learning family of methods, + in which trainable virtual tokens are inserted into the model + input prompt to induce it to perform a task. Virtual tokens (also + called "continuous" or "soft" tokens) are embeddings that have no + concrete mapping to strings or characters within the model’s + vocabulary. They are simply 1D vectors that match the + dimensionality of real tokens which make up the model's + vocabulary. + - In p-tuning, an intermediate LSTM or MLP model is used to generate + virtual token embeddings. We refer to this intermediate model as + our ``prompt_encoder``. The prompt encoder parameters are randomly + initialized at the start of p-tuning. All base model parameters + are frozen, and only the prompt encoder weights are updated at + each training step. diff --git a/examples/nlp/language_modeling/megatron_t5_prompt_learning.py b/examples/nlp/language_modeling/megatron_t5_prompt_learning.py index ba335e39c225..3edca99e15a5 100644 --- a/examples/nlp/language_modeling/megatron_t5_prompt_learning.py +++ b/examples/nlp/language_modeling/megatron_t5_prompt_learning.py @@ -29,6 +29,7 @@ ) from nemo.core.config import hydra_runner from nemo.utils import logging +from nemo.utils.decorators import deprecated from nemo.utils.exp_manager import exp_manager mp.set_start_method("spawn", force=True) @@ -43,6 +44,10 @@ """ +@deprecated( + explanation=f"{__file__} is deprecated. Please use MegatronT5SFTModel.add_adapter() for PEFT features." + "See updated scripts `megatron_t5_peft_tuning.py` and `megatron_t5_peft_eval.py` for examples." +) @hydra_runner(config_path="conf", config_name="megatron_t5_prompt_learning.yaml") def main(cfg) -> None: logging.info("\n\n************** Experiment configuration ***********") diff --git a/examples/nlp/language_modeling/megatron_t5_prompt_learning_eval.py b/examples/nlp/language_modeling/megatron_t5_prompt_learning_eval.py index 3b932e99ced3..67640138b3ff 100644 --- a/examples/nlp/language_modeling/megatron_t5_prompt_learning_eval.py +++ b/examples/nlp/language_modeling/megatron_t5_prompt_learning_eval.py @@ -24,6 +24,7 @@ from nemo.collections.nlp.parts.nlp_overrides import NLPDDPStrategy from nemo.core.config import hydra_runner from nemo.utils.app_state import AppState +from nemo.utils.decorators import deprecated try: from megatron.core import parallel_state @@ -37,6 +38,10 @@ raise EnvironmentError("GPU is needed for the inference") +@deprecated( + explanation=f"{__file__} is deprecated. Please use MegatronT5SFTModel.add_adapter() for PEFT features." + "See updated scripts `megatron_t5_peft_tuning.py` and `megatron_t5_peft_eval.py` for examples." +) @hydra_runner(config_path="conf", config_name="megatron_t5_prompt_learning_inference") def main(cfg) -> None: diff --git a/examples/nlp/language_modeling/megatron_t5_seq2seq_eval.py b/examples/nlp/language_modeling/megatron_t5_seq2seq_eval.py index f00b37612663..6a5ab9fbf481 100644 --- a/examples/nlp/language_modeling/megatron_t5_seq2seq_eval.py +++ b/examples/nlp/language_modeling/megatron_t5_seq2seq_eval.py @@ -18,9 +18,9 @@ from pytorch_lightning.plugins.environments import TorchElasticEnvironment from pytorch_lightning.plugins.precision import MixedPrecisionPlugin -from nemo.collections.nlp.models.language_modeling.megatron_finetune_model import MegatronT5FinetuneModel from nemo.collections.nlp.models.language_modeling.megatron_glue_model import MegatronT5GLUEModel from nemo.collections.nlp.models.language_modeling.megatron_t0_model import MegatronT0Model +from nemo.collections.nlp.models.language_modeling.megatron_t5_sft_model import MegatronT5SFTModel from nemo.collections.nlp.parts.nlp_overrides import GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy from nemo.core.config import hydra_runner from nemo.utils import logging @@ -122,13 +122,13 @@ def main(cfg) -> None: model = load_from_checkpoint_dir(MegatronT0Model, cfg, trainer, modify_confg_fn=_modify_config) else: if cfg.model.restore_from_path: - t5_cfg = MegatronT5FinetuneModel.restore_from( + t5_cfg = MegatronT5SFTModel.restore_from( restore_path=cfg.model.restore_from_path, trainer=trainer, return_config=True ) - model = load_from_nemo(MegatronT5FinetuneModel, cfg, trainer, t5_cfg, modify_confg_fn=_modify_config) + model = load_from_nemo(MegatronT5SFTModel, cfg, trainer, t5_cfg, modify_confg_fn=_modify_config) else: validate_checkpoint_loading_args(cfg.model.pretrained_checkpoint) - model = load_from_checkpoint_dir(MegatronT5FinetuneModel, cfg, trainer, modify_confg_fn=_modify_config) + model = load_from_checkpoint_dir(MegatronT5SFTModel, cfg, trainer, modify_confg_fn=_modify_config) model.freeze() trainer.validate(model) diff --git a/examples/nlp/language_modeling/megatron_t5_seq2seq_finetune.py b/examples/nlp/language_modeling/megatron_t5_seq2seq_finetune.py index e8e4ff0f9868..59ca94082f26 100644 --- a/examples/nlp/language_modeling/megatron_t5_seq2seq_finetune.py +++ b/examples/nlp/language_modeling/megatron_t5_seq2seq_finetune.py @@ -21,9 +21,9 @@ from pytorch_lightning.plugins.environments import TorchElasticEnvironment from pytorch_lightning.trainer.connectors.checkpoint_connector import _CheckpointConnector -from nemo.collections.nlp.models.language_modeling.megatron_finetune_model import MegatronT5FinetuneModel from nemo.collections.nlp.models.language_modeling.megatron_glue_model import MegatronT5GLUEModel from nemo.collections.nlp.models.language_modeling.megatron_t0_model import MegatronT0Model +from nemo.collections.nlp.models.language_modeling.megatron_t5_sft_model import MegatronT5SFTModel from nemo.collections.nlp.modules.common.megatron.megatron_init import fake_initialize_model_parallel from nemo.collections.nlp.parts.nlp_overrides import ( CustomProgressBar, @@ -207,13 +207,13 @@ def main(cfg) -> None: model = load_from_checkpoint_dir(MegatronT0Model, cfg, trainer, modify_confg_fn=_modify_config) else: if cfg.model.restore_from_path: - t5_cfg = MegatronT5FinetuneModel.restore_from( + t5_cfg = MegatronT5SFTModel.restore_from( restore_path=cfg.model.restore_from_path, trainer=trainer, return_config=True ) - model = load_from_nemo(MegatronT5FinetuneModel, cfg, trainer, t5_cfg, modify_confg_fn=_modify_config) + model = load_from_nemo(MegatronT5SFTModel, cfg, trainer, t5_cfg, modify_confg_fn=_modify_config) else: validate_checkpoint_loading_args(cfg.model.pretrained_checkpoint) - model = load_from_checkpoint_dir(MegatronT5FinetuneModel, cfg, trainer, modify_confg_fn=_modify_config) + model = load_from_checkpoint_dir(MegatronT5SFTModel, cfg, trainer, modify_confg_fn=_modify_config) trainer.fit(model) trainer.validate(model) diff --git a/examples/nlp/language_modeling/tuning/conf/megatron_gpt_peft_eval_config.yaml b/examples/nlp/language_modeling/tuning/conf/megatron_gpt_peft_eval_config.yaml index ffa5385a7d6c..0409e0fec0aa 100755 --- a/examples/nlp/language_modeling/tuning/conf/megatron_gpt_peft_eval_config.yaml +++ b/examples/nlp/language_modeling/tuning/conf/megatron_gpt_peft_eval_config.yaml @@ -26,7 +26,7 @@ exp_manager: resume_ignore_no_checkpoint: True create_checkpoint_callback: True checkpoint_callback_params: - monitor: validation_${model.data.validation_ds.metric.name} + monitor: validation_${model.data.test_ds.metric.name} save_top_k: 1 mode: max save_nemo_on_train_end: True @@ -39,12 +39,12 @@ model: seed: 1234 tensor_model_parallel_size: 1 # intra-layer model parallelism pipeline_model_parallel_size: 1 # inter-layer model parallelism - + global_batch_size: 1 micro_batch_size: 1 restore_from_path: ??? # Path to an existing .nemo model you wish to add new tasks to or run inference with resume_from_checkpoint: null # The path to a checkpoint file to continue the training, restores the whole state including the epoch, step, LR schedulers, apex, etc. - save_nemo_on_validation_end: True # Saves an inference ready .nemo file every time a checkpoint is saved during training. + save_nemo_on_validation_end: True # Saves an inference ready .nemo file every time a checkpoint is saved during training. sync_batch_comm: False megatron_amp_O2: False @@ -53,8 +53,8 @@ model: # See Reducing Activation Recomputation in Large Transformer Models: https://arxiv.org/abs/2205.05198 for more details. sequence_parallel: False - ## Activation Checkpoint - activations_checkpoint_granularity: null # 'selective' or 'full' + ## Activation Checkpoint + activations_checkpoint_granularity: null # 'selective' or 'full' activations_checkpoint_method: null # 'uniform', 'block', not used with 'selective' # 'uniform' divides the total number of transformer layers and checkpoints the input activation # of each chunk at the specified granularity @@ -73,17 +73,29 @@ model: restore_from_path: null restore_from_ckpt_name: null restore_from_hparams_path: null - + # Used for adapter peft training adapter_tuning: type: 'parallel_adapter' # this should be either 'parallel_adapter' or 'linear_adapter' adapter_dim: 32 adapter_dropout: 0.0 - norm_position: 'pre' # This can be set to 'pre' or 'post', 'pre' is normally what is used. + norm_position: 'pre' # This can be set to 'pre', 'post' or null, 'pre' is normally what is used. column_init_method: 'xavier' # IGNORED if linear_adapter is used, options: xavier, zero or normal row_init_method: 'zero' # IGNORED if linear_adapter is used, options: xavier, zero or normal norm_type: 'mixedfusedlayernorm' # IGNORED if layer_adapter is used, options are ['layernorm', 'mixedfusedlayernorm'] - + layer_selection: null # selects in which layers to add adapters, e.g. [1,12] will add adapters to layer 1 (lowest) and 12. null will apply adapters to all layers + weight_tying: False + position_embedding_strategy: null # used only when weight_tying is True + + lora_tuning: + adapter_dim: 32 + adapter_dropout: 0.0 + column_init_method: 'xavier' # IGNORED if linear_adapter is used, options: xavier, zero or normal + row_init_method: 'zero' # IGNORED if linear_adapter is used, options: xavier, zero or normal + layer_selection: null # selects in which layers to add lora adapters. e.g. [1,12] will add lora to layer 1 (lowest) and 12. null will apply adapters to all layers + weight_tying: False + position_embedding_strategy: null # used only when weight_tying is True + # Used for p-tuning peft training p_tuning: virtual_tokens: 10 # The number of virtual tokens the prompt encoder should add at the start of the sequence @@ -91,18 +103,22 @@ model: embedding_dim: 1024 # the size of the prompt encoder embeddings init_std: 0.023 + ia3_tuning: + layer_selection: null # selects in which layers to add ia3 adapters. e.g. [1,12] will add lora to layer 1 (lowest) and 12. null will apply adapters to all layers + data: test_ds: file_names: ??? # Path to a list of JSONL files corresponding to the source data. Data format is identical to train_ds. names: ??? # Names of the corresponding datasets used to log metrics. - global_batch_size: ??? - micro_batch_size: ??? + global_batch_size: 1 + micro_batch_size: 1 shuffle: False num_workers: 0 pin_memory: True max_seq_length: 2048 min_seq_length: 1 drop_last: False + context_key: 'input' label_key: ${data.train_ds.label_key} add_eos: ${data.train_ds.add_eos} add_sep: ${data.train_ds.add_sep} diff --git a/examples/nlp/language_modeling/tuning/conf/megatron_gpt_peft_tuning_config.yaml b/examples/nlp/language_modeling/tuning/conf/megatron_gpt_peft_tuning_config.yaml index b020c1aa49ad..6b6af4b1c81b 100755 --- a/examples/nlp/language_modeling/tuning/conf/megatron_gpt_peft_tuning_config.yaml +++ b/examples/nlp/language_modeling/tuning/conf/megatron_gpt_peft_tuning_config.yaml @@ -1,4 +1,4 @@ -name: megatron_gpt_peft_tuning +name: megatron_gpt_peft_${model.peft.peft_scheme}_tuning trainer: devices: 1 @@ -10,7 +10,7 @@ trainer: use_distributed_sampler: False max_epochs: 9999 max_steps: 20000 # consumed_samples = global_step * micro_batch_size * data_parallel_size * accumulate_grad_batches - log_every_n_steps: 10 # frequency with which training steps are logged + log_every_n_steps: 10 # frequency with which training steps are logged val_check_interval: 200 # If is an int n > 1, will run val every n training steps, if a float 0.0 - 1.0 will run val every epoch fraction, e.g. 0.25 will run val every quarter epoch gradient_clip_val: 1.0 @@ -47,12 +47,12 @@ model: seed: 1234 tensor_model_parallel_size: 1 # intra-layer model parallelism pipeline_model_parallel_size: 1 # inter-layer model parallelism - + global_batch_size: 128 micro_batch_size: 4 restore_from_path: ??? # Path to an existing .nemo model you wish to add new tasks to or run inference with resume_from_checkpoint: null # The path to a checkpoint file to continue the training, restores the whole state including the epoch, step, LR schedulers, apex, etc. - save_nemo_on_validation_end: False # Saves an inference ready .nemo file every time a checkpoint is saved during training. + save_nemo_on_validation_end: False # Saves an inference ready .nemo file every time a checkpoint is saved during training. sync_batch_comm: False megatron_amp_O2: False @@ -61,8 +61,8 @@ model: # See Reducing Activation Recomputation in Large Transformer Models: https://arxiv.org/abs/2205.05198 for more details. sequence_parallel: False - ## Activation Checkpoint - activations_checkpoint_granularity: null # 'selective' or 'full' + ## Activation Checkpoint + activations_checkpoint_granularity: null # 'selective' or 'full' activations_checkpoint_method: null # 'uniform', 'block', not used with 'selective' # 'uniform' divides the total number of transformer layers and checkpoints the input activation # of each chunk at the specified granularity @@ -79,7 +79,7 @@ model: peft: peft_scheme: "adapter" # can be either adapter,ia3, or ptuning restore_from_path: null - + # Used for adapter peft training adapter_tuning: type: 'parallel_adapter' # this should be either 'parallel_adapter' or 'linear_adapter' @@ -92,7 +92,7 @@ model: layer_selection: null # selects in which layers to add adapters, e.g. [1,12] will add adapters to layer 1 (lowest) and 12. null will apply adapters to all layers weight_tying: False position_embedding_strategy: null # used only when weight_tying is True - + lora_tuning: adapter_dim: 32 adapter_dropout: 0.0 @@ -101,21 +101,21 @@ model: layer_selection: null # selects in which layers to add lora adapters. e.g. [1,12] will add lora to layer 1 (lowest) and 12. null will apply adapters to all layers weight_tying: False position_embedding_strategy: null # used only when weight_tying is True - + # Used for p-tuning peft training p_tuning: virtual_tokens: 10 # The number of virtual tokens the prompt encoder should add at the start of the sequence bottleneck_dim: 1024 # the size of the prompt encoder mlp bottleneck embedding_dim: 1024 # the size of the prompt encoder embeddings init_std: 0.023 - + ia3_tuning: layer_selection: null # selects in which layers to add ia3 adapters. e.g. [1,12] will add lora to layer 1 (lowest) and 12. null will apply adapters to all layers data: train_ds: # Example of how to specify paths to multiple datasets - # file_names: + # file_names: # - /path/to/squad.jsonl # - /path/to/mnli.jsonl # - /path/to/boolq.jsonl diff --git a/examples/nlp/language_modeling/tuning/conf/megatron_t5_peft_eval_config.yaml b/examples/nlp/language_modeling/tuning/conf/megatron_t5_peft_eval_config.yaml new file mode 100644 index 000000000000..0ef90a2343fa --- /dev/null +++ b/examples/nlp/language_modeling/tuning/conf/megatron_t5_peft_eval_config.yaml @@ -0,0 +1,213 @@ +name: megatron_t5_peft_${model.peft.peft_scheme}_tuning + +trainer: + devices: 1 + accelerator: gpu + num_nodes: 1 + precision: 16 + logger: False # logger provided by exp_manager + enable_checkpointing: False + use_distributed_sampler: False + max_epochs: 9999 + max_steps: 20000 # consumed_samples = global_step * micro_batch_size * data_parallel_size * accumulate_grad_batches + log_every_n_steps: 10 # frequency with which training steps are logged + val_check_interval: 200 # If is an int n > 1, will run val every n training steps, if a float 0.0 - 1.0 will run val every epoch fraction, e.g. 0.25 will run val every quarter epoch + gradient_clip_val: 1.0 + +exp_manager: + explicit_log_dir: null + exp_dir: null + name: ${name} + create_wandb_logger: False + wandb_logger_kwargs: + project: null + name: null + resume_if_exists: True + resume_ignore_no_checkpoint: True + create_checkpoint_callback: True + checkpoint_callback_params: + monitor: validation_${model.data.test_ds.metric.name} + save_top_k: 1 + mode: max + save_nemo_on_train_end: True + filename: '${name}--{${exp_manager.checkpoint_callback_params.monitor}:.3f}-{step}-{consumed_samples}' + model_parallel_size: ${model.tensor_model_parallel_size} + always_save_nemo: True + save_best_model: False + +model: + seed: 1234 + tensor_model_parallel_size: 1 # intra-layer model parallelism + pipeline_model_parallel_size: 1 # inter-layer model parallelism + + global_batch_size: 1 + micro_batch_size: 1 + restore_from_path: ??? # Path to an existing .nemo model you wish to add new tasks to or run inference with + resume_from_checkpoint: null # The path to a checkpoint file to continue the training, restores the whole state including the epoch, step, LR schedulers, apex, etc. + save_nemo_on_validation_end: True # Saves an inference ready .nemo file every time a checkpoint is saved during training. + sync_batch_comm: False + megatron_amp_O2: False + + ## Sequence Parallelism + # Makes tensor parallelism more memory efficient for LLMs (20B+) by parallelizing layer norms and dropout sequentially + # See Reducing Activation Recomputation in Large Transformer Models: https://arxiv.org/abs/2205.05198 for more details. + sequence_parallel: False + + ## Activation Checkpoint + activations_checkpoint_granularity: null # 'selective' or 'full' + activations_checkpoint_method: null # 'uniform', 'block', not used with 'selective' + # 'uniform' divides the total number of transformer layers and checkpoints the input activation + # of each chunk at the specified granularity + # 'block' checkpoints the specified number of layers per pipeline stage at the specified granularity + activations_checkpoint_num_layers: null # not used with 'selective' + activations_checkpoint_layers_per_pipeline: null + answer_only_loss: False # not used right now + gradient_as_bucket_view: False + + hidden_dropout: 0.0 + attention_dropout: 0.0 + ffn_dropout: 0.0 + + peft: + peft_scheme: "adapter" # can be either adapter,ia3, or ptuning + restore_from_path: null + restore_from_ckpt_name: null + restore_from_hparams_path: null + + # Used for adapter peft training + adapter_tuning: + type: 'parallel_adapter' # this should be either 'parallel_adapter' or 'linear_adapter' + adapter_dim: 32 + adapter_dropout: 0.0 + norm_position: 'pre' # This can be set to 'pre', 'post' or null, 'pre' is normally what is used. + column_init_method: 'xavier' # IGNORED if linear_adapter is used, options: xavier, zero or normal + row_init_method: 'zero' # IGNORED if linear_adapter is used, options: xavier, zero or normal + norm_type: 'mixedfusedlayernorm' # IGNORED if layer_adapter is used, options are ['layernorm', 'mixedfusedlayernorm'] + layer_selection: null # selects in which layers to add adapters, e.g. [1,12] will add adapters to layer 1 (lowest) and 12. null will apply adapters to all layers + weight_tying: False + position_embedding_strategy: null # used only when weight_tying is True + + lora_tuning: + adapter_dim: 32 + adapter_dropout: 0.0 + column_init_method: 'xavier' # IGNORED if linear_adapter is used, options: xavier, zero or normal + row_init_method: 'zero' # IGNORED if linear_adapter is used, options: xavier, zero or normal + layer_selection: null # selects in which layers to add lora adapters. e.g. [1,12] will add lora to layer 1 (lowest) and 12. null will apply adapters to all layers + weight_tying: False + position_embedding_strategy: null # used only when weight_tying is True + + # Used for p-tuning peft training + p_tuning: + virtual_tokens: 10 # The number of virtual tokens the prompt encoder should add at the start of the sequence + bottleneck_dim: 1024 # the size of the prompt encoder mlp bottleneck + embedding_dim: 1024 # the size of the prompt encoder embeddings + init_std: 0.023 + + ia3_tuning: + layer_selection: null # selects in which layers to add ia3 adapters. e.g. [1,12] will add lora to layer 1 (lowest) and 12. null will apply adapters to all layers + + data: + test_ds: + file_names: ??? # Path to a list of JSONL files corresponding to the source data. Data format is identical to train_ds. + names: ??? # Names of the corresponding datasets used to log metrics. + global_batch_size: 1 #${model.global_batch_size} + micro_batch_size: 1 #${model.micro_batch_size} + shuffle: False + num_workers: 0 + pin_memory: True + max_seq_length: 2048 + min_seq_length: 1 + drop_last: False + context_key: 'input' + label_key: ${data.train_ds.label_key} + add_eos: ${data.train_ds.add_eos} + add_sep: ${data.train_ds.add_sep} + add_bos: ${data.train_ds.add_bos} + separate_prompt_and_response_with_newline: ${data.train_ds.separate_prompt_and_response_with_newline} + write_predictions_to_file: False + output_file_path_prefix: null # Prefix of the file to write predictions to. + truncation_field: ${data.train_ds.truncation_field} # Options: keys in prompt_template index_mapping_dir: null # Path to a directory to write index mapping files. + index_mapping_dir: null # Path to a directory to write index mapping files. + prompt_template: ${data.train_ds.prompt_template} + tokens_to_generate: 32 # decide how many tokens we want to generate to evaluate performance with string metrics + + metric: + name: "loss" # Name of the evaluation metric to use. Options: ['exact_string_match', 'loss'] + average: null # Average the metric over the dataset. Options: ['macro', 'micro']. Works only for 'F1', 'accuracy' etc. Refer to torchmetrics for metrics where this is supported. + num_classes: null + +inference: + greedy: True # Whether or not to use sampling ; use greedy decoding otherwise + top_k: 0 # The number of highest probability vocabulary tokens to keep for top-k-filtering. + top_p: 0.9 # If set to float < 1, only the most probable tokens with probabilities that add up to top_p or higher are kept for generation. + temperature: 1.0 # sampling temperature + all_probs: False # whether return the log prob for all the tokens in vocab + repetition_penalty: 1.2 # The parameter for repetition penalty. 1.0 means no penalty. + min_tokens_to_generate: 0 # The minimum length of the sequence to be generated. + compute_logprob: False # a flag used to compute logprob of all the input text, a very special case of running inference, default False + outfile_path: output.txt + compute_attention_mask: True + +# server-related configs +server: False # whether launch the API server +port: 5555 # the port number for the inference server +web_server: False # whether launch the web inference server +share: True # whether create a public URL +username: test # user name for web client +password: test2 # password for web client +web_port: 9889 # the port number of the web server 1058 +chat: False # use the chat interface +chatbot_config: + value: False # whether to inject the value attributes + attributes: + - name: Quality + min: 0 + max: 4 + key: quality + type: int + default: 4 + - name: Toxicity + min: 0 + max: 4 + key: toxcity + type: int + default: 0 + - name: Humor + min: 0 + max: 4 + key: humor + type: int + default: 0 + - name: Creativity + min: 0 + max: 4 + key: creativity + type: int + default: 0 + - name: Violence + min: 0 + max: 4 + key: violence + type: int + default: 0 + - name: Helpfulness + min: 0 + max: 4 + key: helpfulness + type: int + default: 4 + - name: Not_Appropriate + min: 0 + max: 4 + key: not_appropriate + type: int + default: 0 + - name: Language + choices: ['ar', 'bg', 'bn', 'ca', 'cs', 'da', 'de', 'el', 'en', 'eo', 'es', 'eu', 'fa', 'fi', 'fr', 'gl', 'he', 'hu', 'id', 'it', 'ja', 'ko', 'nb', 'nl', 'pl', 'pt', 'ro', 'ru', 'sk', 'sv', 'th', 'tr', 'uk', 'vi', 'zh'] + key: lang + type: list + default: en + + user: User + assistant: Assistant + system: "A chat between a curious human and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the human's questions.\n\n" \ No newline at end of file diff --git a/examples/nlp/language_modeling/tuning/conf/megatron_t5_peft_tuning_config.yaml b/examples/nlp/language_modeling/tuning/conf/megatron_t5_peft_tuning_config.yaml new file mode 100644 index 000000000000..d0ee2c417856 --- /dev/null +++ b/examples/nlp/language_modeling/tuning/conf/megatron_t5_peft_tuning_config.yaml @@ -0,0 +1,220 @@ +name: megatron_t5_peft_${model.peft.peft_scheme}_tuning + +trainer: + devices: 1 + accelerator: gpu + num_nodes: 1 + precision: 16 + logger: False # logger provided by exp_manager + enable_checkpointing: False + use_distributed_sampler: False + max_epochs: 9999 + max_steps: 20000 # consumed_samples = global_step * micro_batch_size * data_parallel_size * accumulate_grad_batches + log_every_n_steps: 10 # frequency with which training steps are logged + val_check_interval: 200 # If is an int n > 1, will run val every n training steps, if a float 0.0 - 1.0 will run val every epoch fraction, e.g. 0.25 will run val every quarter epoch + gradient_clip_val: 1.0 + +exp_manager: + explicit_log_dir: null + exp_dir: null + name: ${name} + create_wandb_logger: False + wandb_logger_kwargs: + project: null + name: null + resume_if_exists: True + resume_ignore_no_checkpoint: True + create_checkpoint_callback: True + checkpoint_callback_params: + monitor: validation_${model.data.validation_ds.metric.name} + save_top_k: 1 + mode: min + save_nemo_on_train_end: True + filename: '${name}--{${exp_manager.checkpoint_callback_params.monitor}:.3f}-{step}-{consumed_samples}' + model_parallel_size: ${model.tensor_model_parallel_size} + always_save_nemo: False + save_best_model: True + create_early_stopping_callback: True + early_stopping_callback_params: + monitor: "val_loss" + mode: "min" + min_delta: 0.001 + patience: 10 + verbose: True + strict: False # Should be False to avoid a runtime error where EarlyStopping says monitor is unavailable, which sometimes happens with resumed training. + +model: + seed: 1234 + tensor_model_parallel_size: 1 # intra-layer model parallelism + pipeline_model_parallel_size: 1 # inter-layer model parallelism + + global_batch_size: 128 + micro_batch_size: 4 + restore_from_path: ??? # Path to an existing .nemo model you wish to add new tasks to or run inference with + resume_from_checkpoint: null # The path to a checkpoint file to continue the training, restores the whole state including the epoch, step, LR schedulers, apex, etc. + save_nemo_on_validation_end: False # Saves an inference ready .nemo file every time a checkpoint is saved during training. + sync_batch_comm: False + megatron_amp_O2: False + + ## Sequence Parallelism + # Makes tensor parallelism more memory efficient for LLMs (20B+) by parallelizing layer norms and dropout sequentially + # See Reducing Activation Recomputation in Large Transformer Models: https://arxiv.org/abs/2205.05198 for more details. + sequence_parallel: False + + ## Activation Checkpoint + activations_checkpoint_granularity: null # 'selective' or 'full' + activations_checkpoint_method: null # 'uniform', 'block', not used with 'selective' + # 'uniform' divides the total number of transformer layers and checkpoints the input activation + # of each chunk at the specified granularity + # 'block' checkpoints the specified number of layers per pipeline stage at the specified granularity + activations_checkpoint_num_layers: null # not used with 'selective' + activations_checkpoint_layers_per_pipeline: null + answer_only_loss: True + gradient_as_bucket_view: False + + hidden_dropout: 0.0 + attention_dropout: 0.0 + ffn_dropout: 0.0 + + peft: + peft_scheme: "adapter" # can be either adapter,ia3, or ptuning + restore_from_path: null + + # Used for adapter peft training + adapter_tuning: + type: 'parallel_adapter' # this should be either 'parallel_adapter' or 'linear_adapter' + adapter_dim: 32 + adapter_dropout: 0.0 + norm_position: 'pre' # This can be set to 'pre', 'post' or null, 'pre' is normally what is used. + column_init_method: 'xavier' # IGNORED if linear_adapter is used, options: xavier, zero or normal + row_init_method: 'zero' # IGNORED if linear_adapter is used, options: xavier, zero or normal + norm_type: 'mixedfusedlayernorm' # IGNORED if layer_adapter is used, options are ['layernorm', 'mixedfusedlayernorm'] + layer_selection: null # selects in which layers to add adapters, e.g. [1,12] will add adapters to layer 1 (lowest) and 12. null will apply adapters to all layers + weight_tying: False + position_embedding_strategy: null # used only when weight_tying is True + + lora_tuning: + adapter_dim: 32 + adapter_dropout: 0.0 + column_init_method: 'xavier' # IGNORED if linear_adapter is used, options: xavier, zero or normal + row_init_method: 'zero' # IGNORED if linear_adapter is used, options: xavier, zero or normal + layer_selection: null # selects in which layers to add lora adapters. e.g. [1,12] will add lora to layer 1 (lowest) and 12. null will apply adapters to all layers + weight_tying: False + position_embedding_strategy: null # used only when weight_tying is True + + # Used for p-tuning peft training + p_tuning: + virtual_tokens: 10 # The number of virtual tokens the prompt encoder should add at the start of the sequence + bottleneck_dim: 1024 # the size of the prompt encoder mlp bottleneck + embedding_dim: 1024 # the size of the prompt encoder embeddings + init_std: 0.023 + + ia3_tuning: + layer_selection: null # selects in which layers to add ia3 adapters. e.g. [1,12] will add lora to layer 1 (lowest) and 12. null will apply adapters to all layers + + data: + train_ds: + # Example of how to specify paths to multiple datasets + # file_names: + # - /path/to/squad.jsonl + # - /path/to/mnli.jsonl + # - /path/to/boolq.jsonl + # Example of how each dataset is formatted + # {'input': 'John von Neumann\nVon Neumann made fundamental contributions .... Q: What did the math of artificial viscosity do?', 'output': 'smoothed the shock transition without sacrificing basic physics'} + file_names: ??? # Path to a list of JSONL files corresponding to the source data. + global_batch_size: ${model.global_batch_size} + micro_batch_size: ${model.micro_batch_size} + shuffle: True + num_workers: 0 + memmap_workers: 2 + pin_memory: True + max_seq_length: 2048 + min_seq_length: 1 + drop_last: True + # Example of how to specify concat_sampling_probabilities + # concat_sampling_probabilities: + # - 0.5 + # - 0.25 + # - 0.25 + concat_sampling_probabilities: null # When providing a list of datasets, this arg defines the sampling probabilities from each dataset when strategy='random' + context_key: 'input' + label_key: 'output' + add_eos: True + add_sep: False + add_bos: False + separate_prompt_and_response_with_newline: False + truncation_field: "context" # Options: ['context', 'answer'] + index_mapping_dir: null # Path to a directory to write index mapping files. + prompt_template: "{input} {output}" # fstring to use for assistant prompt. Example: "Q: {input}\nA: {output}" + + validation_ds: + file_names: ??? # Path to a list of JSONL files corresponding to the source data. Data format is identical to train_ds. + names: null # Names of the corresponding datasets used to log metrics. + global_batch_size: ${model.global_batch_size} + micro_batch_size: ${model.micro_batch_size} + shuffle: False + num_workers: 0 + memmap_workers: ${model.data.train_ds.memmap_workers} + pin_memory: True + max_seq_length: 2048 + min_seq_length: 1 + drop_last: False + context_key: 'input' + label_key: 'output' + add_eos: ${model.data.train_ds.add_eos} + add_sep: ${model.data.train_ds.add_sep} + add_bos: ${model.data.train_ds.add_bos} + separate_prompt_and_response_with_newline: ${model.data.train_ds.separate_prompt_and_response_with_newline} + write_predictions_to_file: False + output_file_path_prefix: null # Prefix of the file to write predictions to. + truncation_field: "context" # Options: ['context', 'answer'] + index_mapping_dir: null # Path to a directory to write index mapping files. + prompt_template: ${model.data.train_ds.prompt_template} # fstring to use for assistant prompt. Example: "Q: {input}\nA: {output}" + tokens_to_generate: 32 # decide how many tokens we want to generate to evaluate performance with string metrics + metric: + name: "loss" # Name of the evaluation metric to use. Options: ['exact_string_match', 'loss'] + average: null # Average the metric over the dataset. Options: ['macro', 'micro']. Works only for 'F1', 'accuracy' etc. Refer to torchmetrics for metrics where this is supported. + num_classes: null + test_ds: + file_names: null # Path to a list of JSONL files corresponding to the source data. Data format is identical to train_ds. + names: null # Names of the corresponding datasets used to log metrics. + global_batch_size: ${model.global_batch_size} + micro_batch_size: ${model.micro_batch_size} + shuffle: False + num_workers: 0 + memmap_workers: ${model.data.train_ds.memmap_workers} + pin_memory: True + max_seq_length: 2048 + min_seq_length: 1 + drop_last: False + context_key: 'input' + label_key: 'output' + add_eos: ${model.data.train_ds.add_eos} + add_sep: ${model.data.train_ds.add_sep} + add_bos: ${model.data.train_ds.add_bos} + separate_prompt_and_response_with_newline: ${model.data.train_ds.separate_prompt_and_response_with_newline} + write_predictions_to_file: False + output_file_path_prefix: null # Prefix of the file to write predictions to. + truncation_field: "context" # Options: ['context', 'answer'] + index_mapping_dir: null # Path to a directory to write index mapping files. + prompt_template: ${model.data.train_ds.prompt_template} + tokens_to_generate: 32 # decide how many tokens we want to generate to evaluate performance with string metrics + metric: + name: "loss" # Name of the evaluation metric to use. Options: ['exact_string_match', 'loss'] + average: null # Average the metric over the dataset. Options: ['macro', 'micro']. Works only for 'F1', 'accuracy' etc. Refer to torchmetrics for metrics where this is supported. + num_classes: null + + optim: + name: fused_adam + lr: 1e-4 + weight_decay: 0.01 + betas: + - 0.9 + - 0.98 + sched: + name: CosineAnnealing + warmup_steps: 50 + min_lr: 0.0 # min_lr must be 0.0 for prompt learning when pipeline parallel > 1 + constant_steps: 0 # Constant steps should also be 0 when min_lr=0 + monitor: val_loss + reduce_on_plateau: false \ No newline at end of file diff --git a/examples/nlp/language_modeling/tuning/megatron_gpt_adapter_eval.py b/examples/nlp/language_modeling/tuning/megatron_gpt_adapter_eval.py index ea368992bd5a..d08f4291321a 100644 --- a/examples/nlp/language_modeling/tuning/megatron_gpt_adapter_eval.py +++ b/examples/nlp/language_modeling/tuning/megatron_gpt_adapter_eval.py @@ -27,6 +27,7 @@ from nemo.collections.nlp.parts.nlp_overrides import NLPDDPStrategy, NLPSaveRestoreConnector from nemo.core.config import hydra_runner from nemo.utils import logging +from nemo.utils.decorators import deprecated mp.set_start_method("spawn", force=True) @@ -47,6 +48,10 @@ raise EnvironmentError("GPU is needed for the inference") +@deprecated( + explanation=f"{__file__} is deprecated. Please use MegatronGPTSFTModel.add_adapter() for PEFT features." + "See updated scripts `megatron_gpt_peft_tuning.py` and `megatron_gpt_peft_eval.py` for examples." +) @hydra_runner(config_path="conf", config_name="megatron_gpt_adapter_inference") def main(cfg) -> None: diff --git a/examples/nlp/language_modeling/tuning/megatron_gpt_adapter_tuning.py b/examples/nlp/language_modeling/tuning/megatron_gpt_adapter_tuning.py index 7aebad7e9010..15a7ed800ce4 100644 --- a/examples/nlp/language_modeling/tuning/megatron_gpt_adapter_tuning.py +++ b/examples/nlp/language_modeling/tuning/megatron_gpt_adapter_tuning.py @@ -29,6 +29,7 @@ ) from nemo.core.config import hydra_runner from nemo.utils import logging +from nemo.utils.decorators import deprecated from nemo.utils.exp_manager import exp_manager mp.set_start_method("spawn", force=True) @@ -56,6 +57,10 @@ """ +@deprecated( + explanation=f"{__file__} is deprecated. Please use MegatronGPTSFTModel.add_adapter() for PEFT features." + "See updated scripts `megatron_gpt_peft_tuning.py` and `megatron_gpt_peft_eval.py` for examples." +) @hydra_runner(config_path="conf", config_name="megatron_gpt_adapter_tuning_config") def main(cfg) -> None: logging.info("\n\n************** Experiment configuration ***********") diff --git a/examples/nlp/language_modeling/tuning/megatron_gpt_ia3_eval.py b/examples/nlp/language_modeling/tuning/megatron_gpt_ia3_eval.py index 5bf33b37ac72..2a6ca4b48049 100644 --- a/examples/nlp/language_modeling/tuning/megatron_gpt_ia3_eval.py +++ b/examples/nlp/language_modeling/tuning/megatron_gpt_ia3_eval.py @@ -26,6 +26,7 @@ from nemo.core.config import hydra_runner from nemo.utils import logging +from nemo.utils.decorators import deprecated mp.set_start_method("spawn", force=True) @@ -46,6 +47,10 @@ raise EnvironmentError("GPU is needed for the inference") +@deprecated( + explanation=f"{__file__} is deprecated. Please use MegatronGPTSFTModel.add_adapter() for PEFT features." + "See updated scripts `megatron_gpt_peft_tuning.py` and `megatron_gpt_peft_eval.py` for examples." +) @hydra_runner(config_path="conf", config_name="megatron_gpt_adapter_inference") def main(cfg) -> None: diff --git a/examples/nlp/language_modeling/tuning/megatron_gpt_ia3_tuning.py b/examples/nlp/language_modeling/tuning/megatron_gpt_ia3_tuning.py index b74d215b6ef7..b55739b08794 100644 --- a/examples/nlp/language_modeling/tuning/megatron_gpt_ia3_tuning.py +++ b/examples/nlp/language_modeling/tuning/megatron_gpt_ia3_tuning.py @@ -29,6 +29,7 @@ ) from nemo.core.config import hydra_runner from nemo.utils import logging +from nemo.utils.decorators import deprecated from nemo.utils.exp_manager import exp_manager mp.set_start_method("spawn", force=True) @@ -56,6 +57,10 @@ """ +@deprecated( + explanation=f"{__file__} is deprecated. Please use MegatronGPTSFTModel.add_adapter() for PEFT features." + "See updated scripts `megatron_gpt_peft_tuning.py` and `megatron_gpt_peft_eval.py` for examples." +) @hydra_runner(config_path="conf", config_name="megatron_gpt_ia3_tuning_config") def main(cfg) -> None: logging.info("\n\n************** Experiment configuration ***********") diff --git a/examples/nlp/language_modeling/tuning/megatron_gpt_peft_eval.py b/examples/nlp/language_modeling/tuning/megatron_gpt_peft_eval.py index 9edb44f5cb18..8098e62684ee 100644 --- a/examples/nlp/language_modeling/tuning/megatron_gpt_peft_eval.py +++ b/examples/nlp/language_modeling/tuning/megatron_gpt_peft_eval.py @@ -14,31 +14,18 @@ import asyncio -import json -import os import threading from functools import partial import torch import torch.multiprocessing as mp -from omegaconf.omegaconf import OmegaConf, open_dict -from pytorch_lightning import Trainer -from pytorch_lightning.plugins.environments import TorchElasticEnvironment -from torch.utils.data import DataLoader +from omegaconf.omegaconf import OmegaConf + -from nemo.collections.nlp.models.language_modeling.megatron_gpt_peft_models import MegatronGPTPEFTModel from nemo.collections.nlp.models.language_modeling.megatron_gpt_sft_model import MegatronGPTSFTModel -from nemo.collections.nlp.models.nlp_model import NLPModel from nemo.collections.nlp.modules.common.text_generation_server import MegatronServer from nemo.collections.nlp.modules.common.text_generation_utils import generate -from nemo.collections.nlp.parts.nlp_overrides import ( - GradScaler, - MegatronHalfPrecisionPlugin, - NLPDDPStrategy, - NLPSaveRestoreConnector, - PEFTSaveRestoreConnector, - PipelineMixedPrecisionPlugin, -) +from nemo.collections.nlp.parts.megatron_trainer_builder import MegatronLMPPTrainerBuilder from nemo.core.config import hydra_runner from nemo.utils import logging @@ -82,109 +69,64 @@ """ +def use_inference_server(cfg, model, trainer): + if not HAVE_MEGATRON_CORE: + raise ValueError('Megatron-core needs to be installed to use this feature!') + + from nemo.collections.nlp.modules.common.megatron_web_server import get_chatbot_demo, get_demo + + trainer.test(model, dataloaders=None) + + if parallel_state.is_pipeline_first_stage() and parallel_state.get_tensor_model_parallel_rank() == 0: + if cfg.web_server: + if cfg.chat: + defaults = { + 'user': cfg.chatbot_config.user, + 'assistant': cfg.chatbot_config.assistant, + 'system': cfg.chatbot_config.system, + } + web_ui = partial( + get_chatbot_demo, + defaults=defaults, + value=cfg.chatbot_config.value, + attributes=cfg.chatbot_config.attributes, + ) + else: + web_ui = get_demo + loop = asyncio.new_event_loop() + thread = threading.Thread( + target=web_ui, daemon=True, args=(cfg.share, cfg.username, cfg.password, cfg.port, cfg.web_port, loop), + ) + thread.start() + server = MegatronServer(model.cuda()) + server.run("0.0.0.0", port=cfg.port) + + while True: + choice = torch.cuda.LongTensor(1) + torch.distributed.broadcast(choice, 0) + if choice[0].item() == 0: + generate(model.cuda()) + + @hydra_runner(config_path="conf", config_name="megatron_gpt_peft_eval_config") def main(cfg) -> None: logging.info("\n\n************** Experiment configuration ***********") logging.info(f"\n{OmegaConf.to_yaml(cfg)}") - assert cfg.model.restore_from_path is not None - megatron_amp_o2 = cfg.model.get("megatron_amp_O2", False) - with_distributed_adam = False - - plugins = [] - strategy = NLPDDPStrategy( - no_ddp_communication_hook=True, # we don't use DDP for async grad allreduce - gradient_as_bucket_view=cfg.model.gradient_as_bucket_view, - find_unused_parameters=False, - ) - if cfg.trainer.precision in [16, '16', 'bf16', '16-mixed', 'bf16-mixed']: - scaler = None - if cfg.trainer.precision in [16, '16', '16-mixed']: - scaler = GradScaler( - init_scale=cfg.model.get("native_amp_init_scale", 2 ** 32), - growth_interval=cfg.model.get("native_amp_growth_interval", 1000), - hysteresis=cfg.model.get("hysteresis", 2), - enabled=False - if cfg.model.pipeline_model_parallel_size > 1 - else True, # turn off the grad scale for pipeline parallel LM model - ) - # MixedPrecisionPlugin in PTL >= 2.0 requires precision to be 16-mixed or bf16-mixed - plugin_precision = '16-mixed' - else: - plugin_precision = 'bf16-mixed' - if megatron_amp_o2 and not with_distributed_adam: - plugins.append(MegatronHalfPrecisionPlugin(precision=plugin_precision, device="cuda", scaler=scaler)) - else: - plugins.append(PipelineMixedPrecisionPlugin(precision=plugin_precision, device="cuda", scaler=scaler)) - - if cfg.get("cluster_type", None) == "BCP": - plugins.append(TorchElasticEnvironment()) - - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer) - if cfg.model.peft.restore_from_path: - if cfg.model.peft.restore_from_path.endswith(".nemo"): - peft_model_cfg = MegatronGPTPEFTModel.restore_from( - restore_path=cfg.model.peft.restore_from_path, trainer=trainer, return_config=True, - ) - elif cfg.model.peft.restore_from_hparams_path: # not a .nemo model we expect a hparams.yaml file - peft_model_cfg = OmegaConf.to_container(OmegaConf.load(cfg.model.peft.restore_from_hparams_path).cfg) - peft_model_cfg = OmegaConf.create(peft_model_cfg) - # extract dict inside cfg key and convert it to DictConfig - # this allows interpolation to work the same way as config from the .restore_from method - else: - raise RuntimeError("This script requires a .nemo peft model or path to hparams.yaml (and a ckpt path).") - else: - peft_model_cfg = MegatronGPTSFTModel.restore_from( - restore_path=cfg.model.restore_from_path, trainer=trainer, return_config=True, - ) - - # hydra interpolation does not work here as the interpolation key is lost when PTL saves hparams - with open_dict(peft_model_cfg): - # update the model config of the trained model with params we want to set at inference time. - peft_model_cfg.precision = cfg.trainer.precision - peft_model_cfg.data.test_ds = cfg.model.data.test_ds - peft_model_cfg.activations_checkpoint_granularity = None - peft_model_cfg.activations_checkpoint_method = None - peft_model_cfg.activations_checkpoint_layers_per_pipeline = None - if peft_model_cfg.get("use_flash_attention", False): - peft_model_cfg.use_flash_attention = cfg.model.use_flash_attention - if cfg.model.get("seq_len_interpolation_factor", None) is not None: - peft_model_cfg["seq_len_interpolation_factor"] = cfg.model.seq_len_interpolation_factor - - with open_dict(cfg): - # update the config with the trained model config - # required for hydra interpolation to work inside cfg.inference - cfg.inference.add_BOS = peft_model_cfg.data.test_ds.add_bos - cfg.inference.tokens_to_generate = peft_model_cfg.data.test_ds.tokens_to_generate + trainer = MegatronLMPPTrainerBuilder(cfg).create_trainer() if cfg.model.peft.restore_from_path: - if cfg.model.peft.restore_from_path.endswith(".nemo"): - save_restore_connector = PEFTSaveRestoreConnector( - peft_model_nemo_path=cfg.model.peft.restore_from_path, peft_model_ckpt_path=None, - ) - else: - # attempting to load a ckpt peft model. - if cfg.model.peft.restore_from_ckpt_name: - ckpt_name = cfg.model.peft.restore_from_ckpt_name - else: - ckpt_name = "model_weights.ckpt" - save_restore_connector = PEFTSaveRestoreConnector( - peft_model_nemo_path=None, - peft_model_ckpt_path=cfg.model.peft.restore_from_path, - peft_model_ckpt_name=ckpt_name, - ) + model_cfg = MegatronGPTSFTModel.merge_inference_cfg(cfg.model.peft.restore_from_path, cfg) else: - save_restore_connector = NLPSaveRestoreConnector() + model_cfg = MegatronGPTSFTModel.merge_inference_cfg(cfg.model.restore_from_path, cfg) + + model = MegatronGPTSFTModel.restore_from(cfg.model.restore_from_path, model_cfg, trainer=trainer) - if os.path.isdir(cfg.model.restore_from_path): - save_restore_connector.model_extracted_dir = cfg.model.restore_from_path - model = MegatronGPTSFTModel.restore_from( - restore_path=cfg.model.restore_from_path, - trainer=trainer, - override_config_path=peft_model_cfg, - save_restore_connector=save_restore_connector, - ) + if cfg.model.peft.restore_from_path: + model.load_adapters(cfg.model.peft.restore_from_path) model.freeze() + logging.info(f"Freezing parameters for PEFT eval:\n{model.summarize()}") + if not cfg.model.get('use_flash_attention', False): cfg.inference.compute_attention_mask = True config = OmegaConf.to_container(cfg.inference, resolve=True) @@ -193,44 +135,7 @@ def main(cfg) -> None: if not cfg.server: trainer.test(model) else: - if not HAVE_MEGATRON_CORE: - raise ValueError('Megatron-core needs to be installed to use this feature!') - - from nemo.collections.nlp.modules.common.megatron_web_server import get_chatbot_demo, get_demo - - trainer.test(model, dataloaders=None) - - if parallel_state.is_pipeline_first_stage() and parallel_state.get_tensor_model_parallel_rank() == 0: - if cfg.web_server: - if cfg.chat: - defaults = { - 'user': cfg.chatbot_config.user, - 'assistant': cfg.chatbot_config.assistant, - 'system': cfg.chatbot_config.system, - } - web_ui = partial( - get_chatbot_demo, - defaults=defaults, - value=cfg.chatbot_config.value, - attributes=cfg.chatbot_config.attributes, - ) - else: - web_ui = get_demo - loop = asyncio.new_event_loop() - thread = threading.Thread( - target=web_ui, - daemon=True, - args=(cfg.share, cfg.username, cfg.password, cfg.port, cfg.web_port, loop), - ) - thread.start() - server = MegatronServer(model.cuda()) - server.run("0.0.0.0", port=cfg.port) - - while True: - choice = torch.cuda.LongTensor(1) - torch.distributed.broadcast(choice, 0) - if choice[0].item() == 0: - generate(model.cuda()) + use_inference_server(cfg, model, trainer) if __name__ == "__main__": diff --git a/examples/nlp/language_modeling/tuning/megatron_gpt_peft_tuning.py b/examples/nlp/language_modeling/tuning/megatron_gpt_peft_tuning.py index 2c9293b2600e..cffe974a071a 100644 --- a/examples/nlp/language_modeling/tuning/megatron_gpt_peft_tuning.py +++ b/examples/nlp/language_modeling/tuning/megatron_gpt_peft_tuning.py @@ -12,46 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. - -import os -import tempfile - import torch.multiprocessing as mp -from omegaconf.omegaconf import OmegaConf, open_dict -from pytorch_lightning import Trainer -from pytorch_lightning.plugins.environments import TorchElasticEnvironment -from pytorch_lightning.trainer.connectors.checkpoint_connector import _CheckpointConnector -from torch.utils.data import DataLoader, Dataset +from omegaconf.omegaconf import OmegaConf + +from nemo.collections.nlp.models.language_modeling.megatron_gpt_sft_model import MegatronGPTSFTModel +from nemo.collections.nlp.parts.megatron_trainer_builder import MegatronLMPPTrainerBuilder +from nemo.collections.nlp.parts.peft_config import PEFT_CONFIG_MAP -from nemo.collections.nlp.models.language_modeling.megatron_gpt_peft_models import ( - MegatronGPTAdapterModel, - MegatronGPTAdapterModelWeightTying, - MegatronGPTAdapterPTuningModel, - MegatronGPTIA3Model, - MegatronGPTLoRAModel, - MegatronGPTLoRAModelWeightTying, - MegatronGPTPTuningModel, -) -from nemo.collections.nlp.models.language_modeling.megatron_gpt_sft_model import MegatronGPTModel -from nemo.collections.nlp.modules.common.megatron.megatron_init import fake_initialize_model_parallel -from nemo.collections.nlp.parts.nlp_overrides import ( - CustomProgressBar, - GradScaler, - MegatronHalfPrecisionPlugin, - NLPDDPStrategy, - NLPSaveRestoreConnector, - PEFTSaveRestoreConnector, - PipelineMixedPrecisionPlugin, -) from nemo.core.config import hydra_runner -from nemo.utils import AppState, logging +from nemo.utils import logging from nemo.utils.exp_manager import exp_manager -from nemo.utils.model_utils import inject_model_parallel_rank mp.set_start_method("spawn", force=True) """ -This is the script to train an Adapter infused GPT Model for text generation. +This is the script to finetuning a GPT Model with any PEFT method. A base GPT Model is required as a starting point. This script will then insert Adapters into each Transformer layer and will train/update only these adapters during training. The base GPT Model weights will remain frozen. @@ -63,189 +38,41 @@ Usage: Assuming the base model is a 125m GPT Model, with TP=1, PP=1: a. run a training run for a base gpt nemo file: - python megatron_gpt_adapter_tuning.py \ - "model.data.train_ds=[PATH TO TRAINING JSONL FILE]", - "model.data.validation_ds=[PATH TO VALIDATION JSONL FILE]", - model.language_model_path="PATH TO BASE GPT MODEL .nemo FILE" + python megatron_gpt_peft_tuning.py \ + "model.data.train_ds.file_names=[PATH TO TRAINING JSONL FILE]", + "model.data.train_ds.concat_sampling_probabilities=[SAMPLING VAL]", + "model.data.validation_ds.file_names=[PATH TO VALIDATION JSONL FILE]", + "model.data.validation_ds.names=[NAME FOR METRIC LOGGING]", + model.restore_from_path="PATH TO BASE GPT MODEL .nemo FILE" + model.peft.peft_scheme='lora' # lora, ptuning, adapter, ia3, or none for full fineutning name="NAME OF TRAINING RUN" exp_manager.exp_dir="DIR TO SAVE CHECKPOINTS and .nemo FILE", - trainer.max_epochs=2 +Please see lora.ipynb for a step-by-step guide. """ -def _modify_config(gpt_cfg, cfg, add_cfg_to_tree=False): - """ - This function modifies the original gpt pre-training config (gpt_cfg) with attributes from the finetuning config (cfg). - The `add_cfg_to_tree` arg adds `cfg` to the top of the yaml tree which is needed for all `hparams.yaml` files when passed as an arg to `load_from_checkpoint()`. - """ - OmegaConf.set_struct(gpt_cfg, True) - OmegaConf.resolve(cfg) - with open_dict(gpt_cfg): - gpt_cfg.megatron_amp_O2 = cfg.model.get('megatron_amp_O2', False) - gpt_cfg.micro_batch_size = cfg.model.data.train_ds.micro_batch_size - gpt_cfg.global_batch_size = cfg.model.data.train_ds.global_batch_size - gpt_cfg.sequence_parallel = cfg.model.get("sequence_parallel", False) - gpt_cfg.activations_checkpoint_granularity = cfg.model.get("activations_checkpoint_granularity", None) - gpt_cfg.activations_checkpoint_num_layers = cfg.model.get("activations_checkpoint_num_layers", None) - gpt_cfg.activations_checkpoint_method = cfg.model.get("activations_checkpoint_method", None) - gpt_cfg.activations_checkpoint_layers_per_pipeline = cfg.model.get( - "activations_checkpoint_layers_per_pipeline", None - ) - gpt_cfg.data = cfg.model.data - gpt_cfg.optim = cfg.model.optim - gpt_cfg.precision = cfg.trainer.precision - gpt_cfg.answer_only_loss = cfg.model.answer_only_loss - gpt_cfg.restore_from_path = cfg.model.restore_from_path - gpt_cfg.resume_from_checkpoint = cfg.model.resume_from_checkpoint - gpt_cfg.save_nemo_on_validation_end = cfg.model.save_nemo_on_validation_end - gpt_cfg.gradient_as_bucket_view = cfg.model.gradient_as_bucket_view - gpt_cfg.hidden_dropout = cfg.model.get('hidden_dropout', 0.0) - gpt_cfg.attention_dropout = cfg.model.get('attention_dropout', 0.0) - gpt_cfg.ffn_dropout = cfg.model.ffn_dropout - gpt_cfg.peft = cfg.model.peft - peft_cls = _get_peft_scheme(cfg.model) - gpt_cfg.target = f"{peft_cls.__module__}.{peft_cls.__name__}" - - # This is needed when modifying a hparam file directly to load `.ckpt` files. - # This is not needed to modify the cfg in `.nemo` files. - if add_cfg_to_tree: - OmegaConf.resolve(gpt_cfg) - gpt_cfg.cfg = gpt_cfg - - return gpt_cfg - - -def _get_peft_scheme(cfg): - if cfg.peft.peft_scheme == "adapter": - if cfg.peft.adapter_tuning.weight_tying: - peft_cls = MegatronGPTAdapterModelWeightTying - else: - peft_cls = MegatronGPTAdapterModel - elif cfg.peft.peft_scheme == "ia3": - peft_cls = MegatronGPTIA3Model - elif cfg.peft.peft_scheme == "ptuning": - peft_cls = MegatronGPTPTuningModel - elif cfg.peft.peft_scheme == "adapter_and_ptuning": - peft_cls = MegatronGPTAdapterPTuningModel - elif cfg.peft.peft_scheme == "lora": - if cfg.peft.lora_tuning.weight_tying: - peft_cls = MegatronGPTLoRAModelWeightTying - else: - peft_cls = MegatronGPTLoRAModel - else: - raise RuntimeError("Invalid Peft scheme") - return peft_cls - - -def load_from_checkpoint_dir(cls, cfg, trainer, modify_confg_fn): - app_state = AppState() - if cfg.model.tensor_model_parallel_size > 1 or cfg.model.pipeline_model_parallel_size > 1: - app_state.model_parallel_size = cfg.model.tensor_model_parallel_size * cfg.model.pipeline_model_parallel_size - app_state.tensor_model_parallel_size = cfg.model.tensor_model_parallel_size - app_state.pipeline_model_parallel_size = cfg.model.pipeline_model_parallel_size - ( - app_state.tensor_model_parallel_rank, - app_state.pipeline_model_parallel_rank, - app_state.model_parallel_size, - app_state.data_parallel_size, - app_state.pipeline_model_parallel_split_rank, - app_state.virtual_pipeline_model_parallel_rank, - ) = fake_initialize_model_parallel( - world_size=app_state.model_parallel_size, - rank=trainer.global_rank, - tensor_model_parallel_size_=cfg.model.tensor_model_parallel_size, - pipeline_model_parallel_size_=cfg.model.pipeline_model_parallel_size, - pipeline_model_parallel_split_rank_=cfg.model.pipeline_model_parallel_split_rank, - ) - checkpoint_path = inject_model_parallel_rank( - os.path.join(cfg.model.pretrained_checkpoint.checkpoint_dir, cfg.model.pretrained_checkpoint.checkpoint_name) - ) - hparams_file = OmegaConf.load(cfg.model.pretrained_checkpoint.hparams_file) - gpt_cfg = modify_confg_fn(hparams_file.cfg, cfg, add_cfg_to_tree=True) - with tempfile.NamedTemporaryFile(suffix='.yaml') as f: - OmegaConf.save(config=gpt_cfg, f=f.name) - model = cls.load_from_checkpoint(checkpoint_path=checkpoint_path, trainer=trainer, hparams_file=f.name,) - return model - - -def validate_checkpoint_loading_args(cfg): - if cfg.checkpoint_dir is None or not os.path.isdir(cfg.checkpoint_dir): - raise ValueError(f'Checkpoint directory {cfg.checkpoint_dir} does not exist or is not a directory.') - if cfg.checkpoint_name is None: - raise ValueError(f'Checkpoint name {cfg.checkpoint_name} is not valid.') - if cfg.hparams_file is None or not os.path.isfile(cfg.hparams_file): - raise ValueError(f'Hparams file {cfg.hparams_file} does not exist or is not a file.') - - @hydra_runner(config_path="conf", config_name="megatron_gpt_peft_tuning_config") def main(cfg) -> None: logging.info("\n\n************** Experiment configuration ***********") logging.info(f'\n{OmegaConf.to_yaml(cfg)}') - megatron_amp_o2 = cfg.model.get('megatron_amp_O2', False) - with_distributed_adam = cfg.model.optim.get('name') == 'distributed_fused_adam' - - plugins = [] - strategy = NLPDDPStrategy( - no_ddp_communication_hook=True, # we don't use DDP for async grad allreduce - gradient_as_bucket_view=cfg.model.gradient_as_bucket_view, - find_unused_parameters=False, - ) - if cfg.trainer.precision in [16, '16', 'bf16', '16-mixed', 'bf16-mixed']: - scaler = None - if cfg.trainer.precision in [16, '16', '16-mixed']: - scaler = GradScaler( - init_scale=cfg.model.get('native_amp_init_scale', 2 ** 32), - growth_interval=cfg.model.get('native_amp_growth_interval', 1000), - hysteresis=cfg.model.get('hysteresis', 2), - enabled=False - if cfg.model.pipeline_model_parallel_size > 1 - else True, # turn off the grad scale for pipeline parallel LM model - ) - # MixedPrecisionPlugin in PTL >= 2.0 requires precision to be 16-mixed or bf16-mixed - plugin_precision = '16-mixed' - else: - plugin_precision = 'bf16-mixed' - if megatron_amp_o2 and not with_distributed_adam: - plugins.append(MegatronHalfPrecisionPlugin(precision=plugin_precision, device='cuda', scaler=scaler)) - else: - plugins.append(PipelineMixedPrecisionPlugin(precision=plugin_precision, device='cuda', scaler=scaler)) - - if cfg.get('cluster_type', None) == 'BCP': - plugins.append(TorchElasticEnvironment()) - - trainer = Trainer(plugins=plugins, strategy=strategy, **cfg.trainer, callbacks=[CustomProgressBar()]) + trainer = MegatronLMPPTrainerBuilder(cfg).create_trainer() exp_manager(trainer, cfg.exp_manager) - # update resume from checkpoint found by exp_manager - if cfg.model.resume_from_checkpoint is not None: - trainer.ckpt_path = cfg.model.resume_from_checkpoint - logging.info(f'Resuming training from checkpoint: {trainer.ckpt_path}') - if cfg.model.restore_from_path: - base_model_save_restore_connector = NLPSaveRestoreConnector() - if os.path.isdir(cfg.model.restore_from_path): - base_model_save_restore_connector.model_extracted_dir = cfg.model.restore_from_path - base_model_cfg = MegatronGPTModel.restore_from( - restore_path=cfg.model.restore_from_path, - trainer=trainer, - return_config=True, - save_restore_connector=base_model_save_restore_connector, - ) - base_model_cfg = _modify_config(base_model_cfg, cfg, add_cfg_to_tree=False) - save_restore_connector = PEFTSaveRestoreConnector( - peft_model_nemo_path=cfg.model.peft.restore_from_path, peft_model_ckpt_path=trainer.ckpt_path - ) - if os.path.isdir(cfg.model.restore_from_path): - save_restore_connector.model_extracted_dir = cfg.model.restore_from_path - peft_cls = _get_peft_scheme(cfg.model) - model = peft_cls.restore_from( - restore_path=cfg.model.restore_from_path, - trainer=trainer, - override_config_path=base_model_cfg, - save_restore_connector=save_restore_connector, - ) + model_cfg = MegatronGPTSFTModel.merge_cfg_with(cfg.model.restore_from_path, cfg) + model = MegatronGPTSFTModel.restore_from(cfg.model.restore_from_path, model_cfg, trainer=trainer) + peft_cfg_cls = PEFT_CONFIG_MAP[cfg.model.peft.peft_scheme] + + if cfg.model.peft.restore_from_path is not None: + # initialize peft weights from a checkpoint instead of randomly + # This is not the same as resume training because optimizer states are not restored. + logging.info("PEFT Weights will be loaded from", cfg.model.peft.restore_from_path) + model.load_adapters(cfg.model.peft.restore_from_path, peft_cfg_cls(model_cfg)) + elif peft_cfg_cls is not None: + logging.info("Adding adapter weights to the model for PEFT") + model.add_adapter(peft_cfg_cls(model_cfg)) else: - raise RuntimeError("PEFT training needs a trained base model present.") + logging.info(f"Running full finetuning since no peft scheme is given.\n{model.summarize()}") trainer.fit(model) diff --git a/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py b/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py index 2d0a0d38a762..870ff2921cce 100644 --- a/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py +++ b/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py @@ -32,6 +32,7 @@ ) from nemo.core.config import hydra_runner from nemo.utils import AppState, logging +from nemo.utils.decorators import deprecated from nemo.utils.exp_manager import exp_manager from nemo.utils.model_utils import inject_model_parallel_rank @@ -145,6 +146,10 @@ def validate_checkpoint_loading_args(cfg): raise ValueError(f'Hparams file {cfg.hparams_file} does not exist or is not a file.') +@deprecated( + explanation=f"{__file__} is deprecated. PEFT and SFT scripts are now consolidated" + "See updated scripts `megatron_gpt_peft_tuning.py` and `megatron_gpt_peft_eval.py` for examples." +) @hydra_runner(config_path="conf", config_name="megatron_gpt_sft") def main(cfg) -> None: logging.info("\n\n************** Experiment configuration ***********") diff --git a/examples/nlp/language_modeling/tuning/megatron_t5_adapter_eval.py b/examples/nlp/language_modeling/tuning/megatron_t5_adapter_eval.py index bdeec2b5a9c1..5fd07e85ce2d 100644 --- a/examples/nlp/language_modeling/tuning/megatron_t5_adapter_eval.py +++ b/examples/nlp/language_modeling/tuning/megatron_t5_adapter_eval.py @@ -25,6 +25,7 @@ from nemo.collections.nlp.parts.nlp_overrides import NLPDDPStrategy from nemo.core.config import hydra_runner from nemo.utils.app_state import AppState +from nemo.utils.decorators import deprecated mp.set_start_method("spawn", force=True) @@ -45,6 +46,10 @@ raise EnvironmentError("GPU is needed for the inference") +@deprecated( + explanation=f"{__file__} is deprecated. Please use MegatronT5SFTModel.add_adapter() for PEFT features." + "See updated scripts `megatron_t5_peft_tuning.py` and `megatron_t5_peft_eval.py` for examples." +) @hydra_runner(config_path="conf", config_name="megatron_t5_adapter_inference") def main(cfg) -> None: diff --git a/examples/nlp/language_modeling/tuning/megatron_t5_adapter_tuning.py b/examples/nlp/language_modeling/tuning/megatron_t5_adapter_tuning.py index c096a75d0b9d..ebc6a0327b78 100644 --- a/examples/nlp/language_modeling/tuning/megatron_t5_adapter_tuning.py +++ b/examples/nlp/language_modeling/tuning/megatron_t5_adapter_tuning.py @@ -29,6 +29,7 @@ ) from nemo.core.config import hydra_runner from nemo.utils import logging +from nemo.utils.decorators import deprecated from nemo.utils.exp_manager import exp_manager mp.set_start_method("spawn", force=True) @@ -56,6 +57,10 @@ """ +@deprecated( + explanation=f"{__file__} is deprecated. Please use MegatronT5SFTModel.add_adapter() for PEFT features." + "See updated scripts `megatron_t5_peft_tuning.py` and `megatron_t5_peft_eval.py` for examples." +) @hydra_runner(config_path="conf", config_name="megatron_t5_adapter_tuning_config") def main(cfg) -> None: logging.info("\n\n************** Experiment configuration ***********") diff --git a/examples/nlp/language_modeling/tuning/megatron_t5_ia3_eval.py b/examples/nlp/language_modeling/tuning/megatron_t5_ia3_eval.py index 8a8ddae166e1..cc9dfef059b8 100644 --- a/examples/nlp/language_modeling/tuning/megatron_t5_ia3_eval.py +++ b/examples/nlp/language_modeling/tuning/megatron_t5_ia3_eval.py @@ -25,6 +25,7 @@ from nemo.collections.nlp.parts.nlp_overrides import NLPDDPStrategy from nemo.core.config import hydra_runner from nemo.utils.app_state import AppState +from nemo.utils.decorators import deprecated mp.set_start_method("spawn", force=True) @@ -45,6 +46,10 @@ raise EnvironmentError("GPU is needed for the inference") +@deprecated( + explanation=f"{__file__} is deprecated. Please use MegatronT5SFTModel.add_adapter() for PEFT features." + "See updated scripts `megatron_t5_peft_tuning.py` and `megatron_t5_peft_eval.py` for examples." +) @hydra_runner(config_path="conf", config_name="megatron_t5_ia3_inference") def main(cfg) -> None: diff --git a/examples/nlp/language_modeling/tuning/megatron_t5_ia3_tuning.py b/examples/nlp/language_modeling/tuning/megatron_t5_ia3_tuning.py index 46b590acd725..fc508611c9b1 100644 --- a/examples/nlp/language_modeling/tuning/megatron_t5_ia3_tuning.py +++ b/examples/nlp/language_modeling/tuning/megatron_t5_ia3_tuning.py @@ -29,6 +29,7 @@ ) from nemo.core.config import hydra_runner from nemo.utils import logging +from nemo.utils.decorators import deprecated from nemo.utils.exp_manager import exp_manager mp.set_start_method("spawn", force=True) @@ -56,6 +57,10 @@ """ +@deprecated( + explanation=f"{__file__} is deprecated. Please use MegatronT5SFTModel.add_adapter() for PEFT features." + "See updated scripts `megatron_t5_peft_tuning.py` and `megatron_t5_peft_eval.py` for examples." +) @hydra_runner(config_path="conf", config_name="megatron_t5_ia3_tuning_config") def main(cfg) -> None: logging.info("\n\n************** Experiment configuration ***********") diff --git a/examples/nlp/language_modeling/tuning/megatron_t5_lora_eval.py b/examples/nlp/language_modeling/tuning/megatron_t5_lora_eval.py index d9de94843071..38032d06a8c8 100644 --- a/examples/nlp/language_modeling/tuning/megatron_t5_lora_eval.py +++ b/examples/nlp/language_modeling/tuning/megatron_t5_lora_eval.py @@ -25,6 +25,7 @@ from nemo.collections.nlp.parts.nlp_overrides import NLPDDPStrategy from nemo.core.config import hydra_runner from nemo.utils.app_state import AppState +from nemo.utils.decorators import deprecated mp.set_start_method("spawn", force=True) @@ -45,6 +46,10 @@ raise EnvironmentError("GPU is needed for the inference") +@deprecated( + explanation=f"{__file__} is deprecated. Please use MegatronT5SFTModel.add_adapter() for PEFT features." + "See updated scripts `megatron_t5_peft_tuning.py` and `megatron_t5_peft_eval.py` for examples." +) @hydra_runner(config_path="conf", config_name="megatron_t5_adapter_inference") def main(cfg) -> None: diff --git a/examples/nlp/language_modeling/tuning/megatron_t5_lora_tuning.py b/examples/nlp/language_modeling/tuning/megatron_t5_lora_tuning.py index fb982a273662..73cfae76b7a5 100644 --- a/examples/nlp/language_modeling/tuning/megatron_t5_lora_tuning.py +++ b/examples/nlp/language_modeling/tuning/megatron_t5_lora_tuning.py @@ -29,6 +29,7 @@ ) from nemo.core.config import hydra_runner from nemo.utils import logging +from nemo.utils.decorators import deprecated from nemo.utils.exp_manager import exp_manager mp.set_start_method("spawn", force=True) @@ -56,6 +57,10 @@ """ +@deprecated( + explanation=f"{__file__} is deprecated. Please use MegatronT5SFTModel.add_adapter() for PEFT features." + "See updated scripts `megatron_t5_peft_tuning.py` and `megatron_t5_peft_eval.py` for examples." +) @hydra_runner(config_path="conf", config_name="megatron_t5_lora_tuning_config") def main(cfg) -> None: logging.info("\n\n************** Experiment configuration ***********") diff --git a/examples/nlp/language_modeling/tuning/megatron_t5_peft_eval.py b/examples/nlp/language_modeling/tuning/megatron_t5_peft_eval.py new file mode 100644 index 000000000000..d9d4d075362b --- /dev/null +++ b/examples/nlp/language_modeling/tuning/megatron_t5_peft_eval.py @@ -0,0 +1,135 @@ +# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import asyncio +import threading +from functools import partial + +import torch +import torch.multiprocessing as mp +from omegaconf.omegaconf import OmegaConf + + +from nemo.collections.nlp.models.language_modeling.megatron_t5_sft_model import MegatronT5SFTModel +from nemo.collections.nlp.modules.common.text_generation_server import MegatronServer +from nemo.collections.nlp.modules.common.text_generation_utils import generate +from nemo.collections.nlp.parts.megatron_trainer_builder import MegatronLMPPTrainerBuilder +from nemo.core.config import hydra_runner +from nemo.utils import logging + +try: + from megatron.core import parallel_state + + HAVE_MEGATRON_CORE = True +except (ImportError, ModuleNotFoundError): + HAVE_MEGATRON_CORE = False + +mp.set_start_method("spawn", force=True) +""" +This is the script to run inference with a PEFT model or an SFT Model. + +If you want to evaluate an SFT .nemo file: + +python examples/nlp/language_modeling/tuning/megatron_t5_peft_eval.py \ + model.restore_from_path= \ + model.peft.restore_from_path=null \ + trainer.devices=1 model.data.test_ds.file_names=\[, ] \ + model.data.test_ds.names=\['name_for_test_file1', 'name_for_test_file2'] \ # this is not the filename just some identifier + model.data.test_ds.global_batch_size=4 \ # or some other value + model.data.test_ds.micro_batch_size=4 \ + model.data.test_ds.tokens_to_generate=30 \ + inference.greedy=True \ + inference.outfile_path=\'' + +If you want to evaluate a PEFT Model, you should provide a base T5 model and a PEFT model .nemo file + +python examples/nlp/language_modeling/tuning/megatron_t5_peft_eval.py \ + model.restore_from_path= \ + model.peft.restore_from_path= \ # this will be created if you use `megatron_t5_peft_tuning.py` + trainer.devices=1 model.data.test_ds.file_names=\[, ] \ + model.data.test_ds.names=\['name_for_test_file1', 'name_for_test_file2'] \ # this is not the filename just some identifier + model.data.test_ds.global_batch_size=4 \ # or some other value + model.data.test_ds.micro_batch_size=4 \ + model.data.test_ds.tokens_to_generate=30 \ + inference.greedy=True \ + inference.outfile_path=\'' + +""" + + +def use_inference_server(cfg, model, trainer): + if not HAVE_MEGATRON_CORE: + raise ValueError('Megatron-core needs to be installed to use this feature!') + + from nemo.collections.nlp.modules.common.megatron_web_server import get_chatbot_demo, get_demo + + trainer.test(model, dataloaders=None) + + if parallel_state.is_pipeline_first_stage() and parallel_state.get_tensor_model_parallel_rank() == 0: + if cfg.web_server: + if cfg.chat: + defaults = { + 'user': cfg.chatbot_config.user, + 'assistant': cfg.chatbot_config.assistant, + 'system': cfg.chatbot_config.system, + } + web_ui = partial( + get_chatbot_demo, + defaults=defaults, + value=cfg.chatbot_config.value, + attributes=cfg.chatbot_config.attributes, + ) + else: + web_ui = get_demo + loop = asyncio.new_event_loop() + thread = threading.Thread( + target=web_ui, daemon=True, args=(cfg.share, cfg.username, cfg.password, cfg.port, cfg.web_port, loop), + ) + thread.start() + server = MegatronServer(model.cuda()) + server.run("0.0.0.0", port=cfg.port) + + while True: + choice = torch.cuda.LongTensor(1) + torch.distributed.broadcast(choice, 0) + if choice[0].item() == 0: + generate(model.cuda()) + + +@hydra_runner(config_path="conf", config_name="megatron_t5_peft_eval_config") +def main(cfg) -> None: + logging.info("\n\n************** Experiment configuration ***********") + logging.info(f"\n{OmegaConf.to_yaml(cfg)}") + trainer = MegatronLMPPTrainerBuilder(cfg).create_trainer() + + model_cfg = MegatronT5SFTModel.merge_inference_cfg(cfg.model.peft.restore_from_path, cfg) + model = MegatronT5SFTModel.restore_from(cfg.model.restore_from_path, model_cfg, trainer=trainer) + + model.load_adapters(cfg.model.peft.restore_from_path) + + model.freeze() + logging.info(f"Freezing parameters for PEFT eval:\n{model.summarize()}") + + if not cfg.model.get('use_flash_attention', False): + cfg.inference.compute_attention_mask = True + + if not cfg.server: + trainer.test(model) + else: + use_inference_server(cfg, model, trainer) + + +if __name__ == "__main__": + main() diff --git a/examples/nlp/language_modeling/tuning/megatron_t5_peft_tuning.py b/examples/nlp/language_modeling/tuning/megatron_t5_peft_tuning.py new file mode 100644 index 000000000000..04e25956aed2 --- /dev/null +++ b/examples/nlp/language_modeling/tuning/megatron_t5_peft_tuning.py @@ -0,0 +1,65 @@ +# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import torch.multiprocessing as mp +from omegaconf.omegaconf import OmegaConf + +from nemo.collections.nlp.models.language_modeling.megatron_t5_sft_model import MegatronT5SFTModel +from nemo.collections.nlp.parts.megatron_trainer_builder import MegatronLMPPTrainerBuilder +from nemo.collections.nlp.parts.peft_config import PEFT_CONFIG_MAP +from nemo.core.config import hydra_runner +from nemo.utils import logging +from nemo.utils.exp_manager import exp_manager + +mp.set_start_method("spawn", force=True) + +""" +This is the script to finetuning a T5 Model with any PEFT method. +A base T5 Model is required as a starting point. This script will then insert +Adapters into each Transformer layer and will train/update only these adapters +during training. The base T5 Model weights will remain frozen. + +This script is exactly the same as the peft tuning script for GPT. For more details +please refer to the GPT script and docs. +""" + + +@hydra_runner(config_path="conf", config_name="megatron_t5_peft_tuning_config") +def main(cfg) -> None: + logging.info("\n\n************** Experiment configuration ***********") + logging.info(f'\n{OmegaConf.to_yaml(cfg)}') + + trainer = MegatronLMPPTrainerBuilder(cfg).create_trainer() + exp_manager(trainer, cfg.exp_manager) + + model_cfg = MegatronT5SFTModel.merge_cfg_with(cfg.model.restore_from_path, cfg) + model = MegatronT5SFTModel.restore_from(cfg.model.restore_from_path, model_cfg, trainer=trainer) + peft_cfg_cls = PEFT_CONFIG_MAP[cfg.model.peft.peft_scheme] + + if cfg.model.peft.restore_from_path is not None: + # initialize peft weights from a checkpoint instead of randomly + # This is not the same as resume training because optimizer states are not restored. + logging.info("PEFT Weights will be loaded from", cfg.model.peft.restore_from_path) + model.load_adapters(cfg.model.peft.restore_from_path, peft_cfg_cls(model_cfg)) + elif peft_cfg_cls is not None: + logging.info("Adding adapter weights to the model for PEFT") + model.add_adapter(peft_cfg_cls(model_cfg)) + else: + logging.info(f"Running full finetuning since no peft scheme is given.\n{model.summarize()}") + + trainer.fit(model) + + +if __name__ == '__main__': + main() diff --git a/nemo/collections/nlp/data/language_modeling/megatron/t5_sft_dataset.py b/nemo/collections/nlp/data/language_modeling/megatron/t5_sft_dataset.py new file mode 100644 index 000000000000..7e60fdd11f80 --- /dev/null +++ b/nemo/collections/nlp/data/language_modeling/megatron/t5_sft_dataset.py @@ -0,0 +1,169 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import json +import os +from typing import Optional + +import numpy as np +import torch +from datasets import load_dataset + +from nemo.collections.common.tokenizers.tokenizer_spec import TokenizerSpec +from nemo.collections.nlp.data.language_modeling.text_memmap_dataset import JSONLMemMapDataset +from nemo.core.classes import Dataset +from nemo.utils import logging + +__all__ = ['T5SFTDataset'] + + +class T5SFTDataset(Dataset): + """Sequence to Sequence Dataset in memory. + Similar to SequenceToSequenceDataset but with the same input format as GPTSFTDataset + """ + + def __init__( + self, + file_path: str, + src_tokenizer: TokenizerSpec, + tgt_tokenizer: TokenizerSpec, + max_src_seq_length: int, + max_tgt_seq_length: int, + add_bos_to_input: bool = True, + add_eos_to_input: bool = True, + replace_bos_with_pad: bool = False, + index_mapping_dir: str = None, + memmap_workers: Optional[int] = None, + hf_dataset: bool = False, + ): + """ + index_mapping_dir: Directory to save the index mapping to. If None, will write to the same folder as the dataset. + hf_dataset: Whether to load the json file with the HuggingFace dataset. otherwise, will load the jsonl file with the JSONLMemMapDataset. + """ + super().__init__() + self.file_path = file_path + self.src_tokenizer = src_tokenizer + self.tgt_tokenizer = tgt_tokenizer + self.max_src_seq_length = max_src_seq_length + self.max_tgt_seq_length = max_tgt_seq_length + self.add_bos_to_input = add_bos_to_input + self.add_eos_to_input = add_eos_to_input + self.replace_bos_with_pad = replace_bos_with_pad + assert self.max_src_seq_length > 0 + assert self.max_tgt_seq_length > 0 + + # check file exists + if not os.path.exists(self.file_path): + raise FileNotFoundError(f"Data file {self.file_path} not found") + + if hf_dataset: + self.indexed_dataset = load_dataset( + 'json', data_files=file_path, cache_dir=index_mapping_dir, num_proc=memmap_workers, split='train' + ) + else: + self.indexed_dataset = JSONLMemMapDataset( + dataset_paths=[file_path], + tokenizer=None, + header_lines=0, + index_mapping_dir=index_mapping_dir, + workers=memmap_workers, + ) + + def _process_src(self, src): + src = self.src_tokenizer.text_to_ids(src.strip()) + if self.add_bos_to_input: + src = [self.src_tokenizer.pad_id if self.replace_bos_with_pad else self.src_tokenizer.bos_id] + src + if self.add_eos_to_input: + src = src + [self.src_tokenizer.eos_id] + if len(src) > self.max_src_seq_length: + src = src[-self.max_src_seq_length + 1 :] + return src + + def _process_tgt(self, tgt): + tgt = ( + [self.tgt_tokenizer.pad_id if self.replace_bos_with_pad else self.tgt_tokenizer.bos_id] + + self.tgt_tokenizer.text_to_ids(tgt.strip()) + + [self.tgt_tokenizer.eos_id] + ) + if len(tgt) > self.max_tgt_seq_length: + tgt = tgt[-self.max_tgt_seq_length + 1 :] + return tgt + + def __len__(self): + return len(self.indexed_dataset) + + def __getitem__(self, idx): + example = self.indexed_dataset[idx] + text_enc = self._process_src(example['input']) + tgt = self._process_tgt(example['output']) + text_dec = tgt[:-1] + labels = tgt[1:] + return {'text_enc': text_enc, 'text_dec': text_dec, 'labels': labels} + + def collate_fn(self, batch): + text_enc = [item['text_enc'] for item in batch] + text_dec = [item['text_dec'] for item in batch] + labels = [item['labels'] for item in batch] + + if isinstance(text_enc[0], np.ndarray): + text_enc = [x.tolist() for x in text_enc] + + if isinstance(text_dec[0], np.ndarray): + text_dec = [x.tolist() for x in text_dec] + + if isinstance(labels[0], np.ndarray): + labels = [x.tolist() for x in labels] + + max_dec_input_length = max([len(item) for item in text_dec]) if text_dec else 0 + max_enc_input_length = max([len(item) for item in text_enc]) if text_enc else 0 + max_label_length = max([len(item) for item in labels]) if labels else 0 + + loss_mask = [([1] * (len(item))) + ([0] * (max_label_length - len(item))) for item in labels] + text_enc = [item + [self.src_tokenizer.pad_id] * (max_enc_input_length - len(item)) for item in text_enc] + text_dec = [item + [self.tgt_tokenizer.pad_id] * (max_dec_input_length - len(item)) for item in text_dec] + labels = [item + [self.tgt_tokenizer.pad_id] * (max_label_length - len(item)) for item in labels] + + text_enc = torch.LongTensor(text_enc) + text_dec = torch.LongTensor(text_dec) + labels = torch.LongTensor(labels) + loss_mask = torch.LongTensor(loss_mask) + + enc_mask = (text_enc != self.src_tokenizer.pad_id).long() + dec_mask = (text_dec != self.tgt_tokenizer.pad_id).long() + + return { + 'text_enc': text_enc, + 'text_dec': text_dec, + 'labels': labels, + 'loss_mask': loss_mask, + 'enc_mask': enc_mask, + 'dec_mask': dec_mask, + } + + +def convert_data_file_format(src_file_name, tgt_file_name, output_file_name): + """ + Converts the old two-file format used by SequenceToSequenceDataset to the new JSONL format used by T5SFTDataset + """ + output_lines = [] + with open(src_file_name, encoding='utf8') as f_src, open(tgt_file_name, encoding='utf8') as f_tgt: + for i, (src, tgt) in enumerate(zip(f_src, f_tgt)): + if i % 10000 == 0 and i != 0: + logging.info(f"Read {i} lines from {src_file_name} & {tgt_file_name}") + output_lines.append({'input': src, 'output': tgt}) + + logging.info(f'Dataset Length : {len(output_lines)}') + + with open(output_file_name, "w") as f_json: + for line in output_lines: + f_json.write(json.dumps(line) + '\n') diff --git a/nemo/collections/nlp/models/language_modeling/megatron_glue_model.py b/nemo/collections/nlp/models/language_modeling/megatron_glue_model.py index 5cc0f7ea3a32..c0a4b6351530 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_glue_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_glue_model.py @@ -18,7 +18,7 @@ TextToTextGLUEDataset, TextToTextXNLIDataset, ) -from nemo.collections.nlp.models.language_modeling.megatron_finetune_model import MegatronT5FinetuneModel +from nemo.collections.nlp.models.language_modeling.megatron_t5_sft_model import MegatronT5SFTModel from nemo.utils import logging try: @@ -33,8 +33,8 @@ __all__ = ['MegatronT5GLUEModel'] -class MegatronT5GLUEModel(MegatronT5FinetuneModel): - """GLUE Model that Inherits from MegatronT5FinetuneModel and overrides the dataset building.""" +class MegatronT5GLUEModel(MegatronT5SFTModel): + """GLUE Model that Inherits from MegatronT5SFTModel and overrides the dataset building.""" def __init__(self, cfg: DictConfig, trainer: Trainer): super().__init__(cfg, trainer=trainer) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py index 1f905430e87c..3d7a5a127399 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py @@ -196,9 +196,8 @@ def __init__(self, cfg: DictConfig, trainer: Trainer): raise ImportError( "Apex was not found. Please see the NeMo README for installation instructions: https://github.com/NVIDIA/NeMo#megatron-gpt." ) - if not HAVE_MEGATRON_CORE: - raise ImportError( + logging.warning( "megatron-core was not found. Please see the NeMo README for installation instructions: https://github.com/NVIDIA/NeMo#megatron-gpt." ) # this prevents base constructor from initializing tokenizer diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_peft_models.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_peft_models.py index ba7dd4529326..dfe2627b70d1 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_peft_models.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_peft_models.py @@ -35,6 +35,7 @@ ) from nemo.core.classes.mixins import adapter_mixins from nemo.utils import logging, model_utils +from nemo.utils.decorators import deprecated try: from megatron.core import parallel_state @@ -46,6 +47,10 @@ HAVE_MEGATRON_CORE = False +@deprecated( + explanation="Please use MegatronGPTSFTModel.add_adapter() for PEFT features." + "See the updated `megatron_gpt_peft_tuning.py` for an example." +) class MegatronGPTPEFTModel(MegatronGPTSFTModel): """ base class for all mixin based adapter models @@ -70,7 +75,7 @@ def first_stage_of_pipeline(self): return False def init_peft_modules(self): - """ + """ Randomly initialize the peft params and add them to the appropriate modules. """ assert len(self.peft_name_keys) > 0, "peft_name_keys have not been set no PEFT modules will be added" @@ -118,7 +123,7 @@ def setup(self, stage=None): self.setup_complete = True def get_all_keys(self,): - """ + """ Returns all the keys in the model """ k = [n for n, p in self.named_parameters()] @@ -127,7 +132,7 @@ def get_all_keys(self,): return set(k + b) def get_peft_state_dict(self,): - """ + """ Gets the keys associated with the adapters only. """ state_dict = self.model.state_dict(prefix="model.module." if self.cfg.megatron_amp_O2 else "model.") @@ -205,13 +210,13 @@ def on_load_checkpoint(self, checkpoint) -> None: def setup_optimizer_param_groups(self): """ - ModelPT override. Optimizer will get self._optimizer_param_groups. + ModelPT override. Optimizer will get self._optimizer_param_groups. Makes two optimizer param groups, one for the frozen model params - and one for the prompt-table/prompt-encoder params. The learning + and one for the prompt-table/prompt-encoder params. The learning rate for the frozen model's params will always be zero effectively freezing the model's params but still allowing for the needed gradients - to be passed around in pipeline parallel models. The prompt-encoder - and/or prompt table will use the learning rate set by the user. + to be passed around in pipeline parallel models. The prompt-encoder + and/or prompt table will use the learning rate set by the user. """ self.freeze() # Freeze the entire model opt_params = [] @@ -231,7 +236,7 @@ def __init__( super().__init__(cfg, trainer) def init_peft_modules(self): - """ + """ Randomly initialize the peft params and add them to the appropriate modules. """ assert len(self.peft_name_keys) > 0, "peft_name_keys have not been set no PEFT modules will be added" @@ -294,8 +299,8 @@ class MegatronGPTAdapterModel(MegatronGPTLayerwisePEFTModel): Two adapter's are inserted into each Transformer layer in the base GPT Model. It is assumed that these set of adapters will then be trained for a specific task. - Once trained, the adapter weights will be saved and can be re-loaded - and infused into the same GPT Model for inference. + Once trained, the adapter weights will be saved and can be re-loaded + and infused into the same GPT Model for inference. """ def __init__( @@ -331,7 +336,7 @@ def __init__( class MegatronGPTAdapterModelWeightTying(MegatronGPTLayerwisePEFTModel): """ - TODO + TODO """ def __init__( @@ -411,8 +416,8 @@ class MegatronGPTIA3Model(MegatronGPTLayerwisePEFTModel): Three adapter's are inserted into each Transformer layer in the base GPT Model. Each adapter is basically a vector that simply scales the key, value or ffn hidden representations. It is assumed that these set of adapters will then be trained for a specific task. - Once trained, the adapter weights will be saved and can be re-loaded - and infused into the same GPT Model for inference. + Once trained, the adapter weights will be saved and can be re-loaded + and infused into the same GPT Model for inference. """ def __init__(self, cfg: DictConfig, trainer: Trainer): @@ -444,7 +449,7 @@ def __init__(self, cfg: DictConfig, trainer: Trainer): class MegatronGPTPTuningModel(MegatronGPTPEFTModel): """ - MegatronGPTPTuningModel is a model that combines a base model (GPTSFTModel) with a p-tuning prefix in the + MegatronGPTPTuningModel is a model that combines a base model (GPTSFTModel) with a p-tuning prefix in the input word embedding representations using a prompt-encoder as descripted in Liu et al. https://arxiv.org/pdf/2103.10385.pdf The mixin framework adds the output of prompt-encoder (i.e. the virtual embeddings) inside @@ -467,7 +472,7 @@ def __init__(self, cfg: DictConfig, trainer: Trainer): self.virtual_tokens = cfg.peft.p_tuning.virtual_tokens def init_peft_modules(self,): - """ + """ Initialize the p-tuning prompt encoder in the mixin. This should only happen in the first stage of the pipeline unlike other PEFT methods like Lora or Adapters because p-tuning only adds params at input to the encoder layer. @@ -479,7 +484,7 @@ def init_peft_modules(self,): return True def state_dict(self, destination=None, prefix=None, keep_vars=False): - """ + """ Reimplement state_dict for ptuning because we also need to check the stage of the pipeline. The check is required to make pp>1 to work. """ @@ -493,7 +498,7 @@ def state_dict(self, destination=None, prefix=None, keep_vars=False): return self.model.state_dict(prefix="model.") def load_state_dict(self, state_dict, strict: bool = True): - """ + """ Reimplement load_state_dict for ptuning because we also need to check the stage of the pipeline. The check is required to make pp>1 to work. """ @@ -629,7 +634,7 @@ def __init__( class MegatronGPTLoRAModelWeightTying(MegatronGPTLayerwisePEFTModel): """ - TODO + TODO """ def __init__( diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_sft_model.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_sft_model.py index b38a81460097..8f2a108fcfc0 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_sft_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_sft_model.py @@ -32,13 +32,9 @@ ) from nemo.collections.nlp.models.language_modeling.megatron_gpt_model import MegatronGPTModel from nemo.collections.nlp.modules.common.megatron.utils import get_iterator_k_split -from nemo.collections.nlp.modules.common.text_generation_utils import ( - LengthParam, - SamplingParam, - generate, - get_computeprob_response, - megatron_gpt_generate, -) +from nemo.collections.nlp.modules.common.text_generation_utils import generate, get_computeprob_response + +from nemo.collections.nlp.parts.mixins.nlp_adapter_mixins import NLPAdapterModelMixin from nemo.collections.nlp.parts.utils_funcs import get_last_rank from nemo.utils import AppState, logging @@ -68,7 +64,7 @@ __all__ = ['MegatronGPTSFTModel'] -class MegatronGPTSFTModel(MegatronGPTModel): +class MegatronGPTSFTModel(NLPAdapterModelMixin, MegatronGPTModel): """ Megatron GPT Supervised Fine-Tuning """ @@ -169,6 +165,7 @@ def setup(self, stage=None): # NOTE: super().__init__ will try and setup train/val/test datasets, but we sidestep this using a if self._train_ds is not None condition # We then set things up for real only once setup() of this class is called. resume_checkpoint_path = self.trainer.ckpt_path + self.setup_complete = True if resume_checkpoint_path: init_consumed_samples = self._extract_consumed_samples_from_ckpt(resume_checkpoint_path) else: @@ -209,6 +206,7 @@ def setup(self, stage=None): if self.cfg.get('transformer_engine', False): self.setup_transformer_engine_tp_groups() + self.setup_complete = True def _build_dataset(self, data_cfg, is_train=True): datasets = [] @@ -567,7 +565,7 @@ def inference_epoch_end(self, outputs, mode, data_cfg): # Logging of the averaged metrics: averaged_loss = sum(averaged_loss) / len(averaged_loss) - averaged_metric = sum(averaged_metric) / len(averaged_metric) if len(averaged_metric) > 1 else None + averaged_metric = sum(averaged_metric) / len(averaged_metric) if len(averaged_metric) >= 1 else None # Handle case where metrics can be nan or inf. This can break checkpoint save/load. if averaged_metric is not None and (torch.isinf(averaged_metric) or torch.isnan(averaged_metric)): @@ -835,6 +833,9 @@ def on_test_epoch_start(self): ) return super().on_test_epoch_start() + def on_predict_epoch_start(self): + return self.on_test_epoch_start() + def on_test_epoch_end(self): _ = self.inference_epoch_end(self.test_step_outputs, 'test', self.cfg.data.test_ds) # Commenting as on_test_epoch_end was a no-op in PTL 1.9 diff --git a/nemo/collections/nlp/models/language_modeling/megatron_t0_model.py b/nemo/collections/nlp/models/language_modeling/megatron_t0_model.py index fb62dc0db8ee..4d4d80b71a98 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_t0_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_t0_model.py @@ -23,7 +23,7 @@ MegatronPretrainingBatchSampler, ) from nemo.collections.nlp.data.language_modeling.t0_dataset import T0Dataset -from nemo.collections.nlp.models.language_modeling.megatron_finetune_model import MegatronT5FinetuneModel +from nemo.collections.nlp.models.language_modeling.megatron_t5_sft_model import MegatronT5SFTModel from nemo.utils import AppState, logging try: @@ -48,8 +48,8 @@ __all__ = ['MegatronT0Model'] -class MegatronT0Model(MegatronT5FinetuneModel): - """T0 (https://arxiv.org/abs/2110.08207) Model that Inherits from MegatronT5FinetuneModel and overrides the dataset building.""" +class MegatronT0Model(MegatronT5SFTModel): + """T0 (https://arxiv.org/abs/2110.08207) Model that Inherits from MegatronT5SFTModel and overrides the dataset building.""" def __init__(self, cfg: DictConfig, trainer: Trainer): super().__init__(cfg, trainer=trainer) @@ -154,7 +154,7 @@ def _build_dataset(self, data_cfg, check_implict_grad_acc=False, is_train=True): return datasets def training_step(self, dataloader_iter, batch_idx): - return super(MegatronT5FinetuneModel, self).training_step(dataloader_iter, batch_idx) + return super().training_step(dataloader_iter, batch_idx) # Override the parent batch reconfiguring logic. def _reconfigure_and_process_inference_batch(self, batch): @@ -236,10 +236,10 @@ def setup_eval_dataloader(self, datasets, data_cfg): # TODO: Temporary overrides of finetune model. This needs to removed in the finetune model. def on_train_start(self) -> None: - super(MegatronT5FinetuneModel, self).on_train_start() + super().on_train_start() def on_validation_start(self) -> None: - super(MegatronT5FinetuneModel, self).on_validation_start() + super().on_validation_start() def on_test_start(self) -> None: - super(MegatronT5FinetuneModel, self).on_test_start() + super().on_test_start() diff --git a/nemo/collections/nlp/models/language_modeling/megatron_t5_adapter_model.py b/nemo/collections/nlp/models/language_modeling/megatron_t5_adapter_model.py index eaf4004e7371..d1332831ef1d 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_t5_adapter_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_t5_adapter_model.py @@ -26,11 +26,11 @@ from pytorch_lightning.trainer.trainer import Trainer from nemo.collections.common.parts.adapter_modules import LinearAdapterConfig -from nemo.collections.nlp.models.language_modeling.megatron_finetune_model import MegatronT5FinetuneModel from nemo.collections.nlp.models.language_modeling.megatron_t5_model import MegatronT5Model from nemo.collections.nlp.models.language_modeling.megatron_t5_prompt_learning_model import ( MegatronT5PromptLearningModel, ) +from nemo.collections.nlp.models.language_modeling.megatron_t5_sft_model import MegatronT5SFTModel from nemo.collections.nlp.modules.common import VirtualPromptStyle from nemo.collections.nlp.modules.common.megatron.adapters.parallel_adapters import ( AdapterName, @@ -183,11 +183,11 @@ def predict_step(self, batch: Any, batch_idx: int, dataloader_idx: int = 0) -> A ) # Special ids to text function to handle stripping and special tokens with sentencepiece tokenizers. - preds_text = MegatronT5FinetuneModel.ids_to_text(predicted_token_ids, self.tokenizer) - input_text = MegatronT5FinetuneModel.ids_to_text(enc_input, self.tokenizer) + preds_text = MegatronT5SFTModel.ids_to_text(predicted_token_ids, self.tokenizer) + input_text = MegatronT5SFTModel.ids_to_text(enc_input, self.tokenizer) if labels is not None: - labels_text = MegatronT5FinetuneModel.ids_to_text(labels, self.tokenizer) + labels_text = MegatronT5SFTModel.ids_to_text(labels, self.tokenizer) else: labels_text = [None] * len(preds_text) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_t5_prompt_learning_model.py b/nemo/collections/nlp/models/language_modeling/megatron_t5_prompt_learning_model.py index f1a13c2abee4..a6afb103cb89 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_t5_prompt_learning_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_t5_prompt_learning_model.py @@ -25,8 +25,8 @@ from nemo.collections.nlp.models.language_modeling.megatron_base_prompt_learning_model import ( MegatronBasePromptLearningModel, ) -from nemo.collections.nlp.models.language_modeling.megatron_finetune_model import MegatronT5FinetuneModel from nemo.collections.nlp.models.language_modeling.megatron_t5_model import MegatronT5Model +from nemo.collections.nlp.models.language_modeling.megatron_t5_sft_model import MegatronT5SFTModel from nemo.collections.nlp.modules.common.megatron.utils import ( average_losses_across_data_parallel_group, get_iterator_k_split, @@ -296,9 +296,9 @@ def get_predictions(self, input_ids, enc_mask, encoder_input, labels): else self.tokenizer.bos_id, ) # Special ids to text function to handle stripping and special tokens with sentencepiece tokenizers. - preds_text = MegatronT5FinetuneModel.ids_to_text(predicted_token_ids, self.tokenizer) - labels_text = MegatronT5FinetuneModel.ids_to_text(labels, self.tokenizer) - input_text = MegatronT5FinetuneModel.ids_to_text(input_ids, self.tokenizer) + preds_text = MegatronT5SFTModel.ids_to_text(predicted_token_ids, self.tokenizer) + labels_text = MegatronT5SFTModel.ids_to_text(labels, self.tokenizer) + input_text = MegatronT5SFTModel.ids_to_text(input_ids, self.tokenizer) return { 'predicted_token_ids': preds_text, 'labels': labels_text, @@ -482,11 +482,11 @@ def predict_step(self, batch: Any, batch_idx: int, dataloader_idx: int = 0) -> A else self.tokenizer.bos_id, ) # Special ids to text function to handle stripping and special tokens with sentencepiece tokenizers. - preds_text = MegatronT5FinetuneModel.ids_to_text(predicted_token_ids, self.tokenizer) - input_text = MegatronT5FinetuneModel.ids_to_text(input_ids, self.tokenizer) + preds_text = MegatronT5SFTModel.ids_to_text(predicted_token_ids, self.tokenizer) + input_text = MegatronT5SFTModel.ids_to_text(input_ids, self.tokenizer) if labels is not None: - labels_text = MegatronT5FinetuneModel.ids_to_text(labels, self.tokenizer) + labels_text = MegatronT5SFTModel.ids_to_text(labels, self.tokenizer) else: labels_text = [None] * len(preds_text) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_finetune_model.py b/nemo/collections/nlp/models/language_modeling/megatron_t5_sft_model.py similarity index 74% rename from nemo/collections/nlp/models/language_modeling/megatron_finetune_model.py rename to nemo/collections/nlp/models/language_modeling/megatron_t5_sft_model.py index b95017a17302..22483731a534 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_finetune_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_t5_sft_model.py @@ -23,8 +23,11 @@ from nemo.collections.common.metrics import MetricStringToTorchMetric from nemo.collections.common.metrics.classification_accuracy import ExactStringPerCategoryMatchMetric from nemo.collections.nlp.data.common.sequence_to_sequence_dataset import SequenceToSequenceDataset +from nemo.collections.nlp.data.language_modeling.megatron.t5_sft_dataset import T5SFTDataset from nemo.collections.nlp.models.language_modeling.megatron_t5_model import MegatronT5Model, T5Sentinel from nemo.collections.nlp.modules.common.megatron.utils import get_iterator_k_split + +from nemo.collections.nlp.parts.mixins.nlp_adapter_mixins import NLPAdapterModelMixin from nemo.collections.nlp.parts.utils_funcs import get_last_rank from nemo.utils import AppState, logging @@ -50,19 +53,25 @@ HAVE_MEGATRON_CORE = False -__all__ = ['MegatronT5FinetuneModel'] +__all__ = ['MegatronT5SFTModel'] -class MegatronT5FinetuneModel(MegatronT5Model): - """Finetune Model that Inherits from MegatronT5Model instead.""" +class MegatronT5SFTModel(NLPAdapterModelMixin, MegatronT5Model): + """ T5 Finetuning model in the same format as MegatronGPTSFTModel """ def __init__(self, cfg: DictConfig, trainer: Trainer): + if not HAVE_APEX: + raise ImportError( + "Apex was not found. Please see the NeMo README for installation instructions: https://github.com/NVIDIA/NeMo#megatron-gpt." + ) super().__init__(cfg, trainer=trainer) - self.val_metric, self.val_metric_name = self.setup_metric(self.cfg.data.validation_ds) - self.val_metric = torch.nn.ModuleList(self.val_metric) + self.val_metric = self.test_metric = None + if hasattr(self.cfg.data, "validation_ds"): + self.val_metric, self.val_metric_name = self.setup_metric(self.cfg.data.validation_ds) + self.val_metric = torch.nn.ModuleList(self.val_metric) if self.val_metric is not None else None if hasattr(self.cfg.data, "test_ds"): self.test_metric, self.test_metric_name = self.setup_metric(self.cfg.data.test_ds) - self.test_metric = torch.nn.ModuleList(self.test_metric) + self.test_metric = torch.nn.ModuleList(self.test_metric) if self.test_metric is not None else None def setup_metric(self, data_cfg): # XNLI is a special case. @@ -71,10 +80,12 @@ def setup_metric(self, data_cfg): metric = [ExactStringPerCategoryMatchMetric(self.cfg.eval_languages)] else: if not hasattr(data_cfg, "metric"): - metric = MetricStringToTorchMetric["exact_string_match"] + metric_class = MetricStringToTorchMetric["exact_string_match"] else: if not hasattr(data_cfg.metric, "name"): raise ValueError("Metric name is not provided in the metric config.") + if data_cfg.metric.name == "loss": + return None, "loss" if data_cfg.metric.name not in MetricStringToTorchMetric: raise KeyError( f"{data_cfg.metric.name} is not supported. List of supported metrics: {MetricStringToTorchMetric.keys()}" @@ -104,9 +115,8 @@ def setup_metric(self, data_cfg): raise ValueError( f"Number of class labels {len(data_cfg.metric.get('class_labels', None))} does not match `num_classes` : {data_cfg.metric.num_classes}" ) - - metric_name = data_cfg.metric.name - metric_class = MetricStringToTorchMetric[metric_name] + metric_name = data_cfg.metric.name + metric_class = MetricStringToTorchMetric[metric_name] # GLUE will not have a "src_file_name" attribute and will always have only a single metric. if hasattr(data_cfg, "src_file_name") or hasattr(data_cfg, "file_names"): @@ -143,6 +153,10 @@ def setup_metric(self, data_cfg): def _metrics_require_string2category_map(self): return set(["f1", "accuracy", "average_precision"]) + @property + def model(self): + return self.enc_dec_model + def setup(self, stage=None): # This is just to keep the parent class happy since we override its setup() method. self.init_consumed_samples = 0 @@ -158,6 +172,7 @@ def setup(self, stage=None): self.setup_test_data() if hasattr(self, '_train_ds'): self.setup_training_data() + self.setup_complete = True def on_validation_epoch_start(self): app_state = AppState() @@ -322,30 +337,31 @@ def inference_step(self, dataloader_iter, batch_idx: int, mode: str, dataloader_ ) # Special ids to text function to handle stripping and special tokens with sentencepiece tokenizers. - preds_text = MegatronT5FinetuneModel.ids_to_text(predicted_token_ids, self.tokenizer) - labels_text = MegatronT5FinetuneModel.ids_to_text(batch['labels'], self.tokenizer) - input_text = MegatronT5FinetuneModel.ids_to_text(batch['text_enc'], self.tokenizer) + preds_text = MegatronT5SFTModel.ids_to_text(predicted_token_ids, self.tokenizer) + labels_text = MegatronT5SFTModel.ids_to_text(batch['labels'], self.tokenizer) + input_text = MegatronT5SFTModel.ids_to_text(batch['text_enc'], self.tokenizer) if not batch_has_lang_information: categories = [None] * len(preds_text) else: categories = batch['lang'] - metric = self.val_metric[dataloader_idx] if mode == 'validation' else self.test_metric[dataloader_idx] - assert len(categories) == len(preds_text) == len(labels_text) - for _, (pred, label, category) in enumerate(zip(preds_text, labels_text, categories)): - # To compute metrics like pearson or spearman correlation, we need to cast the predicted string and labels to floats. - pred, label = self.cast_for_metric( - pred=pred, - label=label, - metric_name=self.val_metric_name if mode == 'validation' else self.test_metric_name, - class_labels=data_cfg.metric.get('class_labels', None), - labels_are_strings=data_cfg.metric.get('labels_are_strings', False), - ) - if batch_has_lang_information: - _ = metric(pred, label, category) - else: - _ = metric(pred, label) + if self.val_metric is not None or self.test_metric is not None: + metric = self.val_metric[dataloader_idx] if mode == 'validation' else self.test_metric[dataloader_idx] + assert len(categories) == len(preds_text) == len(labels_text) + for _, (pred, label, category) in enumerate(zip(preds_text, labels_text, categories)): + # To compute metrics like pearson or spearman correlation, we need to cast the predicted string and labels to floats. + pred, label = self.cast_for_metric( + pred=pred, + label=label, + metric_name=self.val_metric_name if mode == 'validation' else self.test_metric_name, + class_labels=data_cfg.metric.get('class_labels', None), + labels_are_strings=data_cfg.metric.get('labels_are_strings', False), + ) + if batch_has_lang_information: + _ = metric(pred, label, category) + else: + _ = metric(pred, label) outputs = { 'preds': preds_text, @@ -435,40 +451,40 @@ def inference_epoch_end(self, outputs, mode, data_cfg): torch.distributed.broadcast(loss, get_last_rank()) self.log('val_loss', loss, prog_bar=True, rank_zero_only=True, batch_size=1) self.log('global_step', self.trainer.global_step, prog_bar=True, rank_zero_only=True, batch_size=1) + averaged_loss.append(loss) # Determine the key used to log the loss based on the user provided name of the dataset or the dataloader index. loss_log_key = self._determine_log_key(data_cfg, dataloader_idx, "loss", mode) - # Determine the key used to log the eval metric based on the user provided name of the dataset or the dataloader index. - metric_log_key = self._determine_log_key(data_cfg, dataloader_idx, metric_name, mode) - self.log(loss_log_key, loss, batch_size=1) - metric_object = ( - self.val_metric[dataloader_idx] if mode == 'validation' else self.test_metric[dataloader_idx] - ) - metric = metric_object.compute() - if metric_name == 'rouge': - metric = metric['rouge1_fmeasure'] - # Handle logging of GLUE/XNLI separately here. XNLI has a separate metric per language. - if isinstance(metric, dict): - # GLUE case: - if len(metric) == 1 and 'acc' in metric: - metric = metric['acc'] - self.log(metric_log_key, metric, batch_size=1) - logging.info(f"{mode} {metric_name}: {metric}") - # XNLI case where the metric dictionary contains the language and the computed metric as values. - else: - for k, v in metric.items(): - if k != 'acc' and 'total' not in k: - self.log(metric_log_key + f'_{k}', v, batch_size=1) - logging.info(f"{mode} {metric_name} lang {k} : {v}") - if metric_name != 'rouge': + if metric_name != 'loss': + # Determine the key used to log the eval metric based on the user provided name of the dataset or the dataloader index. + metric_log_key = self._determine_log_key(data_cfg, dataloader_idx, metric_name, mode) + self.log(loss_log_key, loss, batch_size=1) + metric_object = ( + self.val_metric[dataloader_idx] if mode == 'validation' else self.test_metric[dataloader_idx] + ) + metric = metric_object.compute() + if metric_name == 'rouge': + metric = metric['rouge1_fmeasure'] + # Handle logging of GLUE/XNLI separately here. XNLI has a separate metric per language. + if isinstance(metric, dict): + # GLUE case: + if len(metric) == 1 and 'acc' in metric: metric = metric['acc'] - else: - self.log(metric_log_key, metric, batch_size=1) - logging.info(f"{metric_log_key}: {metric}") - metric_object.reset() - - averaged_loss.append(loss) - averaged_metric.append(metric) + self.log(metric_log_key, metric, batch_size=1) + logging.info(f"{mode} {metric_name}: {metric}") + # XNLI case where the metric dictionary contains the language and the computed metric as values. + else: + for k, v in metric.items(): + if k != 'acc' and 'total' not in k: + self.log(metric_log_key + f'_{k}', v, batch_size=1) + logging.info(f"{mode} {metric_name} lang {k} : {v}") + if metric_name != 'rouge': + metric = metric['acc'] + else: + self.log(metric_log_key, metric, batch_size=1) + logging.info(f"{metric_log_key}: {metric}") + metric_object.reset() + averaged_metric.append(metric) # Write predictions, labels, and inputs to a file for each validation/test dataset. if data_cfg.get("write_predictions_to_file", False): @@ -479,7 +495,7 @@ def inference_epoch_end(self, outputs, mode, data_cfg): f"Cannot write predictions to file when output_file_path_prefix is not set or present in the yaml config file." ) - # Gather the outputs object from all data parallel ranks since we are using the DistributedSampler which splits data across DDP ranks. + # Gather the outputs object from all data parallel ranks since we are using the DistributedSampler which splits data across DDPDDP ranks. gathered_outputs = [None for _ in range(parallel_state.get_data_parallel_world_size())] torch.distributed.all_gather_object( gathered_outputs, @@ -527,10 +543,10 @@ def inference_epoch_end(self, outputs, mode, data_cfg): # Logging of the averaged metrics: averaged_loss = sum(averaged_loss) / len(averaged_loss) - averaged_metric = sum(averaged_metric) / len(averaged_metric) + averaged_metric = sum(averaged_metric) / len(averaged_metric) if len(averaged_metric) >= 1 else None # Handle case where metrics can be nan or inf. This can break checkpoint save/load. - if torch.isinf(averaged_metric) or torch.isnan(averaged_metric): + if averaged_metric is not None and (torch.isinf(averaged_metric) or torch.isnan(averaged_metric)): app_state = AppState() monitor_mode = app_state.checkpoint_callback_params.mode assert monitor_mode in ['min', 'max'] @@ -538,10 +554,12 @@ def inference_epoch_end(self, outputs, mode, data_cfg): if mode == 'validation': self.log("validation_loss", averaged_loss, batch_size=1) - self.log(f"validation_{self.val_metric_name}", averaged_metric, batch_size=1) + if averaged_metric is not None: + self.log(f"validation_{self.val_metric_name}", averaged_metric, batch_size=1) elif mode == 'test': self.log("test_loss", averaged_loss, batch_size=1) - self.log(f"test_{self.test_metric_name}", averaged_metric, batch_size=1) + if averaged_metric is not None: + self.log(f"test_{self.test_metric_name}", averaged_metric, batch_size=1) app_state = AppState() if hasattr(self, "_train_ds"): @@ -635,7 +653,9 @@ def setup_eval_data(self, datasets, data_cfg): for dataset in datasets: eval_dl = self.build_data_loader( dataset, - global_batch_size=self.cfg.data.train_ds.global_batch_size, + global_batch_size=self.cfg.data.test_ds.global_batch_size + if hasattr(self.cfg.data, "test_ds") + else self.cfg.data.validation_ds.global_batch_size, shuffle=data_cfg.shuffle, num_workers=data_cfg.num_workers, pin_memory=data_cfg.pin_memory, @@ -660,32 +680,52 @@ def _build_train_dataset(self, data_cfg): f"Cannot use drop_last=False in your training data with gradient accumulation found grad acc of {data_cfg.global_batch_size // (data_cfg.micro_batch_size * parallel_state.get_data_parallel_world_size())} with global_batch_size {data_cfg.global_batch_size}, micro_batch_size {data_cfg.micro_batch_size}, data parallel size {parallel_state.get_data_parallel_world_size()}" ) datasets = [] - # Determine if we are using a single dataset or a list of datasets. - is_src_list_config = isinstance(data_cfg.src_file_name, ListConfig) - is_tgt_list_config = isinstance(data_cfg.tgt_file_name, ListConfig) - - if (is_src_list_config and not is_tgt_list_config) or (is_tgt_list_config and not is_src_list_config): - raise ValueError("src_list and tgt_list must both be either a ListConfig or a string. ") - if is_src_list_config: - if len(data_cfg.src_file_name) != len(data_cfg.tgt_file_name): - raise ValueError("src_file_name and tgt_file_name must have the same number of elements. ") + if hasattr(data_cfg, "src_file_name") and hasattr(data_cfg, "tgt_file_name"): + # Determine if we are using a single dataset or a list of datasets. + is_src_list_config = isinstance(data_cfg.src_file_name, ListConfig) + is_tgt_list_config = isinstance(data_cfg.tgt_file_name, ListConfig) + + if (is_src_list_config and not is_tgt_list_config) or (is_tgt_list_config and not is_src_list_config): + raise ValueError("src_list and tgt_list must both be either a ListConfig or a string. ") + if is_src_list_config: + if len(data_cfg.src_file_name) != len(data_cfg.tgt_file_name): + raise ValueError("src_file_name and tgt_file_name must have the same number of elements. ") + else: + data_cfg.src_file_name = [data_cfg.src_file_name] + data_cfg.tgt_file_name = [data_cfg.tgt_file_name] + + for src, tgt in zip(data_cfg.src_file_name, data_cfg.tgt_file_name): + dataset = SequenceToSequenceDataset( + src_file_name=src, + tgt_file_name=tgt, + src_tokenizer=self.tokenizer, + tgt_tokenizer=self.tokenizer, + max_src_seq_length=data_cfg.max_src_seq_length, + max_tgt_seq_length=data_cfg.max_tgt_seq_length, + add_bos_to_input=data_cfg.get('add_bos_to_input', True), + add_eos_to_input=data_cfg.get('add_eos_to_input', True), + replace_bos_with_pad=data_cfg.get('replace_bos_with_pad', False), + ) + datasets.append(dataset) + elif hasattr(data_cfg, "file_names"): + for file_path in data_cfg.file_names: + dataset = T5SFTDataset( + file_path=file_path, + src_tokenizer=self.tokenizer, + tgt_tokenizer=self.tokenizer, + max_src_seq_length=data_cfg.max_seq_length, + max_tgt_seq_length=data_cfg.max_seq_length, + add_bos_to_input=data_cfg.get('add_bos', True), + add_eos_to_input=data_cfg.get( + 'add_eos', True + ), # review: need domain knowledge to undertand if these args are ok + index_mapping_dir=data_cfg.get('index_mapping_dir', None), + memmap_workers=data_cfg.get('memmap_workers', None), + hf_dataset=data_cfg.get('hf_dataset', False), + ) + datasets.append(dataset) else: - data_cfg.src_file_name = [data_cfg.src_file_name] - data_cfg.tgt_file_name = [data_cfg.tgt_file_name] - - for src, tgt in zip(data_cfg.src_file_name, data_cfg.tgt_file_name): - dataset = SequenceToSequenceDataset( - src_file_name=src, - tgt_file_name=tgt, - src_tokenizer=self.tokenizer, - tgt_tokenizer=self.tokenizer, - max_src_seq_length=data_cfg.max_src_seq_length, - max_tgt_seq_length=data_cfg.max_tgt_seq_length, - add_bos_to_input=data_cfg.get('add_bos_to_input', True), - add_eos_to_input=data_cfg.get('add_eos_to_input', True), - replace_bos_with_pad=data_cfg.get('replace_bos_with_pad', False), - ) - datasets.append(dataset) + raise ValueError("You must specify either (src_file_name and tgt_file_name) or file_names in data config") if len(datasets) > 1: dataset = ConcatMapDataset( @@ -707,41 +747,58 @@ def _build_eval_dataset(self, data_cfg): f'You are trying to use "implicit gradient accumulation" of {data_cfg.global_batch_size // (data_cfg.micro_batch_size * parallel_state.get_data_parallel_world_size())} in your validation/test datasets. This is not supported. Please set global_batch_size equal to micro_batch_size * data_parallel_world_size.' ) datasets = [] - # Determine if we are using a single dataset or a list of datasets. - is_src_list_config = isinstance(data_cfg.src_file_name, ListConfig) - is_tgt_list_config = isinstance(data_cfg.tgt_file_name, ListConfig) - is_names_list_config = False - if hasattr(data_cfg, "names"): - if isinstance(data_cfg.names, ListConfig): - is_names_list_config = True - - if (is_src_list_config and not is_tgt_list_config) or (is_tgt_list_config and not is_src_list_config): - raise ValueError("src_list and tgt_list must both be either a ListConfig or a string. ") - if is_src_list_config: - if len(data_cfg.src_file_name) != len(data_cfg.tgt_file_name): - raise ValueError("src_file_name and tgt_file_name must have the same number of elements. ") - if is_names_list_config and len(data_cfg.names) != len(data_cfg.src_file_name): - raise ValueError( - "If you are providing names for each src/tgt file, they must have the same number of elements." + if hasattr(data_cfg, "src_file_name") and hasattr(data_cfg, "tgt_file_name"): + # Determine if we are using a single dataset or a list of datasets. + is_src_list_config = isinstance(data_cfg.src_file_name, ListConfig) + is_tgt_list_config = isinstance(data_cfg.tgt_file_name, ListConfig) + is_names_list_config = False + if hasattr(data_cfg, "names"): + if isinstance(data_cfg.names, ListConfig): + is_names_list_config = True + + if (is_src_list_config and not is_tgt_list_config) or (is_tgt_list_config and not is_src_list_config): + raise ValueError("src_list and tgt_list must both be either a ListConfig or a string. ") + if is_src_list_config: + if len(data_cfg.src_file_name) != len(data_cfg.tgt_file_name): + raise ValueError("src_file_name and tgt_file_name must have the same number of elements. ") + if is_names_list_config and len(data_cfg.names) != len(data_cfg.src_file_name): + raise ValueError( + "If you are providing names for each src/tgt file, they must have the same number of elements." + ) + else: + data_cfg.src_file_name = [data_cfg.src_file_name] + data_cfg.tgt_file_name = [data_cfg.tgt_file_name] + + for src, tgt in zip(data_cfg.src_file_name, data_cfg.tgt_file_name): + dataset = SequenceToSequenceDataset( + src_file_name=src, + tgt_file_name=tgt, + src_tokenizer=self.tokenizer, + tgt_tokenizer=self.tokenizer, + max_src_seq_length=data_cfg.max_src_seq_length, + max_tgt_seq_length=data_cfg.max_tgt_seq_length, + add_bos_to_input=data_cfg.get('add_bos_to_input', True), + add_eos_to_input=data_cfg.get('add_eos_to_input', True), + replace_bos_with_pad=data_cfg.get('replace_bos_with_pad', False), ) + datasets.append(dataset) + elif hasattr(data_cfg, "file_names"): + for file_path in data_cfg.file_names: + dataset = T5SFTDataset( + file_path=file_path, + src_tokenizer=self.tokenizer, + tgt_tokenizer=self.tokenizer, + max_src_seq_length=data_cfg.max_seq_length, + max_tgt_seq_length=data_cfg.max_seq_length, + add_bos_to_input=data_cfg.get('add_bos', True), + add_eos_to_input=data_cfg.get('add_eos', True), + index_mapping_dir=data_cfg.get('index_mapping_dir', None), + memmap_workers=data_cfg.get('memmap_workers', None), + hf_dataset=data_cfg.get('hf_dataset', False), + ) + datasets.append(dataset) else: - data_cfg.src_file_name = [data_cfg.src_file_name] - data_cfg.tgt_file_name = [data_cfg.tgt_file_name] - - for src, tgt in zip(data_cfg.src_file_name, data_cfg.tgt_file_name): - dataset = SequenceToSequenceDataset( - src_file_name=src, - tgt_file_name=tgt, - src_tokenizer=self.tokenizer, - tgt_tokenizer=self.tokenizer, - max_src_seq_length=data_cfg.max_src_seq_length, - max_tgt_seq_length=data_cfg.max_tgt_seq_length, - add_bos_to_input=data_cfg.get('add_bos_to_input', True), - add_eos_to_input=data_cfg.get('add_eos_to_input', True), - replace_bos_with_pad=data_cfg.get('replace_bos_with_pad', False), - ) - datasets.append(dataset) - + raise ValueError("You must specify either (src_file_name and tgt_file_name) or file_names in data config") return datasets def build_train_valid_test_datasets(self, stage): diff --git a/nemo/collections/nlp/models/nlp_model.py b/nemo/collections/nlp/models/nlp_model.py index 0f0de87d3887..ac3a8c998ba7 100644 --- a/nemo/collections/nlp/models/nlp_model.py +++ b/nemo/collections/nlp/models/nlp_model.py @@ -16,8 +16,9 @@ import hashlib import json import os -from typing import Any, Mapping, Optional +from typing import Any, Mapping, Optional, Union +import torch from lightning_fabric.utilities.cloud_io import _load as pl_load from omegaconf import DictConfig, OmegaConf from pytorch_lightning import Trainer @@ -39,6 +40,7 @@ from nemo.collections.nlp.parts.nlp_overrides import NLPSaveRestoreConnector from nemo.core.classes import ModelPT from nemo.core.classes.exportable import Exportable +from nemo.core.connectors.save_restore_connector import SaveRestoreConnector from nemo.utils import AppState, logging try: @@ -439,3 +441,22 @@ def load_state_dict(self, state_dict: Mapping[str, Any], strict: bool = True): del state_dict["bert_model.embeddings.position_ids"] results = super(NLPModel, self).load_state_dict(state_dict, strict=strict) return results + + @classmethod + def restore_from( + cls, + restore_path: str, + override_config_path: Optional[Union[OmegaConf, str]] = None, + map_location: Optional[torch.device] = None, + strict: bool = True, + return_config: bool = False, + save_restore_connector: SaveRestoreConnector = None, + trainer: Optional[Trainer] = None, + ): + if save_restore_connector is None: + save_restore_connector = NLPSaveRestoreConnector() + if os.path.isdir(restore_path): + save_restore_connector.model_extracted_dir = restore_path + return super().restore_from( + restore_path, override_config_path, map_location, strict, return_config, save_restore_connector, trainer + ) diff --git a/nemo/collections/nlp/modules/common/megatron/adapters/parallel_adapters.py b/nemo/collections/nlp/modules/common/megatron/adapters/parallel_adapters.py index d38530c1bd5d..989b16d694e1 100644 --- a/nemo/collections/nlp/modules/common/megatron/adapters/parallel_adapters.py +++ b/nemo/collections/nlp/modules/common/megatron/adapters/parallel_adapters.py @@ -27,6 +27,7 @@ from nemo.collections.nlp.modules.common.megatron.fused_bias_gelu import fused_bias_gelu from nemo.collections.nlp.modules.common.megatron.utils import ApexGuardDefaults, init_method_const, init_method_normal from nemo.core.classes.mixins import adapter_mixin_strategies +from nemo.core.classes.mixins.adapter_mixins import AdapterConfig try: @@ -53,7 +54,7 @@ class AdapterName(str, enum.Enum): """ - Names for adapters used in NLP Adapters and IA3. Note: changing this will break backward compatibility. + Names for adapters used in NLP Adapters and IA3. Note: changing this will break backward compatibility. """ MLP_INFUSED = "mlp_infused_adapter" @@ -95,14 +96,14 @@ def forward(self, x): class MLPInfusedAdapter(InfusedAdapter): """ MLPInfusedAdapter is basically a clone of InfusedAdapter. We do this to make the adapter_mixin agnostic to adapter names - and only check adapter class types. + and only check adapter class types. """ pass @dataclass -class InfusedAdapterConfig: +class InfusedAdapterConfig(AdapterConfig): in_features: int _target_: str = "{0}.{1}".format(InfusedAdapter.__module__, InfusedAdapter.__name__) @@ -232,7 +233,7 @@ def forward(self, x): @dataclass -class ParallelLinearAdapterConfig: +class ParallelLinearAdapterConfig(AdapterConfig): in_features: int out_features: int dim: int @@ -248,7 +249,7 @@ class ParallelLinearAdapterConfig: class LoraKQVAdapter(ParallelLinearAdapter): """ - Lora Adapters are the same arch as regular adapters but with potentially different input and output feature sizes + Lora Adapters are the same arch as regular adapters but with potentially different input and output feature sizes and they do not use an bottleneck activation function """ @@ -257,7 +258,7 @@ class LoraKQVAdapter(ParallelLinearAdapter): class LoraKVAdapter(ParallelLinearAdapter): """ - Lora Adapters are the same arch as regular adapters but with potentially different input and output feature sizes + Lora Adapters are the same arch as regular adapters but with potentially different input and output feature sizes and they do not use an bottleneck activation function """ @@ -266,7 +267,7 @@ class LoraKVAdapter(ParallelLinearAdapter): class LoraQAdapter(ParallelLinearAdapter): """ - Lora Adapters are the same arch as regular adapters but with potentially different input and output feature sizes + Lora Adapters are the same arch as regular adapters but with potentially different input and output feature sizes and they do not use an bottleneck activation function """ @@ -290,7 +291,7 @@ class LoraKVAdapterConfig(ParallelLinearAdapterConfig): class PromptEncoderAdapter(nn.Module, AdapterModuleUtil): """ - The Tensor Parallel MLP prompt encoder network that is used to generate the virtual + The Tensor Parallel MLP prompt encoder network that is used to generate the virtual token embeddings for p-tuning. It only have two layers. TODO: (@adithyare) Need to add all the functionality from the PromptEncoder class """ @@ -311,7 +312,7 @@ def __init__( virtual_tokens: the number of vitural tokens hidden_size: hidden dimension output_size: the output dimension - init_std: the MLP init std value + init_std: the MLP init std value """ super().__init__() self.bottleneck_dim = bottleneck_dim @@ -384,7 +385,7 @@ def inner_forward(self,): return output_embeds def forward(self, batch_size: int, use_cached_reps: bool = False) -> torch.Tensor: - """ + """ Forward pass through the encoder with caching of prompt representations """ if use_cached_reps: @@ -406,7 +407,7 @@ def forward(self, batch_size: int, use_cached_reps: bool = False) -> torch.Tenso @dataclass -class PromptEncoderAdapterConfig: +class PromptEncoderAdapterConfig(AdapterConfig): virtual_tokens: int bottleneck_dim: int embedding_dim: int @@ -558,7 +559,7 @@ class ParallelLinearAdapterWeightTyingConfig: class LoraKQVAdapterWeightTying(ParallelLinearAdapterWeightTying): """ - TODO + TODO """ pass diff --git a/nemo/collections/nlp/modules/common/megatron/token_level_encoder_decoder.py b/nemo/collections/nlp/modules/common/megatron/token_level_encoder_decoder.py index 5f8c1c977a99..67e08c8db103 100644 --- a/nemo/collections/nlp/modules/common/megatron/token_level_encoder_decoder.py +++ b/nemo/collections/nlp/modules/common/megatron/token_level_encoder_decoder.py @@ -15,6 +15,10 @@ import torch from omegaconf import DictConfig +from nemo.collections.nlp.modules.common.megatron.adapters.parallel_adapters import ( + AdapterName, + PromptEncoderAdapterConfig, +) from nemo.collections.nlp.modules.common.megatron.hiddens import get_hiddens_module from nemo.collections.nlp.modules.common.megatron.language_model import Embedding from nemo.collections.nlp.modules.common.megatron.layer_type import LayerType @@ -38,6 +42,7 @@ ) from nemo.collections.nlp.modules.common.megatron.vocab_parallel_cross_entropy import vocab_parallel_cross_entropy from nemo.collections.nlp.parts import utils_funcs +from nemo.core.classes.mixins import adapter_mixins try: from apex.transformer.enums import AttnMaskType, ModelType @@ -102,7 +107,7 @@ def forward(self, hidden_states, word_embeddings_weight): # TODO: add soft prompts as an Embedding sub-class -class MegatronTokenLevelEncoderDecoderModule(MegatronModule): +class MegatronTokenLevelEncoderDecoderModule(MegatronModule, adapter_mixins.AdapterModuleMixin): """Token-based (input/output is tokens) encoder-decoder model (e.g. T5 Language model.)""" def __init__( @@ -427,6 +432,7 @@ def __init__( ) self._tokens_head_key = 'tokens_head' + self.set_accepted_adapter_types([PromptEncoderAdapterConfig._target_]) def _validate_kv_channels(self, cfg): kv_channels = cfg.kv_channels @@ -548,6 +554,18 @@ def forward( else: enc_position_ids = None enc_input = self.encoder_embedding(enc_input_ids, enc_position_ids, token_type_ids=token_type_ids) + if self.is_adapter_available(): + _sq, _bs, _hs = enc_input.size() + ptuning_adapter = self.get_adapter_module(AdapterName.PTUNING_ADAPTER) + v = ptuning_adapter.virtual_tokens + if ( + ptuning_adapter and _sq >= v + ): # The sequence should be longer the v to insert virtual embeddings. + virtual_embeddings = ptuning_adapter(_bs) + enc_input = enc_input[ + v:, :, : + ] # the first v tokens are pads so that they can be swapped out with virtual embeddings. + enc_input = torch.concat([virtual_embeddings, enc_input], dim=0) else: enc_input = None else: diff --git a/nemo/collections/nlp/parts/megatron_trainer_builder.py b/nemo/collections/nlp/parts/megatron_trainer_builder.py index 02f0a7f4f4aa..3c8bbc5db0d3 100644 --- a/nemo/collections/nlp/parts/megatron_trainer_builder.py +++ b/nemo/collections/nlp/parts/megatron_trainer_builder.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import sys + from omegaconf import DictConfig from pytorch_lightning import Trainer from pytorch_lightning.callbacks import ModelSummary @@ -22,8 +24,10 @@ GradScaler, MegatronHalfPrecisionPlugin, NLPDDPStrategy, + NLPDDPStrategyNotebook, PipelineMixedPrecisionPlugin, ) +from nemo.utils import logging class MegatronTrainerBuilder: @@ -39,6 +43,12 @@ def _training_strategy(self) -> NLPDDPStrategy: """ Returns a ddp strategy passed to Trainer.strategy. """ + # check interactive environment + _IS_INTERACTIVE = hasattr(sys, "ps1") or bool(sys.flags.interactive) + if _IS_INTERACTIVE and self.cfg.trainer.devices == 1: + logging.info("Detected interactive environment, using NLPDDPStrategyNotebook") + return NLPDDPStrategyNotebook(no_ddp_communication_hook=True, find_unused_parameters=False,) + return NLPDDPStrategy( no_ddp_communication_hook=True, gradient_as_bucket_view=self.cfg.model.gradient_as_bucket_view, @@ -61,7 +71,9 @@ def _plugins(self) -> list: plugins: list of plugins passed to Trainer.plugins including precision plugins. """ megatron_amp_o2 = self.cfg.model.get('megatron_amp_O2', False) - with_distributed_adam = self.cfg.model.optim.get('name') == 'distributed_fused_adam' + with_distributed_adam = ( + self.cfg.model.optim.get('name') == 'distributed_fused_adam' if self.cfg.model.get('optim') else False + ) plugins = [] if self.cfg.trainer.precision in [16, '16', 'bf16', '16-mixed', 'bf16-mixed']: @@ -110,3 +122,15 @@ def create_trainer(self) -> Trainer: **self.cfg.trainer, callbacks=[ModelSummary(max_depth=3), CustomProgressBar()] ) + + +class MegatronLMPPTrainerBuilder(MegatronTrainerBuilder): + """Builder for scripts where grad scaler is turned off for pipeline parallel LM model. E.g. PEFT tuning scripts""" + + def _grad_scaler(self) -> GradScaler: + return GradScaler( + init_scale=self.cfg.model.get("native_amp_init_scale", 2 ** 32), + growth_interval=self.cfg.model.get("native_amp_growth_interval", 1000), + hysteresis=self.cfg.model.get("hysteresis", 2), + enabled=False if self.cfg.model.pipeline_model_parallel_size > 1 else True, + ) diff --git a/nemo/collections/nlp/parts/mixins/__init__.py b/nemo/collections/nlp/parts/mixins/__init__.py new file mode 100644 index 000000000000..4fc50543f1d2 --- /dev/null +++ b/nemo/collections/nlp/parts/mixins/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/nemo/collections/nlp/parts/mixins/nlp_adapter_mixins.py b/nemo/collections/nlp/parts/mixins/nlp_adapter_mixins.py new file mode 100644 index 000000000000..16a3850852d4 --- /dev/null +++ b/nemo/collections/nlp/parts/mixins/nlp_adapter_mixins.py @@ -0,0 +1,484 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import tempfile +from typing import List, Optional, Union + +import torch +from omegaconf import DictConfig, OmegaConf, open_dict + +from nemo.utils.model_utils import inject_model_parallel_rank + +try: + from nemo.collections.nlp.modules.common.megatron.adapters.mcore_mixins import swap_mcore_mixin + + HAVE_MEGATRON_CORE = True +except (ImportError, ModuleNotFoundError): + HAVE_MEGATRON_CORE = False + + +from nemo.collections.nlp.modules.common.megatron.adapters.parallel_adapters import PromptEncoderAdapterConfig +from nemo.collections.nlp.parts.peft_config import ( + PEFT_CONFIG_MAP, + CanonicalAdaptersPEFTConfig, + LoraPEFTConfig, + PEFTConfig, + PtuningPEFTConfig, +) +from nemo.core.classes.mixins.adapter_mixins import AdapterModuleMixin +from nemo.core.connectors.save_restore_connector import SaveRestoreConnector +from nemo.utils import logging, model_utils + +try: + from megatron.core import parallel_state +except (ImportError, ModuleNotFoundError): + HAVE_MEGATRON_CORE = False + + +class NLPAdapterModelMixin: + """ NLP Adapter Mixin that can augment any transformer-based model with Adapter module support. + This mixin class should be used only with a top level ModelPT subclass, that includes either a `model` or an `enc_dec_model` submodule. + This mixin class adds several utility methods to add, load and save adapters. + + An Adapter module is any Pytorch nn.Module that possess a few properties : + + - It's input and output dimension are the same, while the hidden dimension need not be the same. + - The final layer of the Adapter module is zero-initialized, so that the residual connection to the adapter yields the original output. + + This mixin class aims to integrate with PEFT, which is one or more adapters modules. + The two features of PEFT, layer selection and weight tying, are also supported in this mixin class. + """ + + def __init__(self, *args, **kwargs): + self.use_peft = False + self.setup_complete = False + self.use_ptuning_only = False + super().__init__(*args, **kwargs) + if hasattr(self, "enc_dec_model"): + self.model_prefix = "enc_dec_model." # for T5 + else: + self.model_prefix = "model.module." if self.cfg.megatron_amp_O2 else "model." + + self.use_mcore_gpt = hasattr(self, 'mcore_gpt') and self.mcore_gpt + if self.use_mcore_gpt: + assert HAVE_MEGATRON_CORE, "You set `mcore_gpt` as True but megatron core is not found." + + def first_stage_of_pipeline(self): + if hasattr(self, "model") and hasattr(self.model, "pre_process"): + return self.model.pre_process + elif hasattr(self, "model") and hasattr(self.model, "module") and hasattr(self.model.module, "pre_process"): + # (guyueh1): this if condition is used to handle amp O2 + # when amp_O2 is on, self.model will be wrapped by the Float16Module class + return self.model.module.pre_process + logging.warning("no attribute named model or no model.pre_process found. Can not detect stage of pipeline...") + return False + + def _get_all_keys(self,): + """ + Returns all the keys in the model + """ + k = [n for n, p in self.named_parameters()] + b = [n for n, p in self.named_buffers() if n.replace("model.module.", "model.", 1) in self.state_dict().keys()] + # we include buffers because ptuning representations are cached in a buffer and saved to state_dict for inference time use. + return set(k + b) + + def _check_and_add_adapter(self, name, module, peft_name, peft_cfg, name_key_to_mcore_mixins=None): + if name_key_to_mcore_mixins is not None: + for mcore_target, mcore_mixin in name_key_to_mcore_mixins[peft_name]: + if name in [ + mcore_target, + f'model.{mcore_target}', + f'model.module.{mcore_target}', + ]: # simple string match for now + swap_mcore_mixin(module, mcore_mixin) + if model_utils.import_class_by_path(peft_cfg._target_) in module.get_accepted_adapter_types(): + module.add_adapter( + name=peft_name, + cfg=peft_cfg, + base_model_cfg=self.cfg, + model_parallel_config=self.model_parallel_config, + ) + elif isinstance(module, AdapterModuleMixin): + if model_utils.import_class_by_path(peft_cfg._target_) in module.get_accepted_adapter_types(): + module.add_adapter( + name=peft_name, + cfg=peft_cfg, + base_model_cfg=self.cfg, + model_parallel_config=self.model_parallel_config, + ) + + def _check_and_add_peft_cfg(self, peft_cfg): + + layer_selection = peft_cfg.layer_selection + + assert not self.use_mcore_gpt or hasattr( + peft_cfg, 'name_key_to_mcore_mixins' + ), f"{peft_cfg.__class__.__name__} is not supported in megatron core mode yet." + name_key_to_mcore_mixins = peft_cfg.name_key_to_mcore_mixins if self.use_mcore_gpt else None + + for adapter_name, adapter_cfg in peft_cfg.get_config_dict().items(): + # self.mcore_gpt means is GPT and not T5 + if hasattr(self, 'mcore_gpt') and not isinstance(adapter_cfg, PromptEncoderAdapterConfig): + if layer_selection is not None: + logging.info( + f"Layer selection {layer_selection} is enabled for the current model (" + f"{self.__class__.__name__} + {adapter_name})" + ) + if self.use_mcore_gpt: + if self.cfg.megatron_amp_O2: + layers = self.model.module.decoder.layers + else: + layers = self.model.decoder.layers + else: + if self.cfg.megatron_amp_O2: + layers = self.model.module.language_model.encoder.layers + else: + layers = self.model.language_model.encoder.layers + if layer_selection is None: + layer_selection = list(range(1, self.cfg.num_layers + 1)) + for layer in layers: + if layer.layer_number in layer_selection: + for name, module in layer.named_modules(): + self._check_and_add_adapter( + name, module, adapter_name, adapter_cfg, name_key_to_mcore_mixins + ) + else: + # Non GPT models, as well as GPT+PTuning do not support layer selection + if layer_selection is not None: + logging.warning( + "Layer selection is specified, but it is not supported for either " + f"{self.__class__.__name__} or {adapter_name})" + ) + for name, module in self.named_modules(): + self._check_and_add_adapter(name, module, adapter_name, adapter_cfg, name_key_to_mcore_mixins) + + def add_adapter(self, peft_cfgs: Union[PEFTConfig, List[PEFTConfig]]): + """ + High level API to add one or more adapter modules to the model, and freeze the base weights + This method supports adding adapter modules from PEFTConfig or list of PEFTConfig. It would add + corresponding adapter modules. Layer selection and weight tying would be applied if it's in PEFTConfig + + Args: + peft_cfgs: One or more PEFTConfig objects that specify the PEFT method configuration + """ + + if not isinstance(peft_cfgs, List): + peft_cfgs = [peft_cfgs] + + self.base_keys = self._get_all_keys() + self.freeze() + logging.info(f"Before adding PEFT params:\n{self.summarize()}") + + self.use_ptuning_only = len(peft_cfgs) == 1 and isinstance(peft_cfgs[0], PtuningPEFTConfig) + + for peft_cfg in peft_cfgs: + if self.use_ptuning_only: + if not self.first_stage_of_pipeline(): + # There are no params to add if we are not in the first state of the pipeline + continue + self.virtual_tokens = peft_cfg.virtual_tokens + + self._check_and_add_peft_cfg(peft_cfg) + + logging.info(f"After adding PEFT params:\n{self.summarize()}") + self.adapter_keys = self._get_all_keys() - self.base_keys + + for cfg in peft_cfgs: + if cfg.weight_tying: + self.tie_weights(cfg) + self.use_peft = True + + def _get_config_and_state_dict_from_nemo(self, filepath, map_location): + cwd = os.getcwd() + + with tempfile.TemporaryDirectory() as tmpdir: + try: + SaveRestoreConnector._unpack_nemo_file(filepath, tmpdir) + + os.chdir(tmpdir) + + config_yaml = "model_config.yaml" + model_weights_ckpt = "model_weights.ckpt" + + conf = OmegaConf.load(config_yaml) + + os.chdir(cwd) + model_weights = os.path.join(tmpdir, model_weights_ckpt) + model_weights = inject_model_parallel_rank(model_weights) + state_dict = torch.load(model_weights, map_location=map_location) + + return conf, state_dict + finally: + os.chdir(cwd) + + def setup_optimizer_param_groups(self): + """ + ModelPT override. Optimizer will get self._optimizer_param_groups. + Makes two optimizer param groups, one for the frozen model params + and one for the prompt-table/prompt-encoder params. The learning + rate for the frozen model's params will always be zero effectively + freezing the model's params but still allowing for the needed gradients + to be passed around in pipeline parallel models. The prompt-encoder + and/or prompt table will use the learning rate set by the user. + """ + if self.use_peft: + self.freeze() # Freeze the entire model + opt_params = [] + for _, module in self.named_modules(): + if isinstance(module, AdapterModuleMixin) and module.is_adapter_available(): + module.set_enabled_adapters(enabled=True) + module.unfreeze_enabled_adapters() # selectively unfreeze the adapter modules. + opt_params += [p for p in module.parameters() if p.requires_grad] + self._optimizer_param_groups = ({"params": opt_params},) + logging.info(f"Optimizer groups set:\n{self.summarize()}") + else: + super().setup_optimizer_param_groups() + + def load_adapters( + self, filepath: str, peft_cfgs: Optional[Union[PEFTConfig, List[PEFTConfig]]] = None, map_location: str = None, + ): + """ + Utility method that restores only the adapter module(s), and not the entire model itself. + This allows the sharing of adapters which are often just a fraction of the size of the full model, + enabling easier deliver. + + .. note:: + + During restoration, assumes that the model does not currently already have one or more adapter modules. + + Args: + filepath: Filepath of the .ckpt or .nemo file. + peft_cfgs: One or more PEFTConfig objects that specify the PEFT method configuration. + If none, will infer from the .nemo checkpoint + map_location: Pytorch flag, where to place the adapter(s) state dict(s). + """ + + # Determine device + if map_location is None: + if torch.cuda.is_available(): + map_location = 'cuda' + else: + map_location = 'cpu' + + if filepath.endswith('.nemo'): + conf, state_dict = self._get_config_and_state_dict_from_nemo(filepath, map_location) + elif filepath.endswith('.ckpt'): + state_dict = torch.load(filepath, map_location)['state_dict'] + else: + raise RuntimeError(f"{filepath} is not nemo file or ckpt file") + if not peft_cfgs: + assert filepath.endswith( + '.nemo' + ), "Inferring peft scheme is only supported for .nemo checkpoints. Please supply the `peft_cfgs` argument." + peft_cfgs = [PEFT_CONFIG_MAP[conf.peft.peft_scheme](conf)] + self.add_adapter(peft_cfgs) + assert set(state_dict.keys()) == self.adapter_keys + super().load_state_dict(state_dict, strict=False) + + def tie_weights(self, peft_cfg): + pos_idx = 0 + + if self.use_mcore_gpt: + if self.cfg.megatron_amp_O2: + layers = self.model.module.decoder.layers + else: + layers = self.model.decoder.layers + else: + if self.cfg.megatron_amp_O2: + layers = self.model.module.language_model.encoder.layers + else: + layers = self.model.language_model.encoder.layers + + if isinstance(peft_cfg, LoraPEFTConfig): + layer0 = layers[0].self_attention + elif isinstance(peft_cfg, CanonicalAdaptersPEFTConfig): + layer0 = layers[0] + else: + raise RuntimeError(f"{peft_cfg} is not supported for tied weights") + + for adapter_name in layer0.adapter_layer: + adapter = layer0.get_adapter_module(adapter_name) + print(adapter_name, pos_idx) + adapter.set_position(pos_idx) + pos_idx += 1 + + for layer in layers[1:]: + if isinstance(peft_cfg, LoraPEFTConfig): + layer = layer.self_attention + for adapter_name in layer.adapter_layer: + print(adapter_name, pos_idx) + adapter_l = layer.get_adapter_module(adapter_name) + adapter_0 = layer0.get_adapter_module(adapter_name) + adapter_l.tie_weights(pos_idx, adapter_0) + pos_idx += 1 + + def get_peft_state_dict(self): + """ + Gets the keys associated with the adapters only. + """ + state_dict = self.model.state_dict(prefix=self.model_prefix) + peft_state_dict = {} + for k in self.adapter_keys: + # state_dict keys needs to be in non-O2 format and will be corrected in PEFTSaveRestoreConnector if O2=True + new_k = k.replace("model.module.", "model.", 1) + peft_state_dict[new_k] = state_dict[k] + return peft_state_dict + + def state_dict(self, destination=None, prefix=None, keep_vars=False): + if self.use_peft and self.setup_complete: + # Once setup is complete we no longer need to track the frozen part of the model. Only there adapter state dict keeps changing so state_dict only track these. + return self.get_peft_state_dict() + else: + # we want all the params with the same keys as calling self.state_dict() + # but we can't call self.state_dict() here as it would be a recursive call. + # so we call self.model.state_dict(prefix="model.") which will return all the keys and params same as calling self.state_dict() + return self.model.state_dict(prefix=self.model_prefix) + + def sharded_state_dict(self, prefix: str = ''): + use_mcore_gpt = hasattr(self, 'mcore_gpt') and self.mcore_gpt + if not use_mcore_gpt or (self.use_peft and self.setup_complete): + return None + else: + return self.model.sharded_state_dict(prefix=self.model_prefix) + + def load_state_dict(self, state_dict, strict: bool = True): + if len(state_dict) == 0: + return # checkpoint is loaded in on_load_checkpoint() + if self.use_peft and self.setup_complete: + # at this stage only adapter params will appear in the state_dict arg + # so we only update those while the rest of the model is frozen. + # setting strict=False will ignore the missing keys (which are not being updated anyway) + # explicitly check if state_dict.keys matches all the expected self.adapter_keys since we don't have the + # safety in strict=True anymore. + assert set(state_dict.keys()) == self.adapter_keys + super().load_state_dict(state_dict, strict=False) + else: + super().load_state_dict(state_dict, strict=True) + + def on_load_checkpoint(self, checkpoint) -> None: + """LightningModule hook: + https://pytorch-lightning.readthedocs.io/en/stable/common/lightning_module.html#on-load-checkpoint + """ + if self.use_peft and self.setup_complete: + if not self.use_ptuning_only or self.first_stage_of_pipeline(): + # same as super().on_load_checkpoint() but strict=False and only check unexpected keys + # mcore uses distributed checkpointing + if hasattr(self, 'mcore_gpt') and self.mcore_gpt: + for index, module in enumerate(self.get_gpt_module_list()): + if parallel_state.get_virtual_pipeline_model_parallel_world_size() is not None: + checkpoint_state_dict = checkpoint['state_dict'][f'model_{index}'] + else: + checkpoint_state_dict = checkpoint['state_dict'] + # checkpoint_state_dict has "model." but module does not so we need to remove it when loading + checkpoint_state_dict = { + key.replace('model.', ''): checkpoint_state_dict.pop(key) + for key in list(checkpoint_state_dict.keys()) + } + missing_keys, unexpected_keys = module.load_state_dict(checkpoint_state_dict, strict=False) + + assert len(unexpected_keys) == 0, 'Unexpected key(s) in state_dict: {}. '.format( + ', '.join('"{}"'.format(k) for k in unexpected_keys) + ) + + # legacy checkpointing for interleaved + else: + if isinstance(self.model, list): + for i in range(len(self.model)): + parallel_state.set_virtual_pipeline_model_parallel_rank(i) + self.model[i].module.load_state_dict(checkpoint[f'model{i}'], strict=True) + parallel_state.set_virtual_pipeline_model_parallel_rank(0) + else: + super().on_load_checkpoint(checkpoint) + + @classmethod + def merge_cfg_with(cls, path: str, cfg: DictConfig) -> DictConfig: + """ + Merge a given configuration dictionary `cfg` with the configuration dictionary + obtained from restoring a MegatronGPTSFTModel or MegatronT5SFTModel at the specified `path`. + + Args: + path (str): The path to the SFT model checkpoint to be restored. + cfg (DictConfig): The configuration dictionary to merge. + + Returns: + DictConfig: The merged configuration dictionary. + + Examples: + >>> path = "/path/to/model/checkpoint" + >>> cfg = DictConfig({"model": {"key": "value"}, "trainer": {"precision": 16}}) + >>> merged_cfg = merge_cfg_with(path, cfg) + + Notes: + - The function resolves variables within the `cfg` dictionary using `OmegaConf.resolve`. + - Keys in `cfg.model` will override the corresponding keys in the output dictionary. + - If "train_ds" exists in `cfg.model.data`, it updates `micro_batch_size` and `global_batch_size`. + - If `cfg.trainer` contains a "precision" key, it updates `output.precision`. + + """ + + base_cfg = cls.restore_from(path, return_config=True) + + OmegaConf.resolve(cfg) + with open_dict(base_cfg): + for key, val in cfg.model.items(): + base_cfg[key] = val + if "train_ds" in cfg.model.data: + base_cfg.micro_batch_size = cfg.model.data.train_ds.micro_batch_size + base_cfg.global_batch_size = cfg.model.data.train_ds.global_batch_size + if cfg.get("trainer", None) and cfg.trainer.get("precision"): + base_cfg.precision = cfg.trainer.precision + + return base_cfg + + @classmethod + def merge_inference_cfg(cls, path: str, cfg: DictConfig) -> DictConfig: + """ + Generate a configuration dictionary by a given configuration dictionary `cfg` with + the configuration dictionary obtained from restoring a MegatronGPTSFTModel or MegatronT5SFTModel + at the specified `path` and modify `cfg` for inference + + Args: + path (str): The path to the SFT model checkpoint to be restored. + cfg (DictConfig): The configuration dictionary to modify for inference. + + Returns: + DictConfig: The configuration dictionary for inference. + + Examples: + >>> path = "/path/to/model/checkpoint" + >>> cfg = DictConfig({"model": {"key": "value"}, "trainer": {"precision": 16}}) + >>> merged_cfg = merge_inference_cfg(path, cfg) + + Notes: + - "precision" and "test_ds" from `cfg` will override the corresponding keys in the output dictionary + - "activations_checkpoint" will be ovrrided to None in the output dictionary + - "use_flash_attention" will be True if in one of the configuration dictionarys is True + - "seq_len_interpolation_factor" will be overrided from `cfg` if it's not None from checkpoint + """ + + peft_cfg = cls.restore_from(path, return_config=True) + with open_dict(peft_cfg): + # update the model config of the trained model with params we want to set at inference time. + peft_cfg.precision = cfg.trainer.precision + for key, val in cfg.model.items(): + if key != 'data': + peft_cfg[key] = val + peft_cfg.data.test_ds = cfg.model.data.test_ds + + with open_dict(cfg): + cfg.inference.add_BOS = peft_cfg.data.test_ds.add_bos + cfg.inference.tokens_to_generate = peft_cfg.data.test_ds.tokens_to_generate + + return peft_cfg diff --git a/nemo/collections/nlp/parts/nlp_overrides.py b/nemo/collections/nlp/parts/nlp_overrides.py index a116c8e60299..5332a9d2f115 100644 --- a/nemo/collections/nlp/parts/nlp_overrides.py +++ b/nemo/collections/nlp/parts/nlp_overrides.py @@ -432,6 +432,18 @@ def restore_checkpoint_after_setup(self) -> bool: return True +class NLPDDPStrategyNotebook(NLPDDPStrategy): + """ Version of NLPDDPStrategy to be used in a Jupyter Notebook + A large portion of Megatron code has DDP dependency, so it has been necessary to use NLPDDPStrategy even for + single-GPU training (e.g. in a Jupyter notebook) + A PTL 2.0 changes has prevented DDPStrategy to be used in a notebook. + This version of NLPDDPStrategy enables megatron training in a notebook in PTL 2.0. + """ + + def _configure_launcher(self): + self._launcher = None + + class NLPSaveRestoreConnector(SaveRestoreConnector): def __init__(self) -> None: if not HAVE_APEX: @@ -675,156 +687,6 @@ def dummy(): return instance -class PEFTSaveRestoreConnector(NLPSaveRestoreConnector): - """ - PEFT models require the ability to load/save a small subset of the full model (once PEFT params have been infused into the base model.) - The PEFTSaveRestoreConnector is used to allow loading and saving only the PEFT params while not saving the entire model. - - Args: - peft_model_nemo_path: Used to provide the .nemo file corresponding to a PEFT model (which will only contain a small set of params) - peft_model_ckpt_path: Used to provide the path to .ckpt files of a PEFT model. This is required when no .nemo is available (yet) such as during resumed training. - peft_model_ckpt_name: The filename of the ckpt file inside the peft_model_ckpt_path folder - If both are provided the peft_model_ckpt_path takes precedence. - If neither are provided, PEFT params are initialized at random (not loaded from any external source). - """ - - def __init__( - self, - peft_model_nemo_path: Optional[str] = None, - peft_model_ckpt_path: Optional[str] = None, - peft_model_ckpt_name: Optional[str] = "model_weights.ckpt", - ) -> None: - super().__init__() - self.peft_model_ckpt_name = peft_model_ckpt_name - if peft_model_ckpt_path: - # First we will try to load a adapter ckpt path - # this is given priority over loading from nemo path to make resumption of training possible - ckpt_name = os.path.basename(peft_model_ckpt_path) - if not ckpt_name.strip() == '': - # update the weights file name inside the ckpt path rank folders - self.peft_model_ckpt_name = ckpt_name - self.peft_model_ckpt_dir = os.path.dirname(peft_model_ckpt_path) - assert os.path.isdir(self.peft_model_ckpt_dir) - self.peft_model_nemo_path = None - elif peft_model_nemo_path: - # If resumption is not possible we will try to load a adapter nemo path - self.peft_model_nemo_path = peft_model_nemo_path - assert os.path.exists(self.peft_model_nemo_path) - self.peft_model_ckpt_dir = None - else: - # We are not resuming training from a nemo file or a ckpt - # We are training the adapter from randomly initialization - self.peft_model_nemo_path = None - self.peft_model_ckpt_dir = None - - def _load_state_dict_from_disk(self, model_weights, map_location=None): - """ - Infuse the state_dict of the base model with PEFT params from either a peft_model_nemo_path or peft_model_ckpt_path - """ - # first load based model weights - base_model_state_dict = super()._load_state_dict_from_disk(model_weights, map_location) - # Next, We want to load PEFT model's weights - if self.peft_model_nemo_path: - # if the PEFT weights are provided in a .nemo file - # we need to untar the .nemo if its still tarred - with tempfile.TemporaryDirectory() as tmpdir: - self._unpack_nemo_file(self.peft_model_nemo_path, tmpdir) - model_weights_path = self._inject_model_parallel_rank_for_ckpt(tmpdir, self.peft_model_ckpt_name) - peft_state_dict = torch.load(model_weights_path, map_location) - elif self.peft_model_ckpt_dir: - # if the PEFT weights are provided in a ckpt path file - # we don't need to untar - model_weights_path = self._inject_model_parallel_rank_for_ckpt( - self.peft_model_ckpt_dir, self.peft_model_ckpt_name - ) - peft_state_dict = torch.load(model_weights_path, map_location)['state_dict'] - else: - peft_state_dict = {} - if base_model_state_dict: - base_model_state_dict.update(peft_state_dict) # add the PEFT state_dict into the base model's state_dict - return base_model_state_dict - - def restore_from( - self, - calling_cls, - restore_path: str, - override_config_path: Optional[Union[OmegaConf, str]] = None, - map_location: Optional[torch.device] = None, - strict: bool = True, - return_config: bool = False, - trainer: Trainer = None, - ): - """ - Extends the restore_from method of the `NLPSaveRestoreConnector` so that PEFT params are inserted into the state_dict which is required when training a PEFT model from scratch. - """ - # Get path where the command is executed - the artifacts will be "retrieved" there - # (original .nemo behavior) - loaded_params = super().load_config_and_state_dict( - calling_cls, restore_path, override_config_path, map_location, strict, return_config, trainer, - ) - if not isinstance(loaded_params, tuple) or return_config is True: - return loaded_params - conf, instance, state_dict = loaded_params - - # if we're using dist checkpointing then state_dict will be None - if state_dict is None: - # dist checkpointing needs torch.distributed to load the checkpoint - if parallel_state.is_unitialized(): - - def dummy(): - return - - if trainer.strategy.launcher is not None: - trainer.strategy.launcher.launch(dummy, trainer=trainer) - trainer.strategy.setup_environment() - - with tempfile.TemporaryDirectory() as tmpdir: - # Check if self.model_extracted_dir is set, and is a valid path - if self.model_extracted_dir is not None and os.path.isdir(self.model_extracted_dir): - # Log that NeMo will use the provided `model_extracted_dir` - logging.info( - f"Restoration will occur within pre-extracted directory : " f"`{self.model_extracted_dir}`." - ) - - # Override `tmpdir` above with the pre-extracted `model_extracted_dir` - tmpdir = self.model_extracted_dir - - else: - # Extract the nemo file into the temporary directory - self._unpack_nemo_file( - path2file=restore_path, out_folder=tmpdir, extract_config_only=return_config is True - ) - checkpoint = {} - sharded_state_dict = instance.sharded_state_dict() - peft_state_dict = instance.get_peft_state_dict() - for k in peft_state_dict.keys(): - sharded_state_dict.pop(k) - checkpoint['state_dict'] = sharded_state_dict - # remove model weights extension - tmp_model_weights_ckpt = os.path.join(tmpdir, self.model_weights_ckpt) - tmp_model_weights_dir = os.path.splitext(tmp_model_weights_ckpt)[0] - assert os.path.isdir(tmp_model_weights_dir), f'Expected {tmp_model_weights_dir} to be a directory.' - checkpoint = dist_checkpointing.load( - sharded_state_dict=checkpoint, checkpoint_dir=tmp_model_weights_dir - ) - checkpoint['state_dict'].update(peft_state_dict) - instance.on_load_checkpoint(checkpoint) - if hasattr(instance, 'setup_transformer_engine_tp_groups'): - instance.setup_transformer_engine_tp_groups() - - else: - if ( - self.peft_model_nemo_path is None and self.peft_model_ckpt_dir is None - ): # we have this check only for training PEFT from scratch - peft_state_dict = instance.get_peft_state_dict() - state_dict.update(peft_state_dict) - state_dict = self.modify_state_dict(conf, state_dict) - self.load_instance_with_state_dict(instance, state_dict, strict) - - logging.info(f'Model {instance.__class__.__name__} was successfully restored from {restore_path}.') - return instance - - class PipelineMixedPrecisionPlugin(MixedPrecisionPlugin): """ Overrides PTL autocasting to not wrap training/val/test_step. We do this because we have the megatron-core fwd/bwd functions in training_step. diff --git a/nemo/collections/nlp/parts/peft_config.py b/nemo/collections/nlp/parts/peft_config.py new file mode 100644 index 000000000000..dd75747fd73c --- /dev/null +++ b/nemo/collections/nlp/parts/peft_config.py @@ -0,0 +1,190 @@ +# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Dict + +from omegaconf import DictConfig + +try: + from nemo.collections.nlp.modules.common.megatron.adapters.mcore_mixins import ( + MCoreGPTEmbeddingMixin, + MCoreSelfAttentionMixin, + MCoreTransformerLayerMixin, + ) +except (ImportError, ModuleNotFoundError): + MCoreGPTEmbeddingMixin = MCoreSelfAttentionMixin = MCoreTransformerLayerMixin = None + +from nemo.collections.nlp.modules.common.megatron.adapters.parallel_adapters import ( + AdapterName, + InfusedAdapterConfig, + LoraKQVAdapterConfig, + LoraKQVAdapterWeightTyingConfig, + MLPInfusedAdapterConfig, + ParallelLinearAdapterConfig, + ParallelLinearAdapterWeightTyingConfig, + PromptEncoderAdapterConfig, +) + + +class PEFTConfig: + # superclass for adapter name and config + def __init__(self, peft_cfg: DictConfig, name_key_to_cfg: Dict): + self.name_key_to_cfg = name_key_to_cfg + + self.layer_selection = peft_cfg.get("layer_selection", None) + self.weight_tying = peft_cfg.get("weight_tying", False) + + def get_config_dict(self): + return self.name_key_to_cfg + + +class LoraPEFTConfig(PEFTConfig): + def __init__(self, cfg): + lora_cfg = cfg.peft.lora_tuning + if cfg.get("kv_channels", None) is None: + assert ( + cfg.hidden_size % cfg.num_attention_heads == 0 + ), 'hidden_size must be divisible by num_attention_heads if kv_channels is None' + kv_channels = cfg.hidden_size // cfg.num_attention_heads + else: + kv_channels = cfg.kv_channels + projection_size = kv_channels * cfg.num_attention_heads + + config_args = { + "in_features": cfg.hidden_size, + "out_features": 3 * projection_size, + "dim": lora_cfg.adapter_dim, + "norm_position": None, + "norm_type": None, + "activation": "identity", + "column_init_method": lora_cfg.get("column_init_method", "normal"), + "row_init_method": lora_cfg.get("row_init_method", "zero"), + "gather_output": False, + "dropout": lora_cfg.adapter_dropout, + } + + if lora_cfg.weight_tying: + position_embedding_strategy = lora_cfg.get("position_embedding_strategy", None) + if position_embedding_strategy is None: + dim_position_embeddings = 0 + elif position_embedding_strategy == "add": + dim_position_embeddings = cfg.hidden_size + elif position_embedding_strategy == "biasadd": + dim_position_embeddings = 3 * projection_size + elif position_embedding_strategy == "concat": + dim_position_embeddings = lora_cfg.adapter_dim + elif position_embedding_strategy == "mlpconcat": + dim_position_embeddings = lora_cfg.adapter_dim + else: + raise RuntimeError( + f"Unknown position embedding strategy {position_embedding_strategy} for tied weights" + ) + config_args.update( + { + "num_position_embeddings": cfg.num_layers, + "dim_position_embeddings": dim_position_embeddings, + "position_embedding_strategy": position_embedding_strategy, + } + ) + adapter_cfg = LoraKQVAdapterWeightTyingConfig(**config_args) + else: + adapter_cfg = LoraKQVAdapterConfig(**config_args) + + name_key_to_cfg = { + AdapterName.LORA_KQV_ADAPTER: adapter_cfg, + } + self.name_key_to_mcore_mixins = {AdapterName.LORA_KQV_ADAPTER: [("self_attention", MCoreSelfAttentionMixin)]} + + super().__init__(lora_cfg, name_key_to_cfg) + + +class IA3PEFTConfig(PEFTConfig): + def __init__(self, cfg): + mlp_infused_adapter_cfg = MLPInfusedAdapterConfig( + in_features=cfg.ffn_hidden_size // cfg.tensor_model_parallel_size + ) + infused_adapter_cfg = InfusedAdapterConfig(in_features=cfg.hidden_size // cfg.tensor_model_parallel_size) + + name_key_to_cfg = { + AdapterName.KEY_INFUSED: infused_adapter_cfg, + AdapterName.VALUE_INFUSED: infused_adapter_cfg, + AdapterName.MLP_INFUSED: mlp_infused_adapter_cfg, + } + + super().__init__(cfg.peft.ia3_tuning, name_key_to_cfg) + + +class PtuningPEFTConfig(PEFTConfig): + def __init__(self, cfg): + adapter_cfg = PromptEncoderAdapterConfig( + cfg.peft.p_tuning.virtual_tokens, + cfg.peft.p_tuning.bottleneck_dim, + cfg.peft.p_tuning.embedding_dim, + cfg.peft.p_tuning.init_std, + cfg.hidden_size, + ) + name_key_to_cfg = {AdapterName.PTUNING_ADAPTER: adapter_cfg} + self.name_key_to_mcore_mixins = {AdapterName.PTUNING_ADAPTER: [('embedding', MCoreGPTEmbeddingMixin)]} + self.virtual_tokens = cfg.peft.p_tuning.virtual_tokens + + super().__init__(cfg.peft.p_tuning, name_key_to_cfg) + + +class CanonicalAdaptersPEFTConfig(PEFTConfig): + def __init__(self, cfg): + adapter_tuning_cfg = cfg.peft.adapter_tuning + + config_args = { + "in_features": cfg.hidden_size, + "out_features": cfg.hidden_size, + "dim": adapter_tuning_cfg.adapter_dim, + "norm_position": adapter_tuning_cfg.get("norm_position", "pre"), + "norm_type": adapter_tuning_cfg.get("norm_type", "mixedfusedlayernorm"), + "column_init_method": adapter_tuning_cfg.get("column_init_method", "xavier"), + "row_init_method": adapter_tuning_cfg.get("row_init_method", "zero"), + "dropout": adapter_tuning_cfg.adapter_dropout, + } + + if adapter_tuning_cfg.weight_tying: + config_args.update( + { + "num_position_embeddings": cfg.num_layers * 2, + "dim_position_embeddings": cfg.hidden_size, + "position_embedding_strategy": adapter_tuning_cfg.get("position_embedding_strategy", None), + } + ) + adapter_cfg = ParallelLinearAdapterWeightTyingConfig(**config_args) + else: + adapter_cfg = ParallelLinearAdapterConfig(**config_args) + + name_key_to_cfg = { + AdapterName.PRE_ATTN_ADAPTER: adapter_cfg, + AdapterName.POST_ATTN_ADAPTER: adapter_cfg, + } + self.name_key_to_mcore_mixins = { + AdapterName.PRE_ATTN_ADAPTER: [("", MCoreTransformerLayerMixin)], + AdapterName.POST_ATTN_ADAPTER: [("", MCoreTransformerLayerMixin)], + } + + super().__init__(adapter_tuning_cfg, name_key_to_cfg) + + +PEFT_CONFIG_MAP = { + "adapter": CanonicalAdaptersPEFTConfig, + "ia3": IA3PEFTConfig, + "ptuning": PtuningPEFTConfig, + "lora": LoraPEFTConfig, + 'none': None, + None: None, +} diff --git a/nemo/core/classes/mixins/adapter_mixins.py b/nemo/core/classes/mixins/adapter_mixins.py index a7f94e90f9b9..2a05f374d464 100644 --- a/nemo/core/classes/mixins/adapter_mixins.py +++ b/nemo/core/classes/mixins/adapter_mixins.py @@ -42,6 +42,11 @@ def __post_init__(self): self.adapter_class_path = f'{self.adapter_class.__module__}.{self.adapter_class.__name__}' +class AdapterConfig: + # superclass for all adapter config dataclasses + pass + + def register_adapter(base_class: type, adapter_class: type): """ Registers a pair (Base class, Adapter class) into the adapter registry, used for de-referencing. @@ -144,8 +149,8 @@ class AdapterModuleMixin(ABC): metadata of the adapter config. .. note:: - - This module is **not** responsible for maintaining its config. Subclasses must ensure config is updated + + This module is **not** responsible for maintaining its config. Subclasses must ensure config is updated or preserved as needed. It is the responsibility of the subclasses to propagate the most up to date config to lower layers. """ @@ -153,7 +158,7 @@ class AdapterModuleMixin(ABC): adapter_global_cfg_key = "global_cfg" adapter_metadata_cfg_key = "adapter_meta_cfg" - def add_adapter(self, name: str, cfg: DictConfig, **kwargs): + def add_adapter(self, name: str, cfg: Union[DictConfig, AdapterConfig], **kwargs): """ Add an Adapter module to this module. @@ -520,7 +525,7 @@ def forward_single_enabled_adapter_( Perform the forward step of a single adapter module on some input data. .. note:: - + Subclasses can override this method to accommodate more complicate adapter forward steps. Args: @@ -608,7 +613,7 @@ def setup_adapters(self): f"Finished setup of adapter : '{full_adapter_name}'. Enabled: {adapter_cfg.get('enabled', True)}." ) - def add_adapter(self, name: str, cfg: DictConfig): + def add_adapter(self, name: str, cfg: Union[DictConfig, AdapterConfig]): """ Add an Adapter module to this model. @@ -758,7 +763,7 @@ def save_adapters(self, filepath: str, name: str = None): Utility method that saves only the adapter module(s), and not the entire model itself. This allows the sharing of adapters which are often just a fraction of the size of the full model, enabling easier deliver. - + .. note:: The saved file is a pytorch compatible pickle file, containing the state dicts of the adapter(s), @@ -840,7 +845,7 @@ def load_adapters(self, filepath: str, name: str = None, map_location: str = Non enabling easier deliver. .. note:: - + During restoration, assumes that the model does not currently already have an adapter with the name (if provided), or any adapter that shares a name with the state dict's modules (if name is not provided). This is to ensure that each adapter name is globally unique @@ -971,7 +976,7 @@ def adapter_module_names(self) -> List[str]: List of valid adapter modules that are supported by the model. .. note:: - + Subclasses should override this property and return a list of str names, of all the modules that they support, which will enable users to determine where to place the adapter modules. diff --git a/scripts/metric_calculation/peft_metric_calc.py b/scripts/metric_calculation/peft_metric_calc.py index 819d1f2a8c4c..ca13f83281c5 100755 --- a/scripts/metric_calculation/peft_metric_calc.py +++ b/scripts/metric_calculation/peft_metric_calc.py @@ -92,46 +92,35 @@ def metric_max_over_ground_truths(metric_fn, prediction, ground_truths): def main(): parser = argparse.ArgumentParser(description='Process some integers.') parser.add_argument( - '--ground-truth', - type=str, - help="ground truth .jsonl file made from /NeMo/scripts/dataset_processing/nlp/squad/prompt_learning_squad_preprocessing.py", - ) - parser.add_argument( - '--preds', + '--pred-file', type=str, help="Text file with test set prompts + model predictions. Prediction file can be made by running NeMo/examples/nlp/language_modeling/megatron_gpt_prompt_learning_eval.py", ) parser.add_argument( - '--split-string', + '--pred-field', type=str, - help="The text at the end of the prompt, write before the predicted answer. This will be used to find the model's predictions in pred files when the pred file containers both the prompt and prediction.", - default=None, - ) # If the pred file only has preditions, just pass none + help="The field in the json file that contains the prediction tokens", + default="pred", + ) parser.add_argument( - '--answer-field', + '--ground-truth-field', type=str, help="The field in the json file that contains the ground truth tokens", - default="answer", + default="original_answers", ) args = parser.parse_args() - ground_truth_file = args.ground_truth - pred_file = args.preds + pred_file = args.pred_file scorer = rouge_scorer.RougeScorer(['rougeL'], use_stemmer=True) preds = open(pred_file, encoding="utf-8").readlines() - ground_truth = open(ground_truth_file).readlines() f1 = exact_match = total = r_score = 0 for i in range(len(preds)): - truth = json.loads(ground_truth[i]) - pred_answer = json.loads(preds[i]) - - # Need to separate out preditions from prompt, spliting on the provided "split string" - if args.split_string is not None: - pred_answer = pred_answer["sentence"].split(args.split_string)[-1].strip() + pred_line = json.loads(preds[i]) - true_answers = truth[args.answer_field] + pred_answer = pred_line[args.pred_field] + true_answers = pred_line[args.ground_truth_field] if not isinstance(true_answers, list): true_answers = [true_answers] diff --git a/tutorials/nlp/lora.ipynb b/tutorials/nlp/lora.ipynb index fc79f74a6e2a..8603bbb62411 100644 --- a/tutorials/nlp/lora.ipynb +++ b/tutorials/nlp/lora.ipynb @@ -1,38 +1,45 @@ { "cells": [ + { + "cell_type": "markdown", + "source": [ + "Currently, this notebook must be run in a NeMo container.\n", + "An example command to launch the container:\n", + "```bash\n", + "docker run --gpus all -it --rm -v :/NeMo --shm-size=8g -p 8888:8888 -p 6006:6006 --ulimit memlock=-1 --ulimit stack=67108864 \n", + "```" + ], + "metadata": { + "collapsed": false + } + }, { "cell_type": "code", - "execution_count": 2, - "id": "b7a434f4", - "metadata": {}, + "execution_count": null, "outputs": [], "source": [ - "BRANCH='main'\n", - "import os\n", - "import wget" - ] + "# Update megatron version to the newest.\n", + "!cd /workspace && python -m pip install -e git+https://github.com/NVIDIA/Megatron-LM#egg=megatron-core" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", "execution_count": null, - "id": "developmental-gibraltar", - "metadata": {}, "outputs": [], "source": [ - "\"\"\"\n", - "You can run either this notebook locally (if you have all the dependencies and a GPU) or on Google Colab.\n", - "\n", - "Instructions for setting up Colab are as follows:\n", - "1. Open a new Python 3 notebook.\n", - "2. Import this notebook from GitHub (File -> Upload Notebook -> \"GITHUB\" tab -> copy/paste GitHub URL)\n", - "3. Connect to an instance with a GPU (Runtime -> Change runtime type -> select \"GPU\" for hardware accelerator)\n", - "4. Run this cell to set up dependencies.\n", - "\"\"\"\n", - "# If you're using Google Colab and not running locally, run this cell\n", - "\n", - "# install NeMo\n", - "!python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[nlp]" - ] + "%cd /NeMo/tutorials/nlp\n", + "BRANCH='main'\n", + "import os\n", + "import wget\n", + "import sys\n", + "sys.path.insert(0, \"../..\") # find the local nemo first before the installed nemo" + ], + "metadata": { + "collapsed": false + } }, { "attachments": {}, @@ -42,16 +49,15 @@ "source": [ "### Introduction\n", "\n", - "In this notebook we demonstrate how to use NeMo's implementation of LoRA (Low Rank Adaptation) for fine-tuning large language models. Our implementation is based on the [paper](https://openreview.net/pdf?id=nZeVKeeFYf9) by Hu et al.\n", + "This notebook demonstrates how to apply PEFT in NeMo. For brevity, we have chosen LoRA as the PEFT technique and GPT as the language model, but the same recipe can be used for other PEFT techniques and language models, as described in the [Training](#training) section.\n", + "\n", + " The implementation of LoRA is based on the paper, [LoRA: Low-Rank Adaptation of Large Language Models](https://openreview.net/pdf?id=nZeVKeeFYf9) by Hu et al.\n", + "\n", + "This example demonstrates how to:\n", "\n", - "We are going to show you how to:\n", - " \n", " 1. Train a LoRA model on a simple Extractive QA task.\n", " 2. Inspect the trained LoRA model showing the parameters it contains.\n", - " 3. Run inference with the based model with the LoRA parameters.\n", - " 4. Merge the LoRA parameters into the base model and run inference again on the merged model.\n", - "\n", - "In this tutorial we will be focusing on LoRA, but the training and evaluation methods described here will be applicable for other Parameter-efficient Fine tuning (PEFT) methods in NeMo." + " 3. Run inference with the base model with the LoRA parameters." ] }, { @@ -79,7 +85,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "0dbd41fd", "metadata": {}, "outputs": [], @@ -87,7 +93,9 @@ "# You can replace DATA_DIR and NEMO_DIR with your own locations\n", "DATA_DIR = \"data\"\n", "NEMO_DIR = \".\"\n", - "os.makedirs(DATA_DIR, exist_ok=True)" + "os.makedirs(DATA_DIR, exist_ok=True)\n", + "SQUAD_DIR = os.path.join(DATA_DIR, \"SQuAD\")\n", + "os.makedirs(SQUAD_DIR, exist_ok=True)" ] }, { @@ -102,19 +110,10 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "e72a1dc1", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "File ‘prompt_learning_squad_preprocessing.py’ already there; not retrieving.\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "# download the preprocessing scripts from github for the purpose of this tutorial\n", "! wget -nc https://raw.githubusercontent.com/NVIDIA/NeMo/{BRANCH}/scripts/dataset_processing/nlp/squad/prompt_learning_squad_preprocessing.py" @@ -131,43 +130,11 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "fa16d8ac", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--2023-05-30 14:07:23-- https://rajpurkar.github.io/SQuAD-explorer/dataset/train-v1.1.json\n", - "Resolving rajpurkar.github.io (rajpurkar.github.io)... 185.199.109.153, 185.199.111.153, 185.199.108.153, ...\n", - "Connecting to rajpurkar.github.io (rajpurkar.github.io)|185.199.109.153|:443... connected.\n", - "HTTP request sent, awaiting response... 200 OK\n", - "Length: 30288272 (29M) [application/json]\n", - "Saving to: ‘train-v1.1.json’\n", - "\n", - "train-v1.1.json 100%[===================>] 28.88M 84.3MB/s in 0.3s \n", - "\n", - "2023-05-30 14:07:25 (84.3 MB/s) - ‘train-v1.1.json’ saved [30288272/30288272]\n", - "\n", - "--2023-05-30 14:07:26-- https://rajpurkar.github.io/SQuAD-explorer/dataset/dev-v1.1.json\n", - "Resolving rajpurkar.github.io (rajpurkar.github.io)... 185.199.110.153, 185.199.108.153, 185.199.111.153, ...\n", - "Connecting to rajpurkar.github.io (rajpurkar.github.io)|185.199.110.153|:443... connected.\n", - "HTTP request sent, awaiting response... 200 OK\n", - "Length: 4854279 (4.6M) [application/json]\n", - "Saving to: ‘dev-v1.1.json’\n", - "\n", - "dev-v1.1.json 100%[===================>] 4.63M --.-KB/s in 0.1s \n", - "\n", - "2023-05-30 14:07:27 (43.8 MB/s) - ‘dev-v1.1.json’ saved [4854279/4854279]\n", - "\n" - ] - } - ], + "outputs": [], "source": [ - "SQUAD_DIR = os.path.join(DATA_DIR, \"SQuAD\")\n", - "os.makedirs(SQUAD_DIR, exist_ok=True)\n", - "\n", "# Download the SQuAD dataset\n", "!wget -nc https://rajpurkar.github.io/SQuAD-explorer/dataset/train-v1.1.json\n", "!wget -nc https://rajpurkar.github.io/SQuAD-explorer/dataset/dev-v1.1.json\n", @@ -177,25 +144,10 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "64e3e25b", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving train split to data/SQuAD/squad_train.jsonl\n", - "100%|█████████████████████████████████| 87599/87599 [00:00<00:00, 204336.27it/s]\n", - "Saving val split to data/SQuAD/squad_val.jsonl\n", - "100%|█████████████████████████████████| 10570/10570 [00:00<00:00, 158654.55it/s]\n", - "Saving test split to data/SQuAD/squad_test_ground_truth.jsonl\n", - "100%|█████████████████████████████████| 10570/10570 [00:00<00:00, 183040.92it/s]\n", - "Saving test split to data/SQuAD/squad_test.jsonl\n", - "100%|█████████████████████████████████| 10570/10570 [00:00<00:00, 196367.94it/s]\n" - ] - } - ], + "outputs": [], "source": [ "# Preprocess squad data\n", "!python prompt_learning_squad_preprocessing.py --sft-format --data-dir {SQUAD_DIR}" @@ -203,25 +155,10 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "b562d1de", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\"input\": \"User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24\\u201310 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \\\"golden anniversary\\\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \\\"Super Bowl L\\\"), so that the logo could prominently feature the Arabic numerals 50. Question:Which NFL team represented the AFC at Super Bowl 50?\\n\\nAssistant:\", \"output\": \"Denver Broncos\"}\n", - "{\"input\": \"User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24\\u201310 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \\\"golden anniversary\\\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \\\"Super Bowl L\\\"), so that the logo could prominently feature the Arabic numerals 50. Question:Which NFL team represented the NFC at Super Bowl 50?\\n\\nAssistant:\", \"output\": \"Carolina Panthers\"}\n", - "{\"input\": \"User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24\\u201310 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \\\"golden anniversary\\\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \\\"Super Bowl L\\\"), so that the logo could prominently feature the Arabic numerals 50. Question:Where did Super Bowl 50 take place?\\n\\nAssistant:\", \"output\": \"Santa Clara, California\"}\n", - "{\"input\": \"User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24\\u201310 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \\\"golden anniversary\\\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \\\"Super Bowl L\\\"), so that the logo could prominently feature the Arabic numerals 50. Question:Which NFL team won Super Bowl 50?\\n\\nAssistant:\", \"output\": \"Denver Broncos\"}\n", - "{\"input\": \"User: Context:Architecturally, the school has a Catholic character. Atop the Main Building's gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend \\\"Venite Ad Me Omnes\\\". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary. Question:To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France?\\n\\nAssistant:\", \"output\": \"Saint Bernadette Soubirous\"}\n", - "{\"input\": \"User: Context:Architecturally, the school has a Catholic character. Atop the Main Building's gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend \\\"Venite Ad Me Omnes\\\". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary. Question:What is in front of the Notre Dame Main Building?\\n\\nAssistant:\", \"output\": \"a copper statue of Christ\"}\n", - "{\"input\": \"User: Context:Architecturally, the school has a Catholic character. Atop the Main Building's gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend \\\"Venite Ad Me Omnes\\\". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary. Question:The Basilica of the Sacred heart at Notre Dame is beside to which structure?\\n\\nAssistant:\", \"output\": \"the Main Building\"}\n", - "{\"input\": \"User: Context:Architecturally, the school has a Catholic character. Atop the Main Building's gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend \\\"Venite Ad Me Omnes\\\". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary. Question:What is the Grotto at Notre Dame?\\n\\nAssistant:\", \"output\": \"a Marian place of prayer and reflection\"}\n" - ] - } - ], + "outputs": [], "source": [ "# What the squad dataset looks like after processing\n", "! head -200 $SQUAD_DIR/squad_train.jsonl > $SQUAD_DIR/squad_short_train.jsonl\n", @@ -237,12 +174,14 @@ "metadata": {}, "source": [ "### Model Config Setup\n", - "Now we will begin setting up the config file needed for PEFT tuning. We use a single config for all supported PEFT methods (LoRA, Adapter and P-Tuning). All PEFT methods use classes defined in [megatron_gpt_peft_models.py](https://github.com/NVIDIA/NeMo/blob/main/nemo/collections/nlp/models/language_modeling/megatron_gpt_peft_models.py). All PEFT Classes inherit from `MegatronGPTSFTModel` which is the class that governs instruction tuning." + "Now we will begin setting up the config file needed for PEFT tuning. We use a single config for all supported PEFT methods (LoRA, Adapter, IA3 and P-Tuning, as well as combinations of these). All PEFT methods use the GPT finetuning class `MegatronGPTSFTModel` as the frozen base network, and use the `add_adapter()` method to add adapter weights for PEFT.\n", + "\n", + "Let's create a config object for LoRA training." ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "5749c387", "metadata": {}, "outputs": [], @@ -266,15 +205,15 @@ "id": "ce966bcf", "metadata": {}, "source": [ - "The `config` contains several attributes required by the `MegatronGPTPEFTModel`. First we will set the training data path and the validation data path in the config.\n", - "The `config` allows us to set a list of `jsonl` files as training files and sample examples from each file with different probabilities. For simplicity we are going to use just one training file and thus the sampling probability is set to `1.0`\n", + "The `config` contains several attributes required by the `MegatronGPTSFTModel`. First we will set the training data path and the validation data path in the config.\n", + "The `config` allows us to set a list of `jsonl` files as training files and sample examples from each file with different probabilities. For simplicity, we are going to use just one training file and thus the sampling probability is set to `1.0`\n", "\n", "We can also monitor validation loss from multiple validation files during training. Again for simplicity we will use just one validation file." ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "6bb1590f", "metadata": {}, "outputs": [], @@ -292,7 +231,7 @@ "metadata": {}, "source": [ "### PEFT Config\n", - "The attribute [config.model.peft](https://github.com/NVIDIA/NeMo/blob/main/examples/nlp/language_modeling/tuning/conf/megatron_gpt_peft_tuning_config.yaml#L78) contains settings that control the PEFT training method and its related hyperpameters. We currently support `lora`, `adapters`, `ptuning` and `ia3`. We can instruct the training script to use one of these methods by setting the config.model.peft.peft_scheme attribute.\n", + "The attribute [config.model.peft](https://github.com/NVIDIA/NeMo/blob/main/examples/nlp/language_modeling/tuning/conf/megatron_gpt_peft_tuning_config.yaml#L78) contains settings that control the PEFT training method and its related hyperpameters. We currently support `lora`, `adapter`, `ptuning` and `ia3`. We can instruct the training script to use one of these methods by setting the config.model.peft.peft_scheme attribute.\n", "\n", "The other hyperparams associated with lora tuning are present in the [config.model.peft.lora_tuning](https://github.com/NVIDIA/NeMo/blob/main/examples/nlp/language_modeling/tuning/conf/megatron_gpt_peft_tuning_config.yaml#L92) attribute." ] @@ -324,12 +263,12 @@ "metadata": {}, "source": [ "### Prompt Formatting\n", - "The `config.model.data.train_ds.prompt_template` attribute allows us to further tweak the format of the input and output if needed. In this example, we have \"encoding\" our format inside the `jsonl` file directly. So we can keep the `prompt_template` in the config simple.(See previous section on Data Preparation). " + "The `config.model.data.train_ds.prompt_template` attribute allows us to further tweak the format of the input and output if needed. In this example, we have already incorporated our format inside the `jsonl` file during preprocessing, so we can keep the `prompt_template` in the config simple. (See previous section on Data Preparation)." ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "1b6aa5c7", "metadata": {}, "outputs": [], @@ -349,29 +288,10 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "48cdf868", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[NeMo W 2023-05-30 14:08:23 experimental:27] Module is experimental, not ready for production and is not fully supported. Use at your own risk.\n", - "[NeMo W 2023-05-30 14:08:24 experimental:27] Module is experimental, not ready for production and is not fully supported. Use at your own risk.\n" - ] - }, - { - "data": { - "text/plain": [ - "'https://api.ngc.nvidia.com/v2/models/nvidia/nemo/megatron_gpt_345m/versions/1/files/megatron_gpt_345m.nemo'" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# Check what GPT .nemo models we have available on NGC\n", "from nemo.collections.nlp.models.language_modeling.megatron_gpt_model import MegatronGPTModel\n", @@ -391,26 +311,28 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "364439a1", "metadata": { "scrolled": true }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "File ‘./megatron_gpt_345m.nemo’ already there; not retrieving.\n" - ] - } - ], + "outputs": [], "source": [ "# Download the model from NGC\n", - "gpt_file_name = \"megatron_gpt_345m.nemo\"\n", - "!wget -nc --content-disposition {megatron_gpt_345m_nemo_url} -O {NEMO_DIR}/{gpt_file_name}" + "gpt_file_name = \"megatron_gpt_345m.nemo\"" ] }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "!wget -nc --content-disposition {megatron_gpt_345m_nemo_url} -O {NEMO_DIR}/{gpt_file_name}" + ], + "metadata": { + "collapsed": false + } + }, { "attachments": {}, "cell_type": "markdown", @@ -422,7 +344,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "2778a5fa", "metadata": {}, "outputs": [], @@ -442,7 +364,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "id": "a278cbdf", "metadata": {}, "outputs": [], @@ -468,151 +390,12 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "12a37ada", "metadata": { "scrolled": true }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "seed: 1234\n", - "tensor_model_parallel_size: 1\n", - "pipeline_model_parallel_size: 1\n", - "global_batch_size: 4\n", - "micro_batch_size: 1\n", - "restore_from_path: megatron_gpt_345m.nemo\n", - "resume_from_checkpoint: null\n", - "save_nemo_on_validation_end: false\n", - "sync_batch_comm: false\n", - "megatron_amp_O2: false\n", - "sequence_parallel: false\n", - "activations_checkpoint_granularity: null\n", - "activations_checkpoint_method: null\n", - "activations_checkpoint_num_layers: null\n", - "answer_only_loss: true\n", - "gradient_as_bucket_view: false\n", - "hidden_dropout: 0.0\n", - "attention_dropout: 0.0\n", - "ffn_dropout: 0.0\n", - "peft:\n", - " peft_scheme: adapter\n", - " restore_from_path: null\n", - " adapter_tuning:\n", - " type: parallel_adapter\n", - " adapter_dim: 32\n", - " adapter_dropout: 0.0\n", - " norm_position: pre\n", - " column_init_method: xavier\n", - " row_init_method: zero\n", - " norm_type: mixedfusedlayernorm\n", - " lora_tuning:\n", - " adapter_dim: 32\n", - " adapter_dropout: 0.0\n", - " column_init_method: xavier\n", - " row_init_method: zero\n", - " p_tuning:\n", - " virtual_tokens: 10\n", - " bottleneck_dim: 1024\n", - " embedding_dim: 1024\n", - " init_std: 0.023\n", - "data:\n", - " train_ds:\n", - " file_names:\n", - " - data/SQuAD/squad_short_train.jsonl\n", - " global_batch_size: ${model.global_batch_size}\n", - " micro_batch_size: ${model.micro_batch_size}\n", - " shuffle: true\n", - " num_workers: 0\n", - " pin_memory: true\n", - " max_seq_length: 2048\n", - " min_seq_length: 1\n", - " drop_last: true\n", - " concat_sampling_probabilities:\n", - " - 1.0\n", - " context_key: input\n", - " label_key: output\n", - " add_eos: true\n", - " add_sep: false\n", - " add_bos: false\n", - " separate_prompt_and_response_with_newline: false\n", - " truncation_field: context\n", - " index_mapping_dir: null\n", - " prompt_template: '{input} {output}'\n", - " validation_ds:\n", - " file_names:\n", - " - data/SQuAD/squad_short_val.jsonl\n", - " names:\n", - " - squad_val\n", - " global_batch_size: ${model.global_batch_size}\n", - " micro_batch_size: ${model.micro_batch_size}\n", - " shuffle: false\n", - " num_workers: 0\n", - " pin_memory: true\n", - " max_seq_length: 2048\n", - " min_seq_length: 1\n", - " drop_last: false\n", - " context_key: input\n", - " label_key: output\n", - " add_eos: ${model.data.train_ds.add_eos}\n", - " add_sep: ${model.data.train_ds.add_sep}\n", - " add_bos: ${model.data.train_ds.add_bos}\n", - " separate_prompt_and_response_with_newline: ${model.data.train_ds.separate_prompt_and_response_with_newline}\n", - " write_predictions_to_file: false\n", - " output_file_path_prefix: null\n", - " truncation_field: context\n", - " index_mapping_dir: null\n", - " prompt_template: ${model.data.train_ds.prompt_template}\n", - " metric:\n", - " name: loss\n", - " average: null\n", - " num_classes: null\n", - "test_ds:\n", - " file_names: null\n", - " names: null\n", - " global_batch_size: ${model.global_batch_size}\n", - " micro_batch_size: ${model.micro_batch_size}\n", - " shuffle: false\n", - " num_workers: 4\n", - " pin_memory: true\n", - " max_seq_length: 2048\n", - " min_seq_length: 1\n", - " drop_last: false\n", - " context_key: input\n", - " label_key: output\n", - " add_eos: ${model.data.train_ds.add_eos}\n", - " add_sep: ${model.data.train_ds.add_sep}\n", - " add_bos: ${model.data.train_ds.add_bos}\n", - " separate_prompt_and_response_with_newline: ${model.data.train_ds.separate_prompt_and_response_with_newline}\n", - " write_predictions_to_file: false\n", - " output_file_path_prefix: null\n", - " truncation_field: context\n", - " index_mapping_dir: null\n", - " prompt_template: ${model.data.train_ds.prompt_template}\n", - " metric:\n", - " name: loss\n", - " average: null\n", - " num_classes: null\n", - "optim:\n", - " name: fused_adam\n", - " lr: 0.0001\n", - " weight_decay: 0.01\n", - " betas:\n", - " - 0.9\n", - " - 0.98\n", - " sched:\n", - " name: CosineAnnealing\n", - " warmup_steps: 50\n", - " min_lr: 0.0\n", - " constant_steps: 0\n", - " monitor: val_loss\n", - " reduce_on_plateau: false\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "# Final model config\n", "print(OmegaConf.to_yaml(config.model))" @@ -632,49 +415,15 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "id": "90f85b2a", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Using 16bit None Automatic Mixed Precision (AMP)\n", - "GPU available: True (cuda), used: True\n", - "TPU available: False, using: 0 TPU cores\n", - "IPU available: False, using: 0 IPUs\n", - "HPU available: False, using: 0 HPUs\n", - "`Trainer(val_check_interval=1.0)` was configured so validation will run at the end of the training epoch..\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Trainer config - \n", - "\n", - "devices: 1\n", - "accelerator: gpu\n", - "num_nodes: 1\n", - "precision: 16\n", - "logger: false\n", - "enable_checkpointing: false\n", - "replace_sampler_ddp: false\n", - "max_epochs: 4\n", - "max_steps: 100\n", - "log_every_n_steps: 10\n", - "val_check_interval: 1.0\n", - "gradient_clip_val: 1.0\n", - "\n" - ] - } - ], + "outputs": [], "source": [ + "from nemo.collections.nlp.parts.nlp_overrides import NLPDDPStrategy\n", "import torch\n", "import pytorch_lightning as pl\n", - "from nemo.collections.nlp.parts.nlp_overrides import NLPDDPStrategy\n", - "from pytorch_lightning.plugins.environments import TorchElasticEnvironment\n", + "from nemo.collections.nlp.parts.megatron_trainer_builder import MegatronTrainerBuilder\n", "\n", "# let's modify some trainer configs\n", "# check if we have GPU available and uses it\n", @@ -688,16 +437,11 @@ "config.trainer.precision = 16 if torch.cuda.is_available() else 32\n", "\n", "# setup cluster environment parameters\"\n", - "# use torch elastic cluster environment so `create_process_externally` is True\n", - "# the launcher is set to None. It will not try to spawn new processes.\n", - "# It won't create the misconfiguration error because of the `interactive session`\n", "os.environ[\"LOCAL_RANK\"] = '0'\n", "os.environ[\"RANK\"] = '0'\n", "os.environ[\"WORLD_SIZE\"] = '1'\n", "\n", - "strategy = NLPDDPStrategy(find_unused_parameters=False, no_ddp_communication_hook=True)\n", - "plugins = [TorchElasticEnvironment()]\n", - "trainer = pl.Trainer(plugins= plugins, strategy=strategy, **config.trainer)\n", + "trainer = MegatronTrainerBuilder(config).create_trainer()\n", "\n", "print(\"Trainer config - \\n\")\n", "print(OmegaConf.to_yaml(config.trainer))" @@ -726,41 +470,10 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "id": "f2c943ba", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[NeMo E 2023-05-30 14:09:17 exp_manager:646] exp_manager received explicit_log_dir: training_info and at least one of exp_dir: ./peft_lora, or version: None. Please note that exp_dir, name, and version will be ignored.\n", - "[NeMo W 2023-05-30 14:09:17 exp_manager:651] Exp_manager is logging to training_info, but it already exists.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[NeMo I 2023-05-30 14:09:17 exp_manager:374] Experiments will be logged at training_info\n", - "[NeMo I 2023-05-30 14:09:17 exp_manager:797] TensorboardLogger has been set up\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[NeMo W 2023-05-30 14:09:17 exp_manager:893] The checkpoint callback was told to monitor a validation value and trainer's max_steps was set to 100. Please ensure that max_steps will run for at least 1 epochs to ensure that checkpointing will not error out.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "training_info\n" - ] - } - ], + "outputs": [], "source": [ "from nemo.utils.exp_manager import exp_manager\n", "\n", @@ -780,423 +493,71 @@ "id": "298b3dce", "metadata": {}, "source": [ - "### LoRA Training\n", - "We now set up the process for training a LoRA model. We first require a config that contains details about the base language model upon which we will train our LoRA model. So we first extract the `base_model_cfg`" + "### Training\n", + "We now set up the process for training a LoRA model. We first require a config that contains details about the base language model upon which we will train our LoRA model. So we first extract the `model_cfg` from the checkpoint and update it with any new settings we employ in our current (LoRA) `config`. These are combined in the `merge_cfg_with` function.\n", + "\n" ] }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "id": "edb38445", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[NeMo W 2023-05-30 14:09:30 experimental:27] Module is experimental, not ready for production and is not fully supported. Use at your own risk.\n" - ] - } - ], + "outputs": [], "source": [ - "from nemo.collections.nlp.models.language_modeling.megatron_gpt_sft_model import MegatronGPTModel\n", - "from nemo.collections.nlp.parts.nlp_overrides import NLPSaveRestoreConnector, PEFTSaveRestoreConnector\n", - "base_model_save_restore_connector = NLPSaveRestoreConnector()\n", - "base_model_cfg = MegatronGPTModel.restore_from(\n", - " restore_path=config.model.restore_from_path,\n", - " trainer=trainer,\n", - " return_config=True,\n", - " save_restore_connector=base_model_save_restore_connector,\n", - " )" + "from nemo.collections.nlp.models.language_modeling.megatron_gpt_sft_model import MegatronGPTSFTModel\n", + "\n", + "model_cfg = MegatronGPTSFTModel.merge_cfg_with(config.model.restore_from_path, config)" ] }, { "attachments": {}, "cell_type": "markdown", - "id": "16bace39", + "id": "dfc55a1c", "metadata": {}, "source": [ - "Next, we update the `base_model_cfg` with any new settings we employ in our current (LoRA) `config`." + "Next, we instantiate the GPT model class and add the LoRA adapter\n", + "When we call `add_adapter`, the model prints out the parameter count before and after the operation. We can clearly see the number of trainable parameters increase after adding the adapter.\n", + "To print the parameter count manually, we can call `model.summarize()`." ] }, { "cell_type": "code", - "execution_count": 19, - "id": "fd350dbc", + "execution_count": null, + "id": "a81d8741", "metadata": {}, "outputs": [], "source": [ - "from omegaconf.omegaconf import open_dict\n", - "from nemo.collections.nlp.models.language_modeling.megatron_gpt_peft_models import MegatronGPTLoRAModel\n", - "OmegaConf.set_struct(base_model_cfg, True)\n", - "OmegaConf.resolve(config)\n", - "with open_dict(base_model_cfg):\n", - " base_model_cfg.megatron_amp_O2 = config.model.get('megatron_amp_O2', False)\n", - " base_model_cfg.micro_batch_size = config.model.data.train_ds.micro_batch_size\n", - " base_model_cfg.global_batch_size = config.model.data.train_ds.global_batch_size\n", - " base_model_cfg.sequence_parallel = config.model.get(\"sequence_parallel\", False)\n", - " base_model_cfg.data = config.model.data\n", - " base_model_cfg.optim = config.model.optim\n", - " base_model_cfg.precision = config.trainer.precision\n", - " base_model_cfg.answer_only_loss = config.model.answer_only_loss\n", - " base_model_cfg.restore_from_path = config.model.restore_from_path\n", - " base_model_cfg.resume_from_checkpoint = config.model.resume_from_checkpoint\n", - " base_model_cfg.save_nemo_on_validation_end = config.model.save_nemo_on_validation_end\n", - " base_model_cfg.peft = config.model.peft\n", - " base_model_cfg.target = f\"{MegatronGPTLoRAModel.__module__}.{MegatronGPTLoRAModel.__name__}\"" + "from nemo.collections.nlp.parts.peft_config import LoraPEFTConfig\n", + "\n", + "model = MegatronGPTSFTModel.restore_from(config.model.restore_from_path, model_cfg, trainer=trainer)\n", + "model.add_adapter(LoraPEFTConfig(model_cfg))\n", + "# print(\"Parameter count manually:\\n\", model.summarize())" ] }, { - "attachments": {}, "cell_type": "markdown", - "id": "dfc55a1c", - "metadata": {}, "source": [ - "Next, we instantiate the LoRA model class" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "a81d8741", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[NeMo I 2023-05-30 14:09:39 megatron_init:232] Rank 0 has data parallel group: [0]\n", - "[NeMo I 2023-05-30 14:09:39 megatron_init:235] All data parallel group ranks: [[0]]\n", - "[NeMo I 2023-05-30 14:09:39 megatron_init:236] Ranks 0 has data parallel rank: 0\n", - "[NeMo I 2023-05-30 14:09:39 megatron_init:244] Rank 0 has model parallel group: [0]\n", - "[NeMo I 2023-05-30 14:09:39 megatron_init:245] All model parallel group ranks: [[0]]\n", - "[NeMo I 2023-05-30 14:09:39 megatron_init:255] Rank 0 has tensor model parallel group: [0]\n", - "[NeMo I 2023-05-30 14:09:39 megatron_init:259] All tensor model parallel group ranks: [[0]]\n", - "[NeMo I 2023-05-30 14:09:39 megatron_init:260] Rank 0 has tensor model parallel rank: 0\n", - "[NeMo I 2023-05-30 14:09:39 megatron_init:274] Rank 0 has pipeline model parallel group: [0]\n", - "[NeMo I 2023-05-30 14:09:39 megatron_init:286] Rank 0 has embedding group: [0]\n", - "[NeMo I 2023-05-30 14:09:39 megatron_init:292] All pipeline model parallel group ranks: [[0]]\n", - "[NeMo I 2023-05-30 14:09:39 megatron_init:293] Rank 0 has pipeline model parallel rank 0\n", - "[NeMo I 2023-05-30 14:09:39 megatron_init:294] All embedding group ranks: [[0]]\n", - "[NeMo I 2023-05-30 14:09:39 megatron_init:295] Rank 0 has embedding rank: 0\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[NeMo W 2023-05-30 14:09:39 modelPT:244] You tried to register an artifact under config key=tokenizer.vocab_file but an artifact for it has already been registered.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[NeMo I 2023-05-30 14:09:39 tokenizer_utils:204] Getting Megatron tokenizer for pretrained model name: megatron-gpt-345m, custom vocab file: /tmp/tmp1qljai9b/bfcdca5e44814366bdb5dcd651325152_gpt2-vocab.json, and merges file: /tmp/tmp1qljai9b/315a11fd68be49d6abdb34363e8c4997_gpt2-merge.txt\n", - "[NeMo I 2023-05-30 14:09:39 tokenizer_utils:130] Getting HuggingFace AutoTokenizer with pretrained_model_name: gpt2, vocab_file: /tmp/tmp1qljai9b/bfcdca5e44814366bdb5dcd651325152_gpt2-vocab.json, merges_files: /tmp/tmp1qljai9b/315a11fd68be49d6abdb34363e8c4997_gpt2-merge.txt, special_tokens_dict: {}, and use_fast: False\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Using sep_token, but it is not set yet.\n", - "Using cls_token, but it is not set yet.\n", - "Using pad_token, but it is not set yet.\n", - "Using mask_token, but it is not set yet.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[NeMo I 2023-05-30 14:09:40 megatron_base_model:238] Padded vocab_size: 50304, original vocab_size: 50257, dummy tokens: 47.\n", - "[NeMo I 2023-05-30 14:09:41 megatron_gpt_peft_models:56] Before adding PEFT params:\n", - " | Name | Type | Params\n", - " -----------------------------------\n", - " 0 | model | GPTModel | 354 M \n", - " -----------------------------------\n", - " 354 M Trainable params\n", - " 0 Non-trainable params\n", - " 354 M Total params\n", - " 1,419.485 Total estimated model params size (MB)\n", - "[NeMo I 2023-05-30 14:09:41 megatron_gpt_peft_models:65] After adding PEFT params:\n", - " | Name | Type | Params\n", - " -----------------------------------\n", - " 0 | model | GPTModel | 358 M \n", - " -----------------------------------\n", - " 358 M Trainable params\n", - " 0 Non-trainable params\n", - " 358 M Total params\n", - " 1,432.068 Total estimated model params size (MB)\n", - "[NeMo I 2023-05-30 14:09:42 nlp_overrides:491] Model MegatronGPTLoRAModel was successfully restored from /home/adithyare/NeMo/tutorials/nlp/megatron_gpt_345m.nemo.\n" - ] - } + "Simply substitute with the `MegatronT5SFTModel` class to use T5 instead of GPT.\n", + "\n", + "To use a different PEFT method, you can use a different config class in place of `LoraPEFTConfig`, such as `CanonicalAdaptersPEFTConfig`, `IA3PEFTConfig`, `PtuningPEFTConfig`. You can also use a combination of the methods by passing in a list:\n", + "`model.add_adapter([LoraPEFTConfig(model_cfg), PtuningPEFTConfig(model_cfg)])`\n", + "\n", + "We're now ready to start training." ], - "source": [ - "from nemo.collections.nlp.parts.nlp_overrides import PEFTSaveRestoreConnector\n", - "peft_save_restore_connector = PEFTSaveRestoreConnector(\n", - " peft_model_nemo_path=None, peft_model_ckpt_path=None\n", - " )\n", - "model = MegatronGPTLoRAModel.restore_from(\n", - " restore_path=config.model.restore_from_path,\n", - " trainer=trainer,\n", - " override_config_path=base_model_cfg,\n", - " save_restore_connector=peft_save_restore_connector,\n", - ")" - ] + "metadata": { + "collapsed": false + } }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "id": "2d99f433", "metadata": { "scrolled": true }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[NeMo W 2023-05-30 14:09:46 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/pytorch_lightning/trainer/configuration_validator.py:175: UserWarning: The `batch_idx` argument in `MegatronGPTLoRAModel.on_train_batch_start` hook may not match with the actual batch index when using a `dataloader_iter` argument in your `training_step`.\n", - " rank_zero_warn(\n", - " \n", - "[NeMo W 2023-05-30 14:09:46 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/pytorch_lightning/trainer/configuration_validator.py:175: UserWarning: The `batch_idx` argument in `MegatronGPTLoRAModel.on_train_batch_end` hook may not match with the actual batch index when using a `dataloader_iter` argument in your `training_step`.\n", - " rank_zero_warn(\n", - " \n", - "[NeMo W 2023-05-30 14:09:46 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/lightning_fabric/plugins/environments/torchelastic.py:36: UserWarning: MASTER_ADDR environment variable is not defined. Set as localhost\n", - " rank_zero_warn(\"MASTER_ADDR environment variable is not defined. Set as localhost\")\n", - " \n", - "[NeMo W 2023-05-30 14:09:46 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/lightning_fabric/plugins/environments/torchelastic.py:44: UserWarning: MASTER_PORT environment variable is not defined. Set as 12910\n", - " rank_zero_warn(\"MASTER_PORT environment variable is not defined. Set as 12910\")\n", - " \n", - "Initializing distributed: GLOBAL_RANK: 0, MEMBER: 1/1\n", - "----------------------------------------------------------------------------------------------------\n", - "distributed_backend=nccl\n", - "All distributed processes registered. Starting with 1 processes\n", - "----------------------------------------------------------------------------------------------------\n", - "\n", - "You are using a CUDA device ('NVIDIA RTX A6000') that has Tensor Cores. To properly utilize them, you should set `torch.set_float32_matmul_precision('medium' | 'high')` which will trade-off precision for performance. For more details, read https://pytorch.org/docs/stable/generated/torch.set_float32_matmul_precision.html#torch.set_float32_matmul_precision\n", - "[NeMo W 2023-05-30 14:09:46 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/pytorch_lightning/callbacks/model_checkpoint.py:613: UserWarning: Checkpoint directory /home/adithyare/NeMo/tutorials/nlp/training_info/checkpoints exists and is not empty.\n", - " rank_zero_warn(f\"Checkpoint directory {dirpath} exists and is not empty.\")\n", - " \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[NeMo I 2023-05-30 14:09:46 megatron_gpt_sft_model:634] Building GPT SFT validation datasets.\n", - "[NeMo I 2023-05-30 14:09:46 text_memmap_dataset:104] Building data files\n", - "[NeMo I 2023-05-30 14:09:46 text_memmap_dataset:343] Processing 1 data files using 12 workers\n", - "[NeMo I 2023-05-30 14:09:47 text_memmap_dataset:349] Time building 0 / 1 mem-mapped files: 0:00:00.360761\n", - "[NeMo I 2023-05-30 14:09:47 text_memmap_dataset:114] Loading data files\n", - "[NeMo I 2023-05-30 14:09:47 text_memmap_dataset:205] Loading data/SQuAD/squad_short_val.jsonl\n", - "[NeMo I 2023-05-30 14:09:47 text_memmap_dataset:117] Time loading 1 mem-mapped files: 0:00:00.002361\n", - "[NeMo I 2023-05-30 14:09:47 text_memmap_dataset:121] Computing global indices\n", - "[NeMo I 2023-05-30 14:09:47 megatron_gpt_sft_model:637] Length of val dataset: 20\n", - "[NeMo I 2023-05-30 14:09:47 megatron_gpt_sft_model:648] Building GPT SFT traing datasets.\n", - "[NeMo I 2023-05-30 14:09:47 text_memmap_dataset:104] Building data files\n", - "[NeMo I 2023-05-30 14:09:47 text_memmap_dataset:343] Processing 1 data files using 12 workers\n", - "[NeMo I 2023-05-30 14:09:47 text_memmap_dataset:349] Time building 0 / 1 mem-mapped files: 0:00:00.299554\n", - "[NeMo I 2023-05-30 14:09:47 text_memmap_dataset:114] Loading data files\n", - "[NeMo I 2023-05-30 14:09:47 text_memmap_dataset:205] Loading data/SQuAD/squad_short_train.jsonl\n", - "[NeMo I 2023-05-30 14:09:47 text_memmap_dataset:117] Time loading 1 mem-mapped files: 0:00:00.001065\n", - "[NeMo I 2023-05-30 14:09:47 text_memmap_dataset:121] Computing global indices\n", - "[NeMo I 2023-05-30 14:09:47 dataset_utils:1341] > loading indexed mapping from data/SQuAD/squad_short_train.jsonl_squad_short_train.jsonl_indexmap_402mns_2046msl_0.00ssp_1234s.npy\n", - "[NeMo I 2023-05-30 14:09:47 dataset_utils:1344] loaded indexed file in 0.001 seconds\n", - "[NeMo I 2023-05-30 14:09:47 dataset_utils:1345] total number of samples: 600\n", - "make: Entering directory '/home/adithyare/NeMo/nemo/collections/nlp/data/language_modeling/megatron'\n", - "make: Nothing to be done for 'default'.\n", - "make: Leaving directory '/home/adithyare/NeMo/nemo/collections/nlp/data/language_modeling/megatron'\n", - "[NeMo I 2023-05-30 14:09:47 blendable_dataset:67] > elapsed time for building blendable dataset indices: 0.09 (sec)\n", - "> building indices for blendable datasets ...\n", - " > sample ratios:\n", - " dataset 0, input: 1, achieved: 1\n", - "[NeMo I 2023-05-30 14:09:47 megatron_gpt_sft_model:650] Length of train dataset: 402\n", - "[NeMo I 2023-05-30 14:09:47 megatron_gpt_sft_model:655] Building dataloader with consumed samples: 0\n", - "[NeMo I 2023-05-30 14:09:47 megatron_gpt_sft_model:655] Building dataloader with consumed samples: 0\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[NeMo I 2023-05-30 14:09:47 nlp_overrides:124] Configuring DDP for model parallelism.\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 adapter_mixins:430] Unfrozen adapter : lora_kqv_adapter\n", - "[NeMo I 2023-05-30 14:09:47 megatron_gpt_peft_models:130] Optimizer groups set:\n", - " | Name | Type | Params\n", - " -----------------------------------\n", - " 0 | model | GPTModel | 358 M \n", - " -----------------------------------\n", - " 3.1 M Trainable params\n", - " 354 M Non-trainable params\n", - " 358 M Total params\n", - " 716.034 Total estimated model params size (MB)\n", - "[NeMo I 2023-05-30 14:09:47 modelPT:721] Optimizer config = FusedAdam (\n", - " Parameter Group 0\n", - " betas: [0.9, 0.98]\n", - " bias_correction: True\n", - " eps: 1e-08\n", - " lr: 0.0001\n", - " weight_decay: 0.01\n", - " )\n", - "[NeMo I 2023-05-30 14:09:47 lr_scheduler:910] Scheduler \"\" \n", - " will be used during training (effective maximum steps = 100) - \n", - " Parameters : \n", - " (warmup_steps: 50\n", - " min_lr: 0.0\n", - " constant_steps: 0\n", - " max_steps: 100\n", - " )\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - " | Name | Type | Params\n", - "-----------------------------------\n", - "0 | model | GPTModel | 358 M \n", - "-----------------------------------\n", - "3.1 M Trainable params\n", - "354 M Non-trainable params\n", - "358 M Total params\n", - "716.034 Total estimated model params size (MB)\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "3cb87a7b9d4b46e4a0fb0f0670351fbd", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Sanity Checking: 0it [00:00, ?it/s]" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[NeMo W 2023-05-30 14:09:48 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:224: PossibleUserWarning: The dataloader, val_dataloader 0, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 24 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", - " rank_zero_warn(\n", - " \n", - "[NeMo W 2023-05-30 14:09:48 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/pytorch_lightning/loops/dataloader/evaluation_loop.py:401: UserWarning: Found `dataloader_iter` argument in the `validation_step`. Note that the support for this signature is experimental and the behavior is subject to change.\n", - " rank_zero_warn(\n", - " \n", - "[NeMo W 2023-05-30 14:09:48 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/apex/transformer/pipeline_parallel/utils.py:81: UserWarning: This function is only for unittest\n", - " warnings.warn(\"This function is only for unittest\")\n", - " \n", - "[NeMo W 2023-05-30 14:09:49 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/pytorch_lightning/trainer/connectors/logger_connector/result.py:536: PossibleUserWarning: It is recommended to use `self.log('val_loss', ..., sync_dist=True)` when logging on epoch level in distributed setting to accumulate the metric across devices.\n", - " warning_cache.warn(\n", - " \n", - "[NeMo W 2023-05-30 14:09:49 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/pytorch_lightning/trainer/connectors/logger_connector/result.py:536: PossibleUserWarning: It is recommended to use `self.log('validation_loss_squad_val', ..., sync_dist=True)` when logging on epoch level in distributed setting to accumulate the metric across devices.\n", - " warning_cache.warn(\n", - " \n", - "[NeMo W 2023-05-30 14:09:49 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/pytorch_lightning/trainer/connectors/logger_connector/result.py:536: PossibleUserWarning: It is recommended to use `self.log('validation_loss', ..., sync_dist=True)` when logging on epoch level in distributed setting to accumulate the metric across devices.\n", - " warning_cache.warn(\n", - " \n", - "[NeMo W 2023-05-30 14:09:49 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:224: PossibleUserWarning: The dataloader, train_dataloader, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 24 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", - " rank_zero_warn(\n", - " \n", - "[NeMo W 2023-05-30 14:09:49 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/pytorch_lightning/loops/fit_loop.py:344: UserWarning: Found `dataloader_iter` argument in the `training_step`. Note that the support for this signature is experimental and the behavior is subject to change.\n", - " rank_zero_warn(\n", - " \n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "c7a473adeca64c828d2a1338dab1e76b", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Training: 0it [00:00, ?it/s]" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[NeMo W 2023-05-30 14:09:51 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/pytorch_lightning/trainer/connectors/logger_connector/result.py:232: UserWarning: You called `self.log('global_step', ...)` in your `training_step` but the value needs to be floating point. Converting it to torch.float32.\n", - " warning_cache.warn(\n", - " \n", - "[NeMo W 2023-05-30 14:09:51 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/pytorch_lightning/trainer/connectors/logger_connector/result.py:232: UserWarning: You called `self.log('consumed_samples', ...)` in your `training_step` but the value needs to be floating point. Converting it to torch.float32.\n", - " warning_cache.warn(\n", - " \n", - "[NeMo W 2023-05-30 14:09:51 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/torch/optim/lr_scheduler.py:139: UserWarning: Detected call of `lr_scheduler.step()` before `optimizer.step()`. In PyTorch 1.1.0 and later, you should call them in the opposite order: `optimizer.step()` before `lr_scheduler.step()`. Failure to do this will result in PyTorch skipping the first value of the learning rate schedule. See more details at https://pytorch.org/docs/stable/optim.html#how-to-adjust-learning-rate\n", - " warnings.warn(\"Detected call of `lr_scheduler.step()` before `optimizer.step()`. \"\n", - " \n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "a0606700c7ab495eb08ed88c16949569", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Validation: 0it [00:00, ?it/s]" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0, global step 100: 'validation_loss' reached 0.30823 (best 0.30823), saving model to '/home/adithyare/NeMo/tutorials/nlp/training_info/checkpoints/lora_example_tuning--validation_loss=0.308-step=100-consumed_samples=396.0-v2.ckpt' as top 1\n", - "Metric val_loss improved. New best score: 0.308\n", - "`Trainer.fit` stopped: `max_steps=100` reached.\n", - "Restoring states from the checkpoint path at /home/adithyare/NeMo/tutorials/nlp/training_info/checkpoints/lora_example_tuning--validation_loss=0.308-step=100-consumed_samples=396.0-v2.ckpt\n", - "Restored all states from the checkpoint file at /home/adithyare/NeMo/tutorials/nlp/training_info/checkpoints/lora_example_tuning--validation_loss=0.308-step=100-consumed_samples=396.0-v2.ckpt\n" - ] - } - ], + "outputs": [], "source": [ - "# Training set to 2 epochs by default in a cell above\n", "trainer.fit(model)" ] }, @@ -1206,31 +567,15 @@ "id": "b8210d6d", "metadata": {}, "source": [ - "Once training is completed you should see a saved '.nemo' file in this folder `{config.exp_manager.explicit_log_dir}/checkpoints`" + "Once training is completed you should see a saved '.nemo' file in this folder `{config.exp_manager.explicit_log_dir}/checkpoints`. This checkpoint will only contain the trained adapter weights, and not the frozen base model weights." ] }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "id": "e4e19e65", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "total 230M\n", - "-rw-rw-r-- 1 adithyare adithyare 14M May 30 14:10 lora_example_tuning.nemo\n", - "-rw-rw-r-- 1 adithyare adithyare 37M May 27 09:47 'lora_example_tuning--validation_loss=0.308-step=100-consumed_samples=396.0.ckpt'\n", - "-rw-rw-r-- 1 adithyare adithyare 37M May 27 09:47 'lora_example_tuning--validation_loss=0.308-step=100-consumed_samples=396.0-last.ckpt'\n", - "-rw-rw-r-- 1 adithyare adithyare 37M May 30 11:12 'lora_example_tuning--validation_loss=0.308-step=100-consumed_samples=396.0-last-v1.ckpt'\n", - "-rw-rw-r-- 1 adithyare adithyare 37M May 30 14:10 'lora_example_tuning--validation_loss=0.308-step=100-consumed_samples=396.0-last-v2.ckpt'\n", - "-rw-rw-r-- 1 adithyare adithyare 37M May 30 11:12 'lora_example_tuning--validation_loss=0.308-step=100-consumed_samples=396.0-v1.ckpt'\n", - "-rw-rw-r-- 1 adithyare adithyare 37M May 30 14:10 'lora_example_tuning--validation_loss=0.308-step=100-consumed_samples=396.0-v2.ckpt'\n", - "training_info\n" - ] - } - ], + "outputs": [], "source": [ "# The trained '.nemo' model is saved in the location below:\n", "! ls -lh {config.exp_manager.explicit_log_dir}/checkpoints\n", @@ -1244,21 +589,62 @@ "metadata": {}, "source": [ "### Inference\n", - "The model object from `trainer.fit(model)` is also capable of doing inference. But for the tutorial we will re-load the saved `.nemo` lora model along with a `.nemo` base language model to simulate a more realistic scenario (where training does not happen right before inference).\n", + "The model object from `trainer.fit(model)` is also capable of doing inference. For the tutorial, however, we will re-load the saved `.nemo` lora model along with a `.nemo` base language model to simulate a more realistic scenario (where training does not happen right before inference).\n", "\n", - "First, we will load and modify a config file that will be used for inference." + "Run the cell below to reimport libraries and classes in case you did not run the training cells above." ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, + "outputs": [], + "source": [ + "# reimport libraries and classes in case one wants to only run cells from the Inference section\n", + "%cd /NeMo/tutorials/nlp\n", + "import wget, os, sys\n", + "sys.path.insert(0, \"../..\") # find the local nemo first before the installed nemo\n", + "from omegaconf import OmegaConf\n", + "from nemo.collections.nlp.parts.megatron_trainer_builder import MegatronTrainerBuilder\n", + "from nemo.collections.nlp.parts.peft_config import LoraPEFTConfig\n", + "from nemo.collections.nlp.models.language_modeling.megatron_gpt_sft_model import MegatronGPTSFTModel\n", + "\n", + "NEMO_DIR = \".\"\n", + "DATA_DIR = \"data\"\n", + "CONFIG_DIR = os.path.join(NEMO_DIR, \"conf\")\n", + "SQUAD_DIR = os.path.join(DATA_DIR, \"SQuAD\")\n" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "First, we will load and modify a config file that will be used for inference.\n" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "# Download the example config file\n", + "wget.download(f'https://raw.githubusercontent.com/NVIDIA/NeMo/{BRANCH}/examples/nlp/language_modeling/tuning/conf/megatron_gpt_peft_eval_config.yaml', CONFIG_DIR)" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, "id": "41ab98a9", "metadata": {}, "outputs": [], "source": [ - "# Download the example config file\n", - "wget.download(f'https://raw.githubusercontent.com/NVIDIA/NeMo/{BRANCH}/examples/nlp/language_modeling/tuning/conf/megatron_gpt_peft_eval_config.yaml', CONFIG_DIR)\n", - "\n", "# Load the example config file so we can start editing it\n", "CONFIG_EVAL_PATH = os.path.join(CONFIG_DIR, \"megatron_gpt_peft_eval_config.yaml\")\n", "config_eval = OmegaConf.load(CONFIG_EVAL_PATH)" @@ -1277,7 +663,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "id": "64a4e71a", "metadata": {}, "outputs": [], @@ -1294,28 +680,12 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "id": "d8ace8f9", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Using 16bit None Automatic Mixed Precision (AMP)\n", - "GPU available: True (cuda), used: True\n", - "TPU available: False, using: 0 TPU cores\n", - "IPU available: False, using: 0 IPUs\n", - "HPU available: False, using: 0 HPUs\n" - ] - } - ], + "outputs": [], "source": [ - "strategy_eval = NLPDDPStrategy(find_unused_parameters=False, no_ddp_communication_hook=True)\n", - "plugins_eval = [TorchElasticEnvironment()]\n", - "# notice the plugins, strategy and config.trainer args are the same as is training portion of this tutorial\n", - "# we just create a new object with no overlap from the training section of this tutorial\n", - "trainer_eval = pl.Trainer(plugins= plugins_eval, strategy=strategy_eval, **config_eval.trainer) " + "trainer_eval = MegatronTrainerBuilder(config_eval).create_trainer()" ] }, { @@ -1324,63 +694,47 @@ "id": "e745ac5e", "metadata": {}, "source": [ - "The `config_eval` object is the hydra config at \"inference/test time\". This means it should contain information relevant for inference/test time. But we still need to know some properties that were set at training time. For example, was the training done with `BOS` enabled or not? And other model specific attributes.\n", + "The `config_eval` object is the hydra config at \"inference/test time\". This means it should contain information relevant for inference/test time, although some properties that were set at training time are still relevant. For example, whether training was done with `BOS` enabled or not, and other model specific attributes.\n", "\n", - "So we extract the `peft_model_cfg` from the '.nemo' file of the lora model we just trained." + "So we extract the relevant information from the '.nemo' file of the lora model we just trained using the `merge_inference_cfg` function." ] }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "id": "e04a2201", "metadata": {}, "outputs": [], "source": [ - "from nemo.collections.nlp.models.language_modeling.megatron_gpt_peft_models import MegatronGPTPEFTModel\n", - "peft_model_cfg = MegatronGPTPEFTModel.restore_from(\n", - " restore_path=\"./training_info/checkpoints/lora_example_tuning.nemo\", trainer=trainer_eval, return_config=True,\n", - ")" + "eval_model_cfg = MegatronGPTSFTModel.merge_inference_cfg(config_eval.model.peft.restore_from_path, config_eval)" ] }, { - "attachments": {}, "cell_type": "markdown", - "id": "79a17ac7", - "metadata": {}, "source": [ - "We modify `peft_model_cfg` to include attributes from the `config_eval` that are specific to inference time." - ] + "The cell below is required if you are running the notebook end-to-end, and if you use a different batch size for training and evaluation. In this case, the microbatch calculator needs to be rest. If you are running training only or inference only, feel free to ignore this cell." + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", - "execution_count": 27, - "id": "0e0a17aa", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'file_names': ['data/SQuAD/squad_short_val.jsonl'], 'names': ['test_set'], 'global_batch_size': 1, 'micro_batch_size': 1, 'shuffle': False, 'num_workers': 0, 'pin_memory': True, 'max_seq_length': 2048, 'min_seq_length': 1, 'drop_last': False, 'context_key': '${data.train_ds.context_key}', 'label_key': '${data.train_ds.label_key}', 'add_eos': '${data.train_ds.add_eos}', 'add_sep': '${data.train_ds.add_sep}', 'add_bos': '${data.train_ds.add_bos}', 'separate_prompt_and_response_with_newline': '${data.train_ds.separate_prompt_and_response_with_newline}', 'write_predictions_to_file': False, 'output_file_path_prefix': None, 'truncation_field': '${data.train_ds.truncation_field}', 'index_mapping_dir': None, 'prompt_template': '${data.train_ds.prompt_template}', 'tokens_to_generate': 30, 'metric': {'name': 'loss', 'average': None, 'num_classes': None}}\n" - ] - } - ], + "execution_count": null, + "outputs": [], "source": [ - "with open_dict(peft_model_cfg):\n", - " # update the model config of the trained model with params we want to set at inference time.\n", - " peft_model_cfg.precision = config_eval.trainer.precision\n", - " peft_model_cfg.data.test_ds = config_eval.model.data.test_ds\n", - " peft_model_cfg.activations_checkpoint_granularity = None\n", - " peft_model_cfg.activations_checkpoint_method = None\n", - "\n", - "with open_dict(config_eval):\n", - " # update the config with the trained model config\n", - " # required for hydra interpolation to work inside cfg.inference\n", - " config_eval.inference.add_BOS = peft_model_cfg.data.test_ds.add_bos\n", - " config_eval.inference.tokens_to_generate = peft_model_cfg.data.test_ds.tokens_to_generate\n", - "\n", - "print(peft_model_cfg.data.test_ds)" - ] + "from apex.transformer.pipeline_parallel.utils import _reconfigure_microbatch_calculator\n", + "_reconfigure_microbatch_calculator(\n", + " rank=0,\n", + " rampup_batch_size=None,\n", + " global_batch_size=config_eval.model.global_batch_size,\n", + " micro_batch_size=config_eval.model.micro_batch_size,\n", + " data_parallel_size=1,\n", + ")" + ], + "metadata": { + "collapsed": false + } }, { "attachments": {}, @@ -1388,101 +742,21 @@ "id": "132ae378", "metadata": {}, "source": [ - "Next, we load the base language model as well as the lora model we just trained." + "Then, we load the base language model as well as the lora model we just trained." ] }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "id": "b19cd0ce", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[NeMo I 2023-05-30 14:11:11 megatron_init:232] Rank 0 has data parallel group: [0]\n", - "[NeMo I 2023-05-30 14:11:11 megatron_init:235] All data parallel group ranks: [[0]]\n", - "[NeMo I 2023-05-30 14:11:11 megatron_init:236] Ranks 0 has data parallel rank: 0\n", - "[NeMo I 2023-05-30 14:11:11 megatron_init:244] Rank 0 has model parallel group: [0]\n", - "[NeMo I 2023-05-30 14:11:11 megatron_init:245] All model parallel group ranks: [[0]]\n", - "[NeMo I 2023-05-30 14:11:11 megatron_init:255] Rank 0 has tensor model parallel group: [0]\n", - "[NeMo I 2023-05-30 14:11:11 megatron_init:259] All tensor model parallel group ranks: [[0]]\n", - "[NeMo I 2023-05-30 14:11:11 megatron_init:260] Rank 0 has tensor model parallel rank: 0\n", - "[NeMo I 2023-05-30 14:11:11 megatron_init:274] Rank 0 has pipeline model parallel group: [0]\n", - "[NeMo I 2023-05-30 14:11:11 megatron_init:286] Rank 0 has embedding group: [0]\n", - "[NeMo I 2023-05-30 14:11:11 megatron_init:292] All pipeline model parallel group ranks: [[0]]\n", - "[NeMo I 2023-05-30 14:11:11 megatron_init:293] Rank 0 has pipeline model parallel rank 0\n", - "[NeMo I 2023-05-30 14:11:11 megatron_init:294] All embedding group ranks: [[0]]\n", - "[NeMo I 2023-05-30 14:11:11 megatron_init:295] Rank 0 has embedding rank: 0\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[NeMo W 2023-05-30 14:11:11 modelPT:244] You tried to register an artifact under config key=tokenizer.vocab_file but an artifact for it has already been registered.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[NeMo I 2023-05-30 14:11:11 tokenizer_utils:204] Getting Megatron tokenizer for pretrained model name: megatron-gpt-345m, custom vocab file: /tmp/tmp5lxz3z8d/bfcdca5e44814366bdb5dcd651325152_gpt2-vocab.json, and merges file: /tmp/tmp5lxz3z8d/315a11fd68be49d6abdb34363e8c4997_gpt2-merge.txt\n", - "[NeMo I 2023-05-30 14:11:11 tokenizer_utils:130] Getting HuggingFace AutoTokenizer with pretrained_model_name: gpt2, vocab_file: /tmp/tmp5lxz3z8d/bfcdca5e44814366bdb5dcd651325152_gpt2-vocab.json, merges_files: /tmp/tmp5lxz3z8d/315a11fd68be49d6abdb34363e8c4997_gpt2-merge.txt, special_tokens_dict: {}, and use_fast: False\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Using sep_token, but it is not set yet.\n", - "Using cls_token, but it is not set yet.\n", - "Using pad_token, but it is not set yet.\n", - "Using mask_token, but it is not set yet.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[NeMo I 2023-05-30 14:11:12 megatron_base_model:238] Padded vocab_size: 50304, original vocab_size: 50257, dummy tokens: 47.\n", - "[NeMo I 2023-05-30 14:11:12 build_model:143] > number of parameters on (tensor, pipeline) model parallel rank (0, 0): 354871296\n", - "[NeMo I 2023-05-30 14:11:12 megatron_gpt_peft_models:56] Before adding PEFT params:\n", - " | Name | Type | Params\n", - " -----------------------------------\n", - " 0 | model | GPTModel | 354 M \n", - " -----------------------------------\n", - " 354 M Trainable params\n", - " 0 Non-trainable params\n", - " 354 M Total params\n", - " 1,419.485 Total estimated model params size (MB)\n", - "[NeMo I 2023-05-30 14:11:12 megatron_gpt_peft_models:65] After adding PEFT params:\n", - " | Name | Type | Params\n", - " -----------------------------------\n", - " 0 | model | GPTModel | 358 M \n", - " -----------------------------------\n", - " 358 M Trainable params\n", - " 0 Non-trainable params\n", - " 358 M Total params\n", - " 1,432.068 Total estimated model params size (MB)\n", - "[NeMo I 2023-05-30 14:11:13 nlp_overrides:491] Model MegatronGPTLoRAModel was successfully restored from /home/adithyare/NeMo/tutorials/nlp/megatron_gpt_345m.nemo.\n" - ] - } - ], + "outputs": [], "source": [ - "save_restore_connector = PEFTSaveRestoreConnector(\n", - " peft_model_nemo_path=config_eval.model.peft.restore_from_path, peft_model_ckpt_path=None,\n", - ")\n", - "from nemo.collections.nlp.models.nlp_model import NLPModel\n", - "model_eval = MegatronGPTPEFTModel.restore_from(\n", - " restore_path=config_eval.model.restore_from_path,\n", - " trainer=trainer,\n", - " override_config_path=peft_model_cfg,\n", - " save_restore_connector=save_restore_connector,\n", - ")\n", + "model_eval = MegatronGPTSFTModel.restore_from(config_eval.model.restore_from_path, eval_model_cfg, trainer=trainer_eval)\n", + "model_eval.load_adapters(config_eval.model.peft.restore_from_path, LoraPEFTConfig(eval_model_cfg))\n", + "model_eval.freeze()\n", "\n", - "model_eval.freeze()" + "print(\"Parameter count manually:\\n\", model_eval.summarize())" ] }, { @@ -1496,34 +770,20 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "id": "12c390f8", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[NeMo I 2023-05-30 14:11:18 text_memmap_dataset:104] Building data files\n", - "[NeMo I 2023-05-30 14:11:18 text_memmap_dataset:343] Processing 1 data files using 12 workers\n", - "[NeMo I 2023-05-30 14:11:18 text_memmap_dataset:349] Time building 0 / 1 mem-mapped files: 0:00:00.706630\n", - "[NeMo I 2023-05-30 14:11:18 text_memmap_dataset:114] Loading data files\n", - "[NeMo I 2023-05-30 14:11:18 text_memmap_dataset:205] Loading data/SQuAD/squad_short_val.jsonl\n", - "[NeMo I 2023-05-30 14:11:18 text_memmap_dataset:117] Time loading 1 mem-mapped files: 0:00:00.001054\n", - "[NeMo I 2023-05-30 14:11:18 text_memmap_dataset:121] Computing global indices\n" - ] - } - ], + "outputs": [], "source": [ - "_test_ds = model_eval._build_dataset(peft_model_cfg.data.test_ds, is_train=False)\n", + "_test_ds = model_eval._build_dataset(eval_model_cfg.data.test_ds, is_train=False)\n", "from torch.utils.data import DataLoader\n", "request_dl = DataLoader(\n", " dataset=_test_ds[0],\n", - " batch_size=peft_model_cfg.data.test_ds.global_batch_size,\n", + " batch_size=eval_model_cfg.data.test_ds.global_batch_size,\n", " collate_fn=_test_ds[0].collate_fn,\n", ")\n", "config_inference = OmegaConf.to_container(config_eval.inference, resolve=True)\n", - "model_eval.set_inference_config(config_inference)\n" + "model_eval.set_inference_config(config_inference)" ] }, { @@ -1535,161 +795,14 @@ "And finally, we call `trainer.predict` which triggers the inference process. The `response` object contains the outputs of the model." ] }, - { - "cell_type": "markdown", - "id": "733c172c", - "metadata": {}, - "source": [] - }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "id": "5ba6a70c", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "You are using a CUDA device ('NVIDIA RTX A6000') that has Tensor Cores. To properly utilize them, you should set `torch.set_float32_matmul_precision('medium' | 'high')` which will trade-off precision for performance. For more details, read https://pytorch.org/docs/stable/generated/torch.set_float32_matmul_precision.html#torch.set_float32_matmul_precision\n", - "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n", - "[NeMo W 2023-05-30 14:11:30 nemo_logging:349] /home/adithyare/miniconda3/envs/n22/lib/python3.8/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:224: PossibleUserWarning: The dataloader, predict_dataloader 0, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 24 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", - " rank_zero_warn(\n", - " \n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "ddcc3ce26ed74665a8429953b929a037", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Predicting: 100it [00:00, ?it/s]" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[NeMo W 2023-05-30 14:11:30 nemo_logging:349] /home/adithyare/NeMo/nemo/collections/nlp/modules/common/text_generation_utils.py:306: UserWarning: The given NumPy array is not writable, and PyTorch does not support non-writable tensors. This means writing to this tensor will result in undefined behavior. You may want to copy the array to protect its data or make it writable before converting it to a tensor. This type of warning will be suppressed for the rest of this program. (Triggered internally at /opt/conda/conda-bld/pytorch_1678402379298/work/torch/csrc/utils/tensor_numpy.cpp:206.)\n", - " string_tensor = torch.as_tensor(\n", - " \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:Which NFL team represented the AFC at Super Bowl 50?\n", - "\n", - "Assistant: Denver Broncos\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:Which NFL team represented the NFC at Super Bowl 50?\n", - "\n", - "Assistant: Denver Broncos\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:Where did Super Bowl 50 take place?\n", - "\n", - "Assistant: Santa Clara, California\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:Which NFL team won Super Bowl 50?\n", - "\n", - "Assistant: Denver Broncos\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:What color was used to emphasize the 50th anniversary of the Super Bowl?\n", - "\n", - "Assistant: gold\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:What was the theme of Super Bowl 50?\n", - "\n", - "Assistant: \"Gold\"\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:What day was the game played on?\n", - "\n", - "Assistant: February 7, 2016\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:What is the AFC short for?\n", - "\n", - "Assistant: Super Bowl 50\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:What was the theme of Super Bowl 50?\n", - "\n", - "Assistant: \"Gold\"\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:What does AFC stand for?\n", - "\n", - "Assistant: Super Bowl L\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:What day was the Super Bowl played on?\n", - "\n", - "Assistant: February 7, 2016\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:Who won Super Bowl 50?\n", - "\n", - "Assistant: Denver Broncos\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:What venue did Super Bowl 50 take place in?\n", - "\n", - "Assistant: Levi's Stadium\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:What city did Super Bowl 50 take place in?\n", - "\n", - "Assistant: San Francisco\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:If Roman numerals were used, what would Super Bowl 50 have been called?\n", - "\n", - "Assistant: Super Bowl L\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:Super Bowl 50 decided the NFL champion for what season?\n", - "\n", - "Assistant: 2015\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:What year did the Denver Broncos secure a Super Bowl title for the third time?\n", - "\n", - "Assistant: 2015\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:What city did Super Bowl 50 take place in?\n", - "\n", - "Assistant: San Francisco\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:What stadium did Super Bowl 50 take place in?\n", - "\n", - "Assistant: Levi's Stadium\n", - "\n", - "\n", - "User: Context:Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi's Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the \"golden anniversary\" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as \"Super Bowl L\"), so that the logo could prominently feature the Arabic numerals 50. Question:What was the final score of Super Bowl 50? \n", - "\n", - "Assistant: 24–10\n", - "\n", - "\n" - ] - } - ], + "outputs": [], "source": [ - "response = trainer.predict(model_eval, request_dl)\n", + "response = trainer_eval.predict(model_eval, request_dl)\n", "for batch in response:\n", " for s in batch['sentences']:\n", " print(f\"{s}\\n\\n\")" From 1da24ef820f0953e92dd0e5edb494285318b06da Mon Sep 17 00:00:00 2001 From: Li Tao Date: Fri, 29 Sep 2023 02:01:51 +0800 Subject: [PATCH 070/112] fix a typo (#7496) Signed-off-by: BestJuly Signed-off-by: Sasha Meister --- .../collections/nlp/data/language_modeling/megatron/helpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nemo/collections/nlp/data/language_modeling/megatron/helpers.cpp b/nemo/collections/nlp/data/language_modeling/megatron/helpers.cpp index ff262c053dab..4a65b313c816 100644 --- a/nemo/collections/nlp/data/language_modeling/megatron/helpers.cpp +++ b/nemo/collections/nlp/data/language_modeling/megatron/helpers.cpp @@ -38,7 +38,7 @@ void build_blending_indices(py::array_t& dataset_index, const int32_t num_datasets, const int64_t size, const bool verbose) { /* Given multiple datasets and a weighting array, build samples - such that it follows those wieghts.*/ + such that it follows those weights.*/ if (verbose) { std::cout << "> building indices for blendable datasets ..." << std::endl; From c3d3ffcc7512d7274a38f3f6f79a5f7a2e10c07c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Sep 2023 16:46:41 -0700 Subject: [PATCH 071/112] [TTS] remove curly braces from ${BRANCH} in jupyer notebook cell. (#7554) (#7560) * remove curly braces. * remove installation of pynini. --------- Signed-off-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Co-authored-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Signed-off-by: Sasha Meister --- tutorials/tts/FastPitch_ChineseTTS_Training.ipynb | 8 ++------ tutorials/tts/FastPitch_GermanTTS_Training.ipynb | 8 ++------ tutorials/tts/Vits_Training.ipynb | 8 ++------ 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb b/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb index 2a12b417a271..6be81479c750 100644 --- a/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb +++ b/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb @@ -61,12 +61,8 @@ "# !pip install wget text-unidecode matplotlib>=3.3.2\n", "\n", "## Install NeMo\n", - "BRANCH = 'main'\n", - "# !python -m pip install \"git+https://github.com/NVIDIA/NeMo.git@${BRANCH}#egg=nemo_toolkit[all]\"\n", - "\n", - "## Install pynini\n", - "# !wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/nemo_text_processing/install_pynini.sh\n", - "# !bash install_pynini.sh\n", + "BRANCH = 'r1.21.0'\n", + "# !python -m pip install \"git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\"\n", "\n", "# !pip install opencc-python-reimplemented\n", "\n", diff --git a/tutorials/tts/FastPitch_GermanTTS_Training.ipynb b/tutorials/tts/FastPitch_GermanTTS_Training.ipynb index e7cb0e896650..fa29916b43d2 100644 --- a/tutorials/tts/FastPitch_GermanTTS_Training.ipynb +++ b/tutorials/tts/FastPitch_GermanTTS_Training.ipynb @@ -61,12 +61,8 @@ "# !pip install wget text-unidecode matplotlib>=3.3.2\n", "\n", "## Install NeMo\n", - "BRANCH = 'main'\n", - "# !python -m pip install \"git+https://github.com/NVIDIA/NeMo.git@${BRANCH}#egg=nemo_toolkit[all]\"\n", - "\n", - "## Install pynini\n", - "# !wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/nemo_text_processing/install_pynini.sh\n", - "# !bash install_pynini.sh\n", + "BRANCH = 'r1.21.0'\n", + "# !python -m pip install \"git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\"\n", "\n", "\"\"\"\n", "Remember to restart the runtime for the kernel to pick up any upgraded packages (e.g. matplotlib)!\n", diff --git a/tutorials/tts/Vits_Training.ipynb b/tutorials/tts/Vits_Training.ipynb index 296661c6ca5e..dbfc0edbe82c 100644 --- a/tutorials/tts/Vits_Training.ipynb +++ b/tutorials/tts/Vits_Training.ipynb @@ -63,12 +63,8 @@ "# !pip install wget text-unidecode matplotlib>=3.3.2\n", "\n", "## Install NeMo\n", - "BRANCH = 'main'\n", - "# !python -m pip install \"git+https://github.com/NVIDIA/NeMo.git@${BRANCH}#egg=nemo_toolkit[all]\"\n", - "\n", - "## Install pynini\n", - "# !wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/nemo_text_processing/install_pynini.sh\n", - "# !bash install_pynini.sh\n", + "BRANCH = 'r1.21.0'\n", + "# !python -m pip install \"git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\"\n", "\n", "\"\"\"\n", "Remember to restart the runtime for the kernel to pick up any upgraded packages (e.g. matplotlib)!\n", From 6849c944e65a5282400b46faa2148146511c96eb Mon Sep 17 00:00:00 2001 From: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Date: Fri, 29 Sep 2023 09:39:50 -0700 Subject: [PATCH 072/112] add youtube embed url (#7570) Signed-off-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Co-authored-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Signed-off-by: Sasha Meister --- README.rst | 17 ++++++++++++++++- docs/source/starthere/intro.rst | 11 ++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 6dc491523a99..15a2120d30b5 100644 --- a/README.rst +++ b/README.rst @@ -67,7 +67,22 @@ For scaling NeMo LLM training on Slurm clusters or public clouds, please see the The NM launcher has extensive recipes, scripts, utilities, and documentation for training NeMo LLMs and also has an `Autoconfigurator `_ which can be used to find the optimal model parallel configuration for training on a specific cluster. -Also see our `introductory video `_ for a high level overview of NeMo. +Also see the two introductory videos below for a high level overview of NeMo. + +* Developing State-Of-The-Art Conversational AI Models in Three Lines of Code. +* NVIDIA NeMo: Toolkit for Conversational AI at PyData Yerevan 2022. + +|three_lines| |pydata| + +.. |pydata| image:: https://img.youtube.com/vi/J-P6Sczmas8/maxres3.jpg + :target: https://www.youtube.com/embed/J-P6Sczmas8?mute=0&start=14&autoplay=0 + :width: 600 + :alt: Develop Conversational AI Models in 3 Lines + +.. |three_lines| image:: https://img.youtube.com/vi/wBgpMf_KQVw/maxresdefault.jpg + :target: https://www.youtube.com/embed/wBgpMf_KQVw?mute=0&start=0&autoplay=0 + :width: 600 + :alt: Introduction at PyData@Yerevan 2022 Key Features ------------ diff --git a/docs/source/starthere/intro.rst b/docs/source/starthere/intro.rst index 70426d3fe4a0..9297b7ef53b3 100644 --- a/docs/source/starthere/intro.rst +++ b/docs/source/starthere/intro.rst @@ -19,14 +19,23 @@ Conversational AI architectures are typically large and require a lot of data an for training. NeMo uses `PyTorch Lightning `_ for easy and performant multi-GPU/multi-node mixed-precision training. -`Pre-trained NeMo models. `_ +`Pre-trained NeMo models. `_ +Also see the two introductory videos below for a high level overview of NeMo. + +* Developing State-Of-The-Art Conversational AI Models in Three Lines of Code. .. raw:: html
+* NVIDIA NeMo: Toolkit for Conversational AI at PyData Yerevan 2022. +.. image:: https://img.youtube.com/vi/J-P6Sczmas8/maxres3.jpg + :target: https://www.youtube.com/embed/J-P6Sczmas8?mute=0&start=14&autoplay=0 + :width: 560 + :alt: Develop Conversational AI Models in 3 Lines + For more information and questions, visit the `NVIDIA NeMo Discussion Board `_. Prerequisites From ec4f8c37a8bad6de3002ee7611d935cb6317ef9d Mon Sep 17 00:00:00 2001 From: Robin Dong Date: Sat, 30 Sep 2023 02:57:58 +1000 Subject: [PATCH 073/112] Remap speakers to continuous range of speaker_id for dataset AISHELL3 (#7536) * Remap speakers to continuous range of speaker_id for dataset AISHELL3 * Add new key/value pair to record raw speaker for AISHELL3 dataset Signed-off-by: Robin Dong --------- Signed-off-by: Robin Dong Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../fastpitch_align_multispeaker_22050.yaml | 2 +- .../tts/aishell3/get_data.py | 38 ++++++++++++------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/examples/tts/conf/zh/fastpitch_align_multispeaker_22050.yaml b/examples/tts/conf/zh/fastpitch_align_multispeaker_22050.yaml index 2464e546598e..55c918c28b72 100644 --- a/examples/tts/conf/zh/fastpitch_align_multispeaker_22050.yaml +++ b/examples/tts/conf/zh/fastpitch_align_multispeaker_22050.yaml @@ -40,7 +40,7 @@ model: learn_alignment: true bin_loss_warmup_epochs: 100 - n_speakers: 1958 + n_speakers: 175 max_token_duration: 75 symbols_embedding_dim: 384 pitch_embedding_kernel_size: 3 diff --git a/scripts/dataset_processing/tts/aishell3/get_data.py b/scripts/dataset_processing/tts/aishell3/get_data.py index 904ab0314653..1b3043bbf0d3 100755 --- a/scripts/dataset_processing/tts/aishell3/get_data.py +++ b/scripts/dataset_processing/tts/aishell3/get_data.py @@ -86,14 +86,17 @@ def __process_transcript(file_path: str): text_normalizer_call_kwargs = {"punct_pre_process": True, "punct_post_process": True} normalizer_call = lambda x: text_normalizer.normalize(x, **text_normalizer_call_kwargs) entries = [] - i = 0 SPEAKER_LEN = 7 + + candidates = [] + speakers = set() with open(file_path / "train" / "content.txt", encoding="utf-8") as fin: for line in fin: content = line.split() wav_name, text = content[0], "".join(content[1::2]) + "。" wav_name = wav_name.replace(u'\ufeff', '') speaker = wav_name[:SPEAKER_LEN] + speakers.add(speaker) wav_file = file_path / "train" / "wav" / speaker / wav_name assert os.path.exists(wav_file), f"{wav_file} not found!" duration = subprocess.check_output(f"soxi -D {wav_file}", shell=True) @@ -102,18 +105,27 @@ def __process_transcript(file_path: str): processed_file = file_path / "processed" / wav_name # convert wav to mono 22050HZ, 16 bit (as SFSpeech dataset) subprocess.run(f"sox {wav_file} -r 22050 -c 1 -b 16 {processed_file}", shell=True) - simplified_text = cc.convert(text) - normalized_text = normalizer_call(simplified_text) - entry = { - 'audio_filepath': os.path.abspath(processed_file), - 'duration': float(duration), - 'text': text, - 'normalized_text': normalized_text, - 'speaker': int(speaker[3:]), - } - - i += 1 - entries.append(entry) + candidates.append((processed_file, duration, text, speaker)) + + # remapping the speakder to speaker_id (start from 1) + remapping = {} + for index, speaker in enumerate(sorted(speakers)): + remapping[speaker] = index + 1 + + for processed_file, duration, text, speaker in candidates: + simplified_text = cc.convert(text) + normalized_text = normalizer_call(simplified_text) + entry = { + 'audio_filepath': os.path.abspath(processed_file), + 'duration': float(duration), + 'text': text, + 'normalized_text': normalized_text, + 'speaker_raw': speaker, + 'speaker': remapping[speaker], + } + + entries.append(entry) + return entries From 4858db444d10ad58080f7f4c40ec4782d27a3dda Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 Sep 2023 11:12:43 -0700 Subject: [PATCH 074/112] fix validation_step_outputs initialization for multi-dataloader (#7546) (#7572) * added correct validation_step_outputs initialization for mutli-dataloader * changed kernel for display * Update logic for validation and test step outputs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * revert multidataloader changes in multilang ASR notebook --------- Signed-off-by: KunalDhawan Signed-off-by: smajumdar Co-authored-by: Kunal Dhawan Co-authored-by: Somshubra Majumdar Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- nemo/core/classes/modelPT.py | 99 +++++++++++++++++++++++++++---- tutorials/asr/Multilang_ASR.ipynb | 11 +++- 2 files changed, 94 insertions(+), 16 deletions(-) diff --git a/nemo/core/classes/modelPT.py b/nemo/core/classes/modelPT.py index 6e616319b12b..bc6f3bcef1cc 100644 --- a/nemo/core/classes/modelPT.py +++ b/nemo/core/classes/modelPT.py @@ -179,18 +179,11 @@ def __init__(self, cfg: DictConfig, trainer: Trainer = None): # Create list of lists for val and test outputs to support multiple dataloaders # Initialize an empty list as sometimes self._validation_dl can be None at this stage - self.validation_step_outputs = [] - # Check len(self._validation_dl) > 1 as sometimes single dataloader can be in a list: [] when ds_item in - # config has 1 item passed in a list - if self._validation_dl and type(self._validation_dl) == list and len(self._validation_dl) > 1: - for _ in range(len(self._validation_dl)): - self.validation_step_outputs.append([]) + self._validation_step_outputs = None # Initialize an empty list as sometimes self._test_dl can be None at this stage - self.test_step_outputs = [] - if self._test_dl and type(self._test_dl) == list and len(self._test_dl) > 1: - for _ in range(len(self._test_dl)): - self.test_step_outputs.append([]) + self._test_step_outputs = None + # ModelPT wrappers over subclass implementations self.training_step = model_utils.wrap_training_step(self.training_step) @@ -1573,6 +1566,61 @@ def cfg(self, cfg): if hasattr(self, '_hparams_initial') and 'cfg' in self._hparams_initial: self._hparams_initial['cfg'] = OmegaConf.to_object(self._cfg) + @property + def validation_step_outputs(self): + """ + Cached outputs of validation_step. It can be a list of items (for single data loader) or a list of lists + (for multiple data loaders). + + Returns: + List of outputs of validation_step. + """ + if self._validation_step_outputs is not None: + return self._validation_step_outputs + + # Initialize new output list + self._validation_step_outputs = [] + # Check len(self._validation_dl) > 1 as sometimes single dataloader can be in a list: [] when ds_item in + # config has 1 item passed in a list + if ( + self._validation_dl is not None + and isinstance(self._validation_dl, (list, tuple)) + and len(self._validation_dl) > 1 + ): + for _ in range(len(self._validation_dl)): + self._validation_step_outputs.append([]) + + return self._validation_step_outputs + + @validation_step_outputs.setter + def validation_step_outputs(self, value): + self._validation_step_outputs = value + + @property + def test_step_outputs(self): + """ + Cached outputs of test_step. It can be a list of items (for single data loader) or a list of lists (for multiple data loaders). + + Returns: + List of outputs of test_step. + """ + if self._test_step_outputs is not None: + return self._test_step_outputs + + # Initialize new output list + self._test_step_outputs = [] + # Check len(self._test_dl) > 1 as sometimes single dataloader can be in a list: [] when ds_item in + # config has 1 item passed in a list + if self._test_dl is not None and isinstance(self._test_dl, (list, tuple)) and len(self._test_dl) > 1: + for _ in range(len(self._test_dl)): + self._test_step_outputs.append([]) + + return self._test_step_outputs + + @test_step_outputs.setter + def test_step_outputs(self, value): + self._test_step_outputs = value + @staticmethod def _is_model_being_restored() -> bool: app_state = AppState() @@ -1714,15 +1762,40 @@ def on_train_batch_end(self, outputs, batch: Any, batch_idx: int, unused: int = logging.info("====== End nsys profiling ======") torch.cuda.cudart().cudaProfilerStop() + def _cleanup_on_execution_end(self): + """ + Utility function to clean up the module state at the end of execution. + """ + + # dynamic freezing cleanup + if hasattr(self, '_freeze_cfg'): + delattr(self, '_freeze_cfg') + + # Clear up the val and test output caches + self._validation_step_outputs = None + self._test_step_outputs = None + def on_train_end(self): """ PyTorch Lightning hook: https://pytorch-lightning.readthedocs.io/en/stable/common/lightning_module.html#on-train-end We use it here to cleanup the dynamic freezing config. """ - # dynamic freezing cleanup - if hasattr(self, '_freeze_cfg'): - delattr(self, '_freeze_cfg') + self._cleanup_on_execution_end() + + def on_test_end(self): + """ PyTorch Lightning hook: + https://pytorch-lightning.readthedocs.io/en/stable/common/lightning_module.html#on-test-end + """ + + self._cleanup_on_execution_end() + + def on_predict_end(self): + """ PyTorch Lightning hook: + https://pytorch-lightning.readthedocs.io/en/stable/common/lightning_module.html#on-test-end + """ + + self._cleanup_on_execution_end() # TODO: Remove in PTL 1.7.2 def cuda(self, device=None): diff --git a/tutorials/asr/Multilang_ASR.ipynb b/tutorials/asr/Multilang_ASR.ipynb index a1edeea815d0..7a11cb7dc6a6 100644 --- a/tutorials/asr/Multilang_ASR.ipynb +++ b/tutorials/asr/Multilang_ASR.ipynb @@ -1713,7 +1713,7 @@ }, "outputs": [], "source": [ - "asr_model.setup_multiple_validation_data(val_data_config=validation_ds) " + "asr_model.setup_multiple_validation_data(val_data_config=validation_ds)" ] }, { @@ -2273,7 +2273,7 @@ "provenance": [] }, "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "base", "language": "python", "name": "python3" }, @@ -2287,11 +2287,16 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.13" + "version": "3.8.12" }, "nteract": { "version": "0.28.0" }, + "vscode": { + "interpreter": { + "hash": "1aaa02ce0ce2638a6e16a203f0ce39bc7495f7236d7115882d2d3541e1318e7a" + } + }, "widgets": { "application/vnd.jupyter.widget-state+json": { "013abc9bfddf456abf15dc2b0567d969": { From 122eced8e66f40470a1e98c19f4acfff650b63c8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 Sep 2023 12:31:21 -0700 Subject: [PATCH 075/112] Append output of val step to self.validation_step_outputs (#7530) (#7532) Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../nlp/models/glue_benchmark/glue_benchmark_model.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/nemo/collections/nlp/models/glue_benchmark/glue_benchmark_model.py b/nemo/collections/nlp/models/glue_benchmark/glue_benchmark_model.py index 7843da422c4e..4a073e2ada1c 100644 --- a/nemo/collections/nlp/models/glue_benchmark/glue_benchmark_model.py +++ b/nemo/collections/nlp/models/glue_benchmark/glue_benchmark_model.py @@ -173,16 +173,18 @@ def validation_step(self, batch, batch_idx, dataloader_idx=0): model_output = torch.argmax(model_output, 1) eval_tensors = {'preds': model_output, 'labels': labels} - return {'val_loss': val_loss, 'eval_tensors': eval_tensors} + output = {'val_loss': val_loss, 'eval_tensors': eval_tensors} + self.validation_step_outputs.append(output) + return output def multi_validation_epoch_end(self, outputs, dataloader_idx: int = 0): """ Called at the end of validation to aggregate outputs. outputs: list of individual outputs of each validation step. """ - avg_loss = torch.stack([x['val_loss'] for x in outputs]).mean() - preds = torch.cat([x['eval_tensors']['preds'] for x in outputs]) - labels = torch.cat([x['eval_tensors']['labels'] for x in outputs]) + avg_loss = torch.stack([x['val_loss'] for x in self.validation_step_outputs]).mean() + preds = torch.cat([x['eval_tensors']['preds'] for x in self.validation_step_outputs]) + labels = torch.cat([x['eval_tensors']['labels'] for x in self.validation_step_outputs]) all_preds = [] all_labels = [] From ec9e251324ca4bcfa4991bf693ea368b4b80b8da Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 Sep 2023 14:16:51 -0700 Subject: [PATCH 076/112] [TTS] fixed trainer's accelerator and strategy. (#7569) (#7574) Signed-off-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Co-authored-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Signed-off-by: Sasha Meister --- tutorials/tts/Vits_Training.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/tts/Vits_Training.ipynb b/tutorials/tts/Vits_Training.ipynb index dbfc0edbe82c..db7161c06c61 100644 --- a/tutorials/tts/Vits_Training.ipynb +++ b/tutorials/tts/Vits_Training.ipynb @@ -304,8 +304,8 @@ " phoneme_dict_path=tts_dataset_files/ipa_cmudict-0.7b_nv23.01.txt \\\n", " heteronyms_path=tts_dataset_files/heteronyms-052722 \\\n", " trainer.max_epochs=3 \\\n", - " trainer.accelerator=auto \\\n", - " trainer.strategy=auto \\\n", + " trainer.accelerator='gpu' \\\n", + " trainer.strategy='ddp_find_unused_parameters_true' \\\n", " trainer.check_val_every_n_epoch=1 \\\n", " trainer.devices=1)" ] From e99d5303199471bfcc29d61a705d3239f016f1be Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 Sep 2023 16:16:36 -0700 Subject: [PATCH 077/112] Append val/test output to instance variable in EncDecSpeakerLabelModel (#7562) (#7573) * Append val/test output to the instance variable in EncDecSpeakerLabelModel * Handle test case in evaluation_step * Replace type with isinstance --------- Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Sasha Meister --- nemo/collections/asr/models/enhancement_models.py | 10 ++++++++++ nemo/collections/asr/models/label_models.py | 14 +++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/nemo/collections/asr/models/enhancement_models.py b/nemo/collections/asr/models/enhancement_models.py index a25bf882a23b..a441c6f7a8b0 100644 --- a/nemo/collections/asr/models/enhancement_models.py +++ b/nemo/collections/asr/models/enhancement_models.py @@ -434,6 +434,16 @@ def evaluation_step(self, batch, batch_idx, dataloader_idx: int = 0, tag: str = # Log global step self.log('global_step', torch.tensor(self.trainer.global_step, dtype=torch.float32), sync_dist=True) + if tag == 'val': + if isinstance(self.trainer.val_dataloaders, (list, tuple)) and len(self.trainer.val_dataloaders) > 1: + self.validation_step_outputs[dataloader_idx].append(output_dict) + else: + self.validation_step_outputs.append(output_dict) + else: + if isinstance(self.trainer.test_dataloaders, (list, tuple)) and len(self.trainer.test_dataloaders) > 1: + self.test_step_outputs[dataloader_idx].append(output_dict) + else: + self.test_step_outputs.append(output_dict) return output_dict @classmethod diff --git a/nemo/collections/asr/models/label_models.py b/nemo/collections/asr/models/label_models.py index 1a284aca609d..83e57ece59e3 100644 --- a/nemo/collections/asr/models/label_models.py +++ b/nemo/collections/asr/models/label_models.py @@ -373,13 +373,25 @@ def evaluation_step(self, batch, batch_idx, dataloader_idx: int = 0, tag: str = self._macro_accuracy.update(preds=logits, target=labels) stats = self._macro_accuracy._final_state() - return { + output = { f'{tag}_loss': loss_value, f'{tag}_correct_counts': correct_counts, f'{tag}_total_counts': total_counts, f'{tag}_acc_micro_top_k': acc_top_k, f'{tag}_acc_macro_stats': stats, } + if tag == 'val': + if isinstance(self.trainer.val_dataloaders, (list, tuple)) and len(self.trainer.val_dataloaders) > 1: + self.validation_step_outputs[dataloader_idx].append(output) + else: + self.validation_step_outputs.append(output) + else: + if isinstance(self.trainer.test_dataloaders, (list, tuple)) and len(self.trainer.test_dataloaders) > 1: + self.test_step_outputs[dataloader_idx].append(output) + else: + self.test_step_outputs.append(output) + + return output def multi_evaluation_epoch_end(self, outputs, dataloader_idx: int = 0, tag: str = 'val'): loss_mean = torch.stack([x[f'{tag}_loss'] for x in outputs]).mean() From e7b3b715fc3c181ee56aba58661a63dc22dbbead Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 Sep 2023 17:06:10 -0700 Subject: [PATCH 078/112] Fix CustomProgressBar for resume (#7427) (#7522) * Fix CustomProgress Bar for resume and multiple epochs * Edit num_training_batches * Use max_steps as total for progress bar for resume * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- nemo/collections/nlp/parts/nlp_overrides.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/nemo/collections/nlp/parts/nlp_overrides.py b/nemo/collections/nlp/parts/nlp_overrides.py index 5332a9d2f115..8d027a00248d 100644 --- a/nemo/collections/nlp/parts/nlp_overrides.py +++ b/nemo/collections/nlp/parts/nlp_overrides.py @@ -994,6 +994,12 @@ class CustomProgressBar(TQDMProgressBar): for megatron models """ + def get_current_epoch_step(self, trainer): + """ + Get the value of step within an epoch + """ + return trainer.fit_loop.epoch_loop.automatic_optimization.optim_progress.optimizer.step.current.completed + def init_train_tqdm(self): """ Override bar_format to not have 's/it' @@ -1002,11 +1008,22 @@ def init_train_tqdm(self): self.bar.bar_format = "{desc}: {percentage:3.0f}%|{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}{postfix}]" return self.bar + def on_train_epoch_start(self, trainer, *_): + if trainer.max_steps > 0 and (trainer.ckpt_path is not None): + # while resuming from a ckpt use trainer.max_steps as the total for progress bar as trainer.num_training_batches + # is truncated to max_steps - step being resumed at + num_training_batches = trainer.max_steps + else: + num_training_batches = trainer.num_training_batches + self.train_progress_bar.reset(num_training_batches) + self.train_progress_bar.initial = 0 + self.train_progress_bar.set_description(f"Epoch {trainer.current_epoch}") + def on_train_batch_end(self, trainer, pl_module, *_, **__): """ - Override parent class on_train_batch_end to update progress bar per global_step instead of per microbatch + Override parent class on_train_batch_end to update progress bar per global batch instead of per microbatch """ - n = trainer.global_step + n = self.get_current_epoch_step(trainer) if self._should_update(n, self.train_progress_bar.total): _update_n(self.train_progress_bar, n) self.train_progress_bar.set_postfix(self.get_metrics(trainer, pl_module)) From abbef3b8e671d7be47db39fd3d7de0c85493d186 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 Sep 2023 19:12:52 -0700 Subject: [PATCH 079/112] fix typos in nfa and speech enhancement tutorials (#7580) (#7583) Signed-off-by: Elena Rastorgueva Co-authored-by: Elena Rastorgueva <80532067+erastorgueva-nv@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../speech_enhancement/Speech_Enhancement_with_NeMo.ipynb | 8 ++++---- tutorials/tools/NeMo_Forced_Aligner_Tutorial.ipynb | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb b/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb index d8a15cbd5e1c..d7cd6571c16a 100644 --- a/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb +++ b/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb @@ -9,7 +9,7 @@ "source": [ "# Introduction\n", "\n", - "The goal of this tutorial is to demonstrate the basic steps required to setup and train train a simple single-channel speech enhancement model in NeMo.\n", + "The goal of this tutorial is to demonstrate the basic steps required to setup and train a simple single-channel speech enhancement model in NeMo.\n", "\n", "This notebook covers the following steps:\n", "\n", @@ -21,7 +21,7 @@ "Note that this tutorial is only for demonstration purposes.\n", "To achieve best performance for a particular use case, carefully prepared data and more advanced models should be used.\n", "\n", - "*Disclamer:*\n", + "*Disclaimer:*\n", "User is responsible for checking the content of datasets and the applicable licenses and determining if suitable for the intended use." ] }, @@ -411,7 +411,7 @@ "\n", "Here, a simple encoder-mask-decoder model will be used to process the noisy input signal and produce an enhanced output signal.\n", "\n", - "In general, an encoder-mask-decoder model can be confugured using `EncMaskDecAudioToAudioModel` class, which is depicted in the following block diagram." + "In general, an encoder-mask-decoder model can be configured using `EncMaskDecAudioToAudioModel` class, which is depicted in the following block diagram." ] }, { @@ -470,7 +470,7 @@ "In this particular configuration, the model structure can be described as follows:\n", "* `AudioToSpectrogram` implements the analysis STFT transform.\n", "* `MaskEstimatorRNN` is a mask estimator using RNNs.\n", - "* `MaskReferenceChannel` is a simple processor whith applies the estimated mask on the reference channel. In this tutorial, the input signal has only a single channel, so the reference channel will be set to `0`.\n", + "* `MaskReferenceChannel` is a simple processor which applies the estimated mask on the reference channel. In this tutorial, the input signal has only a single channel, so the reference channel will be set to `0`.\n", "* `SpectrogramToAudio` implements the synthesis STFT transform." ] }, diff --git a/tutorials/tools/NeMo_Forced_Aligner_Tutorial.ipynb b/tutorials/tools/NeMo_Forced_Aligner_Tutorial.ipynb index a6ab57854bad..3ca3c32c1074 100644 --- a/tutorials/tools/NeMo_Forced_Aligner_Tutorial.ipynb +++ b/tutorials/tools/NeMo_Forced_Aligner_Tutorial.ipynb @@ -347,7 +347,7 @@ "id": "dHU-YmALUvVf" }, "source": [ - "The alignment process should have finished successfuly, let's look at some of the output files." + "The alignment process should have finished successfully, let's look at some of the output files." ] }, { @@ -472,7 +472,7 @@ "\n", "You can see that the token timestamps (in the first video) are very accurate, even despite the poor audio quality of the video.\n", "\n", - "The word timestamps (in the second video) are also very good. The only noticeable mistakes are when the the word has punctuation at the end (or beginning). This is because punctuation that is not separated from a word by a space is considered to be part of that word. If the alignment for the punctuation is in a region of non-speech, then the word alignment will also contain that region of non-speech." + "The word timestamps (in the second video) are also very good. The only noticeable mistakes are when the word has punctuation at the end (or beginning). This is because punctuation that is not separated from a word by a space is considered to be part of that word. If the alignment for the punctuation is in a region of non-speech, then the word alignment will also contain that region of non-speech." ] }, { From 468e8b0cad8b59428313e8b4b67a6c3b82b03eb1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 Sep 2023 23:57:01 -0700 Subject: [PATCH 080/112] Add strategy as ddp_find_unused_parameters_true for glue_benchmark.py (#7454) (#7461) Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Sasha Meister --- examples/nlp/glue_benchmark/glue_benchmark.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/nlp/glue_benchmark/glue_benchmark.py b/examples/nlp/glue_benchmark/glue_benchmark.py index 87486dbc47b0..3cb5f8e4af3e 100644 --- a/examples/nlp/glue_benchmark/glue_benchmark.py +++ b/examples/nlp/glue_benchmark/glue_benchmark.py @@ -46,6 +46,10 @@ @hydra_runner(config_name="glue_benchmark_config") def main(cfg: DictConfig) -> None: + # PTL 2.0 has find_unused_parameters as False by default, so its required to set it to True + # when there are unused parameters like here + if cfg.trainer.strategy == 'ddp': + cfg.trainer.strategy = "ddp_find_unused_parameters_true" logging.info(f'Config: {OmegaConf.to_yaml(cfg)}') trainer = pl.Trainer(**cfg.trainer) exp_manager_cfg = cfg.get("exp_manager", None) From 8406b69b27983817ec9d485d3e0488c52cbb3500 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 30 Sep 2023 13:11:13 -0700 Subject: [PATCH 081/112] update strategy (#7577) (#7578) Signed-off-by: Nithin Rao Koluguri Co-authored-by: Nithin Rao Signed-off-by: Sasha Meister --- tutorials/asr/Self_Supervised_Pre_Training.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/asr/Self_Supervised_Pre_Training.ipynb b/tutorials/asr/Self_Supervised_Pre_Training.ipynb index 113979314da9..c39445dabfd1 100644 --- a/tutorials/asr/Self_Supervised_Pre_Training.ipynb +++ b/tutorials/asr/Self_Supervised_Pre_Training.ipynb @@ -312,7 +312,7 @@ "\n", "if torch.cuda.is_available():\n", " cfg.trainer.accelerator = 'gpu'\n", - " cfg.trainer.strategy = 'dp'\n", + " cfg.trainer.strategy = 'auto'\n", " cfg.trainer.devices = 1\n", "else:\n", " cfg.trainer.accelerator = 'cpu'\n", @@ -534,7 +534,7 @@ "\n", "if torch.cuda.is_available():\n", " cfg.trainer.accelerator = 'gpu'\n", - " cfg.trainer.strategy = 'dp'\n", + " cfg.trainer.strategy = 'auto'\n", " cfg.trainer.devices = 1\n", "else:\n", " cfg.trainer.accelerator = 'cpu'\n", From 02dac3b60ce7e98c5a8a3d96e399fe054a97ba16 Mon Sep 17 00:00:00 2001 From: Igor Gitman Date: Mon, 2 Oct 2023 08:00:51 -0700 Subject: [PATCH 082/112] Fix typos (#7581) Signed-off-by: Sasha Meister --- tutorials/asr/ASR_Confidence_Estimation.ipynb | 14 +++++++------- tutorials/asr/Confidence_Ensembles.ipynb | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tutorials/asr/ASR_Confidence_Estimation.ipynb b/tutorials/asr/ASR_Confidence_Estimation.ipynb index 7a92ed026f07..93c40b576115 100644 --- a/tutorials/asr/ASR_Confidence_Estimation.ipynb +++ b/tutorials/asr/ASR_Confidence_Estimation.ipynb @@ -422,7 +422,7 @@ "1. Initialize _ConfidenceConfig_\n", "2. Put the created _ConfidenceConfig_ into the model decoding config.\n", "\n", - "The folloving cell contains an example of _ConfidenceConfig_ initialization and updating the the model's decoding config.\n", + "The following cell contains an example of _ConfidenceConfig_ initialization and updating the model's decoding config.\n", "\n", "For the _ConfidenceConfig_ there are also listed possible values for its parameters.\n", "\n", @@ -627,14 +627,14 @@ "4. Normalized Cross Entropy ($\\mathrm{NCE}$): how close of confidence for correct predictions to $1.0$ and of incorrect predictions to $0.0$. It ranges from $-\\infty$ to $1.0$, with negative scores indicating that the confidence method performs worse than the setting confidence score to $1-\\mathrm{WER}$. This metric is also known as Normalized Mutual Information.\n", "5. Expected Calibration Error ($\\mathrm{ECE}$): a weighted average over the absolute accuracy/confidence difference. It ranges from $0.0$ to $1.0$ with the best value $0.0$.\n", "\n", - "Metrics based on the Youden's curve (see https://en.wikipedia.org/wiki/Youden%27s_J_statistic) can also be condsidered. They are:\n", + "Metrics based on the Youden's curve (see https://en.wikipedia.org/wiki/Youden%27s_J_statistic) can also be considered. They are:\n", "1. Area Under the Youden's curve ($\\mathrm{AUC}_\\mathrm{YC}$): the rate of the effective threshold range (i.e. the adjustability or responsiveness). It ranges from $0.0$ to $1.0$ with the best value $0.5$.\n", "2. Maximum of the Youden's curve $\\mathrm{MAX}_\\mathrm{YC}$: the optimal $\\mathrm{TNR}$ vs. $\\mathrm{FNR}$ tradeoff. It's unnormalized version can be used as a criterion for selecting the optimal $\\tau$. It ranges from $0.0$ to $1.0$ with the best value $1.0$.\n", "3. The standard deviation of the Youden's curve values ($\\mathrm{STD}_\\mathrm{YC}$): indicates that $\\mathrm{TNR}$ and $\\mathrm{FNR}$ increase at different rates (viz. $\\mathrm{TNR}$ grows faster) as the $\\tau$ increases. It ranges from $0.0$ to $0.5$ with the best value around $0.25$.\n", "\n", - "When selecting/tuning a confidence method, it is recommended to maximize $\\mathrm{AUC}_\\mathrm{ROC}$ first as this is the main mectic of confidence estimation quality. Then, for overconfident models, maximizing $\\mathrm{AUC}_\\mathrm{NT}$ should take precedence over $\\mathrm{AUC}_\\mathrm{PR}$. Finally, a trade-off between $\\mathrm{NCE}$/$\\mathrm{ECE}$ and the family of $\\mathrm{YC}$ metrics considered as a compromise between formal correctness and controllability.\n", + "When selecting/tuning a confidence method, it is recommended to maximize $\\mathrm{AUC}_\\mathrm{ROC}$ first as this is the main metric of confidence estimation quality. Then, for overconfident models, maximizing $\\mathrm{AUC}_\\mathrm{NT}$ should take precedence over $\\mathrm{AUC}_\\mathrm{PR}$. Finally, a trade-off between $\\mathrm{NCE}$/$\\mathrm{ECE}$ and the family of $\\mathrm{YC}$ metrics considered as a compromise between formal correctness and controllability.\n", "\n", - "Let's see how well our confidence performs according to the metrcis above." + "Let's see how well our confidence performs according to the metrics above." ] }, { @@ -891,7 +891,7 @@ "id": "dbb82877" }, "source": [ - "## 4.1. Small WER improvenent\n", + "## 4.1. Small WER improvement\n", "\n", "Good confidence scores can slightly reduce WER by removing low confidence words from recognition results.\n", "\n", @@ -1190,7 +1190,7 @@ "id": "f28da61f", "metadata": {}, "source": [ - "The original examples contain speech, music, or noise. The resulring audio recordings are considered to contain no recognizable speech.\n", + "The original examples contain speech, music, or noise. The resulting audio recordings are considered to contain no recognizable speech.\n", "\n", "You can listen to an example of the audios." ] @@ -1397,7 +1397,7 @@ }, "source": [ "# Summary\n", - "This tutorial covered the basics of ASR confidence estimation and two examples of using ASR word confidence: WER reduction and hallusinations removal.\n", + "This tutorial covered the basics of ASR confidence estimation and two examples of using ASR word confidence: WER reduction and hallucinations removal.\n", "\n", "You can follow this tutorial on [ASR Confidence-based Ensembles](https://github.com/NVIDIA/NeMo/blob/main/tutorials/asr/Confidence_Ensembles.ipynb) to see another important application of ASR confidence estimation." ] diff --git a/tutorials/asr/Confidence_Ensembles.ipynb b/tutorials/asr/Confidence_Ensembles.ipynb index 4516d2b70d6d..eab46d3b06e5 100644 --- a/tutorials/asr/Confidence_Ensembles.ipynb +++ b/tutorials/asr/Confidence_Ensembles.ipynb @@ -48,7 +48,7 @@ "\n", "# clone SDP and install requirements\n", "!git clone https://github.com/NVIDIA/NeMo-speech-data-processor $WORKSPACE_DIR/NeMo-speech-data-processor\n", - "!pip install -r $WORKSPACE_DIR/NeMo-speech-data-processor/requirements.txt\n", + "!pip install -r $WORKSPACE_DIR/NeMo-speech-data-processor/requirements/main.txt\n", "\n", "\"\"\"\n", "Remember to restart the runtime for the kernel to pick up any upgraded packages.\n", @@ -106,13 +106,13 @@ "\n", "A short answer — you can use any ASR models. E.g., you can combine a number of CTC models, or Transducer models, or even mix-and-match. \n", "\n", - "A more detailed answer is that hte performance of the confidence ensemble is upper-bounded by the performance of the best model on each of the input examples. Thus you will benefit if some of your models work really well on part of the input compared to other models. This way you will get more gains compared to each separate model, and it will also make correct model identification easier.\n", + "A more detailed answer is that the performance of the confidence ensemble is upper-bounded by the performance of the best model on each of the input examples. Thus you will benefit if some of your models work really well on part of the input compared to other models. This way you will get more gains compared to each separate model, and it will also make correct model identification easier.\n", "\n", "### How to estimate a model's confidence?\n", "\n", "Good news, we have a whole separate [tutorial](https://github.com/NVIDIA/NeMo/blob/main/tutorials/asr/ASR_Confidence_Estimation.ipynb) on this topic! You can go through it if you want to know all the details about different ways to estimate confidence of NeMo ASR models. There are different confidence measures and aggregation functions and for the absolute best performance, you will need to run a grid-search to pick the best confidence estimation way for your specific models and data.\n", "\n", - "That being said, we found that there exist a set of confidence parameters that work pretty well on a large set of models and datsets. They are default in NeMo and so you might not need to worry about running the search. If you do want to maximize the performance by tuning the confidence parameters, you only need to add [a few extra config lines](#Building-and-evaluating-ensemble-(tuned-parameters)).\n", + "That being said, we found that there exist a set of confidence parameters that work pretty well on a large set of models and datasets. They are default in NeMo and so you might not need to worry about running the search. If you do want to maximize the performance by tuning the confidence parameters, you only need to add [a few extra config lines](#Building-and-evaluating-ensemble-(tuned-parameters)).\n", "\n", "### How to calibrate confidence values?\n", "\n", From e434de95a0da90e0900c92d2491e6db8f970db95 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 09:35:22 -0700 Subject: [PATCH 083/112] Change hifigan finetune strategy to ddp_find_unused_parameters_true (#7579) (#7584) * Change strategy to auto --------- Signed-off-by: Cheng-Ping Hsieh Co-authored-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Signed-off-by: Sasha Meister --- tutorials/tts/FastPitch_Adapter_Finetuning.ipynb | 2 +- tutorials/tts/FastPitch_MultiSpeaker_Pretraining.ipynb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/tts/FastPitch_Adapter_Finetuning.ipynb b/tutorials/tts/FastPitch_Adapter_Finetuning.ipynb index 5fe61d596f4b..5220519e01ad 100644 --- a/tutorials/tts/FastPitch_Adapter_Finetuning.ipynb +++ b/tutorials/tts/FastPitch_Adapter_Finetuning.ipynb @@ -615,7 +615,7 @@ "+trainer.max_epochs=50 \\\n", "trainer.check_val_every_n_epoch=5 \\\n", "trainer.devices=-1 \\\n", - "trainer.strategy='ddp' \\\n", + "trainer.strategy='ddp_find_unused_parameters_true' \\\n", "trainer.precision=16 \\\n", "exp_manager.exp_dir={logs_dir} \\\n", "exp_manager.create_wandb_logger=True \\\n", diff --git a/tutorials/tts/FastPitch_MultiSpeaker_Pretraining.ipynb b/tutorials/tts/FastPitch_MultiSpeaker_Pretraining.ipynb index a031723f549b..ad0a49067ca4 100644 --- a/tutorials/tts/FastPitch_MultiSpeaker_Pretraining.ipynb +++ b/tutorials/tts/FastPitch_MultiSpeaker_Pretraining.ipynb @@ -511,7 +511,7 @@ "+trainer.max_epochs=5 \\\n", "trainer.check_val_every_n_epoch=5 \\\n", "trainer.devices=1 \\\n", - "trainer.strategy='auto' \\\n", + "trainer.strategy='ddp_find_unused_parameters_true' \\\n", "trainer.precision=16 \\\n", "exp_manager.exp_dir={logs_dir} \\\n", "exp_manager.create_wandb_logger=True \\\n", From 6a2a145181efa436464fa04991866820a2337d54 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 09:55:07 -0700 Subject: [PATCH 084/112] [BugFix] Add missing quotes for auto strategy in tutorial notebooks (#7541) (#7548) * Add missing quotes for auto strategy * Revert trainer.gpus to trainer.devices in Self_Supervised_Pre_Training.ipynb --------- Signed-off-by: Abhishree Signed-off-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Sasha Meister --- tutorials/asr/ASR_TTS_Tutorial.ipynb | 2 +- tutorials/asr/Self_Supervised_Pre_Training.ipynb | 4 ++-- tutorials/asr/Speech_Commands.ipynb | 2 +- tutorials/asr/Voice_Activity_Detection.ipynb | 2 +- .../speech_enhancement/Speech_Enhancement_with_NeMo.ipynb | 4 ++-- tutorials/nlp/Entity_Linking_Medical.ipynb | 2 +- tutorials/nlp/GLUE_Benchmark.ipynb | 2 +- tutorials/nlp/Joint_Intent_and_Slot_Classification.ipynb | 4 ++-- tutorials/nlp/Punctuation_and_Capitalization.ipynb | 4 ++-- .../nlp/Punctuation_and_Capitalization_Lexical_Audio.ipynb | 4 ++-- tutorials/nlp/Relation_Extraction-BioMegatron.ipynb | 2 +- tutorials/nlp/Text_Classification_Sentiment_Analysis.ipynb | 4 ++-- tutorials/nlp/Token_Classification-BioMegatron.ipynb | 2 +- .../nlp/Token_Classification_Named_Entity_Recognition.ipynb | 2 +- tutorials/nlp/Zero_Shot_Intent_Recognition.ipynb | 2 +- tutorials/speaker_tasks/Speaker_Diarization_Training.ipynb | 2 +- .../speaker_tasks/Speaker_Identification_Verification.ipynb | 2 +- 17 files changed, 23 insertions(+), 23 deletions(-) diff --git a/tutorials/asr/ASR_TTS_Tutorial.ipynb b/tutorials/asr/ASR_TTS_Tutorial.ipynb index 9bbcc8e4aa34..267c84bca9d2 100644 --- a/tutorials/asr/ASR_TTS_Tutorial.ipynb +++ b/tutorials/asr/ASR_TTS_Tutorial.ipynb @@ -553,7 +553,7 @@ "config.trainer.max_epochs = NUM_EPOCHS\n", "\n", "config.trainer.devices = 1\n", - "config.trainer.strategy = auto # use 1 device, no need for ddp strategy\n", + "config.trainer.strategy = 'auto' # use 1 device, no need for ddp strategy\n", "\n", "OmegaConf.resolve(config)" ] diff --git a/tutorials/asr/Self_Supervised_Pre_Training.ipynb b/tutorials/asr/Self_Supervised_Pre_Training.ipynb index c39445dabfd1..5f2c4dcc14c8 100644 --- a/tutorials/asr/Self_Supervised_Pre_Training.ipynb +++ b/tutorials/asr/Self_Supervised_Pre_Training.ipynb @@ -316,7 +316,7 @@ " cfg.trainer.devices = 1\n", "else:\n", " cfg.trainer.accelerator = 'cpu'\n", - " cfg.trainer.strategy = auto\n", + " cfg.trainer.strategy = 'auto'\n", " cfg.trainer.devices = 0\n", "\n", "cfg.exp_manager.exp_dir = data_dir + \"/content/exp\"\n", @@ -538,7 +538,7 @@ " cfg.trainer.devices = 1\n", "else:\n", " cfg.trainer.accelerator = 'cpu'\n", - " cfg.trainer.strategy = auto\n", + " cfg.trainer.strategy = 'auto'\n", " cfg.trainer.devices = 0\n", "\n", "cfg.model.tokenizer.dir = data_dir + \"/tokenizers/an4/tokenizer_spe_unigram_v128/\" # note this is a directory, not a path to a vocabulary file\n", diff --git a/tutorials/asr/Speech_Commands.ipynb b/tutorials/asr/Speech_Commands.ipynb index c1566ae71850..e46ac4f7ec04 100644 --- a/tutorials/asr/Speech_Commands.ipynb +++ b/tutorials/asr/Speech_Commands.ipynb @@ -441,7 +441,7 @@ "config.trainer.max_epochs = 5\n", "\n", "# Remove distributed training flags\n", - "config.trainer.strategy = auto" + "config.trainer.strategy = 'auto'" ], "execution_count": null, "outputs": [] diff --git a/tutorials/asr/Voice_Activity_Detection.ipynb b/tutorials/asr/Voice_Activity_Detection.ipynb index 7c7b95e99416..fb7dbe27d21f 100644 --- a/tutorials/asr/Voice_Activity_Detection.ipynb +++ b/tutorials/asr/Voice_Activity_Detection.ipynb @@ -462,7 +462,7 @@ "config.trainer.max_epochs = 5\n", "\n", "# Remove distributed training flags\n", - "config.trainer.strategy = auto" + "config.trainer.strategy = 'auto'" ] }, { diff --git a/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb b/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb index d7cd6571c16a..09226c83d654 100644 --- a/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb +++ b/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb @@ -667,7 +667,7 @@ "config.trainer.max_epochs = 10\n", "\n", "# Remove distributed training flags\n", - "config.trainer.strategy = auto\n", + "config.trainer.strategy = 'auto'\n", "\n", "# Instantiate the trainer\n", "trainer = pl.Trainer(**config.trainer)" @@ -1144,7 +1144,7 @@ "config_dual_output.trainer.max_epochs = 10\n", "\n", "# Remove distributed training flags\n", - "config_dual_output.trainer.strategy = auto\n", + "config_dual_output.trainer.strategy = 'auto'\n", "\n", "# Instantiate the trainer\n", "trainer = pl.Trainer(**config_dual_output.trainer)\n", diff --git a/tutorials/nlp/Entity_Linking_Medical.ipynb b/tutorials/nlp/Entity_Linking_Medical.ipynb index 4f56a85eabfa..78fe66d9f233 100644 --- a/tutorials/nlp/Entity_Linking_Medical.ipynb +++ b/tutorials/nlp/Entity_Linking_Medical.ipynb @@ -187,7 +187,7 @@ "cfg.model.validation_ds.data_file = os.path.join(DATA_DIR, \"tiny_example_validation_pairs.tsv\")\n", "\n", "# remove distributed training flags\n", - "cfg.trainer.strategy = auto\n", + "cfg.trainer.strategy = 'auto'\n", "cfg.trainer.accelerator = None" ] }, diff --git a/tutorials/nlp/GLUE_Benchmark.ipynb b/tutorials/nlp/GLUE_Benchmark.ipynb index 445ff6705028..b77b3439b444 100644 --- a/tutorials/nlp/GLUE_Benchmark.ipynb +++ b/tutorials/nlp/GLUE_Benchmark.ipynb @@ -342,7 +342,7 @@ "# config.trainer.amp_level = O1\n", "\n", "# remove distributed training flags\n", - "config.trainer.strategy = auto\n", + "config.trainer.strategy = 'auto'\n", "\n", "# setup max number of steps to reduce training time for demonstration purposes of this tutorial\n", "config.trainer.max_steps = 128\n", diff --git a/tutorials/nlp/Joint_Intent_and_Slot_Classification.ipynb b/tutorials/nlp/Joint_Intent_and_Slot_Classification.ipynb index 7513183fba28..675fdfd5351c 100644 --- a/tutorials/nlp/Joint_Intent_and_Slot_Classification.ipynb +++ b/tutorials/nlp/Joint_Intent_and_Slot_Classification.ipynb @@ -286,7 +286,7 @@ "# config.trainer.amp_level = O1\n", "\n", "# remove distributed training flags\n", - "config.trainer.strategy = auto\n", + "config.trainer.strategy = 'auto'\n", "\n", "# setup a small number of epochs for demonstration purposes of this tutorial\n", "config.trainer.max_epochs = 5\n", @@ -705,7 +705,7 @@ "config.trainer.accelerator = accelerator\n", "\n", "# remove distributed training flags\n", - "config.trainer.strategy = auto\n", + "config.trainer.strategy = 'auto'\n", "\n", "trainer = pl.Trainer(**config.trainer)\n", "config.exp_manager.exp_dir = os.path.join(DATA_DIR, \"output/\" + run_name)\n", diff --git a/tutorials/nlp/Punctuation_and_Capitalization.ipynb b/tutorials/nlp/Punctuation_and_Capitalization.ipynb index e9d1060f6442..f88c33fada34 100644 --- a/tutorials/nlp/Punctuation_and_Capitalization.ipynb +++ b/tutorials/nlp/Punctuation_and_Capitalization.ipynb @@ -550,7 +550,7 @@ "config.trainer.max_epochs = 1\n", "\n", "# Remove distributed training flags\n", - "config.trainer.strategy = auto\n", + "config.trainer.strategy = 'auto'\n", "\n", "trainer = pl.Trainer(**config.trainer)" ] @@ -745,7 +745,7 @@ "config.trainer.accelerator = accelerator\n", "config.trainer.precision = 16 if torch.cuda.is_available() else 32\n", "config.trainer.max_epochs = 1\n", - "config.trainer.strategy = auto\n", + "config.trainer.strategy = 'auto'\n", "\n", "# Exp manager\n", "config.exp_manager.explicit_log_dir = 'tarred_experiment'\n", diff --git a/tutorials/nlp/Punctuation_and_Capitalization_Lexical_Audio.ipynb b/tutorials/nlp/Punctuation_and_Capitalization_Lexical_Audio.ipynb index 778e14e63b70..2afbb19c0e66 100644 --- a/tutorials/nlp/Punctuation_and_Capitalization_Lexical_Audio.ipynb +++ b/tutorials/nlp/Punctuation_and_Capitalization_Lexical_Audio.ipynb @@ -645,7 +645,7 @@ "config.trainer.max_epochs = 1\n", "\n", "# Remove distributed training flags\n", - "config.trainer.strategy = auto\n", + "config.trainer.strategy = 'auto'\n", "config.exp_manager.use_datetime_version=False\n", "config.exp_manager.explicit_log_dir='Punctuation_And_Capitalization_Lexical_Audio'\n", "\n", @@ -860,7 +860,7 @@ "config.trainer.accelerator = accelerator\n", "config.trainer.precision = 16 if torch.cuda.is_available() else 32\n", "config.trainer.max_epochs = 1\n", - "config.trainer.strategy = auto\n", + "config.trainer.strategy = 'auto'\n", "\n", "# Exp manager\n", "config.exp_manager.explicit_log_dir = 'tarred_experiment'\n", diff --git a/tutorials/nlp/Relation_Extraction-BioMegatron.ipynb b/tutorials/nlp/Relation_Extraction-BioMegatron.ipynb index 451c40152c8d..d6b1e98b428e 100644 --- a/tutorials/nlp/Relation_Extraction-BioMegatron.ipynb +++ b/tutorials/nlp/Relation_Extraction-BioMegatron.ipynb @@ -403,7 +403,7 @@ "config.trainer.precision = 16 if torch.cuda.is_available() else 32\n", "\n", "# remove distributed training flags\n", - "config.trainer.strategy = auto\n", + "config.trainer.strategy = 'auto'\n", "\n", "trainer = pl.Trainer(**config.trainer)" ] diff --git a/tutorials/nlp/Text_Classification_Sentiment_Analysis.ipynb b/tutorials/nlp/Text_Classification_Sentiment_Analysis.ipynb index 8e44aca9d0d1..bbb6a868a4da 100644 --- a/tutorials/nlp/Text_Classification_Sentiment_Analysis.ipynb +++ b/tutorials/nlp/Text_Classification_Sentiment_Analysis.ipynb @@ -370,7 +370,7 @@ "# config.trainer.amp_level = O1\n", "\n", "# disable distributed training when using Colab to prevent the errors\n", - "config.trainer.strategy = auto\n", + "config.trainer.strategy = 'auto'\n", "\n", "# setup max number of steps to reduce training time for demonstration purposes of this tutorial\n", "# Training stops when max_step or max_epochs is reached (earliest)\n", @@ -573,7 +573,7 @@ "# create a copy of the trainer config and update it to be used for final evaluation\n", "eval_trainer_cfg = config.trainer.copy()\n", "eval_trainer_cfg.accelerator = 'gpu' if torch.cuda.is_available() else 'cpu' # it is safer to perform evaluation on single GPU as PT is buggy with the last batch on multi-GPUs\n", - "eval_trainer_cfg.strategy = auto # 'ddp' is buggy with test process in the current PT, it looks like it has been fixed in the latest master\n", + "eval_trainer_cfg.strategy = 'auto' # 'ddp' is buggy with test process in the current PT, it looks like it has been fixed in the latest master\n", "eval_trainer = pl.Trainer(**eval_trainer_cfg)\n", "\n", "eval_trainer.test(model=eval_model, verbose=False) # test_dataloaders=eval_dataloader,\n" diff --git a/tutorials/nlp/Token_Classification-BioMegatron.ipynb b/tutorials/nlp/Token_Classification-BioMegatron.ipynb index e5e5aa81b859..afbc8394aa84 100644 --- a/tutorials/nlp/Token_Classification-BioMegatron.ipynb +++ b/tutorials/nlp/Token_Classification-BioMegatron.ipynb @@ -434,7 +434,7 @@ "config.trainer.precision = 16 if torch.cuda.is_available() else 32\n", "\n", "# remove distributed training flags\n", - "config.trainer.strategy = auto\n", + "config.trainer.strategy = 'auto'\n", "\n", "trainer = pl.Trainer(**config.trainer)" ] diff --git a/tutorials/nlp/Token_Classification_Named_Entity_Recognition.ipynb b/tutorials/nlp/Token_Classification_Named_Entity_Recognition.ipynb index 1c1999cc08c1..3ab98f6c19fd 100644 --- a/tutorials/nlp/Token_Classification_Named_Entity_Recognition.ipynb +++ b/tutorials/nlp/Token_Classification_Named_Entity_Recognition.ipynb @@ -533,7 +533,7 @@ "# config.trainer.amp_level = O1\n", "\n", "# remove distributed training flags\n", - "config.trainer.strategy = auto\n", + "config.trainer.strategy = 'auto'\n", "\n", "# setup max number of steps to reduce training time for demonstration purposes of this tutorial\n", "config.trainer.max_steps = 32\n", diff --git a/tutorials/nlp/Zero_Shot_Intent_Recognition.ipynb b/tutorials/nlp/Zero_Shot_Intent_Recognition.ipynb index f571fa176e96..7f1baf536d87 100644 --- a/tutorials/nlp/Zero_Shot_Intent_Recognition.ipynb +++ b/tutorials/nlp/Zero_Shot_Intent_Recognition.ipynb @@ -400,7 +400,7 @@ "# config.trainer.amp_level = O1\n", "\n", "# remove distributed training flags\n", - "config.trainer.strategy = auto\n", + "config.trainer.strategy = 'auto'\n", "\n", "# setup max number of steps to reduce training time for demonstration purposes of this tutorial\n", "config.trainer.max_steps = 128\n", diff --git a/tutorials/speaker_tasks/Speaker_Diarization_Training.ipynb b/tutorials/speaker_tasks/Speaker_Diarization_Training.ipynb index efd86e1ef242..7db905b6d225 100644 --- a/tutorials/speaker_tasks/Speaker_Diarization_Training.ipynb +++ b/tutorials/speaker_tasks/Speaker_Diarization_Training.ipynb @@ -761,7 +761,7 @@ "source": [ "config.model.diarizer.speaker_embeddings.model_path=\"titanet_large\"\n", "config.trainer.max_epochs = 5\n", - "config.trainer.strategy = auto" + "config.trainer.strategy = 'auto'" ] }, { diff --git a/tutorials/speaker_tasks/Speaker_Identification_Verification.ipynb b/tutorials/speaker_tasks/Speaker_Identification_Verification.ipynb index f0ad1c19f5c9..5a5ec9c46552 100644 --- a/tutorials/speaker_tasks/Speaker_Identification_Verification.ipynb +++ b/tutorials/speaker_tasks/Speaker_Identification_Verification.ipynb @@ -475,7 +475,7 @@ "config.trainer.max_epochs = 10\n", "\n", "# Remove distributed training flags\n", - "config.trainer.strategy = auto\n", + "config.trainer.strategy = 'auto'\n", "\n", "# Remove augmentations\n", "config.model.train_ds.augmentor=None" From 6db0b2b782251b35c9fe104c22507c7354239a54 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 13:06:36 -0700 Subject: [PATCH 085/112] add build os key (#7596) (#7599) * add build os key * add tools * update to stable version --------- Signed-off-by: Nithin Rao Koluguri Co-authored-by: Nithin Rao Signed-off-by: Sasha Meister --- .readthedocs.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 226be6a7eab0..5ee18e6dee1e 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -20,12 +20,16 @@ # Required field. version: 2 +build: + os: ubuntu-22.04 + tools: + python: "3.10" + # Build documentation in the docs/ directory with Sphinx. sphinx: configuration: docs/source/conf.py # Set the version of Python and requirements required to build your docs python: - version: 3.8 install: - requirements: requirements/requirements_docs.txt From 8f2a31caead1e6a11553681713781b03460ceac7 Mon Sep 17 00:00:00 2001 From: Jan Lasek Date: Mon, 2 Oct 2023 22:43:54 +0200 Subject: [PATCH 086/112] StarCoder SFT test + bump PyT NGC image to 23.09 (#7540) * Add SFT StarCoder test Signed-off-by: Jan Lasek * Remove _modify_config call as it is covered in load_from_nemo just below Signed-off-by: Jan Lasek * Test with pyt:23.09 container Signed-off-by: Jan Lasek --------- Signed-off-by: Jan Lasek Signed-off-by: Sasha Meister --- Jenkinsfile | 36 ++++++++++++++++++- .../tuning/megatron_gpt_sft.py | 1 - 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 92aa65ae660b..3d262931915b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,7 @@ pipeline { agent { docker { - image 'nvcr.io/nvidia/pytorch:23.08-py3' + image 'nvcr.io/nvidia/pytorch:23.09-py3' args '--device=/dev/nvidia0 --gpus all --user 0:128 -v /home/TestData:/home/TestData -v $HOME/.cache:/root/.cache --shm-size=8g --env TRANSFORMERS_OFFLINE=1 --env HYDRA_FULL_ERROR=1' } } @@ -3621,6 +3621,40 @@ assert_frame_equal(training_curve, gt_curve, rtol=1e-3, atol=1e-3)"''' sh "rm -rf examples/nlp/language_modeling/gpt_sft_results" } } + stage('L2: Megatron GPT Finetuning StarCoder PP=1') { + when { + anyOf { + branch 'main' + changeRequest target: 'main' + } + } + failFast true + steps { + sh "python examples/nlp/language_modeling/tuning/megatron_gpt_sft.py \ + trainer.devices=1 \ + trainer.num_nodes=1 \ + trainer.precision=32 \ + trainer.max_steps=4 \ + trainer.val_check_interval=4 \ + trainer.enable_checkpointing=False \ + +trainer.limit_val_batches=2 \ + +trainer.limit_test_batches=2 \ + exp_manager.checkpoint_callback_params.save_best_model=False \ + exp_manager.exp_dir=examples/nlp/language_modeling/gpt_sft_results \ + model.optim.name=distributed_fused_adam \ + model.restore_from_path=/home/TestData/nlp/megatron_gpt/starcoder-ci-nemo/megatron_starcoder_tp1_pp1.nemo \ + model.tensor_model_parallel_size=1 \ + model.pipeline_model_parallel_size=1 \ + model.data.train_ds.file_names=[/home/TestData/nlp/megatron_sft/quarel.jsonl] \ + model.data.train_ds.num_workers=0 \ + model.data.test_ds.file_names=[/home/TestData/nlp/megatron_sft/quarel.jsonl] \ + model.data.validation_ds.num_workers=0 \ + model.data.validation_ds.file_names=[/home/TestData/nlp/megatron_sft/quarel.jsonl] \ + model.data.test_ds.num_workers=0 \ + model.data.train_ds.concat_sampling_probabilities=[1.0]" + sh "rm -rf examples/nlp/language_modeling/gpt_sft_results" + } + } stage('L2: Megatron GPT PEFT Lora PP=2') { when { anyOf { diff --git a/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py b/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py index 870ff2921cce..9a70671f8073 100644 --- a/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py +++ b/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py @@ -202,7 +202,6 @@ def main(cfg) -> None: return_config=True, save_restore_connector=save_restore_connector, ) - gpt_cfg = _modify_config(gpt_cfg, cfg, add_cfg_to_tree=False) model = load_from_nemo(MegatronGPTSFTModel, cfg, trainer, gpt_cfg, modify_confg_fn=_modify_config) else: validate_checkpoint_loading_args(cfg.model.pretrained_checkpoint) From 8f306a20f7436af0ef1ac0825c92b1d2898aece9 Mon Sep 17 00:00:00 2001 From: Adi Renduchintala Date: Mon, 2 Oct 2023 20:20:02 -0700 Subject: [PATCH 087/112] defaults changed (#7600) * defaults changed Signed-off-by: arendu * typo Signed-off-by: arendu * update Signed-off-by: arendu --------- Signed-off-by: arendu Signed-off-by: Sasha Meister --- .../metric_calculation/peft_metric_calc.py | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/scripts/metric_calculation/peft_metric_calc.py b/scripts/metric_calculation/peft_metric_calc.py index ca13f83281c5..9fd2452fb17b 100755 --- a/scripts/metric_calculation/peft_metric_calc.py +++ b/scripts/metric_calculation/peft_metric_calc.py @@ -21,26 +21,18 @@ """ -This script can be used to calcualte exact match and F1 scores for many different tasks, not just squad. - -Example command for T5 Preds - - ``` - python squad_metric_calc.py \ - --ground-truth squad_test_gt.jsonl \ - --preds squad_preds_t5.txt - ``` +This script can be used to calcualte exact match and F1 scores for many different tasks. +The file "squad_test_predictions.jsonl" is assumed to be generated by the +`examples/nlp/language_modeling/tuning/megatron_gpt_peft_eval.py` script Example command for GPT Preds ``` - python squad_metric_calc.py \ - --ground-truth squad_test_gt.jsonl \ - --preds squad_preds_gpt.txt \ - --split-string "answer:" + python peft_metric_calc.py \ + --pred_file squad_test_predictions.jsonl \ + --label_field "original_answers" \ ``` - In this case, the prediction file will be split on "answer: " when looking for the LM's predicted answer. """ @@ -92,21 +84,21 @@ def metric_max_over_ground_truths(metric_fn, prediction, ground_truths): def main(): parser = argparse.ArgumentParser(description='Process some integers.') parser.add_argument( - '--pred-file', + '--pred_file', type=str, help="Text file with test set prompts + model predictions. Prediction file can be made by running NeMo/examples/nlp/language_modeling/megatron_gpt_prompt_learning_eval.py", ) parser.add_argument( - '--pred-field', + '--pred_field', type=str, help="The field in the json file that contains the prediction tokens", default="pred", ) parser.add_argument( - '--ground-truth-field', + '--label_field', type=str, help="The field in the json file that contains the ground truth tokens", - default="original_answers", + default="label", ) args = parser.parse_args() @@ -120,7 +112,7 @@ def main(): pred_line = json.loads(preds[i]) pred_answer = pred_line[args.pred_field] - true_answers = pred_line[args.ground_truth_field] + true_answers = pred_line[args.label_field] if not isinstance(true_answers, list): true_answers = [true_answers] From 2f6fa29c0078000e7165301d6260fe448a03ca67 Mon Sep 17 00:00:00 2001 From: Giacomo Leone Maria Cavallini <72698188+GiacomoLeoneMaria@users.noreply.github.com> Date: Tue, 3 Oct 2023 05:40:12 +0200 Subject: [PATCH 088/112] add ItalianPhonemesTokenizer (#7587) * add ItalianPhonemesTokenizer Signed-off-by: GiacomoLeoneMaria * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix Italian phonemes Signed-off-by: GiacomoLeoneMaria * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add test Signed-off-by: GiacomoLeoneMaria --------- Signed-off-by: GiacomoLeoneMaria Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../tokenizers/text_to_speech/ipa_lexicon.py | 7 +- .../text_to_speech/tts_tokenizers.py | 73 ++++++++++++++++++- .../text_to_speech/test_tts_tokenizers.py | 16 ++++ 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/nemo/collections/common/tokenizers/text_to_speech/ipa_lexicon.py b/nemo/collections/common/tokenizers/text_to_speech/ipa_lexicon.py index 2e1bb359102b..338b3536519b 100644 --- a/nemo/collections/common/tokenizers/text_to_speech/ipa_lexicon.py +++ b/nemo/collections/common/tokenizers/text_to_speech/ipa_lexicon.py @@ -88,7 +88,7 @@ 'ɢ','ʛ','ɦ','ɧ','ħ','ɥ','ʜ','ɨ','ɬ','ɫ','ɮ','ʟ', 'ɱ','ɯ','ɰ','ɳ','ɵ','ɸ','œ','ɶ','ʘ','ɺ','ɻ','ʀ','ʁ', 'ɽ','ʂ','ʈ','ʧ','ʉ','ʋ','ⱱ','ɤ','ʍ','χ','ʏ','ʑ','ʐ', - 'ʔ','ʡ','ʕ','ʢ','ǀ','ǁ','ǂ','ᵻ' + 'ʔ','ʡ','ʕ','ʢ','ǀ','ǁ','ǂ','ᵻ', 'ʃ','ː', ), } @@ -181,7 +181,10 @@ def get_ipa_punctuation_list(locale): '↑', '→', '↗', - '↘,', + '↘', + '”', + '’', + '-', ] ) elif locale == "es-ES": diff --git a/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py b/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py index 32f725c9c73f..25b9d88a59dc 100644 --- a/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py +++ b/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py @@ -284,7 +284,7 @@ def __init__( non_default_punct_list: List of punctuation marks which will be used instead default. """ - it_alphabet = "abcdefghijklmnopqrstuvwxyzàèéìòù" + it_alphabet = "abcdefghijklmnopqrstuvwxyzàèéìòùó" super().__init__( chars=it_alphabet, punct=punct, @@ -367,6 +367,77 @@ def encode(self, text): return [self._token2id[p] for p in cs] +class ItalianPhonemesTokenizer(BaseCharsTokenizer): + # fmt: off + PUNCT_LIST = ( + ',', '.', '!', '?', '-', + ':', ';', '/', '"', '(', + ')', '[', ']', '{', '}', + '„', '“', '”', '‘', '’', '‒', '—', '«', '»', '‹', '›', '_', + ) + # fmt: on + + def __init__( + self, + punct=True, + apostrophe=True, + add_blank_at=None, + pad_with_space=False, + non_default_punct_list=None, + text_preprocessing_func=italian_text_preprocessing, + ): + """Italian phoneme-based tokenizer. + Args: + punct: Whether to reserve grapheme for basic punctuation or not. + apostrophe: Whether to use apostrophe or not. + add_blank_at: Add blank to labels in the specified order ("last") or after tokens (any non None), + if None then no blank in labels. + pad_with_space: Whether to pad text with spaces at the beginning and at the end or not. + non_default_punct_list: List of punctuation marks which will be used instead default. + text_preprocessing_func: Text preprocessing function for correct execution of the tokenizer. + Currently, it only applies lower() function. + """ + + it_ipa = "abcdefghijklmnopqrstuvwxyzàèéìòùóæɐɑɔəɚɜɬɹʌʔᵻðŋɛɡɣɪɲɾʃʊʎʒʝβθd͡'t͡'øɒɕɓçɖɘɝɞɟʄɡɠɢʛɦɧħɥʜɨɬɫɮʟɱɯɰɳɵɸœɶʘɺɻʀʁɽʂʈʧʉʋⱱɤʍχʏʑʐʔʡʕʢǀǁǂᵻʃ'ː" + super().__init__( + chars=it_ipa, + punct=punct, + apostrophe=apostrophe, + add_blank_at=add_blank_at, + pad_with_space=pad_with_space, + non_default_punct_list=non_default_punct_list, + text_preprocessing_func=text_preprocessing_func, + ) + + def encode(self, text): + """See base class.""" + cs, space, tokens = [], self.tokens[self.space], set(self.tokens) + + text = self.text_preprocessing_func(text) + for c in text: + # Add space if last one isn't one + if c == space and len(cs) > 0 and cs[-1] != space: + cs.append(c) + # Add next char + elif (c.isalnum() or c == "'" or c == "\u0303") and c in tokens: + cs.append(c) + # Add punct + elif (c in self.PUNCT_LIST) and self.punct: + cs.append(c) + # Warn about unknown char + elif c != space: + logging.warning(f"Text: [{text}] contains unknown char: [{c}]. Symbol will be skipped.") + + # Remove trailing spaces + while cs[-1] == space: + cs.pop() + + if self.pad_with_space: + cs = [space] + cs + [space] + + return [self._token2id[p] for p in cs] + + class EnglishPhonemesTokenizer(BaseTokenizer): # fmt: off PUNCT_LIST = ( # Derived from LJSpeech and "/" additionally diff --git a/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py b/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py index 62c571bc16b7..bc065e75fa66 100644 --- a/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py +++ b/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py @@ -34,6 +34,10 @@ class TestTTSTokenizers: "BUENOS": ["bwˈenos"], "DÍAS": ["dˈias"], } + PHONEME_DICT_IT = { + "CIAO": ["tʃˈao"], + "MONDO": ["mˈondo"], + } @staticmethod def _parse_text(tokenizer, text): @@ -146,6 +150,18 @@ def test_ipa_tokenizer_de_de(self): assert chars == expected_output + @pytest.mark.run_only_on('CPU') + @pytest.mark.unit + def test_ipa_tokenizer_it_it(self): + input_text = "Ciao mondo" + expected_output = "tʃˈao mˈondo" + + g2p = IpaG2p(phoneme_dict=self.PHONEME_DICT_IT, locale="it-IT") + tokenizer = IPATokenizer(g2p=g2p, locale="it-IT") + chars, tokens = self._parse_text(tokenizer, input_text) + + assert chars == expected_output + @pytest.mark.run_only_on('CPU') @pytest.mark.unit def test_ipa_tokenizer_en_us(self): From 71f327f29c1328e0d594dc30880a126363f5061e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 20:42:39 -0700 Subject: [PATCH 089/112] best ckpt fix (#7564) (#7588) Signed-off-by: dimapihtar Co-authored-by: Dmytro Pykhtar <37850217+dimapihtar@users.noreply.github.com> Signed-off-by: Sasha Meister --- nemo/utils/callbacks/nemo_model_checkpoint.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nemo/utils/callbacks/nemo_model_checkpoint.py b/nemo/utils/callbacks/nemo_model_checkpoint.py index d4759ecf5949..5089f50bd527 100644 --- a/nemo/utils/callbacks/nemo_model_checkpoint.py +++ b/nemo/utils/callbacks/nemo_model_checkpoint.py @@ -209,6 +209,8 @@ def on_train_end(self, trainer, pl_module): "were found. Saving latest model instead." ) else: + if os.path.isdir(self.best_model_path.split('.ckpt')[0]): + self.best_model_path = self.best_model_path.split('.ckpt')[0] self.best_model_path = trainer.strategy.broadcast(self.best_model_path) trainer._checkpoint_connector.restore(self.best_model_path) From 6517360e3e3ab4621c2278fc38773213f999aac9 Mon Sep 17 00:00:00 2001 From: George <37293288+Jorjeous@users.noreply.github.com> Date: Tue, 3 Oct 2023 18:54:21 +0400 Subject: [PATCH 090/112] Add files via upload (#7598) specifies the branch Signed-off-by: George <37293288+Jorjeous@users.noreply.github.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Signed-off-by: Sasha Meister --- tutorials/tools/SDE_HowTo_v2.ipynb | 837 +---------------------------- 1 file changed, 5 insertions(+), 832 deletions(-) diff --git a/tutorials/tools/SDE_HowTo_v2.ipynb b/tutorials/tools/SDE_HowTo_v2.ipynb index a6d3f8dd2723..4087219a8dad 100644 --- a/tutorials/tools/SDE_HowTo_v2.ipynb +++ b/tutorials/tools/SDE_HowTo_v2.ipynb @@ -44,840 +44,13 @@ "cell_type": "code", "execution_count": null, "metadata": { - "id": "c3919489", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "87f4e2f4-a06c-432d-d986-429fbe6714af" + "id": "c3919489" }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Cloning into 'NeMo'...\n", - "remote: Enumerating objects: 121443, done.\u001b[K\n", - "remote: Counting objects: 100% (1811/1811), done.\u001b[K\n", - "remote: Compressing objects: 100% (945/945), done.\u001b[K\n", - "remote: Total 121443 (delta 1299), reused 1238 (delta 864), pack-reused 119632\u001b[K\n", - "Receiving objects: 100% (121443/121443), 228.05 MiB | 21.34 MiB/s, done.\n", - "Resolving deltas: 100% (90608/90608), done.\n", - "Get:1 http://security.ubuntu.com/ubuntu jammy-security InRelease [110 kB]\n", - "Hit:2 http://archive.ubuntu.com/ubuntu jammy InRelease\n", - "Get:3 http://archive.ubuntu.com/ubuntu jammy-updates InRelease [119 kB]\n", - "Get:4 https://cloud.r-project.org/bin/linux/ubuntu jammy-cran40/ InRelease [3,626 B]\n", - "Get:5 http://archive.ubuntu.com/ubuntu jammy-backports InRelease [109 kB]\n", - "Hit:6 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64 InRelease\n", - "Hit:7 https://ppa.launchpadcontent.net/c2d4u.team/c2d4u4.0+/ubuntu jammy InRelease\n", - "Get:8 https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu jammy InRelease [18.1 kB]\n", - "Hit:9 https://ppa.launchpadcontent.net/graphics-drivers/ppa/ubuntu jammy InRelease\n", - "Hit:10 https://ppa.launchpadcontent.net/ubuntugis/ppa/ubuntu jammy InRelease\n", - "Get:11 http://security.ubuntu.com/ubuntu jammy-security/main amd64 Packages [962 kB]\n", - "Get:12 http://security.ubuntu.com/ubuntu jammy-security/restricted amd64 Packages [1,059 kB]\n", - "Get:13 http://security.ubuntu.com/ubuntu jammy-security/universe amd64 Packages [993 kB]\n", - "Get:14 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 Packages [1,254 kB]\n", - "Get:15 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 Packages [1,230 kB]\n", - "Get:16 http://archive.ubuntu.com/ubuntu jammy-updates/restricted amd64 Packages [1,079 kB]\n", - "Get:17 https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu jammy/main amd64 Packages [21.8 kB]\n", - "Fetched 6,959 kB in 1s (5,794 kB/s)\n", - "Reading package lists... Done\n", - "Reading package lists... Done\n", - "Building dependency tree... Done\n", - "Reading state information... Done\n", - "libsndfile1 is already the newest version (1.0.31-2build1).\n", - "ffmpeg is already the newest version (7:4.4.2-0ubuntu0.22.04.1).\n", - "0 upgraded, 0 newly installed, 0 to remove and 16 not upgraded.\n", - "Requirement already satisfied: pip in /usr/local/lib/python3.10/dist-packages (23.1.2)\n", - "Collecting pip\n", - " Downloading pip-23.2.1-py3-none-any.whl (2.1 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.1/2.1 MB\u001b[0m \u001b[31m12.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hInstalling collected packages: pip\n", - " Attempting uninstall: pip\n", - " Found existing installation: pip 23.1.2\n", - " Uninstalling pip-23.1.2:\n", - " Successfully uninstalled pip-23.1.2\n", - "Successfully installed pip-23.2.1\n", - "Uninstalling stuff\n", - "\u001b[33mWARNING: Skipping nemo_toolkit as it is not installed.\u001b[0m\u001b[33m\n", - "\u001b[0m\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n", - "\u001b[0m\u001b[33mWARNING: Skipping sacrebleu as it is not installed.\u001b[0m\u001b[33m\n", - "\u001b[0m\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n", - "\u001b[0m\u001b[33mWARNING: Skipping nemo_asr as it is not installed.\u001b[0m\u001b[33m\n", - "\u001b[0m\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n", - "\u001b[0m\u001b[33mWARNING: Skipping nemo_nlp as it is not installed.\u001b[0m\u001b[33m\n", - "\u001b[0m\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n", - "\u001b[0m\u001b[33mWARNING: Skipping nemo_tts as it is not installed.\u001b[0m\u001b[33m\n", - "\u001b[0m\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n", - "\u001b[0mInstalling nemo\n", - "Obtaining file:///content/NeMo\n", - " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", - " Checking if build backend supports build_editable ... \u001b[?25l\u001b[?25hdone\n", - " Getting requirements to build editable ... \u001b[?25l\u001b[?25hdone\n", - " Preparing editable metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", - "Collecting huggingface-hub (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for huggingface-hub from https://files.pythonhosted.org/packages/7f/c4/adcbe9a696c135578cabcbdd7331332daad4d49b7c43688bc2d36b3a47d2/huggingface_hub-0.16.4-py3-none-any.whl.metadata\n", - " Downloading huggingface_hub-0.16.4-py3-none-any.whl.metadata (12 kB)\n", - "Requirement already satisfied: numba in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (0.56.4)\n", - "Requirement already satisfied: numpy<1.24,>=1.22 in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (1.23.5)\n", - "Collecting onnx>=1.7.0 (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for onnx>=1.7.0 from https://files.pythonhosted.org/packages/47/d4/f2d212558245e252b936247666c3f5981e6dba62ec470ff8be3df3389364/onnx-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", - " Downloading onnx-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (15 kB)\n", - "Requirement already satisfied: python-dateutil in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (2.8.2)\n", - "Collecting ruamel.yaml (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for ruamel.yaml from https://files.pythonhosted.org/packages/d9/0e/2a05efa11ea33513fbdf4a2e2576fe94fd8fa5ad226dbb9c660886390974/ruamel.yaml-0.17.32-py3-none-any.whl.metadata\n", - " Downloading ruamel.yaml-0.17.32-py3-none-any.whl.metadata (17 kB)\n", - "Requirement already satisfied: scikit-learn in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (1.2.2)\n", - "Collecting setuptools==65.5.1 (from nemo-toolkit==1.21.0rc0)\n", - " Downloading setuptools-65.5.1-py3-none-any.whl (1.2 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.2/1.2 MB\u001b[0m \u001b[31m13.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: tensorboard in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (2.12.3)\n", - "Requirement already satisfied: text-unidecode in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (1.3)\n", - "Requirement already satisfied: torch in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (2.0.1+cu118)\n", - "Requirement already satisfied: tqdm>=4.41.0 in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (4.66.1)\n", - "Collecting wget (from nemo-toolkit==1.21.0rc0)\n", - " Downloading wget-3.2.zip (10 kB)\n", - " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Requirement already satisfied: wrapt in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (1.14.1)\n", - "Collecting black==19.10b0 (from nemo-toolkit==1.21.0rc0)\n", - " Downloading black-19.10b0-py36-none-any.whl (97 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m97.5/97.5 kB\u001b[0m \u001b[31m12.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting click==8.0.2 (from nemo-toolkit==1.21.0rc0)\n", - " Downloading click-8.0.2-py3-none-any.whl (97 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m97.6/97.6 kB\u001b[0m \u001b[31m12.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting isort<6.0.0,>5.1.0 (from nemo-toolkit==1.21.0rc0)\n", - " Downloading isort-5.12.0-py3-none-any.whl (91 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m91.2/91.2 kB\u001b[0m \u001b[31m11.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting parameterized (from nemo-toolkit==1.21.0rc0)\n", - " Downloading parameterized-0.9.0-py2.py3-none-any.whl (20 kB)\n", - "Requirement already satisfied: pytest in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (7.4.1)\n", - "Collecting pytest-runner (from nemo-toolkit==1.21.0rc0)\n", - " Downloading pytest_runner-6.0.0-py3-none-any.whl (7.2 kB)\n", - "Requirement already satisfied: sphinx in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (5.0.2)\n", - "Collecting sphinxcontrib-bibtex (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for sphinxcontrib-bibtex from https://files.pythonhosted.org/packages/79/59/fafc5c480506cc356e2a7ea009d7c7d75812475b4385fe851ae55575661c/sphinxcontrib_bibtex-2.6.1-py3-none-any.whl.metadata\n", - " Downloading sphinxcontrib_bibtex-2.6.1-py3-none-any.whl.metadata (6.1 kB)\n", - "Collecting wandb (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for wandb from https://files.pythonhosted.org/packages/fe/10/18b03623c460fd433525d9b4739af58c5e69f5974328dcdd037cfbc855d7/wandb-0.15.10-py3-none-any.whl.metadata\n", - " Downloading wandb-0.15.10-py3-none-any.whl.metadata (9.6 kB)\n", - "Collecting hydra-core<=1.3.2,>1.3 (from nemo-toolkit==1.21.0rc0)\n", - " Downloading hydra_core-1.3.2-py3-none-any.whl (154 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m154.5/154.5 kB\u001b[0m \u001b[31m16.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting omegaconf<=2.3 (from nemo-toolkit==1.21.0rc0)\n", - " Downloading omegaconf-2.3.0-py3-none-any.whl (79 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m79.5/79.5 kB\u001b[0m \u001b[31m11.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting pytorch-lightning<=2.0.7,>=2.0 (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for pytorch-lightning<=2.0.7,>=2.0 from https://files.pythonhosted.org/packages/d5/ef/39994adec1fe1d5f25fd0dd0a82abcd8bd61fc968283790b9da7463f0279/pytorch_lightning-2.0.7-py3-none-any.whl.metadata\n", - " Downloading pytorch_lightning-2.0.7-py3-none-any.whl.metadata (23 kB)\n", - "Collecting torchmetrics>=0.11.0 (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for torchmetrics>=0.11.0 from https://files.pythonhosted.org/packages/e3/86/47091c33ecf05f8826d134fd518485d4c68ca524c053b2fdd4e041c20547/torchmetrics-1.1.1-py3-none-any.whl.metadata\n", - " Downloading torchmetrics-1.1.1-py3-none-any.whl.metadata (21 kB)\n", - "Collecting transformers>=4.0.1 (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for transformers>=4.0.1 from https://files.pythonhosted.org/packages/13/30/54b59e73400df3de506ad8630284e9fd63f4b94f735423d55fc342181037/transformers-4.33.1-py3-none-any.whl.metadata\n", - " Downloading transformers-4.33.1-py3-none-any.whl.metadata (119 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m119.9/119.9 kB\u001b[0m \u001b[31m12.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting webdataset<=0.1.62,>=0.1.48 (from nemo-toolkit==1.21.0rc0)\n", - " Downloading webdataset-0.1.62-py3-none-any.whl (32 kB)\n", - "Requirement already satisfied: inflect in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (7.0.0)\n", - "Requirement already satisfied: pandas in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (1.5.3)\n", - "Collecting pydantic<2 (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for pydantic<2 from https://files.pythonhosted.org/packages/bc/e0/0371e9b6c910afe502e5fe18cc94562bfd9399617c7b4f5b6e13c29115b3/pydantic-1.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", - " Downloading pydantic-1.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (149 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m149.3/149.3 kB\u001b[0m \u001b[31m17.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting sacremoses>=0.0.43 (from nemo-toolkit==1.21.0rc0)\n", - " Downloading sacremoses-0.0.53.tar.gz (880 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m880.6/880.6 kB\u001b[0m \u001b[31m24.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Collecting sentencepiece<1.0.0 (from nemo-toolkit==1.21.0rc0)\n", - " Downloading sentencepiece-0.1.99-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.3/1.3 MB\u001b[0m \u001b[31m34.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting youtokentome>=1.0.5 (from nemo-toolkit==1.21.0rc0)\n", - " Downloading youtokentome-1.0.6.tar.gz (86 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m86.7/86.7 kB\u001b[0m \u001b[31m11.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Collecting braceexpand (from nemo-toolkit==1.21.0rc0)\n", - " Downloading braceexpand-0.1.7-py2.py3-none-any.whl (5.9 kB)\n", - "Requirement already satisfied: editdistance in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (0.6.2)\n", - "Collecting g2p-en (from nemo-toolkit==1.21.0rc0)\n", - " Downloading g2p_en-2.1.0-py3-none-any.whl (3.1 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.1/3.1 MB\u001b[0m \u001b[31m51.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: ipywidgets in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (7.7.1)\n", - "Collecting jiwer (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for jiwer from https://files.pythonhosted.org/packages/0d/4f/ee537ab20144811dd99321735ff92ef2b3a3230b77ed7454bed4c44d21fc/jiwer-3.0.3-py3-none-any.whl.metadata\n", - " Downloading jiwer-3.0.3-py3-none-any.whl.metadata (2.6 kB)\n", - "Collecting kaldi-python-io (from nemo-toolkit==1.21.0rc0)\n", - " Downloading kaldi-python-io-1.2.2.tar.gz (8.8 kB)\n", - " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Collecting kaldiio (from nemo-toolkit==1.21.0rc0)\n", - " Downloading kaldiio-2.18.0-py3-none-any.whl (28 kB)\n", - "Requirement already satisfied: librosa>=0.9.0 in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (0.10.1)\n", - "Collecting marshmallow (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for marshmallow from https://files.pythonhosted.org/packages/ed/3c/cebfdcad015240014ff08b883d1c0c427f2ba45ae8c6572851b6ef136cad/marshmallow-3.20.1-py3-none-any.whl.metadata\n", - " Downloading marshmallow-3.20.1-py3-none-any.whl.metadata (7.8 kB)\n", - "Requirement already satisfied: matplotlib in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (3.7.1)\n", - "Requirement already satisfied: packaging in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (23.1)\n", - "Collecting pyannote.core (from nemo-toolkit==1.21.0rc0)\n", - " Downloading pyannote.core-5.0.0-py3-none-any.whl (58 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m58.5/58.5 kB\u001b[0m \u001b[31m7.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting pyannote.metrics (from nemo-toolkit==1.21.0rc0)\n", - " Downloading pyannote.metrics-3.2.1-py3-none-any.whl (51 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m51.4/51.4 kB\u001b[0m \u001b[31m6.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting pydub (from nemo-toolkit==1.21.0rc0)\n", - " Downloading pydub-0.25.1-py2.py3-none-any.whl (32 kB)\n", - "Requirement already satisfied: scipy>=0.14 in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (1.10.1)\n", - "Requirement already satisfied: soundfile in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (0.12.1)\n", - "Collecting sox (from nemo-toolkit==1.21.0rc0)\n", - " Downloading sox-1.4.1-py2.py3-none-any.whl (39 kB)\n", - "Collecting texterrors (from nemo-toolkit==1.21.0rc0)\n", - " Downloading texterrors-0.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.1/1.1 MB\u001b[0m \u001b[31m50.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting boto3 (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for boto3 from https://files.pythonhosted.org/packages/6e/7f/ffb72ddc9f465183af04623baf9d0ea70e73cb0957407e4c333b9e0263fb/boto3-1.28.43-py3-none-any.whl.metadata\n", - " Downloading boto3-1.28.43-py3-none-any.whl.metadata (6.7 kB)\n", - "Collecting datasets (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for datasets from https://files.pythonhosted.org/packages/09/7e/fd4d6441a541dba61d0acb3c1fd5df53214c2e9033854e837a99dd9e0793/datasets-2.14.5-py3-none-any.whl.metadata\n", - " Downloading datasets-2.14.5-py3-none-any.whl.metadata (19 kB)\n", - "Collecting einops (from nemo-toolkit==1.21.0rc0)\n", - " Downloading einops-0.6.1-py3-none-any.whl (42 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m42.2/42.2 kB\u001b[0m \u001b[31m4.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting faiss-cpu (from nemo-toolkit==1.21.0rc0)\n", - " Downloading faiss_cpu-1.7.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (17.6 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m17.6/17.6 MB\u001b[0m \u001b[31m60.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting fasttext (from nemo-toolkit==1.21.0rc0)\n", - " Downloading fasttext-0.9.2.tar.gz (68 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m68.8/68.8 kB\u001b[0m \u001b[31m8.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Collecting flask-restful (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for flask-restful from https://files.pythonhosted.org/packages/d7/7b/f0b45f0df7d2978e5ae51804bb5939b7897b2ace24306009da0cc34d8d1f/Flask_RESTful-0.3.10-py2.py3-none-any.whl.metadata\n", - " Downloading Flask_RESTful-0.3.10-py2.py3-none-any.whl.metadata (1.0 kB)\n", - "Collecting ftfy (from nemo-toolkit==1.21.0rc0)\n", - " Downloading ftfy-6.1.1-py3-none-any.whl (53 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m53.1/53.1 kB\u001b[0m \u001b[31m7.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: gdown in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (4.6.6)\n", - "Requirement already satisfied: h5py in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (3.9.0)\n", - "Collecting ijson (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for ijson from https://files.pythonhosted.org/packages/6b/78/2cbeb7020a7a319d148c92331951cfc710864990e32ff6c7f4859729fb48/ijson-3.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", - " Downloading ijson-3.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (20 kB)\n", - "Requirement already satisfied: jieba in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (0.42.1)\n", - "Collecting markdown2 (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for markdown2 from https://files.pythonhosted.org/packages/f1/98/61276a753f078dd2f3171c9a69fd3f451d220e806b2b1cdca41b8e368b0f/markdown2-2.4.10-py2.py3-none-any.whl.metadata\n", - " Downloading markdown2-2.4.10-py2.py3-none-any.whl.metadata (2.0 kB)\n", - "Collecting megatron-core==0.2.0 (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for megatron-core==0.2.0 from https://files.pythonhosted.org/packages/33/f1/d94f2282b91950e31223efc39138748d71907dbf857e5523d1e73619fd62/megatron_core-0.2.0-py3-none-any.whl.metadata\n", - " Downloading megatron_core-0.2.0-py3-none-any.whl.metadata (1.6 kB)\n", - "Requirement already satisfied: nltk>=3.6.5 in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (3.8.1)\n", - "Collecting opencc (from nemo-toolkit==1.21.0rc0)\n", - " Downloading OpenCC-1.1.6-cp310-cp310-manylinux1_x86_64.whl (778 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m778.3/778.3 kB\u001b[0m \u001b[31m70.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting pangu (from nemo-toolkit==1.21.0rc0)\n", - " Downloading pangu-4.0.6.1-py3-none-any.whl (6.4 kB)\n", - "Collecting rapidfuzz (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for rapidfuzz from https://files.pythonhosted.org/packages/35/04/9ca97b17da457ed294519477da2aad0799c9ba8eebf37761a5ca94c35534/rapidfuzz-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", - " Downloading rapidfuzz-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (11 kB)\n", - "Collecting rouge-score (from nemo-toolkit==1.21.0rc0)\n", - " Downloading rouge_score-0.1.2.tar.gz (17 kB)\n", - " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Collecting sacrebleu[ja] (from nemo-toolkit==1.21.0rc0)\n", - " Downloading sacrebleu-2.3.1-py3-none-any.whl (118 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m118.9/118.9 kB\u001b[0m \u001b[31m16.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting sentence-transformers (from nemo-toolkit==1.21.0rc0)\n", - " Downloading sentence-transformers-2.2.2.tar.gz (85 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m86.0/86.0 kB\u001b[0m \u001b[31m12.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Requirement already satisfied: tensorstore in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (0.1.41)\n", - "Collecting zarr (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for zarr from https://files.pythonhosted.org/packages/ba/55/0f5ec28561a1698ac5c11edc5724f8c6d48d01baecf740ffd62107d95e7f/zarr-2.16.1-py3-none-any.whl.metadata\n", - " Downloading zarr-2.16.1-py3-none-any.whl.metadata (5.8 kB)\n", - "Collecting attrdict (from nemo-toolkit==1.21.0rc0)\n", - " Downloading attrdict-2.0.1-py2.py3-none-any.whl (9.9 kB)\n", - "Collecting kornia (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for kornia from https://files.pythonhosted.org/packages/55/da/72cb83aa364ebb4d0109965e20c5d33d7063ccab15332c3fd0acfd5609c9/kornia-0.7.0-py2.py3-none-any.whl.metadata\n", - " Downloading kornia-0.7.0-py2.py3-none-any.whl.metadata (12 kB)\n", - "Collecting nemo-text-processing (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for nemo-text-processing from https://files.pythonhosted.org/packages/bd/82/b776d01ba650c3ab42ba5e381e34b35e507a21b16ad1246f51026fa13f0b/nemo_text_processing-0.2.0rc0-py3-none-any.whl.metadata\n", - " Downloading nemo_text_processing-0.2.0rc0-py3-none-any.whl.metadata (7.2 kB)\n", - "Collecting pypinyin (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for pypinyin from https://files.pythonhosted.org/packages/00/fc/3e82bf38739a7b2c4f699245ce6c84ff254723c678c2cdc5d2ecbddf9afb/pypinyin-0.49.0-py2.py3-none-any.whl.metadata\n", - " Downloading pypinyin-0.49.0-py2.py3-none-any.whl.metadata (12 kB)\n", - "Collecting pypinyin-dict (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for pypinyin-dict from https://files.pythonhosted.org/packages/89/31/16c26425685a84191503a226450837e0f4d540c164665d8567f2472861a9/pypinyin_dict-0.6.0-py2.py3-none-any.whl.metadata\n", - " Downloading pypinyin_dict-0.6.0-py2.py3-none-any.whl.metadata (3.6 kB)\n", - "Collecting progress>=1.5 (from nemo-toolkit==1.21.0rc0)\n", - " Downloading progress-1.6.tar.gz (7.8 kB)\n", - " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Requirement already satisfied: tabulate>=0.8.7 in /usr/local/lib/python3.10/dist-packages (from nemo-toolkit==1.21.0rc0) (0.9.0)\n", - "Collecting textdistance>=4.1.5 (from nemo-toolkit==1.21.0rc0)\n", - " Downloading textdistance-4.5.0-py3-none-any.whl (31 kB)\n", - "Requirement already satisfied: attrs>=18.1.0 in /usr/local/lib/python3.10/dist-packages (from black==19.10b0->nemo-toolkit==1.21.0rc0) (23.1.0)\n", - "Requirement already satisfied: appdirs in /usr/local/lib/python3.10/dist-packages (from black==19.10b0->nemo-toolkit==1.21.0rc0) (1.4.4)\n", - "Requirement already satisfied: toml>=0.9.4 in /usr/local/lib/python3.10/dist-packages (from black==19.10b0->nemo-toolkit==1.21.0rc0) (0.10.2)\n", - "Collecting typed-ast>=1.4.0 (from black==19.10b0->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for typed-ast>=1.4.0 from https://files.pythonhosted.org/packages/e2/ed/b9b8b794b37b55c9247b1e8d38b0361e8158795c181636d34d6c11b506e7/typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", - " Downloading typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.7 kB)\n", - "Requirement already satisfied: regex in /usr/local/lib/python3.10/dist-packages (from black==19.10b0->nemo-toolkit==1.21.0rc0) (2023.6.3)\n", - "Collecting pathspec<1,>=0.6 (from black==19.10b0->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for pathspec<1,>=0.6 from https://files.pythonhosted.org/packages/b4/2a/9b1be29146139ef459188f5e420a66e835dda921208db600b7037093891f/pathspec-0.11.2-py3-none-any.whl.metadata\n", - " Downloading pathspec-0.11.2-py3-none-any.whl.metadata (19 kB)\n", - "Collecting antlr4-python3-runtime==4.9.* (from hydra-core<=1.3.2,>1.3->nemo-toolkit==1.21.0rc0)\n", - " Downloading antlr4-python3-runtime-4.9.3.tar.gz (117 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m117.0/117.0 kB\u001b[0m \u001b[31m10.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "INFO: pip is looking at multiple versions of jiwer to determine which version is compatible with other requirements. This could take a while.\n", - "Collecting jiwer (from nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for jiwer from https://files.pythonhosted.org/packages/23/a3/92c29a5e422acd87e3b4f2e6dc0ce877070cc9b2f81d30fe84122032338a/jiwer-3.0.2-py3-none-any.whl.metadata\n", - " Downloading jiwer-3.0.2-py3-none-any.whl.metadata (2.6 kB)\n", - " Downloading jiwer-3.0.1-py3-none-any.whl (21 kB)\n", - " Downloading jiwer-3.0.0-py3-none-any.whl (21 kB)\n", - " Downloading jiwer-2.6.0-py3-none-any.whl (20 kB)\n", - " Downloading jiwer-2.5.2-py3-none-any.whl (15 kB)\n", - "Collecting rapidfuzz (from nemo-toolkit==1.21.0rc0)\n", - " Downloading rapidfuzz-2.13.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.2 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.2/2.2 MB\u001b[0m \u001b[31m96.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: audioread>=2.1.9 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (3.0.0)\n", - "Requirement already satisfied: joblib>=0.14 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (1.3.2)\n", - "Requirement already satisfied: decorator>=4.3.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (4.4.2)\n", - "Requirement already satisfied: pooch>=1.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (1.7.0)\n", - "Requirement already satisfied: soxr>=0.3.2 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (0.3.6)\n", - "Requirement already satisfied: typing-extensions>=4.1.1 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (4.7.1)\n", - "Requirement already satisfied: lazy-loader>=0.1 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (0.3)\n", - "Requirement already satisfied: msgpack>=1.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (1.0.5)\n", - "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib->nemo-toolkit==1.21.0rc0) (1.1.0)\n", - "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib->nemo-toolkit==1.21.0rc0) (0.11.0)\n", - "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib->nemo-toolkit==1.21.0rc0) (4.42.1)\n", - "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib->nemo-toolkit==1.21.0rc0) (1.4.5)\n", - "Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib->nemo-toolkit==1.21.0rc0) (9.4.0)\n", - "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib->nemo-toolkit==1.21.0rc0) (3.1.1)\n", - "Requirement already satisfied: llvmlite<0.40,>=0.39.0dev0 in /usr/local/lib/python3.10/dist-packages (from numba->nemo-toolkit==1.21.0rc0) (0.39.1)\n", - "Requirement already satisfied: PyYAML>=5.1.0 in /usr/local/lib/python3.10/dist-packages (from omegaconf<=2.3->nemo-toolkit==1.21.0rc0) (6.0.1)\n", - "Requirement already satisfied: protobuf>=3.20.2 in /usr/local/lib/python3.10/dist-packages (from onnx>=1.7.0->nemo-toolkit==1.21.0rc0) (3.20.3)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil->nemo-toolkit==1.21.0rc0) (1.16.0)\n", - "Requirement already satisfied: fsspec[http]>2021.06.0 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning<=2.0.7,>=2.0->nemo-toolkit==1.21.0rc0) (2023.6.0)\n", - "Collecting lightning-utilities>=0.7.0 (from pytorch-lightning<=2.0.7,>=2.0->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for lightning-utilities>=0.7.0 from https://files.pythonhosted.org/packages/46/ee/8641eeb6a062f383b7d6875604e1f3f83bd2c93a0b4dbcabd3150b32de6e/lightning_utilities-0.9.0-py3-none-any.whl.metadata\n", - " Downloading lightning_utilities-0.9.0-py3-none-any.whl.metadata (4.6 kB)\n", - "Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn->nemo-toolkit==1.21.0rc0) (3.2.0)\n", - "Requirement already satisfied: cffi>=1.0 in /usr/local/lib/python3.10/dist-packages (from soundfile->nemo-toolkit==1.21.0rc0) (1.15.1)\n", - "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from torch->nemo-toolkit==1.21.0rc0) (3.12.3)\n", - "Requirement already satisfied: sympy in /usr/local/lib/python3.10/dist-packages (from torch->nemo-toolkit==1.21.0rc0) (1.12)\n", - "Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from torch->nemo-toolkit==1.21.0rc0) (3.1)\n", - "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from torch->nemo-toolkit==1.21.0rc0) (3.1.2)\n", - "Requirement already satisfied: triton==2.0.0 in /usr/local/lib/python3.10/dist-packages (from torch->nemo-toolkit==1.21.0rc0) (2.0.0)\n", - "Requirement already satisfied: cmake in /usr/local/lib/python3.10/dist-packages (from triton==2.0.0->torch->nemo-toolkit==1.21.0rc0) (3.27.2)\n", - "Requirement already satisfied: lit in /usr/local/lib/python3.10/dist-packages (from triton==2.0.0->torch->nemo-toolkit==1.21.0rc0) (16.0.6)\n", - "Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from transformers>=4.0.1->nemo-toolkit==1.21.0rc0) (2.31.0)\n", - "Collecting tokenizers!=0.11.3,<0.14,>=0.11.1 (from transformers>=4.0.1->nemo-toolkit==1.21.0rc0)\n", - " Downloading tokenizers-0.13.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.8 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m7.8/7.8 MB\u001b[0m \u001b[31m81.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting safetensors>=0.3.1 (from transformers>=4.0.1->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for safetensors>=0.3.1 from https://files.pythonhosted.org/packages/6c/f0/c17bbdb1e5f9dab29d44cade445135789f75f8f08ea2728d04493ea8412b/safetensors-0.3.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", - " Downloading safetensors-0.3.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (4.7 kB)\n", - "Collecting botocore<1.32.0,>=1.31.43 (from boto3->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for botocore<1.32.0,>=1.31.43 from https://files.pythonhosted.org/packages/88/37/68fd026cde5d1c802ab34290285c5019e7ba3de3eea8e6c07756cfb827ae/botocore-1.31.43-py3-none-any.whl.metadata\n", - " Downloading botocore-1.31.43-py3-none-any.whl.metadata (6.0 kB)\n", - "Collecting jmespath<2.0.0,>=0.7.1 (from boto3->nemo-toolkit==1.21.0rc0)\n", - " Downloading jmespath-1.0.1-py3-none-any.whl (20 kB)\n", - "Collecting s3transfer<0.7.0,>=0.6.0 (from boto3->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for s3transfer<0.7.0,>=0.6.0 from https://files.pythonhosted.org/packages/d9/17/a3b666f5ef9543cfd3c661d39d1e193abb9649d0cfbbfee3cf3b51d5af02/s3transfer-0.6.2-py3-none-any.whl.metadata\n", - " Downloading s3transfer-0.6.2-py3-none-any.whl.metadata (1.8 kB)\n", - "Requirement already satisfied: pyarrow>=8.0.0 in /usr/local/lib/python3.10/dist-packages (from datasets->nemo-toolkit==1.21.0rc0) (9.0.0)\n", - "Collecting dill<0.3.8,>=0.3.0 (from datasets->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for dill<0.3.8,>=0.3.0 from https://files.pythonhosted.org/packages/f5/3a/74a29b11cf2cdfcd6ba89c0cecd70b37cd1ba7b77978ce611eb7a146a832/dill-0.3.7-py3-none-any.whl.metadata\n", - " Downloading dill-0.3.7-py3-none-any.whl.metadata (9.9 kB)\n", - "Collecting xxhash (from datasets->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for xxhash from https://files.pythonhosted.org/packages/13/c3/e942893f4864a424514c81640f114980cfd5aff7e7414d1e0255f4571111/xxhash-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", - " Downloading xxhash-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (12 kB)\n", - "Collecting multiprocess (from datasets->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for multiprocess from https://files.pythonhosted.org/packages/35/a8/36d8d7b3e46b377800d8dec47891cdf05842d1a2366909ae4a0c89fbc5e6/multiprocess-0.70.15-py310-none-any.whl.metadata\n", - " Downloading multiprocess-0.70.15-py310-none-any.whl.metadata (7.2 kB)\n", - "Requirement already satisfied: aiohttp in /usr/local/lib/python3.10/dist-packages (from datasets->nemo-toolkit==1.21.0rc0) (3.8.5)\n", - "Collecting pybind11>=2.2 (from fasttext->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for pybind11>=2.2 from https://files.pythonhosted.org/packages/06/55/9f73c32dda93fa4f539fafa268f9504e83c489f460c380371d94296126cd/pybind11-2.11.1-py3-none-any.whl.metadata\n", - " Using cached pybind11-2.11.1-py3-none-any.whl.metadata (9.5 kB)\n", - "Collecting aniso8601>=0.82 (from flask-restful->nemo-toolkit==1.21.0rc0)\n", - " Downloading aniso8601-9.0.1-py2.py3-none-any.whl (52 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m52.8/52.8 kB\u001b[0m \u001b[31m7.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: Flask>=0.8 in /usr/local/lib/python3.10/dist-packages (from flask-restful->nemo-toolkit==1.21.0rc0) (2.2.5)\n", - "Requirement already satisfied: pytz in /usr/local/lib/python3.10/dist-packages (from flask-restful->nemo-toolkit==1.21.0rc0) (2023.3.post1)\n", - "Requirement already satisfied: wcwidth>=0.2.5 in /usr/local/lib/python3.10/dist-packages (from ftfy->nemo-toolkit==1.21.0rc0) (0.2.6)\n", - "Collecting distance>=0.1.3 (from g2p-en->nemo-toolkit==1.21.0rc0)\n", - " Downloading Distance-0.1.3.tar.gz (180 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m180.3/180.3 kB\u001b[0m \u001b[31m24.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Requirement already satisfied: beautifulsoup4 in /usr/local/lib/python3.10/dist-packages (from gdown->nemo-toolkit==1.21.0rc0) (4.11.2)\n", - "Requirement already satisfied: ipykernel>=4.5.1 in /usr/local/lib/python3.10/dist-packages (from ipywidgets->nemo-toolkit==1.21.0rc0) (5.5.6)\n", - "Requirement already satisfied: ipython-genutils~=0.2.0 in /usr/local/lib/python3.10/dist-packages (from ipywidgets->nemo-toolkit==1.21.0rc0) (0.2.0)\n", - "Requirement already satisfied: traitlets>=4.3.1 in /usr/local/lib/python3.10/dist-packages (from ipywidgets->nemo-toolkit==1.21.0rc0) (5.7.1)\n", - "Requirement already satisfied: widgetsnbextension~=3.6.0 in /usr/local/lib/python3.10/dist-packages (from ipywidgets->nemo-toolkit==1.21.0rc0) (3.6.5)\n", - "Requirement already satisfied: ipython>=4.0.0 in /usr/local/lib/python3.10/dist-packages (from ipywidgets->nemo-toolkit==1.21.0rc0) (7.34.0)\n", - "Requirement already satisfied: jupyterlab-widgets>=1.0.0 in /usr/local/lib/python3.10/dist-packages (from ipywidgets->nemo-toolkit==1.21.0rc0) (3.0.8)\n", - "Collecting cdifflib (from nemo-text-processing->nemo-toolkit==1.21.0rc0)\n", - " Downloading cdifflib-1.2.6.tar.gz (11 kB)\n", - " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", - " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", - " Installing backend dependencies ... \u001b[?25l\u001b[?25hdone\n", - " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", - "Collecting pynini==2.1.5 (from nemo-text-processing->nemo-toolkit==1.21.0rc0)\n", - " Downloading pynini-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (161.3 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m161.3/161.3 MB\u001b[0m \u001b[31m6.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: Cython>=0.29 in /usr/local/lib/python3.10/dist-packages (from pynini==2.1.5->nemo-text-processing->nemo-toolkit==1.21.0rc0) (0.29.36)\n", - "Requirement already satisfied: sortedcontainers>=2.0.4 in /usr/local/lib/python3.10/dist-packages (from pyannote.core->nemo-toolkit==1.21.0rc0) (2.4.0)\n", - "Collecting pyannote.database>=4.0.1 (from pyannote.metrics->nemo-toolkit==1.21.0rc0)\n", - " Downloading pyannote.database-5.0.1-py3-none-any.whl (48 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m48.1/48.1 kB\u001b[0m \u001b[31m4.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting docopt>=0.6.2 (from pyannote.metrics->nemo-toolkit==1.21.0rc0)\n", - " Downloading docopt-0.6.2.tar.gz (25 kB)\n", - " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Requirement already satisfied: iniconfig in /usr/local/lib/python3.10/dist-packages (from pytest->nemo-toolkit==1.21.0rc0) (2.0.0)\n", - "Requirement already satisfied: pluggy<2.0,>=0.12 in /usr/local/lib/python3.10/dist-packages (from pytest->nemo-toolkit==1.21.0rc0) (1.3.0)\n", - "Requirement already satisfied: exceptiongroup>=1.0.0rc8 in /usr/local/lib/python3.10/dist-packages (from pytest->nemo-toolkit==1.21.0rc0) (1.1.3)\n", - "Requirement already satisfied: tomli>=1.0.0 in /usr/local/lib/python3.10/dist-packages (from pytest->nemo-toolkit==1.21.0rc0) (2.0.1)\n", - "Requirement already satisfied: absl-py in /usr/local/lib/python3.10/dist-packages (from rouge-score->nemo-toolkit==1.21.0rc0) (1.4.0)\n", - "Collecting ruamel.yaml.clib>=0.2.7 (from ruamel.yaml->nemo-toolkit==1.21.0rc0)\n", - " Downloading ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl (485 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m485.6/485.6 kB\u001b[0m \u001b[31m49.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting portalocker (from sacrebleu[ja]->nemo-toolkit==1.21.0rc0)\n", - " Downloading portalocker-2.7.0-py2.py3-none-any.whl (15 kB)\n", - "Collecting colorama (from sacrebleu[ja]->nemo-toolkit==1.21.0rc0)\n", - " Downloading colorama-0.4.6-py2.py3-none-any.whl (25 kB)\n", - "Requirement already satisfied: lxml in /usr/local/lib/python3.10/dist-packages (from sacrebleu[ja]->nemo-toolkit==1.21.0rc0) (4.9.3)\n", - "Collecting mecab-python3==1.0.5 (from sacrebleu[ja]->nemo-toolkit==1.21.0rc0)\n", - " Downloading mecab_python3-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (581 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m581.1/581.1 kB\u001b[0m \u001b[31m56.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting ipadic<2.0,>=1.0 (from sacrebleu[ja]->nemo-toolkit==1.21.0rc0)\n", - " Downloading ipadic-1.0.0.tar.gz (13.4 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m13.4/13.4 MB\u001b[0m \u001b[31m96.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Requirement already satisfied: torchvision in /usr/local/lib/python3.10/dist-packages (from sentence-transformers->nemo-toolkit==1.21.0rc0) (0.15.2+cu118)\n", - "Requirement already satisfied: sphinxcontrib-applehelp in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (1.0.7)\n", - "Requirement already satisfied: sphinxcontrib-devhelp in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (1.0.5)\n", - "Requirement already satisfied: sphinxcontrib-jsmath in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (1.0.1)\n", - "Requirement already satisfied: sphinxcontrib-htmlhelp>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (2.0.4)\n", - "Requirement already satisfied: sphinxcontrib-serializinghtml>=1.1.5 in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (1.1.9)\n", - "Requirement already satisfied: sphinxcontrib-qthelp in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (1.0.6)\n", - "Requirement already satisfied: Pygments>=2.0 in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (2.16.1)\n", - "Requirement already satisfied: docutils<0.19,>=0.14 in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (0.18.1)\n", - "Requirement already satisfied: snowballstemmer>=1.1 in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (2.2.0)\n", - "Requirement already satisfied: babel>=1.3 in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (2.12.1)\n", - "Requirement already satisfied: alabaster<0.8,>=0.7 in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (0.7.13)\n", - "Requirement already satisfied: imagesize in /usr/local/lib/python3.10/dist-packages (from sphinx->nemo-toolkit==1.21.0rc0) (1.4.1)\n", - "Collecting docutils<0.19,>=0.14 (from sphinx->nemo-toolkit==1.21.0rc0)\n", - " Downloading docutils-0.17.1-py2.py3-none-any.whl (575 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m575.5/575.5 kB\u001b[0m \u001b[31m42.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting pybtex>=0.24 (from sphinxcontrib-bibtex->nemo-toolkit==1.21.0rc0)\n", - " Downloading pybtex-0.24.0-py2.py3-none-any.whl (561 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m561.4/561.4 kB\u001b[0m \u001b[31m48.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting pybtex-docutils>=1.0.0 (from sphinxcontrib-bibtex->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for pybtex-docutils>=1.0.0 from https://files.pythonhosted.org/packages/11/b1/ce1f4596211efb5410e178a803f08e59b20bedb66837dcf41e21c54f9ec1/pybtex_docutils-1.0.3-py3-none-any.whl.metadata\n", - " Downloading pybtex_docutils-1.0.3-py3-none-any.whl.metadata (4.3 kB)\n", - "Requirement already satisfied: grpcio>=1.48.2 in /usr/local/lib/python3.10/dist-packages (from tensorboard->nemo-toolkit==1.21.0rc0) (1.57.0)\n", - "Requirement already satisfied: google-auth<3,>=1.6.3 in /usr/local/lib/python3.10/dist-packages (from tensorboard->nemo-toolkit==1.21.0rc0) (2.17.3)\n", - "Requirement already satisfied: google-auth-oauthlib<1.1,>=0.5 in /usr/local/lib/python3.10/dist-packages (from tensorboard->nemo-toolkit==1.21.0rc0) (1.0.0)\n", - "Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.10/dist-packages (from tensorboard->nemo-toolkit==1.21.0rc0) (3.4.4)\n", - "Requirement already satisfied: tensorboard-data-server<0.8.0,>=0.7.0 in /usr/local/lib/python3.10/dist-packages (from tensorboard->nemo-toolkit==1.21.0rc0) (0.7.1)\n", - "Requirement already satisfied: werkzeug>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from tensorboard->nemo-toolkit==1.21.0rc0) (2.3.7)\n", - "Requirement already satisfied: wheel>=0.26 in /usr/local/lib/python3.10/dist-packages (from tensorboard->nemo-toolkit==1.21.0rc0) (0.41.2)\n", - "Collecting plac (from texterrors->nemo-toolkit==1.21.0rc0)\n", - " Downloading plac-1.3.5-py2.py3-none-any.whl (22 kB)\n", - "Collecting loguru (from texterrors->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for loguru from https://files.pythonhosted.org/packages/19/a9/4e91197b121a41c640367641a510fd9a05bb7a3259fc9678ee2976c8fd00/loguru-0.7.1-py3-none-any.whl.metadata\n", - " Downloading loguru-0.7.1-py3-none-any.whl.metadata (22 kB)\n", - "Requirement already satisfied: termcolor in /usr/local/lib/python3.10/dist-packages (from texterrors->nemo-toolkit==1.21.0rc0) (2.3.0)\n", - "Collecting Levenshtein (from texterrors->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for Levenshtein from https://files.pythonhosted.org/packages/e6/02/0a4ed6a9e2b78f6b57f25a87fc194d7d10c2bbe95d985f36390e86285232/Levenshtein-0.21.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", - " Downloading Levenshtein-0.21.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (3.4 kB)\n", - "Collecting GitPython!=3.1.29,>=1.0.0 (from wandb->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for GitPython!=3.1.29,>=1.0.0 from https://files.pythonhosted.org/packages/0f/c6/bb9e2276b6fed126aa21e292493b45a3df4cfba7cbfcf2ab8809a6b0e718/GitPython-3.1.35-py3-none-any.whl.metadata\n", - " Downloading GitPython-3.1.35-py3-none-any.whl.metadata (10 kB)\n", - "Requirement already satisfied: psutil>=5.0.0 in /usr/local/lib/python3.10/dist-packages (from wandb->nemo-toolkit==1.21.0rc0) (5.9.5)\n", - "Collecting sentry-sdk>=1.0.0 (from wandb->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for sentry-sdk>=1.0.0 from https://files.pythonhosted.org/packages/17/22/dbd5f854f373214d48585eeb6844e50a8dd1600f435d9033493f76f66dfa/sentry_sdk-1.30.0-py2.py3-none-any.whl.metadata\n", - " Downloading sentry_sdk-1.30.0-py2.py3-none-any.whl.metadata (9.6 kB)\n", - "Collecting docker-pycreds>=0.4.0 (from wandb->nemo-toolkit==1.21.0rc0)\n", - " Downloading docker_pycreds-0.4.0-py2.py3-none-any.whl (9.0 kB)\n", - "Collecting pathtools (from wandb->nemo-toolkit==1.21.0rc0)\n", - " Downloading pathtools-0.1.2.tar.gz (11 kB)\n", - " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Collecting setproctitle (from wandb->nemo-toolkit==1.21.0rc0)\n", - " Downloading setproctitle-1.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (30 kB)\n", - "Collecting asciitree (from zarr->nemo-toolkit==1.21.0rc0)\n", - " Downloading asciitree-0.3.3.tar.gz (4.0 kB)\n", - " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Collecting fasteners (from zarr->nemo-toolkit==1.21.0rc0)\n", - " Downloading fasteners-0.18-py3-none-any.whl (18 kB)\n", - "Collecting numcodecs>=0.10.0 (from zarr->nemo-toolkit==1.21.0rc0)\n", - " Downloading numcodecs-0.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.7 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m6.7/6.7 MB\u001b[0m \u001b[31m116.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting urllib3<1.27,>=1.25.4 (from botocore<1.32.0,>=1.31.43->boto3->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for urllib3<1.27,>=1.25.4 from https://files.pythonhosted.org/packages/c5/05/c214b32d21c0b465506f95c4f28ccbcba15022e000b043b72b3df7728471/urllib3-1.26.16-py2.py3-none-any.whl.metadata\n", - " Downloading urllib3-1.26.16-py2.py3-none-any.whl.metadata (48 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m48.4/48.4 kB\u001b[0m \u001b[31m6.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: pycparser in /usr/local/lib/python3.10/dist-packages (from cffi>=1.0->soundfile->nemo-toolkit==1.21.0rc0) (2.21)\n", - "Requirement already satisfied: itsdangerous>=2.0 in /usr/local/lib/python3.10/dist-packages (from Flask>=0.8->flask-restful->nemo-toolkit==1.21.0rc0) (2.1.2)\n", - "Requirement already satisfied: charset-normalizer<4.0,>=2.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets->nemo-toolkit==1.21.0rc0) (3.2.0)\n", - "Requirement already satisfied: multidict<7.0,>=4.5 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets->nemo-toolkit==1.21.0rc0) (6.0.4)\n", - "Requirement already satisfied: async-timeout<5.0,>=4.0.0a3 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets->nemo-toolkit==1.21.0rc0) (4.0.3)\n", - "Requirement already satisfied: yarl<2.0,>=1.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets->nemo-toolkit==1.21.0rc0) (1.9.2)\n", - "Requirement already satisfied: frozenlist>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets->nemo-toolkit==1.21.0rc0) (1.4.0)\n", - "Requirement already satisfied: aiosignal>=1.1.2 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets->nemo-toolkit==1.21.0rc0) (1.3.1)\n", - "Collecting gitdb<5,>=4.0.1 (from GitPython!=3.1.29,>=1.0.0->wandb->nemo-toolkit==1.21.0rc0)\n", - " Downloading gitdb-4.0.10-py3-none-any.whl (62 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m62.7/62.7 kB\u001b[0m \u001b[31m8.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: cachetools<6.0,>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from google-auth<3,>=1.6.3->tensorboard->nemo-toolkit==1.21.0rc0) (5.3.1)\n", - "Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.10/dist-packages (from google-auth<3,>=1.6.3->tensorboard->nemo-toolkit==1.21.0rc0) (0.3.0)\n", - "Requirement already satisfied: rsa<5,>=3.1.4 in /usr/local/lib/python3.10/dist-packages (from google-auth<3,>=1.6.3->tensorboard->nemo-toolkit==1.21.0rc0) (4.9)\n", - "Requirement already satisfied: requests-oauthlib>=0.7.0 in /usr/local/lib/python3.10/dist-packages (from google-auth-oauthlib<1.1,>=0.5->tensorboard->nemo-toolkit==1.21.0rc0) (1.3.1)\n", - "Requirement already satisfied: jupyter-client in /usr/local/lib/python3.10/dist-packages (from ipykernel>=4.5.1->ipywidgets->nemo-toolkit==1.21.0rc0) (6.1.12)\n", - "Requirement already satisfied: tornado>=4.2 in /usr/local/lib/python3.10/dist-packages (from ipykernel>=4.5.1->ipywidgets->nemo-toolkit==1.21.0rc0) (6.3.2)\n", - "Collecting jedi>=0.16 (from ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for jedi>=0.16 from https://files.pythonhosted.org/packages/8e/46/7e3ae3aa2dcfcffc5138c6cef5448523218658411c84a2000bf75c8d3ec1/jedi-0.19.0-py2.py3-none-any.whl.metadata\n", - " Downloading jedi-0.19.0-py2.py3-none-any.whl.metadata (22 kB)\n", - "Requirement already satisfied: pickleshare in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.7.5)\n", - "Requirement already satisfied: prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0) (3.0.39)\n", - "Requirement already satisfied: backcall in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.2.0)\n", - "Requirement already satisfied: matplotlib-inline in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.1.6)\n", - "Requirement already satisfied: pexpect>4.3 in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0) (4.8.0)\n", - "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->torch->nemo-toolkit==1.21.0rc0) (2.1.3)\n", - "Requirement already satisfied: entrypoints in /usr/local/lib/python3.10/dist-packages (from numcodecs>=0.10.0->zarr->nemo-toolkit==1.21.0rc0) (0.4)\n", - "Requirement already satisfied: platformdirs>=2.5.0 in /usr/local/lib/python3.10/dist-packages (from pooch>=1.0->librosa>=0.9.0->nemo-toolkit==1.21.0rc0) (3.10.0)\n", - "Requirement already satisfied: typer[all]>=0.2.1 in /usr/local/lib/python3.10/dist-packages (from pyannote.database>=4.0.1->pyannote.metrics->nemo-toolkit==1.21.0rc0) (0.9.0)\n", - "Collecting latexcodec>=1.0.4 (from pybtex>=0.24->sphinxcontrib-bibtex->nemo-toolkit==1.21.0rc0)\n", - " Downloading latexcodec-2.0.1-py2.py3-none-any.whl (18 kB)\n", - "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests->transformers>=4.0.1->nemo-toolkit==1.21.0rc0) (3.4)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests->transformers>=4.0.1->nemo-toolkit==1.21.0rc0) (2023.7.22)\n", - "Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.10/dist-packages (from sympy->torch->nemo-toolkit==1.21.0rc0) (1.3.0)\n", - "Requirement already satisfied: notebook>=4.4.1 in /usr/local/lib/python3.10/dist-packages (from widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (6.5.5)\n", - "Requirement already satisfied: soupsieve>1.2 in /usr/local/lib/python3.10/dist-packages (from beautifulsoup4->gdown->nemo-toolkit==1.21.0rc0) (2.5)\n", - "Requirement already satisfied: PySocks!=1.5.7,>=1.5.6 in /usr/local/lib/python3.10/dist-packages (from requests->transformers>=4.0.1->nemo-toolkit==1.21.0rc0) (1.7.1)\n", - "Collecting smmap<6,>=3.0.1 (from gitdb<5,>=4.0.1->GitPython!=3.1.29,>=1.0.0->wandb->nemo-toolkit==1.21.0rc0)\n", - " Downloading smmap-5.0.0-py3-none-any.whl (24 kB)\n", - "Requirement already satisfied: parso<0.9.0,>=0.8.3 in /usr/local/lib/python3.10/dist-packages (from jedi>=0.16->ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.8.3)\n", - "Requirement already satisfied: pyzmq<25,>=17 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (23.2.1)\n", - "Requirement already satisfied: argon2-cffi in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (23.1.0)\n", - "Requirement already satisfied: jupyter-core>=4.6.1 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (5.3.1)\n", - "Requirement already satisfied: nbformat in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (5.9.2)\n", - "Requirement already satisfied: nbconvert>=5 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (6.5.4)\n", - "Requirement already satisfied: nest-asyncio>=1.5 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.5.7)\n", - "Requirement already satisfied: Send2Trash>=1.8.0 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.8.2)\n", - "Requirement already satisfied: terminado>=0.8.3 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.17.1)\n", - "Requirement already satisfied: prometheus-client in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.17.1)\n", - "Requirement already satisfied: nbclassic>=0.4.7 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.0.0)\n", - "Requirement already satisfied: ptyprocess>=0.5 in /usr/local/lib/python3.10/dist-packages (from pexpect>4.3->ipython>=4.0.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.7.0)\n", - "Requirement already satisfied: pyasn1<0.6.0,>=0.4.6 in /usr/local/lib/python3.10/dist-packages (from pyasn1-modules>=0.2.1->google-auth<3,>=1.6.3->tensorboard->nemo-toolkit==1.21.0rc0) (0.5.0)\n", - "Requirement already satisfied: oauthlib>=3.0.0 in /usr/local/lib/python3.10/dist-packages (from requests-oauthlib>=0.7.0->google-auth-oauthlib<1.1,>=0.5->tensorboard->nemo-toolkit==1.21.0rc0) (3.2.2)\n", - "Collecting shellingham<2.0.0,>=1.3.0 (from typer[all]>=0.2.1->pyannote.database>=4.0.1->pyannote.metrics->nemo-toolkit==1.21.0rc0)\n", - " Obtaining dependency information for shellingham<2.0.0,>=1.3.0 from https://files.pythonhosted.org/packages/57/70/0265437683625b2e6491736706d3d679d90e2a26f6bff59f4e46e09872b9/shellingham-1.5.3-py2.py3-none-any.whl.metadata\n", - " Downloading shellingham-1.5.3-py2.py3-none-any.whl.metadata (3.4 kB)\n", - "Requirement already satisfied: rich<14.0.0,>=10.11.0 in /usr/local/lib/python3.10/dist-packages (from typer[all]>=0.2.1->pyannote.database>=4.0.1->pyannote.metrics->nemo-toolkit==1.21.0rc0) (13.5.2)\n", - "Requirement already satisfied: jupyter-server>=1.8 in /usr/local/lib/python3.10/dist-packages (from nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.24.0)\n", - "Requirement already satisfied: notebook-shim>=0.2.3 in /usr/local/lib/python3.10/dist-packages (from nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.2.3)\n", - "Requirement already satisfied: bleach in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (6.0.0)\n", - "Requirement already satisfied: defusedxml in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.7.1)\n", - "Requirement already satisfied: jupyterlab-pygments in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.2.2)\n", - "Requirement already satisfied: mistune<2,>=0.8.1 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.8.4)\n", - "Requirement already satisfied: nbclient>=0.5.0 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.8.0)\n", - "Requirement already satisfied: pandocfilters>=1.4.1 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.5.0)\n", - "Requirement already satisfied: tinycss2 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.2.1)\n", - "Requirement already satisfied: fastjsonschema in /usr/local/lib/python3.10/dist-packages (from nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (2.18.0)\n", - "Requirement already satisfied: jsonschema>=2.6 in /usr/local/lib/python3.10/dist-packages (from nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (4.19.0)\n", - "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.10/dist-packages (from rich<14.0.0,>=10.11.0->typer[all]>=0.2.1->pyannote.database>=4.0.1->pyannote.metrics->nemo-toolkit==1.21.0rc0) (3.0.0)\n", - "Requirement already satisfied: argon2-cffi-bindings in /usr/local/lib/python3.10/dist-packages (from argon2-cffi->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (21.2.0)\n", - "Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=2.6->nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (2023.7.1)\n", - "Requirement already satisfied: referencing>=0.28.4 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=2.6->nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.30.2)\n", - "Requirement already satisfied: rpds-py>=0.7.1 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=2.6->nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.10.2)\n", - "Requirement already satisfied: anyio<4,>=3.1.0 in /usr/local/lib/python3.10/dist-packages (from jupyter-server>=1.8->nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (3.7.1)\n", - "Requirement already satisfied: websocket-client in /usr/local/lib/python3.10/dist-packages (from jupyter-server>=1.8->nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.6.2)\n", - "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0->rich<14.0.0,>=10.11.0->typer[all]>=0.2.1->pyannote.database>=4.0.1->pyannote.metrics->nemo-toolkit==1.21.0rc0) (0.1.2)\n", - "Requirement already satisfied: webencodings in /usr/local/lib/python3.10/dist-packages (from bleach->nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (0.5.1)\n", - "Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.10/dist-packages (from anyio<4,>=3.1.0->jupyter-server>=1.8->nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets->nemo-toolkit==1.21.0rc0) (1.3.0)\n", - "Downloading megatron_core-0.2.0-py3-none-any.whl (46 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m46.0/46.0 kB\u001b[0m \u001b[31m4.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading onnx-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (14.6 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m14.6/14.6 MB\u001b[0m \u001b[31m71.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading pydantic-1.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.1 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.1/3.1 MB\u001b[0m \u001b[31m81.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading pytorch_lightning-2.0.7-py3-none-any.whl (724 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m725.0/725.0 kB\u001b[0m \u001b[31m55.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading torchmetrics-1.1.1-py3-none-any.whl (763 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m763.4/763.4 kB\u001b[0m \u001b[31m51.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading transformers-4.33.1-py3-none-any.whl (7.6 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m7.6/7.6 MB\u001b[0m \u001b[31m90.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading huggingface_hub-0.16.4-py3-none-any.whl (268 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m268.8/268.8 kB\u001b[0m \u001b[31m31.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading boto3-1.28.43-py3-none-any.whl (135 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m135.8/135.8 kB\u001b[0m \u001b[31m18.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading datasets-2.14.5-py3-none-any.whl (519 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m519.6/519.6 kB\u001b[0m \u001b[31m48.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading Flask_RESTful-0.3.10-py2.py3-none-any.whl (26 kB)\n", - "Downloading ijson-3.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (111 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m111.8/111.8 kB\u001b[0m \u001b[31m14.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading kornia-0.7.0-py2.py3-none-any.whl (705 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m705.7/705.7 kB\u001b[0m \u001b[31m35.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading markdown2-2.4.10-py2.py3-none-any.whl (39 kB)\n", - "Downloading marshmallow-3.20.1-py3-none-any.whl (49 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m49.4/49.4 kB\u001b[0m \u001b[31m6.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading nemo_text_processing-0.2.0rc0-py3-none-any.whl (2.4 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.4/2.4 MB\u001b[0m \u001b[31m50.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading pypinyin-0.49.0-py2.py3-none-any.whl (1.4 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.4/1.4 MB\u001b[0m \u001b[31m69.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading pypinyin_dict-0.6.0-py2.py3-none-any.whl (9.5 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m9.5/9.5 MB\u001b[0m \u001b[31m86.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading ruamel.yaml-0.17.32-py3-none-any.whl (112 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m112.2/112.2 kB\u001b[0m \u001b[31m11.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading sphinxcontrib_bibtex-2.6.1-py3-none-any.whl (40 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m40.9/40.9 kB\u001b[0m \u001b[31m5.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading wandb-0.15.10-py3-none-any.whl (2.1 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.1/2.1 MB\u001b[0m \u001b[31m76.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading zarr-2.16.1-py3-none-any.whl (206 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m206.9/206.9 kB\u001b[0m \u001b[31m21.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading botocore-1.31.43-py3-none-any.whl (11.2 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m11.2/11.2 MB\u001b[0m \u001b[31m87.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading dill-0.3.7-py3-none-any.whl (115 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m115.3/115.3 kB\u001b[0m \u001b[31m14.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading GitPython-3.1.35-py3-none-any.whl (188 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m188.8/188.8 kB\u001b[0m \u001b[31m23.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading lightning_utilities-0.9.0-py3-none-any.whl (23 kB)\n", - "Downloading pathspec-0.11.2-py3-none-any.whl (29 kB)\n", - "Using cached pybind11-2.11.1-py3-none-any.whl (227 kB)\n", - "Downloading pybtex_docutils-1.0.3-py3-none-any.whl (6.4 kB)\n", - "Downloading s3transfer-0.6.2-py3-none-any.whl (79 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m79.8/79.8 kB\u001b[0m \u001b[31m10.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading safetensors-0.3.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.3/1.3 MB\u001b[0m \u001b[31m77.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading sentry_sdk-1.30.0-py2.py3-none-any.whl (218 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m218.8/218.8 kB\u001b[0m \u001b[31m26.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (824 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m824.7/824.7 kB\u001b[0m \u001b[31m52.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading Levenshtein-0.21.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (172 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m172.5/172.5 kB\u001b[0m \u001b[31m21.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading loguru-0.7.1-py3-none-any.whl (61 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m61.4/61.4 kB\u001b[0m \u001b[31m8.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading multiprocess-0.70.15-py310-none-any.whl (134 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m134.8/134.8 kB\u001b[0m \u001b[31m16.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading xxhash-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (194 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m194.1/194.1 kB\u001b[0m \u001b[31m14.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading jedi-0.19.0-py2.py3-none-any.whl (1.6 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.6/1.6 MB\u001b[0m \u001b[31m66.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading urllib3-1.26.16-py2.py3-none-any.whl (143 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m143.1/143.1 kB\u001b[0m \u001b[31m18.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading shellingham-1.5.3-py2.py3-none-any.whl (9.7 kB)\n", - "Building wheels for collected packages: antlr4-python3-runtime, progress, sacremoses, youtokentome, fasttext, kaldi-python-io, nemo-toolkit, rouge-score, sentence-transformers, wget, distance, docopt, ipadic, asciitree, cdifflib, pathtools\n", - " Building wheel for antlr4-python3-runtime (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for antlr4-python3-runtime: filename=antlr4_python3_runtime-4.9.3-py3-none-any.whl size=144554 sha256=ad5d3c0bd8ee550570d532ca332fe7204baee65f2066eefcf5fc70cd2afdb382\n", - " Stored in directory: /root/.cache/pip/wheels/12/93/dd/1f6a127edc45659556564c5730f6d4e300888f4bca2d4c5a88\n", - " Building wheel for progress (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for progress: filename=progress-1.6-py3-none-any.whl size=9610 sha256=79cc5bc84a633414a3856bc3a35542e0489aaffa62e7cdae95e51474a38f064c\n", - " Stored in directory: /root/.cache/pip/wheels/a2/68/5f/c339b20a41659d856c93ccdce6a33095493eb82c3964aac5a1\n", - " Building wheel for sacremoses (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for sacremoses: filename=sacremoses-0.0.53-py3-none-any.whl size=895241 sha256=1fd4f16d193691e8ff7869591f5d11f5a32e21289fbb9a8422a800bc1bf781c7\n", - " Stored in directory: /root/.cache/pip/wheels/00/24/97/a2ea5324f36bc626e1ea0267f33db6aa80d157ee977e9e42fb\n", - " Building wheel for youtokentome (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for youtokentome: filename=youtokentome-1.0.6-cp310-cp310-linux_x86_64.whl size=1883668 sha256=d3e4925cf4b245a76184b4afe612a4fffd683de42889b5c1a07ca44be98dde9b\n", - " Stored in directory: /root/.cache/pip/wheels/df/85/f8/301d2ba45f43f30bed2fe413efa760bc726b8b660ed9c2900c\n", - " Building wheel for fasttext (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for fasttext: filename=fasttext-0.9.2-cp310-cp310-linux_x86_64.whl size=4199767 sha256=5bcc699963ee85f0f02846c1e278140b3ba3d95a7877d60b48c5b10f8f424bb1\n", - " Stored in directory: /root/.cache/pip/wheels/a5/13/75/f811c84a8ab36eedbaef977a6a58a98990e8e0f1967f98f394\n", - " Building wheel for kaldi-python-io (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for kaldi-python-io: filename=kaldi_python_io-1.2.2-py3-none-any.whl size=8948 sha256=d2e946cb4c2298a69803c0492c1533a3d08d313ced846ba7f1555a11ab164bd6\n", - " Stored in directory: /root/.cache/pip/wheels/b7/23/5f/49d3a826be576faf61d84e8028e1914bb36a5586ee2613b087\n", - " Building editable for nemo-toolkit (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for nemo-toolkit: filename=nemo_toolkit-1.21.0rc0-0.editable-py3-none-any.whl size=9181 sha256=d5c26c7630db00d8c6a483e19f89aa11a4d7a11920dcb052d8bb6a56689eb77d\n", - " Stored in directory: /tmp/pip-ephem-wheel-cache-4nra31ak/wheels/41/a7/71/00ccdddfb43c015e8d025853cafb90117dd722fd8ec557581b\n", - " Building wheel for rouge-score (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for rouge-score: filename=rouge_score-0.1.2-py3-none-any.whl size=24932 sha256=7f61e2fe8b2f326d21f3a0a06e5b58e2f1103a5523bac58ff728481bfdf203d8\n", - " Stored in directory: /root/.cache/pip/wheels/5f/dd/89/461065a73be61a532ff8599a28e9beef17985c9e9c31e541b4\n", - " Building wheel for sentence-transformers (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for sentence-transformers: filename=sentence_transformers-2.2.2-py3-none-any.whl size=125923 sha256=b4fb59313f79b135e68441f9b4780a35d2658bac193693e6177887cb018824a7\n", - " Stored in directory: /root/.cache/pip/wheels/62/f2/10/1e606fd5f02395388f74e7462910fe851042f97238cbbd902f\n", - " Building wheel for wget (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for wget: filename=wget-3.2-py3-none-any.whl size=9655 sha256=dd1cfe282f727f91b5a5db464ae58f43372f62a80d0a083733b36c68edc6ee76\n", - " Stored in directory: /root/.cache/pip/wheels/8b/f1/7f/5c94f0a7a505ca1c81cd1d9208ae2064675d97582078e6c769\n", - " Building wheel for distance (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for distance: filename=Distance-0.1.3-py3-none-any.whl size=16258 sha256=287628136639656b068a6f2e67ab0f5a80bc4ad466590f19499978136c5e3726\n", - " Stored in directory: /root/.cache/pip/wheels/e8/bb/de/f71bf63559ea9a921059a5405806f7ff6ed612a9231c4a9309\n", - " Building wheel for docopt (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for docopt: filename=docopt-0.6.2-py2.py3-none-any.whl size=13705 sha256=9af9a132b49b73c475f9838a56ff79e968af41bb509c8b38b81f7c08c3da87dd\n", - " Stored in directory: /root/.cache/pip/wheels/fc/ab/d4/5da2067ac95b36618c629a5f93f809425700506f72c9732fac\n", - " Building wheel for ipadic (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for ipadic: filename=ipadic-1.0.0-py3-none-any.whl size=13556703 sha256=9e40daac3101fb3d52fcf9c986017638569c576c3be36805d1437e722a2d8803\n", - " Stored in directory: /root/.cache/pip/wheels/5b/ea/e3/2f6e0860a327daba3b030853fce4483ed37468bbf1101c59c3\n", - " Building wheel for asciitree (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for asciitree: filename=asciitree-0.3.3-py3-none-any.whl size=5034 sha256=69d1478059cdad8cb54b8a1a29e66bd3229cf3458dcf96aeedb978381d8116f3\n", - " Stored in directory: /root/.cache/pip/wheels/7f/4e/be/1171b40f43b918087657ec57cf3b81fa1a2e027d8755baa184\n", - " Building wheel for cdifflib (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for cdifflib: filename=cdifflib-1.2.6-cp310-cp310-linux_x86_64.whl size=27681 sha256=f4ccba50f62b7265ea20b84bac762b6ed3dacae8d42ea7346d54e07da2a6ad9e\n", - " Stored in directory: /root/.cache/pip/wheels/87/a7/fd/8061e24ed08689045cb6d1ca303768dc463b20a5a338174841\n", - " Building wheel for pathtools (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for pathtools: filename=pathtools-0.1.2-py3-none-any.whl size=8791 sha256=95b669a715a9eefa4cd1222ac5c899a97064dbb765dccbd3a5b35c0208aa1389\n", - " Stored in directory: /root/.cache/pip/wheels/e7/f3/22/152153d6eb222ee7a56ff8617d80ee5207207a8c00a7aab794\n", - "Successfully built antlr4-python3-runtime progress sacremoses youtokentome fasttext kaldi-python-io nemo-toolkit rouge-score sentence-transformers wget distance docopt ipadic asciitree cdifflib pathtools\n", - "Installing collected packages: wget, tokenizers, sentencepiece, safetensors, pydub, progress, plac, pathtools, pangu, opencc, mecab-python3, ipadic, ijson, faiss-cpu, docopt, distance, braceexpand, asciitree, antlr4-python3-runtime, aniso8601, xxhash, webdataset, urllib3, typed-ast, textdistance, sox, smmap, shellingham, setuptools, setproctitle, ruamel.yaml.clib, rapidfuzz, pytest-runner, pypinyin, pynini, pydantic, pybind11, portalocker, pathspec, parameterized, onnx, omegaconf, numcodecs, marshmallow, markdown2, loguru, lightning-utilities, latexcodec, kaldiio, kaldi-python-io, jmespath, jedi, isort, ftfy, fasteners, einops, docutils, docker-pycreds, dill, colorama, click, cdifflib, attrdict, zarr, youtokentome, sentry-sdk, sacremoses, sacrebleu, ruamel.yaml, pypinyin-dict, pybtex, pyannote.core, multiprocess, Levenshtein, jiwer, hydra-core, gitdb, fasttext, botocore, black, texterrors, s3transfer, rouge-score, pybtex-docutils, huggingface-hub, GitPython, g2p-en, flask-restful, wandb, transformers, pyannote.database, datasets, boto3, pyannote.metrics, nemo-text-processing, torchmetrics, sphinxcontrib-bibtex, sentence-transformers, pytorch-lightning, nemo-toolkit, megatron-core, kornia\n", - " Attempting uninstall: urllib3\n", - " Found existing installation: urllib3 2.0.4\n", - " Uninstalling urllib3-2.0.4:\n", - " Successfully uninstalled urllib3-2.0.4\n", - " Attempting uninstall: setuptools\n", - " Found existing installation: setuptools 67.7.2\n", - " Uninstalling setuptools-67.7.2:\n", - " Successfully uninstalled setuptools-67.7.2\n", - " Attempting uninstall: pydantic\n", - " Found existing installation: pydantic 2.3.0\n", - " Uninstalling pydantic-2.3.0:\n", - " Successfully uninstalled pydantic-2.3.0\n", - " Attempting uninstall: docutils\n", - " Found existing installation: docutils 0.18.1\n", - " Uninstalling docutils-0.18.1:\n", - " Successfully uninstalled docutils-0.18.1\n", - " Attempting uninstall: click\n", - " Found existing installation: click 8.1.7\n", - " Uninstalling click-8.1.7:\n", - " Successfully uninstalled click-8.1.7\n", - "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", - "cvxpy 1.3.2 requires setuptools>65.5.1, but you have setuptools 65.5.1 which is incompatible.\u001b[0m\u001b[31m\n", - "\u001b[0mSuccessfully installed GitPython-3.1.35 Levenshtein-0.21.1 aniso8601-9.0.1 antlr4-python3-runtime-4.9.3 asciitree-0.3.3 attrdict-2.0.1 black-19.10b0 boto3-1.28.43 botocore-1.31.43 braceexpand-0.1.7 cdifflib-1.2.6 click-8.0.2 colorama-0.4.6 datasets-2.14.5 dill-0.3.7 distance-0.1.3 docker-pycreds-0.4.0 docopt-0.6.2 docutils-0.17.1 einops-0.6.1 faiss-cpu-1.7.4 fasteners-0.18 fasttext-0.9.2 flask-restful-0.3.10 ftfy-6.1.1 g2p-en-2.1.0 gitdb-4.0.10 huggingface-hub-0.16.4 hydra-core-1.3.2 ijson-3.2.3 ipadic-1.0.0 isort-5.12.0 jedi-0.19.0 jiwer-2.5.2 jmespath-1.0.1 kaldi-python-io-1.2.2 kaldiio-2.18.0 kornia-0.7.0 latexcodec-2.0.1 lightning-utilities-0.9.0 loguru-0.7.1 markdown2-2.4.10 marshmallow-3.20.1 mecab-python3-1.0.5 megatron-core-0.2.0 multiprocess-0.70.15 nemo-text-processing-0.2.0rc0 nemo-toolkit-1.21.0rc0 numcodecs-0.11.0 omegaconf-2.3.0 onnx-1.14.1 opencc-1.1.6 pangu-4.0.6.1 parameterized-0.9.0 pathspec-0.11.2 pathtools-0.1.2 plac-1.3.5 portalocker-2.7.0 progress-1.6 pyannote.core-5.0.0 pyannote.database-5.0.1 pyannote.metrics-3.2.1 pybind11-2.11.1 pybtex-0.24.0 pybtex-docutils-1.0.3 pydantic-1.10.12 pydub-0.25.1 pynini-2.1.5 pypinyin-0.49.0 pypinyin-dict-0.6.0 pytest-runner-6.0.0 pytorch-lightning-2.0.7 rapidfuzz-2.13.7 rouge-score-0.1.2 ruamel.yaml-0.17.32 ruamel.yaml.clib-0.2.7 s3transfer-0.6.2 sacrebleu-2.3.1 sacremoses-0.0.53 safetensors-0.3.3 sentence-transformers-2.2.2 sentencepiece-0.1.99 sentry-sdk-1.30.0 setproctitle-1.3.2 setuptools-65.5.1 shellingham-1.5.3 smmap-5.0.0 sox-1.4.1 sphinxcontrib-bibtex-2.6.1 textdistance-4.5.0 texterrors-0.4.4 tokenizers-0.13.3 torchmetrics-1.1.1 transformers-4.33.1 typed-ast-1.5.5 urllib3-1.26.16 wandb-0.15.10 webdataset-0.1.62 wget-3.2 xxhash-3.3.0 youtokentome-1.0.6 zarr-2.16.1\n", - "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n", - "\u001b[0mAll done!\n", - "Reading package lists... Done\n", - "Building dependency tree... Done\n", - "Reading state information... Done\n", - "The following additional packages will be installed:\n", - " libopencore-amrnb0 libopencore-amrwb0 libsox-fmt-alsa libsox-fmt-base\n", - " libsox3 libwavpack1\n", - "Suggested packages:\n", - " libsox-fmt-all\n", - "The following NEW packages will be installed:\n", - " libopencore-amrnb0 libopencore-amrwb0 libsox-fmt-alsa libsox-fmt-base\n", - " libsox3 libwavpack1 sox\n", - "0 upgraded, 7 newly installed, 0 to remove and 16 not upgraded.\n", - "Need to get 617 kB of archives.\n", - "After this operation, 1,764 kB of additional disk space will be used.\n", - "Get:1 http://archive.ubuntu.com/ubuntu jammy/universe amd64 libopencore-amrnb0 amd64 0.1.5-1 [94.8 kB]\n", - "Get:2 http://archive.ubuntu.com/ubuntu jammy/universe amd64 libopencore-amrwb0 amd64 0.1.5-1 [49.1 kB]\n", - "Get:3 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 libsox3 amd64 14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1 [240 kB]\n", - "Get:4 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 libsox-fmt-alsa amd64 14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1 [11.2 kB]\n", - "Get:5 http://archive.ubuntu.com/ubuntu jammy/main amd64 libwavpack1 amd64 5.4.0-1build2 [83.7 kB]\n", - "Get:6 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 libsox-fmt-base amd64 14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1 [33.7 kB]\n", - "Get:7 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 sox amd64 14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1 [104 kB]\n", - "Fetched 617 kB in 0s (5,224 kB/s)\n", - "Selecting previously unselected package libopencore-amrnb0:amd64.\n", - "(Reading database ... 120901 files and directories currently installed.)\n", - "Preparing to unpack .../0-libopencore-amrnb0_0.1.5-1_amd64.deb ...\n", - "Unpacking libopencore-amrnb0:amd64 (0.1.5-1) ...\n", - "Selecting previously unselected package libopencore-amrwb0:amd64.\n", - "Preparing to unpack .../1-libopencore-amrwb0_0.1.5-1_amd64.deb ...\n", - "Unpacking libopencore-amrwb0:amd64 (0.1.5-1) ...\n", - "Selecting previously unselected package libsox3:amd64.\n", - "Preparing to unpack .../2-libsox3_14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1_amd64.deb ...\n", - "Unpacking libsox3:amd64 (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", - "Selecting previously unselected package libsox-fmt-alsa:amd64.\n", - "Preparing to unpack .../3-libsox-fmt-alsa_14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1_amd64.deb ...\n", - "Unpacking libsox-fmt-alsa:amd64 (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", - "Selecting previously unselected package libwavpack1:amd64.\n", - "Preparing to unpack .../4-libwavpack1_5.4.0-1build2_amd64.deb ...\n", - "Unpacking libwavpack1:amd64 (5.4.0-1build2) ...\n", - "Selecting previously unselected package libsox-fmt-base:amd64.\n", - "Preparing to unpack .../5-libsox-fmt-base_14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1_amd64.deb ...\n", - "Unpacking libsox-fmt-base:amd64 (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", - "Selecting previously unselected package sox.\n", - "Preparing to unpack .../6-sox_14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1_amd64.deb ...\n", - "Unpacking sox (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", - "Setting up libsox3:amd64 (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", - "Setting up libopencore-amrwb0:amd64 (0.1.5-1) ...\n", - "Setting up libsox-fmt-alsa:amd64 (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", - "Setting up libwavpack1:amd64 (5.4.0-1build2) ...\n", - "Setting up libopencore-amrnb0:amd64 (0.1.5-1) ...\n", - "Setting up libsox-fmt-base:amd64 (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", - "Setting up sox (14.4.2+git20190427-2+deb11u2ubuntu0.22.04.1) ...\n", - "Processing triggers for man-db (2.10.2-1) ...\n", - "Processing triggers for libc-bin (2.35-0ubuntu3.1) ...\n", - "/sbin/ldconfig.real: /usr/local/lib/libtbbbind_2_5.so.3 is not a symbolic link\n", - "\n", - "/sbin/ldconfig.real: /usr/local/lib/libtbbbind.so.3 is not a symbolic link\n", - "\n", - "/sbin/ldconfig.real: /usr/local/lib/libtbbmalloc_proxy.so.2 is not a symbolic link\n", - "\n", - "/sbin/ldconfig.real: /usr/local/lib/libtbb.so.12 is not a symbolic link\n", - "\n", - "/sbin/ldconfig.real: /usr/local/lib/libtbbmalloc.so.2 is not a symbolic link\n", - "\n", - "/sbin/ldconfig.real: /usr/local/lib/libtbbbind_2_0.so.3 is not a symbolic link\n", - "\n", - "Collecting dash>=2.1.0 (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1))\n", - " Obtaining dependency information for dash>=2.1.0 from https://files.pythonhosted.org/packages/9b/b4/d522c16b41a8da013fd60a67f9618e57c504cd2c80e02a7a861413b93906/dash-2.13.0-py3-none-any.whl.metadata\n", - " Downloading dash-2.13.0-py3-none-any.whl.metadata (11 kB)\n", - "Collecting dash_bootstrap_components>=1.0.3 (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 2))\n", - " Obtaining dependency information for dash_bootstrap_components>=1.0.3 from https://files.pythonhosted.org/packages/cd/2a/cf963336e8b6745406d357e2f2b33ff1f236531fcadbe250096931855ec0/dash_bootstrap_components-1.5.0-py3-none-any.whl.metadata\n", - " Downloading dash_bootstrap_components-1.5.0-py3-none-any.whl.metadata (5.2 kB)\n", - "Collecting diff_match_patch (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 3))\n", - " Downloading diff_match_patch-20230430-py3-none-any.whl (42 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m42.8/42.8 kB\u001b[0m \u001b[31m2.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: editdistance in /usr/local/lib/python3.10/dist-packages (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 4)) (0.6.2)\n", - "Requirement already satisfied: jiwer in /usr/local/lib/python3.10/dist-packages (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 5)) (2.5.2)\n", - "Requirement already satisfied: librosa>=0.9.1 in /usr/local/lib/python3.10/dist-packages (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (0.10.1)\n", - "Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 7)) (1.23.5)\n", - "Requirement already satisfied: plotly in /usr/local/lib/python3.10/dist-packages (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 8)) (5.15.0)\n", - "Requirement already satisfied: SoundFile in /usr/local/lib/python3.10/dist-packages (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 9)) (0.12.1)\n", - "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from -r ./NeMo/tools/speech_data_explorer/requirements.txt (line 10)) (4.66.1)\n", - "Requirement already satisfied: Flask<2.3.0,>=1.0.4 in /usr/local/lib/python3.10/dist-packages (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (2.2.5)\n", - "Collecting Werkzeug<2.3.0 (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1))\n", - " Downloading Werkzeug-2.2.3-py3-none-any.whl (233 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m233.6/233.6 kB\u001b[0m \u001b[31m9.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting dash-html-components==2.0.0 (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1))\n", - " Downloading dash_html_components-2.0.0-py3-none-any.whl (4.1 kB)\n", - "Collecting dash-core-components==2.0.0 (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1))\n", - " Downloading dash_core_components-2.0.0-py3-none-any.whl (3.8 kB)\n", - "Collecting dash-table==5.0.0 (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1))\n", - " Downloading dash_table-5.0.0-py3-none-any.whl (3.9 kB)\n", - "Requirement already satisfied: typing-extensions>=4.1.1 in /usr/local/lib/python3.10/dist-packages (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (4.7.1)\n", - "Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (2.31.0)\n", - "Collecting retrying (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1))\n", - " Downloading retrying-1.3.4-py3-none-any.whl (11 kB)\n", - "Collecting ansi2html (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1))\n", - " Downloading ansi2html-1.8.0-py3-none-any.whl (16 kB)\n", - "Requirement already satisfied: nest-asyncio in /usr/local/lib/python3.10/dist-packages (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (1.5.7)\n", - "Requirement already satisfied: setuptools in /usr/local/lib/python3.10/dist-packages (from dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (65.5.1)\n", - "Requirement already satisfied: rapidfuzz==2.13.7 in /usr/local/lib/python3.10/dist-packages (from jiwer->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 5)) (2.13.7)\n", - "Requirement already satisfied: audioread>=2.1.9 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (3.0.0)\n", - "Requirement already satisfied: scipy>=1.2.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (1.10.1)\n", - "Requirement already satisfied: scikit-learn>=0.20.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (1.2.2)\n", - "Requirement already satisfied: joblib>=0.14 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (1.3.2)\n", - "Requirement already satisfied: decorator>=4.3.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (4.4.2)\n", - "Requirement already satisfied: numba>=0.51.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (0.56.4)\n", - "Requirement already satisfied: pooch>=1.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (1.7.0)\n", - "Requirement already satisfied: soxr>=0.3.2 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (0.3.6)\n", - "Requirement already satisfied: lazy-loader>=0.1 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (0.3)\n", - "Requirement already satisfied: msgpack>=1.0 in /usr/local/lib/python3.10/dist-packages (from librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (1.0.5)\n", - "Requirement already satisfied: tenacity>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from plotly->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 8)) (8.2.3)\n", - "Requirement already satisfied: packaging in /usr/local/lib/python3.10/dist-packages (from plotly->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 8)) (23.1)\n", - "Requirement already satisfied: cffi>=1.0 in /usr/local/lib/python3.10/dist-packages (from SoundFile->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 9)) (1.15.1)\n", - "Requirement already satisfied: pycparser in /usr/local/lib/python3.10/dist-packages (from cffi>=1.0->SoundFile->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 9)) (2.21)\n", - "Requirement already satisfied: Jinja2>=3.0 in /usr/local/lib/python3.10/dist-packages (from Flask<2.3.0,>=1.0.4->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (3.1.2)\n", - "Requirement already satisfied: itsdangerous>=2.0 in /usr/local/lib/python3.10/dist-packages (from Flask<2.3.0,>=1.0.4->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (2.1.2)\n", - "Requirement already satisfied: click>=8.0 in /usr/local/lib/python3.10/dist-packages (from Flask<2.3.0,>=1.0.4->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (8.0.2)\n", - "Requirement already satisfied: llvmlite<0.40,>=0.39.0dev0 in /usr/local/lib/python3.10/dist-packages (from numba>=0.51.0->librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (0.39.1)\n", - "Requirement already satisfied: platformdirs>=2.5.0 in /usr/local/lib/python3.10/dist-packages (from pooch>=1.0->librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (3.10.0)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (3.2.0)\n", - "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (3.4)\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (1.26.16)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (2023.7.22)\n", - "Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=0.20.0->librosa>=0.9.1->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 6)) (3.2.0)\n", - "Requirement already satisfied: MarkupSafe>=2.1.1 in /usr/local/lib/python3.10/dist-packages (from Werkzeug<2.3.0->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (2.1.3)\n", - "Requirement already satisfied: six>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from retrying->dash>=2.1.0->-r ./NeMo/tools/speech_data_explorer/requirements.txt (line 1)) (1.16.0)\n", - "Downloading dash-2.13.0-py3-none-any.whl (10.4 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m10.4/10.4 MB\u001b[0m \u001b[31m46.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading dash_bootstrap_components-1.5.0-py3-none-any.whl (221 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m221.2/221.2 kB\u001b[0m \u001b[31m23.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hInstalling collected packages: dash-table, dash-html-components, dash-core-components, Werkzeug, retrying, diff_match_patch, ansi2html, dash, dash_bootstrap_components\n", - " Attempting uninstall: Werkzeug\n", - " Found existing installation: Werkzeug 2.3.7\n", - " Uninstalling Werkzeug-2.3.7:\n", - " Successfully uninstalled Werkzeug-2.3.7\n", - "Successfully installed Werkzeug-2.2.3 ansi2html-1.8.0 dash-2.13.0 dash-core-components-2.0.0 dash-html-components-2.0.0 dash-table-5.0.0 dash_bootstrap_components-1.5.0 diff_match_patch-20230430 retrying-1.3.4\n", - "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n", - "\u001b[0m" - ] - } - ], + "outputs": [], "source": [ - "!git clone https://github.com/NVIDIA/NeMo.git\n", + "BRANCH = 'main'\n", + "\n", + "!git clone -b $BRANCH https://github.com/NVIDIA/NeMo\n", "\n", "!apt-get update && apt-get install -y libsndfile1 ffmpeg\n", "!cd NeMo;./reinstall.sh\n", From e52c99b19e4c7c1b0cc97bccb6f2d8745ecfe79a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:15:08 -0700 Subject: [PATCH 091/112] Fix validation in G2PModel and ThutmoseTaggerModel (#7597) (#7606) Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../thutmose_tagger.py | 11 +++++++---- nemo/collections/tts/g2p/models/t5.py | 13 ++++++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/nemo/collections/nlp/models/text_normalization_as_tagging/thutmose_tagger.py b/nemo/collections/nlp/models/text_normalization_as_tagging/thutmose_tagger.py index be82d6a31582..f6e5c155646d 100644 --- a/nemo/collections/nlp/models/text_normalization_as_tagging/thutmose_tagger.py +++ b/nemo/collections/nlp/models/text_normalization_as_tagging/thutmose_tagger.py @@ -236,14 +236,15 @@ def validation_step(self, batch, batch_idx): val_loss_tag = self.loss_fn(logits=tag_logits, labels=tag_labels, loss_mask=labels_mask) val_loss_semiotic = self.loss_fn(logits=semiotic_logits, labels=semiotic_labels, loss_mask=labels_mask) val_loss = val_loss_tag + val_loss_semiotic + self.validation_step_outputs.append(val_loss) return {'val_loss': val_loss} - def on_validation_epoch_end(self, outputs): + def on_validation_epoch_end(self): """ Called at the end of validation to aggregate outputs. :param outputs: list of individual outputs of each validation step. """ - avg_loss = torch.stack([x['val_loss'] for x in outputs]).mean() + avg_loss = torch.stack([x['val_loss'] for x in self.validation_step_outputs]).mean() # calculate metrics and classification report # In our task recall = accuracy, and the recall column - is the per class accuracy @@ -269,6 +270,8 @@ def on_validation_epoch_end(self, outputs): self.tag_multiword_classification_report.reset() self.semiotic_classification_report.reset() + self.validation_step_outputs.clear() # free memory + def test_step(self, batch, batch_idx): """ Lightning calls this inside the test loop with the data from the test dataloader @@ -276,12 +279,12 @@ def test_step(self, batch, batch_idx): """ return self.validation_step(batch, batch_idx) - def on_test_epoch_end(self, outputs): + def on_test_epoch_end(self): """ Called at the end of test to aggregate outputs. :param outputs: list of individual outputs of each test step. """ - return self.on_validation_epoch_end(outputs) + return self.on_validation_epoch_end() # Functions for inference @torch.no_grad() diff --git a/nemo/collections/tts/g2p/models/t5.py b/nemo/collections/tts/g2p/models/t5.py index 16f1f1933fb0..b41fcf1d5945 100644 --- a/nemo/collections/tts/g2p/models/t5.py +++ b/nemo/collections/tts/g2p/models/t5.py @@ -170,7 +170,18 @@ def validation_step(self, batch, batch_idx, dataloader_idx=0, split="val"): ) generated_str, _, _ = self._generate_predictions(input_ids=input_ids, model_max_target_len=self.max_target_len) per = word_error_rate(hypotheses=generated_str, references=labels_str, use_cer=True) - return {f"{split}_loss": val_loss, 'per': per} + output = {f"{split}_loss": val_loss, 'per': per} + if split == 'val': + if isinstance(self.trainer.val_dataloaders, (list, tuple)) and len(self.trainer.val_dataloaders) > 1: + self.validation_step_outputs[dataloader_idx].append(output) + else: + self.validation_step_outputs.append(output) + else: + if isinstance(self.trainer.test_dataloaders, (list, tuple)) and len(self.trainer.test_dataloaders) > 1: + self.test_step_outputs[dataloader_idx].append(output) + else: + self.test_step_outputs.append(output) + return output def test_step(self, batch, batch_idx, dataloader_idx=0): """ From cbb499cd53833451f7f99131828f7b1e69b61c69 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 13:11:09 -0700 Subject: [PATCH 092/112] Broadcast loss only when using pipeline parallelism and within the pipeline parallel domain (#7576) (#7586) * Broadcast loss only when using pipeline parallelism and within the pipeline parallel domain * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Sangkug Lym Co-authored-by: Sangkug Lym Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../language_modeling/megatron_gpt_model.py | 48 ++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py index 3d7a5a127399..46d3f37f5d9b 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py @@ -13,6 +13,7 @@ # limitations under the License. import itertools +import os import queue import warnings from dataclasses import fields @@ -273,6 +274,8 @@ def __init__(self, cfg: DictConfig, trainer: Trainer): self.get_attention_mask_from_fusion = self.cfg.get('get_attention_mask_from_fusion', True) self.initialize_ub = self.cfg.get('ub_tp_comm_overlap', False) + self.log_train_loss = bool(int(os.getenv("NEMO_LOG_TRAIN_LOSS", 1))) + self.loss_broadcast_src_rank = None self.inference_params = None @@ -627,17 +630,29 @@ def training_step(self, dataloader_iter, batch_idx): self.allreduce_first_last_embeddings() ## logging - # we can only log on one rank if it is rank zero so we broadcast from last rank - # we can avoid this broadcast by updating the PTL log function to accept specific ranks - torch.distributed.broadcast(loss_mean, get_last_rank()) + if self.log_train_loss: + # When using pipeline parallelism, loss is calculated only in the last pipeline stage and + # it should be casted to other pipeline stages for logging. + # we can avoid this broadcast by updating the PTL log function to accept specific ranks + if parallel_state.get_pipeline_model_parallel_world_size() > 1: + if self.loss_broadcast_src_rank is None: + dp_size = parallel_state.get_data_parallel_world_size() + tp_size = parallel_state.get_tensor_model_parallel_world_size() + pp_size = parallel_state.get_pipeline_model_parallel_world_size() + rank_in_dp_tp_group = torch.distributed.get_rank() % (dp_size * tp_size) + last_pipeline_stage_offset = (tp_size * dp_size) * (pp_size - 1) + self.loss_broadcast_src_rank = last_pipeline_stage_offset + rank_in_dp_tp_group + torch.distributed.broadcast( + loss_mean, self.loss_broadcast_src_rank, group=parallel_state.get_pipeline_model_parallel_group(), + ) + self.log('reduced_train_loss', loss_mean, prog_bar=True, rank_zero_only=True, batch_size=1) - # (@adithyare) we need to check for the _scaler attribute to enable pp>1 for adapter training - if self.torch_dtype == torch.float16 and hasattr(self.trainer.precision_plugin.scaler, "_scale"): - loss_scale = self.trainer.precision_plugin.scaler._scale - if loss_scale is not None: - self.log('loss_scale', loss_scale, batch_size=1) + # (@adithyare) we need to check for the _scaler attribute to enable pp>1 for adapter training + if self.cfg.precision == 16 and hasattr(self.trainer.precision_plugin.scaler, "_scale"): + loss_scale = self.trainer.precision_plugin.scaler._scale + if loss_scale is not None: + self.log('loss_scale', loss_scale, batch_size=1) - self.log('reduced_train_loss', loss_mean, prog_bar=True, rank_zero_only=True, batch_size=1) lr = self._optimizer.param_groups[0]['lr'] self.log('lr', lr, rank_zero_only=True, batch_size=1) self.log( @@ -962,8 +977,19 @@ def on_validation_epoch_end(self): else: averaged_loss = torch.tensor(0.0, dtype=torch.float32).cuda() - # we can only log on one rank if it is rank zero so we broadcast from last rank - torch.distributed.broadcast(averaged_loss, get_last_rank()) + # When using pipeline parallelism, loss is calculated only in the last pipeline stage and + # it should be casted to other pipeline stages for logging. + if parallel_state.get_pipeline_model_parallel_world_size() > 1: + if self.loss_broadcast_src_rank is None: + dp_size = parallel_state.get_data_parallel_world_size() + tp_size = parallel_state.get_tensor_model_parallel_world_size() + pp_size = parallel_state.get_pipeline_model_parallel_world_size() + rank_in_dp_tp_group = torch.distributed.get_rank() % (dp_size * tp_size) + last_pipeline_stage_offset = (tp_size * dp_size) * (pp_size - 1) + self.loss_broadcast_src_rank = last_pipeline_stage_offset + rank_in_dp_tp_group + torch.distributed.broadcast( + averaged_loss, self.loss_broadcast_src_rank, group=parallel_state.get_pipeline_model_parallel_group(), + ) self.log('val_loss', averaged_loss, prog_bar=True, rank_zero_only=True, batch_size=1) self.validation_step_outputs.clear() # free memory From 2da5c027bce03cc59b47cc22e9eed3f1c235e746 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 3 Oct 2023 16:28:25 -0400 Subject: [PATCH 093/112] Safeguard nemo_text_processing installation on ARM (#7485) * safeguard nemo_text_processing installing Signed-off-by: Jason * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update check Signed-off-by: Jason --------- Signed-off-by: Jason Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../duplex_text_normalization_infer.py | 10 +++++- .../nn_wfst/en/electronic/normalize.py | 13 +++++-- .../en/electronic/tokenize_and_classify.py | 35 +++++++++++-------- .../nn_wfst/en/electronic/verbalize.py | 14 +++++--- .../nn_wfst/en/electronic/verbalize_final.py | 18 ++++++---- .../nn_wfst/en/whitelist/normalize.py | 13 +++++-- .../en/whitelist/tokenize_and_classify.py | 35 +++++++++++-------- .../nn_wfst/en/whitelist/verbalize.py | 14 +++++--- .../nn_wfst/en/whitelist/verbalize_final.py | 17 ++++++--- requirements/requirements_tts.txt | 3 +- .../tts/hui_acg/get_data.py | 10 +++++- .../tts/ljspeech/get_data.py | 10 +++++- .../dataset_processing/tts/preprocess_text.py | 10 +++++- .../tts/sfbilingual/get_data.py | 10 +++++- .../tts/thorsten_neutral/get_data.py | 10 +++++- .../asr/test_text_to_text_dataset.py | 10 +++++- tools/ctc_segmentation/requirements.txt | 3 +- tutorials/asr/ASR_TTS_Tutorial.ipynb | 9 ++++- .../tts/FastPitch_MixerTTS_Training.ipynb | 11 ++++-- tutorials/tts/NeMo_TTS_Primer.ipynb | 11 ++++-- 20 files changed, 199 insertions(+), 67 deletions(-) diff --git a/examples/nlp/duplex_text_normalization/duplex_text_normalization_infer.py b/examples/nlp/duplex_text_normalization/duplex_text_normalization_infer.py index 4cf25e12fc89..6bcc69de7db9 100644 --- a/examples/nlp/duplex_text_normalization/duplex_text_normalization_infer.py +++ b/examples/nlp/duplex_text_normalization/duplex_text_normalization_infer.py @@ -50,7 +50,6 @@ from typing import List from helpers import DECODER_MODEL, TAGGER_MODEL, instantiate_model_and_trainer -from nemo_text_processing.text_normalization.data_loader_utils import post_process_punct from nn_wfst.en.electronic.normalize import ElectronicNormalizer from nn_wfst.en.whitelist.normalize import WhitelistNormalizer from omegaconf import DictConfig, OmegaConf @@ -60,6 +59,15 @@ from nemo.core.config import hydra_runner from nemo.utils import logging +try: + from nemo_text_processing.text_normalization.data_loader_utils import post_process_punct +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) + @hydra_runner(config_path="conf", config_name="duplex_tn_config") def main(cfg: DictConfig) -> None: diff --git a/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/normalize.py b/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/normalize.py index e0d83b42222d..a1f8caa7d959 100644 --- a/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/normalize.py +++ b/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/normalize.py @@ -12,8 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -from nemo_text_processing.text_normalization.normalize import Normalizer -from nemo_text_processing.text_normalization.token_parser import TokenParser +try: + from nemo_text_processing.text_normalization.normalize import Normalizer + from nemo_text_processing.text_normalization.token_parser import TokenParser +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) from nemo.collections.common.tokenizers.moses_tokenizers import MosesProcessor @@ -21,7 +28,7 @@ class ElectronicNormalizer(Normalizer): """ Normalizer for ELECTRONIC. - + Args: input_case: accepting either "lower_cased" or "cased" input. lang: language diff --git a/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/tokenize_and_classify.py b/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/tokenize_and_classify.py index 59a9d9784038..9e0c284d84b0 100644 --- a/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/tokenize_and_classify.py +++ b/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/tokenize_and_classify.py @@ -15,18 +15,25 @@ import os -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_WHITE_SPACE, - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.text_normalization.en.taggers.electronic import ElectronicFst -from nemo_text_processing.text_normalization.en.taggers.punctuation import PunctuationFst -from nemo_text_processing.text_normalization.en.taggers.word import WordFst -from pynini.lib import pynutil +try: + import pynini + from nemo_text_processing.text_normalization.en.graph_utils import ( + NEMO_WHITE_SPACE, + GraphFst, + delete_extra_space, + delete_space, + generator_main, + ) + from nemo_text_processing.text_normalization.en.taggers.electronic import ElectronicFst + from nemo_text_processing.text_normalization.en.taggers.punctuation import PunctuationFst + from nemo_text_processing.text_normalization.en.taggers.word import WordFst + from pynini.lib import pynutil +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) from nemo.utils import logging @@ -34,9 +41,9 @@ class ClassifyFst(GraphFst): """ Final class that composes all other classification grammars. This class can process an entire sentence including punctuation. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. + For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. More details to deployment at NeMo/tools/text_processing_deployment. - + Args: input_case: accepting either "lower_cased" or "cased" input. deterministic: if True will provide a single transduction option, diff --git a/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/verbalize.py b/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/verbalize.py index 6366942d34c8..7236be7a1994 100644 --- a/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/verbalize.py +++ b/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/verbalize.py @@ -12,15 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. - -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from nemo_text_processing.text_normalization.en.verbalizers.electronic import ElectronicFst +try: + from nemo_text_processing.text_normalization.en.graph_utils import GraphFst + from nemo_text_processing.text_normalization.en.verbalizers.electronic import ElectronicFst +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) class VerbalizeFst(GraphFst): """ Composes other verbalizer grammars. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. + For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. More details to deployment at NeMo/tools/text_processing_deployment. Args: diff --git a/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/verbalize_final.py b/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/verbalize_final.py index 4d5d716bd01e..b2cc69ca9e09 100644 --- a/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/verbalize_final.py +++ b/examples/nlp/duplex_text_normalization/nn_wfst/en/electronic/verbalize_final.py @@ -12,12 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, delete_extra_space, delete_space -from nemo_text_processing.text_normalization.en.verbalizers.word import WordFst -from nn_wfst.en.electronic.verbalize import VerbalizeFst -from pynini.lib import pynutil +try: + import pynini + from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, delete_extra_space, delete_space + from nemo_text_processing.text_normalization.en.verbalizers.word import WordFst + from nn_wfst.en.electronic.verbalize import VerbalizeFst + from pynini.lib import pynutil +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) class VerbalizeFinalFst(GraphFst): diff --git a/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/normalize.py b/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/normalize.py index 4109109ec83a..cfb4bef5d1c3 100644 --- a/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/normalize.py +++ b/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/normalize.py @@ -12,8 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -from nemo_text_processing.text_normalization.normalize import Normalizer -from nemo_text_processing.text_normalization.token_parser import TokenParser +try: + from nemo_text_processing.text_normalization.normalize import Normalizer + from nemo_text_processing.text_normalization.token_parser import TokenParser +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) from nemo.collections.common.tokenizers.moses_tokenizers import MosesProcessor @@ -21,7 +28,7 @@ class WhitelistNormalizer(Normalizer): """ Normalizer for WHITELIST. - + Args: input_case: accepting either "lower_cased" or "cased" input. lang: language diff --git a/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/tokenize_and_classify.py b/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/tokenize_and_classify.py index 712812fa8190..c2d69e765bb4 100644 --- a/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/tokenize_and_classify.py +++ b/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/tokenize_and_classify.py @@ -15,18 +15,25 @@ import os -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_WHITE_SPACE, - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.text_normalization.en.taggers.punctuation import PunctuationFst -from nemo_text_processing.text_normalization.en.taggers.whitelist import WhiteListFst -from nemo_text_processing.text_normalization.en.taggers.word import WordFst -from pynini.lib import pynutil +try: + import pynini + from nemo_text_processing.text_normalization.en.graph_utils import ( + NEMO_WHITE_SPACE, + GraphFst, + delete_extra_space, + delete_space, + generator_main, + ) + from nemo_text_processing.text_normalization.en.taggers.punctuation import PunctuationFst + from nemo_text_processing.text_normalization.en.taggers.whitelist import WhiteListFst + from nemo_text_processing.text_normalization.en.taggers.word import WordFst + from pynini.lib import pynutil +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) from nemo.utils import logging @@ -34,9 +41,9 @@ class ClassifyFst(GraphFst): """ Final class that composes all other classification grammars. This class can process an entire sentence including punctuation. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. + For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. More details to deployment at NeMo/tools/text_processing_deployment. - + Args: input_case: accepting either "lower_cased" or "cased" input. deterministic: if True will provide a single transduction option, diff --git a/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/verbalize.py b/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/verbalize.py index e85f067acf96..c647a142ef8c 100644 --- a/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/verbalize.py +++ b/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/verbalize.py @@ -12,15 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. - -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from nemo_text_processing.text_normalization.en.verbalizers.whitelist import WhiteListFst +try: + from nemo_text_processing.text_normalization.en.graph_utils import GraphFst + from nemo_text_processing.text_normalization.en.verbalizers.whitelist import WhiteListFst +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) class VerbalizeFst(GraphFst): """ Composes other verbalizer grammars. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. + For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. More details to deployment at NeMo/tools/text_processing_deployment. Args: diff --git a/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/verbalize_final.py b/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/verbalize_final.py index 4d5d716bd01e..550a8a85d797 100644 --- a/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/verbalize_final.py +++ b/examples/nlp/duplex_text_normalization/nn_wfst/en/whitelist/verbalize_final.py @@ -13,11 +13,18 @@ # limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, delete_extra_space, delete_space -from nemo_text_processing.text_normalization.en.verbalizers.word import WordFst -from nn_wfst.en.electronic.verbalize import VerbalizeFst -from pynini.lib import pynutil +try: + import pynini + from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, delete_extra_space, delete_space + from nemo_text_processing.text_normalization.en.verbalizers.word import WordFst + from nn_wfst.en.electronic.verbalize import VerbalizeFst + from pynini.lib import pynutil +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) class VerbalizeFinalFst(GraphFst): diff --git a/requirements/requirements_tts.txt b/requirements/requirements_tts.txt index bb330aaf2e58..9536faec8c78 100644 --- a/requirements/requirements_tts.txt +++ b/requirements/requirements_tts.txt @@ -4,7 +4,8 @@ jieba kornia librosa matplotlib -nemo_text_processing +# pynini does not currently support aarch, disable nemo_text_processing for now +nemo_text_processing; 'arm' not in platform_machine and 'aarch' not in platform_machine nltk pandas pypinyin diff --git a/scripts/dataset_processing/tts/hui_acg/get_data.py b/scripts/dataset_processing/tts/hui_acg/get_data.py index dfde19f33f57..668d532f321a 100644 --- a/scripts/dataset_processing/tts/hui_acg/get_data.py +++ b/scripts/dataset_processing/tts/hui_acg/get_data.py @@ -21,9 +21,17 @@ import pandas as pd from joblib import Parallel, delayed -from nemo_text_processing.text_normalization.normalize import Normalizer from tqdm import tqdm +try: + from nemo_text_processing.text_normalization.normalize import Normalizer +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) + from nemo.utils import logging # full corpus. diff --git a/scripts/dataset_processing/tts/ljspeech/get_data.py b/scripts/dataset_processing/tts/ljspeech/get_data.py index c8aeed5dbfca..8007b5a0f05a 100644 --- a/scripts/dataset_processing/tts/ljspeech/get_data.py +++ b/scripts/dataset_processing/tts/ljspeech/get_data.py @@ -20,9 +20,17 @@ import sox import wget -from nemo_text_processing.text_normalization.normalize import Normalizer from tqdm import tqdm +try: + from nemo_text_processing.text_normalization.normalize import Normalizer +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) + def get_args(): parser = argparse.ArgumentParser(description='Download LJSpeech and create manifests with predefined split') diff --git a/scripts/dataset_processing/tts/preprocess_text.py b/scripts/dataset_processing/tts/preprocess_text.py index 580a84a02d6f..6afab42a1d6b 100644 --- a/scripts/dataset_processing/tts/preprocess_text.py +++ b/scripts/dataset_processing/tts/preprocess_text.py @@ -32,10 +32,18 @@ from hydra.utils import instantiate from joblib import Parallel, delayed -from nemo_text_processing.text_normalization.normalize import Normalizer from omegaconf import OmegaConf from tqdm import tqdm +try: + from nemo_text_processing.text_normalization.normalize import Normalizer +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) + from nemo.collections.asr.parts.utils.manifest_utils import read_manifest, write_manifest diff --git a/scripts/dataset_processing/tts/sfbilingual/get_data.py b/scripts/dataset_processing/tts/sfbilingual/get_data.py index bb38a6d127ba..806f9882a9f4 100755 --- a/scripts/dataset_processing/tts/sfbilingual/get_data.py +++ b/scripts/dataset_processing/tts/sfbilingual/get_data.py @@ -20,9 +20,17 @@ from pathlib import Path import numpy as np -from nemo_text_processing.text_normalization.normalize import Normalizer from opencc import OpenCC +try: + from nemo_text_processing.text_normalization.normalize import Normalizer +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) + def get_args(): parser = argparse.ArgumentParser( diff --git a/scripts/dataset_processing/tts/thorsten_neutral/get_data.py b/scripts/dataset_processing/tts/thorsten_neutral/get_data.py index 9422c0cd5498..d49d362064fd 100644 --- a/scripts/dataset_processing/tts/thorsten_neutral/get_data.py +++ b/scripts/dataset_processing/tts/thorsten_neutral/get_data.py @@ -32,9 +32,17 @@ from pathlib import Path from joblib import Parallel, delayed -from nemo_text_processing.text_normalization.normalize import Normalizer from tqdm import tqdm +try: + from nemo_text_processing.text_normalization.normalize import Normalizer +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) + from nemo.utils import logging # Thorsten Müller published two neural voice datasets, 21.02 and 22.10. diff --git a/tests/collections/asr/test_text_to_text_dataset.py b/tests/collections/asr/test_text_to_text_dataset.py index bc7a0a9d01dd..92205de41a1b 100644 --- a/tests/collections/asr/test_text_to_text_dataset.py +++ b/tests/collections/asr/test_text_to_text_dataset.py @@ -20,9 +20,17 @@ import pytest from hydra.utils import instantiate -from nemo_text_processing.text_normalization.normalize import Normalizer from omegaconf import OmegaConf +try: + from nemo_text_processing.text_normalization.normalize import Normalizer +except (ImportError, ModuleNotFoundError): + raise ModuleNotFoundError( + "The package `nemo_text_processing` was not installed in this environment. Please refer to" + " https://github.com/NVIDIA/NeMo-text-processing and install this package before using " + "this script" + ) + from nemo.collections.asr.data.text_to_text import TextToTextDataset, TextToTextItem, TextToTextIterableDataset from nemo.collections.common import tokenizers diff --git a/tools/ctc_segmentation/requirements.txt b/tools/ctc_segmentation/requirements.txt index f010b225a66e..bb51e49a0c87 100644 --- a/tools/ctc_segmentation/requirements.txt +++ b/tools/ctc_segmentation/requirements.txt @@ -1,3 +1,4 @@ ctc_segmentation==1.7.1 -nemo_text_processing==0.1.6rc0 +# pynini does not currently support aarch, disable nemo_text_processing for now +nemo_text_processing==0.1.6rc0; 'arm' not in platform_machine and 'aarch' not in platform_machine num2words diff --git a/tutorials/asr/ASR_TTS_Tutorial.ipynb b/tutorials/asr/ASR_TTS_Tutorial.ipynb index 267c84bca9d2..067c007ea3df 100644 --- a/tutorials/asr/ASR_TTS_Tutorial.ipynb +++ b/tutorials/asr/ASR_TTS_Tutorial.ipynb @@ -183,7 +183,14 @@ "from nemo.collections.tts.models import FastPitchModel, SpectrogramEnhancerModel\n", "from nemo.utils.notebook_utils import download_an4\n", "\n", - "from nemo_text_processing.text_normalization.normalize import Normalizer" + "try:\n", + " from nemo_text_processing.text_normalization.normalize import Normalizer\n", + "except ModuleNotFoundError:\n", + " raise ModuleNotFoundError(\n", + " \"The package `nemo_text_processing` was not installed in this environment. Please refer to\"\n", + " \" https://github.com/NVIDIA/NeMo-text-processing and install this package before using \"\n", + " \"this script\"\n", + " )" ] }, { diff --git a/tutorials/tts/FastPitch_MixerTTS_Training.ipynb b/tutorials/tts/FastPitch_MixerTTS_Training.ipynb index 558c0d95d30b..cfcd607ee93c 100644 --- a/tutorials/tts/FastPitch_MixerTTS_Training.ipynb +++ b/tutorials/tts/FastPitch_MixerTTS_Training.ipynb @@ -198,8 +198,15 @@ "source": [ "from nemo.collections.tts.g2p.models.en_us_arpabet import EnglishG2p\n", "from nemo.collections.tts.data.dataset import TTSDataset\n", - "from nemo_text_processing.text_normalization.normalize import Normalizer\n", - "from nemo.collections.common.tokenizers.text_to_speech.tts_tokenizers import EnglishPhonemesTokenizer, EnglishCharsTokenizer" + "from nemo.collections.common.tokenizers.text_to_speech.tts_tokenizers import EnglishPhonemesTokenizer, EnglishCharsTokenizer\n", + "try:\n", + " from nemo_text_processing.text_normalization.normalize import Normalizer\n", + "except ModuleNotFoundError:\n", + " raise ModuleNotFoundError(\n", + " \"The package `nemo_text_processing` was not installed in this environment. Please refer to\"\n", + " \" https://github.com/NVIDIA/NeMo-text-processing and install this package before using \"\n", + " \"this script\"\n", + " )" ] }, { diff --git a/tutorials/tts/NeMo_TTS_Primer.ipynb b/tutorials/tts/NeMo_TTS_Primer.ipynb index 99306744dd05..fe2e34659554 100644 --- a/tutorials/tts/NeMo_TTS_Primer.ipynb +++ b/tutorials/tts/NeMo_TTS_Primer.ipynb @@ -240,7 +240,14 @@ }, "outputs": [], "source": [ - "from nemo_text_processing.text_normalization.normalize import Normalizer\n", + "try:\n", + " from nemo_text_processing.text_normalization.normalize import Normalizer\n", + "except ModuleNotFoundError:\n", + " raise ModuleNotFoundError(\n", + " \"The package `nemo_text_processing` was not installed in this environment. Please refer to\"\n", + " \" https://github.com/NVIDIA/NeMo-text-processing and install this package before using \"\n", + " \"this script\"\n", + " )\n", "\n", "text_normalizer = Normalizer(input_case=\"cased\", lang=\"en\")" ] @@ -777,7 +784,7 @@ "While raw audio shows amplitude versus time and is useful for easily recording and listening, it is not optimal when it comes to processing.\n", "\n", "For processing, it is usually preferable to represent the audio as a **spectrogram** which shows frequency versus time. Specifically, we:\n", - "\n", + "\n", "1. Group together audio samples into a much smaller set of time buckets, called **audio frames**. An audio frame will usually bucket around 50ms of audio.\n", "2. For each audio frame, use the [Fast Fourier transform](https://en.wikipedia.org/wiki/Fast_Fourier_transform) (**FFT**) to calculate the magnitude (ie. energy, amplitude or \"loudness\") and phase (which we don't use) of each frequency bin. We refer to the magnitudes of the frequency bins as a spectrogram\n", "3. Map the original frequency bins onto the [mel scale](https://en.wikipedia.org/wiki/Mel_scale), using overlapped [triangular filters](https://en.wikipedia.org/wiki/Window_function#Triangular_window) to create mel filterbanks.\n", From 5f01aab2adf859f50a7924209c77117947b60673 Mon Sep 17 00:00:00 2001 From: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Date: Tue, 3 Oct 2023 19:35:05 -0700 Subject: [PATCH 094/112] Bound transformers version in requirements (#7620) Signed-off-by: Abhishree Signed-off-by: Sasha Meister --- requirements/requirements_lightning.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements_lightning.txt b/requirements/requirements_lightning.txt index 62468a37e972..adc6aa0e0026 100644 --- a/requirements/requirements_lightning.txt +++ b/requirements/requirements_lightning.txt @@ -2,6 +2,6 @@ hydra-core>1.3,<=1.3.2 omegaconf<=2.3 pytorch-lightning>=2.0,<=2.0.7 torchmetrics>=0.11.0 -transformers>=4.0.1 +transformers>=4.0.1,<=4.33.3 wandb webdataset>=0.1.48,<=0.1.62 From 36ba71f2ae1a2fe5608df78b36acceced5335cdc Mon Sep 17 00:00:00 2001 From: Chen Cui Date: Wed, 4 Oct 2023 12:29:12 -0400 Subject: [PATCH 095/112] fix llama2 70b lora tuning bug (#7622) * fix llama2 70b lora tuning bug Signed-off-by: Chen Cui * Update peft_config.py brackets Signed-off-by: Adi Renduchintala --------- Signed-off-by: Chen Cui Signed-off-by: Adi Renduchintala Co-authored-by: Adi Renduchintala Signed-off-by: Sasha Meister --- nemo/collections/nlp/parts/peft_config.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nemo/collections/nlp/parts/peft_config.py b/nemo/collections/nlp/parts/peft_config.py index dd75747fd73c..524a7fb62368 100644 --- a/nemo/collections/nlp/parts/peft_config.py +++ b/nemo/collections/nlp/parts/peft_config.py @@ -60,10 +60,14 @@ def __init__(self, cfg): else: kv_channels = cfg.kv_channels projection_size = kv_channels * cfg.num_attention_heads + num_query_groups = cfg.get("num_query_groups", None) + if num_query_groups is None: + num_query_groups = cfg.num_attention_heads + qkv_projection_size = projection_size + (2 * kv_channels * num_query_groups) config_args = { "in_features": cfg.hidden_size, - "out_features": 3 * projection_size, + "out_features": qkv_projection_size, "dim": lora_cfg.adapter_dim, "norm_position": None, "norm_type": None, From f8980ba2eb418ff033632caf9fa03d40fa894c34 Mon Sep 17 00:00:00 2001 From: Mehadi Hasan Menon Date: Wed, 4 Oct 2023 22:32:12 +0600 Subject: [PATCH 096/112] Fix import error no module name model_utils (#7629) Signed-off-by: Mehadi Hasan Menon Signed-off-by: Sasha Meister --- .../speech_recognition/confidence/benchmark_asr_confidence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/speech_recognition/confidence/benchmark_asr_confidence.py b/scripts/speech_recognition/confidence/benchmark_asr_confidence.py index 246aa61c2c0e..f4558fa85256 100644 --- a/scripts/speech_recognition/confidence/benchmark_asr_confidence.py +++ b/scripts/speech_recognition/confidence/benchmark_asr_confidence.py @@ -32,7 +32,7 @@ ) from nemo.collections.asr.parts.utils.asr_confidence_utils import ConfidenceConfig from nemo.core.config import hydra_runner -from nemo.utils import logging +from nemo.utils import logging, model_utils """ Get confidence metrics and curve plots for a given model, dataset, and confidence parameters. From c8aa8ac1eefd191ad6140dc0239bff4dde23fbbd Mon Sep 17 00:00:00 2001 From: Nithin Rao Date: Wed, 4 Oct 2023 15:19:03 -0700 Subject: [PATCH 097/112] add fc large ls models (#7641) Signed-off-by: Nithin Rao Koluguri Co-authored-by: Nithin Rao Koluguri Signed-off-by: Sasha Meister --- docs/source/asr/data/benchmark_en.csv | 2 ++ nemo/collections/asr/models/ctc_bpe_models.py | 7 +++++++ nemo/collections/asr/models/rnnt_bpe_models.py | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/docs/source/asr/data/benchmark_en.csv b/docs/source/asr/data/benchmark_en.csv index b41c675f423c..1669ecdeefb5 100644 --- a/docs/source/asr/data/benchmark_en.csv +++ b/docs/source/asr/data/benchmark_en.csv @@ -26,6 +26,8 @@ stt_en_conformer_transducer_medium,EncDecRNNTBPEModel,"https://ngc.nvidia.com/ca stt_en_conformer_transducer_large,EncDecRNNTBPEModel,"https://ngc.nvidia.com/catalog/models/nvidia:nemo:stt_en_conformer_transducer_large" stt_en_conformer_transducer_xlarge,EncDecRNNTBPEModel,"https://ngc.nvidia.com/catalog/models/nvidia:nemo:stt_en_conformer_transducer_xlarge" stt_en_conformer_transducer_xxlarge,EncDecRNNTBPEModel,"https://ngc.nvidia.com/catalog/models/nvidia:nemo:stt_en_conformer_transducer_xxlarge" +stt_en_fastconformer_ctc_large_ls,EncDecCTCModelBPE,"https://ngc.nvidia.com/catalog/models/nvidia:nemo:stt_en_fastconformer_ctc_large_ls" +stt_en_fastconformer_transducer_large_ls,EncDecRNNTBPEModel,"https://ngc.nvidia.com/catalog/models/nvidia:nemo:stt_en_fastconformer_transducer_large_ls" stt_en_fastconformer_transducer_large,EncDecRNNTBPEModel,"https://ngc.nvidia.com/catalog/models/nvidia:nemo:stt_en_fastconformer_transducer_large" stt_en_fastconformer_ctc_large,EncDecCTCModelBPE,"https://ngc.nvidia.com/catalog/models/nvidia:nemo:stt_en_fastconformer_ctc_large" stt_en_fastconformer_hybrid_large_pc,EncDecHybridRNNTCTCBPEModel,"https://ngc.nvidia.com/catalog/models/nvidia:nemo:stt_en_fastconformer_hybrid_large_pc" diff --git a/nemo/collections/asr/models/ctc_bpe_models.py b/nemo/collections/asr/models/ctc_bpe_models.py index aa26f27c29ab..7b17f7918e20 100644 --- a/nemo/collections/asr/models/ctc_bpe_models.py +++ b/nemo/collections/asr/models/ctc_bpe_models.py @@ -606,6 +606,13 @@ def list_available_models(cls) -> List[PretrainedModelInfo]: ) results.append(model) + model = PretrainedModelInfo( + pretrained_model_name="stt_en_fastconformer_ctc_large_ls", + description="For details about this model, please visit https://ngc.nvidia.com/catalog/models/nvidia:nemo:stt_en_fastconformer_ctc_large_ls", + location="https://api.ngc.nvidia.com/v2/models/nvidia/nemo/stt_en_fastconformer_ctc_large_ls/versions/1.0.0/files/stt_en_fastconformer_ctc_large_ls.nemo", + ) + results.append(model) + model = PretrainedModelInfo( pretrained_model_name="stt_en_fastconformer_ctc_xlarge", description="For details about this model, please visit https://ngc.nvidia.com/catalog/models/nvidia:nemo:stt_en_fastconformer_ctc_xlarge", diff --git a/nemo/collections/asr/models/rnnt_bpe_models.py b/nemo/collections/asr/models/rnnt_bpe_models.py index c72d18a8023b..2b8ed315903c 100644 --- a/nemo/collections/asr/models/rnnt_bpe_models.py +++ b/nemo/collections/asr/models/rnnt_bpe_models.py @@ -253,6 +253,13 @@ def list_available_models(cls) -> List[PretrainedModelInfo]: ) results.append(model) + model = PretrainedModelInfo( + pretrained_model_name="stt_en_fastconformer_transducer_large_ls", + description="For details about this model, please visit https://ngc.nvidia.com/catalog/models/nvidia:nemo:stt_en_fastconformer_transducer_large_ls", + location="https://api.ngc.nvidia.com/v2/models/nvidia/nemo/stt_en_fastconformer_transducer_large_ls/versions/1.0.0/files/stt_en_fastconformer_transducer_large_ls.nemo", + ) + results.append(model) + model = PretrainedModelInfo( pretrained_model_name="stt_en_fastconformer_transducer_xlarge", description="For details about this model, please visit https://ngc.nvidia.com/catalog/models/nvidia:nemo:stt_en_fastconformer_transducer_xlarge", From ad3a4de14d06093a469559a9899c45e7acd0a623 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 22:40:58 -0700 Subject: [PATCH 098/112] bugfix: trainer.gpus, trainer.strategy, trainer.accelerator (#7621) (#7642) * [TTS] bugfix for Tacotron2 tutorial due to PTL 2.0 * trainer.gpus -> trainer.devices * fixed related tutorial bugs --------- Signed-off-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Signed-off-by: Sasha Meister --- docs/source/asr/speaker_diarization/datasets.rst | 2 +- examples/asr/experimental/k2/align_speech_parallel.py | 2 +- examples/asr/experimental/k2/speech_to_text_bpe.py | 2 +- .../asr/speech_translation/speech_to_text_transformer.py | 2 +- .../multi_label_intent_slot_classification.py | 2 +- tutorials/tts/FastPitch_MixerTTS_Training.ipynb | 5 +++-- tutorials/tts/Tacotron2_Training.ipynb | 4 ++-- tutorials/tts/Vits_Training.ipynb | 2 +- 8 files changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/source/asr/speaker_diarization/datasets.rst b/docs/source/asr/speaker_diarization/datasets.rst index ff73dad8601a..9f1a43a58f11 100644 --- a/docs/source/asr/speaker_diarization/datasets.rst +++ b/docs/source/asr/speaker_diarization/datasets.rst @@ -107,7 +107,7 @@ Prepare the msdd training dataset for both train and validation. After the train .. code-block:: bash python ./multiscale_diar_decoder.py --config-path='../conf/neural_diarizer' --config-name='msdd_5scl_15_05_50Povl_256x3x32x2.yaml' \ - trainer.gpus=1 \ + trainer.devices=1 \ trainer.max_epochs=20 \ model.base.diarizer.speaker_embeddings.model_path="titanet_large" \ model.train_ds.manifest_filepath="" \ diff --git a/examples/asr/experimental/k2/align_speech_parallel.py b/examples/asr/experimental/k2/align_speech_parallel.py index dcccee48ee27..8ddf036f3e38 100644 --- a/examples/asr/experimental/k2/align_speech_parallel.py +++ b/examples/asr/experimental/k2/align_speech_parallel.py @@ -46,7 +46,7 @@ python align_speech_parallel.py \ trainer.precision=16 \ - trainer.gpus=2 \ + trainer.devices=2 \ ... You may control the dataloader's config by setting the predict_ds: diff --git a/examples/asr/experimental/k2/speech_to_text_bpe.py b/examples/asr/experimental/k2/speech_to_text_bpe.py index 5eefdfaf1fe3..ee3924c7b8ac 100644 --- a/examples/asr/experimental/k2/speech_to_text_bpe.py +++ b/examples/asr/experimental/k2/speech_to_text_bpe.py @@ -50,7 +50,7 @@ model.validation_ds.manifest_filepath= \ model.tokenizer.dir= \ model.tokenizer.type= \ - trainer.gpus=-1 \ + trainer.devices=-1 \ trainer.accelerator="ddp" \ trainer.max_epochs=100 \ model.optim.name="adamw" \ diff --git a/examples/asr/speech_translation/speech_to_text_transformer.py b/examples/asr/speech_translation/speech_to_text_transformer.py index 0c0882859b88..dce19df87a72 100644 --- a/examples/asr/speech_translation/speech_to_text_transformer.py +++ b/examples/asr/speech_translation/speech_to_text_transformer.py @@ -24,7 +24,7 @@ model.tokenizer.dir= \ model.tokenizer.model_path= \ model.tokenizer.type= \ - trainer.gpus=-1 \ + trainer.devices=-1 \ trainer.accelerator="ddp" \ trainer.max_epochs=100 \ model.optim.name="adamw" \ diff --git a/examples/nlp/intent_slot_classification/multi_label_intent_slot_classification.py b/examples/nlp/intent_slot_classification/multi_label_intent_slot_classification.py index bed58ecc43dc..2441885e2ed2 100644 --- a/examples/nlp/intent_slot_classification/multi_label_intent_slot_classification.py +++ b/examples/nlp/intent_slot_classification/multi_label_intent_slot_classification.py @@ -19,7 +19,7 @@ model.data_dir=/home/user/multiatis \ model.validation_ds.prefix=dev \ model.test_ds.prefix=dev \ - trainer.gpus=[0] \ + trainer.devices=[0] \ +trainer.fast_dev_run=true \ exp_manager.exp_dir=checkpoints diff --git a/tutorials/tts/FastPitch_MixerTTS_Training.ipynb b/tutorials/tts/FastPitch_MixerTTS_Training.ipynb index cfcd607ee93c..747ecfa43127 100644 --- a/tutorials/tts/FastPitch_MixerTTS_Training.ipynb +++ b/tutorials/tts/FastPitch_MixerTTS_Training.ipynb @@ -515,7 +515,7 @@ " model.train_ds.dataloader_params.batch_size=24 \\\n", " model.validation_ds.dataloader_params.batch_size=24 \\\n", " exp_manager.exp_dir=./fastpitch_log_dir \\\n", - " model.n_speakers=1 trainer.devices=1 trainer.strategy=null \\\n", + " model.n_speakers=1 trainer.devices=1 trainer.strategy=\"ddp_find_unused_parameters_true\" \\\n", ")" ] }, @@ -565,7 +565,8 @@ "model.train_ds.dataloader_params.num_workers=0 \\\n", "model.validation_ds.dataloader_params.num_workers=0 \\\n", "trainer.max_epochs=3 \\\n", - "trainer.strategy=null \\\n", + "trainer.accelerator=\"gpu\" \\\n", + "trainer.strategy=\"ddp_find_unused_parameters_true\" \\\n", "trainer.check_val_every_n_epoch=1" ] }, diff --git a/tutorials/tts/Tacotron2_Training.ipynb b/tutorials/tts/Tacotron2_Training.ipynb index e2ae5082e608..79546bb79db9 100644 --- a/tutorials/tts/Tacotron2_Training.ipynb +++ b/tutorials/tts/Tacotron2_Training.ipynb @@ -295,9 +295,9 @@ " train_dataset=tests/data/asr/an4_train.json \\\n", " validation_datasets=tests/data/asr/an4_val.json \\\n", " trainer.max_epochs=3 \\\n", - " trainer.accelerator=null \\\n", + " trainer.accelerator='gpu' \\\n", " trainer.check_val_every_n_epoch=1 \\\n", - " +trainer.gpus=1)" + " trainer.devices=1)" ] }, { diff --git a/tutorials/tts/Vits_Training.ipynb b/tutorials/tts/Vits_Training.ipynb index db7161c06c61..a8a7ccc76ae2 100644 --- a/tutorials/tts/Vits_Training.ipynb +++ b/tutorials/tts/Vits_Training.ipynb @@ -251,7 +251,7 @@ " num_nodes: 1\n", " devices: 2\n", " accelerator: gpu\n", - " strategy: ddp\n", + " strategy: ddp_find_unused_parameters_true\n", " precision: 32\n", " max_epochs: -1\n", " accumulate_grad_batches: 1\n", From f83edf6425a63022fe125fbc823f3cb33d26f315 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 22:44:48 -0700 Subject: [PATCH 099/112] fix ssl models ptl monitor val through logging (#7608) (#7614) Signed-off-by: Nithin Rao Koluguri Co-authored-by: Nithin Rao Co-authored-by: Eric Harper Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Signed-off-by: Sasha Meister --- nemo/collections/asr/models/ssl_models.py | 16 ++++++++++++---- tutorials/asr/Self_Supervised_Pre_Training.ipynb | 6 +++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/nemo/collections/asr/models/ssl_models.py b/nemo/collections/asr/models/ssl_models.py index 8de713ca948d..6dca3815119f 100644 --- a/nemo/collections/asr/models/ssl_models.py +++ b/nemo/collections/asr/models/ssl_models.py @@ -527,7 +527,7 @@ def training_step(self, batch, batch_nb): return {'loss': loss_value, 'log': tensorboard_logs} - def validation_step(self, batch, batch_idx, dataloader_idx=0): + def validation_pass(self, batch, batch_idx, dataloader_idx=0): # Set flag to register tensors self._in_validation_step = True @@ -554,9 +554,17 @@ def validation_step(self, batch, batch_idx, dataloader_idx=0): self.reset_registry() del self._in_validation_step - return { - 'val_loss': loss_value, - } + metrics = {'val_loss': loss_value} + + return metrics + + def validation_step(self, batch, batch_idx, dataloader_idx=0): + metrics = self.validation_pass(batch, batch_idx, dataloader_idx) + if type(self.trainer.val_dataloaders) == list and len(self.trainer.val_dataloaders) > 1: + self.validation_step_outputs[dataloader_idx].append(metrics) + else: + self.validation_step_outputs.append(metrics) + return metrics def multi_validation_epoch_end(self, outputs, dataloader_idx: int = 0): val_loss_mean = torch.stack([x['val_loss'] for x in outputs]).mean() diff --git a/tutorials/asr/Self_Supervised_Pre_Training.ipynb b/tutorials/asr/Self_Supervised_Pre_Training.ipynb index 5f2c4dcc14c8..b055f14f5885 100644 --- a/tutorials/asr/Self_Supervised_Pre_Training.ipynb +++ b/tutorials/asr/Self_Supervised_Pre_Training.ipynb @@ -215,7 +215,7 @@ " file_id[file_id.find('-')+1 : file_id.rfind('-')],\n", " file_id + '.wav')\n", "\n", - " duration = librosa.core.get_duration(filename=audio_path)\n", + " duration = librosa.core.get_duration(path=audio_path)\n", "\n", " # Write the metadata to the manifest\n", " metadata = {\n", @@ -331,7 +331,7 @@ "\n", "cfg.model.optim.sched.name = \"CosineAnnealing\"\n", "cfg.model.optim.sched.warmup_steps = 1000\n", - "cfg.model.optim.sched.max_steps = 5000\n", + "cfg.model.optim.sched.max_steps = 2000\n", "#in practice you will usually want a much larger amount of pre-training steps\n", "cfg.model.optim.sched.min_lr = 0\n", "cfg.model.optim.lr = 0.015\n", @@ -554,7 +554,7 @@ "\n", "cfg.model.optim.sched.name = \"CosineAnnealing\"\n", "cfg.model.optim.sched.warmup_steps = 500\n", - "cfg.model.optim.sched.max_steps = 2000\n", + "cfg.model.optim.sched.max_steps = 1000\n", "cfg.model.optim.sched.min_lr = 0\n", "cfg.model.optim.lr = 0.015 #if encoder is frozen, lr can be much higher\n", "cfg.model.optim.weight_decay = 0\n", From 99a914ba382aacc1af7250d8ae6ddb67669a141f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 22:45:19 -0700 Subject: [PATCH 100/112] Fix metrics for SE tutorial (#7604) (#7612) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ante Jukić Co-authored-by: anteju <108555623+anteju@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../asr/models/audio_to_audio_model.py | 34 +++++++++++-------- .../Speech_Enhancement_with_NeMo.ipynb | 9 ++--- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/nemo/collections/asr/models/audio_to_audio_model.py b/nemo/collections/asr/models/audio_to_audio_model.py index b48cd0c14e62..21860cf8ab56 100644 --- a/nemo/collections/asr/models/audio_to_audio_model.py +++ b/nemo/collections/asr/models/audio_to_audio_model.py @@ -17,7 +17,7 @@ import hydra import torch -from omegaconf import DictConfig +from omegaconf import DictConfig, OmegaConf from pytorch_lightning import Trainer from nemo.collections.asr.metrics.audio import AudioMetricWrapper @@ -67,12 +67,15 @@ def _setup_metrics(self, tag: str = 'val'): logging.debug('Found %d metrics for tag %s, not necesary to initialize again', num_dataloaders, tag) return - if 'metrics' not in self._cfg or tag not in self._cfg['metrics']: + if self.cfg.get('metrics') is None: # Metrics are not available in the configuration, nothing to do - logging.debug('No metrics configured for %s in model.metrics.%s', tag, tag) + logging.debug('No metrics configured in model.metrics') return - metrics_cfg = self._cfg['metrics'][tag] + if (metrics_cfg := self.cfg['metrics'].get(tag)) is None: + # Metrics configuration is not available in the configuration, nothing to do + logging.debug('No metrics configured for %s in model.metrics', tag) + return if 'loss' in metrics_cfg: raise ValueError( @@ -86,16 +89,19 @@ def _setup_metrics(self, tag: str = 'val'): # Setup metrics for each dataloader self.metrics[tag] = torch.nn.ModuleList() for dataloader_idx in range(num_dataloaders): - metrics_dataloader_idx = torch.nn.ModuleDict( - { - name: AudioMetricWrapper( - metric=hydra.utils.instantiate(cfg), - channel=cfg.get('channel'), - metric_using_batch_averaging=cfg.get('metric_using_batch_averaging'), - ) - for name, cfg in metrics_cfg.items() - } - ) + metrics_dataloader_idx = {} + for name, cfg in metrics_cfg.items(): + logging.debug('Initialize %s for dataloader_idx %s', name, dataloader_idx) + cfg_dict = OmegaConf.to_container(cfg) + cfg_channel = cfg_dict.pop('channel', None) + cfg_batch_averaging = cfg_dict.pop('metric_using_batch_averaging', None) + metrics_dataloader_idx[name] = AudioMetricWrapper( + metric=hydra.utils.instantiate(cfg_dict), + channel=cfg_channel, + metric_using_batch_averaging=cfg_batch_averaging, + ) + + metrics_dataloader_idx = torch.nn.ModuleDict(metrics_dataloader_idx) self.metrics[tag].append(metrics_dataloader_idx.to(self.device)) logging.info( diff --git a/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb b/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb index 09226c83d654..a3706ab9c0ec 100644 --- a/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb +++ b/tutorials/audio_tasks/speech_enhancement/Speech_Enhancement_with_NeMo.ipynb @@ -102,11 +102,6 @@ "from nemo.collections.asr.parts.utils.manifest_utils import read_manifest, write_manifest\n", "\n", "\n", - "# Used to download data processing scripts\n", - "USER = 'anteju' # TODO: change to 'NVIDIA'\n", - "BRANCH = 'dev/se-tutorial' # TODO: change to 'main'\n", - "\n", - "\n", "# Utility functions for displaying signals and metrics\n", "def show_signal(signal: np.ndarray, sample_rate: int = 16000, tag: str = 'Signal'):\n", " \"\"\"Show the time-domain signal and its spectrogram.\n", @@ -607,7 +602,7 @@ " '_target_': 'torchmetrics.audio.SignalDistortionRatio',\n", " }\n", "})\n", - "config.model.metrics.validation = metrics\n", + "config.model.metrics.val = metrics\n", "config.model.metrics.test = metrics\n", "\n", "print(\"Metrics config:\")\n", @@ -1112,7 +1107,7 @@ " 'channel': 1,\n", " },\n", "})\n", - "config_dual_output.model.metrics.validation = metrics\n", + "config_dual_output.model.metrics.val = metrics\n", "config_dual_output.model.metrics.test = metrics" ] }, From f1f38352797750ffb9f84fe176ceda431f5f321f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:21:49 -0700 Subject: [PATCH 101/112] Add ddp_find_unused_parameters=True and change accelerator to auto (#7623) (#7644) * Add ddp_find_unused_parameters=True and change acclerator to auto * Add ddp_find_unused_parameters True for normalization_as_tagging_train.py --------- Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../intent_slot_classification/intent_slot_classification.py | 4 ++++ examples/nlp/question_answering/question_answering.py | 5 ++++- .../normalization_as_tagging_train.py | 4 ++++ .../nlp/token_classification/token_classification_train.py | 2 +- .../models/text_normalization_as_tagging/thutmose_tagger.py | 2 +- tutorials/nlp/Entity_Linking_Medical.ipynb | 2 +- 6 files changed, 15 insertions(+), 4 deletions(-) diff --git a/examples/nlp/intent_slot_classification/intent_slot_classification.py b/examples/nlp/intent_slot_classification/intent_slot_classification.py index 39b4b7c5dde6..a112ea7785f5 100644 --- a/examples/nlp/intent_slot_classification/intent_slot_classification.py +++ b/examples/nlp/intent_slot_classification/intent_slot_classification.py @@ -23,6 +23,10 @@ @hydra_runner(config_path="conf", config_name="intent_slot_classification_config") def main(cfg: DictConfig) -> None: + # PTL 2.0 has find_unused_parameters as False by default, so its required to set it to True + # when there are unused parameters like here + if cfg.trainer.strategy == 'ddp': + cfg.trainer.strategy = "ddp_find_unused_parameters_true" logging.info(f'Config Params:\n {OmegaConf.to_yaml(cfg)}') trainer = pl.Trainer(**cfg.trainer) exp_manager(trainer, cfg.get("exp_manager", None)) diff --git a/examples/nlp/question_answering/question_answering.py b/examples/nlp/question_answering/question_answering.py index 2bcaf8f9eca2..fcde03582e5c 100644 --- a/examples/nlp/question_answering/question_answering.py +++ b/examples/nlp/question_answering/question_answering.py @@ -28,7 +28,10 @@ @hydra_runner(config_path="conf", config_name="qa_conf") def main(cfg: DictConfig) -> None: pl.seed_everything(42) - + # PTL 2.0 has find_unused_parameters as False by default, so its required to set it to True + # when there are unused parameters like here + if cfg.trainer.strategy == 'ddp': + cfg.trainer.strategy = "ddp_find_unused_parameters_true" logging.info(f'Config: {OmegaConf.to_yaml(cfg)}') trainer = pl.Trainer(**cfg.trainer) exp_dir = exp_manager(trainer, cfg.get("exp_manager", None)) diff --git a/examples/nlp/text_normalization_as_tagging/normalization_as_tagging_train.py b/examples/nlp/text_normalization_as_tagging/normalization_as_tagging_train.py index e87ff7748185..36fe97d2341b 100644 --- a/examples/nlp/text_normalization_as_tagging/normalization_as_tagging_train.py +++ b/examples/nlp/text_normalization_as_tagging/normalization_as_tagging_train.py @@ -62,6 +62,10 @@ @hydra_runner(config_path="conf", config_name="thutmose_tagger_itn_config") def main(cfg: DictConfig) -> None: + # PTL 2.0 has find_unused_parameters as False by default, so its required to set it to True + # when there are unused parameters like here + if cfg.trainer.strategy == 'ddp': + cfg.trainer.strategy = "ddp_find_unused_parameters_true" logging.info(f'Config Params: {OmegaConf.to_yaml(cfg)}') # Train the model diff --git a/examples/nlp/token_classification/token_classification_train.py b/examples/nlp/token_classification/token_classification_train.py index 9b18d10b24e6..51983a1af98b 100644 --- a/examples/nlp/token_classification/token_classification_train.py +++ b/examples/nlp/token_classification/token_classification_train.py @@ -103,7 +103,7 @@ @hydra_runner(config_path="conf", config_name="token_classification_config") def main(cfg: DictConfig) -> None: try: - strategy = NLPDDPStrategy() + strategy = NLPDDPStrategy(find_unused_parameters=True) except (ImportError, ModuleNotFoundError): strategy = None diff --git a/nemo/collections/nlp/models/text_normalization_as_tagging/thutmose_tagger.py b/nemo/collections/nlp/models/text_normalization_as_tagging/thutmose_tagger.py index f6e5c155646d..4c11dc157b2b 100644 --- a/nemo/collections/nlp/models/text_normalization_as_tagging/thutmose_tagger.py +++ b/nemo/collections/nlp/models/text_normalization_as_tagging/thutmose_tagger.py @@ -236,7 +236,7 @@ def validation_step(self, batch, batch_idx): val_loss_tag = self.loss_fn(logits=tag_logits, labels=tag_labels, loss_mask=labels_mask) val_loss_semiotic = self.loss_fn(logits=semiotic_logits, labels=semiotic_labels, loss_mask=labels_mask) val_loss = val_loss_tag + val_loss_semiotic - self.validation_step_outputs.append(val_loss) + self.validation_step_outputs.append({'val_loss': val_loss}) return {'val_loss': val_loss} def on_validation_epoch_end(self): diff --git a/tutorials/nlp/Entity_Linking_Medical.ipynb b/tutorials/nlp/Entity_Linking_Medical.ipynb index 78fe66d9f233..dfdf594e6804 100644 --- a/tutorials/nlp/Entity_Linking_Medical.ipynb +++ b/tutorials/nlp/Entity_Linking_Medical.ipynb @@ -188,7 +188,7 @@ "\n", "# remove distributed training flags\n", "cfg.trainer.strategy = 'auto'\n", - "cfg.trainer.accelerator = None" + "cfg.trainer.accelerator = 'auto'" ] }, { From 1f280a97599119435722263e132d3e05c5e7627a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 10:18:32 -0700 Subject: [PATCH 102/112] Fix py3.11 dataclasses issue (#7616) * Fix py3.11 dataclasses issue (#7582) * Update ASR configs to support Python 3.11 Signed-off-by: smajumdar * Update TTS configs to support Python 3.11 Signed-off-by: smajumdar * Guard MeCab and Ipadic Signed-off-by: smajumdar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix remaining ASR dataclasses Signed-off-by: smajumdar * Fix remaining ASR dataclasses Signed-off-by: smajumdar * Fix scripts Signed-off-by: smajumdar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: smajumdar Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Update name to ConfidenceMethodConfig Signed-off-by: smajumdar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Broadcast loss only when using pipeline parallelism and within the pipeline parallel domain (#7576) (#7586) * Broadcast loss only when using pipeline parallelism and within the pipeline parallel domain * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Sangkug Lym Co-authored-by: Sangkug Lym Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Safeguard nemo_text_processing installation on ARM (#7485) * safeguard nemo_text_processing installing Signed-off-by: Jason * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update check Signed-off-by: Jason --------- Signed-off-by: Jason Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Fix changes to confidence measure Signed-off-by: smajumdar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: smajumdar Signed-off-by: Sangkug Lym Signed-off-by: Jason Co-authored-by: Somshubra Majumdar Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Sangkug Lym Co-authored-by: Jason Signed-off-by: Sasha Meister --- .../experimental/k2/align_speech_parallel.py | 10 +- nemo/collections/asr/metrics/rnnt_wer.py | 8 +- nemo/collections/asr/metrics/wer.py | 12 +- .../asr/models/configs/aligner_config.py | 8 +- .../asr/models/configs/asr_models_config.py | 30 +-- .../configs/classification_models_config.py | 40 ++-- .../asr/models/configs/diarizer_config.py | 28 +-- .../configs/k2_sequence_models_config.py | 8 +- .../asr/models/configs/matchboxnet_config.py | 36 ++-- .../asr/models/configs/quartznet_config.py | 30 ++- .../asr/modules/audio_preprocessing.py | 6 + nemo/collections/asr/parts/k2/classes.py | 4 +- .../multi_head_attention_adapter_module.py | 14 +- .../asr/parts/submodules/ctc_beam_decoding.py | 6 +- .../parts/submodules/ctc_greedy_decoding.py | 4 +- .../parts/submodules/rnnt_greedy_decoding.py | 6 +- .../asr/parts/utils/asr_confidence_utils.py | 4 +- .../common/parts/adapter_modules.py | 6 +- .../common/tokenizers/en_ja_tokenizers.py | 15 +- .../machine_translation/mt_enc_dec_config.py | 172 ++++++++++-------- .../punctuation_capitalization_config.py | 32 ++-- .../common/megatron/megatron_encoders.py | 8 +- nemo/collections/tts/models/fastpitch.py | 6 +- nemo/collections/tts/models/tacotron2.py | 4 +- nemo/core/config/modelPT.py | 10 +- nemo/utils/exp_manager.py | 18 +- requirements/requirements_nlp.txt | 2 +- .../ngram_lm/eval_beamsearch_ngram.py | 6 +- .../eval_beamsearch_ngram_transducer.py | 2 +- .../confidence_ensembles/build_ensemble.py | 26 ++- .../confidence/benchmark_asr_confidence.py | 6 +- .../convert_to_tarred_audio_dataset.py | 2 +- .../asr/test_text_to_text_dataset.py | 4 +- tools/nemo_forced_aligner/align.py | 4 +- 34 files changed, 342 insertions(+), 235 deletions(-) diff --git a/examples/asr/experimental/k2/align_speech_parallel.py b/examples/asr/experimental/k2/align_speech_parallel.py index 8ddf036f3e38..bd03420e94c1 100644 --- a/examples/asr/experimental/k2/align_speech_parallel.py +++ b/examples/asr/experimental/k2/align_speech_parallel.py @@ -74,7 +74,7 @@ import os -from dataclasses import dataclass, is_dataclass +from dataclasses import dataclass, field, is_dataclass from typing import Optional import pytorch_lightning as ptl @@ -94,12 +94,14 @@ @dataclass class ParallelAlignmentConfig: model: Optional[str] = None # name - predict_ds: ASRDatasetConfig = ASRDatasetConfig(return_sample_id=True, num_workers=4) - aligner_args: K2AlignerWrapperModelConfig = K2AlignerWrapperModelConfig() + predict_ds: ASRDatasetConfig = field( + default_factory=lambda: ASRDatasetConfig(return_sample_id=True, num_workers=4) + ) + aligner_args: K2AlignerWrapperModelConfig = field(default_factory=lambda: K2AlignerWrapperModelConfig()) output_path: str = MISSING model_stride: int = 8 - trainer: TrainerConfig = TrainerConfig(gpus=-1, accelerator="ddp") + trainer: TrainerConfig = field(default_factory=lambda: TrainerConfig(gpus=-1, accelerator="ddp")) # there arguments will be ignored return_predictions: bool = False diff --git a/nemo/collections/asr/metrics/rnnt_wer.py b/nemo/collections/asr/metrics/rnnt_wer.py index 97c9c4575982..5518c8f0a25c 100644 --- a/nemo/collections/asr/metrics/rnnt_wer.py +++ b/nemo/collections/asr/metrics/rnnt_wer.py @@ -15,7 +15,7 @@ import copy import re from abc import abstractmethod -from dataclasses import dataclass, is_dataclass +from dataclasses import dataclass, field, is_dataclass from typing import Callable, Dict, List, Optional, Tuple, Union import editdistance @@ -1299,7 +1299,7 @@ class RNNTDecodingConfig: preserve_alignments: Optional[bool] = None # confidence config - confidence_cfg: ConfidenceConfig = ConfidenceConfig() + confidence_cfg: ConfidenceConfig = field(default_factory=lambda: ConfidenceConfig()) # RNNT Joint fused batch size fused_batch_size: Optional[int] = None @@ -1317,10 +1317,10 @@ class RNNTDecodingConfig: rnnt_timestamp_type: str = "all" # can be char, word or all for both # greedy decoding config - greedy: greedy_decode.GreedyRNNTInferConfig = greedy_decode.GreedyRNNTInferConfig() + greedy: greedy_decode.GreedyRNNTInferConfig = field(default_factory=lambda: greedy_decode.GreedyRNNTInferConfig()) # beam decoding config - beam: beam_decode.BeamRNNTInferConfig = beam_decode.BeamRNNTInferConfig(beam_size=4) + beam: beam_decode.BeamRNNTInferConfig = field(default_factory=lambda: beam_decode.BeamRNNTInferConfig(beam_size=4)) # can be used to change temperature for decoding temperature: float = 1.0 diff --git a/nemo/collections/asr/metrics/wer.py b/nemo/collections/asr/metrics/wer.py index 14fa46b308ab..46984ff86435 100644 --- a/nemo/collections/asr/metrics/wer.py +++ b/nemo/collections/asr/metrics/wer.py @@ -14,7 +14,7 @@ import re from abc import abstractmethod -from dataclasses import dataclass, is_dataclass +from dataclasses import dataclass, field, is_dataclass from typing import Callable, Dict, List, Optional, Tuple, Union import editdistance @@ -1297,13 +1297,17 @@ class CTCDecodingConfig: batch_dim_index: int = 0 # greedy decoding config - greedy: ctc_greedy_decoding.GreedyCTCInferConfig = ctc_greedy_decoding.GreedyCTCInferConfig() + greedy: ctc_greedy_decoding.GreedyCTCInferConfig = field( + default_factory=lambda: ctc_greedy_decoding.GreedyCTCInferConfig() + ) # beam decoding config - beam: ctc_beam_decoding.BeamCTCInferConfig = ctc_beam_decoding.BeamCTCInferConfig(beam_size=4) + beam: ctc_beam_decoding.BeamCTCInferConfig = field( + default_factory=lambda: ctc_beam_decoding.BeamCTCInferConfig(beam_size=4) + ) # confidence config - confidence_cfg: ConfidenceConfig = ConfidenceConfig() + confidence_cfg: ConfidenceConfig = field(default_factory=lambda: ConfidenceConfig()) # can be used to change temperature for decoding temperature: float = 1.0 diff --git a/nemo/collections/asr/models/configs/aligner_config.py b/nemo/collections/asr/models/configs/aligner_config.py index 06b41b5c115b..cf2cdd176719 100644 --- a/nemo/collections/asr/models/configs/aligner_config.py +++ b/nemo/collections/asr/models/configs/aligner_config.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dataclasses import dataclass +from dataclasses import dataclass, field from nemo.collections.asr.parts.k2.classes import GraphModuleConfig @@ -35,10 +35,10 @@ class AlignerWrapperModelConfig: word_output: bool = True cpu_decoding: bool = False decode_batch_size: int = 0 - ctc_cfg: AlignerCTCConfig = AlignerCTCConfig() - rnnt_cfg: AlignerRNNTConfig = AlignerRNNTConfig() + ctc_cfg: AlignerCTCConfig = field(default_factory=lambda: AlignerCTCConfig()) + rnnt_cfg: AlignerRNNTConfig = field(default_factory=lambda: AlignerRNNTConfig()) @dataclass class K2AlignerWrapperModelConfig(AlignerWrapperModelConfig): - decoder_module_cfg: GraphModuleConfig = GraphModuleConfig() + decoder_module_cfg: GraphModuleConfig = field(default_factory=lambda: GraphModuleConfig()) diff --git a/nemo/collections/asr/models/configs/asr_models_config.py b/nemo/collections/asr/models/configs/asr_models_config.py index 609d42216659..ce480cac8428 100644 --- a/nemo/collections/asr/models/configs/asr_models_config.py +++ b/nemo/collections/asr/models/configs/asr_models_config.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import Any, Dict, List, Optional from omegaconf import MISSING @@ -74,24 +74,32 @@ class EncDecCTCConfig(model_cfg.ModelConfig): labels: List[str] = MISSING # Dataset configs - train_ds: ASRDatasetConfig = ASRDatasetConfig(manifest_filepath=None, shuffle=True) - validation_ds: ASRDatasetConfig = ASRDatasetConfig(manifest_filepath=None, shuffle=False) - test_ds: ASRDatasetConfig = ASRDatasetConfig(manifest_filepath=None, shuffle=False) + train_ds: ASRDatasetConfig = field(default_factory=lambda: ASRDatasetConfig(manifest_filepath=None, shuffle=True)) + validation_ds: ASRDatasetConfig = field( + default_factory=lambda: ASRDatasetConfig(manifest_filepath=None, shuffle=False) + ) + test_ds: ASRDatasetConfig = field(default_factory=lambda: ASRDatasetConfig(manifest_filepath=None, shuffle=False)) # Optimizer / Scheduler config - optim: Optional[model_cfg.OptimConfig] = model_cfg.OptimConfig(sched=model_cfg.SchedConfig()) + optim: Optional[model_cfg.OptimConfig] = field( + default_factory=lambda: model_cfg.OptimConfig(sched=model_cfg.SchedConfig()) + ) # Model component configs - preprocessor: AudioToMelSpectrogramPreprocessorConfig = AudioToMelSpectrogramPreprocessorConfig() - spec_augment: Optional[SpectrogramAugmentationConfig] = SpectrogramAugmentationConfig() - encoder: ConvASREncoderConfig = ConvASREncoderConfig() - decoder: ConvASRDecoderConfig = ConvASRDecoderConfig() - decoding: CTCDecodingConfig = CTCDecodingConfig() + preprocessor: AudioToMelSpectrogramPreprocessorConfig = field( + default_factory=lambda: AudioToMelSpectrogramPreprocessorConfig() + ) + spec_augment: Optional[SpectrogramAugmentationConfig] = field( + default_factory=lambda: SpectrogramAugmentationConfig() + ) + encoder: ConvASREncoderConfig = field(default_factory=lambda: ConvASREncoderConfig()) + decoder: ConvASRDecoderConfig = field(default_factory=lambda: ConvASRDecoderConfig()) + decoding: CTCDecodingConfig = field(default_factory=lambda: CTCDecodingConfig()) @dataclass class EncDecCTCModelConfig(model_cfg.NemoConfig): - model: EncDecCTCConfig = EncDecCTCConfig() + model: EncDecCTCConfig = field(default_factory=lambda: EncDecCTCConfig()) @dataclass diff --git a/nemo/collections/asr/models/configs/classification_models_config.py b/nemo/collections/asr/models/configs/classification_models_config.py index 0df911e9e69a..33408f591c8e 100644 --- a/nemo/collections/asr/models/configs/classification_models_config.py +++ b/nemo/collections/asr/models/configs/classification_models_config.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import Any, Dict, List, Optional from omegaconf import MISSING @@ -72,30 +72,40 @@ class EncDecClassificationConfig(model_cfg.ModelConfig): timesteps: int = MISSING # Dataset configs - train_ds: EncDecClassificationDatasetConfig = EncDecClassificationDatasetConfig( - manifest_filepath=None, shuffle=True, trim_silence=False + train_ds: EncDecClassificationDatasetConfig = field( + default_factory=lambda: EncDecClassificationDatasetConfig( + manifest_filepath=None, shuffle=True, trim_silence=False + ) ) - validation_ds: EncDecClassificationDatasetConfig = EncDecClassificationDatasetConfig( - manifest_filepath=None, shuffle=False + validation_ds: EncDecClassificationDatasetConfig = field( + default_factory=lambda: EncDecClassificationDatasetConfig(manifest_filepath=None, shuffle=False) ) - test_ds: EncDecClassificationDatasetConfig = EncDecClassificationDatasetConfig( - manifest_filepath=None, shuffle=False + test_ds: EncDecClassificationDatasetConfig = field( + default_factory=lambda: EncDecClassificationDatasetConfig(manifest_filepath=None, shuffle=False) ) # Optimizer / Scheduler config - optim: Optional[model_cfg.OptimConfig] = model_cfg.OptimConfig(sched=model_cfg.SchedConfig()) + optim: Optional[model_cfg.OptimConfig] = field( + default_factory=lambda: model_cfg.OptimConfig(sched=model_cfg.SchedConfig()) + ) # Model component configs - preprocessor: AudioToMFCCPreprocessorConfig = AudioToMFCCPreprocessorConfig() - spec_augment: Optional[SpectrogramAugmentationConfig] = SpectrogramAugmentationConfig() - crop_or_pad_augment: Optional[CropOrPadSpectrogramAugmentationConfig] = CropOrPadSpectrogramAugmentationConfig( - audio_length=timesteps + preprocessor: AudioToMFCCPreprocessorConfig = field(default_factory=lambda: AudioToMFCCPreprocessorConfig()) + spec_augment: Optional[SpectrogramAugmentationConfig] = field( + default_factory=lambda: SpectrogramAugmentationConfig() + ) + crop_or_pad_augment: Optional[CropOrPadSpectrogramAugmentationConfig] = field( + default_factory=lambda: CropOrPadSpectrogramAugmentationConfig(audio_length=-1) ) - encoder: ConvASREncoderConfig = ConvASREncoderConfig() - decoder: ConvASRDecoderClassificationConfig = ConvASRDecoderClassificationConfig() + encoder: ConvASREncoderConfig = field(default_factory=lambda: ConvASREncoderConfig()) + decoder: ConvASRDecoderClassificationConfig = field(default_factory=lambda: ConvASRDecoderClassificationConfig()) + + def __post_init__(self): + if self.crop_or_pad_augment is not None: + self.crop_or_pad_augment.audio_length = self.timesteps @dataclass class EncDecClassificationModelConfig(model_cfg.NemoConfig): - model: EncDecClassificationConfig = EncDecClassificationConfig() + model: EncDecClassificationConfig = field(default_factory=lambda: EncDecClassificationConfig()) diff --git a/nemo/collections/asr/models/configs/diarizer_config.py b/nemo/collections/asr/models/configs/diarizer_config.py index c09bb1dfb8f4..0745a6f2a451 100644 --- a/nemo/collections/asr/models/configs/diarizer_config.py +++ b/nemo/collections/asr/models/configs/diarizer_config.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dataclasses import asdict, dataclass +from dataclasses import asdict, dataclass, field from typing import Any, Dict, Optional, Tuple, Union @@ -78,9 +78,9 @@ class ASRDiarizerParams(DiarizerComponentConfig): @dataclass class ASRDiarizerConfig(DiarizerComponentConfig): model_path: Optional[str] = "stt_en_conformer_ctc_large" - parameters: ASRDiarizerParams = ASRDiarizerParams() - ctc_decoder_parameters: ASRDiarizerCTCDecoderParams = ASRDiarizerCTCDecoderParams() - realigning_lm_parameters: ASRRealigningLMParams = ASRRealigningLMParams() + parameters: ASRDiarizerParams = field(default_factory=lambda: ASRDiarizerParams()) + ctc_decoder_parameters: ASRDiarizerCTCDecoderParams = field(default_factory=lambda: ASRDiarizerCTCDecoderParams()) + realigning_lm_parameters: ASRRealigningLMParams = field(default_factory=lambda: ASRRealigningLMParams()) @dataclass @@ -102,7 +102,7 @@ class VADParams(DiarizerComponentConfig): class VADConfig(DiarizerComponentConfig): model_path: str = "vad_multilingual_marblenet" # .nemo local model path or pretrained VAD model name external_vad_manifest: Optional[str] = None - parameters: VADParams = VADParams() + parameters: VADParams = field(default_factory=lambda: VADParams()) @dataclass @@ -121,7 +121,7 @@ class SpeakerEmbeddingsParams(DiarizerComponentConfig): class SpeakerEmbeddingsConfig(DiarizerComponentConfig): # .nemo local model path or pretrained model name (titanet_large, ecapa_tdnn or speakerverification_speakernet) model_path: Optional[str] = None - parameters: SpeakerEmbeddingsParams = SpeakerEmbeddingsParams() + parameters: SpeakerEmbeddingsParams = field(default_factory=lambda: SpeakerEmbeddingsParams()) @dataclass @@ -142,7 +142,7 @@ class ClusteringParams(DiarizerComponentConfig): @dataclass class ClusteringConfig(DiarizerComponentConfig): - parameters: ClusteringParams = ClusteringParams() + parameters: ClusteringParams = field(default_factory=lambda: ClusteringParams()) @dataclass @@ -166,7 +166,7 @@ class MSDDParams(DiarizerComponentConfig): @dataclass class MSDDConfig(DiarizerComponentConfig): model_path: Optional[str] = "diar_msdd_telephonic" - parameters: MSDDParams = MSDDParams() + parameters: MSDDParams = field(default_factory=lambda: MSDDParams()) @dataclass @@ -176,16 +176,16 @@ class DiarizerConfig(DiarizerComponentConfig): oracle_vad: bool = False # If True, uses RTTM files provided in the manifest file to get VAD timestamps collar: float = 0.25 # Collar value for scoring ignore_overlap: bool = True # Consider or ignore overlap segments while scoring - vad: VADConfig = VADConfig() - speaker_embeddings: SpeakerEmbeddingsConfig = SpeakerEmbeddingsConfig() - clustering: ClusteringConfig = ClusteringConfig() - msdd_model: MSDDConfig = MSDDConfig() - asr: ASRDiarizerConfig = ASRDiarizerConfig() + vad: VADConfig = field(default_factory=lambda: VADConfig()) + speaker_embeddings: SpeakerEmbeddingsConfig = field(default_factory=lambda: SpeakerEmbeddingsConfig()) + clustering: ClusteringConfig = field(default_factory=lambda: ClusteringConfig()) + msdd_model: MSDDConfig = field(default_factory=lambda: MSDDConfig()) + asr: ASRDiarizerConfig = field(default_factory=lambda: ASRDiarizerConfig()) @dataclass class NeuralDiarizerInferenceConfig(DiarizerComponentConfig): - diarizer: DiarizerConfig = DiarizerConfig() + diarizer: DiarizerConfig = field(default_factory=lambda: DiarizerConfig()) device: str = "cpu" verbose: bool = False batch_size: int = 64 diff --git a/nemo/collections/asr/models/configs/k2_sequence_models_config.py b/nemo/collections/asr/models/configs/k2_sequence_models_config.py index 5a112f626f46..53ed3e1377fe 100644 --- a/nemo/collections/asr/models/configs/k2_sequence_models_config.py +++ b/nemo/collections/asr/models/configs/k2_sequence_models_config.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dataclasses import dataclass +from dataclasses import dataclass, field from nemo.collections.asr.models.configs.asr_models_config import EncDecCTCConfig from nemo.collections.asr.parts.k2.classes import GraphModuleConfig as BackendConfig @@ -26,14 +26,14 @@ class GraphModuleConfig: split_batch_size: int = 0 dec_type: str = "topo" transcribe_training: bool = True - backend_cfg: BackendConfig = BackendConfig() + backend_cfg: BackendConfig = field(default_factory=lambda: BackendConfig()) @dataclass class EncDecK2SeqConfig(EncDecCTCConfig): - graph_module_cfg: GraphModuleConfig = GraphModuleConfig() + graph_module_cfg: GraphModuleConfig = field(default_factory=lambda: GraphModuleConfig()) @dataclass class EncDecK2SeqModelConfig(NemoConfig): - model: EncDecK2SeqConfig = EncDecK2SeqConfig() + model: EncDecK2SeqConfig = field(default_factory=lambda: EncDecK2SeqConfig()) diff --git a/nemo/collections/asr/models/configs/matchboxnet_config.py b/nemo/collections/asr/models/configs/matchboxnet_config.py index 55a8b9fedec1..52ec4c35d9e8 100644 --- a/nemo/collections/asr/models/configs/matchboxnet_config.py +++ b/nemo/collections/asr/models/configs/matchboxnet_config.py @@ -107,30 +107,38 @@ class MatchboxNetModelConfig(clf_cfg.EncDecClassificationConfig): labels: List[str] = MISSING # Dataset configs - train_ds: clf_cfg.EncDecClassificationDatasetConfig = clf_cfg.EncDecClassificationDatasetConfig( - manifest_filepath=None, shuffle=True, trim_silence=False + train_ds: clf_cfg.EncDecClassificationDatasetConfig = field( + default_factory=lambda: clf_cfg.EncDecClassificationDatasetConfig( + manifest_filepath=None, shuffle=True, trim_silence=False + ) ) - validation_ds: clf_cfg.EncDecClassificationDatasetConfig = clf_cfg.EncDecClassificationDatasetConfig( - manifest_filepath=None, shuffle=False + validation_ds: clf_cfg.EncDecClassificationDatasetConfig = field( + default_factory=lambda: clf_cfg.EncDecClassificationDatasetConfig(manifest_filepath=None, shuffle=False) ) - test_ds: clf_cfg.EncDecClassificationDatasetConfig = clf_cfg.EncDecClassificationDatasetConfig( - manifest_filepath=None, shuffle=False + test_ds: clf_cfg.EncDecClassificationDatasetConfig = field( + default_factory=lambda: clf_cfg.EncDecClassificationDatasetConfig(manifest_filepath=None, shuffle=False) ) # Optimizer / Scheduler config - optim: Optional[model_cfg.OptimConfig] = model_cfg.OptimConfig(sched=model_cfg.SchedConfig()) + optim: Optional[model_cfg.OptimConfig] = field( + default_factory=lambda: model_cfg.OptimConfig(sched=model_cfg.SchedConfig()) + ) # Model general component configs - preprocessor: AudioToMFCCPreprocessorConfig = AudioToMFCCPreprocessorConfig(window_size=0.025) - spec_augment: Optional[SpectrogramAugmentationConfig] = SpectrogramAugmentationConfig( - freq_masks=2, time_masks=2, freq_width=15, time_width=25, rect_masks=5, rect_time=25, rect_freq=15 + preprocessor: AudioToMFCCPreprocessorConfig = field( + default_factory=lambda: AudioToMFCCPreprocessorConfig(window_size=0.025) + ) + spec_augment: Optional[SpectrogramAugmentationConfig] = field( + default_factory=lambda: SpectrogramAugmentationConfig( + freq_masks=2, time_masks=2, freq_width=15, time_width=25, rect_masks=5, rect_time=25, rect_freq=15 + ) ) - crop_or_pad_augment: Optional[CropOrPadSpectrogramAugmentationConfig] = CropOrPadSpectrogramAugmentationConfig( - audio_length=128 + crop_or_pad_augment: Optional[CropOrPadSpectrogramAugmentationConfig] = field( + default_factory=lambda: CropOrPadSpectrogramAugmentationConfig(audio_length=128) ) - encoder: ConvASREncoderConfig = ConvASREncoderConfig(activation="relu") - decoder: ConvASRDecoderClassificationConfig = ConvASRDecoderClassificationConfig() + encoder: ConvASREncoderConfig = field(default_factory=lambda: ConvASREncoderConfig(activation="relu")) + decoder: ConvASRDecoderClassificationConfig = field(default_factory=lambda: ConvASRDecoderClassificationConfig()) @dataclass diff --git a/nemo/collections/asr/models/configs/quartznet_config.py b/nemo/collections/asr/models/configs/quartznet_config.py index a1231002af41..93412b0053bf 100644 --- a/nemo/collections/asr/models/configs/quartznet_config.py +++ b/nemo/collections/asr/models/configs/quartznet_config.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import Any, Callable, List, Optional from omegaconf import MISSING @@ -174,20 +174,30 @@ class JasperModelConfig(ctc_cfg.EncDecCTCConfig): labels: List[str] = MISSING # Dataset configs - train_ds: ctc_cfg.ASRDatasetConfig = ctc_cfg.ASRDatasetConfig( - manifest_filepath=None, shuffle=True, trim_silence=True + train_ds: ctc_cfg.ASRDatasetConfig = field( + default_factory=lambda: ctc_cfg.ASRDatasetConfig(manifest_filepath=None, shuffle=True, trim_silence=True) + ) + validation_ds: ctc_cfg.ASRDatasetConfig = field( + default_factory=lambda: ctc_cfg.ASRDatasetConfig(manifest_filepath=None, shuffle=False) + ) + test_ds: ctc_cfg.ASRDatasetConfig = field( + default_factory=lambda: ctc_cfg.ASRDatasetConfig(manifest_filepath=None, shuffle=False) ) - validation_ds: ctc_cfg.ASRDatasetConfig = ctc_cfg.ASRDatasetConfig(manifest_filepath=None, shuffle=False) - test_ds: ctc_cfg.ASRDatasetConfig = ctc_cfg.ASRDatasetConfig(manifest_filepath=None, shuffle=False) # Optimizer / Scheduler config - optim: Optional[model_cfg.OptimConfig] = model_cfg.OptimConfig(sched=model_cfg.SchedConfig()) + optim: Optional[model_cfg.OptimConfig] = field( + default_factory=lambda: model_cfg.OptimConfig(sched=model_cfg.SchedConfig()) + ) # Model general component configs - preprocessor: AudioToMelSpectrogramPreprocessorConfig = AudioToMelSpectrogramPreprocessorConfig() - spec_augment: Optional[SpectrogramAugmentationConfig] = SpectrogramAugmentationConfig() - encoder: ConvASREncoderConfig = ConvASREncoderConfig(activation="relu") - decoder: ConvASRDecoderConfig = ConvASRDecoderConfig() + preprocessor: AudioToMelSpectrogramPreprocessorConfig = field( + default_factory=lambda: AudioToMelSpectrogramPreprocessorConfig() + ) + spec_augment: Optional[SpectrogramAugmentationConfig] = field( + default_factory=lambda: SpectrogramAugmentationConfig() + ) + encoder: ConvASREncoderConfig = field(default_factory=lambda: ConvASREncoderConfig(activation="relu")) + decoder: ConvASRDecoderConfig = field(default_factory=lambda: ConvASRDecoderConfig()) @dataclass diff --git a/nemo/collections/asr/modules/audio_preprocessing.py b/nemo/collections/asr/modules/audio_preprocessing.py index 91c0c10b9604..471488bd9647 100644 --- a/nemo/collections/asr/modules/audio_preprocessing.py +++ b/nemo/collections/asr/modules/audio_preprocessing.py @@ -634,6 +634,12 @@ def __init__(self, audio_length): super(CropOrPadSpectrogramAugmentation, self).__init__() self.audio_length = audio_length + if self.audio_length < 0: + raise ValueError( + 'audio_length must be non-negative. If using a dataclass with OmegaConf, ' + 'please call OmegaConf.to_object(cfg) to call appropriate __post_init__ methods.' + ) + @typecheck() @torch.no_grad() def forward(self, input_signal, length): diff --git a/nemo/collections/asr/parts/k2/classes.py b/nemo/collections/asr/parts/k2/classes.py index bb749e15d4c6..d4c498f32a2d 100644 --- a/nemo/collections/asr/parts/k2/classes.py +++ b/nemo/collections/asr/parts/k2/classes.py @@ -13,7 +13,7 @@ # limitations under the License. from abc import ABC -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import Any, Optional, Tuple import torch @@ -43,7 +43,7 @@ class GraphModuleConfig: topo_with_self_loops: bool = True token_lm: Optional[Any] = None intersect_pruned: bool = False - intersect_conf: GraphIntersectDenseConfig = GraphIntersectDenseConfig() + intersect_conf: GraphIntersectDenseConfig = field(default_factory=lambda: GraphIntersectDenseConfig()) boost_coeff: float = 0.0 predictor_window_size: int = 0 predictor_step_size: int = 1 diff --git a/nemo/collections/asr/parts/submodules/adapters/multi_head_attention_adapter_module.py b/nemo/collections/asr/parts/submodules/adapters/multi_head_attention_adapter_module.py index 563d4219baa7..333b630ff7f6 100644 --- a/nemo/collections/asr/parts/submodules/adapters/multi_head_attention_adapter_module.py +++ b/nemo/collections/asr/parts/submodules/adapters/multi_head_attention_adapter_module.py @@ -13,7 +13,7 @@ # limitations under the License. import math -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import Any, Optional import torch @@ -183,7 +183,7 @@ class MultiHeadAttentionAdapterConfig: n_feat: int dropout_rate: float = 0.0 proj_dim: Optional[int] = None - adapter_strategy: Optional[Any] = MHAResidualAddAdapterStrategyConfig() + adapter_strategy: Optional[Any] = field(default_factory=lambda: MHAResidualAddAdapterStrategyConfig()) _target_: str = "{0}.{1}".format(MultiHeadAttentionAdapter.__module__, MultiHeadAttentionAdapter.__name__) @@ -287,7 +287,7 @@ class RelPositionMultiHeadAttentionAdapterConfig: n_feat: int dropout_rate: float = 0.0 proj_dim: Optional[int] = None - adapter_strategy: Optional[Any] = MHAResidualAddAdapterStrategyConfig() + adapter_strategy: Optional[Any] = field(default_factory=lambda: MHAResidualAddAdapterStrategyConfig()) _target_: str = "{0}.{1}".format( RelPositionMultiHeadAttentionAdapter.__module__, RelPositionMultiHeadAttentionAdapter.__name__ ) @@ -336,7 +336,9 @@ class PositionalEncodingAdapterConfig: d_model: int max_len: int = 5000 xscale: float = 1.0 - adapter_strategy: Optional[Any] = adapter_mixin_strategies.ResidualAddAdapterStrategyConfig() + adapter_strategy: Optional[Any] = field( + default_factory=lambda: adapter_mixin_strategies.ResidualAddAdapterStrategyConfig() + ) _target_: str = "{0}.{1}".format(PositionalEncodingAdapter.__module__, PositionalEncodingAdapter.__name__) @@ -378,5 +380,7 @@ class RelPositionalEncodingAdapterConfig: d_model: int max_len: int = 5000 xscale: float = 1.0 - adapter_strategy: Optional[Any] = adapter_mixin_strategies.ResidualAddAdapterStrategyConfig() + adapter_strategy: Optional[Any] = field( + default_factory=lambda: adapter_mixin_strategies.ResidualAddAdapterStrategyConfig() + ) _target_: str = "{0}.{1}".format(RelPositionalEncodingAdapter.__module__, RelPositionalEncodingAdapter.__name__) diff --git a/nemo/collections/asr/parts/submodules/ctc_beam_decoding.py b/nemo/collections/asr/parts/submodules/ctc_beam_decoding.py index 377e43cd5f91..5ed504fd9c45 100644 --- a/nemo/collections/asr/parts/submodules/ctc_beam_decoding.py +++ b/nemo/collections/asr/parts/submodules/ctc_beam_decoding.py @@ -14,7 +14,7 @@ import math import os -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import List, Optional, Tuple, Union import torch @@ -602,5 +602,5 @@ class BeamCTCInferConfig: beam_beta: float = 0.0 kenlm_path: Optional[str] = None - flashlight_cfg: Optional[FlashlightConfig] = FlashlightConfig() - pyctcdecode_cfg: Optional[PyCTCDecodeConfig] = PyCTCDecodeConfig() + flashlight_cfg: Optional[FlashlightConfig] = field(default_factory=lambda: FlashlightConfig()) + pyctcdecode_cfg: Optional[PyCTCDecodeConfig] = field(default_factory=lambda: PyCTCDecodeConfig()) diff --git a/nemo/collections/asr/parts/submodules/ctc_greedy_decoding.py b/nemo/collections/asr/parts/submodules/ctc_greedy_decoding.py index 44ae9f4a134b..686ef79cabad 100644 --- a/nemo/collections/asr/parts/submodules/ctc_greedy_decoding.py +++ b/nemo/collections/asr/parts/submodules/ctc_greedy_decoding.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import List, Optional import torch @@ -253,7 +253,7 @@ class GreedyCTCInferConfig: preserve_alignments: bool = False compute_timestamps: bool = False preserve_frame_confidence: bool = False - confidence_method_cfg: Optional[ConfidenceMethodConfig] = ConfidenceMethodConfig() + confidence_method_cfg: Optional[ConfidenceMethodConfig] = field(default_factory=lambda: ConfidenceMethodConfig()) def __post_init__(self): # OmegaConf.structured ensures that post_init check is always executed diff --git a/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py b/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py index 95b0bdf5fd13..e37d282ed0de 100644 --- a/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py +++ b/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py @@ -26,7 +26,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import List, Optional, Tuple, Union import numpy as np @@ -2185,7 +2185,7 @@ class GreedyRNNTInferConfig: max_symbols_per_step: Optional[int] = 10 preserve_alignments: bool = False preserve_frame_confidence: bool = False - confidence_method_cfg: Optional[ConfidenceMethodConfig] = ConfidenceMethodConfig() + confidence_method_cfg: Optional[ConfidenceMethodConfig] = field(default_factory=lambda: ConfidenceMethodConfig()) def __post_init__(self): # OmegaConf.structured ensures that post_init check is always executed @@ -2201,7 +2201,7 @@ class GreedyBatchedRNNTInferConfig: max_symbols_per_step: Optional[int] = 10 preserve_alignments: bool = False preserve_frame_confidence: bool = False - confidence_method_cfg: Optional[ConfidenceMethodConfig] = ConfidenceMethodConfig() + confidence_method_cfg: Optional[ConfidenceMethodConfig] = field(default_factory=lambda: ConfidenceMethodConfig()) def __post_init__(self): # OmegaConf.structured ensures that post_init check is always executed diff --git a/nemo/collections/asr/parts/utils/asr_confidence_utils.py b/nemo/collections/asr/parts/utils/asr_confidence_utils.py index ddfac3744c6a..c406f5451e84 100644 --- a/nemo/collections/asr/parts/utils/asr_confidence_utils.py +++ b/nemo/collections/asr/parts/utils/asr_confidence_utils.py @@ -14,7 +14,7 @@ import math from abc import ABC, abstractmethod -from dataclasses import dataclass +from dataclasses import dataclass, field from functools import partial from typing import List, Optional @@ -175,7 +175,7 @@ class ConfidenceConfig: preserve_word_confidence: bool = False exclude_blank: bool = True aggregation: str = "min" - method_cfg: ConfidenceMethodConfig = ConfidenceMethodConfig() + method_cfg: ConfidenceMethodConfig = field(default_factory=lambda: ConfidenceMethodConfig()) def __post_init__(self): # OmegaConf.structured ensures that post_init check is always executed diff --git a/nemo/collections/common/parts/adapter_modules.py b/nemo/collections/common/parts/adapter_modules.py index 46c635e73975..2084147f9cbc 100644 --- a/nemo/collections/common/parts/adapter_modules.py +++ b/nemo/collections/common/parts/adapter_modules.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dataclasses import dataclass, is_dataclass +from dataclasses import dataclass, field, is_dataclass from typing import Any, Optional from hydra.utils import instantiate @@ -160,5 +160,7 @@ class LinearAdapterConfig: activation: str = 'swish' norm_position: str = 'pre' dropout: float = 0.0 - adapter_strategy: Optional[Any] = adapter_mixin_strategies.ResidualAddAdapterStrategyConfig() + adapter_strategy: Optional[Any] = field( + default_factory=lambda: adapter_mixin_strategies.ResidualAddAdapterStrategyConfig() + ) _target_: str = "{0}.{1}".format(LinearAdapter.__module__, LinearAdapter.__name__) diff --git a/nemo/collections/common/tokenizers/en_ja_tokenizers.py b/nemo/collections/common/tokenizers/en_ja_tokenizers.py index 63ae11f5da33..cf58130834e9 100644 --- a/nemo/collections/common/tokenizers/en_ja_tokenizers.py +++ b/nemo/collections/common/tokenizers/en_ja_tokenizers.py @@ -14,11 +14,19 @@ import re from typing import List -import ipadic -import MeCab from pangu import spacing from sacremoses import MosesDetokenizer, MosesPunctNormalizer, MosesTokenizer +try: + import ipadic + import MeCab + + HAVE_MECAB = True + HAVE_IPADIC = True +except (ImportError, ModuleNotFoundError): + HAVE_MECAB = False + HAVE_IPADIC = False + class EnJaProcessor: """ @@ -67,6 +75,9 @@ class JaMecabProcessor: """ def __init__(self): + if not HAVE_MECAB or not HAVE_IPADIC: + raise ImportError("Please ensure that you have installed `MeCab` and `ipadic` to use JaMecabProcessor") + self.mecab_tokenizer = MeCab.Tagger(ipadic.MECAB_ARGS + " -Owakati") def detokenize(self, text: List[str]) -> str: diff --git a/nemo/collections/nlp/models/machine_translation/mt_enc_dec_config.py b/nemo/collections/nlp/models/machine_translation/mt_enc_dec_config.py index ea1e86eee88c..1a69c3a33fc0 100644 --- a/nemo/collections/nlp/models/machine_translation/mt_enc_dec_config.py +++ b/nemo/collections/nlp/models/machine_translation/mt_enc_dec_config.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import Any, Optional, Tuple from omegaconf.omegaconf import MISSING @@ -46,7 +46,7 @@ class MTOptimConfig(OptimConfig): lr: float = 1e-3 betas: Tuple[float, float] = (0.9, 0.98) weight_decay: float = 0.0 - sched: Optional[MTSchedConfig] = MTSchedConfig() + sched: Optional[MTSchedConfig] = field(default_factory=lambda: MTSchedConfig()) @dataclass @@ -74,70 +74,80 @@ class MTEncDecModelConfig(EncDecNLPModelConfig): decoder_tokenizer: Any = MISSING decoder: Any = MISSING - head: TokenClassifierConfig = TokenClassifierConfig(log_softmax=True) + head: TokenClassifierConfig = field(default_factory=lambda: TokenClassifierConfig(log_softmax=True)) # dataset configurations - train_ds: Optional[TranslationDataConfig] = TranslationDataConfig( - src_file_name=MISSING, - tgt_file_name=MISSING, - tokens_in_batch=512, - clean=True, - shuffle=True, - cache_ids=False, - use_cache=False, + train_ds: Optional[TranslationDataConfig] = field( + default_factory=lambda: TranslationDataConfig( + src_file_name=MISSING, + tgt_file_name=MISSING, + tokens_in_batch=512, + clean=True, + shuffle=True, + cache_ids=False, + use_cache=False, + ) ) - validation_ds: Optional[TranslationDataConfig] = TranslationDataConfig( - src_file_name=MISSING, - tgt_file_name=MISSING, - tokens_in_batch=512, - clean=False, - shuffle=False, - cache_ids=False, - use_cache=False, + validation_ds: Optional[TranslationDataConfig] = field( + default_factory=lambda: TranslationDataConfig( + src_file_name=MISSING, + tgt_file_name=MISSING, + tokens_in_batch=512, + clean=False, + shuffle=False, + cache_ids=False, + use_cache=False, + ) ) - test_ds: Optional[TranslationDataConfig] = TranslationDataConfig( - src_file_name=MISSING, - tgt_file_name=MISSING, - tokens_in_batch=512, - clean=False, - shuffle=False, - cache_ids=False, - use_cache=False, + test_ds: Optional[TranslationDataConfig] = field( + default_factory=lambda: TranslationDataConfig( + src_file_name=MISSING, + tgt_file_name=MISSING, + tokens_in_batch=512, + clean=False, + shuffle=False, + cache_ids=False, + use_cache=False, + ) ) - optim: Optional[OptimConfig] = MTOptimConfig() + optim: Optional[OptimConfig] = field(default_factory=lambda: MTOptimConfig()) @dataclass class AAYNBaseConfig(MTEncDecModelConfig): # Attention is All You Need Base Configuration - encoder_tokenizer: TokenizerConfig = TokenizerConfig(library='yttm') - decoder_tokenizer: TokenizerConfig = TokenizerConfig(library='yttm') - - encoder: NeMoTransformerEncoderConfig = NeMoTransformerEncoderConfig( - library='nemo', - model_name=None, - pretrained=False, - hidden_size=512, - inner_size=2048, - num_layers=6, - num_attention_heads=8, - ffn_dropout=0.1, - attn_score_dropout=0.1, - attn_layer_dropout=0.1, + encoder_tokenizer: TokenizerConfig = field(default_factory=lambda: TokenizerConfig(library='yttm')) + decoder_tokenizer: TokenizerConfig = field(default_factory=lambda: TokenizerConfig(library='yttm')) + + encoder: NeMoTransformerEncoderConfig = field( + default_factory=lambda: NeMoTransformerEncoderConfig( + library='nemo', + model_name=None, + pretrained=False, + hidden_size=512, + inner_size=2048, + num_layers=6, + num_attention_heads=8, + ffn_dropout=0.1, + attn_score_dropout=0.1, + attn_layer_dropout=0.1, + ) ) - decoder: NeMoTransformerConfig = NeMoTransformerConfig( - library='nemo', - model_name=None, - pretrained=False, - hidden_size=512, - inner_size=2048, - num_layers=6, - num_attention_heads=8, - ffn_dropout=0.1, - attn_score_dropout=0.1, - attn_layer_dropout=0.1, + decoder: NeMoTransformerConfig = field( + default_factory=lambda: NeMoTransformerConfig( + library='nemo', + model_name=None, + pretrained=False, + hidden_size=512, + inner_size=2048, + num_layers=6, + num_attention_heads=8, + ffn_dropout=0.1, + attn_score_dropout=0.1, + attn_layer_dropout=0.1, + ) ) @@ -150,32 +160,36 @@ class MTBottleneckModelConfig(AAYNBaseConfig): recon_per_token: bool = True log_timing: bool = True - encoder: NeMoTransformerBottleneckEncoderConfig = NeMoTransformerBottleneckEncoderConfig( - library='nemo', - model_name=None, - pretrained=False, - hidden_size=512, - inner_size=2048, - num_layers=6, - num_attention_heads=8, - ffn_dropout=0.1, - attn_score_dropout=0.1, - attn_layer_dropout=0.1, - arch='seq2seq', - hidden_steps=32, - hidden_blocks=1, - hidden_init_method='params', + encoder: NeMoTransformerBottleneckEncoderConfig = field( + default_factory=lambda: NeMoTransformerBottleneckEncoderConfig( + library='nemo', + model_name=None, + pretrained=False, + hidden_size=512, + inner_size=2048, + num_layers=6, + num_attention_heads=8, + ffn_dropout=0.1, + attn_score_dropout=0.1, + attn_layer_dropout=0.1, + arch='seq2seq', + hidden_steps=32, + hidden_blocks=1, + hidden_init_method='params', + ) ) - decoder: NeMoTransformerBottleneckDecoderConfig = NeMoTransformerBottleneckDecoderConfig( - library='nemo', - model_name=None, - pretrained=False, - inner_size=2048, - num_layers=6, - num_attention_heads=8, - ffn_dropout=0.1, - attn_score_dropout=0.1, - attn_layer_dropout=0.1, - arch='seq2seq', + decoder: NeMoTransformerBottleneckDecoderConfig = field( + default_factory=lambda: NeMoTransformerBottleneckDecoderConfig( + library='nemo', + model_name=None, + pretrained=False, + inner_size=2048, + num_layers=6, + num_attention_heads=8, + ffn_dropout=0.1, + attn_score_dropout=0.1, + attn_layer_dropout=0.1, + arch='seq2seq', + ) ) diff --git a/nemo/collections/nlp/models/token_classification/punctuation_capitalization_config.py b/nemo/collections/nlp/models/token_classification/punctuation_capitalization_config.py index 6ca811fe273c..86bf12b92315 100644 --- a/nemo/collections/nlp/models/token_classification/punctuation_capitalization_config.py +++ b/nemo/collections/nlp/models/token_classification/punctuation_capitalization_config.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import Any, Dict, Optional from omegaconf.omegaconf import MISSING, DictConfig, OmegaConf, open_dict @@ -215,13 +215,15 @@ class PunctuationCapitalizationModelConfig: This config is a part of :class:`~PunctuationCapitalizationConfig`. """ - class_labels: ClassLabelsConfig = ClassLabelsConfig() + class_labels: ClassLabelsConfig = field(default_factory=lambda: ClassLabelsConfig()) """A mandatory parameter containing a dictionary with names of label id files used in .nemo checkpoints. These file names can also be used for passing label vocabularies to the model. If you wish to use ``class_labels`` for passing vocabularies, please provide path to vocabulary files in ``model.common_dataset_parameters.label_vocab_dir`` parameter.""" - common_dataset_parameters: Optional[CommonDatasetParametersConfig] = CommonDatasetParametersConfig() + common_dataset_parameters: Optional[CommonDatasetParametersConfig] = field( + default_factory=lambda: CommonDatasetParametersConfig() + ) """Label ids and loss mask information information.""" train_ds: Optional[PunctuationCapitalizationTrainDataConfig] = None @@ -233,16 +235,16 @@ class PunctuationCapitalizationModelConfig: test_ds: Optional[PunctuationCapitalizationEvalDataConfig] = None """A configuration for creating test datasets and data loaders.""" - punct_head: HeadConfig = HeadConfig() + punct_head: HeadConfig = field(default_factory=lambda: HeadConfig()) """A configuration for creating punctuation MLP head that is applied to a language model outputs.""" - capit_head: HeadConfig = HeadConfig() + capit_head: HeadConfig = field(default_factory=lambda: HeadConfig()) """A configuration for creating capitalization MLP head that is applied to a language model outputs.""" - tokenizer: Any = TokenizerConfig() + tokenizer: Any = field(default_factory=lambda: TokenizerConfig()) """A configuration for source text tokenizer.""" - language_model: LanguageModelConfig = LanguageModelConfig() + language_model: LanguageModelConfig = field(default_factory=lambda: LanguageModelConfig()) """A configuration of a BERT-like language model which serves as a model body.""" optim: Optional[Any] = None @@ -311,22 +313,30 @@ class PunctuationCapitalizationConfig(NemoConfig): do_testing: bool = False """Whether ot perform testing of the model.""" - model: PunctuationCapitalizationModelConfig = PunctuationCapitalizationModelConfig() + model: PunctuationCapitalizationModelConfig = field(default_factory=lambda: PunctuationCapitalizationModelConfig()) """A configuration for the :class:`~nemo.collections.nlp.models.token_classification.punctuation_capitalization_model.PunctuationCapitalizationModel` model.""" - trainer: Optional[TrainerConfig] = TrainerConfig() + trainer: Optional[TrainerConfig] = field(default_factory=lambda: TrainerConfig()) """Contains ``Trainer`` Lightning class constructor parameters.""" - exp_manager: Optional[ExpManagerConfig] = ExpManagerConfig(name=name, files_to_copy=[]) + exp_manager: Optional[ExpManagerConfig] = field( + default_factory=lambda: ExpManagerConfig(name=None, files_to_copy=[]) + ) """A configuration with various NeMo training options such as output directories, resuming from checkpoint, tensorboard and W&B logging, and so on. For possible options see :ref:`exp-manager-label`.""" + def __post_init__(self): + if self.exp_manager is not None: + self.exp_manager.name = self.name + @dataclass class PunctuationCapitalizationLexicalAudioConfig(PunctuationCapitalizationConfig): - model: PunctuationCapitalizationLexicalAudioModelConfig = PunctuationCapitalizationLexicalAudioModelConfig() + model: PunctuationCapitalizationLexicalAudioModelConfig = field( + default_factory=lambda: PunctuationCapitalizationLexicalAudioModelConfig() + ) def is_legacy_model_config(model_cfg: DictConfig) -> bool: diff --git a/nemo/collections/nlp/modules/common/megatron/megatron_encoders.py b/nemo/collections/nlp/modules/common/megatron/megatron_encoders.py index 4bd99f7120f0..e2c7f22235d7 100644 --- a/nemo/collections/nlp/modules/common/megatron/megatron_encoders.py +++ b/nemo/collections/nlp/modules/common/megatron/megatron_encoders.py @@ -13,7 +13,6 @@ # limitations under the License. """Transformer based language model.""" -from MeCab import Model from nemo.collections.nlp.modules.common.megatron.megatron_perceiver_encoders import MegatronPerceiverEncoderModule from nemo.collections.nlp.modules.common.megatron.megatron_transformer_encoder import MegatronTransformerEncoderModule from nemo.collections.nlp.modules.common.megatron.retrieval_transformer import ( @@ -25,6 +24,13 @@ scaled_init_method_normal, ) +try: + from MeCab import Model + + HAVE_MECAB = True +except (ImportError, ModuleNotFoundError): + HAVE_MECAB = False + try: from apex.transformer.enums import AttnMaskType, ModelType diff --git a/nemo/collections/tts/models/fastpitch.py b/nemo/collections/tts/models/fastpitch.py index a0e5497c6e92..bfb00e00c4ba 100644 --- a/nemo/collections/tts/models/fastpitch.py +++ b/nemo/collections/tts/models/fastpitch.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import contextlib -from dataclasses import dataclass +from dataclasses import dataclass, field from pathlib import Path from typing import List, Optional @@ -70,12 +70,12 @@ class TextTokenizer: apostrophe: bool = True pad_with_space: bool = True add_blank_at: bool = True - g2p: G2PConfig = G2PConfig() + g2p: G2PConfig = field(default_factory=lambda: G2PConfig()) @dataclass class TextTokenizerConfig: - text_tokenizer: TextTokenizer = TextTokenizer() + text_tokenizer: TextTokenizer = field(default_factory=lambda: TextTokenizer()) class FastPitchModel(SpectrogramGenerator, Exportable, FastPitchAdapterModelMixin): diff --git a/nemo/collections/tts/models/tacotron2.py b/nemo/collections/tts/models/tacotron2.py index 846d8afec06e..3fcdee9832ef 100644 --- a/nemo/collections/tts/models/tacotron2.py +++ b/nemo/collections/tts/models/tacotron2.py @@ -13,7 +13,7 @@ # limitations under the License. import contextlib -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import Any, Dict, List, Optional import torch @@ -53,7 +53,7 @@ class Preprocessor: @dataclass class Tacotron2Config: - preprocessor: Preprocessor = Preprocessor() + preprocessor: Preprocessor = field(default_factory=lambda: Preprocessor()) encoder: Dict[Any, Any] = MISSING decoder: Dict[Any, Any] = MISSING postnet: Dict[Any, Any] = MISSING diff --git a/nemo/core/config/modelPT.py b/nemo/core/config/modelPT.py index 70e9b675e360..713c83379431 100644 --- a/nemo/core/config/modelPT.py +++ b/nemo/core/config/modelPT.py @@ -58,11 +58,13 @@ class HydraConfig: class NemoConfig: name: str = MISSING model: ModelConfig = MISSING - trainer: config.TrainerConfig = config.TrainerConfig( - strategy="ddp", enable_checkpointing=False, logger=False, log_every_n_steps=1, accelerator='gpu' + trainer: config.TrainerConfig = field( + default_factory=lambda: config.TrainerConfig( + strategy="ddp", enable_checkpointing=False, logger=False, log_every_n_steps=1, accelerator='gpu' + ) ) - exp_manager: Optional[Any] = exp_manager.ExpManagerConfig() - hydra: HydraConfig = HydraConfig() + exp_manager: Optional[Any] = field(default_factory=lambda: exp_manager.ExpManagerConfig()) + hydra: HydraConfig = field(default_factory=lambda: HydraConfig()) class ModelConfigBuilder: diff --git a/nemo/utils/exp_manager.py b/nemo/utils/exp_manager.py index 1125ae06cabb..1f90ffe0d2a3 100644 --- a/nemo/utils/exp_manager.py +++ b/nemo/utils/exp_manager.py @@ -18,7 +18,7 @@ import sys import time import warnings -from dataclasses import dataclass +from dataclasses import dataclass, field from datetime import timedelta from pathlib import Path from shutil import copy, move @@ -146,28 +146,30 @@ class ExpManagerConfig: create_wandb_logger: Optional[bool] = False wandb_logger_kwargs: Optional[Dict[Any, Any]] = None create_mlflow_logger: Optional[bool] = False - mlflow_logger_kwargs: Optional[MLFlowParams] = MLFlowParams() + mlflow_logger_kwargs: Optional[MLFlowParams] = field(default_factory=lambda: MLFlowParams()) create_dllogger_logger: Optional[bool] = False - dllogger_logger_kwargs: Optional[DLLoggerParams] = DLLoggerParams() + dllogger_logger_kwargs: Optional[DLLoggerParams] = field(default_factory=lambda: DLLoggerParams()) create_clearml_logger: Optional[bool] = False - clearml_logger_kwargs: Optional[ClearMLParams] = ClearMLParams() + clearml_logger_kwargs: Optional[ClearMLParams] = field(default_factory=lambda: ClearMLParams()) # Checkpointing parameters create_checkpoint_callback: Optional[bool] = True - checkpoint_callback_params: Optional[CallbackParams] = CallbackParams() + checkpoint_callback_params: Optional[CallbackParams] = field(default_factory=lambda: CallbackParams()) create_early_stopping_callback: Optional[bool] = False - early_stopping_callback_params: Optional[EarlyStoppingParams] = EarlyStoppingParams() + early_stopping_callback_params: Optional[EarlyStoppingParams] = field( + default_factory=lambda: EarlyStoppingParams() + ) create_preemption_callback: Optional[bool] = True # Additional exp_manager arguments files_to_copy: Optional[List[str]] = None # logs timing of train/val/test steps log_step_timing: Optional[bool] = True - step_timing_kwargs: Optional[StepTimingParams] = StepTimingParams() + step_timing_kwargs: Optional[StepTimingParams] = field(default_factory=lambda: StepTimingParams()) # Configures creation of log files for different ranks log_local_rank_0_only: Optional[bool] = False log_global_rank_0_only: Optional[bool] = False # disable initial validation when resuming from a checkpoint saved during validation disable_validation_on_resume: Optional[bool] = True - ema: Optional[EMAParams] = EMAParams() + ema: Optional[EMAParams] = field(default_factory=lambda: EMAParams()) # Wall clock time limit max_time_per_run: Optional[str] = None # time to sleep non 0 ranks during initialization diff --git a/requirements/requirements_nlp.txt b/requirements/requirements_nlp.txt index da44a726bd29..2a2941c27f5d 100644 --- a/requirements/requirements_nlp.txt +++ b/requirements/requirements_nlp.txt @@ -17,7 +17,7 @@ opencc pangu rapidfuzz rouge_score -sacrebleu[ja] +sacrebleu # manually install sacrebleu[ja] for Japanese support; MeCab is unsupported in Python 3.11+ sentence_transformers tensorstore zarr diff --git a/scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram.py b/scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram.py index 1846a986bf6e..b1cd385f4198 100644 --- a/scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram.py +++ b/scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram.py @@ -112,14 +112,14 @@ class EvalBeamSearchNGramConfig: beam_beta: List[float] = field(default_factory=lambda: [0.0]) # The beta parameter or list of the betas for the beam search decoding decoding_strategy: str = "beam" - decoding: ctc_beam_decoding.BeamCTCInferConfig = ctc_beam_decoding.BeamCTCInferConfig(beam_size=128) + decoding: ctc_beam_decoding.BeamCTCInferConfig = field(default_factory=lambda: ctc_beam_decoding.BeamCTCInferConfig(beam_size=128)) - text_processing: Optional[TextProcessingConfig] = TextProcessingConfig( + text_processing: Optional[TextProcessingConfig] = field(default_factory=lambda: TextProcessingConfig( punctuation_marks = ".,?", separate_punctuation = False, do_lowercase = False, rm_punctuation = False, - ) + )) # fmt: on diff --git a/scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram_transducer.py b/scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram_transducer.py index bbc33d214636..8548b839024f 100644 --- a/scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram_transducer.py +++ b/scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram_transducer.py @@ -115,7 +115,7 @@ class EvalBeamSearchNGramConfig: hat_subtract_ilm: bool = False hat_ilm_weight: List[float] = field(default_factory=lambda: [0.0]) - decoding: rnnt_beam_decoding.BeamRNNTInferConfig = rnnt_beam_decoding.BeamRNNTInferConfig(beam_size=128) + decoding: rnnt_beam_decoding.BeamRNNTInferConfig = field(default_factory=lambda: rnnt_beam_decoding.BeamRNNTInferConfig(beam_size=128)) # fmt: on diff --git a/scripts/confidence_ensembles/build_ensemble.py b/scripts/confidence_ensembles/build_ensemble.py index 99bfa6187b30..e40997c4aca2 100644 --- a/scripts/confidence_ensembles/build_ensemble.py +++ b/scripts/confidence_ensembles/build_ensemble.py @@ -75,7 +75,7 @@ import sys import tempfile from copy import deepcopy -from dataclasses import dataclass +from dataclasses import dataclass, field from pathlib import Path from typing import Dict, List, Optional, Tuple @@ -209,19 +209,23 @@ class BuildEnsembleConfig: random_seed: int = 0 # for reproducibility # default confidence, can override - confidence: ConfidenceConfig = ConfidenceConfig( - # we keep frame confidences and apply aggregation manually to get full-utterance confidence - preserve_frame_confidence=True, - exclude_blank=True, - aggregation="mean", - method_cfg=ConfidenceMethodConfig(name="entropy", entropy_type="renyi", alpha=0.25, entropy_norm="lin",), + confidence: ConfidenceConfig = field( + default_factory=lambda: ConfidenceConfig( + # we keep frame confidences and apply aggregation manually to get full-utterance confidence + preserve_frame_confidence=True, + exclude_blank=True, + aggregation="mean", + measure_cfg=ConfidenceMethodConfig(name="entropy", entropy_type="renyi", alpha=0.25, entropy_norm="lin",), + ) ) temperature: float = 1.0 # this is optional, but can be used to change any aspect of the transcription # config, such as batch size or amp usage. Note that model, data and confidence # will be overriden by this script - transcription: transcribe_speech.TranscriptionConfig = transcribe_speech.TranscriptionConfig() + transcription: transcribe_speech.TranscriptionConfig = field( + default_factory=lambda: transcribe_speech.TranscriptionConfig() + ) # set to True to tune the confidence. # requires dev manifests to be specified for each model @@ -229,12 +233,14 @@ class BuildEnsembleConfig: # used to specify what to tune over. By default runs tuning over some # reasonalbe grid, so that it does not take forever. # Can be changed as needed - tune_confidence_config: TuneConfidenceConfig = TuneConfidenceConfig() + tune_confidence_config: TuneConfidenceConfig = field(default_factory=lambda: TuneConfidenceConfig()) # very fast to tune and can be important in case of imbalanced datasets # will automatically set to False if dev data is not available tune_logistic_regression: bool = True - tune_logistic_regression_config: TuneLogisticRegressionConfig = TuneLogisticRegressionConfig() + tune_logistic_regression_config: TuneLogisticRegressionConfig = field( + default_factory=lambda: TuneLogisticRegressionConfig() + ) def __post_init__(self): """Checking that if any dev data is provided, all are provided. diff --git a/scripts/speech_recognition/confidence/benchmark_asr_confidence.py b/scripts/speech_recognition/confidence/benchmark_asr_confidence.py index f4558fa85256..0c8ab9535fae 100644 --- a/scripts/speech_recognition/confidence/benchmark_asr_confidence.py +++ b/scripts/speech_recognition/confidence/benchmark_asr_confidence.py @@ -14,7 +14,7 @@ import json import os -from dataclasses import dataclass, is_dataclass +from dataclasses import dataclass, field, is_dataclass from pathlib import Path from typing import Optional @@ -124,7 +124,9 @@ class ConfidenceBenchmarkingConfig: # Confidence configs target_level: str = "auto" # Choices: "word", "token", "auto" (for both word- and token-level confidence) - confidence_cfg: ConfidenceConfig = ConfidenceConfig(preserve_word_confidence=True, preserve_token_confidence=True) + confidence_cfg: ConfidenceConfig = field( + default_factory=lambda: ConfidenceConfig(preserve_word_confidence=True, preserve_token_confidence=True) + ) grid_params: Optional[str] = None # a dictionary with lists of parameters to iteratively benchmark on diff --git a/scripts/speech_recognition/convert_to_tarred_audio_dataset.py b/scripts/speech_recognition/convert_to_tarred_audio_dataset.py index 64c086997ef0..3a739fe3a57d 100644 --- a/scripts/speech_recognition/convert_to_tarred_audio_dataset.py +++ b/scripts/speech_recognition/convert_to_tarred_audio_dataset.py @@ -202,7 +202,7 @@ class ASRTarredDatasetMetadata: num_samples_per_shard: Optional[int] = None is_concatenated_manifest: bool = False - dataset_config: Optional[ASRTarredDatasetConfig] = ASRTarredDatasetConfig() + dataset_config: Optional[ASRTarredDatasetConfig] = field(default_factory=lambda: ASRTarredDatasetConfig()) history: Optional[List[Any]] = field(default_factory=lambda: []) def __post_init__(self): diff --git a/tests/collections/asr/test_text_to_text_dataset.py b/tests/collections/asr/test_text_to_text_dataset.py index 92205de41a1b..bf68b51f1e6e 100644 --- a/tests/collections/asr/test_text_to_text_dataset.py +++ b/tests/collections/asr/test_text_to_text_dataset.py @@ -15,7 +15,7 @@ import json import multiprocessing import os -from dataclasses import dataclass +from dataclasses import dataclass, field from pathlib import Path import pytest @@ -118,7 +118,7 @@ class TextTokenizerCfg: apostrophe: bool = True pad_with_space: bool = True add_blank_at: bool = True - g2p: G2PConfig = G2PConfig() + g2p: G2PConfig = field(default_factory=lambda: G2PConfig()) config = OmegaConf.create(OmegaConf.to_yaml(TextTokenizerCfg())) return instantiate(config) diff --git a/tools/nemo_forced_aligner/align.py b/tools/nemo_forced_aligner/align.py index 77ab3111fd91..d298e8072d58 100644 --- a/tools/nemo_forced_aligner/align.py +++ b/tools/nemo_forced_aligner/align.py @@ -149,8 +149,8 @@ class AlignmentConfig: # Output file configs save_output_file_formats: List[str] = field(default_factory=lambda: ["ctm", "ass"]) - ctm_file_config: CTMFileConfig = CTMFileConfig() - ass_file_config: ASSFileConfig = ASSFileConfig() + ctm_file_config: CTMFileConfig = field(default_factory=lambda: CTMFileConfig()) + ass_file_config: ASSFileConfig = field(default_factory=lambda: ASSFileConfig()) @hydra_runner(config_name="AlignmentConfig", schema=AlignmentConfig) From 2ab6aa30b3e79e1cd58bee4258e31ca0f3996f58 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 20:19:26 -0700 Subject: [PATCH 103/112] Fix issues with Dockerfile (#7650) (#7652) Signed-off-by: smajumdar Co-authored-by: Somshubra Majumdar Signed-off-by: Sasha Meister --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 843c0c27df45..7d1539ad2db3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,7 +38,7 @@ RUN apt-get update && \ libsndfile1 sox \ libfreetype6 \ swig \ - ffmpeg=ffmpeg_5.1.2-3ubuntu1 \ + ffmpeg \ libavdevice-dev && \ rm -rf /var/lib/apt/lists/* @@ -111,7 +111,7 @@ RUN /usr/bin/test -n "$NEMO_VERSION" && \ /bin/echo "export BASE_IMAGE=${BASE_IMAGE}" >> /root/.bashrc # Install NeMo -RUN --mount=from=nemo-src,target=/tmp/nemo cd /tmp/nemo && pip install ".[all]" +RUN --mount=from=nemo-src,target=/tmp/nemo,rw cd /tmp/nemo && pip install ".[all]" # Check install RUN python -c "import nemo.collections.nlp as nemo_nlp" && \ From 290e22858fef23ae313be94fd592515ca1f99245 Mon Sep 17 00:00:00 2001 From: Aleksandr Laptev Date: Fri, 6 Oct 2023 13:03:07 +0700 Subject: [PATCH 104/112] [ASR] RNN-T greedy decoding max_frames fix for alignment and confidence (#7635) * decoding and test fix Signed-off-by: Aleksandr Laptev * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Aleksandr Laptev Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../parts/submodules/rnnt_greedy_decoding.py | 31 +++++++++++-------- .../asr/test_asr_rnnt_encdec_model.py | 24 +++++++++----- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py b/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py index e37d282ed0de..a0aea07f7bc8 100644 --- a/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py +++ b/nemo/collections/asr/parts/submodules/rnnt_greedy_decoding.py @@ -649,6 +649,7 @@ def _greedy_decode_blank_as_pad( # Mask buffers blank_mask = torch.full([batchsize], fill_value=0, dtype=torch.bool, device=device) + blank_mask_prev = None # Get max sequence length max_out_len = out_len.max() @@ -666,6 +667,8 @@ def _greedy_decode_blank_as_pad( # Batch: [B, T, D], but Bi may have seq len < max(seq_lens_in_batch) # Forcibly mask with "blank" tokens, for all sample where current time step T > seq_len blank_mask = time_idx >= out_len + blank_mask_prev = blank_mask.clone() + # Start inner loop while not_blank and (self.max_symbols is None or symbols_added < self.max_symbols): # Batch prediction and joint network steps @@ -694,7 +697,6 @@ def _greedy_decode_blank_as_pad( # This is accumulating blanks over all time steps T and all target steps min(max_symbols, U) k_is_blank = k == self._blank_index blank_mask.bitwise_or_(k_is_blank) - all_blanks = torch.all(blank_mask) del k_is_blank @@ -705,10 +707,9 @@ def _greedy_decode_blank_as_pad( logp_vals = logp.to('cpu') logp_ids = logp_vals.max(1)[1] for batch_idx, is_blank in enumerate(blank_mask): - # we only want to update non-blanks, unless we are at the last step in the loop where - # all elements produced blanks, otherwise there will be duplicate predictions - # saved in alignments - if time_idx < out_len[batch_idx] and (all_blanks or not is_blank): + # we only want to update non-blanks and first-time blanks, + # otherwise alignments will contain duplicate predictions + if time_idx < out_len[batch_idx] and (not blank_mask_prev[batch_idx] or not is_blank): hypotheses[batch_idx].alignments[-1].append( (logp_vals[batch_idx], logp_ids[batch_idx]) ) @@ -720,13 +721,15 @@ def _greedy_decode_blank_as_pad( # Insert probabilities into last timestep per sample confidence = self._get_confidence(logp) for batch_idx, is_blank in enumerate(blank_mask): - if time_idx < out_len[batch_idx] and (all_blanks or not is_blank): + if time_idx < out_len[batch_idx] and (not blank_mask_prev[batch_idx] or not is_blank): hypotheses[batch_idx].frame_confidence[-1].append(confidence[batch_idx]) del logp + blank_mask_prev.bitwise_or_(blank_mask) + # If all samples predict / have predicted prior blanks, exit loop early # This is equivalent to if single sample predicted k - if all_blanks: + if blank_mask.all(): not_blank = False else: # Collect batch indices where blanks occurred now/past @@ -847,6 +850,7 @@ def _greedy_decode_masked( # Mask buffers blank_mask = torch.full([batchsize], fill_value=0, dtype=torch.bool, device=device) + blank_mask_prev = None # Get max sequence length max_out_len = out_len.max() @@ -866,6 +870,7 @@ def _greedy_decode_masked( # Batch: [B, T, D], but Bi may have seq len < max(seq_lens_in_batch) # Forcibly mask with "blank" tokens, for all sample where current time step T > seq_len blank_mask = time_idx >= out_len + blank_mask_prev = blank_mask.clone() # Start inner loop while not_blank and (self.max_symbols is None or symbols_added < self.max_symbols): @@ -904,7 +909,6 @@ def _greedy_decode_masked( # This is accumulating blanks over all time steps T and all target steps min(max_symbols, U) k_is_blank = k == self._blank_index blank_mask.bitwise_or_(k_is_blank) - all_blanks = torch.all(blank_mask) # If preserving alignments, check if sequence length of sample has been reached # before adding alignment @@ -913,10 +917,9 @@ def _greedy_decode_masked( logp_vals = logp.to('cpu') logp_ids = logp_vals.max(1)[1] for batch_idx, is_blank in enumerate(blank_mask): - # we only want to update non-blanks, unless we are at the last step in the loop where - # all elements produced blanks, otherwise there will be duplicate predictions - # saved in alignments - if time_idx < out_len[batch_idx] and (all_blanks or not is_blank): + # we only want to update non-blanks and first-time blanks, + # otherwise alignments will contain duplicate predictions + if time_idx < out_len[batch_idx] and (not blank_mask_prev[batch_idx] or not is_blank): hypotheses[batch_idx].alignments[-1].append( (logp_vals[batch_idx], logp_ids[batch_idx]) ) @@ -929,10 +932,12 @@ def _greedy_decode_masked( # Insert probabilities into last timestep per sample confidence = self._get_confidence(logp) for batch_idx, is_blank in enumerate(blank_mask): - if time_idx < out_len[batch_idx] and (all_blanks or not is_blank): + if time_idx < out_len[batch_idx] and (not blank_mask_prev[batch_idx] or not is_blank): hypotheses[batch_idx].frame_confidence[-1].append(confidence[batch_idx]) del logp + blank_mask_prev.bitwise_or_(blank_mask) + # If all samples predict / have predicted prior blanks, exit loop early # This is equivalent to if single sample predicted k if blank_mask.all(): diff --git a/tests/collections/asr/test_asr_rnnt_encdec_model.py b/tests/collections/asr/test_asr_rnnt_encdec_model.py index 12e08006a3e4..b466d09c460d 100644 --- a/tests/collections/asr/test_asr_rnnt_encdec_model.py +++ b/tests/collections/asr/test_asr_rnnt_encdec_model.py @@ -45,6 +45,10 @@ def predict( add_sos: bool = False, batch_size: Optional[int] = None, ) -> Tuple[torch.Tensor, List[torch.Tensor]]: + if batch_size is None: + batch_size = 1 + if y is not None: + y = y + torch.tensor([0] * self.vocab_size + [1], dtype=torch.float32).repeat(y.size()) if y is not None and state is not None: return (y + state) / 2, y * state elif state is not None: @@ -52,8 +56,8 @@ def predict( elif y is not None: return y, torch.tensor([0] * self.vocab_size + [1], dtype=torch.float32).repeat(y.size()) return ( - torch.tensor([0] * self.vocab_size + [1], dtype=torch.float32).repeat([1, 1, 1]), - torch.tensor([0] * self.vocab_size + [1], dtype=torch.float32).repeat([1, 1, 1]), + torch.tensor([0] * self.vocab_size + [1], dtype=torch.float32).repeat([1, batch_size, 1]), + torch.tensor([0] * self.vocab_size + [1], dtype=torch.float32).repeat([1, batch_size, 1]), ) def initialize_state(self, y: torch.Tensor) -> List[torch.Tensor]: @@ -66,8 +70,11 @@ def score_hypothesis( def batch_select_state(self, batch_states: List[torch.Tensor], idx: int) -> List[List[torch.Tensor]]: if batch_states is not None: - states = batch_states[0][idx] - states = states.long() + try: + states = batch_states[0][idx] + states = states.long() + except Exception as e: + raise Exception(batch_states, idx) return [states] else: return None @@ -92,8 +99,12 @@ def joint(self, f: torch.Tensor, g: torch.Tensor) -> torch.Tensor: setup["decoder"] = DummyRNNTDecoder(vocab_size=2, blank_idx=2, blank_as_pad=True) setup["decoder_masked"] = DummyRNNTDecoder(vocab_size=2, blank_idx=2, blank_as_pad=False) setup["joint"] = DummyRNNTJoint() - setup["encoder_output"] = torch.tensor([[[1, 0, 0], [0, 1, 0], [0, 0, 1]]], dtype=torch.float32).transpose(1, 2) - setup["encoded_lengths"] = torch.tensor([3]) + # expected timesteps for max_symbols_per_step=5 are [[0, 0, 0, 0, 0, 1, 1], [1, 1, 1, 1, 1]], + # so we have both looped and regular iteration on the second frame + setup["encoder_output"] = torch.tensor( + [[[1, 0, 0], [0, 1, 0], [0, 0, 1]], [[0, 0, 1], [2, 0, 0], [0, 0, 0]]], dtype=torch.float32 + ).transpose(1, 2) + setup["encoded_lengths"] = torch.tensor([3, 2]) return setup @@ -726,7 +737,6 @@ def test_greedy_decoding_preserve_frame_confidence(self, greedy_class): decoder, joint_net, blank_index=len(token_list), - preserve_alignments=True, preserve_frame_confidence=True, max_symbols_per_step=max_symbols_per_step, ) From 25e86ab68d17f3bf4ddc4b21b957ae62772ad181 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 23:25:16 -0700 Subject: [PATCH 105/112] [ASR] Fix type error in jasper (#7636) (#7653) Signed-off-by: Ryan Co-authored-by: Ryan Langman Signed-off-by: Sasha Meister --- nemo/collections/asr/parts/submodules/jasper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nemo/collections/asr/parts/submodules/jasper.py b/nemo/collections/asr/parts/submodules/jasper.py index 6496586463e4..900d0bd55a10 100644 --- a/nemo/collections/asr/parts/submodules/jasper.py +++ b/nemo/collections/asr/parts/submodules/jasper.py @@ -375,7 +375,7 @@ def update_masked_length(self, max_len, seq_range=None, device=None): self.lens = self.lens.to(device) else: self.lens = seq_range - self.max_len = max_len + self.max_len = torch.tensor(max_len) def mask_input(self, x, lens): max_len = x.size(2) From d4b6a7530c2a4262b124df2dc348a10fb6c0aa8f Mon Sep 17 00:00:00 2001 From: Ryan Langman Date: Fri, 6 Oct 2023 10:07:15 -0700 Subject: [PATCH 106/112] [TTS] Add STFT and SI-SDR loss to audio codec recipe (#7468) * [TTS] Add STFT and SI-SDR loss to audio codec recipe Signed-off-by: Ryan * [TTS] Fix STFT resolution Signed-off-by: Ryan * [TTS] Fix training metric logging Signed-off-by: Ryan * [TTS] Add docstring to mel and stft losses Signed-off-by: Ryan * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Ryan Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../conf/audio_codec/audio_codec_24000.yaml | 170 ++++++++++++++++ .../{encodec.yaml => encodec_24000.yaml} | 12 +- .../tts/losses/audio_codec_loss.py | 186 +++++++++++++++++- nemo/collections/tts/models/audio_codec.py | 141 ++++++++----- .../tts/modules/encodec_modules.py | 35 ++-- nemo/collections/tts/parts/utils/helpers.py | 2 +- .../tts/losses/test_audio_codec_loss.py | 48 ++++- .../tts/modules/test_audio_codec_modules.py | 7 +- 8 files changed, 508 insertions(+), 93 deletions(-) create mode 100644 examples/tts/conf/audio_codec/audio_codec_24000.yaml rename examples/tts/conf/audio_codec/{encodec.yaml => encodec_24000.yaml} (94%) diff --git a/examples/tts/conf/audio_codec/audio_codec_24000.yaml b/examples/tts/conf/audio_codec/audio_codec_24000.yaml new file mode 100644 index 000000000000..14e6fe236545 --- /dev/null +++ b/examples/tts/conf/audio_codec/audio_codec_24000.yaml @@ -0,0 +1,170 @@ +# This config contains the default values for training 24khz audio codec model +# If you want to train model on other dataset, you can change config values according to your dataset. +# Most dataset-specific arguments are in the head of the config file, see below. + +name: EnCodec + +max_epochs: ??? +# Adjust batch size based on GPU memory +batch_size: 16 +# When doing weighted sampling with multiple manifests, this defines how many training steps are in an epoch. +# If null, then weighted sampling is disabled. +weighted_sampling_steps_per_epoch: null + +# Dataset metadata for each manifest +# https://github.com/NVIDIA/NeMo/blob/main/nemo/collections/tts/data/vocoder_dataset.py#L39-L41 +train_ds_meta: ??? +val_ds_meta: ??? + +log_ds_meta: ??? +log_dir: ??? + +# Modify these values based on your sample rate +sample_rate: 24000 +train_n_samples: 24000 +down_sample_rates: [2, 4, 5, 8] +up_sample_rates: [8, 5, 4, 2] +# The number of samples per encoded audio frame. Should be the product of the down_sample_rates. +# For example 2 * 4 * 5 * 8 = 320. +samples_per_frame: 320 + +model: + + max_epochs: ${max_epochs} + steps_per_epoch: ${weighted_sampling_steps_per_epoch} + + sample_rate: ${sample_rate} + samples_per_frame: ${samples_per_frame} + + mel_loss_l1_scale: 15.0 + mel_loss_l2_scale: 0.0 + stft_loss_scale: 15.0 + time_domain_loss_scale: 0.0 + + # Probability of updating the discriminator during each training step + # For example, update the discriminator 2/3 times (2 updates for every 3 batches) + disc_updates_per_period: 2 + disc_update_period: 3 + + # All resolutions for reconstruction loss, ordered [num_fft, hop_length, window_length] + loss_resolutions: [ + [32, 8, 32], [64, 16, 64], [128, 32, 128], [256, 64, 256], [512, 128, 512], [1024, 256, 1024], [2048, 512, 2048] + ] + mel_loss_dims: [5, 10, 20, 40, 80, 160, 320] + mel_loss_log_guard: 1.0 + stft_loss_log_guard: 1.0 + + train_ds: + dataset: + _target_: nemo.collections.tts.data.vocoder_dataset.VocoderDataset + weighted_sampling_steps_per_epoch: ${weighted_sampling_steps_per_epoch} + sample_rate: ${sample_rate} + n_samples: ${train_n_samples} + min_duration: 1.01 + max_duration: null + dataset_meta: ${train_ds_meta} + + dataloader_params: + batch_size: ${batch_size} + drop_last: true + num_workers: 4 + + validation_ds: + dataset: + _target_: nemo.collections.tts.data.vocoder_dataset.VocoderDataset + sample_rate: ${sample_rate} + n_samples: null + min_duration: null + max_duration: null + trunc_duration: 10.0 # Only use the first 10 seconds of audio for computing validation loss + dataset_meta: ${val_ds_meta} + + dataloader_params: + batch_size: 8 + num_workers: 2 + + # Configures how audio samples are generated and saved during training. + # Remove this section to disable logging. + log_config: + log_dir: ${log_dir} + log_epochs: [10, 50] + epoch_frequency: 100 + log_tensorboard: false + log_wandb: false + + generators: + - _target_: nemo.collections.tts.parts.utils.callbacks.AudioCodecArtifactGenerator + log_audio: true + log_encoding: true + log_dequantized: true + + dataset: + _target_: nemo.collections.tts.data.vocoder_dataset.VocoderDataset + sample_rate: ${sample_rate} + n_samples: null + min_duration: null + max_duration: null + trunc_duration: 15.0 # Only log the first 15 seconds of generated audio. + dataset_meta: ${log_ds_meta} + + dataloader_params: + batch_size: 4 + num_workers: 2 + + audio_encoder: + _target_: nemo.collections.tts.modules.encodec_modules.HifiGanEncoder + down_sample_rates: ${down_sample_rates} + + audio_decoder: + _target_: nemo.collections.tts.modules.encodec_modules.SEANetDecoder + up_sample_rates: ${up_sample_rates} + + vector_quantizer: + _target_: nemo.collections.tts.modules.encodec_modules.ResidualVectorQuantizer + num_codebooks: 8 + + discriminator: + _target_: nemo.collections.tts.modules.encodec_modules.MultiResolutionDiscriminatorSTFT + resolutions: [[128, 32, 128], [256, 64, 256], [512, 128, 512], [1024, 256, 1024], [2048, 512, 2048]] + + # The original EnCodec uses hinged loss, but squared-GAN loss is more stable + # and reduces the need to tune the loss weights or use a gradient balancer. + generator_loss: + _target_: nemo.collections.tts.losses.audio_codec_loss.GeneratorSquaredLoss + + discriminator_loss: + _target_: nemo.collections.tts.losses.audio_codec_loss.DiscriminatorSquaredLoss + + optim: + _target_: torch.optim.Adam + lr: 3e-4 + betas: [0.5, 0.9] + + sched: + name: ExponentialLR + gamma: 0.998 + +trainer: + num_nodes: 1 + devices: 1 + accelerator: gpu + strategy: ddp_find_unused_parameters_true + precision: 32 # Vector quantization only works with 32-bit precision. + max_epochs: ${max_epochs} + accumulate_grad_batches: 1 + enable_checkpointing: False # Provided by exp_manager + logger: false # Provided by exp_manager + log_every_n_steps: 100 + check_val_every_n_epoch: 10 + benchmark: false + +exp_manager: + exp_dir: null + name: ${name} + create_tensorboard_logger: true + create_checkpoint_callback: true + create_wandb_logger: false + checkpoint_callback_params: + monitor: val_loss + resume_if_exists: false + resume_ignore_no_checkpoint: false diff --git a/examples/tts/conf/audio_codec/encodec.yaml b/examples/tts/conf/audio_codec/encodec_24000.yaml similarity index 94% rename from examples/tts/conf/audio_codec/encodec.yaml rename to examples/tts/conf/audio_codec/encodec_24000.yaml index a0f7a50c92dd..8caaea76294b 100644 --- a/examples/tts/conf/audio_codec/encodec.yaml +++ b/examples/tts/conf/audio_codec/encodec_24000.yaml @@ -36,17 +36,23 @@ model: sample_rate: ${sample_rate} samples_per_frame: ${samples_per_frame} - mel_loss_scale: 5.0 + mel_loss_l1_scale: 1.0 + mel_loss_l2_scale: 1.0 + stft_loss_scale: 0.0 time_domain_loss_scale: 0.1 + # Probability of updating the discriminator during each training step # For example, update the discriminator 2/3 times (2 updates for every 3 batches) disc_updates_per_period: 2 disc_update_period: 3 - # All resolutions for mel reconstruction loss, ordered [num_fft, hop_length, window_length] - mel_loss_resolutions: [ + # All resolutions for reconstruction loss, ordered [num_fft, hop_length, window_length] + loss_resolutions: [ [32, 8, 32], [64, 16, 64], [128, 32, 128], [256, 64, 256], [512, 128, 512], [1024, 256, 1024], [2048, 512, 2048] ] + mel_loss_dims: [64, 64, 64, 64, 64, 64, 64] + mel_loss_log_guard: 1E-5 + stft_loss_log_guard: 1.0 train_ds: dataset: diff --git a/nemo/collections/tts/losses/audio_codec_loss.py b/nemo/collections/tts/losses/audio_codec_loss.py index 8819282f07bd..5a36d0378371 100644 --- a/nemo/collections/tts/losses/audio_codec_loss.py +++ b/nemo/collections/tts/losses/audio_codec_loss.py @@ -19,6 +19,7 @@ from einops import rearrange from nemo.collections.asr.parts.preprocessing.features import FilterbankFeatures +from nemo.collections.tts.parts.utils.helpers import get_mask_from_lengths, mask_sequence_tensor from nemo.core.classes import Loss, typecheck from nemo.core.neural_types import ( AudioSignal, @@ -109,14 +110,25 @@ def forward(self, audio_real, audio_gen, audio_len): class MultiResolutionMelLoss(Loss): - def __init__(self, sample_rate: int, mel_dim: int, resolutions: List[List], l1_scale: float = 1.0): + """ + Multi-resolution log mel spectrogram loss. + + Args: + sample_rate: Sample rate of audio. + resolutions: List of resolutions, each being 3 integers ordered [num_fft, hop_length, window_length] + mel_dims: Dimension of mel spectrogram to compute for each resolution. Should be same length as 'resolutions'. + log_guard: Value to add to mel spectrogram to avoid taking log of 0. + """ + + def __init__(self, sample_rate: int, resolutions: List[List], mel_dims: List[int], log_guard: float = 1.0): super(MultiResolutionMelLoss, self).__init__() + assert len(resolutions) == len(mel_dims) - self.l1_loss_fn = MaskedMAELoss(loss_scale=l1_scale) + self.l1_loss_fn = MaskedMAELoss() self.l2_loss_fn = MaskedMSELoss() self.mel_features = torch.nn.ModuleList() - for n_fft, hop_len, win_len in resolutions: + for mel_dim, (n_fft, hop_len, win_len) in zip(mel_dims, resolutions): mel_feature = FilterbankFeatures( sample_rate=sample_rate, nfilt=mel_dim, @@ -126,7 +138,7 @@ def __init__(self, sample_rate: int, mel_dim: int, resolutions: List[List], l1_s pad_to=1, mag_power=1.0, log_zero_guard_type="add", - log_zero_guard_value=1.0, + log_zero_guard_value=log_guard, mel_norm=None, normalize=None, preemph=None, @@ -146,20 +158,176 @@ def input_types(self): @property def output_types(self): return { - "loss": NeuralType(elements_type=LossType()), + "l1_loss": NeuralType(elements_type=LossType()), + "l2_loss": NeuralType(elements_type=LossType()), } @typecheck() def forward(self, audio_real, audio_gen, audio_len): - loss = 0.0 + l1_loss = 0.0 + l2_loss = 0.0 for mel_feature in self.mel_features: mel_real, mel_real_len = mel_feature(x=audio_real, seq_len=audio_len) mel_gen, _ = mel_feature(x=audio_gen, seq_len=audio_len) - loss += self.l1_loss_fn(predicted=mel_gen, target=mel_real, target_len=mel_real_len) - loss += self.l2_loss_fn(predicted=mel_gen, target=mel_real, target_len=mel_real_len) + l1_loss += self.l1_loss_fn(predicted=mel_gen, target=mel_real, target_len=mel_real_len) + l2_loss += self.l2_loss_fn(predicted=mel_gen, target=mel_real, target_len=mel_real_len) + + l1_loss /= len(self.mel_features) + l2_loss /= len(self.mel_features) + + return l1_loss, l2_loss + + +class STFTLoss(Loss): + """ + Log magnitude STFT loss. + + Args: + resolution: Resolution of spectrogram, a list of 3 numbers ordered [num_fft, hop_length, window_length] + log_guard: Value to add to magnitude spectrogram to avoid taking log of 0. + sqrt_guard: Value to add to when computing absolute value of STFT to avoid NaN loss. + """ + + def __init__(self, resolution: List[int], log_guard: float = 1.0, sqrt_guard: float = 1e-5): + super(STFTLoss, self).__init__() + self.loss_fn = MaskedMAELoss() + self.n_fft, self.hop_length, self.win_length = resolution + self.register_buffer("window", torch.hann_window(self.win_length, periodic=False)) + self.log_guard = log_guard + self.sqrt_guard = sqrt_guard + + def _compute_spectrogram(self, audio, spec_len): + # [B, n_fft, T_spec] + spec = torch.stft( + audio, + n_fft=self.n_fft, + hop_length=self.hop_length, + win_length=self.win_length, + window=self.window, + return_complex=True, + ) + # [B, n_fft, T_spec, 2] + spec = torch.view_as_real(spec) + # [B, n_fft, T_spec] + spec_mag = torch.sqrt(spec.pow(2).sum(-1) + self.sqrt_guard) + spec_log = torch.log(spec_mag + self.log_guard) + spec_log = mask_sequence_tensor(spec_log, spec_len) + return spec_log + + @property + def input_types(self): + return { + "audio_real": NeuralType(('B', 'T'), AudioSignal()), + "audio_gen": NeuralType(('B', 'T'), AudioSignal()), + "audio_len": NeuralType(tuple('B'), LengthsType()), + } - loss /= len(self.mel_features) + @property + def output_types(self): + return {"loss": NeuralType(elements_type=LossType())} + + @typecheck() + def forward(self, audio_real, audio_gen, audio_len): + spec_len = (audio_len // self.hop_length) + 1 + spec_real = self._compute_spectrogram(audio=audio_real, spec_len=spec_len) + spec_gen = self._compute_spectrogram(audio=audio_gen, spec_len=spec_len) + loss = self.loss_fn(predicted=spec_gen, target=spec_real, target_len=spec_len) + return loss + + +class MultiResolutionSTFTLoss(Loss): + """ + Multi-resolution log magnitude STFT loss. + + Args: + resolutions: List of resolutions, each being 3 integers ordered [num_fft, hop_length, window_length] + log_guard: Value to add to magnitude spectrogram to avoid taking log of 0. + sqrt_guard: Value to add to when computing absolute value of STFT to avoid NaN loss. + """ + + def __init__(self, resolutions: List[List], log_guard: float = 1.0, sqrt_guard: float = 1e-5): + super(MultiResolutionSTFTLoss, self).__init__() + self.loss_fns = torch.nn.ModuleList( + [STFTLoss(resolution=resolution, log_guard=log_guard, sqrt_guard=sqrt_guard) for resolution in resolutions] + ) + + @property + def input_types(self): + return { + "audio_real": NeuralType(('B', 'T'), AudioSignal()), + "audio_gen": NeuralType(('B', 'T'), AudioSignal()), + "audio_len": NeuralType(tuple('B'), LengthsType()), + } + @property + def output_types(self): + return {"loss": NeuralType(elements_type=LossType())} + + @typecheck() + def forward(self, audio_real, audio_gen, audio_len): + loss = 0.0 + for loss_fn in self.loss_fns: + loss += loss_fn(audio_real=audio_real, audio_gen=audio_gen, audio_len=audio_len) + loss /= len(self.loss_fns) + return loss + + +class SISDRLoss(Loss): + """ + SI-SDR loss based off of torchmetrics.functional.audio.sdr.scale_invariant_signal_distortion_ratio + with added support for masking. + """ + + def __init__(self, epsilon: float = 1e-8): + super(SISDRLoss, self).__init__() + self.epsilon = epsilon + + @property + def input_types(self): + return { + "audio_real": NeuralType(('B', 'T'), AudioSignal()), + "audio_gen": NeuralType(('B', 'T'), AudioSignal()), + "audio_len": NeuralType(tuple('B'), LengthsType()), + } + + @property + def output_types(self): + return {"loss": NeuralType(elements_type=LossType())} + + @typecheck() + def forward(self, audio_real, audio_gen, audio_len): + mask = get_mask_from_lengths(x=audio_real, lengths=audio_len) + audio_len = rearrange(audio_len, 'B -> B 1') + + # Shift audio to have zero-mean + # [B, 1] + target_mean = torch.sum(audio_real, dim=-1, keepdim=True) / audio_len + pred_mean = torch.sum(audio_gen, dim=-1, keepdim=True) / audio_len + + # [B, T] + target = audio_real - target_mean + target = target * mask + pred = audio_gen - pred_mean + pred = pred * mask + + # [B, 1] + ref_pred = torch.sum(pred * target, dim=-1, keepdim=True) + ref_target = torch.sum(target ** 2, dim=-1, keepdim=True) + alpha = (ref_pred + self.epsilon) / (ref_target + self.epsilon) + + # [B, T] + target_scaled = alpha * target + distortion = target_scaled - pred + + # [B] + target_scaled_power = torch.sum(target_scaled ** 2, dim=-1) + distortion_power = torch.sum(distortion ** 2, dim=-1) + + ratio = (target_scaled_power + self.epsilon) / (distortion_power + self.epsilon) + si_sdr = 10 * torch.log10(ratio) + + # [1] + loss = -torch.mean(si_sdr) return loss diff --git a/nemo/collections/tts/models/audio_codec.py b/nemo/collections/tts/models/audio_codec.py index 63140b77f2b5..9b6675db5979 100644 --- a/nemo/collections/tts/models/audio_codec.py +++ b/nemo/collections/tts/models/audio_codec.py @@ -25,7 +25,9 @@ from nemo.collections.tts.losses.audio_codec_loss import ( MultiResolutionMelLoss, + MultiResolutionSTFTLoss, RelativeFeatureMatchingLoss, + SISDRLoss, TimeDomainLoss, ) from nemo.collections.tts.modules.common import GaussianDropout @@ -85,26 +87,43 @@ def __init__(self, cfg: DictConfig, trainer: Trainer = None): # Discriminator setup self.discriminator = instantiate(cfg.discriminator) - # Loss setup - mel_loss_dim = cfg.get("mel_loss_dim", 64) - mel_loss_resolutions = cfg.mel_loss_resolutions - self.time_domain_loss_scale = cfg.get("time_domain_loss_scale", 1.0) - self.mel_loss_scale = cfg.get("mel_loss_scale", 1.0) - mel_loss_l1_scale = cfg.get("mel_loss_l1_scale", 1.0) - self.gen_loss_scale = cfg.get("gen_loss_scale", 1.0) - self.feature_loss_scale = cfg.get("feature_loss_scale", 1.0) - - self.time_domain_loss_fn = TimeDomainLoss() + # Mel loss setup + loss_resolutions = cfg.loss_resolutions + mel_loss_dims = cfg.get("mel_loss_dims") + mel_loss_log_guard = cfg.get("mel_loss_log_guard", 1.0) + self.mel_loss_l1_scale = cfg.get("mel_loss_l1_scale", 1.0) + self.mel_loss_l2_scale = cfg.get("mel_loss_l2_scale", 1.0) self.mel_loss_fn = MultiResolutionMelLoss( sample_rate=self.sample_rate, - mel_dim=mel_loss_dim, - resolutions=mel_loss_resolutions, - l1_scale=mel_loss_l1_scale, + mel_dims=mel_loss_dims, + resolutions=loss_resolutions, + log_guard=mel_loss_log_guard, ) + + # STFT loss setup + stft_loss_log_guard = cfg.get("stft_loss_log_guard", 1.0) + self.stft_loss_scale = cfg.get("stft_loss_scale", 0.0) + self.stft_loss_fn = MultiResolutionSTFTLoss(resolutions=loss_resolutions, log_guard=stft_loss_log_guard,) + + # Time domain loss setup + self.time_domain_loss_scale = cfg.get("time_domain_loss_scale", 1.0) + self.si_sdr_loss_scale = cfg.get("si_sdr_loss_scale", 0.0) + self.time_domain_loss_fn = TimeDomainLoss() + self.si_sdr_loss_fn = SISDRLoss() + + # Discriminator loss setup + self.gen_loss_scale = cfg.get("gen_loss_scale", 1.0) + self.feature_loss_scale = cfg.get("feature_loss_scale", 1.0) self.gen_loss_fn = instantiate(cfg.generator_loss) self.disc_loss_fn = instantiate(cfg.discriminator_loss) self.feature_loss_fn = RelativeFeatureMatchingLoss() + # Codebook loss setup + if self.vector_quantizer: + self.commit_loss_scale = cfg.get("commit_loss_scale", 1.0) + else: + self.commit_loss_scale = 0.0 + # Log setup self.log_config = cfg.get("log_config", None) @@ -336,10 +355,10 @@ def _process_batch(self, batch): if self.vector_quantizer: encoded, _, commit_loss = self.vector_quantizer(inputs=encoded, input_len=encoded_len) else: - commit_loss = None + commit_loss = 0.0 # [B, T] - audio_gen, audio_gen_len = self.audio_decoder(inputs=encoded, input_len=encoded_len) + audio_gen, _ = self.audio_decoder(inputs=encoded, input_len=encoded_len) return audio, audio_len, audio_gen, commit_loss @@ -361,37 +380,65 @@ def training_step(self, batch, batch_idx): audio, audio_len, audio_gen, commit_loss = self._process_batch(batch) + metrics = { + "global_step": self.global_step, + "lr": optim_gen.param_groups[0]['lr'], + } + if self.should_update_disc(batch_idx): # Train discriminator disc_scores_real, disc_scores_gen, _, _ = self.discriminator( audio_real=audio, audio_gen=audio_gen.detach() ) loss_disc = self.disc_loss_fn(disc_scores_real=disc_scores_real, disc_scores_gen=disc_scores_gen) - train_disc_loss = loss_disc + metrics["d_loss"] = loss_disc optim_disc.zero_grad() - self.manual_backward(train_disc_loss) + self.manual_backward(loss_disc) optim_disc.step() - else: - loss_disc = None - loss_time_domain = self.time_domain_loss_fn(audio_real=audio, audio_gen=audio_gen, audio_len=audio_len) - train_loss_time_domain = self.time_domain_loss_scale * loss_time_domain + generator_losses = [] + + loss_mel_l1, loss_mel_l2 = self.mel_loss_fn(audio_real=audio, audio_gen=audio_gen, audio_len=audio_len) + if self.mel_loss_l1_scale: + metrics["g_loss_mel_l1"] = loss_mel_l1 + generator_losses.append(self.mel_loss_l1_scale * loss_mel_l1) + if self.mel_loss_l2_scale: + metrics["g_loss_mel_l2"] = loss_mel_l2 + generator_losses.append(self.mel_loss_l2_scale * loss_mel_l2) + + if self.stft_loss_scale: + loss_stft = self.stft_loss_fn(audio_real=audio, audio_gen=audio_gen, audio_len=audio_len) + metrics["g_loss_stft"] = loss_stft + generator_losses.append(self.stft_loss_scale * loss_stft) - loss_mel = self.mel_loss_fn(audio_real=audio, audio_gen=audio_gen, audio_len=audio_len) - train_loss_mel = self.mel_loss_scale * loss_mel + if self.time_domain_loss_scale: + loss_time_domain = self.time_domain_loss_fn(audio_real=audio, audio_gen=audio_gen, audio_len=audio_len) + metrics["g_loss_time_domain"] = loss_time_domain + generator_losses.append(self.time_domain_loss_scale * loss_time_domain) + + if self.si_sdr_loss_scale: + loss_si_sdr = self.si_sdr_loss_fn(audio_real=audio, audio_gen=audio_gen, audio_len=audio_len) + metrics["g_loss_si_sdr"] = loss_si_sdr + generator_losses.append(self.si_sdr_loss_scale * loss_si_sdr) _, disc_scores_gen, fmaps_real, fmaps_gen = self.discriminator(audio_real=audio, audio_gen=audio_gen) - loss_gen = self.gen_loss_fn(disc_scores_gen=disc_scores_gen) - train_loss_gen = self.gen_loss_scale * loss_gen + if self.gen_loss_scale: + loss_gen = self.gen_loss_fn(disc_scores_gen=disc_scores_gen) + metrics["g_loss_gen"] = loss_gen + generator_losses.append(self.gen_loss_scale * loss_gen) + + if self.feature_loss_scale: + loss_feature = self.feature_loss_fn(fmaps_real=fmaps_real, fmaps_gen=fmaps_gen) + metrics["g_loss_feature"] = loss_feature + generator_losses.append(self.feature_loss_scale * loss_feature) - loss_feature = self.feature_loss_fn(fmaps_real=fmaps_real, fmaps_gen=fmaps_gen) - train_loss_feature = self.feature_loss_scale * loss_feature + if self.commit_loss_scale: + metrics["g_loss_commit"] = commit_loss + generator_losses.append(self.commit_loss_scale * commit_loss) - loss_gen_all = train_loss_time_domain + train_loss_mel + train_loss_gen + train_loss_feature - if commit_loss is not None: - loss_gen_all += commit_loss + loss_gen_all = sum(generator_losses) optim_gen.zero_grad() self.manual_backward(loss_gen_all) @@ -399,36 +446,30 @@ def training_step(self, batch, batch_idx): self.update_lr() - metrics = { - "g_loss_time_domain": loss_time_domain, - "g_loss_mel": loss_mel, - "g_loss_gen": loss_gen, - "g_loss_feature": loss_feature, - "g_loss": loss_gen_all, - "global_step": self.global_step, - "lr": optim_gen.param_groups[0]['lr'], - } - - if loss_disc is not None: - metrics["d_loss"] = loss_disc - - if commit_loss is not None: - metrics["g_loss_commit"] = commit_loss - self.log_dict(metrics, on_step=True, sync_dist=True) - self.log("t_loss", train_loss_mel, prog_bar=True, logger=False, sync_dist=True) + self.log("t_loss", loss_mel_l1, prog_bar=True, logger=False, sync_dist=True) def on_train_epoch_end(self): self.update_lr("epoch") def validation_step(self, batch, batch_idx): audio, audio_len, audio_gen, _ = self._process_batch(batch) + + loss_mel_l1, loss_mel_l2 = self.mel_loss_fn(audio_real=audio, audio_gen=audio_gen, audio_len=audio_len) + loss_stft = self.stft_loss_fn(audio_real=audio, audio_gen=audio_gen, audio_len=audio_len) loss_time_domain = self.time_domain_loss_fn(audio_real=audio, audio_gen=audio_gen, audio_len=audio_len) - loss_mel = self.mel_loss_fn(audio_real=audio, audio_gen=audio_gen, audio_len=audio_len) + loss_si_sdr = self.si_sdr_loss_fn(audio_real=audio, audio_gen=audio_gen, audio_len=audio_len) + + # Use only main reconstruction losses for val_loss + val_loss = loss_mel_l1 + loss_stft + loss_time_domain + metrics = { - "val_loss": loss_time_domain + loss_mel, + "val_loss": val_loss, + "val_loss_mel_l1": loss_mel_l1, + "val_loss_mel_l2": loss_mel_l2, + "val_loss_stft": loss_stft, "val_loss_time_domain": loss_time_domain, - "val_loss_mel": loss_mel, + "val_loss_si_sdr": loss_si_sdr, } self.log_dict(metrics, on_epoch=True, sync_dist=True) diff --git a/nemo/collections/tts/modules/encodec_modules.py b/nemo/collections/tts/modules/encodec_modules.py index b05187ccb74b..8c424351ce35 100644 --- a/nemo/collections/tts/modules/encodec_modules.py +++ b/nemo/collections/tts/modules/encodec_modules.py @@ -198,6 +198,7 @@ def output_types(self): def remove_weight_norm(self): self.pre_conv.remove_weight_norm() + self.post_conv.remove_weight_norm() for res_block in self.res_blocks: res_block.remove_weight_norm() for down_sample_conv in self.down_sample_conv_layers: @@ -544,8 +545,8 @@ def __init__( codebook_size: int, codebook_dim: int, decay: float = 0.99, - threshold_ema_dead_code: Optional[int] = 2, - kmeans_iters: Optional[int] = None, + threshold_ema_dead_code: Optional[float] = 2.0, + kmeans_iters: Optional[int] = 50, ): super().__init__() self.decay = decay @@ -686,7 +687,6 @@ class ResidualVectorQuantizer(NeuralModule): Args: num_codebooks: Number of codebooks to use. - commit_loss_scale: Loss scale for codebook commit loss. codebook_size: Number of codes to use for each codebook. codebook_dim: Dimension of each code. decay: Decay for exponential moving average over the codebooks. @@ -700,20 +700,15 @@ class ResidualVectorQuantizer(NeuralModule): def __init__( self, num_codebooks: int, - commit_loss_scale: float = 1.0, codebook_size: int = 1024, codebook_dim: int = 128, decay: float = 0.99, - threshold_ema_dead_code: Optional[int] = 2, + threshold_ema_dead_code: Optional[float] = 2.0, kmeans_iters: Optional[int] = 50, ): super().__init__() self.codebook_dim = codebook_dim - - if commit_loss_scale: - self.commit_loss_fn = MaskedMSELoss(loss_scale=commit_loss_scale) - else: - self.commit_loss_fn = None + self.commit_loss_fn = MaskedMSELoss() self.codebooks = nn.ModuleList( [ @@ -728,16 +723,6 @@ def __init__( ] ) - def _commit_loss(self, input, target, input_len): - if not self.commit_loss_fn: - return 0.0 - - return self.commit_loss_fn( - predicted=rearrange(input, "B T D -> B D T"), - target=rearrange(target, "B T D -> B D T"), - target_len=input_len, - ) - @property def input_types(self): return { @@ -764,13 +749,17 @@ def forward(self, inputs: Tensor, input_len: Tensor) -> Tuple[Tensor, Tensor, fl dequantized_i, indices_i = codebook(inputs=residual, input_len=input_len) if self.training: - dequantized_i = residual + (dequantized_i - residual).detach() dequantized_i_const = dequantized_i.detach() - commit_loss_i = self._commit_loss(input=residual, target=dequantized_i_const, input_len=input_len) + + commit_loss_i = self.commit_loss_fn( + predicted=rearrange(residual, "B T D -> B D T"), + target=rearrange(dequantized_i_const, "B T D -> B D T"), + target_len=input_len, + ) commit_loss = commit_loss + commit_loss_i residual = residual - dequantized_i_const - + dequantized_i = residual + (dequantized_i - residual).detach() else: residual = residual - dequantized_i diff --git a/nemo/collections/tts/parts/utils/helpers.py b/nemo/collections/tts/parts/utils/helpers.py index 72048882fe78..08d31390107b 100644 --- a/nemo/collections/tts/parts/utils/helpers.py +++ b/nemo/collections/tts/parts/utils/helpers.py @@ -141,7 +141,7 @@ def get_mask_from_lengths(lengths: Optional[torch.Tensor] = None, x: Optional[to lengths: Optional[torch.tensor] (torch.tensor): 1D tensor with lengths x: Optional[torch.tensor] = tensor to be used on, last dimension is for mask Returns: - mask (torch.tensor): num_sequences x max_length x 1 binary tensor + mask (torch.tensor): num_sequences x max_length binary tensor """ if lengths is None: assert x is not None diff --git a/tests/collections/tts/losses/test_audio_codec_loss.py b/tests/collections/tts/losses/test_audio_codec_loss.py index 0fe7991e92cb..60ea8d293655 100644 --- a/tests/collections/tts/losses/test_audio_codec_loss.py +++ b/tests/collections/tts/losses/test_audio_codec_loss.py @@ -14,8 +14,10 @@ import pytest import torch +from torchmetrics import ScaleInvariantSignalDistortionRatio -from nemo.collections.tts.losses.audio_codec_loss import MaskedMAELoss, MaskedMSELoss +from nemo.collections.tts.losses.audio_codec_loss import MaskedMAELoss, MaskedMSELoss, SISDRLoss +from nemo.collections.tts.parts.utils.helpers import mask_sequence_tensor class TestAudioCodecLoss: @@ -42,3 +44,47 @@ def test_masked_loss_l2(self): loss = loss_fn(predicted=predicted, target=target, target_len=target_len) assert loss == (4 / 3) + + @pytest.mark.run_only_on('CPU') + @pytest.mark.unit + def test_si_sdr_loss(self): + loss_fn = SISDRLoss() + sdr_fn = ScaleInvariantSignalDistortionRatio(zero_mean=True) + + num_samples = 1000 + torch.manual_seed(100) + target = torch.rand([1, num_samples]) + predicted = torch.rand([1, num_samples]) + target_len = torch.tensor([num_samples, num_samples]) + + torch_si_sdr = sdr_fn(preds=predicted, target=target) + loss = loss_fn(audio_real=target, audio_gen=predicted, audio_len=target_len) + si_sdr = -loss + + torch.testing.assert_close(actual=si_sdr, expected=torch_si_sdr) + + @pytest.mark.run_only_on('CPU') + @pytest.mark.unit + def test_si_sdr_loss_batch(self): + loss_fn = SISDRLoss() + si_sdr_fn = ScaleInvariantSignalDistortionRatio(zero_mean=True) + + batch_size = 3 + num_samples = 1000 + torch.manual_seed(100) + target = torch.rand([batch_size, num_samples]) + predicted = torch.rand([batch_size, num_samples]) + + target_len = torch.tensor([500, 250, 900]) + target = mask_sequence_tensor(target, lengths=target_len) + predicted = mask_sequence_tensor(predicted, lengths=target_len) + + torch_si_sdr = 0.0 + for i in range(batch_size): + torch_si_sdr += si_sdr_fn(preds=predicted[i, : target_len[i]], target=target[i, : target_len[i]]) + torch_si_sdr /= batch_size + + loss = loss_fn(audio_real=target, audio_gen=predicted, audio_len=target_len) + si_sdr = -loss + + torch.testing.assert_close(actual=si_sdr, expected=torch_si_sdr) diff --git a/tests/collections/tts/modules/test_audio_codec_modules.py b/tests/collections/tts/modules/test_audio_codec_modules.py index 4650a6508edd..b48b415547fe 100644 --- a/tests/collections/tts/modules/test_audio_codec_modules.py +++ b/tests/collections/tts/modules/test_audio_codec_modules.py @@ -15,12 +15,7 @@ import pytest import torch -from nemo.collections.tts.modules.audio_codec_modules import ( - Conv1dNorm, - ConvTranspose1dNorm, - get_down_sample_padding, - get_up_sample_padding, -) +from nemo.collections.tts.modules.audio_codec_modules import Conv1dNorm, ConvTranspose1dNorm, get_down_sample_padding class TestAudioCodecModules: From 03b284631541cda75496b127b3be7c1ecdecc149 Mon Sep 17 00:00:00 2001 From: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Date: Sat, 7 Oct 2023 05:51:11 +0400 Subject: [PATCH 107/112] Create per.py (#7538) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move model precision copy (#7336) * move cfg precision set to megatron base model Signed-off-by: Maanu Grover * remove copy from other models Signed-off-by: Maanu Grover * modify attribute not arg Signed-off-by: Maanu Grover * fix gpt model test for ptl 2.0 Signed-off-by: Maanu Grover * rename function and add docstring Signed-off-by: Maanu Grover * replace precision to dtype conditionals with func call Signed-off-by: Maanu Grover * unnecessary function and cfg reset Signed-off-by: Maanu Grover * set default value Signed-off-by: Maanu Grover * fix precision lookup in a few more places Signed-off-by: Maanu Grover * rename mapping function Signed-off-by: Maanu Grover * ununsed import Signed-off-by: Maanu Grover * save torch datatype to model Signed-off-by: Maanu Grover * set weights precision wrt amp o2 Signed-off-by: Maanu Grover * Revert "set weights precision wrt amp o2" This reverts commit 313a4bfe5eb69d771a6d2433898c0685836aef5c. Signed-off-by: Maanu Grover * revert half precision at inference attempt Signed-off-by: Maanu Grover * move autocast dtype to base model Signed-off-by: Maanu Grover * move params dtype to base model, enable fp16 O2 inf Signed-off-by: Maanu Grover * unused imports Signed-off-by: Maanu Grover --------- Signed-off-by: Maanu Grover Signed-off-by: Sasha Meister * Fix PEFT checkpoint loading (#7388) * Fix PEFT checkpoint loading Signed-off-by: Jason Wang * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Jason Wang Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * Use distributed optimizer support for multiple dtypes (#7359) * Update distopt wrapper with multiple dtype support Remove manual handling of separate FP32 optimizer. Signed-off-by: Tim Moon * Use distopt support for contiguous buffers with multiple dtypes Signed-off-by: Tim Moon * Fix typo Signed-off-by: Tim Moon * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Separate distopt buckets for first GPT layer and non-overlapped params Signed-off-by: Tim Moon * Add distopt logic for int dtypes Signed-off-by: Tim Moon * Update Apex commit Signed-off-by: Tim Moon * Remove unused variables Signed-off-by: Tim Moon * Update Apex commit in README and Jenkensfile Signed-off-by: Tim Moon * Debug Dockerfile and Jenkinsfile Signed-off-by: Tim Moon --------- Signed-off-by: Tim Moon Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Eric Harper Signed-off-by: Sasha Meister * minor fix for llama ckpt conversion script (#7387) * minor fix for llama ckpt conversion script Signed-off-by: Jason Wang * Update Jenkinsfile Signed-off-by: Jason Wang * remove fast_swiglu configuration Signed-off-by: Jason Wang --------- Signed-off-by: Jason Wang Co-authored-by: Eric Harper Signed-off-by: Sasha Meister * Fix wrong calling of librosa.get_duration() in notebook (#7376) Signed-off-by: Robin Dong Co-authored-by: Somshubra Majumdar Signed-off-by: Sasha Meister * [PATCH] PEFT import mcore (#7393) * [PATCH] PEFT import mcore Signed-off-by: Jason Wang * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Jason Wang Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * Create per.py Script for calculation Punctuation Error Rate and related rates (correct rate, deletions rate, etc.) Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * [TTS] Added a callback for logging initial data (#7384) Signed-off-by: Ante Jukić Signed-off-by: Sasha Meister * Update Core Commit (#7402) * Update Core Commit Signed-off-by: Abhinav Khattar * update commit Signed-off-by: Abhinav Khattar --------- Signed-off-by: Abhinav Khattar Signed-off-by: Sasha Meister * Use cfg attribute in bert (#7394) * use cfg attribute instead of arg Signed-off-by: Maanu Grover * use torch_dtype in place of cfg.precision Signed-off-by: Maanu Grover * move precision copy before super constructor Signed-off-by: Maanu Grover * use trainer arg Signed-off-by: Maanu Grover --------- Signed-off-by: Maanu Grover Signed-off-by: Sasha Meister * Add support for bias conversion in Swiglu models (#7386) * Add support for bias conversion in Swiglu models Signed-off-by: smajumdar * Add support for auto extracting tokenizer model Signed-off-by: smajumdar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add support for auto extracting tokenizer model Signed-off-by: smajumdar * Fix issue with missing tokenizer Signed-off-by: smajumdar * Refactor Signed-off-by: smajumdar * Refactor Signed-off-by: smajumdar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: smajumdar Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * Update save_to and restore_from for dist checkpointing (#7343) * add dist ckpt to save to, in progress Signed-off-by: eharper * move dist ckpt Signed-off-by: eharper * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * clean up Signed-off-by: eharper * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update restore from, need to figure out how to initialize distributed Signed-off-by: eharper * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * launch distrib if needed when restoring dist ckpt Signed-off-by: eharper * when using mcore we can change tp pp on the fly Signed-off-by: eharper * add load_from_checkpoint support for dist ckpt Signed-off-by: eharper * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update llama convert script to save dist .nemo Signed-off-by: eharper * fix load dist ckpt Signed-off-by: jasonwan * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * setup TE TP groups if needed Signed-off-by: eharper * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * setup te tp groups if needed Signed-off-by: eharper * remove import Signed-off-by: eharper --------- Signed-off-by: eharper Signed-off-by: jasonwan Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: jasonwan Signed-off-by: Sasha Meister * fix forward for with mcore=false (#7403) Signed-off-by: Jimmy Zhang Co-authored-by: Jimmy Zhang Signed-off-by: Sasha Meister * Fix logging to remove 's/it' from progress bar in Megatron models and add train_step_timing (#7374) * Add CustomProgressBar class to exp_manager and trainer callbacks Signed-off-by: Abhishree * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix the progress bar to reflect total microbatch cnt Signed-off-by: Abhishree * Modify CustomProgressBar class 1) Modify CustomProgressBar class to update progress bar per global_step instead of per microbatch 2) Add the callback to other megatron training/finetuning files that are not using MegatronTrainerBuilder Signed-off-by: Abhishree * Add CustomProgressBar callback to tuning files Signed-off-by: Abhishree * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Abhishree Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * Set Activation Checkpointing Defaults (#7404) * Set Activation Checkpointing Defaults Signed-off-by: Abhinav Khattar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * check for None Signed-off-by: Abhinav Khattar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Abhinav Khattar Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * make loss mask default to false (#7407) Signed-off-by: eharper Signed-off-by: Sasha Meister * Add dummy userbuffer config files (#7408) Signed-off-by: Sangkug Lym Signed-off-by: Sasha Meister * add missing ubconf files (#7412) Signed-off-by: Abhinav Khattar Signed-off-by: Sasha Meister * New tutorial on Speech Data Explorer (#7405) * Added Google Colab based tutorial on Speech Data Explorer Signed-off-by: George Zelenfroynd Signed-off-by: Sasha Meister * Update ptl training ckpt conversion script to work with dist ckpt (#7416) * update ptl convert script Signed-off-by: eharper * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * don't break legacy Signed-off-by: eharper * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: eharper Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * Allow disabling sanity checking when num_sanity_val_steps=0 (#7413) * Allow disabling sanity checking when num_sanity_val_steps=0 Signed-off-by: Abhishree * Update num_sanity_val_steps to be a multiple of num_microbatches Signed-off-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Abhishree Signed-off-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * Add comprehensive error messages (#7261) Signed-off-by: Anton Peganov Signed-off-by: Sasha Meister * check NEMO_PATH (#7418) Signed-off-by: Nikolay Karpov Signed-off-by: Sasha Meister * layer selection for ia3 (#7417) * layer selection for ia3 Signed-off-by: arendu * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: arendu Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * Fix missing pip package 'einops' (#7397) Signed-off-by: Robin Dong Signed-off-by: Sasha Meister * Fix failure of pyaudio in Google Colab (#7396) Signed-off-by: Robin Dong Signed-off-by: Sasha Meister * Update README.md: output_path --> output_manifest_filepath (#7442) Signed-off-by: Samuele Cornell Signed-off-by: Sasha Meister * Add rope dynamic linear scaling (#7437) * Add dynamic linear scaling Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix bug Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix Signed-off-by: Cheng-Ping Hsieh --------- Signed-off-by: Cheng-Ping Hsieh Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Yang Zhang Signed-off-by: Sasha Meister * Fix None dataloader issue in PTL2.0 (#7455) * Fix None dataloader issue in PTL2.0 Signed-off-by: KunalDhawan * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updating values of self._validation_dl and self._test_dl as well Signed-off-by: KunalDhawan * updating values of self._validation_dl and self._test_dl as well Signed-off-by: KunalDhawan * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: KunalDhawan Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * [ASR] Confidence measure -> method renames (#7434) * measure -> method Signed-off-by: Aleksandr Laptev * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Aleksandr Laptev Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * Add steps for document of getting dataset 'SF Bilingual Speech' (#7378) * Add steps for document of getting dataset 'SF Bilingual Speech' Signed-off-by: Robin Dong * Update datasets.rst added a link from a tutorial demonstrating detailed data prep steps. Signed-off-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> --------- Signed-off-by: Robin Dong Signed-off-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Signed-off-by: Sasha Meister * RNN-T confidence and alignment bugfix (#7381) * new frame_confidence and alignments lists are now always created after the while loop Signed-off-by: Aleksandr Laptev * tests added Signed-off-by: Aleksandr Laptev --------- Signed-off-by: Aleksandr Laptev Signed-off-by: Sasha Meister * Fix resume from checkpoint in exp_manager (#7424) (#7426) Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Co-authored-by: Eric Harper Signed-off-by: Sasha Meister * Fix checking of cuda/cpu device for inputs of Decoder (#7444) * Fix checking of cuda/cpu device for inputs of Decoder Signed-off-by: Robin Dong * Update tacotron2.py Signed-off-by: Jason --------- Signed-off-by: Robin Dong Signed-off-by: Jason Co-authored-by: Jason Signed-off-by: Sasha Meister * Fix failure of ljspeech's get_data.py (#7430) * Fix failure of ljspeech's get_data.py Signed-off-by: Robin Dong * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Robin Dong Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * [TTS] Fix audio codec type checks (#7373) * [TTS] Fix audio codec type checks Signed-off-by: Ryan * [TTS] Fix audio codec tests Signed-off-by: Ryan --------- Signed-off-by: Ryan Signed-off-by: Sasha Meister * [TTS] Add dataset to path of logged artifacts (#7462) * [TTS] Add dataset to path of logged artifacts Signed-off-by: Ryan * [TTS] Revert axis name back to Audio Frames Signed-off-by: Ryan --------- Signed-off-by: Ryan Signed-off-by: Sasha Meister * Fix sft dataset truncation (#7464) * Add fix Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix Signed-off-by: Cheng-Ping Hsieh --------- Signed-off-by: Cheng-Ping Hsieh Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * Automatic Lip Reading Recognition (ALR) - ASR/CV (Visual ASR) (#7330) * striding_conv1d_k5 and dw_striding_conv1d_k5 subsampling Signed-off-by: mburchi * transpose conv1d inputs Signed-off-by: mburchi * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: mburchi * Update subsampling.py change striding_conv1d_k5 to striding_conv1d Signed-off-by: Maxime Burchi <60737204+burchim@users.noreply.github.com> * cv branch Signed-off-by: mburchi * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * video manifest Signed-off-by: mburchi * add collection classes Signed-off-by: mburchi * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add test_step_outputs Signed-off-by: mburchi * correct manifest bug when having only audio or only videos Signed-off-by: mburchi * correct manifest bug when having only audio or only videos Signed-off-by: mburchi * clean references Signed-off-by: mburchi * freeze unfreeze transcribe cv models Signed-off-by: mburchi * correct manifest get_full_path bug Signed-off-by: mburchi * update for PR Signed-off-by: mburchi * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * guard torchvision Signed-off-by: mburchi * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update nemo/collections/cv/data/video_to_text_dataset.py Co-authored-by: Igor Gitman Signed-off-by: Maxime Burchi <60737204+burchim@users.noreply.github.com> * _video_speech_collate_fn in cv/data/video_to_text.py Signed-off-by: mburchi * add self.out = None to asr subsampling Signed-off-by: mburchi * Update nemo/collections/cv/data/video_to_text_dataset.py Co-authored-by: Igor Gitman Signed-off-by: Maxime Burchi <60737204+burchim@users.noreply.github.com> * cv -> multimodal/speech_cv branch Signed-off-by: mburchi * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: mburchi Signed-off-by: Maxime Burchi <60737204+burchim@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Igor Gitman Signed-off-by: Sasha Meister * HF StarCoder to NeMo conversion script (#7421) * Script to convert HF StarCoder checkpoint to NeMo Signed-off-by: Jan Lasek * StarCoder conversion test Signed-off-by: Jan Lasek * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Jan Lasek * Fix test Signed-off-by: Jan Lasek * Catch up with save_to changes Signed-off-by: Jan Lasek * Don't abbreviate args for clarity Signed-off-by: Jan Lasek * Configurable precision: BF16 vs FP32 Signed-off-by: Jan Lasek * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Jan Lasek Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * fix bug when loading dist ckpt in peft (#7452) Signed-off-by: Hongbin Liu Co-authored-by: Hongbin Liu Signed-off-by: Sasha Meister * Fix adding positional embeddings in-place in transformer module (#7440) Signed-off-by: Tamerlan Tabolov Co-authored-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Signed-off-by: Sasha Meister * Fix (#7478) Signed-off-by: Cheng-Ping Hsieh Signed-off-by: Sasha Meister * add sleep (#7498) (#7499) * add sleep * add sleep onto config instead * add comment --------- Signed-off-by: Gerald Shen Co-authored-by: Gerald Shen <119401249+gshennvm@users.noreply.github.com> Signed-off-by: Sasha Meister * Fix exp manager check for sleep (#7503) (#7504) Signed-off-by: smajumdar Co-authored-by: Somshubra Majumdar Signed-off-by: Sasha Meister * bugfix: trainer.accelerator=auto from None. (#7492) (#7493) Signed-off-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Co-authored-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Signed-off-by: Sasha Meister * [doc] fix broken link (#7481) Signed-off-by: Stas Bekman Signed-off-by: Sasha Meister * [TTS] Read audio as int32 to avoid flac read errors (#7477) * [TTS] Read audio as int32 to avoid flac read errors Signed-off-by: Ryan * [TTS] Add comment about read failures Signed-off-by: Ryan --------- Signed-off-by: Ryan Signed-off-by: Sasha Meister * Add dataset 'AISHELL-3' from OpenSLR for training mandarin TTS (#7409) * Add dataset 'AISHELL-3' from OpenSLR for training mandarin TTS * Train 'AISHELL-3' dataset with multi-speakers Signed-off-by: Robin Dong * Update get_data.py update copyright header Signed-off-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> * Update get_data.py added a disclaimer Signed-off-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add new configuration file for AISHELL3 with multispeaker of fastpitch Signed-off-by: Robin Dong --------- Signed-off-by: Robin Dong Signed-off-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Signed-off-by: Sasha Meister * dllogger - log on rank 0 only (#7513) Signed-off-by: Stas Bekman Signed-off-by: Sasha Meister * Fix TTS FastPitch tutorial (#7494) (#7516) * Fix --------- Signed-off-by: Cheng-Ping Hsieh Co-authored-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Signed-off-by: Sasha Meister * Fix get_dist() tensor dimension (#7506) (#7515) Signed-off-by: Jocelyn Huang Co-authored-by: Jocelyn Signed-off-by: Sasha Meister * bugfix: specify trainer.strategy=auto when devices=1 (#7509) (#7512) Signed-off-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Co-authored-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Signed-off-by: Sasha Meister * fix (#7511) Signed-off-by: Abhinav Khattar Signed-off-by: Sasha Meister * [TTS] Fix FastPitch data prep tutorial (#7524) Signed-off-by: Ryan Signed-off-by: Sasha Meister * add italian tokenization (#7486) * add italian tokenization Signed-off-by: GiacomoLeoneMaria * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add more ipa lexicon it Signed-off-by: GiacomoLeoneMaria * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix error deletion Signed-off-by: GiacomoLeoneMaria * add test Signed-off-by: GiacomoLeoneMaria * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: GiacomoLeoneMaria Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * Replace None strategy with auto in tutorial notebooks (#7521) (#7527) Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Sasha Meister * unpin setuptools (#7534) (#7535) Signed-off-by: fayejf <36722593+fayejf@users.noreply.github.com> Co-authored-by: fayejf <36722593+fayejf@users.noreply.github.com> Signed-off-by: Sasha Meister * Update per.py - if __name__ == "__main__" removed (now metric can be imported); - removed excessive classes (like "Sample" and "Statistics"); - transition from pandas df to dict of dicts; - removed unnecessary "return"; - notation fixing; - reduced calculation time Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * Create punctuation_rates.py Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * Format fixing Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * added nemo.logging, header, docstrings, how to use Signed-off-by: Sasha Meister * Added asserions to rate_punctuation.py Signed-off-by: Sasha Meister * fix typo Signed-off-by: Sasha Meister * added function for import and call, docstrings Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * remove auto generated examples (#7510) * explicitly remove autogenerated examples for data parallel evaluation Signed-off-by: arendu * mark autogenrated and remove it for test Signed-off-by: arendu * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: arendu Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * Add the `strategy` argument to `MegatronGPTModel.generate()` (#7264) It is passed as an explicit argument rather than through `**strategy_args` so as to ensure someone cannot accidentally pass other arguments that would end up being ignored. It is a keyword-only argument to ensure that if in the future we want to update the signature to `**strategy_args`, we can do it without breaking code. Signed-off-by: Olivier Delalleau <507137+odelalleau@users.noreply.github.com> Signed-off-by: Sasha Meister * Fix PTL2.0 related ASR bugs in r1.21.0: Val metrics logging, None dataloader issue (#7531) (#7533) * fix none dataloader issue ptl2 * ptl2.0 logging fixes for rnnt_models --------- Signed-off-by: KunalDhawan Co-authored-by: Kunal Dhawan Co-authored-by: Nithin Rao Signed-off-by: Sasha Meister * gpus -> devices (#7542) (#7545) Signed-off-by: Nithin Rao Koluguri Co-authored-by: Nithin Rao Signed-off-by: Sasha Meister * Update FFMPEG version to fix issue with torchaudio (#7551) (#7553) Signed-off-by: smajumdar Co-authored-by: Somshubra Majumdar Signed-off-by: Sasha Meister * PEFT GPT & T5 Refactor (#7308) * initial implementation of add_adapters API * correct type hint * Add config in add_adapters for save and load (@author bobchen) * Remove AdapterConfig to avoid import error * Add AdaterConfig back and move adaptermixin to sft model * Add NLPSaveRestoreConnector as default in NLPModel.restore_from * Add restore_from_nemo_with_adapter and test script * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * rename t5 file and classes to be consistent with GPT * add t5 sft dataset * add support for single-file format with T5SFTDataset * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Various small changes to make T5 SFT work like GPT SFT * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add adapter evaluation test script * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add MultiAdaterConfig for ia3 and fix builder issue * Make ptuning for T5SFTModel work using mixin * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add IA3_Adapter for AdapterName * Add adapter name for ptuning and attention adapter * Make test script GPT/T5 agnostic * Add layer selection feature * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Integrate adapter name and config * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update gpt peft tuning script to new API * add t5 peft tuning script with new API * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix IA3 layer selection issue * Override state_dict on SFT model instead of mixin * Add load adapter by adapter config * move peft config map away from example script * auto get config from nemo adapter * Move PEFTConfig to new file * fix ckpt save/load for t5 * name change: add_adapters -> add_adapter * variable name change * update t5 script * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix t5 issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add weight tying * update gpt tuning script * PEFT-API proposal * Fix according to comments * update tuning scripts * move merge_cfg_with to mixin class since it applies to both gpt and t5 and requires the model class for restore * Add mcore_gpt support for NLPAdapterMixin * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix typo * variable name change to distinguish "peft" and "adapter" * override `load_adapters` to support `add_adapter` name change * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update tuning and eval script for adapter save/load * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add Ptuning on first stage only * add lora tutorial for review * Fix layer selection for mcore * add landing page * fix resume training Signed-off-by: jasonwan * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add mcore condition in sharded_state_dict to make sft work * Update lora_tutorial.md First edit of this file for PEFT documentation for NeMO Signed-off-by: hkelly33 <58792115+hkelly33@users.noreply.github.com> * rename Adapter to AttentionAdapter to avoid confusion in doc * Change load_adapters to load .nemo * add quick start guide * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add load_adapters with .ckpt * Remove setup_complete changes in load_adapters * update landing page * remove typo * Updated quick_start.md per Chen Cui Signed-off-by: hkelly33 <58792115+hkelly33@users.noreply.github.com> * Add inference config merger and tutorial * Add doc string for NLPAdapterModelMixin and deprecated warning on MegatronGPTPEFTModel * add supported_methods.md and update other documentations * Update supported_methods.md minor updates. Signed-off-by: Adi Renduchintala * Update landing_page.md minor update. Signed-off-by: Adi Renduchintala * Modify doc string for NLPAdapterModelMixin * Add doc string add_adapters in NLPAdapterModelMixin * rename canonical adapters * remove mcore hard dependency * [PATCH] move microbatch calculator to nemo from apex * remove apex dependency in gpt and t5 sft models * remove apex dependency in gpt model * render doc strings * fix * Add missing virtual_tokens on ptuning * fix docstrings * update gpt-style model coverage in docs * update docstring * Remove pdb * add lightning_fabric to make docstring rendering work * Add Ptuning missing key * try docstring rendering * Fix ptuning issue * update gpt t5 peft tuning and eval scripts * typos * update eval config * fix bug relating to apex dependency removal * typo * make predict step behave the same as test step * make lora tutorial work in notebook * cosmetics * update yaml scripts * mcore_gpt attribute optional * typo * update eval scripts and fix T5 eval bugs * add NLPDDPStrategyNotebook and trainer builder logic to use it * update lora notebook to use new trainer builder * fix microbatch calculator bug for inference after training * Convert markdown files to RST and incorporate with doc * typo * revise language * remove extra cell * remove unnecessary inheritance * remove old tests * move layer selection default so logging messages make sense * remove `save_adapters` as adapter weights are saved automatically during training * initialize weights from a checkpoint instead of randomly * multiple fields can form a context (#7147) * list of context fields and flexible prompt template Signed-off-by: arendu * list of fields for context Signed-off-by: arendu * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix bug Signed-off-by: Cheng-Ping Hsieh * Fix bug Signed-off-by: Cheng-Ping Hsieh * Add multiple truncation fields and middle truncation Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Compatible to old ckpt Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix tokenize detokenize issue Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove detokenization, add truncation augmentation Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Resolve comments Signed-off-by: Cheng-Ping Hsieh * Remove unused import Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * revert eos Signed-off-by: Cheng-Ping Hsieh * Add tokenizer space_sensitive attribute Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix error Signed-off-by: Cheng-Ping Hsieh * Fix erorr and use re Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix bug Signed-off-by: Cheng-Ping Hsieh * Change assert logic Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Follow adi suggestion Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove merge function Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add example and comment Signed-off-by: Cheng-Ping Hsieh * Remove context_key and add comment Signed-off-by: Cheng-Ping Hsieh * Remove random truncation Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix bug Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix template none Signed-off-by: Cheng-Ping Hsieh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix bug Signed-off-by: Cheng-Ping Hsieh --------- Signed-off-by: arendu Signed-off-by: Cheng-Ping Hsieh Signed-off-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Cheng-Ping Hsieh Co-authored-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> * revert config changes * remove accidental breakpoint * support TP>1 loading * infer adapter type from checkpoint in during eval * breakup add adapter * enable interpolation of train_ds and validation_ds * update metric calc script to conform to single-file eval format * remove extraneous print * update lora notebook for updated merge_inference_cfg * Update nlp_adapter_mixins.py variable name change Signed-off-by: Chen Cui * turn off grad scaler for PP to match old scripts * remove PEFTSaveRestoreConnector since functionality all covered by the new mixin class * remove resume_from_checkpoint check since covered in #7335 * revert changes made in eval config interpolation * more interpolation * typo * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove dup line Signed-off-by: Chen Cui * code style warnings Signed-off-by: Chen Cui * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix config mistake Signed-off-by: Chen Cui * add copyright header Signed-off-by: Chen Cui * fix code check warnings Signed-off-by: Chen Cui * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * revert changes to remove apex dependency (mixed apex+nemo microbatch calculator broke some CI tests) Signed-off-by: Chen Cui * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add more deprecation notices Signed-off-by: Chen Cui * update deprecation notices Signed-off-by: Chen Cui * update deprecation notices Signed-off-by: Chen Cui * consolidate peft and sft scripts Signed-off-by: Chen Cui * update CI tests Signed-off-by: Chen Cui * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * notebook branch points to main to prepare for merge Signed-off-by: Chen Cui * fix gpt and t5 validation with any metric other than loss Signed-off-by: Chen Cui * support pre-extracted checkpoints Signed-off-by: Chen Cui --------- Signed-off-by: jasonwan Signed-off-by: hkelly33 <58792115+hkelly33@users.noreply.github.com> Signed-off-by: Adi Renduchintala Signed-off-by: arendu Signed-off-by: Cheng-Ping Hsieh Signed-off-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Signed-off-by: Chen Cui Co-authored-by: Chen Cui Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Marc Romeyn Co-authored-by: jasonwan Co-authored-by: hkelly33 <58792115+hkelly33@users.noreply.github.com> Co-authored-by: Adi Renduchintala Co-authored-by: Yuanzhe Dong Co-authored-by: Cheng-Ping Hsieh Co-authored-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Signed-off-by: Sasha Meister * fix a typo (#7496) Signed-off-by: BestJuly Signed-off-by: Sasha Meister * [TTS] remove curly braces from ${BRANCH} in jupyer notebook cell. (#7554) (#7560) * remove curly braces. * remove installation of pynini. --------- Signed-off-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Co-authored-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Signed-off-by: Sasha Meister * add youtube embed url (#7570) Signed-off-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Co-authored-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Signed-off-by: Sasha Meister * Remap speakers to continuous range of speaker_id for dataset AISHELL3 (#7536) * Remap speakers to continuous range of speaker_id for dataset AISHELL3 * Add new key/value pair to record raw speaker for AISHELL3 dataset Signed-off-by: Robin Dong --------- Signed-off-by: Robin Dong Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * fix validation_step_outputs initialization for multi-dataloader (#7546) (#7572) * added correct validation_step_outputs initialization for mutli-dataloader * changed kernel for display * Update logic for validation and test step outputs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * revert multidataloader changes in multilang ASR notebook --------- Signed-off-by: KunalDhawan Signed-off-by: smajumdar Co-authored-by: Kunal Dhawan Co-authored-by: Somshubra Majumdar Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * Append output of val step to self.validation_step_outputs (#7530) (#7532) Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Sasha Meister * [TTS] fixed trainer's accelerator and strategy. (#7569) (#7574) Signed-off-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Co-authored-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Signed-off-by: Sasha Meister * Append val/test output to instance variable in EncDecSpeakerLabelModel (#7562) (#7573) * Append val/test output to the instance variable in EncDecSpeakerLabelModel * Handle test case in evaluation_step * Replace type with isinstance --------- Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Sasha Meister * Fix CustomProgressBar for resume (#7427) (#7522) * Fix CustomProgress Bar for resume and multiple epochs * Edit num_training_batches * Use max_steps as total for progress bar for resume * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * fix typos in nfa and speech enhancement tutorials (#7580) (#7583) Signed-off-by: Elena Rastorgueva Co-authored-by: Elena Rastorgueva <80532067+erastorgueva-nv@users.noreply.github.com> Signed-off-by: Sasha Meister * Add strategy as ddp_find_unused_parameters_true for glue_benchmark.py (#7454) (#7461) Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Sasha Meister * update strategy (#7577) (#7578) Signed-off-by: Nithin Rao Koluguri Co-authored-by: Nithin Rao Signed-off-by: Sasha Meister * Fix typos (#7581) Signed-off-by: Sasha Meister * Change hifigan finetune strategy to ddp_find_unused_parameters_true (#7579) (#7584) * Change strategy to auto --------- Signed-off-by: Cheng-Ping Hsieh Co-authored-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Signed-off-by: Sasha Meister * [BugFix] Add missing quotes for auto strategy in tutorial notebooks (#7541) (#7548) * Add missing quotes for auto strategy * Revert trainer.gpus to trainer.devices in Self_Supervised_Pre_Training.ipynb --------- Signed-off-by: Abhishree Signed-off-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Sasha Meister * added per tests Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * [PATCH] PEFT import mcore (#7393) * [PATCH] PEFT import mcore Signed-off-by: Jason Wang * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Jason Wang Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * add build os key (#7596) (#7599) * add build os key * add tools * update to stable version --------- Signed-off-by: Nithin Rao Koluguri Co-authored-by: Nithin Rao Signed-off-by: Sasha Meister * StarCoder SFT test + bump PyT NGC image to 23.09 (#7540) * Add SFT StarCoder test Signed-off-by: Jan Lasek * Remove _modify_config call as it is covered in load_from_nemo just below Signed-off-by: Jan Lasek * Test with pyt:23.09 container Signed-off-by: Jan Lasek --------- Signed-off-by: Jan Lasek Signed-off-by: Sasha Meister * defaults changed (#7600) * defaults changed Signed-off-by: arendu * typo Signed-off-by: arendu * update Signed-off-by: arendu --------- Signed-off-by: arendu Signed-off-by: Sasha Meister * add ItalianPhonemesTokenizer (#7587) * add ItalianPhonemesTokenizer Signed-off-by: GiacomoLeoneMaria * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix Italian phonemes Signed-off-by: GiacomoLeoneMaria * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add test Signed-off-by: GiacomoLeoneMaria --------- Signed-off-by: GiacomoLeoneMaria Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Signed-off-by: Sasha Meister * best ckpt fix (#7564) (#7588) Signed-off-by: dimapihtar Co-authored-by: Dmytro Pykhtar <37850217+dimapihtar@users.noreply.github.com> Signed-off-by: Sasha Meister * rate_punctuation.py Fixed output manifest saving Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * Fix tests Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * Add files via upload (#7598) specifies the branch Signed-off-by: George <37293288+Jorjeous@users.noreply.github.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Signed-off-by: Sasha Meister * Fix validation in G2PModel and ThutmoseTaggerModel (#7597) (#7606) Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Sasha Meister * Broadcast loss only when using pipeline parallelism and within the pipeline parallel domain (#7576) (#7586) * Broadcast loss only when using pipeline parallelism and within the pipeline parallel domain * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Sangkug Lym Co-authored-by: Sangkug Lym Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * Safeguard nemo_text_processing installation on ARM (#7485) * safeguard nemo_text_processing installing Signed-off-by: Jason * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update check Signed-off-by: Jason --------- Signed-off-by: Jason Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister * Function name fixing Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * Moving PER to speech_to_text_eval.py Added: - "use_per": PER metric computing; - "scores_per_sample": metrics computation sample by sample for wer/cer/punctuation rates; - "output_with_scores_filename": saving manifest with metrics Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * Update test_metrics.py Updated "punctuation_error_rate" function name Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * Added use_per description Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * guard extra dependencies Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * Write metrics to "output_filename" if "scores_per_sample=True" Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * scores_per_sample description Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * Fix import guards Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * Stats printing when HAVE_TABLUATE_AND_PANDAS=False Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * Bound transformers version in requirements (#7620) Signed-off-by: Abhishree Signed-off-by: Sasha Meister * fix llama2 70b lora tuning bug (#7622) * fix llama2 70b lora tuning bug Signed-off-by: Chen Cui * Update peft_config.py brackets Signed-off-by: Adi Renduchintala --------- Signed-off-by: Chen Cui Signed-off-by: Adi Renduchintala Co-authored-by: Adi Renduchintala Signed-off-by: Sasha Meister * Fix import error no module name model_utils (#7629) Signed-off-by: Mehadi Hasan Menon Signed-off-by: Sasha Meister * Delete examples/asr/rate_punctuation.py Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * Added use_per description Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * metric and variables name fixing Signed-off-by: Sasha Meister * Add else samples = None Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * add fc large ls models (#7641) Signed-off-by: Nithin Rao Koluguri Co-authored-by: Nithin Rao Koluguri Signed-off-by: Sasha Meister * bugfix: trainer.gpus, trainer.strategy, trainer.accelerator (#7621) (#7642) * [TTS] bugfix for Tacotron2 tutorial due to PTL 2.0 * trainer.gpus -> trainer.devices * fixed related tutorial bugs --------- Signed-off-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Signed-off-by: Sasha Meister * fix ssl models ptl monitor val through logging (#7608) (#7614) Signed-off-by: Nithin Rao Koluguri Co-authored-by: Nithin Rao Co-authored-by: Eric Harper Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Signed-off-by: Sasha Meister * Fix metrics for SE tutorial (#7604) (#7612) Signed-off-by: Ante Jukić Co-authored-by: anteju <108555623+anteju@users.noreply.github.com> Signed-off-by: Sasha Meister * Add ddp_find_unused_parameters=True and change accelerator to auto (#7623) (#7644) * Add ddp_find_unused_parameters=True and change acclerator to auto * Add ddp_find_unused_parameters True for normalization_as_tagging_train.py --------- Signed-off-by: Abhishree Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Sasha Meister * Fix py3.11 dataclasses issue (#7616) * Fix py3.11 dataclasses issue (#7582) * Update ASR configs to support Python 3.11 Signed-off-by: smajumdar * Update TTS configs to support Python 3.11 Signed-off-by: smajumdar * Guard MeCab and Ipadic Signed-off-by: smajumdar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix remaining ASR dataclasses Signed-off-by: smajumdar * Fix remaining ASR dataclasses Signed-off-by: smajumdar * Fix scripts Signed-off-by: smajumdar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: smajumdar Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Update name to ConfidenceMethodConfig Signed-off-by: smajumdar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Broadcast loss only when using pipeline parallelism and within the pipeline parallel domain (#7576) (#7586) * Broadcast loss only when using pipeline parallelism and within the pipeline parallel domain * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Sangkug Lym Co-authored-by: Sangkug Lym Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Safeguard nemo_text_processing installation on ARM (#7485) * safeguard nemo_text_processing installing Signed-off-by: Jason * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update check Signed-off-by: Jason --------- Signed-off-by: Jason Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Fix changes to confidence measure Signed-off-by: smajumdar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: smajumdar Signed-off-by: Sangkug Lym Signed-off-by: Jason Co-authored-by: Somshubra Majumdar Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Sangkug Lym Co-authored-by: Jason Signed-off-by: Sasha Meister * moved per sample metrics computing to transcribe_utils Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * Moved punctuation rates printing to punct_er Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * Added reset for DatasetPunctuationErrorRate class Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * Added compute_metrics_per_sample description Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Sasha Meister * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update megatron_gpt_peft_models.py Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update speech_to_text_eval.py Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> * Copyright year fixing Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> * "& AFFILIATES" added Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> --------- Signed-off-by: Maanu Grover Signed-off-by: Sasha Meister Signed-off-by: Jason Wang Signed-off-by: Tim Moon Signed-off-by: Robin Dong Signed-off-by: Sasha Meister <117230141+ssh-meister@users.noreply.github.com> Signed-off-by: Ante Jukić Signed-off-by: Abhinav Khattar Signed-off-by: smajumdar Signed-off-by: eharper Signed-off-by: jasonwan Signed-off-by: Jimmy Zhang Signed-off-by: Abhishree Signed-off-by: Sangkug Lym Signed-off-by: George Zelenfroynd Signed-off-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Signed-off-by: Anton Peganov Signed-off-by: Nikolay Karpov Signed-off-by: arendu Signed-off-by: Samuele Cornell Signed-off-by: Cheng-Ping Hsieh Signed-off-by: KunalDhawan Signed-off-by: Aleksandr Laptev Signed-off-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Signed-off-by: Jason Signed-off-by: Ryan Signed-off-by: mburchi Signed-off-by: Maxime Burchi <60737204+burchim@users.noreply.github.com> Signed-off-by: Jan Lasek Signed-off-by: Hongbin Liu Signed-off-by: Tamerlan Tabolov Signed-off-by: Gerald Shen Signed-off-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Signed-off-by: Stas Bekman Signed-off-by: Jocelyn Huang Signed-off-by: GiacomoLeoneMaria Signed-off-by: fayejf <36722593+fayejf@users.noreply.github.com> Signed-off-by: Olivier Delalleau <507137+odelalleau@users.noreply.github.com> Signed-off-by: Nithin Rao Koluguri Signed-off-by: hkelly33 <58792115+hkelly33@users.noreply.github.com> Signed-off-by: Adi Renduchintala Signed-off-by: arendu Signed-off-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Signed-off-by: Chen Cui Signed-off-by: BestJuly Signed-off-by: Elena Rastorgueva Signed-off-by: dimapihtar Signed-off-by: George <37293288+Jorjeous@users.noreply.github.com> Signed-off-by: Mehadi Hasan Menon Co-authored-by: Maanu Grover <109391026+maanug-nv@users.noreply.github.com> Co-authored-by: Jason Wang Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tim Moon <4406448+timmoon10@users.noreply.github.com> Co-authored-by: Eric Harper Co-authored-by: Robin Dong Co-authored-by: Somshubra Majumdar Co-authored-by: anteju <108555623+anteju@users.noreply.github.com> Co-authored-by: Abhinav Khattar Co-authored-by: JimmyZhang12 <67203904+JimmyZhang12@users.noreply.github.com> Co-authored-by: Jimmy Zhang Co-authored-by: Abhishree Thittenamane <47577437+athitten@users.noreply.github.com> Co-authored-by: Sangkug Lym Co-authored-by: George <37293288+Jorjeous@users.noreply.github.com> Co-authored-by: PeganovAnton Co-authored-by: Nikolay Karpov Co-authored-by: Adi Renduchintala Co-authored-by: Samuele Cornell Co-authored-by: Cheng-Ping Hsieh <37269846+hsiehjackson@users.noreply.github.com> Co-authored-by: Yang Zhang Co-authored-by: Kunal Dhawan Co-authored-by: Aleksandr Laptev Co-authored-by: Xuesong Yang <1646669+XuesongYang@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Jason Co-authored-by: Ryan Langman Co-authored-by: Maxime Burchi <60737204+burchim@users.noreply.github.com> Co-authored-by: Igor Gitman Co-authored-by: Jan Lasek Co-authored-by: Kelvin Liu Co-authored-by: Hongbin Liu Co-authored-by: Tamerlan Tabolov Co-authored-by: Gerald Shen <119401249+gshennvm@users.noreply.github.com> Co-authored-by: Xuesong Yang <16880-xueyang@users.noreply.gitlab-master.nvidia.com> Co-authored-by: Stas Bekman Co-authored-by: Jocelyn Co-authored-by: Giacomo Leone Maria Cavallini <72698188+GiacomoLeoneMaria@users.noreply.github.com> Co-authored-by: fayejf <36722593+fayejf@users.noreply.github.com> Co-authored-by: Olivier Delalleau <507137+odelalleau@users.noreply.github.com> Co-authored-by: Nithin Rao Co-authored-by: meatybobby Co-authored-by: Chen Cui Co-authored-by: Marc Romeyn Co-authored-by: hkelly33 <58792115+hkelly33@users.noreply.github.com> Co-authored-by: Yuanzhe Dong Co-authored-by: Cheng-Ping Hsieh Co-authored-by: Li Tao Co-authored-by: Elena Rastorgueva <80532067+erastorgueva-nv@users.noreply.github.com> Co-authored-by: Igor Gitman Co-authored-by: Dmytro Pykhtar <37850217+dimapihtar@users.noreply.github.com> Co-authored-by: Mehadi Hasan Menon Co-authored-by: Evelina <10428420+ekmb@users.noreply.github.com> Signed-off-by: Sasha Meister --- examples/asr/speech_to_text_eval.py | 44 +- .../asr/parts/utils/transcribe_utils.py | 92 ++++ nemo/collections/common/metrics/punct_er.py | 473 ++++++++++++++++++ tests/collections/common/test_metrics.py | 73 +++ 4 files changed, 680 insertions(+), 2 deletions(-) create mode 100644 nemo/collections/common/metrics/punct_er.py diff --git a/examples/asr/speech_to_text_eval.py b/examples/asr/speech_to_text_eval.py index 452aa8202660..9e24f0172208 100644 --- a/examples/asr/speech_to_text_eval.py +++ b/examples/asr/speech_to_text_eval.py @@ -25,12 +25,18 @@ for full list of arguments >> dataset_manifest: Required - path to dataset JSON manifest file (in NeMo format) - output_filename: Optional - output filename where the transcriptions will be written. + output_filename: Optional - output filename where the transcriptions will be written. (if scores_per_sample=True, + metrics per sample will be written there too) use_cer: Bool, whether to compute CER or WER + use_punct_er: Bool, compute dataset Punctuation Error Rate (set the punctuation marks for metrics computation with + "text_processing.punctuation_marks") + tolerance: Float, minimum WER/CER required to pass some arbitrary tolerance. only_score_manifest: Bool, when set will skip audio transcription and just calculate WER of provided manifest. + scores_per_sample: Bool, compute metrics for each sample separately (if only_score_manifest=True, scores per sample + will be added to the manifest at the dataset_manifest path) # Usage @@ -66,7 +72,12 @@ from omegaconf import MISSING, OmegaConf, open_dict from nemo.collections.asr.metrics.wer import word_error_rate -from nemo.collections.asr.parts.utils.transcribe_utils import PunctuationCapitalization, TextProcessingConfig +from nemo.collections.asr.parts.utils.transcribe_utils import ( + PunctuationCapitalization, + TextProcessingConfig, + compute_metrics_per_sample, +) +from nemo.collections.common.metrics.punct_er import DatasetPunctuationErrorRate from nemo.core.config import hydra_runner from nemo.utils import logging @@ -82,9 +93,11 @@ class EvaluationConfig(transcribe_speech.TranscriptionConfig): att_context_size: Optional[list] = None use_cer: bool = False + use_punct_er: bool = False tolerance: Optional[float] = None only_score_manifest: bool = False + scores_per_sample: bool = False text_processing: Optional[TextProcessingConfig] = TextProcessingConfig( punctuation_marks=".,?", separate_punctuation=False, do_lowercase=False, rm_punctuation=False, @@ -154,6 +167,29 @@ def main(cfg: EvaluationConfig): f"contain value for `pred_text`." ) + if cfg.use_punct_er: + dper_obj = DatasetPunctuationErrorRate( + hypotheses=predicted_text, + references=ground_truth_text, + punctuation_marks=list(cfg.text_processing.punctuation_marks), + ) + dper_obj.compute() + + if cfg.scores_per_sample: + metrics_to_compute = ["wer", "cer"] + + if cfg.use_punct_er: + metrics_to_compute.append("punct_er") + + samples_with_metrics = compute_metrics_per_sample( + manifest_path=cfg.dataset_manifest, + reference_field="text", + hypothesis_field="pred_text", + metrics=metrics_to_compute, + punctuation_marks=cfg.text_processing.punctuation_marks, + output_manifest_path=cfg.output_filename, + ) + # Compute the WER cer = word_error_rate(hypotheses=predicted_text, references=ground_truth_text, use_cer=True) wer = word_error_rate(hypotheses=predicted_text, references=ground_truth_text, use_cer=False) @@ -173,6 +209,10 @@ def main(cfg: EvaluationConfig): logging.info(f'Dataset WER/CER ' + str(round(100 * wer, 2)) + "%/" + str(round(100 * cer, 2)) + "%") + if cfg.use_punct_er: + dper_obj.print() + dper_obj.reset() + # Inject the metric name and score into the config, and return the entire config with open_dict(cfg): cfg.metric_name = metric_name diff --git a/nemo/collections/asr/parts/utils/transcribe_utils.py b/nemo/collections/asr/parts/utils/transcribe_utils.py index f4508709b17c..8d80396dd82e 100644 --- a/nemo/collections/asr/parts/utils/transcribe_utils.py +++ b/nemo/collections/asr/parts/utils/transcribe_utils.py @@ -23,9 +23,11 @@ from tqdm.auto import tqdm import nemo.collections.asr as nemo_asr +from nemo.collections.asr.metrics.wer import word_error_rate from nemo.collections.asr.models import ASRModel, EncDecHybridRNNTCTCModel from nemo.collections.asr.parts.utils import rnnt_utils from nemo.collections.asr.parts.utils.streaming_utils import FrameBatchASR +from nemo.collections.common.metrics.punct_er import OccurancePunctuationErrorRate from nemo.collections.common.parts.preprocessing.manifest import get_full_path from nemo.utils import logging, model_utils @@ -472,6 +474,96 @@ def transcribe_partial_audio( return hypotheses +def compute_metrics_per_sample( + manifest_path: str, + reference_field: str = "text", + hypothesis_field: str = "pred_text", + metrics: list[str] = ["wer"], + punctuation_marks: list[str] = [".", ",", "?"], + output_manifest_path: str = None, +) -> dict: + + ''' + Computes metrics per sample for given manifest + + Args: + manifest_path: str, Required - path to dataset JSON manifest file (in NeMo format) + reference_field: str, Optional - name of field in .json manifest with the reference text ("text" by default). + hypothesis_field: str, Optional - name of field in .json manifest with the hypothesis text ("pred_text" by default). + metrics: list[str], Optional - list of metrics to be computed (currently supported "wer", "cer", "punct_er") + punctuation_marks: list[str], Optional - list of punctuation marks for computing punctuation error rate ([".", ",", "?"] by default). + output_manifest_path: str, Optional - path where .json manifest with calculated metrics will be saved. + + Returns: + samples: dict - Dict of samples with calculated metrics + ''' + + supported_metrics = ["wer", "cer", "punct_er"] + + if len(metrics) == 0: + raise AssertionError( + f"'metrics' list is empty. \ + Select the metrics from the supported: {supported_metrics}." + ) + + for metric in metrics: + if metric not in supported_metrics: + raise AssertionError( + f"'{metric}' metric is not supported. \ + Currently supported metrics are {supported_metrics}." + ) + + if "punct_er" in metrics: + if len(punctuation_marks) == 0: + raise AssertionError("punctuation_marks list can't be empty when 'punct_er' metric is enabled.") + else: + oper_obj = OccurancePunctuationErrorRate(punctuation_marks=punctuation_marks) + + use_wer = "wer" in metrics + use_cer = "cer" in metrics + use_punct_er = "punct_er" in metrics + + with open(manifest_path, 'r') as manifest: + lines = manifest.readlines() + samples = [json.loads(line) for line in lines] + samples_with_metrics = [] + + logging.info(f"Computing {', '.join(metrics)} per sample") + + for sample in tqdm(samples): + reference = sample[reference_field] + hypothesis = sample[hypothesis_field] + + if use_wer: + sample_wer = word_error_rate(hypotheses=[hypothesis], references=[reference], use_cer=False) + sample["wer"] = round(100 * sample_wer, 2) + + if use_cer: + sample_cer = word_error_rate(hypotheses=[hypothesis], references=[reference], use_cer=True) + sample["cer"] = round(100 * sample_cer, 2) + + if use_punct_er: + operation_amounts, substitution_amounts, punctuation_rates = oper_obj.compute( + reference=reference, hypothesis=hypothesis + ) + sample["punct_correct_rate"] = round(100 * punctuation_rates.correct_rate, 2) + sample["punct_deletions_rate"] = round(100 * punctuation_rates.deletions_rate, 2) + sample["punct_insertions_rate"] = round(100 * punctuation_rates.insertions_rate, 2) + sample["punct_substitutions_rate"] = round(100 * punctuation_rates.substitutions_rate, 2) + sample["punct_error_rate"] = round(100 * punctuation_rates.punct_er, 2) + + samples_with_metrics.append(sample) + + if output_manifest_path is not None: + with open(output_manifest_path, 'w') as output: + for sample in samples_with_metrics: + line = json.dumps(sample) + output.writelines(f'{line}\n') + logging.info(f'Output manifest saved: {output_manifest_path}') + + return samples_with_metrics + + class PunctuationCapitalization: def __init__(self, punctuation_marks: str): """ diff --git a/nemo/collections/common/metrics/punct_er.py b/nemo/collections/common/metrics/punct_er.py new file mode 100644 index 000000000000..933c1581f016 --- /dev/null +++ b/nemo/collections/common/metrics/punct_er.py @@ -0,0 +1,473 @@ +# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re +from collections import namedtuple +from tqdm import tqdm + +from nemo.utils import logging + +try: + import pandas as pd + from tabulate import tabulate + + HAVE_TABLUATE_AND_PANDAS = True +except (ImportError, ModuleNotFoundError): + HAVE_TABLUATE_AND_PANDAS = False + + +def punctuation_error_rate( + references: list[str], hypotheses: list[str], punctuation_marks: list[str], punctuation_mask: str = "[PUNCT]", +) -> None: + + """ + Computes Punctuation Error Rate + + Args: + references (list[str]) - list of references + hypotheses (list[str]) - list of hypotheses + punctuation_marks (list[str]) - list of punctuation marks for computing metrics + punctuation_mask (str, by default "[PUNCT]") - mask token that will be applied to + given punctuation marks while edit distance calculation + + Return: + punct_er (float) - Punctuation Error Rate + """ + + dper_obj = DatasetPunctuationErrorRate( + references=references, + hypotheses=hypotheses, + punctuation_marks=punctuation_marks, + punctuation_mask=punctuation_mask, + ) + + dper_obj.compute() + + return dper_obj.punct_er + + +class OccurancePunctuationErrorRate: + """ + Class for computation puncutation-related absolute amounts of operations and thier rates + between reference and hypothesis strings: + - Absolute amounts of correct predictions, deletions, insertions + and substitutions for each given punctuation mark + - Rates of correct predictions, deletions, insertions + and substitutions for each given punctuation mark + - Overall rates of correct predictions, deletions, insertions + and substiturions between reference and hypothesis string + - Punctuation Error Rate + + Args to init: + punctuation_marks (list[str]) - list of punctuation marks for computing metrics + punctuation_mask (str, by default "[PUNCT]") - mask token that will be applied to + given punctuation marks while edit distance calculation + + How to use: + 1. Create object of OccurancePunctuationErrorRate class. + Example: + punctuation_marks = [".", ",", "!", "?"] + oper_obj = OccurancePunctuationErrorRate(punctuation_marks) + + 2. To compute punctuation metrics, pass reference and hypothesis string to the "compute" method + of created object. + Example: + reference_str = "Hi, dear! Nice to see you. What's" + hypothesis_str = "Hi dear! Nice to see you! What's?" + oper_obj.compute(reference_str, hypothesis_str) + + Output (listed in order of output): + 1. Dict of absolute operations amounts for each given punctuation mark: + Example: + {'.': {'Correct': 0, 'Deletions': 0, 'Insertions': 0, 'Substitutions': 1}, + ',': {'Correct': 0, 'Deletions': 1, 'Insertions': 0, 'Substitutions': 0}, + '!': {'Correct': 1, 'Deletions': 0, 'Insertions': 0, 'Substitutions': 0}, + '?': {'Correct': 0, 'Deletions': 0, 'Insertions': 1, 'Substitutions': 0}} + + 2. Dict of substitutions absolute amounts between given punctuation marks: + Example: + {'.': {'.': 0, ',': 0, '!': 1, '?': 0}, + ',': {'.': 0, ',': 0, '!': 0, '?': 0}, + '!': {'.': 0, ',': 0, '!': 0, '?': 0}, + '?': {'.': 0, ',': 0, '!': 0, '?': 0}} + + 3. namedtuple "PunctuationRates" of punctuation operation rates (in range from 0 to 1): + 3.1. correct_rate - overall correct rate + Example: correct_rate=0.25 + 3.2. deletions_rate - overall deletions rate + Example: deletions_rate=0.25 + 3.3. insertions_rate - overall insertions rate + Example: insertions_rate=0.25 + 3.4. substitutions_rate - overall substitutions_rate + Example: substitutions_rate=0.25 + 3.5. punct_er - Punctuation Error Rate + Example: punct_er=0.75 + 3.6. operation_rates - dict of operations rates for each given punctuation mark + Example: + operation_rates={ + '.': {'Correct': 0.0, 'Deletions': 0.0, 'Insertions': 0.0, 'Substitutions': 1.0}, + ',': {'Correct': 0.0, 'Deletions': 1.0, 'Insertions': 0.0, 'Substitutions': 0.0}, + '!': {'Correct': 1.0, 'Deletions': 0.0, 'Insertions': 0.0, 'Substitutions': 0.0}, + '?': {'Correct': 0.0, 'Deletions': 0.0, 'Insertions': 1.0, 'Substitutions': 0.0} + } + + 3.7. substitution_rates - dict of substitution rates for each given punctuation mark + Example: + substitution_rates={ + '.': {'.': 0.0, ',': 0.0, '!': 1.0, '?': 0.0}, + ',': {'.': 0.0, ',': 0.0, '!': 0.0, '?': 0.0}, + '!': {'.': 0.0, ',': 0.0, '!': 0.0, '?': 0.0}, + '?': {'.': 0.0, ',': 0.0, '!': 0.0, '?': 0.0} + } + """ + + def __init__(self, punctuation_marks: list[str], punctuation_mask: str = "[PUNCT]") -> None: + + assert len(punctuation_marks) != 0, f"List of punctuation marks is empty" + + self.punctuation_marks = punctuation_marks + self.punctuation_mask = punctuation_mask + + self.operations = ["Correct", "Deletions", "Insertions", "Substitutions"] + + def compute_rates(self, operation_amounts: dict, substitution_amounts: dict): + operation_rates = {pm: {operation: 0 for operation in self.operations} for pm in self.punctuation_marks} + substitution_rates = {pm: {pm: 0 for pm in self.punctuation_marks} for pm in self.punctuation_marks} + + for pm in self.punctuation_marks: + operations_amount_by_pm = sum(operation_amounts[pm].values()) + + if operations_amount_by_pm == 0: + continue + + operation_rates[pm] = { + operation: (operation_amounts[pm][operation] / operations_amount_by_pm) + for operation in self.operations + } + + substitution_rates[pm] = { + _pm: (substitution_amounts[pm][_pm] / operations_amount_by_pm) + for _pm in substitution_amounts[pm].keys() + } + + _operation_amounts = { + operation: {pm: operation_amounts[operation] for pm, operation_amounts in operation_amounts.items()} + for operation in self.operations + } + + overall_amounts_by_operation = { + operation: sum(_operation_amounts[operation].values()) for operation in _operation_amounts + } + overall_operations_amount = sum(overall_amounts_by_operation.values()) + + punctuation_rates = namedtuple( + 'PunctuationRates', + [ + 'correct_rate', + 'deletions_rate', + 'insertions_rate', + 'substitutions_rate', + 'punct_er', + 'operation_rates', + 'substitution_rates', + ], + ) + + if overall_operations_amount == 0: + rates = punctuation_rates(0, 0, 0, 0, 0, operation_rates, substitution_rates) + else: + correct_rate = overall_amounts_by_operation["Correct"] / overall_operations_amount + deletions_rate = overall_amounts_by_operation["Deletions"] / overall_operations_amount + insertions_rate = overall_amounts_by_operation["Insertions"] / overall_operations_amount + substitutions_rate = overall_amounts_by_operation["Substitutions"] / overall_operations_amount + punct_er = deletions_rate + insertions_rate + substitutions_rate + + rates = punctuation_rates( + correct_rate, + deletions_rate, + insertions_rate, + substitutions_rate, + punct_er, + operation_rates, + substitution_rates, + ) + + return rates + + def compute_operation_amounts(self, reference: str, hypothesis: str): + operation_amounts = {pm: {operation: 0 for operation in self.operations} for pm in self.punctuation_marks} + substitution_amounts = {pm: {pm: 0 for pm in self.punctuation_marks} for pm in self.punctuation_marks} + + def tokenize(text: str, punctuation_marks: list[str]): + punctuation_marks = "\\" + "\\".join(self.punctuation_marks) + tokens = re.findall(rf"[\w']+|[{punctuation_marks}]", text) + return tokens + + def mask_punct_tokens(tokens: list[str], punctuation_marks: list[str], punctuation_mask: str): + masked = [punctuation_mask if token in punctuation_marks else token for token in tokens] + return masked + + r_tokens = tokenize(reference, self.punctuation_marks) + h_tokens = tokenize(hypothesis, self.punctuation_marks) + + r_masked = mask_punct_tokens(r_tokens, self.punctuation_marks, self.punctuation_mask) + h_masked = mask_punct_tokens(h_tokens, self.punctuation_marks, self.punctuation_mask) + + r_punct_amount = r_masked.count(self.punctuation_mask) + h_punct_amount = h_masked.count(self.punctuation_mask) + + if r_punct_amount + h_punct_amount == 0: + return operation_amounts, substitution_amounts + + r_len = len(r_masked) + h_len = len(h_masked) + + costs = [[0 for inner in range(h_len + 1)] for outer in range(r_len + 1)] + backtrace = [[0 for inner in range(h_len + 1)] for outer in range(r_len + 1)] + + COR = 'C' + DEL, DEL_PENALTY = 'D', 1 + INS, INS_PENALTY = 'I', 1 + SUB, SUB_PENALTY = 'S', 1 + + for i in range(1, r_len + 1): + costs[i][0] = DEL_PENALTY * i + backtrace[i][0] = DEL + + for j in range(1, h_len + 1): + costs[0][j] = INS_PENALTY * j + backtrace[0][j] = INS + + for j in range(1, h_len + 1): + costs[0][j] = INS_PENALTY * j + backtrace[0][j] = INS + + for i in range(1, r_len + 1): + for j in range(1, h_len + 1): + if r_masked[i - 1] == h_masked[j - 1]: + costs[i][j] = costs[i - 1][j - 1] + backtrace[i][j] = COR + else: + substitution_cost = costs[i - 1][j - 1] + SUB_PENALTY + insertion_cost = costs[i][j - 1] + INS_PENALTY + deletion_cost = costs[i - 1][j] + DEL_PENALTY + + costs[i][j] = min(substitution_cost, insertion_cost, deletion_cost) + if costs[i][j] == substitution_cost: + backtrace[i][j] = SUB + elif costs[i][j] == insertion_cost: + backtrace[i][j] = INS + else: + backtrace[i][j] = DEL + + i = r_len + j = h_len + + while i > 0 or j > 0: + if backtrace[i][j] == COR: + if r_masked[i - 1] == self.punctuation_mask or h_masked[j - 1] == self.punctuation_mask: + r_token = r_tokens[i - 1] + h_token = h_tokens[j - 1] + + if r_token == h_token: + operation_amounts[r_token]['Correct'] += 1 + else: + operation_amounts[r_token]['Substitutions'] += 1 + substitution_amounts[r_token][h_token] += 1 + i -= 1 + j -= 1 + + elif backtrace[i][j] == SUB: + i -= 1 + j -= 1 + + elif backtrace[i][j] == INS: + j -= 1 + + elif backtrace[i][j] == DEL: + i -= 1 + + for pm in self.punctuation_marks: + num_of_correct = operation_amounts[pm]['Correct'] + + num_substitutions_of_pm = operation_amounts[pm]['Substitutions'] + num_substitutions_to_pm = sum([substitution_amounts[_pm][pm] for _pm in self.punctuation_marks]) + + num_of_deletions = r_tokens.count(pm) - (num_of_correct + num_substitutions_of_pm) + operation_amounts[pm]['Deletions'] = num_of_deletions + + num_of_insertions = h_tokens.count(pm) - (num_of_correct + num_substitutions_to_pm) + operation_amounts[pm]['Insertions'] = num_of_insertions + + return operation_amounts, substitution_amounts + + def compute(self, reference: str, hypothesis: str): + operation_amounts, substitution_amounts = self.compute_operation_amounts(reference, hypothesis) + punctuation_rates = self.compute_rates(operation_amounts, substitution_amounts) + return operation_amounts, substitution_amounts, punctuation_rates + + +class DatasetPunctuationErrorRate: + """ + Class for computation the total puncutation-related absolute amounts of operations and their rates + in pairs of reference and hypothesis strins: + - Absolute amounts of correct predictions, deletions, insertions + and substitutions for each given punctuation mark + - Rates of correct predictions, deletions, insertions + and substitutions for each given punctuation mark + - Total rates of correct predictions, deletions, insertions + and substiturions in pairs of reference and hypothesis strings + - Punctuation Error Rate + + Args to init: + references (list[str]) - list of references + hypotheses (list[str]) - list of hypotheses + punctuation_marks (list[str]) - list of punctuation marks for computing metrics + punctuation_mask (str, by default "[PUNCT]") - mask token that will be applied to + given punctuation marks while edit distance calculation + + How to use: + 1. Create object of DatasetPunctuationErrorRate class. + Example: + references = ["Hi, dear! Nice to see you. What's"] + hypotheses = ["Hi dear! Nice to see you! What's?"] + punctuation_marks = [".", ",", "!", "?"] + + dper_obj = DatasetPunctuationErrorRate(references, hypotheses, punctuation_marks) + + 2. To compute punctuation metrics, call the class method "compute()". + Example: + dper_obj.compute() + + Result: + The following atributes of class object will be updated with calculated metrics values. + The values are available with calling the atributes: + + dper_obj.operation_rates - dict, rates of correctness and errors for each punctuation mark + from `preset dper_obj.punctuation_marks` list. + + dper_obj.substitution_rates - dict, substitution rates between puncutation marks from + `preset dper_obj.punctuation_marks` list. + + dper_obj.correct_rate - float, total rate of correctness between provided pairs of + references and hypotheses. + + dper_obj.deletions_rate - float, total rate of deletions between provided pairs of + references and hypotheses. + + dper_obj.insertions_rate - float, total rate of insertions between provided pairs of + references and hypotheses. + + dper_obj.substitutions_rate - float, total rate of substitutions between provided pairs of + references and hypotheses. + + dper_obj.punct_er - float, total Punctuation Error Rate between provided pairs of + references and hypotheses. + """ + + def __init__( + self, + references: list[str], + hypotheses: list[str], + punctuation_marks: list[str], + punctuation_mask: str = "[PUNCT]", + ) -> None: + + self.references = references + self.hypotheses = hypotheses + self.punctuation_marks = punctuation_marks + self.punctuation_mask = punctuation_mask + + self.oper_obj = OccurancePunctuationErrorRate( + punctuation_marks=self.punctuation_marks, punctuation_mask=self.punctuation_mask + ) + + self.operation_amounts = [] + self.substitution_amounts = [] + self.rates = [] + + self.operation_rates = None + self.substitution_rates = None + self.correct_rate = None + self.deletions_rate = None + self.insertions_rate = None + self.substitutions_rate = None + self.punct_er = None + + def compute(self): + def sum_amounts(amounts_dicts: list[dict]): + amounts = {key: {_key: 0 for _key in amounts_dicts[0][key]} for key in amounts_dicts[0].keys()} + + for amounts_dict in amounts_dicts: + for outer_key, inner_dict in amounts_dict.items(): + for inner_key, value in inner_dict.items(): + amounts[outer_key][inner_key] += value + return amounts + + logging.info("Computing Punctuation Error Rate") + + for reference, hypothesis in tqdm(zip(self.references, self.hypotheses), total=len(self.references)): + operation_amounts, substitution_amounts, punctuation_rates = self.oper_obj.compute(reference, hypothesis) + self.operation_amounts.append(operation_amounts) + self.substitution_amounts.append(substitution_amounts) + self.rates.append(punctuation_rates) + + overall_operation_amounts = sum_amounts(self.operation_amounts) + overall_substitution_amounts = sum_amounts(self.substitution_amounts) + overall_rates = self.oper_obj.compute_rates( + operation_amounts=overall_operation_amounts, substitution_amounts=overall_substitution_amounts + ) + + self.operation_rates = overall_rates.operation_rates + self.substitution_rates = overall_rates.substitution_rates + self.correct_rate = overall_rates.correct_rate + self.deletions_rate = overall_rates.deletions_rate + self.insertions_rate = overall_rates.insertions_rate + self.substitutions_rate = overall_rates.substitutions_rate + self.punct_er = overall_rates.punct_er + + def reset(self): + self.operation_amounts = [] + self.substitution_amounts = [] + self.rates = [] + + self.operation_rates = None + self.substitution_rates = None + self.correct_rate = None + self.deletions_rate = None + self.insertions_rate = None + self.substitutions_rate = None + self.punct_er = None + + def print(self): + logging.info(f'Dataset PER ' + str(round(100 * self.punct_er, 2)) + '%') + + if HAVE_TABLUATE_AND_PANDAS: + rates_by_pm_df = pd.DataFrame(self.operation_rates) * 100 + substitution_rates_by_pm_df = pd.DataFrame(self.substitution_rates) * 100 + + logging.info( + "Rates of punctuation correctness and errors (%):\n" + + tabulate(rates_by_pm_df, headers='keys', tablefmt='psql') + ) + logging.info( + "Substitution rates between punctuation marks (%):\n" + + tabulate(substitution_rates_by_pm_df, headers='keys', tablefmt='psql') + ) + else: + logging.warning("Some of the modules (pandas or tabulate) can't be imported") + logging.info(f"Rates of punctuation correctness and errors (in range [0, 1]):\n{self.operation_rates}\n") + logging.info( + f"Substitution rates between punctuation marks (in range [0, 1]):\n{self.substitution_rates}\n" + ) diff --git a/tests/collections/common/test_metrics.py b/tests/collections/common/test_metrics.py index e4bfde635a06..f9005232a017 100644 --- a/tests/collections/common/test_metrics.py +++ b/tests/collections/common/test_metrics.py @@ -16,6 +16,11 @@ import torch from nemo.collections.common.metrics.classification_accuracy import TopKClassificationAccuracy +from nemo.collections.common.metrics.punct_er import ( + DatasetPunctuationErrorRate, + OccurancePunctuationErrorRate, + punctuation_error_rate, +) from .loss_inputs import ALL_NUM_MEASUREMENTS_ARE_ZERO, NO_ZERO_NUM_MEASUREMENTS, SOME_NUM_MEASUREMENTS_ARE_ZERO from .perplexity_inputs import NO_PROBS_NO_LOGITS, ONLY_LOGITS1, ONLY_LOGITS100, ONLY_PROBS, PROBS_AND_LOGITS @@ -149,3 +154,71 @@ def test_loss(self, ddp, dist_sync_on_step, loss_sum_or_avg, num_measurements, t dist_sync_on_step=dist_sync_on_step, take_avg_loss=take_avg_loss, ) + + +class TestPunctuationErrorRate: + reference = "Hi, dear! Nice to see you. What's" + hypothesis = "Hi dear! Nice to see you! What's?" + punctuation_marks = [".", ",", "!", "?"] + + operation_amounts = { + '.': {'Correct': 0, 'Deletions': 0, 'Insertions': 0, 'Substitutions': 1}, + ',': {'Correct': 0, 'Deletions': 1, 'Insertions': 0, 'Substitutions': 0}, + '!': {'Correct': 1, 'Deletions': 0, 'Insertions': 0, 'Substitutions': 0}, + '?': {'Correct': 0, 'Deletions': 0, 'Insertions': 1, 'Substitutions': 0}, + } + substitution_amounts = { + '.': {'.': 0, ',': 0, '!': 1, '?': 0}, + ',': {'.': 0, ',': 0, '!': 0, '?': 0}, + '!': {'.': 0, ',': 0, '!': 0, '?': 0}, + '?': {'.': 0, ',': 0, '!': 0, '?': 0}, + } + correct_rate = 0.25 + deletions_rate = 0.25 + insertions_rate = 0.25 + substitutions_rate = 0.25 + punct_er = 0.75 + operation_rates = { + '.': {'Correct': 0.0, 'Deletions': 0.0, 'Insertions': 0.0, 'Substitutions': 1.0}, + ',': {'Correct': 0.0, 'Deletions': 1.0, 'Insertions': 0.0, 'Substitutions': 0.0}, + '!': {'Correct': 1.0, 'Deletions': 0.0, 'Insertions': 0.0, 'Substitutions': 0.0}, + '?': {'Correct': 0.0, 'Deletions': 0.0, 'Insertions': 1.0, 'Substitutions': 0.0}, + } + substitution_rates = { + '.': {'.': 0.0, ',': 0.0, '!': 1.0, '?': 0.0}, + ',': {'.': 0.0, ',': 0.0, '!': 0.0, '?': 0.0}, + '!': {'.': 0.0, ',': 0.0, '!': 0.0, '?': 0.0}, + '?': {'.': 0.0, ',': 0.0, '!': 0.0, '?': 0.0}, + } + + @pytest.mark.unit + def test_punctuation_error_rate(self): + assert punctuation_error_rate([self.reference], [self.hypothesis], self.punctuation_marks) == self.punct_er + + @pytest.mark.unit + def test_OccurancePunctuationErrorRate(self): + oper_obj = OccurancePunctuationErrorRate(self.punctuation_marks) + operation_amounts, substitution_amounts, punctuation_rates = oper_obj.compute(self.reference, self.hypothesis) + + assert operation_amounts == self.operation_amounts + assert substitution_amounts == self.substitution_amounts + assert punctuation_rates.correct_rate == self.correct_rate + assert punctuation_rates.deletions_rate == self.deletions_rate + assert punctuation_rates.insertions_rate == self.insertions_rate + assert punctuation_rates.substitutions_rate == self.substitutions_rate + assert punctuation_rates.punct_er == self.punct_er + assert punctuation_rates.operation_rates == self.operation_rates + assert punctuation_rates.substitution_rates == self.substitution_rates + + @pytest.mark.unit + def test_DatasetPunctuationErrorRate(self): + dper_obj = DatasetPunctuationErrorRate([self.reference], [self.hypothesis], self.punctuation_marks) + dper_obj.compute() + + assert dper_obj.correct_rate == self.correct_rate + assert dper_obj.deletions_rate == self.deletions_rate + assert dper_obj.insertions_rate == self.insertions_rate + assert dper_obj.substitutions_rate == self.substitutions_rate + assert dper_obj.punct_er == self.punct_er + assert dper_obj.operation_rates == self.operation_rates + assert dper_obj.substitution_rates == self.substitution_rates From 6c777ad4911004a7f7170546cba51ab5920ef578 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 12:53:33 +0300 Subject: [PATCH 108/112] conversion issue fix (#7648) (#7668) Signed-off-by: dimapihtar Co-authored-by: Dmytro Pykhtar <37850217+dimapihtar@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../nlp/models/language_modeling/megatron_gpt_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py index 46d3f37f5d9b..4d145ee2d330 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py @@ -1333,7 +1333,7 @@ def on_load_checkpoint(self, checkpoint) -> None: # mcore uses distributed checkpointing if self.mcore_gpt: - if 'state_dict' in checkpoint: + if 'state_dict' in checkpoint and checkpoint['state_dict']: for index, module in enumerate(self.get_gpt_module_list()): if parallel_state.get_virtual_pipeline_model_parallel_world_size() is not None: checkpoint_state_dict = checkpoint['state_dict'][f'model_{index}'] From b85e9b487dd8126c814f906f079a8a549bcb3bc2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 12:54:49 +0300 Subject: [PATCH 109/112] layernorm1p fix (#7523) (#7567) * layernorm1p fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add layernorm1p to if statement * config changes * gpt config changes * remove layernorm_zero_centered_gamma from gpt config * change line --------- Signed-off-by: dimapihtar Co-authored-by: Dmytro Pykhtar <37850217+dimapihtar@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Signed-off-by: Sasha Meister --- .../nlp/models/language_modeling/megatron_gpt_model.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py index 4d145ee2d330..b23fe0bbf886 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_model.py @@ -1515,10 +1515,14 @@ def build_transformer_config(self) -> TransformerConfig: activation_func = activation_to_func(activation) normalization = self.cfg.get('normalization', 'layernorm') + layernorm_zero_centered_gamma = self.cfg.get('normalization', 'layernorm') == 'layernorm1p' if normalization == 'layernorm': normalization = 'LayerNorm' elif normalization == 'rmsnorm': normalization = 'RMSNorm' + elif normalization == 'layernorm1p': + normalization = 'LayerNorm' + layernorm_zero_centered_gamma = True else: logging.warning( f"The normalization type: {normalization} might not be supported in megatron core." @@ -1562,7 +1566,7 @@ def build_transformer_config(self) -> TransformerConfig: # any configs that are not in the nemo model config will be added here config_mapping = { 'apply_residual_connection_post_layernorm': False, # we don't use this in NeMo - 'layernorm_zero_centered_gamma': False, # not currently used in NeMo + 'layernorm_zero_centered_gamma': layernorm_zero_centered_gamma, 'add_bias_linear': add_bias_linear, 'gated_linear_unit': gated_linear_unit, 'activation_func': activation_func, From 84e18eb785dd570293e7eb2ad574c29982bb87de Mon Sep 17 00:00:00 2001 From: Yi Dong <43824965+yidong72@users.noreply.github.com> Date: Tue, 10 Oct 2023 08:22:55 -0400 Subject: [PATCH 110/112] generalized chat sft prompt (#7655) * fix dataset issues Signed-off-by: Yi Dong * working version Signed-off-by: Yi Dong * all passed Signed-off-by: Yi Dong * refactor tests Signed-off-by: Yi Dong * all pass Signed-off-by: Yi Dong * working version Signed-off-by: Yi Dong * use end name signal for labels Signed-off-by: Yi Dong * all fixed Signed-off-by: Yi Dong * update doc Signed-off-by: Yi Dong * style fix Signed-off-by: Yi Dong * remove unused imports Signed-off-by: Yi Dong * make sure nccl not timing out Signed-off-by: Yi Dong * style fix Signed-off-by: Yi Dong * generate example template Signed-off-by: Yi Dong * generic end of name token Signed-off-by: Yi Dong * style fix Signed-off-by: Yi Dong * add the chat prompt format into the config Signed-off-by: Yi Dong * make sure sft working Signed-off-by: Yi Dong * address reviewer comment Signed-off-by: Yi Dong * fix non Signed-off-by: Yi Dong * try openAI prompt Signed-off-by: Yi Dong * remove unused imports Signed-off-by: Yi Dong * remove human labels from the data Signed-off-by: Yi Dong * use hf dataset to clean Signed-off-by: Yi Dong * reviewer comments Signed-off-by: Yi Dong --------- Signed-off-by: Yi Dong Signed-off-by: Sasha Meister --- .../language_modeling/megatron_gpt_eval.py | 7 +- .../tuning/conf/megatron_gpt_sft.yaml | 6 + .../tuning/megatron_gpt_sft.py | 12 +- .../megatron/gpt_sft_chat_dataset.py | 257 +++++++---- .../megatron/gpt_sft_dataset.py | 18 +- .../megatron_gpt_sft_model.py | 5 +- .../nlp_language_modeling/sft/data_clean.py | 2 +- .../sft/preprocessing.py | 11 +- .../collections/nlp/test_chat_sft_dataset.py | 408 ++++++++++++------ 9 files changed, 488 insertions(+), 238 deletions(-) diff --git a/examples/nlp/language_modeling/megatron_gpt_eval.py b/examples/nlp/language_modeling/megatron_gpt_eval.py index 04125c6f750e..c9eb013b64e9 100644 --- a/examples/nlp/language_modeling/megatron_gpt_eval.py +++ b/examples/nlp/language_modeling/megatron_gpt_eval.py @@ -13,6 +13,7 @@ # limitations under the License. import asyncio +import datetime import os import threading from functools import partial @@ -167,7 +168,11 @@ def remove_padded_prompts(response, nb_paddings): def main(cfg) -> None: # trainer required for restoring model parallel models - trainer = Trainer(strategy=NLPDDPStrategy(), **cfg.trainer, callbacks=[CustomProgressBar()]) + trainer = Trainer( + strategy=NLPDDPStrategy(timeout=datetime.timedelta(seconds=18000)), + **cfg.trainer, + callbacks=[CustomProgressBar()], + ) if cfg.gpt_model_file is not None: if ( diff --git a/examples/nlp/language_modeling/tuning/conf/megatron_gpt_sft.yaml b/examples/nlp/language_modeling/tuning/conf/megatron_gpt_sft.yaml index b0b8eedb5633..27e73996225f 100644 --- a/examples/nlp/language_modeling/tuning/conf/megatron_gpt_sft.yaml +++ b/examples/nlp/language_modeling/tuning/conf/megatron_gpt_sft.yaml @@ -71,6 +71,12 @@ model: data: chat: False # whether use chatbot data or not + chat_prompt_tokens: # special tokens for the chat prompts, a dictionary of {token_type: token}. note that some tokenizer may combine the characters at the junction between {end_of_turn}{turn_start}. e.g. '', the '><' sometimes is merged to be a single token. This is not supported, try to avoid + system_turn_start: '' + turn_start: '' + label_start: '' + end_of_turn: "\x0A" # \0x0A is '\n' + end_of_name: "\x0A" # \0x0A is '\n' train_ds: # Example of how to specify paths to multiple datasets # file_names: diff --git a/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py b/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py index 9a70671f8073..4c4c001014db 100644 --- a/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py +++ b/examples/nlp/language_modeling/tuning/megatron_gpt_sft.py @@ -15,11 +15,12 @@ import os import tempfile +import torch.multiprocessing as mp from omegaconf.omegaconf import OmegaConf, open_dict from pytorch_lightning import Trainer from pytorch_lightning.plugins.environments import TorchElasticEnvironment -from pytorch_lightning.trainer.connectors.checkpoint_connector import _CheckpointConnector +from nemo.collections.nlp.data.language_modeling.megatron.gpt_sft_chat_dataset import get_prompt_template_example from nemo.collections.nlp.models.language_modeling.megatron_gpt_sft_model import MegatronGPTSFTModel from nemo.collections.nlp.modules.common.megatron.megatron_init import fake_initialize_model_parallel from nemo.collections.nlp.parts.nlp_overrides import ( @@ -36,6 +37,8 @@ from nemo.utils.exp_manager import exp_manager from nemo.utils.model_utils import inject_model_parallel_rank +mp.set_start_method("spawn", force=True) + def _modify_config(gpt_cfg, cfg, add_cfg_to_tree=False): """ @@ -71,6 +74,13 @@ def _modify_config(gpt_cfg, cfg, add_cfg_to_tree=False): gpt_cfg.pipeline_model_parallel_size = cfg.model.get('pipeline_model_parallel_size', 1) gpt_cfg.pipeline_model_parallel_split_rank = cfg.model.get('pipeline_model_parallel_split_rank', 0) + if cfg.model.data.get('chat', False): + # chat model, overwrite the prompt template + prompt_template = get_prompt_template_example(cfg.model.data.chat_prompt_tokens) + gpt_cfg.data.train_ds.prompt_template = prompt_template + gpt_cfg.data.validation_ds.prompt_template = prompt_template + gpt_cfg.data.test_ds.prompt_template = prompt_template + sft_cls = MegatronGPTSFTModel gpt_cfg.target = f"{sft_cls.__module__}.{sft_cls.__name__}" diff --git a/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_chat_dataset.py b/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_chat_dataset.py index 801a58394f06..96cc57a300b8 100644 --- a/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_chat_dataset.py +++ b/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_chat_dataset.py @@ -16,19 +16,19 @@ import torch -from nemo.collections.common.tokenizers.sentencepiece_tokenizer import SentencePieceTokenizer from nemo.collections.common.tokenizers.tokenizer_spec import TokenizerSpec from nemo.collections.nlp.data.language_modeling.megatron.gpt_sft_dataset import GPTSFTDataset from nemo.utils import logging -__all__ = ['GPTSFTChatDataset'] +__all__ = ['GPTSFTChatDataset', 'get_prompt_template_example'] -IGNORE_INDEX = -100 -END_SIGNAL = "\n" -END_NAME_SIGNAL = "\n" -SYSTEM_TOKEN = "System\n" -TURN_TOKEN = "" +PREFIX_STR = ( + "\x00" # the prefix string used in the tokenizer to deal with the added empty token for some of the tokenizers +) + +IGNORE_INDEX = -100 +SYSTEM_TOKEN = "System" TYPE_INSTRUCTION = { 'TEXT_TO_VALUE': "", @@ -36,6 +36,56 @@ } +def _get_header_conversation_type_mask_role(source, special_tokens): + END_SIGNAL = special_tokens['end_of_turn'] + END_NAME_SIGNAL = special_tokens['end_of_name'] + + data_type = None + if 'type' in source: + data_type = source['type'] + if data_type is not None: + assert data_type in TYPE_INSTRUCTION, f"source type {data_type} not supported" + # add end signal and concatenate together + conversation = source['system'] + if data_type is not None: + if TYPE_INSTRUCTION[data_type] != '': + conversation = conversation + '\n' + TYPE_INSTRUCTION[data_type] + mask_role = source.get('mask', 'User') + header = f"{special_tokens['system_turn_start']}{SYSTEM_TOKEN}{END_NAME_SIGNAL}{conversation}{END_SIGNAL}" + conversation = _add_speaker_and_signal(header, source['conversations'], mask_role, data_type, special_tokens) + return header, conversation, data_type, mask_role + + +def get_prompt_template_example(special_tokens): + source = { + 'system': '{system message}', + 'conversations': [ + {'from': 'User', 'value': '{turn 1 user message}', 'label': None}, + {'from': 'Assistant', 'value': '{turn 1 assistant message}', 'label': '{turn 1 assistant label}'}, + {'from': 'User', 'value': '{turn 2 user message}', 'label': None}, + {'from': 'Assistant', 'value': '{turn 2 assistant message}', 'label': '{turn 2 assistant label}'}, + ], + "mask": "User", + "type": "VALUE_TO_TEXT", + } + _, conversation, _, _ = _get_header_conversation_type_mask_role(source, special_tokens) + return conversation + + +def identify_start_index_of_subsequence(subsequence, sequence): + """ find the location of the small tensor in the large tensor. + e.g. small = [1,3], large = [2,3,1,3], returns 2 + small = [3,2], large = [2,3,1,3], returns -1 + Args: + small (tensor): small tensor + large (tensor): large tensor + """ + for i in range(sequence.size(0) - subsequence.size(0) + 1): + if torch.equal(sequence[i : i + subsequence.size(0)], subsequence): + return i + return -1 + + def _mask_targets( target, tokenized_lens, @@ -45,8 +95,10 @@ def _mask_targets( tokenizer, mask_role, gtype, - extra_id_2_token_id, - new_line_token_id, + name_end_token_ids, + special_tokens, + label_start_ids, + num_turn_start_tokens, ): """ This function masks the tokens so the loss is computed only on the non-masked role's responses. For 'TEXT_TO_VALUE' type, the loss is computed on the value attributes. @@ -60,68 +112,88 @@ def _mask_targets( tokenizer (TokenizerSpec): tokenizer object mask_role (str): the speaker id to be masked from loss computation gtype (str): either 'TEXT_TO_VALUE' or 'VALUE_TO_TEXT' - extra_id_2_token_id (int): token id - new_line_token_id (int): new line token id - + name_end_token_ids (int): end of name token ids + special_tokens (dict): special tokens used for the chat prompt. It has the keys: system_turn_start, turn_start, label_start, end_of_turn + label_start_ids (list): list of label start token ids, + num_turn_start_tokens (int): number of tokens of the turn_start str """ + TURN_TOKEN = special_tokens['turn_start'] + END_NAME_SIGNAL = special_tokens['end_of_name'] + label_start_ids = torch.tensor(label_start_ids) + name_end_token_ids = torch.tensor(name_end_token_ids) + cur_idx = header_len tgt_len = target.shape[0] for i, (tokenized_len, speaker, s_id) in enumerate(zip(tokenized_lens, speakers, s_ids)): # note, sentence piece will add extra empty token in front. has to compute the diff - id1 = tokenizer.text_to_ids("") - id2 = tokenizer.text_to_ids("" + TURN_TOKEN + speaker + END_NAME_SIGNAL) - skip_name_len = len(id2) - len(id1) - if extra_id_2_token_id is None: - raise ValueError("extra_id_2 is not in the vocabulary") - if (s_id == extra_id_2_token_id).any().item(): + id1 = tokenizer.text_to_ids(PREFIX_STR) + id2 = tokenizer.text_to_ids(PREFIX_STR + TURN_TOKEN + speaker + END_NAME_SIGNAL) + skip_name_len = len(id2) - len( + id1 + ) # s_ids[:skip_name_len] is the name part of the prompt 'TURN_TOKEN + speaker + END_NAME_SIGNAL' + # get the position of the label start string in this turn + location = identify_start_index_of_subsequence(label_start_ids, s_id) + + if location >= 0: + # if it contains the label start tokens if gtype == 'VALUE_TO_TEXT': - # if contains the token - assert skip_name_len == torch.where((s_id == extra_id_2_token_id))[0].item() - # find new line token id 14 - more_skip_len = torch.where((s_id[skip_name_len:] == new_line_token_id))[0][0].item() + 1 + # handles the case that condition on labels to generate respone + # the next token after the name part of the prompt is the beginning of the label start tokens + assert skip_name_len == location + # find the first new line token after the label part, which indicates the end of the whole label string + # newline_loc = torch.where((s_id[skip_name_len:] == name_end_token_ids))[0] + newline_loc = identify_start_index_of_subsequence(name_end_token_ids, s_id[skip_name_len:]) + if newline_loc < 0: + # cannot find new line token, which means the the whole turn is just a partial label string. Mask the whole turn + target[cur_idx : cur_idx + tokenized_len] = IGNORE_INDEX + continue + # skip the label part and the new line token + more_skip_len = newline_loc + len(name_end_token_ids) + # skip the name part and the label part skip_name_len += more_skip_len elif gtype == 'TEXT_TO_VALUE': - skip_name_len = torch.where((s_id == extra_id_2_token_id))[0].item() + 1 + # handles the case that condition on response to generate label + # skip the name part, response and the label start tokens part, the remainder is the label string without label start, e.g. 'quality:9,toxicity:8...' + skip_name_len = location + len(label_start_ids) if cur_idx >= tgt_len: break elif cur_idx + tokenized_len < tgt_len: - # Check whether the mask is applied to the correct position, the first token is turn token: - # s_id[2:] skips the artifact empty token and the turn token - # target[cur_idx + 1:cur_idx + tokenized_len] skip the turn token + # Check whether the mask is applied to the correct position, the first token is turn start tokens if not torch.equal(target[cur_idx + 1 : cur_idx + tokenized_len], s_id[1:]): logging.warning("a sentence mismatches the corresponding piece " "in the conversation") if i == 0 and (gtype == 'VALUE_TO_TEXT' or gtype is None): - # mask the first turn completely to provide at least one turn as context + # mask the first turn completely to provide at least one turn as context for the rest target[cur_idx : cur_idx + tokenized_len] = IGNORE_INDEX elif speaker == mask_role and i == 1 and gtype == 'TEXT_TO_VALUE': - # leave the first human tag unmasked - target[cur_idx + 1 : cur_idx + tokenized_len] = IGNORE_INDEX + # leave the first turn start tag unmasked, servers severs as the end of turn signal + target[cur_idx + num_turn_start_tokens : cur_idx + tokenized_len] = IGNORE_INDEX elif speaker == mask_role and (i > 1): - # leave the first human tag unmasked - target[cur_idx + 1 : cur_idx + tokenized_len] = IGNORE_INDEX + # leave the first turn start tag unmasked, which severs as the end of turn signal + target[cur_idx + num_turn_start_tokens : cur_idx + tokenized_len] = IGNORE_INDEX elif speaker == mask_role and (i <= 1): # mask out everything in the second turn target[cur_idx : cur_idx + tokenized_len] = IGNORE_INDEX else: - # mask up to the name end, need to remove one as skip name has an extra artifact empty token + # mask up to name part, label part for VALUE_TO_TEXT, or name part, response and label start tokens for TEXT_TO_VALUE, or just the name part if gtype is None target[cur_idx : cur_idx + skip_name_len] = IGNORE_INDEX cur_idx += tokenized_len -def cannonical_form_formater(cannoical_form): - return f'{cannoical_form}\n' - - -def response_value_formater(label): +def response_value_formater(label, label_start, end_signal): if isinstance(label, str): - return '' + label + '\n' + return label_start + label + end_signal elif label is None: return '' else: raise ValueError(f'Unknown label type {type(label)}, only str type is supported') -def _add_speaker_and_signal(header, source, mask_role, gtype): +def _add_speaker_and_signal(header, source, mask_role, gtype, special_tokens): + TURN_TOKEN = special_tokens['turn_start'] + END_SIGNAL = special_tokens['end_of_turn'] + LABEL_START = special_tokens['label_start'] + END_NAME_SIGNAL = special_tokens['end_of_name'] + """Add speaker and start/end signal on each round.""" BEGIN_SIGNAL = "" conversation = header @@ -138,7 +210,11 @@ def _add_speaker_and_signal(header, source, mask_role, gtype): + role_token + sentence_from + END_NAME_SIGNAL - + (response_value_formater(sentence['label']) if 'label' in sentence else '') + + ( + response_value_formater(sentence['label'], LABEL_START, END_NAME_SIGNAL) + if 'label' in sentence + else '' + ) + sentence["value"] + END_SIGNAL ) @@ -150,7 +226,11 @@ def _add_speaker_and_signal(header, source, mask_role, gtype): + END_NAME_SIGNAL + sentence["value"] + END_SIGNAL - + (response_value_formater(sentence['label']) if 'label' in sentence else '') + + ( + response_value_formater(sentence['label'], LABEL_START, END_NAME_SIGNAL) + if 'label' in sentence + else '' + ) ) else: raise ValueError( @@ -163,7 +243,14 @@ def _add_speaker_and_signal(header, source, mask_role, gtype): return conversation -def preprocess(source: dict, tokenizer: TokenizerSpec, extra_id_2_token_id: int, new_line_token_id: int): +def preprocess( + source: dict, + tokenizer: TokenizerSpec, + name_end_token_ids: int, + label_start_ids: list, + special_tokens: dict, + num_turn_start_tokens: int, +): """ Given a conversation list. This transform: 1. Add signal '### ' at the beginning each sentence, with end signal '\n'; @@ -171,36 +258,23 @@ def preprocess(source: dict, tokenizer: TokenizerSpec, extra_id_2_token_id: int, 3. Tokenize the concatenated conversation; 4. Make a deepcopy as the target. Mask human words with IGNORE_INDEX. """ - data_type = None - if 'type' in source: - data_type = source['type'] - assert data_type in TYPE_INSTRUCTION, f"source type {data_type} not supported" - # add end signal and concatenate together - conversation = source['system'] - if data_type is not None: - if TYPE_INSTRUCTION[data_type] != '': - conversation = conversation + '\n' + TYPE_INSTRUCTION[data_type] - mask_role = source.get('mask', 'User') - header = f"{SYSTEM_TOKEN}{conversation}" - conversation = _add_speaker_and_signal(header, source['conversations'], mask_role, data_type) + header, conversation, data_type, mask_role = _get_header_conversation_type_mask_role(source, special_tokens) # tokenize conversations input_ids = tokenizer.text_to_ids(conversation) target = copy.deepcopy(input_ids) - header_len = len(tokenizer.text_to_ids(header)) + header_tokens = tokenizer.text_to_ids(header) + header_len = len(header_tokens) ids = [] tokenized_lens = [] + assert torch.equal(torch.tensor(target[:header_len]), torch.tensor(header_tokens)) for s in source['conversations']: - if isinstance(tokenizer, SentencePieceTokenizer): - tokenized_sentence = tokenizer.text_to_ids(s["value"]) - ids.append(torch.tensor(tokenized_sentence)[1:]) - # remove one token as it adds an empty token in front - tokenized_lens.append(len(tokenized_sentence) - 1) - else: - tokenized_sentence = tokenizer.text_to_ids(s["value"]) - ids.append(torch.tensor(tokenized_sentence)) - # remove one token as it adds an empty token in front - tokenized_lens.append(len(tokenized_sentence)) + # hack to remove the extra empty token in front + id1 = tokenizer.text_to_ids(PREFIX_STR + s["value"]) + id2 = tokenizer.text_to_ids(PREFIX_STR) + tokenized_sentence = id1[len(id2) :] + ids.append(torch.tensor(tokenized_sentence)) + tokenized_lens.append(len(tokenized_sentence)) speakers = [sentence["from"] for sentence in source['conversations']] assert mask_role in speakers, "mask role not in the conversation" target = torch.LongTensor(target) @@ -216,8 +290,10 @@ def preprocess(source: dict, tokenizer: TokenizerSpec, extra_id_2_token_id: int, tokenizer, mask_role, data_type, - extra_id_2_token_id, - new_line_token_id, + name_end_token_ids, + special_tokens, + label_start_ids, + num_turn_start_tokens, ) mask = (target != IGNORE_INDEX).bool() assert mask.sum().item() != 0, "mask is empty" @@ -228,14 +304,6 @@ def preprocess(source: dict, tokenizer: TokenizerSpec, extra_id_2_token_id: int, return dict(input_ids=input_ids, mask=mask, context_ids=context_ids, answer_ids=answer_ids) -def _check_token_in_vocab(tokenizer, token): - ids = tokenizer.text_to_ids(token) - if isinstance(tokenizer, SentencePieceTokenizer): - return len(ids) == 2 - else: - return len(ids) == 1 - - class GPTSFTChatDataset(GPTSFTDataset): def _maybe_validate_prompt_template(self): pass @@ -243,22 +311,20 @@ def _maybe_validate_prompt_template(self): def _build_samples_mapping(self): super()._build_samples_mapping() assert hasattr(self.tokenizer, "vocab"), "tokenizer should have vocab property, not supported" - assert _check_token_in_vocab( - self.tokenizer, '' - ), " not in the tokenizer vocab. not supported" - assert _check_token_in_vocab( - self.tokenizer, '' - ), " not in the tokenizer vocab. not supported" - # calcuilate id value - if _check_token_in_vocab(self.tokenizer, ''): - ids_1 = self.tokenizer.text_to_ids('') - ids_2 = self.tokenizer.text_to_ids('') - self.extra_id_2_token_id = ids_1[len(ids_2) :][0] - else: - self.extra_id_2_token_id = None - ids_1 = self.tokenizer.text_to_ids('\n') - ids_2 = self.tokenizer.text_to_ids('') - self.new_line_token_id = ids_1[len(ids_2) :][0] + LABEL_START = self.special_tokens['label_start'] + END_NAME_SIGNAL = self.special_tokens['end_of_name'] + + id1 = self.tokenizer.text_to_ids(PREFIX_STR) + id2 = self.tokenizer.text_to_ids(PREFIX_STR + LABEL_START) + self.label_start_tokens = id2[len(id1) :] + + id1 = self.tokenizer.text_to_ids(PREFIX_STR + END_NAME_SIGNAL) + id2 = self.tokenizer.text_to_ids(PREFIX_STR) + self.name_end_token_ids = id1[len(id2) :] + + id1 = self.tokenizer.text_to_ids(PREFIX_STR + self.special_tokens['turn_start']) + id2 = self.tokenizer.text_to_ids(PREFIX_STR) + self.num_turn_start_tokens = len(id1) - len(id2) def _process_example(self, example): """ @@ -266,7 +332,14 @@ def _process_example(self, example): Truncation is carried out when needed, but it is performed only on the prompt side. BOS, EOS, and SEP, are added if specified. """ - result = preprocess(example, self.tokenizer, self.extra_id_2_token_id, self.new_line_token_id) + result = preprocess( + example, + self.tokenizer, + self.name_end_token_ids, + self.label_start_tokens, + self.special_tokens, + self.num_turn_start_tokens, + ) # store metadata in dataset, in case user may have keys required in the prediction json files metadata = {k: v for k, v in example.items() if k not in ['conversations']} diff --git a/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_dataset.py b/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_dataset.py index 101201ef7536..9c6e50f5e43f 100644 --- a/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_dataset.py +++ b/nemo/collections/nlp/data/language_modeling/megatron/gpt_sft_dataset.py @@ -13,10 +13,14 @@ # limitations under the License. import re -from typing import List, Optional +from typing import List, Mapping, Optional +import datasets import numpy as np import torch + +# hack to avoid the "not enough disk space" error in some slurm cluster +datasets.builder.has_sufficient_disk_space = lambda needed_bytes, directory='.': True from datasets import load_dataset from nemo.collections.common.tokenizers.tokenizer_spec import TokenizerSpec @@ -52,6 +56,7 @@ def __init__( memmap_workers: Optional[int] = None, hf_dataset: bool = False, truncation_method: str = 'right', + special_tokens: Optional[Mapping[str, str]] = None, # special tokens, a dictory of {token_type: token} ): """ file_path: Path to a JSONL GPT supervised fine-tuning dataset. Data is formatted as multiple JSON lines with each line formatted as follows. {'input': 'John von Neumann\nVon Neumann made fundamental contributions .... Q: What did the math of artificial viscosity do?', 'output': 'smoothed the shock transition without sacrificing basic physics'} @@ -73,6 +78,7 @@ def __init__( prompt_template: Prompt template to inject via an fstring. Formatted like Q: {context_key}\n\nA: {label_key} hf_dataset: Whether to load the json file with the HuggingFace dataset. otherwise, will load the jsonl file with the JSONLMemMapDataset. truncation_method: Truncation from which position. Options: ['left', 'right'] + special_tokens: special tokens for the chat prompts, a dictionary of {token_type: token}. Default: {'system_turn_start': '', 'turn_start': '', 'label_start': '', 'end_of_turn': '\n', "end_of_name": "\n"} """ self.tokenizer = tokenizer self.file_path = file_path @@ -93,6 +99,16 @@ def __init__( self.virtual_tokens = virtual_tokens self.tokens_to_generate = tokens_to_generate self.truncation_method = truncation_method + if special_tokens is None: + self.special_tokens = { + "system_turn_start": "", + "turn_start": "", + "label_start": "", + "end_of_turn": "\n", + "end_of_name": "\n", + } + else: + self.special_tokens = special_tokens if hf_dataset: self.indexed_dataset = load_dataset( diff --git a/nemo/collections/nlp/models/language_modeling/megatron_gpt_sft_model.py b/nemo/collections/nlp/models/language_modeling/megatron_gpt_sft_model.py index 8f2a108fcfc0..84a1f52d1391 100644 --- a/nemo/collections/nlp/models/language_modeling/megatron_gpt_sft_model.py +++ b/nemo/collections/nlp/models/language_modeling/megatron_gpt_sft_model.py @@ -33,7 +33,6 @@ from nemo.collections.nlp.models.language_modeling.megatron_gpt_model import MegatronGPTModel from nemo.collections.nlp.modules.common.megatron.utils import get_iterator_k_split from nemo.collections.nlp.modules.common.text_generation_utils import generate, get_computeprob_response - from nemo.collections.nlp.parts.mixins.nlp_adapter_mixins import NLPAdapterModelMixin from nemo.collections.nlp.parts.utils_funcs import get_last_rank from nemo.utils import AppState, logging @@ -296,9 +295,11 @@ def _build_dataset(self, data_cfg, is_train=True): truncation_method=data_cfg.get( 'truncation_method', 'right' ), # used to choose truncation method. Options: ['random', 'left', 'right'] + special_tokens=self.cfg.data.get( + 'chat_prompt_tokens', None + ), # special tokens for the chat prompts, a dictionary of {token_type: token}. Default: {'system_turn_start': '', 'turn_start': '', 'label_start': '', 'end_of_turn': '\n', "end_of_name": "\n"} ) datasets.append(dataset) - if is_train: dataset = BlendableDataset( datasets=datasets, weights=data_cfg.concat_sampling_probabilities, size=num_train_samples_after_blend diff --git a/scripts/nlp_language_modeling/sft/data_clean.py b/scripts/nlp_language_modeling/sft/data_clean.py index 8c67aa2e3bcd..362f7edeeb3b 100644 --- a/scripts/nlp_language_modeling/sft/data_clean.py +++ b/scripts/nlp_language_modeling/sft/data_clean.py @@ -41,7 +41,7 @@ def data_clean( ) if library == 'huggingface': tokenizer.add_special_tokens({'additional_special_tokens': ['', '', '']}) - d = GPTSFTChatDataset(dataset_file, tokenizer, seq_len, 1) + d = GPTSFTChatDataset(dataset_file, tokenizer, seq_len, 1, hf_dataset=True) total_records = len(d) removed_ids = set() for i in range(total_records): diff --git a/scripts/nlp_language_modeling/sft/preprocessing.py b/scripts/nlp_language_modeling/sft/preprocessing.py index 7a08e055543d..175187a279bc 100644 --- a/scripts/nlp_language_modeling/sft/preprocessing.py +++ b/scripts/nlp_language_modeling/sft/preprocessing.py @@ -80,11 +80,12 @@ def parse_conversations(tree_obj): raise ValueError(f'unknown role {prompt_obj["role"]}') turn = {'value': prompt_obj['text'], 'from': role} if 'labels' in prompt_obj: - turn['human_labels'] = prompt_obj['labels'] - for key in turn['human_labels']: - value_set = label_values.get(key, set()) - value_set.add(turn['human_labels'][key]['value']) - label_values[key] = value_set + # remove human labels + # turn['human_labels'] = prompt_obj['labels'] + # for key in turn['human_labels']: + # value_set = label_values.get(key, set()) + # value_set.add(turn['human_labels'][key]['value']) + # label_values[key] = value_set turn['label'] = encode_labels(prompt_obj['labels']) if 'lang' in prompt_obj: turn['lang'] = prompt_obj['lang'].split('-')[0] diff --git a/tests/collections/nlp/test_chat_sft_dataset.py b/tests/collections/nlp/test_chat_sft_dataset.py index 36d00e3108d7..f7bcecaa3c28 100644 --- a/tests/collections/nlp/test_chat_sft_dataset.py +++ b/tests/collections/nlp/test_chat_sft_dataset.py @@ -16,13 +16,18 @@ import json import os import random +from functools import partial import pytest -from nemo.collections.nlp.data.language_modeling.megatron.gpt_sft_chat_dataset import GPTSFTChatDataset +from nemo.collections.nlp.data.language_modeling.megatron.gpt_sft_chat_dataset import ( + GPTSFTChatDataset, + get_prompt_template_example, +) from nemo.collections.nlp.modules.common.tokenizer_utils import get_nmt_tokenizer TOKENIZER_FILE_43B = '/home/TestData/nlp/megatron_sft/tokenizer.model' +TOKENIZER_FILE_Llama2 = '/home/TestData/nlp/megatron_sft/llama2_tokenizer.model' MERGE_FILE = '/home/TestData/nlp/megatron_sft/merges.txt' VOCAB_FILE = '/home/TestData/nlp/megatron_sft/vocab.json' @@ -54,7 +59,7 @@ def create_data_points(mask_user, turn_num, records, temp_file, t2v, label=True) with open(temp_file, 'w', encoding='utf-8') as f: for r in range(records): record = {} - record['system'] = 'a chat\n\n' + record['system'] = 'a chat' record['type'] = 'TEXT_TO_VALUE' if t2v else 'VALUE_TO_TEXT' record['mask'] = 'User' if mask_user else 'Assistant' turns = [] @@ -74,244 +79,377 @@ def create_data_points(mask_user, turn_num, records, temp_file, t2v, label=True) class TestGPTSFTChatDataset: @classmethod def setup_class(cls): - pass + cls.special_tokens = { + "system_turn_start": "", + "turn_start": "", + "label_start": "", + "end_of_turn": "\n", + "end_of_name": "\n", + } + cls.suffix = cls.special_tokens['end_of_turn'] + cls.special_tokens['turn_start'] + cls.label_suffix = cls.special_tokens['end_of_name'] + cls.special_tokens['turn_start'] - @pytest.mark.unit - def test_43B_tokenizer_mask_user(self): + def _mask_user_test(self, tokenizer, ids_to_text): random.seed(5) temp_file = '/tmp/test_file.jsonl' turn_num = 5 records = 5 try: data_points = create_data_points(True, turn_num, records, temp_file, t2v=False) - tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_43B) - d = GPTSFTChatDataset(temp_file, tokenizer, 4096, 1, index_mapping_dir='/tmp/', hf_dataset=True) + d = GPTSFTChatDataset( + temp_file, + tokenizer, + 4096, + 1, + index_mapping_dir='/tmp/', + hf_dataset=True, + special_tokens=self.special_tokens, + ) for i in range(len(d)): result = d[i] input_ids = result['input_ids'] mask = result['mask'] - text = tokenizer.ids_to_text(input_ids[mask].tolist()) + text = ids_to_text(input_ids[mask].tolist()) expected_text = '' for j in range(1, turn_num, 2): - expected_text += data_points[i]['conversations'][j]['value'] + '\n' + '' + expected_text += data_points[i]['conversations'][j]['value'] + self.suffix assert text == expected_text finally: os.remove(temp_file) - @pytest.mark.unit - def test_43B_tokenizer_mask_assistant(self): + def _mask_assistant_test(self, tokenizer, ids_to_text): random.seed(3) temp_file = '/tmp/test_file.jsonl' turn_num = 5 records = 5 try: data_points = create_data_points(False, turn_num, records, temp_file, t2v=False) - tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_43B) - d = GPTSFTChatDataset(temp_file, tokenizer, 4096, 1, index_mapping_dir='/tmp/', hf_dataset=True) + d = GPTSFTChatDataset( + temp_file, + tokenizer, + 4096, + 1, + index_mapping_dir='/tmp/', + hf_dataset=True, + special_tokens=self.special_tokens, + ) for i in range(len(d)): result = d[i] input_ids = result['input_ids'] mask = result['mask'] - text = tokenizer.ids_to_text(input_ids[mask].tolist()) + text = ids_to_text(input_ids[mask].tolist()) expected_text = '' for j in range(2, turn_num, 2): - expected_text += data_points[i]['conversations'][j]['value'] + '\n' + '' + expected_text += data_points[i]['conversations'][j]['value'] + self.suffix assert text == expected_text finally: os.remove(temp_file) - @pytest.mark.unit - def test_43B_tokenizer_mask_user_t2v(self): + def _mask_user_t2v_test(self, tokenizer, ids_to_text): random.seed(5) temp_file = '/tmp/test_file.jsonl' turn_num = 5 records = 5 try: data_points = create_data_points(True, turn_num, records, temp_file, t2v=True) - tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_43B) - d = GPTSFTChatDataset(temp_file, tokenizer, 4096, 1, index_mapping_dir='/tmp/', hf_dataset=True) + d = GPTSFTChatDataset( + temp_file, + tokenizer, + 4096, + 1, + index_mapping_dir='/tmp/', + hf_dataset=True, + special_tokens=self.special_tokens, + ) for i in range(len(d)): result = d[i] input_ids = result['input_ids'] mask = result['mask'] - text = tokenizer.ids_to_text(input_ids[mask].tolist()) + text = ids_to_text(input_ids[mask].tolist()) expected_text = '' for j in range(1, turn_num, 2): - expected_text += data_points[i]['conversations'][j]['label'] + '\n' + '' + expected_text += data_points[i]['conversations'][j]['label'] + self.label_suffix assert text == expected_text finally: os.remove(temp_file) - @pytest.mark.unit - def test_43B_tokenizer_mask_assistant_t2v(self): + def _mask_assistant_t2v_test(self, tokenizer, ids_to_text): random.seed(5) temp_file = '/tmp/test_file.jsonl' turn_num = 5 records = 5 try: data_points = create_data_points(False, turn_num, records, temp_file, t2v=True) - tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_43B) - d = GPTSFTChatDataset(temp_file, tokenizer, 4096, 1, index_mapping_dir='/tmp/', hf_dataset=True) + d = GPTSFTChatDataset( + temp_file, + tokenizer, + 4096, + 1, + index_mapping_dir='/tmp/', + hf_dataset=True, + special_tokens=self.special_tokens, + ) for i in range(len(d)): result = d[i] input_ids = result['input_ids'] mask = result['mask'] - text = tokenizer.ids_to_text(input_ids[mask].tolist()) + text = ids_to_text(input_ids[mask].tolist()) expected_text = '' for j in range(0, turn_num, 2): - expected_text += data_points[i]['conversations'][j]['label'] + '\n' + '' + expected_text += data_points[i]['conversations'][j]['label'] + self.label_suffix assert text == expected_text finally: os.remove(temp_file) - @pytest.mark.unit - def test_mpt_tokenizer_mask_user(self): + def _mask_user_nolabel_test(self, tokenizer, ids_to_text): random.seed(5) temp_file = '/tmp/test_file.jsonl' turn_num = 5 records = 5 try: - data_points = create_data_points(True, turn_num, records, temp_file, t2v=False) - tokenizer = get_nmt_tokenizer( - library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True - ) - tokenizer.add_special_tokens( - {'additional_special_tokens': ['', '', '']} + data_points = create_data_points(True, turn_num, records, temp_file, t2v=False, label=False) + d = GPTSFTChatDataset( + temp_file, + tokenizer, + 4096, + 1, + index_mapping_dir='/tmp/', + hf_dataset=True, + special_tokens=self.special_tokens, ) - d = GPTSFTChatDataset(temp_file, tokenizer, 4096, 1, index_mapping_dir='/tmp/', hf_dataset=True) for i in range(len(d)): result = d[i] input_ids = result['input_ids'] mask = result['mask'] - text = ids_to_text(tokenizer, input_ids[mask].tolist()) + text = ids_to_text(input_ids[mask].tolist()) expected_text = '' for j in range(1, turn_num, 2): - expected_text += data_points[i]['conversations'][j]['value'] + '\n' + '' + expected_text += data_points[i]['conversations'][j]['value'] + self.suffix assert text == expected_text finally: os.remove(temp_file) - @pytest.mark.unit - def test_mpt_tokenizer_mask_assistant(self): + def _mask_assistant_nolabel_test(self, tokenizer, ids_to_text): random.seed(3) temp_file = '/tmp/test_file.jsonl' turn_num = 5 records = 5 try: - data_points = create_data_points(False, turn_num, records, temp_file, t2v=False) - tokenizer = get_nmt_tokenizer( - library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True - ) - tokenizer.add_special_tokens( - {'additional_special_tokens': ['', '', '']} + data_points = create_data_points(False, turn_num, records, temp_file, t2v=False, label=False) + d = GPTSFTChatDataset( + temp_file, + tokenizer, + 4096, + 1, + index_mapping_dir='/tmp/', + hf_dataset=True, + special_tokens=self.special_tokens, ) - d = GPTSFTChatDataset(temp_file, tokenizer, 4096, 1, index_mapping_dir='/tmp/', hf_dataset=True) for i in range(len(d)): result = d[i] input_ids = result['input_ids'] mask = result['mask'] - text = ids_to_text(tokenizer, input_ids[mask].tolist()) + text = ids_to_text(input_ids[mask].tolist()) expected_text = '' for j in range(2, turn_num, 2): - expected_text += data_points[i]['conversations'][j]['value'] + '\n' + '' + expected_text += data_points[i]['conversations'][j]['value'] + self.suffix assert text == expected_text finally: os.remove(temp_file) - @pytest.mark.unit - def test_mpt_tokenizer_mask_user_t2v(self): + def _test_example_prompt(self, tokenizer): random.seed(5) - temp_file = '/tmp/test_file.jsonl' - turn_num = 5 - records = 5 - try: - data_points = create_data_points(True, turn_num, records, temp_file, t2v=True) - tokenizer = get_nmt_tokenizer( - library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True + conv = get_prompt_template_example(self.special_tokens) + expected = ( + self.special_tokens['system_turn_start'] + + 'System' + + self.special_tokens['end_of_name'] + + '{system message}' + + self.special_tokens['end_of_turn'] + ) + for turn in range(2): + expected += ( + self.special_tokens['turn_start'] + + 'User' + + self.special_tokens['end_of_name'] + + f'{{turn {turn + 1} user message}}' + + self.special_tokens['end_of_turn'] ) - tokenizer.add_special_tokens( - {'additional_special_tokens': ['', '', '']} + expected += self.special_tokens['turn_start'] + 'Assistant' + self.special_tokens['end_of_name'] + expected += ( + self.special_tokens['label_start'] + + f'{{turn {turn + 1} assistant label}}' + + self.special_tokens['end_of_name'] ) - d = GPTSFTChatDataset(temp_file, tokenizer, 4096, 1, index_mapping_dir='/tmp/', hf_dataset=True) - for i in range(len(d)): - result = d[i] - input_ids = result['input_ids'] - mask = result['mask'] - text = ids_to_text(tokenizer, input_ids[mask].tolist()) - expected_text = '' - for j in range(1, turn_num, 2): - expected_text += data_points[i]['conversations'][j]['label'] + '\n' + '' - assert text == expected_text - finally: - os.remove(temp_file) + expected += f'{{turn {turn + 1} assistant message}}' + self.special_tokens['end_of_turn'] + expected += self.special_tokens['turn_start'] + assert conv == expected @pytest.mark.unit - def test_mpt_tokenizer_mask_assistant_t2v(self): - random.seed(5) - temp_file = '/tmp/test_file.jsonl' - turn_num = 5 - records = 5 - try: - data_points = create_data_points(False, turn_num, records, temp_file, t2v=True) - tokenizer = get_nmt_tokenizer( - library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True - ) - tokenizer.add_special_tokens( - {'additional_special_tokens': ['', '', '']} - ) - d = GPTSFTChatDataset(temp_file, tokenizer, 4096, 1, index_mapping_dir='/tmp/', hf_dataset=True) - for i in range(len(d)): - result = d[i] - input_ids = result['input_ids'] - mask = result['mask'] - text = ids_to_text(tokenizer, input_ids[mask].tolist()) - expected_text = '' - for j in range(0, turn_num, 2): - expected_text += data_points[i]['conversations'][j]['label'] + '\n' + '' - assert text == expected_text - finally: - os.remove(temp_file) + def test_43B_example_prompt(self): + tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_43B) + self._test_example_prompt(tokenizer) + + @pytest.mark.unit + def test_43B_tokenizer_mask_user(self): + tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_43B) + self._mask_user_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_43B_tokenizer_mask_assistant(self): + tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_43B) + self._mask_assistant_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_43B_tokenizer_mask_user_t2v(self): + tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_43B) + self._mask_user_t2v_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_43B_tokenizer_mask_assistant_t2v(self): + tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_43B) + self._mask_assistant_t2v_test(tokenizer, tokenizer.ids_to_text) @pytest.mark.unit def test_43B_tokenizer_mask_user_nolabel(self): - random.seed(5) - temp_file = '/tmp/test_file.jsonl' - turn_num = 5 - records = 5 - try: - data_points = create_data_points(True, turn_num, records, temp_file, t2v=False, label=False) - tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_43B) - d = GPTSFTChatDataset(temp_file, tokenizer, 4096, 1, index_mapping_dir='/tmp/', hf_dataset=True) - for i in range(len(d)): - result = d[i] - input_ids = result['input_ids'] - mask = result['mask'] - text = tokenizer.ids_to_text(input_ids[mask].tolist()) - expected_text = '' - for j in range(1, turn_num, 2): - expected_text += data_points[i]['conversations'][j]['value'] + '\n' + '' - assert text == expected_text - finally: - os.remove(temp_file) + tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_43B) + self._mask_user_nolabel_test(tokenizer, tokenizer.ids_to_text) @pytest.mark.unit def test_43B_tokenizer_mask_assistant_nolabel(self): - random.seed(3) - temp_file = '/tmp/test_file.jsonl' - turn_num = 5 - records = 5 - try: - data_points = create_data_points(False, turn_num, records, temp_file, t2v=False, label=False) - tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_43B) - d = GPTSFTChatDataset(temp_file, tokenizer, 4096, 1, index_mapping_dir='/tmp/', hf_dataset=True) - for i in range(len(d)): - result = d[i] - input_ids = result['input_ids'] - mask = result['mask'] - text = tokenizer.ids_to_text(input_ids[mask].tolist()) - expected_text = '' - for j in range(2, turn_num, 2): - expected_text += data_points[i]['conversations'][j]['value'] + '\n' + '' - assert text == expected_text - finally: - os.remove(temp_file) + tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_43B) + self._mask_assistant_nolabel_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_mpt_tokenizer_mask_user(self): + tokenizer = get_nmt_tokenizer( + library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True + ) + tokenizer.add_special_tokens({'additional_special_tokens': ['', '', '']}) + self._mask_user_test(tokenizer, partial(ids_to_text, tokenizer)) + + @pytest.mark.unit + def test_mpt_tokenizer_mask_assistant(self): + tokenizer = get_nmt_tokenizer( + library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True + ) + tokenizer.add_special_tokens({'additional_special_tokens': ['', '', '']}) + self._mask_assistant_test(tokenizer, partial(ids_to_text, tokenizer)) + + @pytest.mark.unit + def test_mpt_tokenizer_mask_user_t2v(self): + tokenizer = get_nmt_tokenizer( + library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True + ) + tokenizer.add_special_tokens({'additional_special_tokens': ['', '', '']}) + self._mask_user_t2v_test(tokenizer, partial(ids_to_text, tokenizer)) + + @pytest.mark.unit + def test_mpt_tokenizer_mask_assistant_t2v(self): + tokenizer = get_nmt_tokenizer( + library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True + ) + tokenizer.add_special_tokens({'additional_special_tokens': ['', '', '']}) + self._mask_assistant_t2v_test(tokenizer, partial(ids_to_text, tokenizer)) + + @pytest.mark.unit + def test_mpt_tokenizer_mask_user_nolabel(self): + tokenizer = get_nmt_tokenizer( + library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True + ) + tokenizer.add_special_tokens({'additional_special_tokens': ['', '', '']}) + self._mask_user_nolabel_test(tokenizer, partial(ids_to_text, tokenizer)) + + @pytest.mark.unit + def test_mpt_tokenizer_mask_assistant_nolabel(self): + tokenizer = get_nmt_tokenizer( + library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True + ) + tokenizer.add_special_tokens({'additional_special_tokens': ['', '', '']}) + self._mask_assistant_nolabel_test(tokenizer, partial(ids_to_text, tokenizer)) + + @pytest.mark.unit + def test_llama2_tokenizer_mask_user(self): + tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_Llama2) + self._mask_user_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_llama2_tokenizer_mask_assistant(self): + tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_Llama2) + self._mask_assistant_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_llama2_tokenizer_mask_user_t2v(self): + tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_Llama2) + self._mask_user_t2v_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_llama2_tokenizer_mask_assistant_t2v(self): + tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_Llama2) + self._mask_assistant_t2v_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_llama2_tokenizer_mask_user_nolabel(self): + tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_Llama2) + self._mask_user_nolabel_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_llama2_tokenizer_mask_assistant_nolabel(self): + tokenizer = get_nmt_tokenizer(library='sentencepiece', tokenizer_model=TOKENIZER_FILE_Llama2) + self._mask_assistant_nolabel_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_normal_mpt_tokenizer_mask_user(self): + tokenizer = get_nmt_tokenizer( + library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True + ) + self._mask_user_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_normal_mpt_tokenizer_mask_assistant(self): + tokenizer = get_nmt_tokenizer( + library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True + ) + self._mask_assistant_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_normal_mpt_tokenizer_mask_user_t2v(self): + tokenizer = get_nmt_tokenizer( + library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True + ) + self._mask_user_t2v_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_normal_mpt_tokenizer_mask_assistant_t2v(self): + tokenizer = get_nmt_tokenizer( + library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True + ) + self._mask_assistant_t2v_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_normal_mpt_tokenizer_mask_user_nolabel(self): + tokenizer = get_nmt_tokenizer( + library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True + ) + self._mask_user_nolabel_test(tokenizer, tokenizer.ids_to_text) + + @pytest.mark.unit + def test_normal_mpt_tokenizer_mask_assistant_nolabel(self): + tokenizer = get_nmt_tokenizer( + library='huggingface', model_name='gpt2', merges_file=MERGE_FILE, vocab_file=VOCAB_FILE, use_fast=True + ) + self._mask_assistant_nolabel_test(tokenizer, tokenizer.ids_to_text) + + +class TestDifferentGPTSFTChatDataset(TestGPTSFTChatDataset): + @classmethod + def setup_class(cls): + cls.special_tokens = { + "system_turn_start": "<|im_start|>", + "turn_start": "<|im_start|>", + "label_start": "<|label|>", + "end_of_turn": "<|im_end|>\n", + "end_of_name": "\n", + } + cls.suffix = cls.special_tokens['end_of_turn'] + cls.special_tokens['turn_start'] + cls.label_suffix = cls.special_tokens['end_of_name'] + cls.special_tokens['turn_start'] From 182b1aa95edaa83d136730e1dad40a7f28c1befe Mon Sep 17 00:00:00 2001 From: Sasha Meister Date: Tue, 10 Oct 2023 14:38:26 +0000 Subject: [PATCH 111/112] Added References Signed-off-by: Sasha Meister --- .../nlp/nemo_megatron/flash_attention.rst | 10 ++- .../nemo_megatron/positional_embeddings.rst | 30 ++++--- docs/source/nlp/nlp_all.bib | 81 +++++++++++++++++++ 3 files changed, 109 insertions(+), 12 deletions(-) diff --git a/docs/source/nlp/nemo_megatron/flash_attention.rst b/docs/source/nlp/nemo_megatron/flash_attention.rst index 78b3b9c2470c..b00b7a38d63a 100644 --- a/docs/source/nlp/nemo_megatron/flash_attention.rst +++ b/docs/source/nlp/nemo_megatron/flash_attention.rst @@ -1,6 +1,6 @@ Flash attention --------------- -`FlashAttention `_ is a method designed to enhance the efficiency of Transformer models, which are widely utilized in applications such as natural language processing. Traditional Transformers are slow and consume a lot of memory, especially with long sequences, due to the quadratic time and memory complexity of self-attention. FlashAttention, an IO-aware exact attention algorithm that leverages tiling to minimize the number of memory reads/writes between the GPU's high bandwidth memory (HBM) and on-chip SRAM. This approach is designed to be more efficient in terms of IO complexity compared to standard attention mechanisms. +Flash Attention :cite:`nlp-megatron-dao2022flashattention` is a method designed to enhance the efficiency of Transformer models, which are widely utilized in applications such as natural language processing. Traditional Transformers are slow and consume a lot of memory, especially with long sequences, due to the quadratic time and memory complexity of self-attention. FlashAttention, an IO-aware exact attention algorithm that leverages tiling to minimize the number of memory reads/writes between the GPU's high bandwidth memory (HBM) and on-chip SRAM. This approach is designed to be more efficient in terms of IO complexity compared to standard attention mechanisms. GPT ^^^ @@ -18,3 +18,11 @@ To enable Flash Attention while Megatron T5 model training, modify the following model.encoder.use_flash_attention=True model.decoder.use_flash_attention=True + +References +---------- + +.. bibliography:: ../nlp_all.bib + :style: plain + :labelprefix: nlp-megatron + :keyprefix: nlp-megatron- diff --git a/docs/source/nlp/nemo_megatron/positional_embeddings.rst b/docs/source/nlp/nemo_megatron/positional_embeddings.rst index 68dce5bd119a..b8dea5280c28 100644 --- a/docs/source/nlp/nemo_megatron/positional_embeddings.rst +++ b/docs/source/nlp/nemo_megatron/positional_embeddings.rst @@ -18,38 +18,38 @@ GPT - .. code:: model.position_embedding_type='learned_absolute' - - `Absolute Position Encodings `_ are position embeddings used in Transformer-based models, added to input embeddings in the encoder and decoder sections. These encodings match the dimension of embeddings and are created using sine and cosine functions of various frequencies. Each dimension in the encoding corresponds to a sinusoid with wavelengths forming a geometric progression. + - Absolute Position Encodings :cite:`nlp-megatron-vaswani2023attention` are position embeddings used in Transformer-based models, added to input embeddings in the encoder and decoder sections. These encodings match the dimension of embeddings and are created using sine and cosine functions of various frequencies. Each dimension in the encoding corresponds to a sinusoid with wavelengths forming a geometric progression. * - **rope** - .. code:: model.position_embedding_type='rope' model.rotary_percentage=1.0 - - `Rotary Position Embedding (RoPE) `_ incorporates positional information by utilizing a rotation matrix to encode the absolute positions of tokens while maintaining relative positional relationships in self-attention formulations by leveraging the geometric properties of vectors and complex numbers, applying a rotation based on a preset non-zero constant and the relative positions of the tokens to the word embeddings. + - Rotary Position Embedding (RoPE) :cite:`nlp-megatron-su2022roformer` incorporates positional information by utilizing a rotation matrix to encode the absolute positions of tokens while maintaining relative positional relationships in self-attention formulations by leveraging the geometric properties of vectors and complex numbers, applying a rotation based on a preset non-zero constant and the relative positions of the tokens to the word embeddings. * - **alibi** - .. code:: model.position_embedding_type='alibi' - - `Attention with Linear Biases (ALiBi) `_ modifies the way attention scores are computed in the attention sublayer of the network. ALiBi introduces a static, non-learned bias after the query-key dot product during the computation of attention scores. This bias is added in the form of a head-specific slope that is determined before training, creating a geometric sequence of slopes for the different heads in the model. The method has an inductive bias towards recency, penalizing attention scores between distant query-key pairs with the penalty increasing as the distance grows, and it leverages different rates of penalty increase across different heads based on the slope magnitude. + - Attention with Linear Biases (ALiBi) :cite:`nlp-megatron-press2022train` modifies the way attention scores are computed in the attention sublayer of the network. ALiBi introduces a static, non-learned bias after the query-key dot product during the computation of attention scores. This bias is added in the form of a head-specific slope that is determined before training, creating a geometric sequence of slopes for the different heads in the model. The method has an inductive bias towards recency, penalizing attention scores between distant query-key pairs with the penalty increasing as the distance grows, and it leverages different rates of penalty increase across different heads based on the slope magnitude. * - **kerple** - .. code:: model.position_embedding_type='kerple' - - `Kernelized Relative Positional Embedding for Length Extrapolation (KERPLE) `_ generalizes relative positional embeddings (RPE) by kernelizing positional differences using conditionally positive definite (CPD) kernels known for generalizing distance metrics. They transform CPD kernels into positive definite (PD) kernels by adding a constant offset, which is absorbed during softmax normalization in the self-attention mechanism of transformers. This approach allows for a variety of RPEs that facilitate length extrapolation in a principled manner. + - Kernelized Relative Positional Embedding for Length Extrapolation (KERPLE) :cite:`nlp-megatron-chi2022kerple` generalizes relative positional embeddings (RPE) by kernelizing positional differences using conditionally positive definite (CPD) kernels known for generalizing distance metrics. They transform CPD kernels into positive definite (PD) kernels by adding a constant offset, which is absorbed during softmax normalization in the self-attention mechanism of transformers. This approach allows for a variety of RPEs that facilitate length extrapolation in a principled manner. * - **xpos** - .. code:: model.position_embedding_type='xpos' - - `Extrapolatable Position Embedding (xPos) `_ + - Extrapolatable Position Embedding (xPos) :cite:`nlp-megatron-sun2022lengthextrapolatable` * - **sandwich** - .. code:: model.position_embedding_type='sandwich' - - `Sandwich `_ + - Sandwich :cite:`nlp-megatron-chi2023dissecting` T5 ^^ @@ -67,32 +67,32 @@ T5 model.encoder.position_embedding_type='learned_absolute' model.decoder.position_embedding_type='learned_absolute' - - `Absolute Position Encodings `_ are position embeddings used in Transformer-based models, added to input embeddings in the encoder and decoder sections. These encodings match the dimension of embeddings and are created using sine and cosine functions of various frequencies. Each dimension in the encoding corresponds to a sinusoid with wavelengths forming a geometric progression. + - Absolute Position Encodings :cite:`nlp-megatron-vaswani2023attention` are position embeddings used in Transformer-based models, added to input embeddings in the encoder and decoder sections. These encodings match the dimension of embeddings and are created using sine and cosine functions of various frequencies. Each dimension in the encoding corresponds to a sinusoid with wavelengths forming a geometric progression. * - **relative** - .. code:: model.encoder.position_embedding_type='relative' model.decoder.position_embedding_type='relative' - - `Relative Position Representations `_ + - Relative Position Representations :cite:`nlp-megatron-shaw2018selfattention` * - **alibi** - .. code:: model.encoder.position_embedding_type='alibi' model.decoder.position_embedding_type='alibi' - - `Attention with Linear Biases (ALiBi) `_ modifies the way attention scores are computed in the attention sublayer of the network. ALiBi introduces a static, non-learned bias after the query-key dot product during the computation of attention scores. This bias is added in the form of a head-specific slope that is determined before training, creating a geometric sequence of slopes for the different heads in the model. The method has an inductive bias towards recency, penalizing attention scores between distant query-key pairs with the penalty increasing as the distance grows, and it leverages different rates of penalty increase across different heads based on the slope magnitude. + - Attention with Linear Biases (ALiBi) :cite:`nlp-megatron-press2022train` modifies the way attention scores are computed in the attention sublayer of the network. ALiBi introduces a static, non-learned bias after the query-key dot product during the computation of attention scores. This bias is added in the form of a head-specific slope that is determined before training, creating a geometric sequence of slopes for the different heads in the model. The method has an inductive bias towards recency, penalizing attention scores between distant query-key pairs with the penalty increasing as the distance grows, and it leverages different rates of penalty increase across different heads based on the slope magnitude. * - **kerple** - .. code:: model.encoder.position_embedding_type='kerple' model.decoder.position_embedding_type='kerple' - - `Kernelized Relative Positional Embedding for Length Extrapolation (KERPLE) `_ generalizes relative positional embeddings (RPE) by kernelizing positional differences using conditionally positive definite (CPD) kernels known for generalizing distance metrics. They transform CPD kernels into positive definite (PD) kernels by adding a constant offset, which is absorbed during softmax normalization in the self-attention mechanism of transformers. This approach allows for a variety of RPEs that facilitate length extrapolation in a principled manner. + - Kernelized Relative Positional Embedding for Length Extrapolation (KERPLE) :cite:`nlp-megatron-chi2022kerple` generalizes relative positional embeddings (RPE) by kernelizing positional differences using conditionally positive definite (CPD) kernels known for generalizing distance metrics. They transform CPD kernels into positive definite (PD) kernels by adding a constant offset, which is absorbed during softmax normalization in the self-attention mechanism of transformers. This approach allows for a variety of RPEs that facilitate length extrapolation in a principled manner. Positional interpolation ------------------------ -`Position Interpolation (PI) `_ is a method introduced to extend the context window sizes of Rotary Position Embedding (RoPE)-based pretrained large language models (LLMs). The central principle of PI is to reduce the position indices so that they align with the initial context window size through interpolation. +Position Interpolation (PI) :cite:`nlp-megatron-chen2023extending` is a method introduced to extend the context window sizes of Rotary Position Embedding (RoPE)-based pretrained large language models (LLMs). The central principle of PI is to reduce the position indices so that they align with the initial context window size through interpolation. Positional Interpolation is supported in Megatron GPT SFT models. Set RoPE Interpolation factor for sequence length :code:`seq_len_interpolation_factor` to enable it. @@ -101,3 +101,11 @@ Positional Interpolation is supported in Megatron GPT SFT models. Set RoPE Inter model.position_embedding_type='rope' model.rotary_percentage=1.0 model.seq_len_interpolation_factor: 2 + +References +---------- + +.. bibliography:: ../nlp_all.bib + :style: plain + :labelprefix: nlp-megatron + :keyprefix: nlp-megatron- \ No newline at end of file diff --git a/docs/source/nlp/nlp_all.bib b/docs/source/nlp/nlp_all.bib index 48a53240e52b..48f97e929fc2 100644 --- a/docs/source/nlp/nlp_all.bib +++ b/docs/source/nlp/nlp_all.bib @@ -225,3 +225,84 @@ @misc{antonova2023spellmapper archivePrefix={arXiv}, primaryClass={cs.CL} } + +@misc{dao2022flashattention, + title={FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness}, + author={Tri Dao and Daniel Y. Fu and Stefano Ermon and Atri Rudra and Christopher Ré}, + year={2022}, + eprint={2205.14135}, + archivePrefix={arXiv}, + primaryClass={cs.LG} +} + +@misc{vaswani2023attention, + title={Attention Is All You Need}, + author={Ashish Vaswani and Noam Shazeer and Niki Parmar and Jakob Uszkoreit and Llion Jones and Aidan N. Gomez and Lukasz Kaiser and Illia Polosukhin}, + year={2023}, + eprint={1706.03762}, + archivePrefix={arXiv}, + primaryClass={cs.CL} +} + +@misc{su2022roformer, + title={RoFormer: Enhanced Transformer with Rotary Position Embedding}, + author={Jianlin Su and Yu Lu and Shengfeng Pan and Ahmed Murtadha and Bo Wen and Yunfeng Liu}, + year={2022}, + eprint={2104.09864}, + archivePrefix={arXiv}, + primaryClass={cs.CL} +} + +@misc{press2022train, + title={Train Short, Test Long: Attention with Linear Biases Enables Input Length Extrapolation}, + author={Ofir Press and Noah A. Smith and Mike Lewis}, + year={2022}, + eprint={2108.12409}, + archivePrefix={arXiv}, + primaryClass={cs.CL} +} + +@misc{chi2022kerple, + title={KERPLE: Kernelized Relative Positional Embedding for Length Extrapolation}, + author={Ta-Chung Chi and Ting-Han Fan and Peter J. Ramadge and Alexander I. Rudnicky}, + year={2022}, + eprint={2205.09921}, + archivePrefix={arXiv}, + primaryClass={cs.CL} +} + +@misc{sun2022lengthextrapolatable, + title={A Length-Extrapolatable Transformer}, + author={Yutao Sun and Li Dong and Barun Patra and Shuming Ma and Shaohan Huang and Alon Benhaim and Vishrav Chaudhary and Xia Song and Furu Wei}, + year={2022}, + eprint={2212.10554}, + archivePrefix={arXiv}, + primaryClass={cs.CL} +} + +@misc{chi2023dissecting, + title={Dissecting Transformer Length Extrapolation via the Lens of Receptive Field Analysis}, + author={Ta-Chung Chi and Ting-Han Fan and Alexander I. Rudnicky and Peter J. Ramadge}, + year={2023}, + eprint={2212.10356}, + archivePrefix={arXiv}, + primaryClass={cs.CL} +} + +@misc{shaw2018selfattention, + title={Self-Attention with Relative Position Representations}, + author={Peter Shaw and Jakob Uszkoreit and Ashish Vaswani}, + year={2018}, + eprint={1803.02155}, + archivePrefix={arXiv}, + primaryClass={cs.CL} +} + +@misc{chen2023extending, + title={Extending Context Window of Large Language Models via Positional Interpolation}, + author={Shouyuan Chen and Sherman Wong and Liangjian Chen and Yuandong Tian}, + year={2023}, + eprint={2306.15595}, + archivePrefix={arXiv}, + primaryClass={cs.CL} +} \ No newline at end of file From 2e175551b46760548f6ef351ccf3de0866450805 Mon Sep 17 00:00:00 2001 From: Sasha Meister Date: Tue, 10 Oct 2023 14:48:49 +0000 Subject: [PATCH 112/112] added to toctree Signed-off-by: Sasha Meister --- docs/source/nlp/nemo_megatron/intro.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/nlp/nemo_megatron/intro.rst b/docs/source/nlp/nemo_megatron/intro.rst index c1b158d77e3e..577291424c80 100644 --- a/docs/source/nlp/nemo_megatron/intro.rst +++ b/docs/source/nlp/nemo_megatron/intro.rst @@ -26,6 +26,8 @@ team at NVIDIA. NeMo Megatron supports several types of models: retro/retro_model hiddens/hiddens_module peft/landing_page + flash_attention + positional_embeddings References