linto-ai/whisper-timestamped

★ 2,844⑂ 0

Multilingual Automatic Speech Recognition with word-level timestamps and confidence

2,844Star
0Fork
0Watch
0Issue
PythonLanguage
-License
Created · last push · repository size 0 KB · default branch -

README

whisper-timestamped

Multilingual Automatic Speech Recognition with word-level timestamps and confidence.

Description

Whisper is a set of multi-lingual, robust speech recognition models trained by OpenAI that achieve state-of-the-art results in many languages. Whisper models were trained to predict approximate timestamps on speech segments (most of the time with 1-second accuracy), but they cannot originally predict word timestamps. This repository proposes an implementation to predict word timestamps and provide a more accurate estimation of speech segments when transcribing with Whisper models. Besides, a confidence score is assigned to each word and each segment.

The approach is based on Dynamic Time Warping (DTW) applied to cross-attention weights, as demonstrated by this notebook by Jong Wook Kim. There are some additions to this notebook:

whisper-timestamped is an extension of the openai-whisper Python package and is meant to be compatible with any version of openai-whisper. It provides more efficient/accurate word timestamps, along with those additional features: to avoid hallucinations due to errors in the training data (for instance, predicting "Thanks you for watching!" on pure silence). Several VAD methods are available: silero (default), auditok, auditok:v3.1 Disclaimer: Please note that this extension is intended for experimental purposes and may significantly impact performance. We are not responsible for any issues or inefficiencies that arise from its use.

Notes on other approaches

An alternative relevant approach to recovering word-level timestamps involves using wav2vec models that predict characters, as successfully implemented in whisperX. However, these approaches have several drawbacks that are not present in approaches based on cross-attention weights such as whisper_timestamped. These drawbacks include:

An alternative approach that does not require an additional model is to look at the probabilities of timestamp tokens estimated by the Whisper model after each (sub)word token is predicted. This was implemented, for instance, in whisper.cpp and stable-ts. However, this approach lacks robustness because Whisper models have not been trained to output meaningful timestamps after each word. Whisper models tend to predict timestamps only after a certain number of words have been predicted (typically at the end of a sentence), and the probability distribution of timestamps outside this condition may be inaccurate. In practice, these methods can produce results that are totally out-of-sync on some periods of time (we observed this especially when there is jingle music). Also, the timestamp precision of Whisper models tends to be rounded to 1 second (as in many video subtitles), which is too inaccurate for words, and reaching better accuracy is tricky.

Installation

First installation

Requirements:

You can install whisper-timestamped either by using pip:
pip3 install whisper-timestamped

or by cloning this repository and running installation:

git clone https://github.com/linto-ai/whisper-timestamped
cd whisper-timestamped/
python3 setup.py install

Additional packages that might be needed

If you want to plot alignment between audio timestamps and words (as in this section), you also need matplotlib:

pip3 install matplotlib

If you want to use VAD option (Voice Activity Detection before running Whisper model), you also need torchaudio and onnxruntime:

pip3 install onnxruntime torchaudio

If you want to use finetuned Whisper models from the Hugging Face Hub, you also need transformers:

pip3 install transformers

Docker

A docker image of about 9GB can be built using:

git clone https://github.com/linto-ai/whisper-timestamped
cd whisper-timestamped/
docker build -t whisper_timestamped:latest .

Light installation for CPU

If you don't have a GPU (or don't want to use it), then you don't need to install the CUDA dependencies. You should then just install a light version of torch before installing whisper-timestamped, for instance as follows:

pip3 install \
     torch==1.13.1+cpu \
     torchaudio==0.13.1+cpu \
     -f https://download.pytorch.org/whl/torch_stable.html

A specific docker image of about 3.5GB can also be built using:

git clone https://github.com/linto-ai/whisper-timestamped
cd whisper-timestamped/
docker build -t whisper_timestamped_cpu:latest -f Dockerfile.cpu .

Upgrade to the latest version

When using pip, the library can be updated to the latest version using:

pip3 install --upgrade --no-deps --force-reinstall git+https://github.com/linto-ai/whisper-timestamped

A specific version of openai-whisper can be used by running, for example:

pip3 install openai-whisper==20230124

Usage

Python

In Python, you can use the function whisper_timestamped.transcribe(), which is similar to the function whisper.transcribe():

import whisper_timestamped
help(whisper_timestamped.transcribe)
The main difference with whisper.transcribe() is that the output will include a key "words" for all segments, with the word start and end position. Note that the word will include punctuation. See the example below.

Besides, the default decoding options are different to favour efficient decoding (greedy decoding instead of beam search, and no temperature sampling fallback). To have same default as in whisper, use ``beam_size=5, best_of=5, temperature=(0.0, 0.2, 0.4, 0.6, 0.8, 1.0)``.

There are also additional options related to word alignement.

In general, if you import whisper_timestamped instead of whisper in your Python script and use transcribe(model, ...) instead of model.transcribe(...), it should do the job:

import whisper_timestamped as whisper

audio = whisper.load_audio("AUDIO.wav")

model = whisper.load_model("tiny", device="cpu")

result = whisper.transcribe(model, audio, language="fr")

import json print(json.dumps(result, indent = 2, ensure_ascii = False))

Note that you can use a finetuned Whisper model from HuggingFace or a local folder by using the load_model method of whisper_timestamped. For instance, if you want to use whisper-large-v2-nob, you can simply do the following:

import whisper_timestamped as whisper

model = whisper.load_model("NbAiLab/whisper-large-v2-nob", device="cpu")

...

Command line

You can also use whisper_timestamped on the command line, similarly to whisper. See help with:

whisper_timestamped --help

The main differences with whisper CLI are:

To set the same as Whisper default, you can use --accurate (which is an alias for ``--beam_size 5 --temperature_increment_on_fallback 0.2 --best_of 5``). An example command to process several files using the tiny model and output the results in the current folder, as would be done by default with whisper, is as follows:
whisper_timestamped audio1.flac audio2.mp3 audio3.wav --model tiny --output_dir .

Note that you can use a fine-tuned Whisper model from HuggingFace or a local folder. For instance, if you want to use the whisper-large-v2-nob model, you can simply do the following:

whisper_timestamped --model NbAiLab/whisper-large-v2-nob <...>

Utility Functions

In addition to the main transcribe function, whisper-timestamped provides some utility functions:

remove_non_speech

Remove non-speech segments from audio using Voice Activity Detection (VAD).

from whisper_timestamped import remove_non_speech

audio_speech, segments, convert_timestamps = remove_non_speech(audio, vad="silero")

load_model

Load a Whisper model from a given name or path, including support for fine-tuned models from HuggingFace.

from whisper_timestamped import load_model

model = load_model("NbAiLab/whisper-large-v2-nob", device="cpu")

Plot of word alignment

Note that you can use the plot_word_alignment option of the whisper_timestamped.transcribe() Python function or the --plot option of the whisper_timestamped CLI to see the word alignment for each segment.

Example alignement

Example output

The output of whisper_timestamped.transcribe() function is a python dictionary, which can be viewed in JSON format using the CLI.

The JSON schema can be seen in tests/json_schema.json.

Here is an example output:

whisper_timestamped AUDIO_FILE.wav --model tiny --language fr
{
  "text": " Bonjour! Est-ce que vous allez bien?",
  "segments": [
    {
      "id": 0,
      "seek": 0,
      "start": 0.5,
      "end": 1.2,
      "text": " Bonjour!",
      "tokens": [ 25431, 2298 ],
      "temperature": 0.0,
      "avg_logprob": -0.6674491882324218,
      "compression_ratio": 0.8181818181818182,
      "no_speech_prob": 0.10241222381591797,
      "confidence": 0.51,
      "words": [
        {
          "text": "Bonjour!",
          "start": 0.5,
          "end": 1.2,
          "confidence": 0.51
        }
      ]
    },
    {
      "id": 1,
      "seek": 200,
      "start": 2.02,
      "end": 4.48,
      "text": " Est-ce que vous allez bien?",
      "tokens": [ 50364, 4410, 12, 384, 631, 2630, 18146, 3610, 2506, 50464 ],
      "temperature": 0.0,
      "avg_logprob": -0.43492694334550336,
      "compression_ratio": 0.7714285714285715,
      "no_speech_prob": 0.06502953916788101,
      "confidence": 0.595,
      "words": [
        {
          "text": "Est-ce",
          "start": 2.02,
          "end": 3.78,
          "confidence": 0.441
        },
        {
          "text": "que",
          "start": 3.78,
          "end": 3.84,
          "confidence": 0.948
        },
        {
          "text": "vous",
          "start": 3.84,
          "end": 4.0,
          "confidence": 0.935
        },
        {
          "text": "allez",
          "start": 4.0,
          "end": 4.14,
          "confidence": 0.347
        },
        {
          "text": "bien?",
          "start": 4.14,
          "end": 4.48,
          "confidence": 0.998
        }
      ]
    }
  ],
  "language": "fr"
}
If the language is not specified (e.g. without option --language fr in the CLI) you will find an additional key with the language probabilities:
{
  ...
  "language": "fr",
  "language_probs": {
    "en": 0.027954353019595146,
    "zh": 0.02743500843644142,
    ...
    "fr": 0.9196318984031677,
    ...
    "su": 3.0119704064190955e-08,
    "yue": 2.2565967810805887e-05
  }
}

API Reference

Main Transcription Function

transcribe_timestamped(model, audio, kwargs)

Transcribe audio using a Whisper model and compute word-level timestamps.

Parameters:
  • model: Whisper model instance
The Whisper model to use for transcription.
  • audio: Union[str, np.ndarray, torch.Tensor]
The path to the audio file to transcribe, or the audio waveform as a NumPy array or PyTorch tensor.
  • language: str, optional (default: None)
The language of the audio. If None, language detection will be performed.
  • task: str, default "transcribe"
The task to perform: either "transcribe" for speech recognition or "translate" for translation to English.
  • vad: Union[bool, str, List[Tuple[float, float]]], optional (default: False)
Whether to use Voice Activity Detection (VAD) to remove non-speech segments. Can be:
  • True/False: Enable/disable VAD (uses Silero VAD by default)
  • "silero": Use Silero VAD
  • "auditok": Use Auditok VAD
  • List of (start, end) timestamps: Explicitly specify speech segments
  • detect_disfluencies: bool, default False
Whether to detect and mark disfluencies (hesitations, filler words, etc.) in the transcription.
  • trust_whisper_timestamps: bool, default True
Whether to rely on Whisper's timestamps for initial segment positions.
  • compute_word_confidence: bool, default True
Whether to compute confidence scores for words.
  • include_punctuation_in_confidence: bool, default False
Whether to include punctuation probability when computing word confidence.
  • refine_whisper_precision: float, default 0.5
How much to refine Whisper segment positions, in seconds. Must be a multiple of 0.02.
  • min_word_duration: float, default 0.02
Minimum duration of a word, in seconds.
  • plot_word_alignment: bool or str, default False
Whether to plot the word alignment for each segment. If a string, save the plot to the given file.
  • word_alignement_most_top_layers: int, optional (default: None)
Number of top layers to use for word alignment. If None, use all layers.
  • remove_empty_words: bool, default False
Whether to remove words with no duration occurring at the end of segments.
  • naive_approach: bool, default False
Force the naive approach of decoding twice (once for transcription, once for alignment).
  • use_backend_timestamps: bool, default False
Whether to use word timestamps provided by the backend (openai-whisper or transformers), instead of the ones computed by more complex heuristics of whisper-timestamped.
  • temperature: Union[float, List[float]], default 0.0
Temperature for sampling. Can be a single value or a list for fallback temperatures.
  • compression_ratio_threshold: float, default 2.4
If the gzip compression ratio is above this value, treat the decoding as failed.
  • logprob_threshold: float, default -1.0
If the average log probability is below this value, treat the decoding as failed.
  • no_speech_threshold: float, default 0.6
Probability threshold for <|nospeech|> tokens.
  • condition_on_previous_text: bool, default True
Whether to provide the previous output as a prompt for the next window.
  • initial_prompt: str, optional (default: None)
Optional text to provide as a prompt for the first window.
  • suppress_tokens: str, default "-1"
Comma-separated list of token ids to suppress during sampling.
  • fp16: bool, optional (default: None)
Whether to perform inference in fp16 precision.
  • verbose: bool or None, default False
Whether to display the text being decoded to the console. If True, displays all details. If False, displays minimal details. If None, does not display anything.
Returns:

A dictionary containing:

  • text: str - The full transcription text
  • segments: List[dict] - List of segment dictionaries, each containing:
  • id: int - Segment ID
  • seek: int - Start position in the audio file (in samples)
  • start: float - Start time of the segment (in seconds)
  • end: float - End time of the segment (in seconds)
  • text: str - Transcribed text for the segment
  • tokens: List[int] - Token IDs for the segment
  • temperature: float - Temperature used for this segment
  • avg_logprob: float - Average log probability of the segment
  • compression_ratio: float - Compression ratio of the segment
  • no_speech_prob: float - Probability of no speech in the segment
  • confidence: float - Confidence score for the segment
  • words: List[dict] - List of word dictionaries, each containing:
  • start: float - Start time of the word (in seconds)
  • end: float - End time of the word (in seconds)
  • text: str - The word text
  • confidence: float - Confidence score for the word (if computed)
  • language: str - Detected or specified language
  • language_probs: dict - Language detection probabilities (if applicable)
Exceptions:
  • RuntimeError: If the VAD method is not properly installed or configured.
  • ValueError: If the refine_whisper_precision is not a positive multiple of 0.02.
  • AssertionError: If the audio duration is shorter than expected or if there are inconsistencies in the number of segments.
Notes:
  • The function uses the Whisper model to transcribe the audio and then performs additional processing to generate word-level timestamps and confidence scores.
  • The VAD feature can significantly improve transcription accuracy by removing non-speech segments, but it requires additional dependencies (e.g., torchaudio and onnxruntime for Silero VAD).
  • The naive_approach parameter can be useful for debugging or when dealing with specific audio characteristics, but it may be slower than the default approach.
  • When use_efficient_by_default is True, some parameters like best_of, beam_size, and temperature_increment_on_fallback are set to None by default for more efficient processing.
  • The function supports both OpenAI Whisper and Transformers backends, which can be specified when loading the model.

Utility Functions

remove_non_speech(audio, kwargs)

Remove non-speech segments from audio using Voice Activity Detection (VAD).

Parameters:
Audio data as a PyTorch tensor. If True, return start and end times in samples instead of seconds. Minimum duration of a speech segment in seconds. Minimum duration of a silence segment in seconds. How much to enlarge each speech segment detected by VAD, in seconds. Sample rate of the audio. VAD method to use. Can be "silero", "auditok", or a list of timestamps. If True, avoid returning an empty speech segment. If True, plot the VAD results. If a string, save the plot to the given file.
Returns:

A tuple containing: 1. torch.Tensor: Audio with non-speech segments removed 2. List[Tuple[float, float]]: List of (start, end) timestamps for speech segments 3. Callable: Function to convert timestamps from the new audio to the original audio

Exceptions:
Notes:

load_model(name, device=None, backend="openai-whisper", download_root=None, in_memory=False)

Load a Whisper model from a given name or path.

Parameters:
Name of the model or path to the model. Can be: Device to use. If None, use CUDA if available, otherwise CPU. Backend to use. Either "transformers" or "openai-whisp

More Audio Trending projects

1

huggingface / transformers

Python★ 166,108⑂ 0
2

harry0703 / MoneyPrinterTurbo

Python★ 123,776⑂ 0
3

unslothai / unsloth

Python★ 76,181⑂ 0
4

RVC-Boss / GPT-SoVITS

Python★ 61,798⑂ 0
5

calesthio / OpenMontage

Python★ 59,205⑂ 0
6

ggml-org / whisper.cpp

C++★ 53,674⑂ 0