mstar.model.whisper.components.alignment#
Word-level timestamps from the decoder’s cross-attention.
Whisper was not trained to emit word boundaries; openai-whisper derives them
from a handful of cross-attention heads (alignment_heads in the
generation config) that track the audio position of the token being
predicted. The recipe, reproduced here on M*’s own components:
run the decoder once more over
<|sot|><|lang|><|task|><|notimestamps|> text... <|eot|>with teacher forcing and keep those heads’ attention probabilities over the encoder frames that hold audio;normalize each head over its tokens, smooth along time with a median filter, average the heads: one
(tokens, frames)alignment matrix (rowiis the step that predicts text tokeni; the last row predicts end-of-text);dynamic time warping through that matrix gives a monotonic path; the frame at which the path first enters a row is when that token starts;
tokens are grouped into words (a new word at a leading space or a lone punctuation mark, or at every run of tokens that decodes to whole characters in a language written without spaces); a word starts with its first token and ends where the next word starts, the last one where end-of-text is predicted.
Steps 1 and 2 are GPU work: the decoder submodule’s align walk emits the
matrix (alignment_output). Steps 3 and 4 run on the host in the API
server, when WhisperModel.postprocess renders that output
(word_timings). The functions here are pure tensor/numpy code with no
engine dependencies, so the same math is unit-tested on the CPU.
Functions
|
|
|
The align walk's one output tensor, from the alignment heads' |
|
Cheapest monotonic path from the top-left to the bottom-right of |
|
Sliding median over the last dim (odd |
|
openai-whisper's |
|
Group text tokens into words, as openai-whisper's |
|
The frame at which the DTW path first enters each row of |
|
|
|
Compile the DTW now, in the process that runs it (the API server's data worker, at model load), rather than inside the first word-timestamp request. |
|
Words with |
- mstar.model.whisper.components.alignment.alignment_matrix(weights, num_frames, medfilt_width=MEDIAN_FILTER_WIDTH)[source]#
weights: (heads, rows, frames)attention probabilities of the alignment heads -> the(rows, audio_frames)matrix DTW walks.Only the frames that hold audio count (
num_framesmel frames, two per encoder position); each head is standardized over its rows so heads with different sharpness weigh alike, smoothed along time, then averaged.
- mstar.model.whisper.components.alignment.alignment_output(weights, tokens, num_frames)[source]#
The align walk’s one output tensor, from the alignment heads’
weightsover the whole teacher-forcedtokens(<|sot|><|lang|><|task|><|notimestamps|> text <|eot|>).Layout, float32
[n_text + 1, frames + 1]:alignment_matrixof the rows that predict each text token and end-of-text, then one column of token ids (exact in float32): rowi’s text token, and on the last row the language token, which word splitting needs. Device ops only, so the walk never waits on the host.
- mstar.model.whisper.components.alignment.dtw(cost)[source]#
Cheapest monotonic path from the top-left to the bottom-right of
cost(rows x frames), moving right, down or diagonally. Returns the path’s row and frame indices, each of lengthlen(path).- Parameters:
cost (ndarray)
- Return type:
tuple[ndarray, ndarray]
- mstar.model.whisper.components.alignment.median_filter(x, width=MEDIAN_FILTER_WIDTH)[source]#
Sliding median over the last dim (odd
width), edges reflected.
- mstar.model.whisper.components.alignment.merge_punctuation(words)[source]#
openai-whisper’s
merge_punctuations: opening marks join the word after them (scanning right to left), then closing marks join the word before them. A word keeps its own timing; the mark’s is dropped.
- mstar.model.whisper.components.alignment.split_words(decode, token_ids, language=None)[source]#
Group text tokens into words, as openai-whisper’s
split_to_word_tokens: first into pieces that decode to whole characters (a token ending inside a multi-byte character continues into the next), then, unlesslanguageis written without spaces, into words that begin at a leading space or a lone punctuation mark.decode(ids) -> stris the tokenizer’s plain decode, without clean-up, so the pieces’ texts add up todecode(token_ids).
- mstar.model.whisper.components.alignment.token_start_frames(matrix)[source]#
The frame at which the DTW path first enters each row of
matrix(a high value is a good alignment, so the path minimizes-matrix).- Parameters:
matrix (Tensor)
- Return type:
ndarray
- mstar.model.whisper.components.alignment.unpack_output(output)[source]#
alignment_output’s tensor -> (matrix, text token ids, language token id).
- mstar.model.whisper.components.alignment.warm_up()[source]#
Compile the DTW now, in the process that runs it (the API server’s data worker, at model load), rather than inside the first word-timestamp request.
- Return type:
None
- mstar.model.whisper.components.alignment.word_timings(matrix, text_tokens, decode, language=None)[source]#
Words with
start/endseconds for one window.matrixisalignment_matrixover the rows that predicttext_tokensplus the final row that predicts end-of-text (len(text_tokens) + 1rows);languageis the transcript’s language code.