| import os |
| from collections.abc import Iterable |
| from contextlib import nullcontext |
| from dataclasses import dataclass |
| from multiprocessing import get_context, get_start_method |
| from multiprocessing.pool import Pool |
| from typing import TYPE_CHECKING |
|
|
| import numpy as np |
|
|
| from transformers.processing_utils import ProcessorMixin |
| from transformers.utils import ModelOutput, logging, requires_backends |
|
|
|
|
| logger = logging.get_logger(__name__) |
|
|
|
|
| if TYPE_CHECKING: |
| from pyctcdecode import BeamSearchDecoderCTC |
|
|
| from transformers.feature_extraction_utils import FeatureExtractionMixin |
| from transformers.video_processing_utils import BaseVideoProcessor |
| from transformers.tokenization_python import PreTrainedTokenizerBase |
|
|
|
|
| ListOfDict = list[dict[str, int | str]] |
|
|
|
|
| @dataclass |
| class Wav2Vec2DecoderWithLMOutput(ModelOutput): |
| """ |
| Output type of [`Wav2Vec2DecoderWithLM`], with transcription. |
| |
| Args: |
| text (list of `str` or `str`): |
| Decoded logits in text from. Usually the speech transcription. |
| logit_score (list of `float` or `float`): |
| Total logit score of the beams associated with produced text. |
| lm_score (list of `float`): |
| Fused lm_score of the beams associated with produced text. |
| word_offsets (list of `list[dict[str, Union[int, str]]]` or `list[dict[str, Union[int, str]]]`): |
| Offsets of the decoded words. In combination with sampling rate and model downsampling rate word offsets |
| can be used to compute time stamps for each word. |
| """ |
|
|
| text: list[list[str]] | list[str] | str |
| logit_score: list[list[float]] | list[float] | float = None |
| lm_score: list[list[float]] | list[float] | float = None |
| word_offsets: list[list[ListOfDict]] | list[ListOfDict] | ListOfDict = None |
|
|
|
|
| class MSPProcessorWithLM(ProcessorMixin): |
| def __init__( |
| self, |
| feature_extractor: "FeatureExtractionMixin", |
| video_processor: "BaseVideoProcessor", |
| tokenizer: "PreTrainedTokenizerBase", |
| decoder: "BeamSearchDecoderCTC", |
| ): |
| r""" |
| decoder (`pyctcdecode.BeamSearchDecoderCTC`): |
| An instance of [`pyctcdecode.BeamSearchDecoderCTC`]. The decoder is a required input. |
| """ |
| from pyctcdecode import BeamSearchDecoderCTC |
|
|
| super().__init__(feature_extractor, |
| video_processor, |
| tokenizer |
| ) |
| if not isinstance(decoder, BeamSearchDecoderCTC): |
| raise TypeError(f"`decoder` has to be of type {BeamSearchDecoderCTC.__class__}, but is {type(decoder)}") |
|
|
| if feature_extractor.__class__.__name__ not in ["MSPAudioFeatureExtractor"]: |
| raise ValueError( |
| f"`feature_extractor` has to be of type `MSPAudioFeatureExtractor`, but is {type(feature_extractor)}" |
| ) |
| if video_processor.__class__.__name__ not in ["MSPVisualVideoProcessor"]: |
| raise ValueError( |
| f"`video_processor` has to be of type `MSPVisualVideoProcessor`, but is {type(video_processor)}" |
| ) |
|
|
| |
| missing_decoder_tokens = self.get_missing_alphabet_tokens(decoder, tokenizer) |
| if len(missing_decoder_tokens) > 0: |
| raise ValueError( |
| f"The tokens {missing_decoder_tokens} are defined in the tokenizer's " |
| "vocabulary, but not in the decoder's alphabet. " |
| f"Make sure to include {missing_decoder_tokens} in the decoder's alphabet." |
| ) |
|
|
| self.decoder = decoder |
|
|
| def save_pretrained(self, save_directory,**kwargs): |
| super().save_pretrained(save_directory, **kwargs) |
| self.decoder.save_to_dir(save_directory) |
|
|
| @classmethod |
| def from_pretrained(cls, pretrained_model_name_or_path, **kwargs): |
| r""" |
| Instantiate a [`Wav2Vec2ProcessorWithLM`] from a pretrained Wav2Vec2 processor. |
| |
| <Tip> |
| |
| This class method is simply calling the feature extractor's |
| [`~feature_extraction_utils.FeatureExtractionMixin.from_pretrained`], Wav2Vec2CTCTokenizer's |
| [`~tokenization_utils_base.PreTrainedTokenizerBase.from_pretrained`], and |
| [`pyctcdecode.BeamSearchDecoderCTC.load_from_hf_hub`]. |
| |
| Please refer to the docstrings of the methods above for more information. |
| |
| </Tip> |
| |
| Args: |
| pretrained_model_name_or_path (`str` or `os.PathLike`): |
| This can be either: |
| |
| - a string, the *model id* of a pretrained feature_extractor hosted inside a model repo on |
| huggingface.co. |
| - a path to a *directory* containing a feature extractor file saved using the |
| [`~SequenceFeatureExtractor.save_pretrained`] method, e.g., `./my_model_directory/`. |
| - a path to a saved feature extractor JSON *file*, e.g., |
| `./my_model_directory/preprocessor_config.json`. |
| **kwargs |
| Additional keyword arguments passed along to both [`SequenceFeatureExtractor`] and |
| [`PreTrainedTokenizer`] |
| """ |
| requires_backends(cls, "pyctcdecode") |
| from pyctcdecode import BeamSearchDecoderCTC |
|
|
| feature_extractor,video_processor,tokenizer= super()._get_arguments_from_pretrained(pretrained_model_name_or_path, **kwargs) |
|
|
| if os.path.isdir(pretrained_model_name_or_path) or os.path.isfile(pretrained_model_name_or_path): |
| unigram_encoding = kwargs.get("unigram_encoding", "utf-8") |
| decoder = BeamSearchDecoderCTC.load_from_dir(pretrained_model_name_or_path, unigram_encoding) |
| else: |
| |
| kwargs.pop("_from_auto", None) |
| |
| kwargs.pop("trust_remote_code", None) |
|
|
| |
| language_model_filenames = os.path.join(BeamSearchDecoderCTC._LANGUAGE_MODEL_SERIALIZED_DIRECTORY, "*") |
| alphabet_filename = BeamSearchDecoderCTC._ALPHABET_SERIALIZED_FILENAME |
| allow_patterns = [language_model_filenames, alphabet_filename] |
|
|
| decoder = BeamSearchDecoderCTC.load_from_hf_hub( |
| pretrained_model_name_or_path, allow_patterns=allow_patterns, **kwargs |
| ) |
|
|
| |
| for attribute in ["alpha", "beta", "unk_score_offset", "score_boundary"]: |
| value = kwargs.pop(attribute, None) |
|
|
| if value is not None: |
| cls._set_language_model_attribute(decoder, attribute, value) |
|
|
| |
| missing_decoder_tokens = cls.get_missing_alphabet_tokens(decoder, tokenizer) |
| if len(missing_decoder_tokens) > 0: |
| raise ValueError( |
| f"The tokens {missing_decoder_tokens} are defined in the tokenizer's " |
| "vocabulary, but not in the decoder's alphabet. " |
| f"Make sure to include {missing_decoder_tokens} in the decoder's alphabet." |
| ) |
|
|
| return cls(feature_extractor=feature_extractor, |
| video_processor=video_processor, |
| tokenizer=tokenizer, |
| decoder=decoder |
| ) |
|
|
| @staticmethod |
| def _set_language_model_attribute(decoder: "BeamSearchDecoderCTC", attribute: str, value: float): |
| setattr(decoder.model_container[decoder._model_key], attribute, value) |
|
|
| @property |
| def language_model(self): |
| return self.decoder.model_container[self.decoder._model_key] |
|
|
| @staticmethod |
| def get_missing_alphabet_tokens(decoder, tokenizer): |
| from pyctcdecode.alphabet import BLANK_TOKEN_PTN, UNK_TOKEN, UNK_TOKEN_PTN |
|
|
| |
| |
| |
| tokenizer_vocab_list = list(tokenizer.get_vocab().keys()) |
|
|
| |
| for i, token in enumerate(tokenizer_vocab_list): |
| if BLANK_TOKEN_PTN.match(token): |
| tokenizer_vocab_list[i] = "" |
| if token == tokenizer.word_delimiter_token: |
| tokenizer_vocab_list[i] = " " |
| if UNK_TOKEN_PTN.match(token): |
| tokenizer_vocab_list[i] = UNK_TOKEN |
|
|
| |
| missing_tokens = set(tokenizer_vocab_list) - set(decoder._alphabet.labels) |
|
|
| return missing_tokens |
|
|
| def __call__(self,audio=None,videos=None,text=None,**kwargs): |
| if audio is None and videos is None and text is None: |
| raise ValueError("Provide at least one of audio, videos, or text.") |
|
|
| inputs = super().__call__( |
| images=None, audio=audio, videos=videos, text=text, **kwargs |
| ) |
|
|
| if "input_ids" in inputs: |
| inputs["labels"] = inputs.pop("input_ids") |
|
|
| if "attention_mask" in inputs: |
| inputs.pop("attention_mask") |
|
|
| return inputs |
|
|
| def pad(self, *args, **kwargs): |
| """ |
| When used in normal mode, this method forwards all its arguments to the feature extractor's |
| [`~FeatureExtractionMixin.pad`] and returns its output. If used in the context |
| [`~Wav2Vec2ProcessorWithLM.as_target_processor`] this method forwards all its arguments to |
| Wav2Vec2CTCTokenizer's [`~Wav2Vec2CTCTokenizer.pad`]. Please refer to the docstring of the above two methods |
| for more information. |
| """ |
| input_features = kwargs.pop("input_features", None) |
| labels = kwargs.pop("labels", None) |
| if len(args) > 0: |
| input_features = args[0] |
| args = args[1:] |
|
|
| if input_features is not None: |
| input_features = self.feature_extractor.pad(input_features, *args, **kwargs) |
| if labels is not None: |
| labels = self.tokenizer.pad(labels, **kwargs) |
|
|
| if labels is None: |
| return input_features |
| elif input_features is None: |
| return labels |
| else: |
| input_features["labels"] = labels["input_ids"] |
| return input_features |
|
|
| def batch_decode( |
| self, |
| logits: np.ndarray, |
| pool: Pool | None = None, |
| num_processes: int | None = None, |
| beam_width: int | None = None, |
| beam_prune_logp: float | None = None, |
| token_min_logp: float | None = None, |
| hotwords: Iterable[str] | None = None, |
| hotword_weight: float | None = None, |
| alpha: float | None = None, |
| beta: float | None = None, |
| unk_score_offset: float | None = None, |
| lm_score_boundary: bool | None = None, |
| output_word_offsets: bool = False, |
| n_best: int = 1, |
| ): |
| """ |
| Batch decode output logits to audio transcription with language model support. |
| |
| <Tip> |
| |
| This function makes use of Python's multiprocessing. Currently, multiprocessing is available only on Unix |
| systems (see this [issue](https://github.com/kensho-technologies/pyctcdecode/issues/65)). |
| |
| If you are decoding multiple batches, consider creating a `Pool` and passing it to `batch_decode`. Otherwise, |
| `batch_decode` will be very slow since it will create a fresh `Pool` for each call. See usage example below. |
| |
| </Tip> |
| |
| Args: |
| logits (`np.ndarray`): |
| The logits output vector of the model representing the log probabilities for each token. |
| pool (`multiprocessing.Pool`, *optional*): |
| An optional user-managed pool. If not set, one will be automatically created and closed. The pool |
| should be instantiated *after* `Wav2Vec2ProcessorWithLM`. Otherwise, the LM won't be available to the |
| pool's sub-processes. |
| |
| <Tip> |
| |
| Currently, only pools created with a 'fork' context can be used. If a 'spawn' pool is passed, it will |
| be ignored and sequential decoding will be used instead. |
| |
| </Tip> |
| |
| num_processes (`int`, *optional*): |
| If `pool` is not set, number of processes on which the function should be parallelized over. Defaults |
| to the number of available CPUs. |
| beam_width (`int`, *optional*): |
| Maximum number of beams at each step in decoding. Defaults to pyctcdecode's DEFAULT_BEAM_WIDTH. |
| beam_prune_logp (`int`, *optional*): |
| Beams that are much worse than best beam will be pruned Defaults to pyctcdecode's DEFAULT_PRUNE_LOGP. |
| token_min_logp (`int`, *optional*): |
| Tokens below this logp are skipped unless they are argmax of frame Defaults to pyctcdecode's |
| DEFAULT_MIN_TOKEN_LOGP. |
| hotwords (`list[str]`, *optional*): |
| List of words with extra importance, can be OOV for LM |
| hotword_weight (`int`, *optional*): |
| Weight factor for hotword importance Defaults to pyctcdecode's DEFAULT_HOTWORD_WEIGHT. |
| alpha (`float`, *optional*): |
| Weight for language model during shallow fusion |
| beta (`float`, *optional*): |
| Weight for length score adjustment of during scoring |
| unk_score_offset (`float`, *optional*): |
| Amount of log score offset for unknown tokens |
| lm_score_boundary (`bool`, *optional*): |
| Whether to have kenlm respect boundaries when scoring |
| output_word_offsets (`bool`, *optional*, defaults to `False`): |
| Whether or not to output word offsets. Word offsets can be used in combination with the sampling rate |
| and model downsampling rate to compute the time-stamps of transcribed words. |
| n_best (`int`, *optional*, defaults to `1`): |
| Number of best hypotheses to return. If `n_best` is greater than 1, the returned `text` will be a list |
| of lists of strings, `logit_score` will be a list of lists of floats, and `lm_score` will be a list of |
| lists of floats, where the length of the outer list will correspond to the batch size and the length of |
| the inner list will correspond to the number of returned hypotheses . The value should be >= 1. |
| |
| <Tip> |
| |
| Please take a look at the Example of [`~Wav2Vec2ProcessorWithLM.decode`] to better understand how to |
| make use of `output_word_offsets`. [`~Wav2Vec2ProcessorWithLM.batch_decode`] works the same way with |
| batched output. |
| |
| </Tip> |
| |
| Returns: |
| [`~models.wav2vec2.Wav2Vec2DecoderWithLMOutput`]. |
| |
| Example: |
| See [Decoding multiple audios](#decoding-multiple-audios). |
| """ |
|
|
| from pyctcdecode.constants import ( |
| DEFAULT_BEAM_WIDTH, |
| DEFAULT_HOTWORD_WEIGHT, |
| DEFAULT_MIN_TOKEN_LOGP, |
| DEFAULT_PRUNE_LOGP, |
| ) |
|
|
| |
| beam_width = beam_width if beam_width is not None else DEFAULT_BEAM_WIDTH |
| beam_prune_logp = beam_prune_logp if beam_prune_logp is not None else DEFAULT_PRUNE_LOGP |
| token_min_logp = token_min_logp if token_min_logp is not None else DEFAULT_MIN_TOKEN_LOGP |
| hotword_weight = hotword_weight if hotword_weight is not None else DEFAULT_HOTWORD_WEIGHT |
|
|
| |
| self.decoder.reset_params( |
| alpha=alpha, beta=beta, unk_score_offset=unk_score_offset, lm_score_boundary=lm_score_boundary |
| ) |
|
|
| |
| |
| logits_list = [array[(array != -100.0).all(axis=-1)] for array in logits] |
|
|
| |
| if pool is None: |
| |
| |
| default_context = get_start_method() |
|
|
| if default_context == "fork": |
| cm = pool = get_context().Pool(num_processes) |
| else: |
| logger.warning( |
| "Parallel batch decoding is not currently supported in this platform. " |
| "Falling back to sequential decoding." |
| ) |
| cm = nullcontext() |
| else: |
| |
| cm = nullcontext() |
|
|
| if num_processes is not None: |
| logger.warning( |
| "Parameter `num_process` was passed, but it will be ignored since `pool` was also specified." |
| ) |
|
|
| |
| with cm: |
| decoded_beams = self.decoder.decode_beams_batch( |
| pool=pool, |
| logits_list=logits_list, |
| beam_width=beam_width, |
| beam_prune_logp=beam_prune_logp, |
| token_min_logp=token_min_logp, |
| hotwords=hotwords, |
| hotword_weight=hotword_weight, |
| ) |
|
|
| |
| batch_texts, logit_scores, lm_scores, word_offsets = [], [], [], [] |
|
|
| for d in decoded_beams: |
| batch_texts.append([beam[0] for beam in d]) |
| logit_scores.append([beam[-2] for beam in d]) |
| lm_scores.append([beam[-1] for beam in d]) |
|
|
| |
|
|
| word_offsets.append( |
| [ |
| [ |
| {"word": word, "start_offset": start_offset, "end_offset": end_offset} |
| for word, (start_offset, end_offset) in beam[1] |
| ] |
| for beam in d |
| ] |
| ) |
|
|
| word_offsets = word_offsets if output_word_offsets else None |
|
|
| if n_best == 1: |
| return Wav2Vec2DecoderWithLMOutput( |
| text=[hyps[0] for hyps in batch_texts], |
| logit_score=[hyps[0] for hyps in logit_scores], |
| lm_score=[hyps[0] for hyps in lm_scores], |
| word_offsets=[hyps[0] for hyps in word_offsets] if word_offsets is not None else None, |
| ) |
| else: |
| return Wav2Vec2DecoderWithLMOutput( |
| text=[hyps[:n_best] for hyps in batch_texts], |
| logit_score=[hyps[:n_best] for hyps in logit_scores], |
| lm_score=[hyps[:n_best] for hyps in lm_scores], |
| word_offsets=[hyps[:n_best] for hyps in word_offsets] if word_offsets is not None else None, |
| ) |
|
|
| def decode( |
| self, |
| logits: np.ndarray, |
| beam_width: int | None = None, |
| beam_prune_logp: float | None = None, |
| token_min_logp: float | None = None, |
| hotwords: Iterable[str] | None = None, |
| hotword_weight: float | None = None, |
| alpha: float | None = None, |
| beta: float | None = None, |
| unk_score_offset: float | None = None, |
| lm_score_boundary: bool | None = None, |
| output_word_offsets: bool = False, |
| n_best: int = 1, |
| ): |
| """ |
| Decode output logits to audio transcription with language model support. |
| |
| Args: |
| logits (`np.ndarray`): |
| The logits output vector of the model representing the log probabilities for each token. |
| beam_width (`int`, *optional*): |
| Maximum number of beams at each step in decoding. Defaults to pyctcdecode's DEFAULT_BEAM_WIDTH. |
| beam_prune_logp (`int`, *optional*): |
| A threshold to prune beams with log-probs less than best_beam_logp + beam_prune_logp. The value should |
| be <= 0. Defaults to pyctcdecode's DEFAULT_PRUNE_LOGP. |
| token_min_logp (`int`, *optional*): |
| Tokens with log-probs below token_min_logp are skipped unless they are have the maximum log-prob for an |
| utterance. Defaults to pyctcdecode's DEFAULT_MIN_TOKEN_LOGP. |
| hotwords (`list[str]`, *optional*): |
| List of words with extra importance which can be missing from the LM's vocabulary, e.g. ["huggingface"] |
| hotword_weight (`int`, *optional*): |
| Weight multiplier that boosts hotword scores. Defaults to pyctcdecode's DEFAULT_HOTWORD_WEIGHT. |
| alpha (`float`, *optional*): |
| Weight for language model during shallow fusion |
| beta (`float`, *optional*): |
| Weight for length score adjustment of during scoring |
| unk_score_offset (`float`, *optional*): |
| Amount of log score offset for unknown tokens |
| lm_score_boundary (`bool`, *optional*): |
| Whether to have kenlm respect boundaries when scoring |
| output_word_offsets (`bool`, *optional*, defaults to `False`): |
| Whether or not to output word offsets. Word offsets can be used in combination with the sampling rate |
| and model downsampling rate to compute the time-stamps of transcribed words. |
| n_best (`int`, *optional*, defaults to `1`): |
| Number of best hypotheses to return. If `n_best` is greater than 1, the returned `text` will be a list |
| of strings, `logit_score` will be a list of floats, and `lm_score` will be a list of floats, where the |
| length of these lists will correspond to the number of returned hypotheses. The value should be >= 1. |
| |
| <Tip> |
| |
| Please take a look at the example below to better understand how to make use of `output_word_offsets`. |
| |
| </Tip> |
| |
| Returns: |
| [`~models.wav2vec2.Wav2Vec2DecoderWithLMOutput`]. |
| |
| Example: |
| |
| ```python |
| >>> # Let's see how to retrieve time steps for a model |
| >>> from transformers import AutoTokenizer, AutoProcessor, AutoModelForCTC |
| >>> from datasets import load_dataset |
| >>> import datasets |
| >>> import torch |
| |
| >>> # import model, feature extractor, tokenizer |
| >>> model = AutoModelForCTC.from_pretrained("patrickvonplaten/wav2vec2-base-100h-with-lm") |
| >>> processor = AutoProcessor.from_pretrained("patrickvonplaten/wav2vec2-base-100h-with-lm") |
| |
| >>> # load first sample of English common_voice |
| >>> dataset = load_dataset("mozilla-foundation/common_voice_11_0", "en", split="train", streaming=True) |
| >>> dataset = dataset.cast_column("audio", datasets.Audio(sampling_rate=16_000)) |
| >>> dataset_iter = iter(dataset) |
| >>> sample = next(dataset_iter) |
| |
| >>> # forward sample through model to get greedily predicted transcription ids |
| >>> input_values = processor(sample["audio"]["array"], return_tensors="pt").input_values |
| >>> with torch.no_grad(): |
| ... logits = model(input_values).logits[0].cpu().numpy() |
| |
| >>> # retrieve word stamps (analogous commands for `output_char_offsets`) |
| >>> outputs = processor.decode(logits, output_word_offsets=True) |
| >>> # compute `time_offset` in seconds as product of downsampling ratio and sampling_rate |
| >>> time_offset = model.config.inputs_to_logits_ratio / processor.feature_extractor.sampling_rate |
| |
| >>> word_offsets = [ |
| ... { |
| ... "word": d["word"], |
| ... "start_time": round(d["start_offset"] * time_offset, 2), |
| ... "end_time": round(d["end_offset"] * time_offset, 2), |
| ... } |
| ... for d in outputs.word_offsets |
| ... ] |
| >>> # compare word offsets with audio `en_train_0/common_voice_en_19121553.mp3` online on the dataset viewer: |
| >>> # https://huggingface.co/datasets/mozilla-foundation/common_voice_11_0/viewer/en |
| >>> word_offsets[:4] |
| [{'word': 'THE', 'start_time': 0.68, 'end_time': 0.78}, {'word': 'TRACK', 'start_time': 0.88, 'end_time': 1.1}, {'word': 'APPEARS', 'start_time': 1.18, 'end_time': 1.66}, {'word': 'ON', 'start_time': 1.86, 'end_time': 1.92}] |
| ```""" |
|
|
| from pyctcdecode.constants import ( |
| DEFAULT_BEAM_WIDTH, |
| DEFAULT_HOTWORD_WEIGHT, |
| DEFAULT_MIN_TOKEN_LOGP, |
| DEFAULT_PRUNE_LOGP, |
| ) |
|
|
| |
| beam_width = beam_width if beam_width is not None else DEFAULT_BEAM_WIDTH |
| beam_prune_logp = beam_prune_logp if beam_prune_logp is not None else DEFAULT_PRUNE_LOGP |
| token_min_logp = token_min_logp if token_min_logp is not None else DEFAULT_MIN_TOKEN_LOGP |
| hotword_weight = hotword_weight if hotword_weight is not None else DEFAULT_HOTWORD_WEIGHT |
|
|
| |
| self.decoder.reset_params( |
| alpha=alpha, beta=beta, unk_score_offset=unk_score_offset, lm_score_boundary=lm_score_boundary |
| ) |
|
|
| |
| decoded_beams = self.decoder.decode_beams( |
| logits, |
| beam_width=beam_width, |
| beam_prune_logp=beam_prune_logp, |
| token_min_logp=token_min_logp, |
| hotwords=hotwords, |
| hotword_weight=hotword_weight, |
| ) |
|
|
| word_offsets = None |
| if output_word_offsets: |
| word_offsets = [ |
| [ |
| {"word": word, "start_offset": start_offset, "end_offset": end_offset} |
| for word, (start_offset, end_offset) in beam[2] |
| ] |
| for beam in decoded_beams |
| ] |
| logit_scores = [beam[-2] for beam in decoded_beams] |
|
|
| lm_scores = [beam[-1] for beam in decoded_beams] |
|
|
| hypotheses = [beam[0] for beam in decoded_beams] |
|
|
| if n_best > len(decoded_beams): |
| logger.info( |
| "N-best size is larger than the number of generated hypotheses, all hypotheses will be returned." |
| ) |
|
|
| if n_best == 1: |
| return Wav2Vec2DecoderWithLMOutput( |
| text=hypotheses[0], |
| logit_score=logit_scores[0], |
| lm_score=lm_scores[0], |
| word_offsets=word_offsets[0] if word_offsets is not None else None, |
| ) |
| else: |
| return Wav2Vec2DecoderWithLMOutput( |
| text=hypotheses[:n_best], |
| logit_score=logit_scores[:n_best], |
| lm_score=lm_scores[:n_best], |
| word_offsets=word_offsets[:n_best] if word_offsets is not None else None, |
| ) |
|
|
|
|