TAI BUI
Back to curriculum
LearnPython

Audio Transformers — Whisper Architecture

A log-mel spectrogram is audio drawn as an image of frequency over time. Whisper is a ViT that eats mel spectrograms and speaks back in text tokens.

TIME: ~45 minutesPREREQUISITES: Phase 7 · 05 (Full Transformer), Phase 7 · 08 (Encoder-Decoder), Phase 7 · 09 (ViT)

Learning Objectives

  • Trace Whisper's pipeline from 16 kHz audio through the log-mel spectrogram, convolutional stem, encoder, and decoder to text tokens.
  • Control Whisper's task, language, and timestamp behavior with the control-token prefix of the decoder prompt.
  • Compute frame counts for a 25 ms window with a 10 ms hop and pad the result to Whisper's 3,000-frame window.
  • Choose between Whisper sizes and alternatives (turbo, faster-whisper, Moonshine, pyannote) from their trade-offs and limits.

The Problem

Automatic speech recognition (ASR) is the task of turning audio into text. Before Whisper (OpenAI, Radford et al. 2022), state-of-the-art ASR meant wav2vec 2.0 and HuBERT: self-supervised feature extractors plus a fine-tuned head. Quality was high, but the data pipelines were expensive and the models were domain-brittle. Multilingual recognition needed separate models per language family.

Whisper made three bets:

  1. Train on everything. 680,000 hours of weakly-labeled audio scraped from the internet across 97 languages. No clean academic corpus, no phoneme labels.
  2. Multi-task single model. One decoder trained jointly on transcription, translation, voice activity detection, language ID, and timestamping via task tokens.
  3. Standard encoder-decoder transformer. The encoder consumes log-mel spectrograms and the decoder produces text tokens autoregressively. No vocoder, no CTC, no HMM.

The result is that Whisper large-v3 holds up across accents, noise, and languages with no clean labeled data. In 2026 it is the default speech front-end for open-source voice assistants and many commercial ones.

Warm-up0/4
1. In an encoder-decoder transformer, which part produces the output tokens one at a time?Warm-up
A diagram with four input tokens feeding a box labelled Encoder, then a box labelled Decoder, and a separate row of output tokens y1 to y4 with a question mark asking which block writes them.
Schematic of the setup in the question.
2. A ViT turns an image into something a transformer can read. How?Warm-up
An 8 by 8 pixel picture on the left, an arrow marked with a question mark, and a Transformer box on the right that reads a sequence of vectors v1, v2, v3, v4 and so on.
Schematic. The pixel pattern is made up for illustration.
3. Why do transformer encoders add positional encodings to their inputs?Warm-up
The sentences dog bites man and man bites dog, made of the same three words with different meanings, and below them the three word embeddings e(dog), e(bites), e(man) that an encoder would receive.
Typed example.
4. Digital audio is sampled at 16 kHz. How many samples does one second of it contain?Warm-up
A one-second wave drawn as a blue line with orange dots marking individual samples, a 0 s to 1 s axis, and a box reading sample rate 16 kHz with a question mark for the number of samples in the second.
Schematic. Only a few dots are drawn; a real recording has many more.
Check: The Problem0/2
1. Does Whisper use CTC (Connectionist Temporal Classification) as its training loss?Define
Eight per-frame labels c c blank a a blank t t, a note to merge repeats and drop blanks giving the word cat, and a box asking whether Whisper's training loss is CTC.
Typed illustration of how a CTC output is collapsed. It is not Whisper output.
2. How did Whisper's training approach differ from wav2vec 2.0 and HuBERT?Explain
Three labelled cards in a row: wav2vec 2.0 (Baevski et al., 2020), HuBERT, and Whisper (Radford et al., 2022), with a question about how Whisper's training differed.
Names and years from the lesson's references.

The Concept

Whisper pipeline: audio → mel → encoder → decoder → text

Whisper is an encoder-decoder transformer that reads a 30-second log-mel spectrogram and writes out text tokens. The pipeline runs in six steps.

Step 1: resample + window

Audio is resampled to 16 kHz and clipped or padded to 30 seconds. The log-mel spectrogram has 80 mel bins at a 10 ms stride, which gives ~3,000 frames × 80 features. That is the "input image" Whisper sees.

Step 2: convolutional stem

Two Conv1D layers with kernel 3 and stride 2 reduce the 3,000 frames to 1,500. That halves the sequence length without adding many parameters.

Step 3: encoder

A 32-layer (for large) transformer encoder runs over the 1,500 timesteps, with sinusoidal positional encoding, self-attention, and a GELU FFN. It produces 1,500 × 1,280 hidden states.

Step 4: decoder

A 32-layer (for large) transformer decoder produces tokens autoregressively from a BPE vocabulary that is a superset of GPT-2's plus a few audio-specific special tokens.

Step 5: task tokens

The decoder prompt starts with control tokens that tell the model what to do:

<|startoftranscript|>  <|en|>  <|transcribe|>

or

<|startoftranscript|>  <|fr|>  <|translate|>

The model was trained on this convention, so you pick the task with the prefix. With timestamps on, the model itself then emits <|0.00|> as its first output token; to turn timestamps off you append <|notimestamps|> to the prefix. It is the 2026 equivalent of instruction-tuning, applied to speech.

Step 6: output

Decoding uses beam search (width 5) with a log-prob threshold. When the <|notimestamps|> token is absent, timestamps are predicted every 0.02 seconds of audio.

Whisper sizes

ModelParamsLayersd_modelHeadsVRAM (fp16)
Tiny39M43846~1 GB
Base74M65128~1 GB
Small244M1276812~2 GB
Medium769M24102416~5 GB
Large1550M32128020~10 GB
Large-v31550M32128020~10 GB
Large-v3-turbo809M32128020~6 GB (4-layer decoder)

Large-v3-turbo (2024) cut the decoder from 32 layers to 4, which gives 8× faster decoding with <1 WER point regression. That decode speed is why Whisper-turbo is the default for real-time voice agents in 2026.

What Whisper does not do

  • No diarization (who is speaking). Pair it with pyannote for that.
  • No native real-time streaming, because the 30-second window is fixed. Wrappers such as faster-whisper and WhisperX add streaming through VAD and overlap.
  • No long-form context beyond 30 s without external chunking. That works well in practice because human speech rarely needs long-range context for transcription.

2026 landscape

TaskModelNotes
English ASRWhisper-turbo, MoonshineMoonshine is 4× faster on edge
Multilingual ASRWhisper-large-v397 languages
Streaming ASRfaster-whisper + VAD150 ms latency targets achievable
TTSPiper, XTTS-v2, KokoroEncoder-decoder pattern, but Whisper-shaped
Audio + languageAudioLM, SeamlessM4TText tokens + audio tokens in one transformer
Check: The Concept0/15
1. What did Whisper large-v3-turbo change relative to large-v3?Define
Two rows, large-v3 and large-v3-turbo, each showing audio going through an Encoder box, then a Decoder box, then text, with the question of what changed between the two rows.
Schematic. Box sizes carry no information.
2. What is a mel spectrogram?Define
A strip showing the loudness of a one-second synthetic clip over time, and below it the mel spectrogram of the same clip as a heat map in which two separate bright bands appear.
Computed with numpy: 440 Hz for 0.5 s then 1,760 Hz for 0.5 s, 400-sample window, 160-sample hop, 80 mel bands (pooled for display).
3. What is a log-mel spectrogram?Define
Two heat maps of the same one-second synthetic clip side by side: on the left a mel spectrogram, on the right a log-mel spectrogram, with the right one showing more of the quieter detail.
Computed with numpy. Clip: 440 Hz then 1,760 Hz; each panel scaled to its own maximum.
4. What is a task token in Whisper?Define
One French recording sent through Whisper in run A, giving Bonjour à tous, and in run B, giving Hello everyone, with a question mark between the runs for what was set differently.
Typed illustration; the two outputs are made-up example text, not captured.
5. What do the two Conv1D layers in Whisper's convolutional stem do?Explain
A pipeline of a log-mel input of about 3,000 frames by 80, two Conv1D boxes, and an Encoder box that receives a question mark number of timesteps.
Schematic of the stem.
6. How do you control which task Whisper performs and which language it works in?Explain
A request card listing Japanese spoken audio and a wanted transcript in the same language with timestamps off, an arrow marked with a question mark into a Whisper box, and text coming out.
Schematic of the stem's situation.
7. Why is a log-mel spectrogram described as an image that Whisper reads?Explain
A log-mel spectrogram of a one-second synthetic clip drawn as a heat map.
Computed with numpy. Clip: 440 Hz then 1,760 Hz; log10 power with a floor 8 below the maximum; pooled for display.
8. In Whisper large-v3, how does the decoder depth compare with the encoder depth?Explain
A one-row table for Whisper large-v3 with 1550M parameters, d_model 1280 and 20 heads, and two boxes, Encoder and Decoder, each with the layer count shown as a question mark.
Row copied from the lesson's size table; layer counts left out.
9. Why does Whisper have no native real-time streaming?Explain
A recording that has arrived up to a marker labelled now with the part after it not yet recorded, and the Whisper stages log-mel, conv stem, encoder, decoder and beam search, with a question about whether partial text can be produced during the call.
Schematic. The waveform is made up for illustration.
10. With timestamps on, what does Whisper do after the prefix <|startoftranscript|> <|en|> <|transcribe|>?Explain
The prefix start of transcript, en, transcribe given to the decoder, an arrow down, and a dashed box with a question mark for the first token that follows.
Prefix tokens from the stem.
11. Why does cutting the decoder from 32 layers to 4 speed up Whisper-turbo's decoding?Explain
Two bars drawn to scale, a 32-layer decoder for large-v3 and a 4-layer decoder for turbo, with a question about why the smaller stack decodes faster.
Bar heights proportional to layer count (32 and 4).
12. A recording is 20 minutes long. What does Whisper's fixed window mean for how you run it?Analyze
A long bar for 20 minutes of audio, 1,200 seconds, and a much smaller box for the one fixed-size window Whisper takes per pass, marked not to scale.
Schematic. The window is not drawn to scale.
13. You wanted plain text, but the output contains timestamp tokens like <|0.00|>. What is missing from the prompt?Analyze
A three-token decoder prefix, start of transcript, en, transcribe, an arrow down, and an output that begins with the timestamp token <|0.00|>, with a question about what is missing from the prompt.
Tokens from the stem.
14. Your meeting transcript needs speaker labels. What do you add alongside Whisper?Design
A table with columns start, end, text and speaker, three rows of ellipses with a question mark in the speaker column.
Layout only; no real transcript content.
15. You must transcribe batches of recorded interviews in many languages with one model and no latency limit. Which choice fits?Design
A requirements card: recorded interviews in batches, many languages, one model allowed, no latency limit.
Requirements restated from the stem.

Build It

See code/main.py. It doesn't train or run Whisper. It builds a simplified version of the preprocessing (framing, a per-frame log-energy stand-in for the mel features, padding to 3,000 frames) plus the task-token prompt formatter, the parts you touch in production. It has no mel filterbank or FFT, does not build the 30-second waveform clip (it only pads or clips the list of frames), and implements no convolutional stem, encoder, decoder, beam search, or timestamp prediction.

Step 1: synthesize audio

Generate a 1-second sine wave at 440 Hz sampled at 16 kHz, which is 16,000 samples.

Step 2: framing and log-energy (simplified mel stand-in)

A full log-mel spectrogram needs an FFT and a mel filterbank. This version does framing plus per-frame log sum-of-squares energy, which shows the pipeline's shape without requiring librosa:

python
def frame_signal(x, frame_size=400, hop=160):
    frames = []
    for start in range(0, len(x) - frame_size + 1, hop):
        frames.append(x[start:start + frame_size])
    return frames

A frame is 25 ms and the hop is 10 ms, which matches Whisper's windowing. Per-frame log-energy stands in for mel bins: one number per frame instead of Whisper's 80. For the 1-second signal this gives 98 frames, not 100, because the last frame must fit entirely inside the signal: (16000 - 400) // 160 + 1 = 98.

Step 3: pad to 30 s

Whisper processes 30-second chunks, so pad_or_clip pads (or clips) the list of frames to 3,000 with all-zero frames. It works on frames, not on the waveform.

Step 4: build the prompt tokens

python
def whisper_prompt(lang="en", task="transcribe", timestamps=True):
    tokens = ["<|startoftranscript|>", f"<|{lang}|>", f"<|{task}|>"]
    if not timestamps:
        tokens.append("<|notimestamps|>")
    return tokens

That function is the whole task-control surface. By default it returns a 3-token prefix (start token, language, task); with timestamps=False it appends <|notimestamps|> for a 4-token prefix.

Step 5: the size table

main.py also prints a size table in large-v3 geometry. It lists large-v3 and turbo but not the plain Large row from the table above. The turbo row shows 32 layers (the encoder depth); the 4-layer decoder is only mentioned in the line printed after the table.

Check: Build It0/9
1. In Whisper's preprocessing, what is a frame?Define
A waveform strip, an arrow down to a row of boxes labelled col 0, col 1, col 2, col 3 and so on for spectrogram columns, and a note that each frame becomes one column.
Schematic of the preprocessing order.
2. Why does 1 second of 16 kHz audio give 98 frames rather than 100 with a 400-sample window and 160-sample hop?Explain
A bar for 1 second of audio holding 16,000 samples, a zoom on the first 720 samples showing windows 0, 1 and 2 of 400 samples each starting 160 samples apart, and a note that 100 windows were expected but 98 are returned.
Schematic drawn to scale in the zoom; counts from main.py.
3. For 1 second of 16 kHz audio with a 400-sample window and 160-sample hop, how many frames does `frame_signal` return?Apply
Four lines of code: x is a 1-second 440 Hz sine of 16,000 samples, frame size 400 and hop 160, frames equals frame_signal of x, and len of frames is a question mark.
Lines from the lesson's main.py.
4. You need French speech translated to English text, with no timestamps. Which prompt prefix selects that?Apply
A job card for French speech to be returned as English text with timestamps off, and an empty dashed slot for the decoder prompt prefix.
Schematic of the stem.
5. What does `whisper_prompt()` in `main.py` return when called with default arguments?Apply
The signature of whisper_prompt with defaults lang en, task transcribe and timestamps True, its body hidden, then a call with no arguments whose result is a question mark.
Signature copied from the lesson; body hidden.
6. `frame_signal` runs on 5 seconds of 16 kHz audio (80,000 samples) with a 400-sample window and 160-sample hop. How many frames come back?Apply
A bar for 5 seconds of audio holding 80,000 samples, a zoom on the first 720 samples showing windows 0, 1 and 2 of 400 samples each starting 160 samples apart, and the call len of frame_signal of x5 returning a question mark.
Schematic drawn to scale in the zoom.
7. What does `whisper_prompt(lang="ja", task="transcribe", timestamps=False)` return?Apply
The signature of whisper_prompt with its body hidden, then a call with lang ja, task transcribe and timestamps False whose returned list is shown as a question mark.
Signature copied from the lesson; body hidden.
8. A 30-second clip gives 2,998 frames. What does `pad_or_clip` do with that frame list?Apply
A row of frame 2994 to frame 2997 followed by two dashed slots numbered 2998 and 2999, with a target length of 3,000 and a question about what the function puts in the two slots.
Schematic of the stem.
9. The `main.py` size table prints 32 layers for `turbo`, and a reader concludes turbo did not shrink at all. What is wrong with that reading?Analyze
The size table printed by main.py with columns name, params(M), layers, d_model, heads, listing tiny to turbo, where large-v3 and turbo both show 32 layers, and a note that a reader concluded turbo did not shrink.
Table lines captured from main.py; the line printed after the table is not shown here.

Use It

python
import whisper
model = whisper.load_model("large-v3-turbo")
result = model.transcribe("meeting.wav", language="en", task="transcribe")
print(result["text"])
print(result["segments"][0]["start"], result["segments"][0]["end"])

Faster, OpenAI-compatible:

python
from faster_whisper import WhisperModel
model = WhisperModel("large-v3-turbo", compute_type="int8_float16")
segments, info = model.transcribe("meeting.wav", vad_filter=True)
for s in segments:
    print(f"{s.start:.2f} - {s.end:.2f}: {s.text}")

When to pick Whisper in 2026:

  • Multilingual ASR with one model.
  • Robust transcription of noisy, diverse audio.
  • Research or prototype ASR, where it is the fastest starting point.

When to pick something else:

  • Ultra-low latency streaming on edge: Moonshine beats Whisper at matched quality.
  • Real-time conversational AI needing <200 ms: a dedicated streaming ASR.
  • Speaker diarization: Whisper does not do this, so bolt on pyannote.
Check: Use It0/4
1. What does voice activity detection (VAD) do in an ASR pipeline?Define
A recording drawn as bursts of speech separated by quiet stretches, feeding a box labelled VAD and then a box labelled Whisper, with a question mark for what audio reaches Whisper.
Schematic. The recording shape is made up for illustration.
2. What is faster-whisper?Define
Five lines of the lesson's faster-whisper example: import WhisperModel, build the model with large-v3-turbo, call transcribe on meeting.wav with vad_filter=True, and print start, end and text of each segment.
Copied from the lesson; the constructor arguments after the model name are replaced by an ellipsis.
3. In the faster-whisper snippet, what does `vad_filter=True` do before transcription?Apply
The lesson's five-line faster-whisper example with the transcribe line, which passes vad_filter=True, highlighted.
Copied from the lesson; the constructor arguments after the model name are replaced by an ellipsis.
4. You are building a voice agent that must answer in under 200 ms. What does Use It recommend?Design
Three steps, user stops talking, agent works, agent starts replying, above a timeline from t equals 0 to under 200 ms, marking the budget in which the reply must start.
Schematic of the stem's latency limit.

Ship It

The skill in outputs/skill-asr-configurator.md picks an ASR model, decoding parameters, and preprocessing pipeline for a new speech application.

Author's Note

I'd get the task prefix right before anything else. In main.py, whisper_prompt takes a language, a task, and a timestamps flag, and the demo prints three prefixes: English transcription, French translation without timestamps, and Japanese transcription. Those three lines are what separate the behaviors, and they are the part of Whisper you control from outside the model.

My bet is that the framing arithmetic is worth checking by hand. The script prints the frame counts for a 1-second and a 5-second signal, then pads the 5-second one out to the 3,000-frame target. If you change the frame size or the hop, predict the new counts before you run it.

I'd treat the per-frame energy as a placeholder. It gives one log-energy number per frame, where the code's own comment says Whisper uses 80 mel bins, so Exercise 2 is where the real spectrogram work happens. I'd read the size table the same careful way: the turbo row in main.py lists 32 layers, and the 4-layer decoder is mentioned in the line printed under the table.

Exercises

  1. Easy. Run code/main.py. Confirm the frame count for a 1-second signal at 16 kHz with a 400-sample window and 160-sample (10 ms) hop. The formula (n - 400) // 160 + 1 gives 98 frames, a little under the ~100 you'd get from 1 s / 10 ms. For 30 seconds the same formula gives 2,998, which is why padding to 3,000 matters.
  2. Medium. Build the full log-mel spectrogram using numpy.fft. Verify 80 mel bins match librosa.feature.melspectrogram(n_mels=80) within numerical error.
  3. Hard. Implement streaming inference: chunk audio into 10 s windows with 2 s overlap, run Whisper on each chunk, merge transcripts. Measure word-error rate vs single-pass on a 5-minute podcast sample.

Key Terms

TermWhat people sayWhat it actually means
Mel spectrogram"Audio image"2D representation: frequency bins on one axis, time frames on the other; log-scaled energy per cell.
Log-mel"What Whisper sees"Mel spectrogram passed through log; approximates human perception of loudness.
Frame"One time slice"A 25 ms window of samples; overlapping at 10 ms stride.
Task token"Prompt prefix for speech"Special tokens like <|transcribe|> / <|translate|> in the decoder prompt.
Voice activity detection (VAD)"Find the speech"Gate that removes silence before ASR; cuts cost a lot.
CTC"Connectionist Temporal Classification"Classic ASR loss for alignment-free training; Whisper does not use it.
Whisper-turbo"Small decoder, full encoder"large-v3 encoder + 4-layer decoder; 8× faster decoding.
Faster-whisper"The production wrapper"CTranslate2 reimplementation; int8 quantization; 4× faster than OpenAI's reference.

Further Reading

Code

Ships With