From 2735c3e0d1e0a24f24e3243c562b97aad5911fca Mon Sep 17 00:00:00 2001 From: OMOTAYO OMOYEMI <58476114+tayo4christ@users.noreply.github.com> Date: Sat, 20 Sep 2025 18:08:09 +0100 Subject: [PATCH 01/10] examples(speech): load Common Voice from Hub; remove deprecated dataset-script references (Windows-friendly notes) --- .../run_speech_recognition_ctc.py | 187 ++++++++++++---- .../run_speech_recognition_ctc_adapter.py | 180 +++++++++++---- .../run_speech_recognition_seq2seq.py | 208 ++++++++++++++---- 3 files changed, 441 insertions(+), 134 deletions(-) diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py b/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py index 4532bc511e9f..51fc15a8d05e 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py @@ -63,7 +63,10 @@ # Will error if the minimal version of Transformers is not installed. Remove at your own risks. check_min_version("4.57.0.dev0") -require_version("datasets>=1.18.0", "To fix: pip install -r examples/pytorch/speech-recognition/requirements.txt") +require_version( + "datasets>=1.18.0", + "To fix: pip install -r examples/pytorch/speech-recognition/requirements.txt", +) logger = logging.getLogger(__name__) @@ -80,26 +83,39 @@ class ModelArguments: """ model_name_or_path: str = field( - metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} + metadata={ + "help": "Path to pretrained model or model identifier from huggingface.co/models" + } ) tokenizer_name_or_path: Optional[str] = field( default=None, - metadata={"help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models"}, + metadata={ + "help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models" + }, ) cache_dir: Optional[str] = field( default=None, - metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, + metadata={ + "help": "Where do you want to store the pretrained models downloaded from huggingface.co" + }, ) freeze_feature_encoder: bool = field( - default=True, metadata={"help": "Whether to freeze the feature encoder layers of the model."} + default=True, + metadata={"help": "Whether to freeze the feature encoder layers of the model."}, ) attention_dropout: float = field( - default=0.0, metadata={"help": "The dropout ratio for the attention probabilities."} + default=0.0, + metadata={"help": "The dropout ratio for the attention probabilities."}, ) activation_dropout: float = field( - default=0.0, metadata={"help": "The dropout ratio for activations inside the fully connected layer."} + default=0.0, + metadata={ + "help": "The dropout ratio for activations inside the fully connected layer." + }, + ) + feat_proj_dropout: float = field( + default=0.0, metadata={"help": "The dropout ratio for the projected features."} ) - feat_proj_dropout: float = field(default=0.0, metadata={"help": "The dropout ratio for the projected features."}) hidden_dropout: float = field( default=0.0, metadata={ @@ -138,9 +154,14 @@ class ModelArguments: default=10, metadata={"help": "Length of vector span to mask along the feature axis."}, ) - layerdrop: float = field(default=0.0, metadata={"help": "The LayerDrop probability."}) + layerdrop: float = field( + default=0.0, metadata={"help": "The LayerDrop probability."} + ) ctc_loss_reduction: Optional[str] = field( - default="mean", metadata={"help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'."} + default="mean", + metadata={ + "help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'." + }, ) ctc_zero_infinity: Optional[bool] = field( default=False, @@ -169,10 +190,15 @@ class DataTrainingArguments: """ dataset_name: str = field( - metadata={"help": "The configuration name of the dataset to use (via the datasets library)."} + metadata={ + "help": "The configuration name of the dataset to use (via the datasets library)." + } ) dataset_config_name: str = field( - default=None, metadata={"help": "The configuration name of the dataset to use (via the datasets library)."} + default=None, + metadata={ + "help": "The configuration name of the dataset to use (via the datasets library)." + }, ) train_split_name: str = field( default="train+validation", @@ -191,14 +217,19 @@ class DataTrainingArguments: ) audio_column_name: str = field( default="audio", - metadata={"help": "The name of the dataset column containing the audio data. Defaults to 'audio'"}, + metadata={ + "help": "The name of the dataset column containing the audio data. Defaults to 'audio'" + }, ) text_column_name: str = field( default="text", - metadata={"help": "The name of the dataset column containing the text data. Defaults to 'text'"}, + metadata={ + "help": "The name of the dataset column containing the text data. Defaults to 'text'" + }, ) overwrite_cache: bool = field( - default=False, metadata={"help": "Overwrite the cached preprocessed datasets or not."} + default=False, + metadata={"help": "Overwrite the cached preprocessed datasets or not."}, ) preprocessing_num_workers: Optional[int] = field( default=None, @@ -228,7 +259,9 @@ class DataTrainingArguments: ) eval_metrics: list[str] = list_field( default=["wer"], - metadata={"help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`"}, + metadata={ + "help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`" + }, ) max_duration_in_seconds: float = field( default=20.0, @@ -240,7 +273,10 @@ class DataTrainingArguments: }, ) min_duration_in_seconds: float = field( - default=0.0, metadata={"help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds"} + default=0.0, + metadata={ + "help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds" + }, ) preprocessing_only: bool = field( default=False, @@ -329,11 +365,18 @@ class DataCollatorCTCWithPadding: pad_to_multiple_of_labels: Optional[int] = None feature_extractor_input_name: Optional[str] = "input_values" - def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> dict[str, torch.Tensor]: + def __call__( + self, features: list[dict[str, Union[list[int], torch.Tensor]]] + ) -> dict[str, torch.Tensor]: # split inputs and labels since they have to be of different lengths and need # different padding methods input_features = [ - {self.feature_extractor_input_name: feature[self.feature_extractor_input_name]} for feature in features + { + self.feature_extractor_input_name: feature[ + self.feature_extractor_input_name + ] + } + for feature in features ] label_features = [{"input_ids": feature["labels"]} for feature in features] @@ -352,7 +395,9 @@ def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> ) # replace padding with -100 to ignore loss correctly - labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100) + labels = labels_batch["input_ids"].masked_fill( + labels_batch.attention_mask.ne(1), -100 + ) batch["labels"] = labels if "attention_mask" in batch: @@ -383,7 +428,8 @@ def extract_all_chars(batch): # take union of all unique characters in each dataset vocab_set = functools.reduce( - lambda vocab_1, vocab_2: set(vocab_1["vocab"][0]) | set(vocab_2["vocab"][0]), vocabs.values() + lambda vocab_1, vocab_2: set(vocab_1["vocab"][0]) | set(vocab_2["vocab"][0]), + vocabs.values(), ) vocab_dict = {v: k for k, v in enumerate(sorted(vocab_set))} @@ -408,11 +454,15 @@ def main(): # or by passing the --help flag to this script. # We now keep distinct sets of args, for a cleaner separation of concerns. - parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments)) + parser = HfArgumentParser( + (ModelArguments, DataTrainingArguments, TrainingArguments) + ) if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): # If we pass only one argument to the script and it's the path to a json file, # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) + model_args, data_args, training_args = parser.parse_json_file( + json_file=os.path.abspath(sys.argv[1]) + ) else: model_args, data_args, training_args = parser.parse_args_into_dataclasses() @@ -422,7 +472,11 @@ def main(): # Detecting last checkpoint. last_checkpoint = None - if os.path.isdir(training_args.output_dir) and training_args.do_train and not training_args.overwrite_output_dir: + if ( + os.path.isdir(training_args.output_dir) + and training_args.do_train + and not training_args.overwrite_output_dir + ): last_checkpoint = get_last_checkpoint(training_args.output_dir) if last_checkpoint is None and len(os.listdir(training_args.output_dir)) > 0: raise ValueError( @@ -441,7 +495,9 @@ def main(): datefmt="%m/%d/%Y %H:%M:%S", handlers=[logging.StreamHandler(sys.stdout)], ) - logger.setLevel(logging.INFO if is_main_process(training_args.local_rank) else logging.WARN) + logger.setLevel( + logging.INFO if is_main_process(training_args.local_rank) else logging.WARN + ) # Log on each process the small summary: logger.warning( @@ -483,7 +539,9 @@ def main(): ) if data_args.max_train_samples is not None: - raw_datasets["train"] = raw_datasets["train"].select(range(data_args.max_train_samples)) + raw_datasets["train"] = raw_datasets["train"].select( + range(data_args.max_train_samples) + ) if training_args.do_eval: raw_datasets["eval"] = load_dataset( @@ -495,25 +553,33 @@ def main(): ) if data_args.max_eval_samples is not None: - raw_datasets["eval"] = raw_datasets["eval"].select(range(data_args.max_eval_samples)) + raw_datasets["eval"] = raw_datasets["eval"].select( + range(data_args.max_eval_samples) + ) # 2. We remove some special characters from the datasets # that make training complicated and do not help in transcribing the speech # E.g. characters, such as `,` and `.` do not really have an acoustic characteristic # that could be easily picked up by the model chars_to_ignore_regex = ( - f"[{''.join(data_args.chars_to_ignore)}]" if data_args.chars_to_ignore is not None else None + f"[{''.join(data_args.chars_to_ignore)}]" + if data_args.chars_to_ignore is not None + else None ) text_column_name = data_args.text_column_name def remove_special_characters(batch): if chars_to_ignore_regex is not None: - batch["target_text"] = re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " + batch["target_text"] = ( + re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " + ) else: batch["target_text"] = batch[text_column_name].lower() + " " return batch - with training_args.main_process_first(desc="dataset map special characters removal"): + with training_args.main_process_first( + desc="dataset map special characters removal" + ): raw_datasets = raw_datasets.map( remove_special_characters, remove_columns=[text_column_name], @@ -575,7 +641,9 @@ def remove_special_characters(batch): # it is defined by `tokenizer_class` if present in config else by `model_type` tokenizer_kwargs = { "config": config if config.tokenizer_class is not None else None, - "tokenizer_type": config.model_type if config.tokenizer_class is None else None, + "tokenizer_type": ( + config.model_type if config.tokenizer_class is None else None + ), "unk_token": unk_token, "pad_token": pad_token, "word_delimiter_token": word_delimiter_token, @@ -640,15 +708,24 @@ def remove_special_characters(batch): # via the `feature_extractor` # make sure that dataset decodes audio with correct sampling rate - dataset_sampling_rate = next(iter(raw_datasets.values())).features[data_args.audio_column_name].sampling_rate + dataset_sampling_rate = ( + next(iter(raw_datasets.values())) + .features[data_args.audio_column_name] + .sampling_rate + ) if dataset_sampling_rate != feature_extractor.sampling_rate: raw_datasets = raw_datasets.cast_column( - data_args.audio_column_name, datasets.features.Audio(sampling_rate=feature_extractor.sampling_rate) + data_args.audio_column_name, + datasets.features.Audio(sampling_rate=feature_extractor.sampling_rate), ) # derive max & min input length for sample rate & max duration - max_input_length = data_args.max_duration_in_seconds * feature_extractor.sampling_rate - min_input_length = data_args.min_duration_in_seconds * feature_extractor.sampling_rate + max_input_length = ( + data_args.max_duration_in_seconds * feature_extractor.sampling_rate + ) + min_input_length = ( + data_args.min_duration_in_seconds * feature_extractor.sampling_rate + ) audio_column_name = data_args.audio_column_name num_workers = data_args.preprocessing_num_workers feature_extractor_input_name = feature_extractor.model_input_names[0] @@ -662,8 +739,12 @@ def prepare_dataset(batch): # load audio sample = batch[audio_column_name] - inputs = feature_extractor(sample["array"], sampling_rate=sample["sampling_rate"]) - batch[feature_extractor_input_name] = getattr(inputs, feature_extractor_input_name)[0] + inputs = feature_extractor( + sample["array"], sampling_rate=sample["sampling_rate"] + ) + batch[feature_extractor_input_name] = getattr( + inputs, feature_extractor_input_name + )[0] # take length of raw audio waveform batch["input_length"] = len(sample["array"].squeeze()) @@ -698,7 +779,10 @@ def is_audio_in_length_range(length): # instantiate a data collator and the trainer # Define evaluation metrics during training, *i.e.* word error rate, character error rate - eval_metrics = {metric: evaluate.load(metric, cache_dir=model_args.cache_dir) for metric in data_args.eval_metrics} + eval_metrics = { + metric: evaluate.load(metric, cache_dir=model_args.cache_dir) + for metric in data_args.eval_metrics + } # for large datasets it is advised to run the preprocessing on a # single machine first with ``args.preprocessing_only`` since there will mostly likely @@ -706,7 +790,9 @@ def is_audio_in_length_range(length): # In a second step ``args.preprocessing_only`` can then be set to `False` to load the # cached dataset if data_args.preprocessing_only: - logger.info(f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}") + logger.info( + f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}" + ) return # For languages like Chinese with large vocabulary size, we need to discard logits @@ -723,7 +809,10 @@ def compute_metrics(pred): # we do not want to group tokens when computing the metrics label_str = tokenizer.batch_decode(pred.label_ids, group_tokens=False) - metrics = {k: v.compute(predictions=pred_str, references=label_str) for k, v in eval_metrics.items()} + metrics = { + k: v.compute(predictions=pred_str, references=label_str) + for k, v in eval_metrics.items() + } return metrics @@ -787,7 +876,9 @@ def compute_metrics(pred): if data_args.max_train_samples is not None else len(vectorized_datasets["train"]) ) - metrics["train_samples"] = min(max_train_samples, len(vectorized_datasets["train"])) + metrics["train_samples"] = min( + max_train_samples, len(vectorized_datasets["train"]) + ) trainer.log_metrics("train", metrics) trainer.save_metrics("train", metrics) @@ -799,15 +890,23 @@ def compute_metrics(pred): logger.info("*** Evaluate ***") metrics = trainer.evaluate() max_eval_samples = ( - data_args.max_eval_samples if data_args.max_eval_samples is not None else len(vectorized_datasets["eval"]) + data_args.max_eval_samples + if data_args.max_eval_samples is not None + else len(vectorized_datasets["eval"]) + ) + metrics["eval_samples"] = min( + max_eval_samples, len(vectorized_datasets["eval"]) ) - metrics["eval_samples"] = min(max_eval_samples, len(vectorized_datasets["eval"])) trainer.log_metrics("eval", metrics) trainer.save_metrics("eval", metrics) # Write model card and (optionally) push to hub - config_name = data_args.dataset_config_name if data_args.dataset_config_name is not None else "na" + config_name = ( + data_args.dataset_config_name + if data_args.dataset_config_name is not None + else "na" + ) kwargs = { "finetuned_from": model_args.model_name_or_path, "tasks": "automatic-speech-recognition", diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py b/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py index 884201d9d993..90213a515001 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py @@ -66,7 +66,10 @@ # Will error if the minimal version of Transformers is not installed. Remove at your own risks. check_min_version("4.57.0.dev0") -require_version("datasets>=1.18.0", "To fix: pip install -r examples/pytorch/speech-recognition/requirements.txt") +require_version( + "datasets>=1.18.0", + "To fix: pip install -r examples/pytorch/speech-recognition/requirements.txt", +) logger = logging.getLogger(__name__) @@ -83,15 +86,21 @@ class ModelArguments: """ model_name_or_path: str = field( - metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} + metadata={ + "help": "Path to pretrained model or model identifier from huggingface.co/models" + } ) tokenizer_name_or_path: Optional[str] = field( default=None, - metadata={"help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models"}, + metadata={ + "help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models" + }, ) cache_dir: Optional[str] = field( default=None, - metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, + metadata={ + "help": "Where do you want to store the pretrained models downloaded from huggingface.co" + }, ) final_dropout: float = field( default=0.0, @@ -125,9 +134,14 @@ class ModelArguments: default=10, metadata={"help": "Length of vector span to mask along the feature axis."}, ) - layerdrop: float = field(default=0.0, metadata={"help": "The LayerDrop probability."}) + layerdrop: float = field( + default=0.0, metadata={"help": "The LayerDrop probability."} + ) ctc_loss_reduction: Optional[str] = field( - default="mean", metadata={"help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'."} + default="mean", + metadata={ + "help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'." + }, ) adapter_attn_dim: int = field( default=16, @@ -148,7 +162,9 @@ class DataTrainingArguments: """ dataset_name: str = field( - metadata={"help": "The configuration name of the dataset to use (via the datasets library)."} + metadata={ + "help": "The configuration name of the dataset to use (via the datasets library)." + } ) target_language: Optional[str] = field( metadata={ @@ -162,7 +178,10 @@ class DataTrainingArguments: }, ) dataset_config_name: str = field( - default=None, metadata={"help": "The configuration name of the dataset to use (via the datasets library)."} + default=None, + metadata={ + "help": "The configuration name of the dataset to use (via the datasets library)." + }, ) train_split_name: str = field( default="train+validation", @@ -181,14 +200,19 @@ class DataTrainingArguments: ) audio_column_name: str = field( default="audio", - metadata={"help": "The name of the dataset column containing the audio data. Defaults to 'audio'"}, + metadata={ + "help": "The name of the dataset column containing the audio data. Defaults to 'audio'" + }, ) text_column_name: str = field( default="text", - metadata={"help": "The name of the dataset column containing the text data. Defaults to 'text'"}, + metadata={ + "help": "The name of the dataset column containing the text data. Defaults to 'text'" + }, ) overwrite_cache: bool = field( - default=False, metadata={"help": "Overwrite the cached preprocessed datasets or not."} + default=False, + metadata={"help": "Overwrite the cached preprocessed datasets or not."}, ) preprocessing_num_workers: Optional[int] = field( default=None, @@ -218,7 +242,9 @@ class DataTrainingArguments: ) eval_metrics: list[str] = list_field( default=["wer"], - metadata={"help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`"}, + metadata={ + "help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`" + }, ) max_duration_in_seconds: float = field( default=20.0, @@ -230,7 +256,10 @@ class DataTrainingArguments: }, ) min_duration_in_seconds: float = field( - default=0.0, metadata={"help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds"} + default=0.0, + metadata={ + "help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds" + }, ) preprocessing_only: bool = field( default=False, @@ -276,7 +305,11 @@ class DataTrainingArguments: ) overwrite_lang_vocab: bool = field( default=False, - metadata={"help": ("If :obj:`True`, will overwrite existing `target_language` vocabulary of tokenizer.")}, + metadata={ + "help": ( + "If :obj:`True`, will overwrite existing `target_language` vocabulary of tokenizer." + ) + }, ) @@ -311,10 +344,14 @@ class DataCollatorCTCWithPadding: pad_to_multiple_of: Optional[int] = None pad_to_multiple_of_labels: Optional[int] = None - def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> dict[str, torch.Tensor]: + def __call__( + self, features: list[dict[str, Union[list[int], torch.Tensor]]] + ) -> dict[str, torch.Tensor]: # split inputs and labels since they have to be of different lengths and need # different padding methods - input_features = [{"input_values": feature["input_values"]} for feature in features] + input_features = [ + {"input_values": feature["input_values"]} for feature in features + ] label_features = [{"input_ids": feature["labels"]} for feature in features] batch = self.processor.pad( @@ -332,7 +369,9 @@ def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> ) # replace padding with -100 to ignore loss correctly - labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100) + labels = labels_batch["input_ids"].masked_fill( + labels_batch.attention_mask.ne(1), -100 + ) batch["labels"] = labels if "attention_mask" in batch: @@ -363,7 +402,8 @@ def extract_all_chars(batch): # take union of all unique characters in each dataset vocab_set = functools.reduce( - lambda vocab_1, vocab_2: set(vocab_1["vocab"][0]) | set(vocab_2["vocab"][0]), vocabs.values() + lambda vocab_1, vocab_2: set(vocab_1["vocab"][0]) | set(vocab_2["vocab"][0]), + vocabs.values(), ) vocab_dict = {v: k for k, v in enumerate(sorted(vocab_set))} @@ -388,11 +428,15 @@ def main(): # or by passing the --help flag to this script. # We now keep distinct sets of args, for a cleaner separation of concerns. - parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments)) + parser = HfArgumentParser( + (ModelArguments, DataTrainingArguments, TrainingArguments) + ) if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): # If we pass only one argument to the script and it's the path to a json file, # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) + model_args, data_args, training_args = parser.parse_json_file( + json_file=os.path.abspath(sys.argv[1]) + ) else: model_args, data_args, training_args = parser.parse_args_into_dataclasses() @@ -402,7 +446,11 @@ def main(): # Detecting last checkpoint. last_checkpoint = None - if os.path.isdir(training_args.output_dir) and training_args.do_train and not training_args.overwrite_output_dir: + if ( + os.path.isdir(training_args.output_dir) + and training_args.do_train + and not training_args.overwrite_output_dir + ): last_checkpoint = get_last_checkpoint(training_args.output_dir) if last_checkpoint is None and len(os.listdir(training_args.output_dir)) > 0: raise ValueError( @@ -421,7 +469,9 @@ def main(): datefmt="%m/%d/%Y %H:%M:%S", handlers=[logging.StreamHandler(sys.stdout)], ) - logger.setLevel(logging.INFO if is_main_process(training_args.local_rank) else logging.WARN) + logger.setLevel( + logging.INFO if is_main_process(training_args.local_rank) else logging.WARN + ) # Log on each process the small summary: logger.warning( @@ -463,7 +513,9 @@ def main(): ) if data_args.max_train_samples is not None: - raw_datasets["train"] = raw_datasets["train"].select(range(data_args.max_train_samples)) + raw_datasets["train"] = raw_datasets["train"].select( + range(data_args.max_train_samples) + ) if training_args.do_eval: raw_datasets["eval"] = load_dataset( @@ -475,25 +527,33 @@ def main(): ) if data_args.max_eval_samples is not None: - raw_datasets["eval"] = raw_datasets["eval"].select(range(data_args.max_eval_samples)) + raw_datasets["eval"] = raw_datasets["eval"].select( + range(data_args.max_eval_samples) + ) # 2. We remove some special characters from the datasets # that make training complicated and do not help in transcribing the speech # E.g. characters, such as `,` and `.` do not really have an acoustic characteristic # that could be easily picked up by the model chars_to_ignore_regex = ( - f"[{''.join(data_args.chars_to_ignore)}]" if data_args.chars_to_ignore is not None else None + f"[{''.join(data_args.chars_to_ignore)}]" + if data_args.chars_to_ignore is not None + else None ) text_column_name = data_args.text_column_name def remove_special_characters(batch): if chars_to_ignore_regex is not None: - batch["target_text"] = re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " + batch["target_text"] = ( + re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " + ) else: batch["target_text"] = batch[text_column_name].lower() + " " return batch - with training_args.main_process_first(desc="dataset map special characters removal"): + with training_args.main_process_first( + desc="dataset map special characters removal" + ): raw_datasets = raw_datasets.map( remove_special_characters, remove_columns=[text_column_name], @@ -533,9 +593,14 @@ def remove_special_characters(batch): ) vocab_dict = tokenizer.vocab.copy() if tokenizer.target_lang is None: - raise ValueError("Make sure to load a multi-lingual tokenizer with a set target language.") + raise ValueError( + "Make sure to load a multi-lingual tokenizer with a set target language." + ) - if data_args.target_language in tokenizer.vocab and not data_args.overwrite_lang_vocab: + if ( + data_args.target_language in tokenizer.vocab + and not data_args.overwrite_lang_vocab + ): logger.info( "Adapter language already exists." " Skipping vocabulary creating. If you want to create a new vocabulary" @@ -582,7 +647,9 @@ def remove_special_characters(batch): # it is defined by `tokenizer_class` if present in config else by `model_type` tokenizer_kwargs = { "config": config if config.tokenizer_class is not None else None, - "tokenizer_type": config.model_type if config.tokenizer_class is None else None, + "tokenizer_type": ( + config.model_type if config.tokenizer_class is None else None + ), "unk_token": unk_token, "pad_token": pad_token, "word_delimiter_token": word_delimiter_token, @@ -651,15 +718,24 @@ def remove_special_characters(batch): # via the `feature_extractor` # make sure that dataset decodes audio with correct sampling rate - dataset_sampling_rate = next(iter(raw_datasets.values())).features[data_args.audio_column_name].sampling_rate + dataset_sampling_rate = ( + next(iter(raw_datasets.values())) + .features[data_args.audio_column_name] + .sampling_rate + ) if dataset_sampling_rate != feature_extractor.sampling_rate: raw_datasets = raw_datasets.cast_column( - data_args.audio_column_name, datasets.features.Audio(sampling_rate=feature_extractor.sampling_rate) + data_args.audio_column_name, + datasets.features.Audio(sampling_rate=feature_extractor.sampling_rate), ) # derive max & min input length for sample rate & max duration - max_input_length = data_args.max_duration_in_seconds * feature_extractor.sampling_rate - min_input_length = data_args.min_duration_in_seconds * feature_extractor.sampling_rate + max_input_length = ( + data_args.max_duration_in_seconds * feature_extractor.sampling_rate + ) + min_input_length = ( + data_args.min_duration_in_seconds * feature_extractor.sampling_rate + ) audio_column_name = data_args.audio_column_name num_workers = data_args.preprocessing_num_workers @@ -669,7 +745,9 @@ def prepare_dataset(batch): # load audio sample = batch[audio_column_name] - inputs = feature_extractor(sample["array"], sampling_rate=sample["sampling_rate"]) + inputs = feature_extractor( + sample["array"], sampling_rate=sample["sampling_rate"] + ) batch["input_values"] = inputs.input_values[0] batch["input_length"] = len(batch["input_values"]) @@ -700,7 +778,10 @@ def is_audio_in_length_range(length): # instantiate a data collator and the trainer # Define evaluation metrics during training, *i.e.* word error rate, character error rate - eval_metrics = {metric: evaluate.load(metric, cache_dir=model_args.cache_dir) for metric in data_args.eval_metrics} + eval_metrics = { + metric: evaluate.load(metric, cache_dir=model_args.cache_dir) + for metric in data_args.eval_metrics + } # for large datasets it is advised to run the preprocessing on a # single machine first with ``args.preprocessing_only`` since there will mostly likely @@ -708,7 +789,9 @@ def is_audio_in_length_range(length): # In a second step ``args.preprocessing_only`` can then be set to `False` to load the # cached dataset if data_args.preprocessing_only: - logger.info(f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}") + logger.info( + f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}" + ) return def compute_metrics(pred): @@ -721,7 +804,10 @@ def compute_metrics(pred): # we do not want to group tokens when computing the metrics label_str = tokenizer.batch_decode(pred.label_ids, group_tokens=False) - metrics = {k: v.compute(predictions=pred_str, references=label_str) for k, v in eval_metrics.items()} + metrics = { + k: v.compute(predictions=pred_str, references=label_str) + for k, v in eval_metrics.items() + } return metrics @@ -782,7 +868,9 @@ def compute_metrics(pred): if data_args.max_train_samples is not None else len(vectorized_datasets["train"]) ) - metrics["train_samples"] = min(max_train_samples, len(vectorized_datasets["train"])) + metrics["train_samples"] = min( + max_train_samples, len(vectorized_datasets["train"]) + ) trainer.log_metrics("train", metrics) trainer.save_metrics("train", metrics) @@ -794,15 +882,23 @@ def compute_metrics(pred): logger.info("*** Evaluate ***") metrics = trainer.evaluate() max_eval_samples = ( - data_args.max_eval_samples if data_args.max_eval_samples is not None else len(vectorized_datasets["eval"]) + data_args.max_eval_samples + if data_args.max_eval_samples is not None + else len(vectorized_datasets["eval"]) + ) + metrics["eval_samples"] = min( + max_eval_samples, len(vectorized_datasets["eval"]) ) - metrics["eval_samples"] = min(max_eval_samples, len(vectorized_datasets["eval"])) trainer.log_metrics("eval", metrics) trainer.save_metrics("eval", metrics) # Write model card and (optionally) push to hub - config_name = data_args.dataset_config_name if data_args.dataset_config_name is not None else "na" + config_name = ( + data_args.dataset_config_name + if data_args.dataset_config_name is not None + else "na" + ) kwargs = { "finetuned_from": model_args.model_name_or_path, "tasks": "automatic-speech-recognition", diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py b/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py index aee6ae3b8bae..a46c80b0426f 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py @@ -62,7 +62,10 @@ # Will error if the minimal version of Transformers is not installed. Remove at your own risks. check_min_version("4.57.0.dev0") -require_version("datasets>=1.18.0", "To fix: pip install -r examples/pytorch/speech-recognition/requirements.txt") +require_version( + "datasets>=1.18.0", + "To fix: pip install -r examples/pytorch/speech-recognition/requirements.txt", +) logger = logging.getLogger(__name__) @@ -74,28 +77,45 @@ class ModelArguments: """ model_name_or_path: str = field( - metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} + metadata={ + "help": "Path to pretrained model or model identifier from huggingface.co/models" + } ) config_name: Optional[str] = field( - default=None, metadata={"help": "Pretrained config name or path if not the same as model_name"} + default=None, + metadata={ + "help": "Pretrained config name or path if not the same as model_name" + }, ) tokenizer_name: Optional[str] = field( - default=None, metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"} + default=None, + metadata={ + "help": "Pretrained tokenizer name or path if not the same as model_name" + }, ) feature_extractor_name: Optional[str] = field( - default=None, metadata={"help": "feature extractor name or path if not the same as model_name"} + default=None, + metadata={ + "help": "feature extractor name or path if not the same as model_name" + }, ) cache_dir: Optional[str] = field( default=None, - metadata={"help": "Where to store the pretrained models downloaded from huggingface.co"}, + metadata={ + "help": "Where to store the pretrained models downloaded from huggingface.co" + }, ) use_fast_tokenizer: bool = field( default=True, - metadata={"help": "Whether to use one of the fast tokenizer (backed by the tokenizers library) or not."}, + metadata={ + "help": "Whether to use one of the fast tokenizer (backed by the tokenizers library) or not." + }, ) model_revision: str = field( default="main", - metadata={"help": "The specific model version to use (can be a branch name, tag name or commit id)."}, + metadata={ + "help": "The specific model version to use (can be a branch name, tag name or commit id)." + }, ) token: str = field( default=None, @@ -117,14 +137,18 @@ class ModelArguments: }, ) freeze_feature_encoder: bool = field( - default=True, metadata={"help": "Whether to freeze the feature encoder layers of the model."} + default=True, + metadata={"help": "Whether to freeze the feature encoder layers of the model."}, ) freeze_encoder: bool = field( - default=False, metadata={"help": "Whether to freeze the entire encoder of the seq2seq model."} + default=False, + metadata={"help": "Whether to freeze the entire encoder of the seq2seq model."}, ) forced_decoder_ids: list[list[int]] = field( default=None, - metadata={"help": "Deprecated. Please use the `language` and `task` arguments instead."}, + metadata={ + "help": "Deprecated. Please use the `language` and `task` arguments instead." + }, ) suppress_tokens: list[int] = field( default=None, @@ -150,13 +174,18 @@ class DataTrainingArguments: """ dataset_name: str = field( - default=None, metadata={"help": "The name of the dataset to use (via the datasets library)."} + default=None, + metadata={"help": "The name of the dataset to use (via the datasets library)."}, ) dataset_config_name: Optional[str] = field( - default=None, metadata={"help": "The configuration name of the dataset to use (via the datasets library)."} + default=None, + metadata={ + "help": "The configuration name of the dataset to use (via the datasets library)." + }, ) overwrite_cache: bool = field( - default=False, metadata={"help": "Overwrite the cached training and evaluation sets"} + default=False, + metadata={"help": "Overwrite the cached training and evaluation sets"}, ) preprocessing_num_workers: Optional[int] = field( default=None, @@ -182,11 +211,15 @@ class DataTrainingArguments: ) audio_column_name: str = field( default="audio", - metadata={"help": "The name of the dataset column containing the audio data. Defaults to 'audio'"}, + metadata={ + "help": "The name of the dataset column containing the audio data. Defaults to 'audio'" + }, ) text_column_name: str = field( default="text", - metadata={"help": "The name of the dataset column containing the text data. Defaults to 'text'"}, + metadata={ + "help": "The name of the dataset column containing the text data. Defaults to 'text'" + }, ) max_duration_in_seconds: float = field( default=20.0, @@ -198,7 +231,10 @@ class DataTrainingArguments: }, ) min_duration_in_seconds: float = field( - default=0.0, metadata={"help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds"} + default=0.0, + metadata={ + "help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds" + }, ) preprocessing_only: bool = field( default=False, @@ -238,7 +274,9 @@ class DataTrainingArguments: ) task: str = field( default="transcribe", - metadata={"help": "Task, either `transcribe` for speech recognition or `translate` for speech translation."}, + metadata={ + "help": "Task, either `transcribe` for speech recognition or `translate` for speech translation." + }, ) @@ -259,22 +297,32 @@ class DataCollatorSpeechSeq2SeqWithPadding: decoder_start_token_id: int forward_attention_mask: bool - def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> dict[str, torch.Tensor]: + def __call__( + self, features: list[dict[str, Union[list[int], torch.Tensor]]] + ) -> dict[str, torch.Tensor]: # split inputs and labels since they have to be of different lengths and need # different padding methods model_input_name = self.processor.model_input_names[0] - input_features = [{model_input_name: feature[model_input_name]} for feature in features] + input_features = [ + {model_input_name: feature[model_input_name]} for feature in features + ] label_features = [{"input_ids": feature["labels"]} for feature in features] - batch = self.processor.feature_extractor.pad(input_features, return_tensors="pt") + batch = self.processor.feature_extractor.pad( + input_features, return_tensors="pt" + ) if self.forward_attention_mask: - batch["attention_mask"] = torch.LongTensor([feature["attention_mask"] for feature in features]) + batch["attention_mask"] = torch.LongTensor( + [feature["attention_mask"] for feature in features] + ) labels_batch = self.processor.tokenizer.pad(label_features, return_tensors="pt") # replace padding with -100 to ignore loss correctly - labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100) + labels = labels_batch["input_ids"].masked_fill( + labels_batch.attention_mask.ne(1), -100 + ) # if bos token is appended in previous tokenization step, # cut bos token here as it's append later anyways @@ -291,12 +339,16 @@ def main(): # See all possible arguments in src/transformers/training_args.py # or by passing the --help flag to this script. # We now keep distinct sets of args, for a cleaner separation of concerns. - parser = HfArgumentParser((ModelArguments, DataTrainingArguments, Seq2SeqTrainingArguments)) + parser = HfArgumentParser( + (ModelArguments, DataTrainingArguments, Seq2SeqTrainingArguments) + ) if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): # If we pass only one argument to the script and it's the path to a json file, # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) + model_args, data_args, training_args = parser.parse_json_file( + json_file=os.path.abspath(sys.argv[1]) + ) else: model_args, data_args, training_args = parser.parse_args_into_dataclasses() @@ -317,7 +369,9 @@ def main(): transformers.utils.logging.enable_default_handler() transformers.utils.logging.enable_explicit_format() - logger.setLevel(logging.INFO if is_main_process(training_args.local_rank) else logging.WARN) + logger.setLevel( + logging.INFO if is_main_process(training_args.local_rank) else logging.WARN + ) # Log on each process the small summary: logger.warning( @@ -333,14 +387,20 @@ def main(): # 3. Detecting last checkpoint and eventually continue from last checkpoint last_checkpoint = None - if os.path.isdir(training_args.output_dir) and training_args.do_train and not training_args.overwrite_output_dir: + if ( + os.path.isdir(training_args.output_dir) + and training_args.do_train + and not training_args.overwrite_output_dir + ): last_checkpoint = get_last_checkpoint(training_args.output_dir) if last_checkpoint is None and len(os.listdir(training_args.output_dir)) > 0: raise ValueError( f"Output directory ({training_args.output_dir}) already exists and is not empty. " "Use --overwrite_output_dir to overcome." ) - elif last_checkpoint is not None and training_args.resume_from_checkpoint is None: + elif ( + last_checkpoint is not None and training_args.resume_from_checkpoint is None + ): logger.info( f"Checkpoint detected, resuming training at {last_checkpoint}. To avoid this behavior, change " "the `--output_dir` or add `--overwrite_output_dir` to train from scratch." @@ -372,7 +432,10 @@ def main(): trust_remote_code=model_args.trust_remote_code, ) - if data_args.audio_column_name not in next(iter(raw_datasets.values())).column_names: + if ( + data_args.audio_column_name + not in next(iter(raw_datasets.values())).column_names + ): raise ValueError( f"--audio_column_name '{data_args.audio_column_name}' not found in dataset '{data_args.dataset_name}'. " "Make sure to set `--audio_column_name` to the correct audio column - one of " @@ -391,7 +454,11 @@ def main(): # Distributed training: # The .from_pretrained methods guarantee that only one local process can concurrently config = AutoConfig.from_pretrained( - model_args.config_name if model_args.config_name else model_args.model_name_or_path, + ( + model_args.config_name + if model_args.config_name + else model_args.model_name_or_path + ), cache_dir=model_args.cache_dir, revision=model_args.model_revision, token=model_args.token, @@ -403,14 +470,22 @@ def main(): config.update({"apply_spec_augment": model_args.apply_spec_augment}) feature_extractor = AutoFeatureExtractor.from_pretrained( - model_args.feature_extractor_name if model_args.feature_extractor_name else model_args.model_name_or_path, + ( + model_args.feature_extractor_name + if model_args.feature_extractor_name + else model_args.model_name_or_path + ), cache_dir=model_args.cache_dir, revision=model_args.model_revision, token=model_args.token, trust_remote_code=model_args.trust_remote_code, ) tokenizer = AutoTokenizer.from_pretrained( - model_args.tokenizer_name if model_args.tokenizer_name else model_args.model_name_or_path, + ( + model_args.tokenizer_name + if model_args.tokenizer_name + else model_args.model_name_or_path + ), cache_dir=model_args.cache_dir, use_fast=model_args.use_fast_tokenizer, revision=model_args.model_revision, @@ -427,7 +502,9 @@ def main(): ) if model.config.decoder_start_token_id is None: - raise ValueError("Make sure that `config.decoder_start_token_id` is correctly defined") + raise ValueError( + "Make sure that `config.decoder_start_token_id` is correctly defined" + ) if model_args.freeze_feature_encoder: model.freeze_feature_encoder() @@ -436,7 +513,10 @@ def main(): model.freeze_encoder() model.model.encoder.gradient_checkpointing = False - if hasattr(model.generation_config, "is_multilingual") and model.generation_config.is_multilingual: + if ( + hasattr(model.generation_config, "is_multilingual") + and model.generation_config.is_multilingual + ): # We only need to set the language and task ids in a multilingual setting tokenizer.set_prefix_tokens(language=data_args.language, task=data_args.task) model.generation_config.language = data_args.language @@ -466,16 +546,25 @@ def main(): model.generation_config.suppress_tokens = model_args.suppress_tokens # 6. Resample speech dataset if necessary - dataset_sampling_rate = next(iter(raw_datasets.values())).features[data_args.audio_column_name].sampling_rate + dataset_sampling_rate = ( + next(iter(raw_datasets.values())) + .features[data_args.audio_column_name] + .sampling_rate + ) if dataset_sampling_rate != feature_extractor.sampling_rate: raw_datasets = raw_datasets.cast_column( - data_args.audio_column_name, datasets.features.Audio(sampling_rate=feature_extractor.sampling_rate) + data_args.audio_column_name, + datasets.features.Audio(sampling_rate=feature_extractor.sampling_rate), ) # 7. Preprocessing the datasets. # We need to read the audio files as arrays and tokenize the targets. - max_input_length = data_args.max_duration_in_seconds * feature_extractor.sampling_rate - min_input_length = data_args.min_duration_in_seconds * feature_extractor.sampling_rate + max_input_length = ( + data_args.max_duration_in_seconds * feature_extractor.sampling_rate + ) + min_input_length = ( + data_args.min_duration_in_seconds * feature_extractor.sampling_rate + ) audio_column_name = data_args.audio_column_name num_workers = data_args.preprocessing_num_workers text_column_name = data_args.text_column_name @@ -489,16 +578,22 @@ def main(): ) if data_args.max_train_samples is not None: - raw_datasets["train"] = raw_datasets["train"].select(range(data_args.max_train_samples)) + raw_datasets["train"] = raw_datasets["train"].select( + range(data_args.max_train_samples) + ) if data_args.max_eval_samples is not None: - raw_datasets["eval"] = raw_datasets["eval"].select(range(data_args.max_eval_samples)) + raw_datasets["eval"] = raw_datasets["eval"].select( + range(data_args.max_eval_samples) + ) def prepare_dataset(batch): # process audio sample = batch[audio_column_name] inputs = feature_extractor( - sample["array"], sampling_rate=sample["sampling_rate"], return_attention_mask=forward_attention_mask + sample["array"], + sampling_rate=sample["sampling_rate"], + return_attention_mask=forward_attention_mask, ) # process audio length batch[model_input_name] = inputs.get(model_input_name)[0] @@ -507,7 +602,11 @@ def prepare_dataset(batch): batch["attention_mask"] = inputs.get("attention_mask")[0] # process targets - input_str = batch[text_column_name].lower() if do_lower_case else batch[text_column_name] + input_str = ( + batch[text_column_name].lower() + if do_lower_case + else batch[text_column_name] + ) batch["labels"] = tokenizer(input_str).input_ids return batch @@ -583,7 +682,9 @@ def compute_metrics(pred): eval_dataset=vectorized_datasets["eval"] if training_args.do_eval else None, processing_class=feature_extractor, data_collator=data_collator, - compute_metrics=compute_metrics if training_args.predict_with_generate else None, + compute_metrics=( + compute_metrics if training_args.predict_with_generate else None + ), ) # 12. Training @@ -602,7 +703,9 @@ def compute_metrics(pred): if data_args.max_train_samples is not None else len(vectorized_datasets["train"]) ) - metrics["train_samples"] = min(max_train_samples, len(vectorized_datasets["train"])) + metrics["train_samples"] = min( + max_train_samples, len(vectorized_datasets["train"]) + ) trainer.log_metrics("train", metrics) trainer.save_metrics("train", metrics) trainer.save_state() @@ -617,20 +720,29 @@ def compute_metrics(pred): num_beams=training_args.generation_num_beams, ) max_eval_samples = ( - data_args.max_eval_samples if data_args.max_eval_samples is not None else len(vectorized_datasets["eval"]) + data_args.max_eval_samples + if data_args.max_eval_samples is not None + else len(vectorized_datasets["eval"]) + ) + metrics["eval_samples"] = min( + max_eval_samples, len(vectorized_datasets["eval"]) ) - metrics["eval_samples"] = min(max_eval_samples, len(vectorized_datasets["eval"])) trainer.log_metrics("eval", metrics) trainer.save_metrics("eval", metrics) # 14. Write Training Stats - kwargs = {"finetuned_from": model_args.model_name_or_path, "tasks": "automatic-speech-recognition"} + kwargs = { + "finetuned_from": model_args.model_name_or_path, + "tasks": "automatic-speech-recognition", + } if data_args.dataset_name is not None: kwargs["dataset_tags"] = data_args.dataset_name if data_args.dataset_config_name is not None: kwargs["dataset_args"] = data_args.dataset_config_name - kwargs["dataset"] = f"{data_args.dataset_name} {data_args.dataset_config_name}" + kwargs["dataset"] = ( + f"{data_args.dataset_name} {data_args.dataset_config_name}" + ) else: kwargs["dataset"] = data_args.dataset_name From 24902d8699dc78cda83b4931e6f49202c8a78436 Mon Sep 17 00:00:00 2001 From: OMOTAYO OMOYEMI <58476114+tayo4christ@users.noreply.github.com> Date: Sat, 20 Sep 2025 22:55:32 +0100 Subject: [PATCH 02/10] docs/examples(speech): pin CTC streaming & other CTC commands to Hub datasets; add Windows notes --- examples/pytorch/speech-recognition/README.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/examples/pytorch/speech-recognition/README.md b/examples/pytorch/speech-recognition/README.md index 2889919655f4..ae860abcf6ed 100644 --- a/examples/pytorch/speech-recognition/README.md +++ b/examples/pytorch/speech-recognition/README.md @@ -59,6 +59,15 @@ OMP_NUM_THREADS=1 python run_speech_recognition_ctc ... If the environment variable is not set, the training script might freeze, *i.e.* see: https://github.com/pytorch/audio/issues/1021#issuecomment-726915239 --- +### Loading Common Voice from the Hub (recommended) + +Use the `datasets` library to load a pinned Common Voice version from the Hub: + +```python +from datasets import load_dataset +# pick the right version/lang that exists on the Hub (example: CV 17.0 English) +ds = load_dataset("mozilla-foundation/common_voice_17_0", "en", split="train+validation") + ### Single GPU CTC @@ -66,7 +75,7 @@ The following command shows how to fine-tune [XLSR-Wav2Vec2](https://huggingface ```bash python run_speech_recognition_ctc.py \ - --dataset_name="common_voice" \ + --dataset_name="mozilla-foundation/common_voice_17_0" \ --model_name_or_path="facebook/wav2vec2-large-xlsr-53" \ --dataset_config_name="tr" \ --output_dir="./wav2vec2-common_voice-tr-demo" \ @@ -102,7 +111,7 @@ The following command shows how to fine-tune [XLSR-Wav2Vec2](https://huggingface ```bash torchrun \ --nproc_per_node 8 run_speech_recognition_ctc.py \ - --dataset_name="common_voice" \ + --dataset_name="mozilla-foundation/common_voice_17_0" \ --model_name_or_path="facebook/wav2vec2-large-xlsr-53" \ --dataset_config_name="tr" \ --output_dir="./wav2vec2-common_voice-tr-demo-dist" \ @@ -149,7 +158,7 @@ However, the `--shuffle_buffer_size` argument controls how many examples we can ```bash **torchrun \ --nproc_per_node 4 run_speech_recognition_ctc_streaming.py \ - --dataset_name="common_voice" \ + --dataset_name="mozilla-foundation/common_voice_17_0" \ --model_name_or_path="facebook/wav2vec2-xls-r-300m" \ --tokenizer_name_or_path="anton-l/wav2vec2-tokenizer-turkish" \ --dataset_config_name="tr" \ @@ -360,6 +369,10 @@ recognition on one of the [official speech recognition datasets](https://hugging ### Whisper Model We can load all components of the Whisper model directly from the pretrained checkpoint, including the pretrained model weights, feature extractor and tokenizer. We simply have to specify our fine-tuning dataset and training hyperparameters. +> **Tip:** If you see errors about “dataset scripts are no longer supported”, make sure your +> `--dataset_name` points to a Hub dataset like `mozilla-foundation/common_voice_XX_X` +> (e.g., `mozilla-foundation/common_voice_17_0`) rather than a local script. + #### Single GPU Whisper Training The following example shows how to fine-tune the [Whisper small](https://huggingface.co/openai/whisper-small) checkpoint on the Hindi subset of [Common Voice 11](https://huggingface.co/datasets/mozilla-foundation/common_voice_11_0) using a single GPU device in half-precision: ```bash From 9b561eb17cc7b0b5ff2a4a794e732c84f35d777c Mon Sep 17 00:00:00 2001 From: Eustache Le Bihan Date: Thu, 25 Sep 2025 19:22:32 +0200 Subject: [PATCH 03/10] make style --- .../run_speech_recognition_ctc.py | 163 ++++------------ .../run_speech_recognition_ctc_adapter.py | 163 ++++------------ .../run_speech_recognition_seq2seq.py | 174 ++++-------------- 3 files changed, 116 insertions(+), 384 deletions(-) diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py b/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py index 51fc15a8d05e..696ad4d20747 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py @@ -83,21 +83,15 @@ class ModelArguments: """ model_name_or_path: str = field( - metadata={ - "help": "Path to pretrained model or model identifier from huggingface.co/models" - } + metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} ) tokenizer_name_or_path: Optional[str] = field( default=None, - metadata={ - "help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models" - }, + metadata={"help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models"}, ) cache_dir: Optional[str] = field( default=None, - metadata={ - "help": "Where do you want to store the pretrained models downloaded from huggingface.co" - }, + metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, ) freeze_feature_encoder: bool = field( default=True, @@ -109,13 +103,9 @@ class ModelArguments: ) activation_dropout: float = field( default=0.0, - metadata={ - "help": "The dropout ratio for activations inside the fully connected layer." - }, - ) - feat_proj_dropout: float = field( - default=0.0, metadata={"help": "The dropout ratio for the projected features."} + metadata={"help": "The dropout ratio for activations inside the fully connected layer."}, ) + feat_proj_dropout: float = field(default=0.0, metadata={"help": "The dropout ratio for the projected features."}) hidden_dropout: float = field( default=0.0, metadata={ @@ -154,14 +144,10 @@ class ModelArguments: default=10, metadata={"help": "Length of vector span to mask along the feature axis."}, ) - layerdrop: float = field( - default=0.0, metadata={"help": "The LayerDrop probability."} - ) + layerdrop: float = field(default=0.0, metadata={"help": "The LayerDrop probability."}) ctc_loss_reduction: Optional[str] = field( default="mean", - metadata={ - "help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'." - }, + metadata={"help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'."}, ) ctc_zero_infinity: Optional[bool] = field( default=False, @@ -190,15 +176,11 @@ class DataTrainingArguments: """ dataset_name: str = field( - metadata={ - "help": "The configuration name of the dataset to use (via the datasets library)." - } + metadata={"help": "The configuration name of the dataset to use (via the datasets library)."} ) dataset_config_name: str = field( default=None, - metadata={ - "help": "The configuration name of the dataset to use (via the datasets library)." - }, + metadata={"help": "The configuration name of the dataset to use (via the datasets library)."}, ) train_split_name: str = field( default="train+validation", @@ -217,15 +199,11 @@ class DataTrainingArguments: ) audio_column_name: str = field( default="audio", - metadata={ - "help": "The name of the dataset column containing the audio data. Defaults to 'audio'" - }, + metadata={"help": "The name of the dataset column containing the audio data. Defaults to 'audio'"}, ) text_column_name: str = field( default="text", - metadata={ - "help": "The name of the dataset column containing the text data. Defaults to 'text'" - }, + metadata={"help": "The name of the dataset column containing the text data. Defaults to 'text'"}, ) overwrite_cache: bool = field( default=False, @@ -259,9 +237,7 @@ class DataTrainingArguments: ) eval_metrics: list[str] = list_field( default=["wer"], - metadata={ - "help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`" - }, + metadata={"help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`"}, ) max_duration_in_seconds: float = field( default=20.0, @@ -274,9 +250,7 @@ class DataTrainingArguments: ) min_duration_in_seconds: float = field( default=0.0, - metadata={ - "help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds" - }, + metadata={"help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds"}, ) preprocessing_only: bool = field( default=False, @@ -365,18 +339,11 @@ class DataCollatorCTCWithPadding: pad_to_multiple_of_labels: Optional[int] = None feature_extractor_input_name: Optional[str] = "input_values" - def __call__( - self, features: list[dict[str, Union[list[int], torch.Tensor]]] - ) -> dict[str, torch.Tensor]: + def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> dict[str, torch.Tensor]: # split inputs and labels since they have to be of different lengths and need # different padding methods input_features = [ - { - self.feature_extractor_input_name: feature[ - self.feature_extractor_input_name - ] - } - for feature in features + {self.feature_extractor_input_name: feature[self.feature_extractor_input_name]} for feature in features ] label_features = [{"input_ids": feature["labels"]} for feature in features] @@ -395,9 +362,7 @@ def __call__( ) # replace padding with -100 to ignore loss correctly - labels = labels_batch["input_ids"].masked_fill( - labels_batch.attention_mask.ne(1), -100 - ) + labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100) batch["labels"] = labels if "attention_mask" in batch: @@ -454,15 +419,11 @@ def main(): # or by passing the --help flag to this script. # We now keep distinct sets of args, for a cleaner separation of concerns. - parser = HfArgumentParser( - (ModelArguments, DataTrainingArguments, TrainingArguments) - ) + parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments)) if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): # If we pass only one argument to the script and it's the path to a json file, # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file( - json_file=os.path.abspath(sys.argv[1]) - ) + model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) else: model_args, data_args, training_args = parser.parse_args_into_dataclasses() @@ -472,11 +433,7 @@ def main(): # Detecting last checkpoint. last_checkpoint = None - if ( - os.path.isdir(training_args.output_dir) - and training_args.do_train - and not training_args.overwrite_output_dir - ): + if os.path.isdir(training_args.output_dir) and training_args.do_train and not training_args.overwrite_output_dir: last_checkpoint = get_last_checkpoint(training_args.output_dir) if last_checkpoint is None and len(os.listdir(training_args.output_dir)) > 0: raise ValueError( @@ -495,9 +452,7 @@ def main(): datefmt="%m/%d/%Y %H:%M:%S", handlers=[logging.StreamHandler(sys.stdout)], ) - logger.setLevel( - logging.INFO if is_main_process(training_args.local_rank) else logging.WARN - ) + logger.setLevel(logging.INFO if is_main_process(training_args.local_rank) else logging.WARN) # Log on each process the small summary: logger.warning( @@ -539,9 +494,7 @@ def main(): ) if data_args.max_train_samples is not None: - raw_datasets["train"] = raw_datasets["train"].select( - range(data_args.max_train_samples) - ) + raw_datasets["train"] = raw_datasets["train"].select(range(data_args.max_train_samples)) if training_args.do_eval: raw_datasets["eval"] = load_dataset( @@ -553,33 +506,25 @@ def main(): ) if data_args.max_eval_samples is not None: - raw_datasets["eval"] = raw_datasets["eval"].select( - range(data_args.max_eval_samples) - ) + raw_datasets["eval"] = raw_datasets["eval"].select(range(data_args.max_eval_samples)) # 2. We remove some special characters from the datasets # that make training complicated and do not help in transcribing the speech # E.g. characters, such as `,` and `.` do not really have an acoustic characteristic # that could be easily picked up by the model chars_to_ignore_regex = ( - f"[{''.join(data_args.chars_to_ignore)}]" - if data_args.chars_to_ignore is not None - else None + f"[{''.join(data_args.chars_to_ignore)}]" if data_args.chars_to_ignore is not None else None ) text_column_name = data_args.text_column_name def remove_special_characters(batch): if chars_to_ignore_regex is not None: - batch["target_text"] = ( - re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " - ) + batch["target_text"] = re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " else: batch["target_text"] = batch[text_column_name].lower() + " " return batch - with training_args.main_process_first( - desc="dataset map special characters removal" - ): + with training_args.main_process_first(desc="dataset map special characters removal"): raw_datasets = raw_datasets.map( remove_special_characters, remove_columns=[text_column_name], @@ -641,9 +586,7 @@ def remove_special_characters(batch): # it is defined by `tokenizer_class` if present in config else by `model_type` tokenizer_kwargs = { "config": config if config.tokenizer_class is not None else None, - "tokenizer_type": ( - config.model_type if config.tokenizer_class is None else None - ), + "tokenizer_type": (config.model_type if config.tokenizer_class is None else None), "unk_token": unk_token, "pad_token": pad_token, "word_delimiter_token": word_delimiter_token, @@ -708,11 +651,7 @@ def remove_special_characters(batch): # via the `feature_extractor` # make sure that dataset decodes audio with correct sampling rate - dataset_sampling_rate = ( - next(iter(raw_datasets.values())) - .features[data_args.audio_column_name] - .sampling_rate - ) + dataset_sampling_rate = next(iter(raw_datasets.values())).features[data_args.audio_column_name].sampling_rate if dataset_sampling_rate != feature_extractor.sampling_rate: raw_datasets = raw_datasets.cast_column( data_args.audio_column_name, @@ -720,12 +659,8 @@ def remove_special_characters(batch): ) # derive max & min input length for sample rate & max duration - max_input_length = ( - data_args.max_duration_in_seconds * feature_extractor.sampling_rate - ) - min_input_length = ( - data_args.min_duration_in_seconds * feature_extractor.sampling_rate - ) + max_input_length = data_args.max_duration_in_seconds * feature_extractor.sampling_rate + min_input_length = data_args.min_duration_in_seconds * feature_extractor.sampling_rate audio_column_name = data_args.audio_column_name num_workers = data_args.preprocessing_num_workers feature_extractor_input_name = feature_extractor.model_input_names[0] @@ -739,12 +674,8 @@ def prepare_dataset(batch): # load audio sample = batch[audio_column_name] - inputs = feature_extractor( - sample["array"], sampling_rate=sample["sampling_rate"] - ) - batch[feature_extractor_input_name] = getattr( - inputs, feature_extractor_input_name - )[0] + inputs = feature_extractor(sample["array"], sampling_rate=sample["sampling_rate"]) + batch[feature_extractor_input_name] = getattr(inputs, feature_extractor_input_name)[0] # take length of raw audio waveform batch["input_length"] = len(sample["array"].squeeze()) @@ -779,10 +710,7 @@ def is_audio_in_length_range(length): # instantiate a data collator and the trainer # Define evaluation metrics during training, *i.e.* word error rate, character error rate - eval_metrics = { - metric: evaluate.load(metric, cache_dir=model_args.cache_dir) - for metric in data_args.eval_metrics - } + eval_metrics = {metric: evaluate.load(metric, cache_dir=model_args.cache_dir) for metric in data_args.eval_metrics} # for large datasets it is advised to run the preprocessing on a # single machine first with ``args.preprocessing_only`` since there will mostly likely @@ -790,9 +718,7 @@ def is_audio_in_length_range(length): # In a second step ``args.preprocessing_only`` can then be set to `False` to load the # cached dataset if data_args.preprocessing_only: - logger.info( - f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}" - ) + logger.info(f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}") return # For languages like Chinese with large vocabulary size, we need to discard logits @@ -809,10 +735,7 @@ def compute_metrics(pred): # we do not want to group tokens when computing the metrics label_str = tokenizer.batch_decode(pred.label_ids, group_tokens=False) - metrics = { - k: v.compute(predictions=pred_str, references=label_str) - for k, v in eval_metrics.items() - } + metrics = {k: v.compute(predictions=pred_str, references=label_str) for k, v in eval_metrics.items()} return metrics @@ -876,9 +799,7 @@ def compute_metrics(pred): if data_args.max_train_samples is not None else len(vectorized_datasets["train"]) ) - metrics["train_samples"] = min( - max_train_samples, len(vectorized_datasets["train"]) - ) + metrics["train_samples"] = min(max_train_samples, len(vectorized_datasets["train"])) trainer.log_metrics("train", metrics) trainer.save_metrics("train", metrics) @@ -890,23 +811,15 @@ def compute_metrics(pred): logger.info("*** Evaluate ***") metrics = trainer.evaluate() max_eval_samples = ( - data_args.max_eval_samples - if data_args.max_eval_samples is not None - else len(vectorized_datasets["eval"]) - ) - metrics["eval_samples"] = min( - max_eval_samples, len(vectorized_datasets["eval"]) + data_args.max_eval_samples if data_args.max_eval_samples is not None else len(vectorized_datasets["eval"]) ) + metrics["eval_samples"] = min(max_eval_samples, len(vectorized_datasets["eval"])) trainer.log_metrics("eval", metrics) trainer.save_metrics("eval", metrics) # Write model card and (optionally) push to hub - config_name = ( - data_args.dataset_config_name - if data_args.dataset_config_name is not None - else "na" - ) + config_name = data_args.dataset_config_name if data_args.dataset_config_name is not None else "na" kwargs = { "finetuned_from": model_args.model_name_or_path, "tasks": "automatic-speech-recognition", diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py b/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py index 90213a515001..6c1d4e4e8323 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py @@ -86,21 +86,15 @@ class ModelArguments: """ model_name_or_path: str = field( - metadata={ - "help": "Path to pretrained model or model identifier from huggingface.co/models" - } + metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} ) tokenizer_name_or_path: Optional[str] = field( default=None, - metadata={ - "help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models" - }, + metadata={"help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models"}, ) cache_dir: Optional[str] = field( default=None, - metadata={ - "help": "Where do you want to store the pretrained models downloaded from huggingface.co" - }, + metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, ) final_dropout: float = field( default=0.0, @@ -134,14 +128,10 @@ class ModelArguments: default=10, metadata={"help": "Length of vector span to mask along the feature axis."}, ) - layerdrop: float = field( - default=0.0, metadata={"help": "The LayerDrop probability."} - ) + layerdrop: float = field(default=0.0, metadata={"help": "The LayerDrop probability."}) ctc_loss_reduction: Optional[str] = field( default="mean", - metadata={ - "help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'." - }, + metadata={"help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'."}, ) adapter_attn_dim: int = field( default=16, @@ -162,9 +152,7 @@ class DataTrainingArguments: """ dataset_name: str = field( - metadata={ - "help": "The configuration name of the dataset to use (via the datasets library)." - } + metadata={"help": "The configuration name of the dataset to use (via the datasets library)."} ) target_language: Optional[str] = field( metadata={ @@ -179,9 +167,7 @@ class DataTrainingArguments: ) dataset_config_name: str = field( default=None, - metadata={ - "help": "The configuration name of the dataset to use (via the datasets library)." - }, + metadata={"help": "The configuration name of the dataset to use (via the datasets library)."}, ) train_split_name: str = field( default="train+validation", @@ -200,15 +186,11 @@ class DataTrainingArguments: ) audio_column_name: str = field( default="audio", - metadata={ - "help": "The name of the dataset column containing the audio data. Defaults to 'audio'" - }, + metadata={"help": "The name of the dataset column containing the audio data. Defaults to 'audio'"}, ) text_column_name: str = field( default="text", - metadata={ - "help": "The name of the dataset column containing the text data. Defaults to 'text'" - }, + metadata={"help": "The name of the dataset column containing the text data. Defaults to 'text'"}, ) overwrite_cache: bool = field( default=False, @@ -242,9 +224,7 @@ class DataTrainingArguments: ) eval_metrics: list[str] = list_field( default=["wer"], - metadata={ - "help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`" - }, + metadata={"help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`"}, ) max_duration_in_seconds: float = field( default=20.0, @@ -257,9 +237,7 @@ class DataTrainingArguments: ) min_duration_in_seconds: float = field( default=0.0, - metadata={ - "help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds" - }, + metadata={"help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds"}, ) preprocessing_only: bool = field( default=False, @@ -305,11 +283,7 @@ class DataTrainingArguments: ) overwrite_lang_vocab: bool = field( default=False, - metadata={ - "help": ( - "If :obj:`True`, will overwrite existing `target_language` vocabulary of tokenizer." - ) - }, + metadata={"help": ("If :obj:`True`, will overwrite existing `target_language` vocabulary of tokenizer.")}, ) @@ -344,14 +318,10 @@ class DataCollatorCTCWithPadding: pad_to_multiple_of: Optional[int] = None pad_to_multiple_of_labels: Optional[int] = None - def __call__( - self, features: list[dict[str, Union[list[int], torch.Tensor]]] - ) -> dict[str, torch.Tensor]: + def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> dict[str, torch.Tensor]: # split inputs and labels since they have to be of different lengths and need # different padding methods - input_features = [ - {"input_values": feature["input_values"]} for feature in features - ] + input_features = [{"input_values": feature["input_values"]} for feature in features] label_features = [{"input_ids": feature["labels"]} for feature in features] batch = self.processor.pad( @@ -369,9 +339,7 @@ def __call__( ) # replace padding with -100 to ignore loss correctly - labels = labels_batch["input_ids"].masked_fill( - labels_batch.attention_mask.ne(1), -100 - ) + labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100) batch["labels"] = labels if "attention_mask" in batch: @@ -428,15 +396,11 @@ def main(): # or by passing the --help flag to this script. # We now keep distinct sets of args, for a cleaner separation of concerns. - parser = HfArgumentParser( - (ModelArguments, DataTrainingArguments, TrainingArguments) - ) + parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments)) if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): # If we pass only one argument to the script and it's the path to a json file, # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file( - json_file=os.path.abspath(sys.argv[1]) - ) + model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) else: model_args, data_args, training_args = parser.parse_args_into_dataclasses() @@ -446,11 +410,7 @@ def main(): # Detecting last checkpoint. last_checkpoint = None - if ( - os.path.isdir(training_args.output_dir) - and training_args.do_train - and not training_args.overwrite_output_dir - ): + if os.path.isdir(training_args.output_dir) and training_args.do_train and not training_args.overwrite_output_dir: last_checkpoint = get_last_checkpoint(training_args.output_dir) if last_checkpoint is None and len(os.listdir(training_args.output_dir)) > 0: raise ValueError( @@ -469,9 +429,7 @@ def main(): datefmt="%m/%d/%Y %H:%M:%S", handlers=[logging.StreamHandler(sys.stdout)], ) - logger.setLevel( - logging.INFO if is_main_process(training_args.local_rank) else logging.WARN - ) + logger.setLevel(logging.INFO if is_main_process(training_args.local_rank) else logging.WARN) # Log on each process the small summary: logger.warning( @@ -513,9 +471,7 @@ def main(): ) if data_args.max_train_samples is not None: - raw_datasets["train"] = raw_datasets["train"].select( - range(data_args.max_train_samples) - ) + raw_datasets["train"] = raw_datasets["train"].select(range(data_args.max_train_samples)) if training_args.do_eval: raw_datasets["eval"] = load_dataset( @@ -527,33 +483,25 @@ def main(): ) if data_args.max_eval_samples is not None: - raw_datasets["eval"] = raw_datasets["eval"].select( - range(data_args.max_eval_samples) - ) + raw_datasets["eval"] = raw_datasets["eval"].select(range(data_args.max_eval_samples)) # 2. We remove some special characters from the datasets # that make training complicated and do not help in transcribing the speech # E.g. characters, such as `,` and `.` do not really have an acoustic characteristic # that could be easily picked up by the model chars_to_ignore_regex = ( - f"[{''.join(data_args.chars_to_ignore)}]" - if data_args.chars_to_ignore is not None - else None + f"[{''.join(data_args.chars_to_ignore)}]" if data_args.chars_to_ignore is not None else None ) text_column_name = data_args.text_column_name def remove_special_characters(batch): if chars_to_ignore_regex is not None: - batch["target_text"] = ( - re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " - ) + batch["target_text"] = re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " else: batch["target_text"] = batch[text_column_name].lower() + " " return batch - with training_args.main_process_first( - desc="dataset map special characters removal" - ): + with training_args.main_process_first(desc="dataset map special characters removal"): raw_datasets = raw_datasets.map( remove_special_characters, remove_columns=[text_column_name], @@ -593,14 +541,9 @@ def remove_special_characters(batch): ) vocab_dict = tokenizer.vocab.copy() if tokenizer.target_lang is None: - raise ValueError( - "Make sure to load a multi-lingual tokenizer with a set target language." - ) + raise ValueError("Make sure to load a multi-lingual tokenizer with a set target language.") - if ( - data_args.target_language in tokenizer.vocab - and not data_args.overwrite_lang_vocab - ): + if data_args.target_language in tokenizer.vocab and not data_args.overwrite_lang_vocab: logger.info( "Adapter language already exists." " Skipping vocabulary creating. If you want to create a new vocabulary" @@ -647,9 +590,7 @@ def remove_special_characters(batch): # it is defined by `tokenizer_class` if present in config else by `model_type` tokenizer_kwargs = { "config": config if config.tokenizer_class is not None else None, - "tokenizer_type": ( - config.model_type if config.tokenizer_class is None else None - ), + "tokenizer_type": (config.model_type if config.tokenizer_class is None else None), "unk_token": unk_token, "pad_token": pad_token, "word_delimiter_token": word_delimiter_token, @@ -718,11 +659,7 @@ def remove_special_characters(batch): # via the `feature_extractor` # make sure that dataset decodes audio with correct sampling rate - dataset_sampling_rate = ( - next(iter(raw_datasets.values())) - .features[data_args.audio_column_name] - .sampling_rate - ) + dataset_sampling_rate = next(iter(raw_datasets.values())).features[data_args.audio_column_name].sampling_rate if dataset_sampling_rate != feature_extractor.sampling_rate: raw_datasets = raw_datasets.cast_column( data_args.audio_column_name, @@ -730,12 +667,8 @@ def remove_special_characters(batch): ) # derive max & min input length for sample rate & max duration - max_input_length = ( - data_args.max_duration_in_seconds * feature_extractor.sampling_rate - ) - min_input_length = ( - data_args.min_duration_in_seconds * feature_extractor.sampling_rate - ) + max_input_length = data_args.max_duration_in_seconds * feature_extractor.sampling_rate + min_input_length = data_args.min_duration_in_seconds * feature_extractor.sampling_rate audio_column_name = data_args.audio_column_name num_workers = data_args.preprocessing_num_workers @@ -745,9 +678,7 @@ def prepare_dataset(batch): # load audio sample = batch[audio_column_name] - inputs = feature_extractor( - sample["array"], sampling_rate=sample["sampling_rate"] - ) + inputs = feature_extractor(sample["array"], sampling_rate=sample["sampling_rate"]) batch["input_values"] = inputs.input_values[0] batch["input_length"] = len(batch["input_values"]) @@ -778,10 +709,7 @@ def is_audio_in_length_range(length): # instantiate a data collator and the trainer # Define evaluation metrics during training, *i.e.* word error rate, character error rate - eval_metrics = { - metric: evaluate.load(metric, cache_dir=model_args.cache_dir) - for metric in data_args.eval_metrics - } + eval_metrics = {metric: evaluate.load(metric, cache_dir=model_args.cache_dir) for metric in data_args.eval_metrics} # for large datasets it is advised to run the preprocessing on a # single machine first with ``args.preprocessing_only`` since there will mostly likely @@ -789,9 +717,7 @@ def is_audio_in_length_range(length): # In a second step ``args.preprocessing_only`` can then be set to `False` to load the # cached dataset if data_args.preprocessing_only: - logger.info( - f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}" - ) + logger.info(f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}") return def compute_metrics(pred): @@ -804,10 +730,7 @@ def compute_metrics(pred): # we do not want to group tokens when computing the metrics label_str = tokenizer.batch_decode(pred.label_ids, group_tokens=False) - metrics = { - k: v.compute(predictions=pred_str, references=label_str) - for k, v in eval_metrics.items() - } + metrics = {k: v.compute(predictions=pred_str, references=label_str) for k, v in eval_metrics.items()} return metrics @@ -868,9 +791,7 @@ def compute_metrics(pred): if data_args.max_train_samples is not None else len(vectorized_datasets["train"]) ) - metrics["train_samples"] = min( - max_train_samples, len(vectorized_datasets["train"]) - ) + metrics["train_samples"] = min(max_train_samples, len(vectorized_datasets["train"])) trainer.log_metrics("train", metrics) trainer.save_metrics("train", metrics) @@ -882,23 +803,15 @@ def compute_metrics(pred): logger.info("*** Evaluate ***") metrics = trainer.evaluate() max_eval_samples = ( - data_args.max_eval_samples - if data_args.max_eval_samples is not None - else len(vectorized_datasets["eval"]) - ) - metrics["eval_samples"] = min( - max_eval_samples, len(vectorized_datasets["eval"]) + data_args.max_eval_samples if data_args.max_eval_samples is not None else len(vectorized_datasets["eval"]) ) + metrics["eval_samples"] = min(max_eval_samples, len(vectorized_datasets["eval"])) trainer.log_metrics("eval", metrics) trainer.save_metrics("eval", metrics) # Write model card and (optionally) push to hub - config_name = ( - data_args.dataset_config_name - if data_args.dataset_config_name is not None - else "na" - ) + config_name = data_args.dataset_config_name if data_args.dataset_config_name is not None else "na" kwargs = { "finetuned_from": model_args.model_name_or_path, "tasks": "automatic-speech-recognition", diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py b/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py index a46c80b0426f..88830b583a07 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py @@ -77,45 +77,31 @@ class ModelArguments: """ model_name_or_path: str = field( - metadata={ - "help": "Path to pretrained model or model identifier from huggingface.co/models" - } + metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} ) config_name: Optional[str] = field( default=None, - metadata={ - "help": "Pretrained config name or path if not the same as model_name" - }, + metadata={"help": "Pretrained config name or path if not the same as model_name"}, ) tokenizer_name: Optional[str] = field( default=None, - metadata={ - "help": "Pretrained tokenizer name or path if not the same as model_name" - }, + metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"}, ) feature_extractor_name: Optional[str] = field( default=None, - metadata={ - "help": "feature extractor name or path if not the same as model_name" - }, + metadata={"help": "feature extractor name or path if not the same as model_name"}, ) cache_dir: Optional[str] = field( default=None, - metadata={ - "help": "Where to store the pretrained models downloaded from huggingface.co" - }, + metadata={"help": "Where to store the pretrained models downloaded from huggingface.co"}, ) use_fast_tokenizer: bool = field( default=True, - metadata={ - "help": "Whether to use one of the fast tokenizer (backed by the tokenizers library) or not." - }, + metadata={"help": "Whether to use one of the fast tokenizer (backed by the tokenizers library) or not."}, ) model_revision: str = field( default="main", - metadata={ - "help": "The specific model version to use (can be a branch name, tag name or commit id)." - }, + metadata={"help": "The specific model version to use (can be a branch name, tag name or commit id)."}, ) token: str = field( default=None, @@ -146,9 +132,7 @@ class ModelArguments: ) forced_decoder_ids: list[list[int]] = field( default=None, - metadata={ - "help": "Deprecated. Please use the `language` and `task` arguments instead." - }, + metadata={"help": "Deprecated. Please use the `language` and `task` arguments instead."}, ) suppress_tokens: list[int] = field( default=None, @@ -179,9 +163,7 @@ class DataTrainingArguments: ) dataset_config_name: Optional[str] = field( default=None, - metadata={ - "help": "The configuration name of the dataset to use (via the datasets library)." - }, + metadata={"help": "The configuration name of the dataset to use (via the datasets library)."}, ) overwrite_cache: bool = field( default=False, @@ -211,15 +193,11 @@ class DataTrainingArguments: ) audio_column_name: str = field( default="audio", - metadata={ - "help": "The name of the dataset column containing the audio data. Defaults to 'audio'" - }, + metadata={"help": "The name of the dataset column containing the audio data. Defaults to 'audio'"}, ) text_column_name: str = field( default="text", - metadata={ - "help": "The name of the dataset column containing the text data. Defaults to 'text'" - }, + metadata={"help": "The name of the dataset column containing the text data. Defaults to 'text'"}, ) max_duration_in_seconds: float = field( default=20.0, @@ -232,9 +210,7 @@ class DataTrainingArguments: ) min_duration_in_seconds: float = field( default=0.0, - metadata={ - "help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds" - }, + metadata={"help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds"}, ) preprocessing_only: bool = field( default=False, @@ -274,9 +250,7 @@ class DataTrainingArguments: ) task: str = field( default="transcribe", - metadata={ - "help": "Task, either `transcribe` for speech recognition or `translate` for speech translation." - }, + metadata={"help": "Task, either `transcribe` for speech recognition or `translate` for speech translation."}, ) @@ -297,32 +271,22 @@ class DataCollatorSpeechSeq2SeqWithPadding: decoder_start_token_id: int forward_attention_mask: bool - def __call__( - self, features: list[dict[str, Union[list[int], torch.Tensor]]] - ) -> dict[str, torch.Tensor]: + def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> dict[str, torch.Tensor]: # split inputs and labels since they have to be of different lengths and need # different padding methods model_input_name = self.processor.model_input_names[0] - input_features = [ - {model_input_name: feature[model_input_name]} for feature in features - ] + input_features = [{model_input_name: feature[model_input_name]} for feature in features] label_features = [{"input_ids": feature["labels"]} for feature in features] - batch = self.processor.feature_extractor.pad( - input_features, return_tensors="pt" - ) + batch = self.processor.feature_extractor.pad(input_features, return_tensors="pt") if self.forward_attention_mask: - batch["attention_mask"] = torch.LongTensor( - [feature["attention_mask"] for feature in features] - ) + batch["attention_mask"] = torch.LongTensor([feature["attention_mask"] for feature in features]) labels_batch = self.processor.tokenizer.pad(label_features, return_tensors="pt") # replace padding with -100 to ignore loss correctly - labels = labels_batch["input_ids"].masked_fill( - labels_batch.attention_mask.ne(1), -100 - ) + labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100) # if bos token is appended in previous tokenization step, # cut bos token here as it's append later anyways @@ -339,16 +303,12 @@ def main(): # See all possible arguments in src/transformers/training_args.py # or by passing the --help flag to this script. # We now keep distinct sets of args, for a cleaner separation of concerns. - parser = HfArgumentParser( - (ModelArguments, DataTrainingArguments, Seq2SeqTrainingArguments) - ) + parser = HfArgumentParser((ModelArguments, DataTrainingArguments, Seq2SeqTrainingArguments)) if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): # If we pass only one argument to the script and it's the path to a json file, # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file( - json_file=os.path.abspath(sys.argv[1]) - ) + model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) else: model_args, data_args, training_args = parser.parse_args_into_dataclasses() @@ -369,9 +329,7 @@ def main(): transformers.utils.logging.enable_default_handler() transformers.utils.logging.enable_explicit_format() - logger.setLevel( - logging.INFO if is_main_process(training_args.local_rank) else logging.WARN - ) + logger.setLevel(logging.INFO if is_main_process(training_args.local_rank) else logging.WARN) # Log on each process the small summary: logger.warning( @@ -387,20 +345,14 @@ def main(): # 3. Detecting last checkpoint and eventually continue from last checkpoint last_checkpoint = None - if ( - os.path.isdir(training_args.output_dir) - and training_args.do_train - and not training_args.overwrite_output_dir - ): + if os.path.isdir(training_args.output_dir) and training_args.do_train and not training_args.overwrite_output_dir: last_checkpoint = get_last_checkpoint(training_args.output_dir) if last_checkpoint is None and len(os.listdir(training_args.output_dir)) > 0: raise ValueError( f"Output directory ({training_args.output_dir}) already exists and is not empty. " "Use --overwrite_output_dir to overcome." ) - elif ( - last_checkpoint is not None and training_args.resume_from_checkpoint is None - ): + elif last_checkpoint is not None and training_args.resume_from_checkpoint is None: logger.info( f"Checkpoint detected, resuming training at {last_checkpoint}. To avoid this behavior, change " "the `--output_dir` or add `--overwrite_output_dir` to train from scratch." @@ -432,10 +384,7 @@ def main(): trust_remote_code=model_args.trust_remote_code, ) - if ( - data_args.audio_column_name - not in next(iter(raw_datasets.values())).column_names - ): + if data_args.audio_column_name not in next(iter(raw_datasets.values())).column_names: raise ValueError( f"--audio_column_name '{data_args.audio_column_name}' not found in dataset '{data_args.dataset_name}'. " "Make sure to set `--audio_column_name` to the correct audio column - one of " @@ -454,11 +403,7 @@ def main(): # Distributed training: # The .from_pretrained methods guarantee that only one local process can concurrently config = AutoConfig.from_pretrained( - ( - model_args.config_name - if model_args.config_name - else model_args.model_name_or_path - ), + (model_args.config_name if model_args.config_name else model_args.model_name_or_path), cache_dir=model_args.cache_dir, revision=model_args.model_revision, token=model_args.token, @@ -470,22 +415,14 @@ def main(): config.update({"apply_spec_augment": model_args.apply_spec_augment}) feature_extractor = AutoFeatureExtractor.from_pretrained( - ( - model_args.feature_extractor_name - if model_args.feature_extractor_name - else model_args.model_name_or_path - ), + (model_args.feature_extractor_name if model_args.feature_extractor_name else model_args.model_name_or_path), cache_dir=model_args.cache_dir, revision=model_args.model_revision, token=model_args.token, trust_remote_code=model_args.trust_remote_code, ) tokenizer = AutoTokenizer.from_pretrained( - ( - model_args.tokenizer_name - if model_args.tokenizer_name - else model_args.model_name_or_path - ), + (model_args.tokenizer_name if model_args.tokenizer_name else model_args.model_name_or_path), cache_dir=model_args.cache_dir, use_fast=model_args.use_fast_tokenizer, revision=model_args.model_revision, @@ -502,9 +439,7 @@ def main(): ) if model.config.decoder_start_token_id is None: - raise ValueError( - "Make sure that `config.decoder_start_token_id` is correctly defined" - ) + raise ValueError("Make sure that `config.decoder_start_token_id` is correctly defined") if model_args.freeze_feature_encoder: model.freeze_feature_encoder() @@ -513,10 +448,7 @@ def main(): model.freeze_encoder() model.model.encoder.gradient_checkpointing = False - if ( - hasattr(model.generation_config, "is_multilingual") - and model.generation_config.is_multilingual - ): + if hasattr(model.generation_config, "is_multilingual") and model.generation_config.is_multilingual: # We only need to set the language and task ids in a multilingual setting tokenizer.set_prefix_tokens(language=data_args.language, task=data_args.task) model.generation_config.language = data_args.language @@ -546,11 +478,7 @@ def main(): model.generation_config.suppress_tokens = model_args.suppress_tokens # 6. Resample speech dataset if necessary - dataset_sampling_rate = ( - next(iter(raw_datasets.values())) - .features[data_args.audio_column_name] - .sampling_rate - ) + dataset_sampling_rate = next(iter(raw_datasets.values())).features[data_args.audio_column_name].sampling_rate if dataset_sampling_rate != feature_extractor.sampling_rate: raw_datasets = raw_datasets.cast_column( data_args.audio_column_name, @@ -559,12 +487,8 @@ def main(): # 7. Preprocessing the datasets. # We need to read the audio files as arrays and tokenize the targets. - max_input_length = ( - data_args.max_duration_in_seconds * feature_extractor.sampling_rate - ) - min_input_length = ( - data_args.min_duration_in_seconds * feature_extractor.sampling_rate - ) + max_input_length = data_args.max_duration_in_seconds * feature_extractor.sampling_rate + min_input_length = data_args.min_duration_in_seconds * feature_extractor.sampling_rate audio_column_name = data_args.audio_column_name num_workers = data_args.preprocessing_num_workers text_column_name = data_args.text_column_name @@ -578,14 +502,10 @@ def main(): ) if data_args.max_train_samples is not None: - raw_datasets["train"] = raw_datasets["train"].select( - range(data_args.max_train_samples) - ) + raw_datasets["train"] = raw_datasets["train"].select(range(data_args.max_train_samples)) if data_args.max_eval_samples is not None: - raw_datasets["eval"] = raw_datasets["eval"].select( - range(data_args.max_eval_samples) - ) + raw_datasets["eval"] = raw_datasets["eval"].select(range(data_args.max_eval_samples)) def prepare_dataset(batch): # process audio @@ -602,11 +522,7 @@ def prepare_dataset(batch): batch["attention_mask"] = inputs.get("attention_mask")[0] # process targets - input_str = ( - batch[text_column_name].lower() - if do_lower_case - else batch[text_column_name] - ) + input_str = batch[text_column_name].lower() if do_lower_case else batch[text_column_name] batch["labels"] = tokenizer(input_str).input_ids return batch @@ -682,9 +598,7 @@ def compute_metrics(pred): eval_dataset=vectorized_datasets["eval"] if training_args.do_eval else None, processing_class=feature_extractor, data_collator=data_collator, - compute_metrics=( - compute_metrics if training_args.predict_with_generate else None - ), + compute_metrics=(compute_metrics if training_args.predict_with_generate else None), ) # 12. Training @@ -703,9 +617,7 @@ def compute_metrics(pred): if data_args.max_train_samples is not None else len(vectorized_datasets["train"]) ) - metrics["train_samples"] = min( - max_train_samples, len(vectorized_datasets["train"]) - ) + metrics["train_samples"] = min(max_train_samples, len(vectorized_datasets["train"])) trainer.log_metrics("train", metrics) trainer.save_metrics("train", metrics) trainer.save_state() @@ -720,13 +632,9 @@ def compute_metrics(pred): num_beams=training_args.generation_num_beams, ) max_eval_samples = ( - data_args.max_eval_samples - if data_args.max_eval_samples is not None - else len(vectorized_datasets["eval"]) - ) - metrics["eval_samples"] = min( - max_eval_samples, len(vectorized_datasets["eval"]) + data_args.max_eval_samples if data_args.max_eval_samples is not None else len(vectorized_datasets["eval"]) ) + metrics["eval_samples"] = min(max_eval_samples, len(vectorized_datasets["eval"])) trainer.log_metrics("eval", metrics) trainer.save_metrics("eval", metrics) @@ -740,9 +648,7 @@ def compute_metrics(pred): kwargs["dataset_tags"] = data_args.dataset_name if data_args.dataset_config_name is not None: kwargs["dataset_args"] = data_args.dataset_config_name - kwargs["dataset"] = ( - f"{data_args.dataset_name} {data_args.dataset_config_name}" - ) + kwargs["dataset"] = f"{data_args.dataset_name} {data_args.dataset_config_name}" else: kwargs["dataset"] = data_args.dataset_name From 5664ade9347943c8f7aabfa53d1bcf2fb9f0d03a Mon Sep 17 00:00:00 2001 From: OMOTAYO OMOYEMI <58476114+tayo4christ@users.noreply.github.com> Date: Fri, 26 Sep 2025 11:34:46 +0100 Subject: [PATCH 04/10] examples(speech): align DataTrainingArguments help with datasets docs; minor wording fixes --- .../run_speech_recognition_ctc.py | 163 ++++++++++++++---- 1 file changed, 125 insertions(+), 38 deletions(-) diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py b/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py index 696ad4d20747..51fc15a8d05e 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py @@ -83,15 +83,21 @@ class ModelArguments: """ model_name_or_path: str = field( - metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} + metadata={ + "help": "Path to pretrained model or model identifier from huggingface.co/models" + } ) tokenizer_name_or_path: Optional[str] = field( default=None, - metadata={"help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models"}, + metadata={ + "help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models" + }, ) cache_dir: Optional[str] = field( default=None, - metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, + metadata={ + "help": "Where do you want to store the pretrained models downloaded from huggingface.co" + }, ) freeze_feature_encoder: bool = field( default=True, @@ -103,9 +109,13 @@ class ModelArguments: ) activation_dropout: float = field( default=0.0, - metadata={"help": "The dropout ratio for activations inside the fully connected layer."}, + metadata={ + "help": "The dropout ratio for activations inside the fully connected layer." + }, + ) + feat_proj_dropout: float = field( + default=0.0, metadata={"help": "The dropout ratio for the projected features."} ) - feat_proj_dropout: float = field(default=0.0, metadata={"help": "The dropout ratio for the projected features."}) hidden_dropout: float = field( default=0.0, metadata={ @@ -144,10 +154,14 @@ class ModelArguments: default=10, metadata={"help": "Length of vector span to mask along the feature axis."}, ) - layerdrop: float = field(default=0.0, metadata={"help": "The LayerDrop probability."}) + layerdrop: float = field( + default=0.0, metadata={"help": "The LayerDrop probability."} + ) ctc_loss_reduction: Optional[str] = field( default="mean", - metadata={"help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'."}, + metadata={ + "help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'." + }, ) ctc_zero_infinity: Optional[bool] = field( default=False, @@ -176,11 +190,15 @@ class DataTrainingArguments: """ dataset_name: str = field( - metadata={"help": "The configuration name of the dataset to use (via the datasets library)."} + metadata={ + "help": "The configuration name of the dataset to use (via the datasets library)." + } ) dataset_config_name: str = field( default=None, - metadata={"help": "The configuration name of the dataset to use (via the datasets library)."}, + metadata={ + "help": "The configuration name of the dataset to use (via the datasets library)." + }, ) train_split_name: str = field( default="train+validation", @@ -199,11 +217,15 @@ class DataTrainingArguments: ) audio_column_name: str = field( default="audio", - metadata={"help": "The name of the dataset column containing the audio data. Defaults to 'audio'"}, + metadata={ + "help": "The name of the dataset column containing the audio data. Defaults to 'audio'" + }, ) text_column_name: str = field( default="text", - metadata={"help": "The name of the dataset column containing the text data. Defaults to 'text'"}, + metadata={ + "help": "The name of the dataset column containing the text data. Defaults to 'text'" + }, ) overwrite_cache: bool = field( default=False, @@ -237,7 +259,9 @@ class DataTrainingArguments: ) eval_metrics: list[str] = list_field( default=["wer"], - metadata={"help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`"}, + metadata={ + "help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`" + }, ) max_duration_in_seconds: float = field( default=20.0, @@ -250,7 +274,9 @@ class DataTrainingArguments: ) min_duration_in_seconds: float = field( default=0.0, - metadata={"help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds"}, + metadata={ + "help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds" + }, ) preprocessing_only: bool = field( default=False, @@ -339,11 +365,18 @@ class DataCollatorCTCWithPadding: pad_to_multiple_of_labels: Optional[int] = None feature_extractor_input_name: Optional[str] = "input_values" - def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> dict[str, torch.Tensor]: + def __call__( + self, features: list[dict[str, Union[list[int], torch.Tensor]]] + ) -> dict[str, torch.Tensor]: # split inputs and labels since they have to be of different lengths and need # different padding methods input_features = [ - {self.feature_extractor_input_name: feature[self.feature_extractor_input_name]} for feature in features + { + self.feature_extractor_input_name: feature[ + self.feature_extractor_input_name + ] + } + for feature in features ] label_features = [{"input_ids": feature["labels"]} for feature in features] @@ -362,7 +395,9 @@ def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> ) # replace padding with -100 to ignore loss correctly - labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100) + labels = labels_batch["input_ids"].masked_fill( + labels_batch.attention_mask.ne(1), -100 + ) batch["labels"] = labels if "attention_mask" in batch: @@ -419,11 +454,15 @@ def main(): # or by passing the --help flag to this script. # We now keep distinct sets of args, for a cleaner separation of concerns. - parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments)) + parser = HfArgumentParser( + (ModelArguments, DataTrainingArguments, TrainingArguments) + ) if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): # If we pass only one argument to the script and it's the path to a json file, # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) + model_args, data_args, training_args = parser.parse_json_file( + json_file=os.path.abspath(sys.argv[1]) + ) else: model_args, data_args, training_args = parser.parse_args_into_dataclasses() @@ -433,7 +472,11 @@ def main(): # Detecting last checkpoint. last_checkpoint = None - if os.path.isdir(training_args.output_dir) and training_args.do_train and not training_args.overwrite_output_dir: + if ( + os.path.isdir(training_args.output_dir) + and training_args.do_train + and not training_args.overwrite_output_dir + ): last_checkpoint = get_last_checkpoint(training_args.output_dir) if last_checkpoint is None and len(os.listdir(training_args.output_dir)) > 0: raise ValueError( @@ -452,7 +495,9 @@ def main(): datefmt="%m/%d/%Y %H:%M:%S", handlers=[logging.StreamHandler(sys.stdout)], ) - logger.setLevel(logging.INFO if is_main_process(training_args.local_rank) else logging.WARN) + logger.setLevel( + logging.INFO if is_main_process(training_args.local_rank) else logging.WARN + ) # Log on each process the small summary: logger.warning( @@ -494,7 +539,9 @@ def main(): ) if data_args.max_train_samples is not None: - raw_datasets["train"] = raw_datasets["train"].select(range(data_args.max_train_samples)) + raw_datasets["train"] = raw_datasets["train"].select( + range(data_args.max_train_samples) + ) if training_args.do_eval: raw_datasets["eval"] = load_dataset( @@ -506,25 +553,33 @@ def main(): ) if data_args.max_eval_samples is not None: - raw_datasets["eval"] = raw_datasets["eval"].select(range(data_args.max_eval_samples)) + raw_datasets["eval"] = raw_datasets["eval"].select( + range(data_args.max_eval_samples) + ) # 2. We remove some special characters from the datasets # that make training complicated and do not help in transcribing the speech # E.g. characters, such as `,` and `.` do not really have an acoustic characteristic # that could be easily picked up by the model chars_to_ignore_regex = ( - f"[{''.join(data_args.chars_to_ignore)}]" if data_args.chars_to_ignore is not None else None + f"[{''.join(data_args.chars_to_ignore)}]" + if data_args.chars_to_ignore is not None + else None ) text_column_name = data_args.text_column_name def remove_special_characters(batch): if chars_to_ignore_regex is not None: - batch["target_text"] = re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " + batch["target_text"] = ( + re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " + ) else: batch["target_text"] = batch[text_column_name].lower() + " " return batch - with training_args.main_process_first(desc="dataset map special characters removal"): + with training_args.main_process_first( + desc="dataset map special characters removal" + ): raw_datasets = raw_datasets.map( remove_special_characters, remove_columns=[text_column_name], @@ -586,7 +641,9 @@ def remove_special_characters(batch): # it is defined by `tokenizer_class` if present in config else by `model_type` tokenizer_kwargs = { "config": config if config.tokenizer_class is not None else None, - "tokenizer_type": (config.model_type if config.tokenizer_class is None else None), + "tokenizer_type": ( + config.model_type if config.tokenizer_class is None else None + ), "unk_token": unk_token, "pad_token": pad_token, "word_delimiter_token": word_delimiter_token, @@ -651,7 +708,11 @@ def remove_special_characters(batch): # via the `feature_extractor` # make sure that dataset decodes audio with correct sampling rate - dataset_sampling_rate = next(iter(raw_datasets.values())).features[data_args.audio_column_name].sampling_rate + dataset_sampling_rate = ( + next(iter(raw_datasets.values())) + .features[data_args.audio_column_name] + .sampling_rate + ) if dataset_sampling_rate != feature_extractor.sampling_rate: raw_datasets = raw_datasets.cast_column( data_args.audio_column_name, @@ -659,8 +720,12 @@ def remove_special_characters(batch): ) # derive max & min input length for sample rate & max duration - max_input_length = data_args.max_duration_in_seconds * feature_extractor.sampling_rate - min_input_length = data_args.min_duration_in_seconds * feature_extractor.sampling_rate + max_input_length = ( + data_args.max_duration_in_seconds * feature_extractor.sampling_rate + ) + min_input_length = ( + data_args.min_duration_in_seconds * feature_extractor.sampling_rate + ) audio_column_name = data_args.audio_column_name num_workers = data_args.preprocessing_num_workers feature_extractor_input_name = feature_extractor.model_input_names[0] @@ -674,8 +739,12 @@ def prepare_dataset(batch): # load audio sample = batch[audio_column_name] - inputs = feature_extractor(sample["array"], sampling_rate=sample["sampling_rate"]) - batch[feature_extractor_input_name] = getattr(inputs, feature_extractor_input_name)[0] + inputs = feature_extractor( + sample["array"], sampling_rate=sample["sampling_rate"] + ) + batch[feature_extractor_input_name] = getattr( + inputs, feature_extractor_input_name + )[0] # take length of raw audio waveform batch["input_length"] = len(sample["array"].squeeze()) @@ -710,7 +779,10 @@ def is_audio_in_length_range(length): # instantiate a data collator and the trainer # Define evaluation metrics during training, *i.e.* word error rate, character error rate - eval_metrics = {metric: evaluate.load(metric, cache_dir=model_args.cache_dir) for metric in data_args.eval_metrics} + eval_metrics = { + metric: evaluate.load(metric, cache_dir=model_args.cache_dir) + for metric in data_args.eval_metrics + } # for large datasets it is advised to run the preprocessing on a # single machine first with ``args.preprocessing_only`` since there will mostly likely @@ -718,7 +790,9 @@ def is_audio_in_length_range(length): # In a second step ``args.preprocessing_only`` can then be set to `False` to load the # cached dataset if data_args.preprocessing_only: - logger.info(f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}") + logger.info( + f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}" + ) return # For languages like Chinese with large vocabulary size, we need to discard logits @@ -735,7 +809,10 @@ def compute_metrics(pred): # we do not want to group tokens when computing the metrics label_str = tokenizer.batch_decode(pred.label_ids, group_tokens=False) - metrics = {k: v.compute(predictions=pred_str, references=label_str) for k, v in eval_metrics.items()} + metrics = { + k: v.compute(predictions=pred_str, references=label_str) + for k, v in eval_metrics.items() + } return metrics @@ -799,7 +876,9 @@ def compute_metrics(pred): if data_args.max_train_samples is not None else len(vectorized_datasets["train"]) ) - metrics["train_samples"] = min(max_train_samples, len(vectorized_datasets["train"])) + metrics["train_samples"] = min( + max_train_samples, len(vectorized_datasets["train"]) + ) trainer.log_metrics("train", metrics) trainer.save_metrics("train", metrics) @@ -811,15 +890,23 @@ def compute_metrics(pred): logger.info("*** Evaluate ***") metrics = trainer.evaluate() max_eval_samples = ( - data_args.max_eval_samples if data_args.max_eval_samples is not None else len(vectorized_datasets["eval"]) + data_args.max_eval_samples + if data_args.max_eval_samples is not None + else len(vectorized_datasets["eval"]) + ) + metrics["eval_samples"] = min( + max_eval_samples, len(vectorized_datasets["eval"]) ) - metrics["eval_samples"] = min(max_eval_samples, len(vectorized_datasets["eval"])) trainer.log_metrics("eval", metrics) trainer.save_metrics("eval", metrics) # Write model card and (optionally) push to hub - config_name = data_args.dataset_config_name if data_args.dataset_config_name is not None else "na" + config_name = ( + data_args.dataset_config_name + if data_args.dataset_config_name is not None + else "na" + ) kwargs = { "finetuned_from": model_args.model_name_or_path, "tasks": "automatic-speech-recognition", From 1f1fd79220d0d4fe275e630632de01245459ece4 Mon Sep 17 00:00:00 2001 From: OMOTAYO OMOYEMI <58476114+tayo4christ@users.noreply.github.com> Date: Fri, 26 Sep 2025 12:23:19 +0100 Subject: [PATCH 05/10] docs/examples(speech): address review remove Hub subsection & Whisper tip; align dataset help text --- examples/pytorch/speech-recognition/README.md | 13 +- .../run_speech_recognition_ctc_adapter.py | 163 ++++++++++++---- .../run_speech_recognition_seq2seq.py | 174 ++++++++++++++---- 3 files changed, 260 insertions(+), 90 deletions(-) diff --git a/examples/pytorch/speech-recognition/README.md b/examples/pytorch/speech-recognition/README.md index ae860abcf6ed..bf6163615a38 100644 --- a/examples/pytorch/speech-recognition/README.md +++ b/examples/pytorch/speech-recognition/README.md @@ -59,15 +59,6 @@ OMP_NUM_THREADS=1 python run_speech_recognition_ctc ... If the environment variable is not set, the training script might freeze, *i.e.* see: https://github.com/pytorch/audio/issues/1021#issuecomment-726915239 --- -### Loading Common Voice from the Hub (recommended) - -Use the `datasets` library to load a pinned Common Voice version from the Hub: - -```python -from datasets import load_dataset -# pick the right version/lang that exists on the Hub (example: CV 17.0 English) -ds = load_dataset("mozilla-foundation/common_voice_17_0", "en", split="train+validation") - ### Single GPU CTC @@ -369,9 +360,7 @@ recognition on one of the [official speech recognition datasets](https://hugging ### Whisper Model We can load all components of the Whisper model directly from the pretrained checkpoint, including the pretrained model weights, feature extractor and tokenizer. We simply have to specify our fine-tuning dataset and training hyperparameters. -> **Tip:** If you see errors about “dataset scripts are no longer supported”, make sure your -> `--dataset_name` points to a Hub dataset like `mozilla-foundation/common_voice_XX_X` -> (e.g., `mozilla-foundation/common_voice_17_0`) rather than a local script. + #### Single GPU Whisper Training The following example shows how to fine-tune the [Whisper small](https://huggingface.co/openai/whisper-small) checkpoint on the Hindi subset of [Common Voice 11](https://huggingface.co/datasets/mozilla-foundation/common_voice_11_0) using a single GPU device in half-precision: diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py b/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py index 6c1d4e4e8323..90213a515001 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py @@ -86,15 +86,21 @@ class ModelArguments: """ model_name_or_path: str = field( - metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} + metadata={ + "help": "Path to pretrained model or model identifier from huggingface.co/models" + } ) tokenizer_name_or_path: Optional[str] = field( default=None, - metadata={"help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models"}, + metadata={ + "help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models" + }, ) cache_dir: Optional[str] = field( default=None, - metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, + metadata={ + "help": "Where do you want to store the pretrained models downloaded from huggingface.co" + }, ) final_dropout: float = field( default=0.0, @@ -128,10 +134,14 @@ class ModelArguments: default=10, metadata={"help": "Length of vector span to mask along the feature axis."}, ) - layerdrop: float = field(default=0.0, metadata={"help": "The LayerDrop probability."}) + layerdrop: float = field( + default=0.0, metadata={"help": "The LayerDrop probability."} + ) ctc_loss_reduction: Optional[str] = field( default="mean", - metadata={"help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'."}, + metadata={ + "help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'." + }, ) adapter_attn_dim: int = field( default=16, @@ -152,7 +162,9 @@ class DataTrainingArguments: """ dataset_name: str = field( - metadata={"help": "The configuration name of the dataset to use (via the datasets library)."} + metadata={ + "help": "The configuration name of the dataset to use (via the datasets library)." + } ) target_language: Optional[str] = field( metadata={ @@ -167,7 +179,9 @@ class DataTrainingArguments: ) dataset_config_name: str = field( default=None, - metadata={"help": "The configuration name of the dataset to use (via the datasets library)."}, + metadata={ + "help": "The configuration name of the dataset to use (via the datasets library)." + }, ) train_split_name: str = field( default="train+validation", @@ -186,11 +200,15 @@ class DataTrainingArguments: ) audio_column_name: str = field( default="audio", - metadata={"help": "The name of the dataset column containing the audio data. Defaults to 'audio'"}, + metadata={ + "help": "The name of the dataset column containing the audio data. Defaults to 'audio'" + }, ) text_column_name: str = field( default="text", - metadata={"help": "The name of the dataset column containing the text data. Defaults to 'text'"}, + metadata={ + "help": "The name of the dataset column containing the text data. Defaults to 'text'" + }, ) overwrite_cache: bool = field( default=False, @@ -224,7 +242,9 @@ class DataTrainingArguments: ) eval_metrics: list[str] = list_field( default=["wer"], - metadata={"help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`"}, + metadata={ + "help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`" + }, ) max_duration_in_seconds: float = field( default=20.0, @@ -237,7 +257,9 @@ class DataTrainingArguments: ) min_duration_in_seconds: float = field( default=0.0, - metadata={"help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds"}, + metadata={ + "help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds" + }, ) preprocessing_only: bool = field( default=False, @@ -283,7 +305,11 @@ class DataTrainingArguments: ) overwrite_lang_vocab: bool = field( default=False, - metadata={"help": ("If :obj:`True`, will overwrite existing `target_language` vocabulary of tokenizer.")}, + metadata={ + "help": ( + "If :obj:`True`, will overwrite existing `target_language` vocabulary of tokenizer." + ) + }, ) @@ -318,10 +344,14 @@ class DataCollatorCTCWithPadding: pad_to_multiple_of: Optional[int] = None pad_to_multiple_of_labels: Optional[int] = None - def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> dict[str, torch.Tensor]: + def __call__( + self, features: list[dict[str, Union[list[int], torch.Tensor]]] + ) -> dict[str, torch.Tensor]: # split inputs and labels since they have to be of different lengths and need # different padding methods - input_features = [{"input_values": feature["input_values"]} for feature in features] + input_features = [ + {"input_values": feature["input_values"]} for feature in features + ] label_features = [{"input_ids": feature["labels"]} for feature in features] batch = self.processor.pad( @@ -339,7 +369,9 @@ def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> ) # replace padding with -100 to ignore loss correctly - labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100) + labels = labels_batch["input_ids"].masked_fill( + labels_batch.attention_mask.ne(1), -100 + ) batch["labels"] = labels if "attention_mask" in batch: @@ -396,11 +428,15 @@ def main(): # or by passing the --help flag to this script. # We now keep distinct sets of args, for a cleaner separation of concerns. - parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments)) + parser = HfArgumentParser( + (ModelArguments, DataTrainingArguments, TrainingArguments) + ) if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): # If we pass only one argument to the script and it's the path to a json file, # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) + model_args, data_args, training_args = parser.parse_json_file( + json_file=os.path.abspath(sys.argv[1]) + ) else: model_args, data_args, training_args = parser.parse_args_into_dataclasses() @@ -410,7 +446,11 @@ def main(): # Detecting last checkpoint. last_checkpoint = None - if os.path.isdir(training_args.output_dir) and training_args.do_train and not training_args.overwrite_output_dir: + if ( + os.path.isdir(training_args.output_dir) + and training_args.do_train + and not training_args.overwrite_output_dir + ): last_checkpoint = get_last_checkpoint(training_args.output_dir) if last_checkpoint is None and len(os.listdir(training_args.output_dir)) > 0: raise ValueError( @@ -429,7 +469,9 @@ def main(): datefmt="%m/%d/%Y %H:%M:%S", handlers=[logging.StreamHandler(sys.stdout)], ) - logger.setLevel(logging.INFO if is_main_process(training_args.local_rank) else logging.WARN) + logger.setLevel( + logging.INFO if is_main_process(training_args.local_rank) else logging.WARN + ) # Log on each process the small summary: logger.warning( @@ -471,7 +513,9 @@ def main(): ) if data_args.max_train_samples is not None: - raw_datasets["train"] = raw_datasets["train"].select(range(data_args.max_train_samples)) + raw_datasets["train"] = raw_datasets["train"].select( + range(data_args.max_train_samples) + ) if training_args.do_eval: raw_datasets["eval"] = load_dataset( @@ -483,25 +527,33 @@ def main(): ) if data_args.max_eval_samples is not None: - raw_datasets["eval"] = raw_datasets["eval"].select(range(data_args.max_eval_samples)) + raw_datasets["eval"] = raw_datasets["eval"].select( + range(data_args.max_eval_samples) + ) # 2. We remove some special characters from the datasets # that make training complicated and do not help in transcribing the speech # E.g. characters, such as `,` and `.` do not really have an acoustic characteristic # that could be easily picked up by the model chars_to_ignore_regex = ( - f"[{''.join(data_args.chars_to_ignore)}]" if data_args.chars_to_ignore is not None else None + f"[{''.join(data_args.chars_to_ignore)}]" + if data_args.chars_to_ignore is not None + else None ) text_column_name = data_args.text_column_name def remove_special_characters(batch): if chars_to_ignore_regex is not None: - batch["target_text"] = re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " + batch["target_text"] = ( + re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " + ) else: batch["target_text"] = batch[text_column_name].lower() + " " return batch - with training_args.main_process_first(desc="dataset map special characters removal"): + with training_args.main_process_first( + desc="dataset map special characters removal" + ): raw_datasets = raw_datasets.map( remove_special_characters, remove_columns=[text_column_name], @@ -541,9 +593,14 @@ def remove_special_characters(batch): ) vocab_dict = tokenizer.vocab.copy() if tokenizer.target_lang is None: - raise ValueError("Make sure to load a multi-lingual tokenizer with a set target language.") + raise ValueError( + "Make sure to load a multi-lingual tokenizer with a set target language." + ) - if data_args.target_language in tokenizer.vocab and not data_args.overwrite_lang_vocab: + if ( + data_args.target_language in tokenizer.vocab + and not data_args.overwrite_lang_vocab + ): logger.info( "Adapter language already exists." " Skipping vocabulary creating. If you want to create a new vocabulary" @@ -590,7 +647,9 @@ def remove_special_characters(batch): # it is defined by `tokenizer_class` if present in config else by `model_type` tokenizer_kwargs = { "config": config if config.tokenizer_class is not None else None, - "tokenizer_type": (config.model_type if config.tokenizer_class is None else None), + "tokenizer_type": ( + config.model_type if config.tokenizer_class is None else None + ), "unk_token": unk_token, "pad_token": pad_token, "word_delimiter_token": word_delimiter_token, @@ -659,7 +718,11 @@ def remove_special_characters(batch): # via the `feature_extractor` # make sure that dataset decodes audio with correct sampling rate - dataset_sampling_rate = next(iter(raw_datasets.values())).features[data_args.audio_column_name].sampling_rate + dataset_sampling_rate = ( + next(iter(raw_datasets.values())) + .features[data_args.audio_column_name] + .sampling_rate + ) if dataset_sampling_rate != feature_extractor.sampling_rate: raw_datasets = raw_datasets.cast_column( data_args.audio_column_name, @@ -667,8 +730,12 @@ def remove_special_characters(batch): ) # derive max & min input length for sample rate & max duration - max_input_length = data_args.max_duration_in_seconds * feature_extractor.sampling_rate - min_input_length = data_args.min_duration_in_seconds * feature_extractor.sampling_rate + max_input_length = ( + data_args.max_duration_in_seconds * feature_extractor.sampling_rate + ) + min_input_length = ( + data_args.min_duration_in_seconds * feature_extractor.sampling_rate + ) audio_column_name = data_args.audio_column_name num_workers = data_args.preprocessing_num_workers @@ -678,7 +745,9 @@ def prepare_dataset(batch): # load audio sample = batch[audio_column_name] - inputs = feature_extractor(sample["array"], sampling_rate=sample["sampling_rate"]) + inputs = feature_extractor( + sample["array"], sampling_rate=sample["sampling_rate"] + ) batch["input_values"] = inputs.input_values[0] batch["input_length"] = len(batch["input_values"]) @@ -709,7 +778,10 @@ def is_audio_in_length_range(length): # instantiate a data collator and the trainer # Define evaluation metrics during training, *i.e.* word error rate, character error rate - eval_metrics = {metric: evaluate.load(metric, cache_dir=model_args.cache_dir) for metric in data_args.eval_metrics} + eval_metrics = { + metric: evaluate.load(metric, cache_dir=model_args.cache_dir) + for metric in data_args.eval_metrics + } # for large datasets it is advised to run the preprocessing on a # single machine first with ``args.preprocessing_only`` since there will mostly likely @@ -717,7 +789,9 @@ def is_audio_in_length_range(length): # In a second step ``args.preprocessing_only`` can then be set to `False` to load the # cached dataset if data_args.preprocessing_only: - logger.info(f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}") + logger.info( + f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}" + ) return def compute_metrics(pred): @@ -730,7 +804,10 @@ def compute_metrics(pred): # we do not want to group tokens when computing the metrics label_str = tokenizer.batch_decode(pred.label_ids, group_tokens=False) - metrics = {k: v.compute(predictions=pred_str, references=label_str) for k, v in eval_metrics.items()} + metrics = { + k: v.compute(predictions=pred_str, references=label_str) + for k, v in eval_metrics.items() + } return metrics @@ -791,7 +868,9 @@ def compute_metrics(pred): if data_args.max_train_samples is not None else len(vectorized_datasets["train"]) ) - metrics["train_samples"] = min(max_train_samples, len(vectorized_datasets["train"])) + metrics["train_samples"] = min( + max_train_samples, len(vectorized_datasets["train"]) + ) trainer.log_metrics("train", metrics) trainer.save_metrics("train", metrics) @@ -803,15 +882,23 @@ def compute_metrics(pred): logger.info("*** Evaluate ***") metrics = trainer.evaluate() max_eval_samples = ( - data_args.max_eval_samples if data_args.max_eval_samples is not None else len(vectorized_datasets["eval"]) + data_args.max_eval_samples + if data_args.max_eval_samples is not None + else len(vectorized_datasets["eval"]) + ) + metrics["eval_samples"] = min( + max_eval_samples, len(vectorized_datasets["eval"]) ) - metrics["eval_samples"] = min(max_eval_samples, len(vectorized_datasets["eval"])) trainer.log_metrics("eval", metrics) trainer.save_metrics("eval", metrics) # Write model card and (optionally) push to hub - config_name = data_args.dataset_config_name if data_args.dataset_config_name is not None else "na" + config_name = ( + data_args.dataset_config_name + if data_args.dataset_config_name is not None + else "na" + ) kwargs = { "finetuned_from": model_args.model_name_or_path, "tasks": "automatic-speech-recognition", diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py b/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py index 88830b583a07..a46c80b0426f 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py @@ -77,31 +77,45 @@ class ModelArguments: """ model_name_or_path: str = field( - metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} + metadata={ + "help": "Path to pretrained model or model identifier from huggingface.co/models" + } ) config_name: Optional[str] = field( default=None, - metadata={"help": "Pretrained config name or path if not the same as model_name"}, + metadata={ + "help": "Pretrained config name or path if not the same as model_name" + }, ) tokenizer_name: Optional[str] = field( default=None, - metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"}, + metadata={ + "help": "Pretrained tokenizer name or path if not the same as model_name" + }, ) feature_extractor_name: Optional[str] = field( default=None, - metadata={"help": "feature extractor name or path if not the same as model_name"}, + metadata={ + "help": "feature extractor name or path if not the same as model_name" + }, ) cache_dir: Optional[str] = field( default=None, - metadata={"help": "Where to store the pretrained models downloaded from huggingface.co"}, + metadata={ + "help": "Where to store the pretrained models downloaded from huggingface.co" + }, ) use_fast_tokenizer: bool = field( default=True, - metadata={"help": "Whether to use one of the fast tokenizer (backed by the tokenizers library) or not."}, + metadata={ + "help": "Whether to use one of the fast tokenizer (backed by the tokenizers library) or not." + }, ) model_revision: str = field( default="main", - metadata={"help": "The specific model version to use (can be a branch name, tag name or commit id)."}, + metadata={ + "help": "The specific model version to use (can be a branch name, tag name or commit id)." + }, ) token: str = field( default=None, @@ -132,7 +146,9 @@ class ModelArguments: ) forced_decoder_ids: list[list[int]] = field( default=None, - metadata={"help": "Deprecated. Please use the `language` and `task` arguments instead."}, + metadata={ + "help": "Deprecated. Please use the `language` and `task` arguments instead." + }, ) suppress_tokens: list[int] = field( default=None, @@ -163,7 +179,9 @@ class DataTrainingArguments: ) dataset_config_name: Optional[str] = field( default=None, - metadata={"help": "The configuration name of the dataset to use (via the datasets library)."}, + metadata={ + "help": "The configuration name of the dataset to use (via the datasets library)." + }, ) overwrite_cache: bool = field( default=False, @@ -193,11 +211,15 @@ class DataTrainingArguments: ) audio_column_name: str = field( default="audio", - metadata={"help": "The name of the dataset column containing the audio data. Defaults to 'audio'"}, + metadata={ + "help": "The name of the dataset column containing the audio data. Defaults to 'audio'" + }, ) text_column_name: str = field( default="text", - metadata={"help": "The name of the dataset column containing the text data. Defaults to 'text'"}, + metadata={ + "help": "The name of the dataset column containing the text data. Defaults to 'text'" + }, ) max_duration_in_seconds: float = field( default=20.0, @@ -210,7 +232,9 @@ class DataTrainingArguments: ) min_duration_in_seconds: float = field( default=0.0, - metadata={"help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds"}, + metadata={ + "help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds" + }, ) preprocessing_only: bool = field( default=False, @@ -250,7 +274,9 @@ class DataTrainingArguments: ) task: str = field( default="transcribe", - metadata={"help": "Task, either `transcribe` for speech recognition or `translate` for speech translation."}, + metadata={ + "help": "Task, either `transcribe` for speech recognition or `translate` for speech translation." + }, ) @@ -271,22 +297,32 @@ class DataCollatorSpeechSeq2SeqWithPadding: decoder_start_token_id: int forward_attention_mask: bool - def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> dict[str, torch.Tensor]: + def __call__( + self, features: list[dict[str, Union[list[int], torch.Tensor]]] + ) -> dict[str, torch.Tensor]: # split inputs and labels since they have to be of different lengths and need # different padding methods model_input_name = self.processor.model_input_names[0] - input_features = [{model_input_name: feature[model_input_name]} for feature in features] + input_features = [ + {model_input_name: feature[model_input_name]} for feature in features + ] label_features = [{"input_ids": feature["labels"]} for feature in features] - batch = self.processor.feature_extractor.pad(input_features, return_tensors="pt") + batch = self.processor.feature_extractor.pad( + input_features, return_tensors="pt" + ) if self.forward_attention_mask: - batch["attention_mask"] = torch.LongTensor([feature["attention_mask"] for feature in features]) + batch["attention_mask"] = torch.LongTensor( + [feature["attention_mask"] for feature in features] + ) labels_batch = self.processor.tokenizer.pad(label_features, return_tensors="pt") # replace padding with -100 to ignore loss correctly - labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100) + labels = labels_batch["input_ids"].masked_fill( + labels_batch.attention_mask.ne(1), -100 + ) # if bos token is appended in previous tokenization step, # cut bos token here as it's append later anyways @@ -303,12 +339,16 @@ def main(): # See all possible arguments in src/transformers/training_args.py # or by passing the --help flag to this script. # We now keep distinct sets of args, for a cleaner separation of concerns. - parser = HfArgumentParser((ModelArguments, DataTrainingArguments, Seq2SeqTrainingArguments)) + parser = HfArgumentParser( + (ModelArguments, DataTrainingArguments, Seq2SeqTrainingArguments) + ) if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): # If we pass only one argument to the script and it's the path to a json file, # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) + model_args, data_args, training_args = parser.parse_json_file( + json_file=os.path.abspath(sys.argv[1]) + ) else: model_args, data_args, training_args = parser.parse_args_into_dataclasses() @@ -329,7 +369,9 @@ def main(): transformers.utils.logging.enable_default_handler() transformers.utils.logging.enable_explicit_format() - logger.setLevel(logging.INFO if is_main_process(training_args.local_rank) else logging.WARN) + logger.setLevel( + logging.INFO if is_main_process(training_args.local_rank) else logging.WARN + ) # Log on each process the small summary: logger.warning( @@ -345,14 +387,20 @@ def main(): # 3. Detecting last checkpoint and eventually continue from last checkpoint last_checkpoint = None - if os.path.isdir(training_args.output_dir) and training_args.do_train and not training_args.overwrite_output_dir: + if ( + os.path.isdir(training_args.output_dir) + and training_args.do_train + and not training_args.overwrite_output_dir + ): last_checkpoint = get_last_checkpoint(training_args.output_dir) if last_checkpoint is None and len(os.listdir(training_args.output_dir)) > 0: raise ValueError( f"Output directory ({training_args.output_dir}) already exists and is not empty. " "Use --overwrite_output_dir to overcome." ) - elif last_checkpoint is not None and training_args.resume_from_checkpoint is None: + elif ( + last_checkpoint is not None and training_args.resume_from_checkpoint is None + ): logger.info( f"Checkpoint detected, resuming training at {last_checkpoint}. To avoid this behavior, change " "the `--output_dir` or add `--overwrite_output_dir` to train from scratch." @@ -384,7 +432,10 @@ def main(): trust_remote_code=model_args.trust_remote_code, ) - if data_args.audio_column_name not in next(iter(raw_datasets.values())).column_names: + if ( + data_args.audio_column_name + not in next(iter(raw_datasets.values())).column_names + ): raise ValueError( f"--audio_column_name '{data_args.audio_column_name}' not found in dataset '{data_args.dataset_name}'. " "Make sure to set `--audio_column_name` to the correct audio column - one of " @@ -403,7 +454,11 @@ def main(): # Distributed training: # The .from_pretrained methods guarantee that only one local process can concurrently config = AutoConfig.from_pretrained( - (model_args.config_name if model_args.config_name else model_args.model_name_or_path), + ( + model_args.config_name + if model_args.config_name + else model_args.model_name_or_path + ), cache_dir=model_args.cache_dir, revision=model_args.model_revision, token=model_args.token, @@ -415,14 +470,22 @@ def main(): config.update({"apply_spec_augment": model_args.apply_spec_augment}) feature_extractor = AutoFeatureExtractor.from_pretrained( - (model_args.feature_extractor_name if model_args.feature_extractor_name else model_args.model_name_or_path), + ( + model_args.feature_extractor_name + if model_args.feature_extractor_name + else model_args.model_name_or_path + ), cache_dir=model_args.cache_dir, revision=model_args.model_revision, token=model_args.token, trust_remote_code=model_args.trust_remote_code, ) tokenizer = AutoTokenizer.from_pretrained( - (model_args.tokenizer_name if model_args.tokenizer_name else model_args.model_name_or_path), + ( + model_args.tokenizer_name + if model_args.tokenizer_name + else model_args.model_name_or_path + ), cache_dir=model_args.cache_dir, use_fast=model_args.use_fast_tokenizer, revision=model_args.model_revision, @@ -439,7 +502,9 @@ def main(): ) if model.config.decoder_start_token_id is None: - raise ValueError("Make sure that `config.decoder_start_token_id` is correctly defined") + raise ValueError( + "Make sure that `config.decoder_start_token_id` is correctly defined" + ) if model_args.freeze_feature_encoder: model.freeze_feature_encoder() @@ -448,7 +513,10 @@ def main(): model.freeze_encoder() model.model.encoder.gradient_checkpointing = False - if hasattr(model.generation_config, "is_multilingual") and model.generation_config.is_multilingual: + if ( + hasattr(model.generation_config, "is_multilingual") + and model.generation_config.is_multilingual + ): # We only need to set the language and task ids in a multilingual setting tokenizer.set_prefix_tokens(language=data_args.language, task=data_args.task) model.generation_config.language = data_args.language @@ -478,7 +546,11 @@ def main(): model.generation_config.suppress_tokens = model_args.suppress_tokens # 6. Resample speech dataset if necessary - dataset_sampling_rate = next(iter(raw_datasets.values())).features[data_args.audio_column_name].sampling_rate + dataset_sampling_rate = ( + next(iter(raw_datasets.values())) + .features[data_args.audio_column_name] + .sampling_rate + ) if dataset_sampling_rate != feature_extractor.sampling_rate: raw_datasets = raw_datasets.cast_column( data_args.audio_column_name, @@ -487,8 +559,12 @@ def main(): # 7. Preprocessing the datasets. # We need to read the audio files as arrays and tokenize the targets. - max_input_length = data_args.max_duration_in_seconds * feature_extractor.sampling_rate - min_input_length = data_args.min_duration_in_seconds * feature_extractor.sampling_rate + max_input_length = ( + data_args.max_duration_in_seconds * feature_extractor.sampling_rate + ) + min_input_length = ( + data_args.min_duration_in_seconds * feature_extractor.sampling_rate + ) audio_column_name = data_args.audio_column_name num_workers = data_args.preprocessing_num_workers text_column_name = data_args.text_column_name @@ -502,10 +578,14 @@ def main(): ) if data_args.max_train_samples is not None: - raw_datasets["train"] = raw_datasets["train"].select(range(data_args.max_train_samples)) + raw_datasets["train"] = raw_datasets["train"].select( + range(data_args.max_train_samples) + ) if data_args.max_eval_samples is not None: - raw_datasets["eval"] = raw_datasets["eval"].select(range(data_args.max_eval_samples)) + raw_datasets["eval"] = raw_datasets["eval"].select( + range(data_args.max_eval_samples) + ) def prepare_dataset(batch): # process audio @@ -522,7 +602,11 @@ def prepare_dataset(batch): batch["attention_mask"] = inputs.get("attention_mask")[0] # process targets - input_str = batch[text_column_name].lower() if do_lower_case else batch[text_column_name] + input_str = ( + batch[text_column_name].lower() + if do_lower_case + else batch[text_column_name] + ) batch["labels"] = tokenizer(input_str).input_ids return batch @@ -598,7 +682,9 @@ def compute_metrics(pred): eval_dataset=vectorized_datasets["eval"] if training_args.do_eval else None, processing_class=feature_extractor, data_collator=data_collator, - compute_metrics=(compute_metrics if training_args.predict_with_generate else None), + compute_metrics=( + compute_metrics if training_args.predict_with_generate else None + ), ) # 12. Training @@ -617,7 +703,9 @@ def compute_metrics(pred): if data_args.max_train_samples is not None else len(vectorized_datasets["train"]) ) - metrics["train_samples"] = min(max_train_samples, len(vectorized_datasets["train"])) + metrics["train_samples"] = min( + max_train_samples, len(vectorized_datasets["train"]) + ) trainer.log_metrics("train", metrics) trainer.save_metrics("train", metrics) trainer.save_state() @@ -632,9 +720,13 @@ def compute_metrics(pred): num_beams=training_args.generation_num_beams, ) max_eval_samples = ( - data_args.max_eval_samples if data_args.max_eval_samples is not None else len(vectorized_datasets["eval"]) + data_args.max_eval_samples + if data_args.max_eval_samples is not None + else len(vectorized_datasets["eval"]) + ) + metrics["eval_samples"] = min( + max_eval_samples, len(vectorized_datasets["eval"]) ) - metrics["eval_samples"] = min(max_eval_samples, len(vectorized_datasets["eval"])) trainer.log_metrics("eval", metrics) trainer.save_metrics("eval", metrics) @@ -648,7 +740,9 @@ def compute_metrics(pred): kwargs["dataset_tags"] = data_args.dataset_name if data_args.dataset_config_name is not None: kwargs["dataset_args"] = data_args.dataset_config_name - kwargs["dataset"] = f"{data_args.dataset_name} {data_args.dataset_config_name}" + kwargs["dataset"] = ( + f"{data_args.dataset_name} {data_args.dataset_config_name}" + ) else: kwargs["dataset"] = data_args.dataset_name From 393567060d4c0223d72c37accefa62959f623bbe Mon Sep 17 00:00:00 2001 From: OMOTAYO OMOYEMI <58476114+tayo4christ@users.noreply.github.com> Date: Fri, 26 Sep 2025 13:12:05 +0100 Subject: [PATCH 06/10] style: apply ruff/black/usort/codespell on examples/speech-recognition --- examples/pytorch/speech-recognition/README.md | 2 +- .../speech-recognition/run_speech_recognition_ctc.py | 4 ++-- .../run_speech_recognition_ctc_adapter.py | 6 +++--- .../speech-recognition/run_speech_recognition_seq2seq.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/pytorch/speech-recognition/README.md b/examples/pytorch/speech-recognition/README.md index bf6163615a38..b97080c67e56 100644 --- a/examples/pytorch/speech-recognition/README.md +++ b/examples/pytorch/speech-recognition/README.md @@ -314,7 +314,7 @@ below 27%. For an example run, you can have a look at [`patrickvonplaten/wav2vec2-common_voice-tr-mms-demo`](https://huggingface.co/patrickvonplaten/wav2vec2-common_voice-tr-mms-demo). -If you'd like to train another adapter model with the same base model, you can simply re-use the same `--output_dir`, +If you'd like to train another adapter model with the same base model, you can simply reuse the same `--output_dir`, but make sure to pass the `--output_dir` folder also to `--tokenizer_name_or_path` so that the vocabulary is not overwritten but **extended**. Assuming you would like to train adapter weights on Swedish in addition to Turkish and save the adapter weights in the same model repo, you can run: diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py b/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py index 51fc15a8d05e..6632346be6d5 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py @@ -40,9 +40,9 @@ import datasets import evaluate import torch -from datasets import DatasetDict, load_dataset import transformers +from datasets import DatasetDict, load_dataset from transformers import ( AutoConfig, AutoFeatureExtractor, @@ -50,10 +50,10 @@ AutoProcessor, AutoTokenizer, HfArgumentParser, + set_seed, Trainer, TrainingArguments, Wav2Vec2Processor, - set_seed, ) from transformers.trainer_utils import get_last_checkpoint, is_main_process from transformers.utils import check_min_version, send_example_telemetry diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py b/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py index 90213a515001..42baf1c0819f 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py @@ -41,10 +41,10 @@ import evaluate import numpy as np import torch -from datasets import DatasetDict, load_dataset -from safetensors.torch import save_file as safe_save_file import transformers +from datasets import DatasetDict, load_dataset +from safetensors.torch import save_file as safe_save_file from transformers import ( AutoConfig, AutoFeatureExtractor, @@ -52,10 +52,10 @@ AutoProcessor, AutoTokenizer, HfArgumentParser, + set_seed, Trainer, TrainingArguments, Wav2Vec2Processor, - set_seed, ) from transformers.models.wav2vec2.modeling_wav2vec2 import WAV2VEC2_ADAPTER_SAFE_FILE from transformers.trainer_utils import get_last_checkpoint, is_main_process diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py b/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py index a46c80b0426f..6db9e9505363 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py @@ -40,9 +40,9 @@ import datasets import evaluate import torch -from datasets import DatasetDict, load_dataset import transformers +from datasets import DatasetDict, load_dataset from transformers import ( AutoConfig, AutoFeatureExtractor, From 39445b79faa8e5aa2abb04068785373c5d099d1d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 30 Sep 2025 07:41:53 +0000 Subject: [PATCH 07/10] Apply style fixes --- .../run_speech_recognition_ctc.py | 167 ++++------------- .../run_speech_recognition_ctc_adapter.py | 169 ++++------------- .../run_speech_recognition_seq2seq.py | 176 ++++-------------- 3 files changed, 122 insertions(+), 390 deletions(-) diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py b/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py index f7dafd7c5670..8a0e44184ecd 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py @@ -40,9 +40,9 @@ import datasets import evaluate import torch +from datasets import DatasetDict, load_dataset import transformers -from datasets import DatasetDict, load_dataset from transformers import ( AutoConfig, AutoFeatureExtractor, @@ -50,10 +50,10 @@ AutoProcessor, AutoTokenizer, HfArgumentParser, - set_seed, Trainer, TrainingArguments, Wav2Vec2Processor, + set_seed, ) from transformers.trainer_utils import get_last_checkpoint, is_main_process from transformers.utils import check_min_version @@ -83,21 +83,15 @@ class ModelArguments: """ model_name_or_path: str = field( - metadata={ - "help": "Path to pretrained model or model identifier from huggingface.co/models" - } + metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} ) tokenizer_name_or_path: Optional[str] = field( default=None, - metadata={ - "help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models" - }, + metadata={"help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models"}, ) cache_dir: Optional[str] = field( default=None, - metadata={ - "help": "Where do you want to store the pretrained models downloaded from huggingface.co" - }, + metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, ) freeze_feature_encoder: bool = field( default=True, @@ -109,13 +103,9 @@ class ModelArguments: ) activation_dropout: float = field( default=0.0, - metadata={ - "help": "The dropout ratio for activations inside the fully connected layer." - }, - ) - feat_proj_dropout: float = field( - default=0.0, metadata={"help": "The dropout ratio for the projected features."} + metadata={"help": "The dropout ratio for activations inside the fully connected layer."}, ) + feat_proj_dropout: float = field(default=0.0, metadata={"help": "The dropout ratio for the projected features."}) hidden_dropout: float = field( default=0.0, metadata={ @@ -154,14 +144,10 @@ class ModelArguments: default=10, metadata={"help": "Length of vector span to mask along the feature axis."}, ) - layerdrop: float = field( - default=0.0, metadata={"help": "The LayerDrop probability."} - ) + layerdrop: float = field(default=0.0, metadata={"help": "The LayerDrop probability."}) ctc_loss_reduction: Optional[str] = field( default="mean", - metadata={ - "help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'." - }, + metadata={"help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'."}, ) ctc_zero_infinity: Optional[bool] = field( default=False, @@ -190,15 +176,11 @@ class DataTrainingArguments: """ dataset_name: str = field( - metadata={ - "help": "The configuration name of the dataset to use (via the datasets library)." - } + metadata={"help": "The configuration name of the dataset to use (via the datasets library)."} ) dataset_config_name: str = field( default=None, - metadata={ - "help": "The configuration name of the dataset to use (via the datasets library)." - }, + metadata={"help": "The configuration name of the dataset to use (via the datasets library)."}, ) train_split_name: str = field( default="train+validation", @@ -217,15 +199,11 @@ class DataTrainingArguments: ) audio_column_name: str = field( default="audio", - metadata={ - "help": "The name of the dataset column containing the audio data. Defaults to 'audio'" - }, + metadata={"help": "The name of the dataset column containing the audio data. Defaults to 'audio'"}, ) text_column_name: str = field( default="text", - metadata={ - "help": "The name of the dataset column containing the text data. Defaults to 'text'" - }, + metadata={"help": "The name of the dataset column containing the text data. Defaults to 'text'"}, ) overwrite_cache: bool = field( default=False, @@ -259,9 +237,7 @@ class DataTrainingArguments: ) eval_metrics: list[str] = list_field( default=["wer"], - metadata={ - "help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`" - }, + metadata={"help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`"}, ) max_duration_in_seconds: float = field( default=20.0, @@ -274,9 +250,7 @@ class DataTrainingArguments: ) min_duration_in_seconds: float = field( default=0.0, - metadata={ - "help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds" - }, + metadata={"help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds"}, ) preprocessing_only: bool = field( default=False, @@ -365,18 +339,11 @@ class DataCollatorCTCWithPadding: pad_to_multiple_of_labels: Optional[int] = None feature_extractor_input_name: Optional[str] = "input_values" - def __call__( - self, features: list[dict[str, Union[list[int], torch.Tensor]]] - ) -> dict[str, torch.Tensor]: + def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> dict[str, torch.Tensor]: # split inputs and labels since they have to be of different lengths and need # different padding methods input_features = [ - { - self.feature_extractor_input_name: feature[ - self.feature_extractor_input_name - ] - } - for feature in features + {self.feature_extractor_input_name: feature[self.feature_extractor_input_name]} for feature in features ] label_features = [{"input_ids": feature["labels"]} for feature in features] @@ -395,9 +362,7 @@ def __call__( ) # replace padding with -100 to ignore loss correctly - labels = labels_batch["input_ids"].masked_fill( - labels_batch.attention_mask.ne(1), -100 - ) + labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100) batch["labels"] = labels if "attention_mask" in batch: @@ -454,25 +419,17 @@ def main(): # or by passing the --help flag to this script. # We now keep distinct sets of args, for a cleaner separation of concerns. - parser = HfArgumentParser( - (ModelArguments, DataTrainingArguments, TrainingArguments) - ) + parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments)) if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): # If we pass only one argument to the script and it's the path to a json file, # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file( - json_file=os.path.abspath(sys.argv[1]) - ) + model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) else: model_args, data_args, training_args = parser.parse_args_into_dataclasses() # Detecting last checkpoint. last_checkpoint = None - if ( - os.path.isdir(training_args.output_dir) - and training_args.do_train - and not training_args.overwrite_output_dir - ): + if os.path.isdir(training_args.output_dir) and training_args.do_train and not training_args.overwrite_output_dir: last_checkpoint = get_last_checkpoint(training_args.output_dir) if last_checkpoint is None and len(os.listdir(training_args.output_dir)) > 0: raise ValueError( @@ -491,9 +448,7 @@ def main(): datefmt="%m/%d/%Y %H:%M:%S", handlers=[logging.StreamHandler(sys.stdout)], ) - logger.setLevel( - logging.INFO if is_main_process(training_args.local_rank) else logging.WARN - ) + logger.setLevel(logging.INFO if is_main_process(training_args.local_rank) else logging.WARN) # Log on each process the small summary: logger.warning( @@ -535,9 +490,7 @@ def main(): ) if data_args.max_train_samples is not None: - raw_datasets["train"] = raw_datasets["train"].select( - range(data_args.max_train_samples) - ) + raw_datasets["train"] = raw_datasets["train"].select(range(data_args.max_train_samples)) if training_args.do_eval: raw_datasets["eval"] = load_dataset( @@ -549,33 +502,25 @@ def main(): ) if data_args.max_eval_samples is not None: - raw_datasets["eval"] = raw_datasets["eval"].select( - range(data_args.max_eval_samples) - ) + raw_datasets["eval"] = raw_datasets["eval"].select(range(data_args.max_eval_samples)) # 2. We remove some special characters from the datasets # that make training complicated and do not help in transcribing the speech # E.g. characters, such as `,` and `.` do not really have an acoustic characteristic # that could be easily picked up by the model chars_to_ignore_regex = ( - f"[{''.join(data_args.chars_to_ignore)}]" - if data_args.chars_to_ignore is not None - else None + f"[{''.join(data_args.chars_to_ignore)}]" if data_args.chars_to_ignore is not None else None ) text_column_name = data_args.text_column_name def remove_special_characters(batch): if chars_to_ignore_regex is not None: - batch["target_text"] = ( - re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " - ) + batch["target_text"] = re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " else: batch["target_text"] = batch[text_column_name].lower() + " " return batch - with training_args.main_process_first( - desc="dataset map special characters removal" - ): + with training_args.main_process_first(desc="dataset map special characters removal"): raw_datasets = raw_datasets.map( remove_special_characters, remove_columns=[text_column_name], @@ -637,9 +582,7 @@ def remove_special_characters(batch): # it is defined by `tokenizer_class` if present in config else by `model_type` tokenizer_kwargs = { "config": config if config.tokenizer_class is not None else None, - "tokenizer_type": ( - config.model_type if config.tokenizer_class is None else None - ), + "tokenizer_type": (config.model_type if config.tokenizer_class is None else None), "unk_token": unk_token, "pad_token": pad_token, "word_delimiter_token": word_delimiter_token, @@ -704,11 +647,7 @@ def remove_special_characters(batch): # via the `feature_extractor` # make sure that dataset decodes audio with correct sampling rate - dataset_sampling_rate = ( - next(iter(raw_datasets.values())) - .features[data_args.audio_column_name] - .sampling_rate - ) + dataset_sampling_rate = next(iter(raw_datasets.values())).features[data_args.audio_column_name].sampling_rate if dataset_sampling_rate != feature_extractor.sampling_rate: raw_datasets = raw_datasets.cast_column( data_args.audio_column_name, @@ -716,12 +655,8 @@ def remove_special_characters(batch): ) # derive max & min input length for sample rate & max duration - max_input_length = ( - data_args.max_duration_in_seconds * feature_extractor.sampling_rate - ) - min_input_length = ( - data_args.min_duration_in_seconds * feature_extractor.sampling_rate - ) + max_input_length = data_args.max_duration_in_seconds * feature_extractor.sampling_rate + min_input_length = data_args.min_duration_in_seconds * feature_extractor.sampling_rate audio_column_name = data_args.audio_column_name num_workers = data_args.preprocessing_num_workers feature_extractor_input_name = feature_extractor.model_input_names[0] @@ -735,12 +670,8 @@ def prepare_dataset(batch): # load audio sample = batch[audio_column_name] - inputs = feature_extractor( - sample["array"], sampling_rate=sample["sampling_rate"] - ) - batch[feature_extractor_input_name] = getattr( - inputs, feature_extractor_input_name - )[0] + inputs = feature_extractor(sample["array"], sampling_rate=sample["sampling_rate"]) + batch[feature_extractor_input_name] = getattr(inputs, feature_extractor_input_name)[0] # take length of raw audio waveform batch["input_length"] = len(sample["array"].squeeze()) @@ -775,10 +706,7 @@ def is_audio_in_length_range(length): # instantiate a data collator and the trainer # Define evaluation metrics during training, *i.e.* word error rate, character error rate - eval_metrics = { - metric: evaluate.load(metric, cache_dir=model_args.cache_dir) - for metric in data_args.eval_metrics - } + eval_metrics = {metric: evaluate.load(metric, cache_dir=model_args.cache_dir) for metric in data_args.eval_metrics} # for large datasets it is advised to run the preprocessing on a # single machine first with ``args.preprocessing_only`` since there will mostly likely @@ -786,9 +714,7 @@ def is_audio_in_length_range(length): # In a second step ``args.preprocessing_only`` can then be set to `False` to load the # cached dataset if data_args.preprocessing_only: - logger.info( - f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}" - ) + logger.info(f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}") return # For languages like Chinese with large vocabulary size, we need to discard logits @@ -805,10 +731,7 @@ def compute_metrics(pred): # we do not want to group tokens when computing the metrics label_str = tokenizer.batch_decode(pred.label_ids, group_tokens=False) - metrics = { - k: v.compute(predictions=pred_str, references=label_str) - for k, v in eval_metrics.items() - } + metrics = {k: v.compute(predictions=pred_str, references=label_str) for k, v in eval_metrics.items()} return metrics @@ -872,9 +795,7 @@ def compute_metrics(pred): if data_args.max_train_samples is not None else len(vectorized_datasets["train"]) ) - metrics["train_samples"] = min( - max_train_samples, len(vectorized_datasets["train"]) - ) + metrics["train_samples"] = min(max_train_samples, len(vectorized_datasets["train"])) trainer.log_metrics("train", metrics) trainer.save_metrics("train", metrics) @@ -886,23 +807,15 @@ def compute_metrics(pred): logger.info("*** Evaluate ***") metrics = trainer.evaluate() max_eval_samples = ( - data_args.max_eval_samples - if data_args.max_eval_samples is not None - else len(vectorized_datasets["eval"]) - ) - metrics["eval_samples"] = min( - max_eval_samples, len(vectorized_datasets["eval"]) + data_args.max_eval_samples if data_args.max_eval_samples is not None else len(vectorized_datasets["eval"]) ) + metrics["eval_samples"] = min(max_eval_samples, len(vectorized_datasets["eval"])) trainer.log_metrics("eval", metrics) trainer.save_metrics("eval", metrics) # Write model card and (optionally) push to hub - config_name = ( - data_args.dataset_config_name - if data_args.dataset_config_name is not None - else "na" - ) + config_name = data_args.dataset_config_name if data_args.dataset_config_name is not None else "na" kwargs = { "finetuned_from": model_args.model_name_or_path, "tasks": "automatic-speech-recognition", diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py b/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py index f22c646cea40..5c96daaee1cc 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py @@ -41,10 +41,10 @@ import evaluate import numpy as np import torch - -import transformers from datasets import DatasetDict, load_dataset from safetensors.torch import save_file as safe_save_file + +import transformers from transformers import ( AutoConfig, AutoFeatureExtractor, @@ -52,10 +52,10 @@ AutoProcessor, AutoTokenizer, HfArgumentParser, - set_seed, Trainer, TrainingArguments, Wav2Vec2Processor, + set_seed, ) from transformers.models.wav2vec2.modeling_wav2vec2 import WAV2VEC2_ADAPTER_SAFE_FILE from transformers.trainer_utils import get_last_checkpoint, is_main_process @@ -86,21 +86,15 @@ class ModelArguments: """ model_name_or_path: str = field( - metadata={ - "help": "Path to pretrained model or model identifier from huggingface.co/models" - } + metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} ) tokenizer_name_or_path: Optional[str] = field( default=None, - metadata={ - "help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models" - }, + metadata={"help": "Path to pretrained tokenizer or tokenizer identifier from huggingface.co/models"}, ) cache_dir: Optional[str] = field( default=None, - metadata={ - "help": "Where do you want to store the pretrained models downloaded from huggingface.co" - }, + metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, ) final_dropout: float = field( default=0.0, @@ -134,14 +128,10 @@ class ModelArguments: default=10, metadata={"help": "Length of vector span to mask along the feature axis."}, ) - layerdrop: float = field( - default=0.0, metadata={"help": "The LayerDrop probability."} - ) + layerdrop: float = field(default=0.0, metadata={"help": "The LayerDrop probability."}) ctc_loss_reduction: Optional[str] = field( default="mean", - metadata={ - "help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'." - }, + metadata={"help": "The way the ctc loss should be reduced. Should be one of 'mean' or 'sum'."}, ) adapter_attn_dim: int = field( default=16, @@ -162,9 +152,7 @@ class DataTrainingArguments: """ dataset_name: str = field( - metadata={ - "help": "The configuration name of the dataset to use (via the datasets library)." - } + metadata={"help": "The configuration name of the dataset to use (via the datasets library)."} ) target_language: Optional[str] = field( metadata={ @@ -179,9 +167,7 @@ class DataTrainingArguments: ) dataset_config_name: str = field( default=None, - metadata={ - "help": "The configuration name of the dataset to use (via the datasets library)." - }, + metadata={"help": "The configuration name of the dataset to use (via the datasets library)."}, ) train_split_name: str = field( default="train+validation", @@ -200,15 +186,11 @@ class DataTrainingArguments: ) audio_column_name: str = field( default="audio", - metadata={ - "help": "The name of the dataset column containing the audio data. Defaults to 'audio'" - }, + metadata={"help": "The name of the dataset column containing the audio data. Defaults to 'audio'"}, ) text_column_name: str = field( default="text", - metadata={ - "help": "The name of the dataset column containing the text data. Defaults to 'text'" - }, + metadata={"help": "The name of the dataset column containing the text data. Defaults to 'text'"}, ) overwrite_cache: bool = field( default=False, @@ -242,9 +224,7 @@ class DataTrainingArguments: ) eval_metrics: list[str] = list_field( default=["wer"], - metadata={ - "help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`" - }, + metadata={"help": "A list of metrics the model should be evaluated on. E.g. `'wer cer'`"}, ) max_duration_in_seconds: float = field( default=20.0, @@ -257,9 +237,7 @@ class DataTrainingArguments: ) min_duration_in_seconds: float = field( default=0.0, - metadata={ - "help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds" - }, + metadata={"help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds"}, ) preprocessing_only: bool = field( default=False, @@ -305,11 +283,7 @@ class DataTrainingArguments: ) overwrite_lang_vocab: bool = field( default=False, - metadata={ - "help": ( - "If :obj:`True`, will overwrite existing `target_language` vocabulary of tokenizer." - ) - }, + metadata={"help": ("If :obj:`True`, will overwrite existing `target_language` vocabulary of tokenizer.")}, ) @@ -344,14 +318,10 @@ class DataCollatorCTCWithPadding: pad_to_multiple_of: Optional[int] = None pad_to_multiple_of_labels: Optional[int] = None - def __call__( - self, features: list[dict[str, Union[list[int], torch.Tensor]]] - ) -> dict[str, torch.Tensor]: + def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> dict[str, torch.Tensor]: # split inputs and labels since they have to be of different lengths and need # different padding methods - input_features = [ - {"input_values": feature["input_values"]} for feature in features - ] + input_features = [{"input_values": feature["input_values"]} for feature in features] label_features = [{"input_ids": feature["labels"]} for feature in features] batch = self.processor.pad( @@ -369,9 +339,7 @@ def __call__( ) # replace padding with -100 to ignore loss correctly - labels = labels_batch["input_ids"].masked_fill( - labels_batch.attention_mask.ne(1), -100 - ) + labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100) batch["labels"] = labels if "attention_mask" in batch: @@ -428,25 +396,17 @@ def main(): # or by passing the --help flag to this script. # We now keep distinct sets of args, for a cleaner separation of concerns. - parser = HfArgumentParser( - (ModelArguments, DataTrainingArguments, TrainingArguments) - ) + parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments)) if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): # If we pass only one argument to the script and it's the path to a json file, # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file( - json_file=os.path.abspath(sys.argv[1]) - ) + model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) else: model_args, data_args, training_args = parser.parse_args_into_dataclasses() # Detecting last checkpoint. last_checkpoint = None - if ( - os.path.isdir(training_args.output_dir) - and training_args.do_train - and not training_args.overwrite_output_dir - ): + if os.path.isdir(training_args.output_dir) and training_args.do_train and not training_args.overwrite_output_dir: last_checkpoint = get_last_checkpoint(training_args.output_dir) if last_checkpoint is None and len(os.listdir(training_args.output_dir)) > 0: raise ValueError( @@ -465,9 +425,7 @@ def main(): datefmt="%m/%d/%Y %H:%M:%S", handlers=[logging.StreamHandler(sys.stdout)], ) - logger.setLevel( - logging.INFO if is_main_process(training_args.local_rank) else logging.WARN - ) + logger.setLevel(logging.INFO if is_main_process(training_args.local_rank) else logging.WARN) # Log on each process the small summary: logger.warning( @@ -509,9 +467,7 @@ def main(): ) if data_args.max_train_samples is not None: - raw_datasets["train"] = raw_datasets["train"].select( - range(data_args.max_train_samples) - ) + raw_datasets["train"] = raw_datasets["train"].select(range(data_args.max_train_samples)) if training_args.do_eval: raw_datasets["eval"] = load_dataset( @@ -523,33 +479,25 @@ def main(): ) if data_args.max_eval_samples is not None: - raw_datasets["eval"] = raw_datasets["eval"].select( - range(data_args.max_eval_samples) - ) + raw_datasets["eval"] = raw_datasets["eval"].select(range(data_args.max_eval_samples)) # 2. We remove some special characters from the datasets # that make training complicated and do not help in transcribing the speech # E.g. characters, such as `,` and `.` do not really have an acoustic characteristic # that could be easily picked up by the model chars_to_ignore_regex = ( - f"[{''.join(data_args.chars_to_ignore)}]" - if data_args.chars_to_ignore is not None - else None + f"[{''.join(data_args.chars_to_ignore)}]" if data_args.chars_to_ignore is not None else None ) text_column_name = data_args.text_column_name def remove_special_characters(batch): if chars_to_ignore_regex is not None: - batch["target_text"] = ( - re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " - ) + batch["target_text"] = re.sub(chars_to_ignore_regex, "", batch[text_column_name]).lower() + " " else: batch["target_text"] = batch[text_column_name].lower() + " " return batch - with training_args.main_process_first( - desc="dataset map special characters removal" - ): + with training_args.main_process_first(desc="dataset map special characters removal"): raw_datasets = raw_datasets.map( remove_special_characters, remove_columns=[text_column_name], @@ -589,14 +537,9 @@ def remove_special_characters(batch): ) vocab_dict = tokenizer.vocab.copy() if tokenizer.target_lang is None: - raise ValueError( - "Make sure to load a multi-lingual tokenizer with a set target language." - ) + raise ValueError("Make sure to load a multi-lingual tokenizer with a set target language.") - if ( - data_args.target_language in tokenizer.vocab - and not data_args.overwrite_lang_vocab - ): + if data_args.target_language in tokenizer.vocab and not data_args.overwrite_lang_vocab: logger.info( "Adapter language already exists." " Skipping vocabulary creating. If you want to create a new vocabulary" @@ -643,9 +586,7 @@ def remove_special_characters(batch): # it is defined by `tokenizer_class` if present in config else by `model_type` tokenizer_kwargs = { "config": config if config.tokenizer_class is not None else None, - "tokenizer_type": ( - config.model_type if config.tokenizer_class is None else None - ), + "tokenizer_type": (config.model_type if config.tokenizer_class is None else None), "unk_token": unk_token, "pad_token": pad_token, "word_delimiter_token": word_delimiter_token, @@ -714,11 +655,7 @@ def remove_special_characters(batch): # via the `feature_extractor` # make sure that dataset decodes audio with correct sampling rate - dataset_sampling_rate = ( - next(iter(raw_datasets.values())) - .features[data_args.audio_column_name] - .sampling_rate - ) + dataset_sampling_rate = next(iter(raw_datasets.values())).features[data_args.audio_column_name].sampling_rate if dataset_sampling_rate != feature_extractor.sampling_rate: raw_datasets = raw_datasets.cast_column( data_args.audio_column_name, @@ -726,12 +663,8 @@ def remove_special_characters(batch): ) # derive max & min input length for sample rate & max duration - max_input_length = ( - data_args.max_duration_in_seconds * feature_extractor.sampling_rate - ) - min_input_length = ( - data_args.min_duration_in_seconds * feature_extractor.sampling_rate - ) + max_input_length = data_args.max_duration_in_seconds * feature_extractor.sampling_rate + min_input_length = data_args.min_duration_in_seconds * feature_extractor.sampling_rate audio_column_name = data_args.audio_column_name num_workers = data_args.preprocessing_num_workers @@ -741,9 +674,7 @@ def prepare_dataset(batch): # load audio sample = batch[audio_column_name] - inputs = feature_extractor( - sample["array"], sampling_rate=sample["sampling_rate"] - ) + inputs = feature_extractor(sample["array"], sampling_rate=sample["sampling_rate"]) batch["input_values"] = inputs.input_values[0] batch["input_length"] = len(batch["input_values"]) @@ -774,10 +705,7 @@ def is_audio_in_length_range(length): # instantiate a data collator and the trainer # Define evaluation metrics during training, *i.e.* word error rate, character error rate - eval_metrics = { - metric: evaluate.load(metric, cache_dir=model_args.cache_dir) - for metric in data_args.eval_metrics - } + eval_metrics = {metric: evaluate.load(metric, cache_dir=model_args.cache_dir) for metric in data_args.eval_metrics} # for large datasets it is advised to run the preprocessing on a # single machine first with ``args.preprocessing_only`` since there will mostly likely @@ -785,9 +713,7 @@ def is_audio_in_length_range(length): # In a second step ``args.preprocessing_only`` can then be set to `False` to load the # cached dataset if data_args.preprocessing_only: - logger.info( - f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}" - ) + logger.info(f"Data preprocessing finished. Files cached at {vectorized_datasets.cache_files}") return def compute_metrics(pred): @@ -800,10 +726,7 @@ def compute_metrics(pred): # we do not want to group tokens when computing the metrics label_str = tokenizer.batch_decode(pred.label_ids, group_tokens=False) - metrics = { - k: v.compute(predictions=pred_str, references=label_str) - for k, v in eval_metrics.items() - } + metrics = {k: v.compute(predictions=pred_str, references=label_str) for k, v in eval_metrics.items()} return metrics @@ -864,9 +787,7 @@ def compute_metrics(pred): if data_args.max_train_samples is not None else len(vectorized_datasets["train"]) ) - metrics["train_samples"] = min( - max_train_samples, len(vectorized_datasets["train"]) - ) + metrics["train_samples"] = min(max_train_samples, len(vectorized_datasets["train"])) trainer.log_metrics("train", metrics) trainer.save_metrics("train", metrics) @@ -878,23 +799,15 @@ def compute_metrics(pred): logger.info("*** Evaluate ***") metrics = trainer.evaluate() max_eval_samples = ( - data_args.max_eval_samples - if data_args.max_eval_samples is not None - else len(vectorized_datasets["eval"]) - ) - metrics["eval_samples"] = min( - max_eval_samples, len(vectorized_datasets["eval"]) + data_args.max_eval_samples if data_args.max_eval_samples is not None else len(vectorized_datasets["eval"]) ) + metrics["eval_samples"] = min(max_eval_samples, len(vectorized_datasets["eval"])) trainer.log_metrics("eval", metrics) trainer.save_metrics("eval", metrics) # Write model card and (optionally) push to hub - config_name = ( - data_args.dataset_config_name - if data_args.dataset_config_name is not None - else "na" - ) + config_name = data_args.dataset_config_name if data_args.dataset_config_name is not None else "na" kwargs = { "finetuned_from": model_args.model_name_or_path, "tasks": "automatic-speech-recognition", diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py b/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py index 96435b37425d..6f895a9dcaaf 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py @@ -40,9 +40,9 @@ import datasets import evaluate import torch +from datasets import DatasetDict, load_dataset import transformers -from datasets import DatasetDict, load_dataset from transformers import ( AutoConfig, AutoFeatureExtractor, @@ -77,45 +77,31 @@ class ModelArguments: """ model_name_or_path: str = field( - metadata={ - "help": "Path to pretrained model or model identifier from huggingface.co/models" - } + metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} ) config_name: Optional[str] = field( default=None, - metadata={ - "help": "Pretrained config name or path if not the same as model_name" - }, + metadata={"help": "Pretrained config name or path if not the same as model_name"}, ) tokenizer_name: Optional[str] = field( default=None, - metadata={ - "help": "Pretrained tokenizer name or path if not the same as model_name" - }, + metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"}, ) feature_extractor_name: Optional[str] = field( default=None, - metadata={ - "help": "feature extractor name or path if not the same as model_name" - }, + metadata={"help": "feature extractor name or path if not the same as model_name"}, ) cache_dir: Optional[str] = field( default=None, - metadata={ - "help": "Where to store the pretrained models downloaded from huggingface.co" - }, + metadata={"help": "Where to store the pretrained models downloaded from huggingface.co"}, ) use_fast_tokenizer: bool = field( default=True, - metadata={ - "help": "Whether to use one of the fast tokenizer (backed by the tokenizers library) or not." - }, + metadata={"help": "Whether to use one of the fast tokenizer (backed by the tokenizers library) or not."}, ) model_revision: str = field( default="main", - metadata={ - "help": "The specific model version to use (can be a branch name, tag name or commit id)." - }, + metadata={"help": "The specific model version to use (can be a branch name, tag name or commit id)."}, ) token: str = field( default=None, @@ -146,9 +132,7 @@ class ModelArguments: ) forced_decoder_ids: list[list[int]] = field( default=None, - metadata={ - "help": "Deprecated. Please use the `language` and `task` arguments instead." - }, + metadata={"help": "Deprecated. Please use the `language` and `task` arguments instead."}, ) suppress_tokens: list[int] = field( default=None, @@ -179,9 +163,7 @@ class DataTrainingArguments: ) dataset_config_name: Optional[str] = field( default=None, - metadata={ - "help": "The configuration name of the dataset to use (via the datasets library)." - }, + metadata={"help": "The configuration name of the dataset to use (via the datasets library)."}, ) overwrite_cache: bool = field( default=False, @@ -211,15 +193,11 @@ class DataTrainingArguments: ) audio_column_name: str = field( default="audio", - metadata={ - "help": "The name of the dataset column containing the audio data. Defaults to 'audio'" - }, + metadata={"help": "The name of the dataset column containing the audio data. Defaults to 'audio'"}, ) text_column_name: str = field( default="text", - metadata={ - "help": "The name of the dataset column containing the text data. Defaults to 'text'" - }, + metadata={"help": "The name of the dataset column containing the text data. Defaults to 'text'"}, ) max_duration_in_seconds: float = field( default=20.0, @@ -232,9 +210,7 @@ class DataTrainingArguments: ) min_duration_in_seconds: float = field( default=0.0, - metadata={ - "help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds" - }, + metadata={"help": "Filter audio files that are shorter than `min_duration_in_seconds` seconds"}, ) preprocessing_only: bool = field( default=False, @@ -274,9 +250,7 @@ class DataTrainingArguments: ) task: str = field( default="transcribe", - metadata={ - "help": "Task, either `transcribe` for speech recognition or `translate` for speech translation." - }, + metadata={"help": "Task, either `transcribe` for speech recognition or `translate` for speech translation."}, ) @@ -297,32 +271,22 @@ class DataCollatorSpeechSeq2SeqWithPadding: decoder_start_token_id: int forward_attention_mask: bool - def __call__( - self, features: list[dict[str, Union[list[int], torch.Tensor]]] - ) -> dict[str, torch.Tensor]: + def __call__(self, features: list[dict[str, Union[list[int], torch.Tensor]]]) -> dict[str, torch.Tensor]: # split inputs and labels since they have to be of different lengths and need # different padding methods model_input_name = self.processor.model_input_names[0] - input_features = [ - {model_input_name: feature[model_input_name]} for feature in features - ] + input_features = [{model_input_name: feature[model_input_name]} for feature in features] label_features = [{"input_ids": feature["labels"]} for feature in features] - batch = self.processor.feature_extractor.pad( - input_features, return_tensors="pt" - ) + batch = self.processor.feature_extractor.pad(input_features, return_tensors="pt") if self.forward_attention_mask: - batch["attention_mask"] = torch.LongTensor( - [feature["attention_mask"] for feature in features] - ) + batch["attention_mask"] = torch.LongTensor([feature["attention_mask"] for feature in features]) labels_batch = self.processor.tokenizer.pad(label_features, return_tensors="pt") # replace padding with -100 to ignore loss correctly - labels = labels_batch["input_ids"].masked_fill( - labels_batch.attention_mask.ne(1), -100 - ) + labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100) # if bos token is appended in previous tokenization step, # cut bos token here as it's append later anyways @@ -339,16 +303,12 @@ def main(): # See all possible arguments in src/transformers/training_args.py # or by passing the --help flag to this script. # We now keep distinct sets of args, for a cleaner separation of concerns. - parser = HfArgumentParser( - (ModelArguments, DataTrainingArguments, Seq2SeqTrainingArguments) - ) + parser = HfArgumentParser((ModelArguments, DataTrainingArguments, Seq2SeqTrainingArguments)) if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): # If we pass only one argument to the script and it's the path to a json file, # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file( - json_file=os.path.abspath(sys.argv[1]) - ) + model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) else: model_args, data_args, training_args = parser.parse_args_into_dataclasses() @@ -365,9 +325,7 @@ def main(): transformers.utils.logging.enable_default_handler() transformers.utils.logging.enable_explicit_format() - logger.setLevel( - logging.INFO if is_main_process(training_args.local_rank) else logging.WARN - ) + logger.setLevel(logging.INFO if is_main_process(training_args.local_rank) else logging.WARN) # Log on each process the small summary: logger.warning( @@ -383,20 +341,14 @@ def main(): # 3. Detecting last checkpoint and eventually continue from last checkpoint last_checkpoint = None - if ( - os.path.isdir(training_args.output_dir) - and training_args.do_train - and not training_args.overwrite_output_dir - ): + if os.path.isdir(training_args.output_dir) and training_args.do_train and not training_args.overwrite_output_dir: last_checkpoint = get_last_checkpoint(training_args.output_dir) if last_checkpoint is None and len(os.listdir(training_args.output_dir)) > 0: raise ValueError( f"Output directory ({training_args.output_dir}) already exists and is not empty. " "Use --overwrite_output_dir to overcome." ) - elif ( - last_checkpoint is not None and training_args.resume_from_checkpoint is None - ): + elif last_checkpoint is not None and training_args.resume_from_checkpoint is None: logger.info( f"Checkpoint detected, resuming training at {last_checkpoint}. To avoid this behavior, change " "the `--output_dir` or add `--overwrite_output_dir` to train from scratch." @@ -428,10 +380,7 @@ def main(): trust_remote_code=model_args.trust_remote_code, ) - if ( - data_args.audio_column_name - not in next(iter(raw_datasets.values())).column_names - ): + if data_args.audio_column_name not in next(iter(raw_datasets.values())).column_names: raise ValueError( f"--audio_column_name '{data_args.audio_column_name}' not found in dataset '{data_args.dataset_name}'. " "Make sure to set `--audio_column_name` to the correct audio column - one of " @@ -450,11 +399,7 @@ def main(): # Distributed training: # The .from_pretrained methods guarantee that only one local process can concurrently config = AutoConfig.from_pretrained( - ( - model_args.config_name - if model_args.config_name - else model_args.model_name_or_path - ), + (model_args.config_name if model_args.config_name else model_args.model_name_or_path), cache_dir=model_args.cache_dir, revision=model_args.model_revision, token=model_args.token, @@ -466,22 +411,14 @@ def main(): config.update({"apply_spec_augment": model_args.apply_spec_augment}) feature_extractor = AutoFeatureExtractor.from_pretrained( - ( - model_args.feature_extractor_name - if model_args.feature_extractor_name - else model_args.model_name_or_path - ), + (model_args.feature_extractor_name if model_args.feature_extractor_name else model_args.model_name_or_path), cache_dir=model_args.cache_dir, revision=model_args.model_revision, token=model_args.token, trust_remote_code=model_args.trust_remote_code, ) tokenizer = AutoTokenizer.from_pretrained( - ( - model_args.tokenizer_name - if model_args.tokenizer_name - else model_args.model_name_or_path - ), + (model_args.tokenizer_name if model_args.tokenizer_name else model_args.model_name_or_path), cache_dir=model_args.cache_dir, use_fast=model_args.use_fast_tokenizer, revision=model_args.model_revision, @@ -498,9 +435,7 @@ def main(): ) if model.config.decoder_start_token_id is None: - raise ValueError( - "Make sure that `config.decoder_start_token_id` is correctly defined" - ) + raise ValueError("Make sure that `config.decoder_start_token_id` is correctly defined") if model_args.freeze_feature_encoder: model.freeze_feature_encoder() @@ -509,10 +444,7 @@ def main(): model.freeze_encoder() model.model.encoder.gradient_checkpointing = False - if ( - hasattr(model.generation_config, "is_multilingual") - and model.generation_config.is_multilingual - ): + if hasattr(model.generation_config, "is_multilingual") and model.generation_config.is_multilingual: # We only need to set the language and task ids in a multilingual setting tokenizer.set_prefix_tokens(language=data_args.language, task=data_args.task) model.generation_config.language = data_args.language @@ -542,11 +474,7 @@ def main(): model.generation_config.suppress_tokens = model_args.suppress_tokens # 6. Resample speech dataset if necessary - dataset_sampling_rate = ( - next(iter(raw_datasets.values())) - .features[data_args.audio_column_name] - .sampling_rate - ) + dataset_sampling_rate = next(iter(raw_datasets.values())).features[data_args.audio_column_name].sampling_rate if dataset_sampling_rate != feature_extractor.sampling_rate: raw_datasets = raw_datasets.cast_column( data_args.audio_column_name, @@ -555,12 +483,8 @@ def main(): # 7. Preprocessing the datasets. # We need to read the audio files as arrays and tokenize the targets. - max_input_length = ( - data_args.max_duration_in_seconds * feature_extractor.sampling_rate - ) - min_input_length = ( - data_args.min_duration_in_seconds * feature_extractor.sampling_rate - ) + max_input_length = data_args.max_duration_in_seconds * feature_extractor.sampling_rate + min_input_length = data_args.min_duration_in_seconds * feature_extractor.sampling_rate audio_column_name = data_args.audio_column_name num_workers = data_args.preprocessing_num_workers text_column_name = data_args.text_column_name @@ -574,14 +498,10 @@ def main(): ) if data_args.max_train_samples is not None: - raw_datasets["train"] = raw_datasets["train"].select( - range(data_args.max_train_samples) - ) + raw_datasets["train"] = raw_datasets["train"].select(range(data_args.max_train_samples)) if data_args.max_eval_samples is not None: - raw_datasets["eval"] = raw_datasets["eval"].select( - range(data_args.max_eval_samples) - ) + raw_datasets["eval"] = raw_datasets["eval"].select(range(data_args.max_eval_samples)) def prepare_dataset(batch): # process audio @@ -598,11 +518,7 @@ def prepare_dataset(batch): batch["attention_mask"] = inputs.get("attention_mask")[0] # process targets - input_str = ( - batch[text_column_name].lower() - if do_lower_case - else batch[text_column_name] - ) + input_str = batch[text_column_name].lower() if do_lower_case else batch[text_column_name] batch["labels"] = tokenizer(input_str).input_ids return batch @@ -678,9 +594,7 @@ def compute_metrics(pred): eval_dataset=vectorized_datasets["eval"] if training_args.do_eval else None, processing_class=feature_extractor, data_collator=data_collator, - compute_metrics=( - compute_metrics if training_args.predict_with_generate else None - ), + compute_metrics=(compute_metrics if training_args.predict_with_generate else None), ) # 12. Training @@ -699,9 +613,7 @@ def compute_metrics(pred): if data_args.max_train_samples is not None else len(vectorized_datasets["train"]) ) - metrics["train_samples"] = min( - max_train_samples, len(vectorized_datasets["train"]) - ) + metrics["train_samples"] = min(max_train_samples, len(vectorized_datasets["train"])) trainer.log_metrics("train", metrics) trainer.save_metrics("train", metrics) trainer.save_state() @@ -716,13 +628,9 @@ def compute_metrics(pred): num_beams=training_args.generation_num_beams, ) max_eval_samples = ( - data_args.max_eval_samples - if data_args.max_eval_samples is not None - else len(vectorized_datasets["eval"]) - ) - metrics["eval_samples"] = min( - max_eval_samples, len(vectorized_datasets["eval"]) + data_args.max_eval_samples if data_args.max_eval_samples is not None else len(vectorized_datasets["eval"]) ) + metrics["eval_samples"] = min(max_eval_samples, len(vectorized_datasets["eval"])) trainer.log_metrics("eval", metrics) trainer.save_metrics("eval", metrics) @@ -736,9 +644,7 @@ def compute_metrics(pred): kwargs["dataset_tags"] = data_args.dataset_name if data_args.dataset_config_name is not None: kwargs["dataset_args"] = data_args.dataset_config_name - kwargs["dataset"] = ( - f"{data_args.dataset_name} {data_args.dataset_config_name}" - ) + kwargs["dataset"] = f"{data_args.dataset_name} {data_args.dataset_config_name}" else: kwargs["dataset"] = data_args.dataset_name From 5794d8a5282af2039847f659bc9935be3fe0006a Mon Sep 17 00:00:00 2001 From: eustlb <94853470+eustlb@users.noreply.github.com> Date: Tue, 30 Sep 2025 09:53:23 +0200 Subject: [PATCH 08/10] Update examples/pytorch/speech-recognition/README.md --- examples/pytorch/speech-recognition/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/pytorch/speech-recognition/README.md b/examples/pytorch/speech-recognition/README.md index b97080c67e56..41df41880b5a 100644 --- a/examples/pytorch/speech-recognition/README.md +++ b/examples/pytorch/speech-recognition/README.md @@ -360,8 +360,6 @@ recognition on one of the [official speech recognition datasets](https://hugging ### Whisper Model We can load all components of the Whisper model directly from the pretrained checkpoint, including the pretrained model weights, feature extractor and tokenizer. We simply have to specify our fine-tuning dataset and training hyperparameters. - - #### Single GPU Whisper Training The following example shows how to fine-tune the [Whisper small](https://huggingface.co/openai/whisper-small) checkpoint on the Hindi subset of [Common Voice 11](https://huggingface.co/datasets/mozilla-foundation/common_voice_11_0) using a single GPU device in half-precision: ```bash From 2b5d9cab2aa6a60e44cd114554967ed1a61f3d39 Mon Sep 17 00:00:00 2001 From: Eustache Le Bihan Date: Tue, 30 Sep 2025 09:59:09 +0200 Subject: [PATCH 09/10] update doc to match load_dataset --- .../speech-recognition/run_speech_recognition_ctc.py | 6 ++++-- .../run_speech_recognition_ctc_adapter.py | 12 +++++++----- .../run_speech_recognition_seq2seq.py | 9 +++++---- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py b/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py index 8a0e44184ecd..c756a6666187 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py @@ -176,11 +176,13 @@ class DataTrainingArguments: """ dataset_name: str = field( - metadata={"help": "The configuration name of the dataset to use (via the datasets library)."} + metadata={"help": "Path or name of the dataset (cf `load_dataset` method of the Datasets library)."} ) dataset_config_name: str = field( default=None, - metadata={"help": "The configuration name of the dataset to use (via the datasets library)."}, + metadata={ + "help": "The configuration name of the dataset to use (cf `load_dataset` method of the Datasets library)." + }, ) train_split_name: str = field( default="train+validation", diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py b/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py index 5c96daaee1cc..29e0f51b36d9 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py @@ -152,7 +152,13 @@ class DataTrainingArguments: """ dataset_name: str = field( - metadata={"help": "The configuration name of the dataset to use (via the datasets library)."} + metadata={"help": "Path or name of the dataset (cf `load_dataset` method of the Datasets library)."} + ) + dataset_config_name: str = field( + default=None, + metadata={ + "help": "The configuration name of the dataset to use (cf `load_dataset` method of the Datasets library)." + }, ) target_language: Optional[str] = field( metadata={ @@ -165,10 +171,6 @@ class DataTrainingArguments: ) }, ) - dataset_config_name: str = field( - default=None, - metadata={"help": "The configuration name of the dataset to use (via the datasets library)."}, - ) train_split_name: str = field( default="train+validation", metadata={ diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py b/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py index 6f895a9dcaaf..4b6cda49925b 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_seq2seq.py @@ -158,12 +158,13 @@ class DataTrainingArguments: """ dataset_name: str = field( - default=None, - metadata={"help": "The name of the dataset to use (via the datasets library)."}, + metadata={"help": "Path or name of the dataset (cf `load_dataset` method of the Datasets library)."} ) - dataset_config_name: Optional[str] = field( + dataset_config_name: str = field( default=None, - metadata={"help": "The configuration name of the dataset to use (via the datasets library)."}, + metadata={ + "help": "The configuration name of the dataset to use (cf `load_dataset` method of the Datasets library)." + }, ) overwrite_cache: bool = field( default=False, From aa8082aede65b342f10b8b6f5205cf7c2ee541a4 Mon Sep 17 00:00:00 2001 From: Eustache Le Bihan Date: Tue, 30 Sep 2025 10:29:13 +0200 Subject: [PATCH 10/10] fix --- .../run_speech_recognition_ctc_adapter.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py b/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py index 29e0f51b36d9..aaebf59c8660 100755 --- a/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py +++ b/examples/pytorch/speech-recognition/run_speech_recognition_ctc_adapter.py @@ -154,13 +154,7 @@ class DataTrainingArguments: dataset_name: str = field( metadata={"help": "Path or name of the dataset (cf `load_dataset` method of the Datasets library)."} ) - dataset_config_name: str = field( - default=None, - metadata={ - "help": "The configuration name of the dataset to use (cf `load_dataset` method of the Datasets library)." - }, - ) - target_language: Optional[str] = field( + target_language: str = field( metadata={ "help": ( "The target language on which the adapter attention layers" @@ -171,6 +165,12 @@ class DataTrainingArguments: ) }, ) + dataset_config_name: str = field( + default=None, + metadata={ + "help": "The configuration name of the dataset to use (cf `load_dataset` method of the Datasets library)." + }, + ) train_split_name: str = field( default="train+validation", metadata={