TAI BUI
Back to curriculum
BuildPython

Whisper — Architecture & Fine-Tuning

Whisper is a 30-second-window transformer encoder-decoder, trained on 680k hours of multilingual, weakly-supervised audio-text pairs. One architecture, multiple tasks, robust across 99 languages: the 2026 reference ASR. Know what's inside it before you fine-tune it.

TIME: ~75 minutesPREREQUISITES: Phase 6 · 04 (ASR), Phase 5 · 10 (Attention), Phase 7 · 05 (Full Transformer)

Learning Objectives

  • Describe Whisper's encoder-decoder architecture -- the 30-second log-mel input, transformer encoder/decoder stack, and the special-token prompt format that steers one model across 99 languages and multiple tasks.
  • Configure Whisper's inference defaults (temperature, condition_on_previous_text, no_speech_threshold) and preprocessing (Whisper's own log-mel stats) to avoid its most common hallucination and silence failure modes.
  • Pick the right Whisper variant and tooling -- Turbo, WhisperX, faster-whisper -- for a given latency, accuracy, and long-form or streaming transcription target.
  • Fine-tune Whisper with LoRA, deciding when to freeze the encoder based on how much domain audio is available.
  • Read a cross-attention heatmap from Whisper's decoder to see how it derives word-level timestamps.

The Problem

Whisper, released by OpenAI in September 2022, was the first ASR model to ship as a commodity: paste in audio, get text back, 99 languages, robust to noise, runs on a laptop. OpenAI shipped Large-v3 and Turbo variants by 2024, and by 2026 Whisper is the default baseline for everything from podcast transcription to voice assistants to YouTube subtitles.

Treat it as a black box forever, though, and it will fail you. Domain shift kills it: technical jargon, speaker accents, proper nouns, short clips, silence. You need to know:

  1. What it actually is inside.
  2. How to feed it chunked, streaming, or long-form audio correctly.
  3. When to fine-tune it, and how.
Warm-up0/3
1. In a transformer encoder-decoder, which part typically drives most of the inference latency: the encoder that runs once over the whole input, or the decoder that runs once per output token?Warm-up
Schematic of a transformer encoder-decoder: M input items go into an encoder, then a decoder, which produces an output sequence of tokens token 1 to token N.
Generic schematic, no timings drawn.
2. Most audio models convert a raw waveform into a spectrogram-style representation before feeding it to a neural network. Why, in general?Warm-up
Two panels of the same one-second synthetic clip: a waveform of level against time on the left, and a spectrogram of frequency against time on the right.
Synthetic clip generated with numpy (three vowel-like bursts, not real speech). Spectrogram computed from it.
3. In speech recognition evaluation, what does word error rate (WER) count?Warm-up
A six-word reference sentence 'turn on the kitchen lights now' above a model output 'turn on kitchen light now please', both shown as word chips.
Typed example, not model output.
Check: The Problem0/1
1. Whisper runs on a laptop, covers 99 languages and handles noise well, yet treating it as a black box eventually fails. What commonly kills its performance?Explain
Three strengths on the left (runs on a laptop, covers 99 languages, handles noise well) lead to Whisper used as a black box, then to a box saying performance later falls, cause unknown.

The Concept

Whisper encoder-decoder, tasks, chunked inference, fine-tune

Architecture. Standard transformer encoder-decoder.

  • Input: 30-second log-mel spectrogram, 80 mels, 10 ms hop → 3000 frames. Clips shorter are zero-padded, clips longer are chunked.
  • Encoder: conv-downsample (stride 2) + N transformer blocks. For Large-v3: 32 layers, 1280-dim, 20 heads.
  • Decoder: N transformer blocks with causal self-attn + cross-attn to encoder output, the same size as the encoder.
  • Output: BPE tokens over a 51,865-token vocab.

Large-v3 has 1.55B params. Turbo drops the decoder from 32 layers to 4, cutting latency 8× for a WER hit under 1%.

The prompt format. Whisper is a multitask model steered by special tokens in the decoder prompt:

<|startoftranscript|><|en|><|transcribe|><|notimestamps|> Hello world.<|endoftext|>
  • <|en|>: the language tag, and it decides translate-vs-transcribe behavior.
  • <|transcribe|> or <|translate|>: verbatim output in the source language, or an English translation from any source language.
  • <|notimestamps|>: skip word-level timestamps for faster decoding.

The prompt is what lets one model do many tasks. Change <|en|> to <|fr|> and it transcribes French instead.

30-second window. Everything is pinned to 30 seconds: longer clips need chunking, shorter clips get padded. Windows aren't streamed natively, which is why WhisperX, Whisper-Streaming, and faster-whisper exist.

Log-mel normalization. (log_mel - mean) / std, where the stats come from Whisper's own training corpus. You must use Whisper's preprocessing (whisper.audio.log_mel_spectrogram), not librosa.feature.melspectrogram.

Variants in 2026

VariantParamsLatency (A100)WER (LibriSpeech-clean)
Tiny39M1× realtime5.4%
Base74M1×4.1%
Small244M1×3.0%
Medium769M1×2.7%
Large-v31.55B2×1.8%
Large-v3-turbo809M8×1.58%
Whisper-Streaming (2024)1.55Bstreaming2.0%

Fine-tuning

Canonical workflow in 2026:

  1. Collect 10–100 hours of target-domain audio with aligned transcripts.
  2. Run transformers.Seq2SeqTrainer with a generate_with_loss callback.
  3. Go parameter-efficient: LoRA (low-rank adapter matrices trained in place of the full attention weights) on q_proj and v_proj cuts GPU memory 4× for a WER cost under 0.3.
  4. Freeze the encoder if you have fewer than 10 hours of data. Only tune the decoder.
  5. Use Whisper's own tokenizer and prompt format. Never swap tokenizers.

Community results back this up: fine-tuning Medium on 20 hours of medical dictation drops WER from 12% to 4.5% on medical vocabulary. Fine-tuning Turbo on 4 hours of Icelandic drops WER from 18% to 6%.

Check: The Concept0/16
1. What input does Whisper's architecture expect?Define
Pipeline of Whisper from an audio file through an unknown input representation marked with a question mark, then an encoder and a decoder, producing text.
2. What role does the <|startoftranscript|> token play in Whisper?Define
Five Whisper special tokens as chips, taken from the lesson's code: startoftranscript (highlighted), endoftext, transcribe, translate and notimestamps.
Token names from the lesson's main.py. Roles are not shown.
3. What is a timestamp token in Whisper's vocabulary?Define
A decoder output laid out as chips: three prompt tokens, then the words Hello and world. and endoftext, with a dashed box below asking how the output carries when each word was spoken.
Typed, illustrative layout. The timing representation is not shown.
4. Whisper's Turbo variant runs about 8x faster than Large-v3 with under 1% WER regression. What change gives it that speedup?Define
Two cards. Large-v3 shows log-mel features feeding a 32-layer encoder and a 32-layer decoder. Large-v3-turbo shows about 8x faster, under 1% WER regression, and 'what changed: ?'.
Large-v3 layer counts from the lesson.
5. What does a LoRA fine-tune of Whisper actually train?Define
Left, a schematic of Whisper: an encoder block with attention and MLP, a decoder block with self-attention, cross-attention and MLP, and output embeddings. Right, a LoraConfig with r=16, lora_alpha=32, the target_modules list elided, dropout 0.1, bias none and task_type SEQ_2_SEQ_LM.
Config from the lesson's LoRA snippet with the target list elided. Diagram is schematic.
6. In Whisper's decoder prompt, what happens when you change the language token from <|en|> to <|fr|>?Explain
Two decoder prompts, startoftranscript then a language token then transcribe then notimestamps. The first uses en and the second uses fr; the output of each is marked with a question mark.
Prompt strings from the lesson's build_prompt, captured. The differing token is highlighted.
7. How can one Whisper model transcribe, translate and handle many languages without separate models for each?Explain
Three request cards (English speech to English, French speech to English, Japanese speech to Japanese with timestamps) all feed one Whisper model with a single set of weights; below is the question of how it is told which request it has.
Requests follow the examples in the lesson's code.
8. Why do hour-long recordings call for wrappers such as WhisperX instead of one plain Whisper pass?Explain
A 60-minute recording timeline from 0 to 60 minutes feeds into a box labelled plain Whisper pass, with the question whether one pass can take the whole file.
9. What problem does putting LoRA adapters on the attention projections solve when fine-tuning Whisper?Explain
A schematic attention block with q_proj and v_proj, each with a LoRA adapter attached below, and a box asking what problem this solves.
Schematic. Projection names from the lesson's LoraConfig.
10. Fine-tuning Medium on 20 hours of medical dictation drops WER on medical vocabulary from 12% to 4.5%. Which failure from the lesson's Problem section does this fix?Explain
Bar chart of Medium's WER on medical vocabulary: 12% before fine-tuning and 4.5% after fine-tuning on 20 hours of medical dictation.
Numbers from the question stem.
11. Which decoder prompt asks Whisper to transcribe Spanish audio verbatim, without timestamps?Apply
A request card (Spanish speech, verbatim words, no timestamps) above a prompt that starts with startoftranscript and has three slots: language token, task token and timestamps token.
Schematic. Slot names, no token values.
12. A 10-minute clip is run through Whisper, which handles audio in 30-second windows. How many windows does the chunk schedule contain?Apply
A bar for a 10-minute (600 s) clip, with the first two 30-second windows outlined below it followed by an ellipsis, and the question of how many windows are in the schedule.
Window boxes are not to scale.
13. The encoder starts from 3000 log-mel frames and applies a conv-downsample with stride 2 before its transformer blocks. How many positions does the transformer see?Apply
The encoder path: 3000 log-mel frames, a stride-2 conv-downsample, a box of unknown positions, then the transformer blocks.
14. Using the variants table, which model has the lowest WER on LibriSpeech-clean, and at what A100 latency?Apply
A table of seven Whisper variants from Tiny to Whisper-Streaming with their parameter counts; the latency and WER columns show question marks.
Params from the lesson's table. Latency and WER are hidden on purpose.
15. Large-v3 has a 32-layer encoder and a decoder of the same size. Turbo keeps the encoder and cuts the decoder to 4 layers. How many transformer blocks does Turbo have in total?Apply
Two boxes for Large-v3-turbo, an encoder with 32 layers and a decoder with 4 layers, with a question mark for the total number of blocks.
Layer counts from the stem.
16. You have 5 hours of labeled audio in a low-resource language and one modest GPU. Which fine-tuning plan fits?Design
Three tiles: 5 hours of labeled audio, 1 modest GPU, and a low-resource language, leading to a box asking for the fine-tuning plan.

Build It

Step 1: run Whisper out of the box

Three of Whisper's defaults are worth overriding on every call. Pin temperature=0.0, because left alone, sampling falls back through 0.0 → 0.2 → 0.4 … the moment confidence dips. Set condition_on_previous_text=False, because left on, a single hallucinated window seeds every window after it. Set no_speech_threshold=0.6 explicitly for cleaner silence detection:

python
import whisper
model = whisper.load_model("large-v3-turbo")
result = model.transcribe(
    "clip.wav",
    language="en",
    task="transcribe",
    temperature=0.0,
    condition_on_previous_text=False,  # prevents runaway repetition
    no_speech_threshold=0.6,  # silence detection
)
print(result["text"])
for seg in result["segments"]:
    print(f"[{seg['start']:.2f}–{seg['end']:.2f}] {seg['text']}")

Step 2: chunked long-form

python
# whisperx is the 2026 reference for long-form with word-level timestamps
import whisperx
model = whisperx.load_model("large-v3-turbo", device="cuda", compute_type="float16")
segments = model.transcribe("1hour.mp3", batch_size=16, chunk_size=30)

WhisperX adds three things on top of vanilla Whisper: Silero VAD gating, word-level alignment via wav2vec 2.0, and diarization via pyannote.audio. It's the 2026 workhorse for production transcription.

Step 3: fine-tune with LoRA

python
from transformers import WhisperForConditionalGeneration, WhisperProcessor
from peft import LoraConfig, get_peft_model
 
model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-large-v3-turbo")
lora = LoraConfig(
    r=16, lora_alpha=32, target_modules=["q_proj", "v_proj"],
    lora_dropout=0.1, bias="none", task_type="SEQ_2_SEQ_LM",
)
model = get_peft_model(model, lora)
# model.print_trainable_parameters()  -> ~3M trainable / 809M total

From there it's a standard Trainer loop: checkpoint every 1000 steps and evaluate WER on a held-out set.

Step 4: inspect what each layer learns

python
# Grab cross-attention weights during decode to see what the decoder attends to.
with torch.inference_mode():
    out = model.generate(
        input_features=features,
        return_dict_in_generate=True,
        output_attentions=True,
    )
# out.cross_attentions: layer × head × step × src_len

Visualize it as a heatmap and you'll see diagonal alignment: decoder steps scanning through encoder frames in order. That diagonal is Whisper's notion of word timestamps.

Check: Build It0/7
1. What does WhisperX add on top of vanilla Whisper?Define
A one-hour file 1hour.mp3 goes into a dashed whisperx box holding a Whisper box and three boxes marked with question marks; below is the lesson's whisperx Python snippet.
Snippet from the lesson, line-wrapped. The ? boxes mark what WhisperX adds.
2. In a cross-attention heatmap from Whisper's decoder, what does the diagonal pattern represent?Explain
A heatmap with decoder steps down the vertical axis and encoder positions along the horizontal axis; the bright cells run from the top left to the bottom right.
Synthetic schematic generated with numpy, not captured from a real model.
3. Why does the lesson pin temperature=0.0 when calling transcribe?Explain
A model.transcribe call with clip.wav, language en, task transcribe and temperature 0.0 highlighted, other arguments elided, and the question why the temperature is pinned.
Lines taken from the lesson's Step 1 snippet.
4. Which attention projection matrices does the LoraConfig in the lesson's fine-tuning code target?Apply
The lesson's LoraConfig call with r=16, lora_alpha=32, dropout 0.1, bias none and task_type SEQ_2_SEQ_LM, and the target_modules list replaced by a question mark.
Copied from the lesson's Step 3, with the list hidden.
5. The LoRA snippet's comment says about 3M trainable parameters out of 809M total. Roughly what fraction of the model is trained?Apply
A full-width bar for 809M total parameters with a very thin orange slice at its left end marking about 3M trainable, drawn to scale, above the lesson's print_trainable_parameters comment.
Comment line from the lesson's LoRA snippet.
6. After one bad segment, your transcript repeats the same wrong phrase across several consecutive 30-second windows. Which setting fixes this?Analyze
Five consecutive 30-second windows: the first two have correct text, and the last three each show the same wrong phrase.
Schematic of the stem's situation, text typed.
7. When would you deliberately leave condition_on_previous_text on, given its cascade risk?Analyze
Window 1 passes its previous text to window 2, with condition_on_previous_text=True highlighted and a note about the cascade risk.

Use It

The 2026 stack:

SituationPick
General English, offlineLarge-v3-turbo via whisperx
Mobile / edgeWhisper-Tiny quantized (int8) or Moonshine
Multilingual long-formLarge-v3 via whisperx + diarization
Low-resource languageFine-tune Medium or Turbo with LoRA
Streaming (2 s latency)Whisper-Streaming or Parakeet-TDT
Word-level timestampsWhisperX (forced alignment via wav2vec 2.0)

faster-whisper (CTranslate2 backend) is the fastest CPU+GPU inference runtime in 2026, running 4× faster than vanilla with identical output.

Check: Use It0/3
1. What does faster-whisper offer over vanilla Whisper inference?Explain
A 10-minute podcast goes to two runtimes, vanilla Whisper and faster-whisper; each is measured on time taken and WER against a human transcript, all four results shown as question marks.
Setup follows the lesson's Medium exercise.
2. You must transcribe 90-minute multilingual panel recordings offline and label who said what. Which pick fits best?Design
Three requirement chips (90-minute panel recordings, multilingual, offline processing) above a mock transcript where each line starts with 'speaker ?:' followed by placeholder text.
Placeholder transcript, not real output.
3. A voice assistant needs partial transcripts about 2 seconds after the speaker talks. Which approach fits?Design
A time axis with a speaker-talking bar starting at 0 s and a dashed partial-transcript bar starting about 2 seconds later, joined by an arrow.
Schematic of the stem's latency target.

Pitfalls that still ship in 2026

  • Hallucinated text on silence. Whisper trained on captions that include "Thanks for watching!", "Subscribe!", and song lyrics, so it reproduces them out of noise. Always VAD-gate before you call it.
  • condition_on_previous_text cascade. One hallucination pollutes every window after it. Leave this False unless you specifically need fluency across chunks.
  • Short-clip padding. A 2-second clip padded to 30 seconds can hallucinate into the trailing silence. Use pad=False or VAD-gate instead.
  • Wrong mel stats. Using librosa's mels instead of Whisper's own produces near-random output. Use whisper.audio.log_mel_spectrogram.
Check: Pitfalls that still ship in 20260/7
1. What does 'hallucination' mean when people describe Whisper's failure modes?Define
A pipeline of audio, Whisper and transcript, above a card saying people say Whisper hallucinates, with the meaning of the word given as a question mark.
2. Why does Whisper emit text like 'Thanks for watching!' on silent audio?Explain
A near-flat waveform labelled silent audio goes into Whisper, which outputs the text 'Thanks for watching!', followed by the question why this text appears.
Schematic. Waveform is synthetic near-silence, output text typed from the stem.
3. A 2-second clip is passed to Whisper without VAD gating or pad=False. About how much of the 30-second input is padding?Apply
A 30-second input bar drawn to scale with a 2-second clip filling its left end and the rest of the bar blank, with tick marks at 0, 2 and 30 seconds.
Drawn to scale.
4. You swap Whisper's preprocessing for librosa.feature.melspectrogram and the transcript turns to near-random text. What most likely explains this?Analyze
Two pipelines into Whisper: audio through whisper.audio.log_mel_spectrogram before, and audio through librosa.feature.melspectrogram after, with the result after the swap described as near-random text.
5. A call recording has long silent gaps, and the transcript contains 'Thanks for watching! Subscribe!' in those spots. Which fix does the lesson give first?Analyze
A call recording drawn as blue speech blocks separated by three flat silent gaps, with the transcript in those gaps reading 'Thanks for watching! Subscribe!'.
Schematic recording, not a real call. Gap lengths are illustrative.
6. Hallucinated text on silence and short-clip padding are both listed as pitfalls. What do they have in common as failure triggers?Analyze
Two boxes, hallucinated text on silence and short-clip padding of a 2 s clip to 30 s, joined by a bracket to a box asking what they share as a trigger.
7. Your Whisper WER on domain audio is poor, and you have not yet checked preprocessing. What is the best next step?Analyze
A pipeline of audio, preprocessing (marked not checked yet), Whisper and WER (marked poor on domain audio), followed by the question of the best next step.

Ship It

outputs/skill-whisper-tuner.md is a reusable skill for designing a Whisper fine-tune or inference pipeline for a given domain.

Author's Note

The mistake I've seen most often with Whisper isn't a modeling mistake, it's a preprocessing one. People jump straight to fine-tuning before checking what's actually broken. Nine times out of ten the fix is boring: use Whisper's own mel stats, VAD-gate before you call the model, and don't pad short clips into hallucinating silence. Get those three right first. If WER is still bad after that, you've earned the right to reach for LoRA and decoder freezing, and you'll usually need less fine-tuning than you expected.

Exercises

  1. Easy. Run code/main.py. It tokenizes a Whisper-style prompt, computes decoded shape budgets, and prints the chunk schedule for a 10-minute clip.
  2. Medium. Install faster-whisper, transcribe a 10-minute podcast, compare WER against a human transcript. Try language="auto" vs forced language="en".
  3. Hard. Using HF datasets, pick a language Whisper struggles with (e.g., Urdu), fine-tune Medium with LoRA for 2 epochs on 2 hours, and report WER delta.

Key Terms

TermWhat people sayWhat it actually means
30-sec windowWhisper's limitHard input cap; chunk longer audio.
SOTStart-of-transcript<|startoftranscript|> kicks off the decoder prompt.
Timestamps tokenTemporal alignmentEvery 0.02 s offset is a special token in the 51k vocab.
TurboThe fast variant4-decoder layers, 8× faster, <1% WER regression.
WhisperXThe long-form wrapperVAD + Whisper + wav2vec alignment + diarization.
LoRA fine-tuneEfficient tuningAdd low-rank adapters to attention; train ~0.3% of params.
HallucinationThe silent failureWhisper produces fluent English from noise/silence.

Further Reading

Code

Ships With