Fine-tuning Whisper means continuing training on OpenAI's open-source ASR model with your own audio-transcript pairs, so it adapts to your domain, accent, or language. The standard recipe uses Hugging Face Transformers: format your data as 16 kHz audio with reference text, process it with WhisperProcessor, and train with Seq2SeqTrainer, optionally with LoRA so even large-v3 fits on a single 24 GB GPU. Done on matched data, 20-50% relative WER reduction on your domain is a realistic outcome.
Before training anything, know when it is the right tool. If Whisper only misses a few product names, the initial_prompt / prompt-conditioning route costs nothing. If it consistently struggles with your acoustic conditions, accent, jargon density, or language, fine-tuning is how you fix it. The deciding factor will be the quality of your paired data, not your hyperparameters.
This guide gives the working recipe as of 2026: setup, code, LoRA, how much data per goal, evaluation, and the pitfalls that waste GPU weeks.
When Fine-Tuning Whisper Helps (and When Prompting Is Enough)
Skip fine-tuning if: the errors are limited to a small set of rare terms, names, or spellings. Whisper's decoder accepts an initial_prompt (in openai-whisper/faster-whisper) or prompt_ids (Transformers) that biases decoding toward supplied vocabulary. It is weak but free, so always establish this baseline first.
Fine-tune if: errors are systematic: accented or dialectal speech, telephony/far-field audio, dense domain vocabulary (medical, trading, logistics), conversational disfluencies, code-switching, or a language where Whisper's coverage is thin. Prompting cannot fix a distribution mismatch; training on the distribution can.
Choose the base checkpoint pragmatically: whisper-small (244M) for fast iteration and edge deployment, whisper-medium or large-v3-turbo (809M) as the usual sweet spot, large-v3 (1.55B) when you need maximum accuracy and will use LoRA.
Dataset Format: 16 kHz Audio + Text Pairs
Whisper's feature extractor expects 16 kHz mono audio; each example is an audio segment (ideally under 30 seconds, since Whisper operates on 30-second windows) paired with its verbatim transcript. With Hugging Face datasets, resampling is one line:
from datasets import load_dataset, Audio
ds = load_dataset("audiofolder", data_dir="my_corpus") # audio files + metadata.csv
ds = ds.cast_column("audio", Audio(sampling_rate=16000))
Transcript conventions matter as much as audio quality: be consistent about casing, punctuation, numerals, and disfluency handling, because the model will learn whatever convention you feed it. Long recordings should be segmented on utterance boundaries using existing time alignments. This is why time-aligned transcripts are the single most valuable property of a fine-tuning corpus, and why dual-channel (one speaker per channel) conversational data is so much easier to prepare than mixed-mono recordings.
Step-by-Step with Hugging Face Transformers
Install: pip install transformers datasets evaluate jiwer accelerate.
1. Load processor and model.
from transformers import WhisperProcessor, WhisperForConditionalGeneration
model_id = "openai/whisper-large-v3-turbo"
processor = WhisperProcessor.from_pretrained(
model_id, language="hindi", task="transcribe"
)
model = WhisperForConditionalGeneration.from_pretrained(model_id)
model.generation_config.language = "hindi"
model.generation_config.task = "transcribe"
2. Feature extraction. Convert audio to log-Mel input features and transcripts to label token IDs:
def prepare_dataset(batch):
audio = batch["audio"]
batch["input_features"] = processor.feature_extractor(
audio["array"], sampling_rate=16000
).input_features[0]
batch["labels"] = processor.tokenizer(batch["text"]).input_ids
return batch
ds = ds.map(prepare_dataset, remove_columns=ds.column_names["train"], num_proc=4)
3. Data collator. Audio features and text labels need different padding, so the standard recipe uses a small custom collator (DataCollatorSpeechSeq2SeqWithPadding from the Hugging Face fine-tuning tutorial): it pads input_features to Whisper's fixed 30-second window, pads label sequences to the batch maximum, and replaces label padding with -100 so those positions are ignored by the cross-entropy loss. It also strips the leading BOS token if the tokenizer added it, since the model prepends it during training.
4. Train with Seq2SeqTrainer.
from transformers import Seq2SeqTrainingArguments, Seq2SeqTrainer
import evaluate
wer_metric = evaluate.load("wer")
args = Seq2SeqTrainingArguments(
output_dir="whisper-turbo-hi-callcenter",
per_device_train_batch_size=16,
gradient_accumulation_steps=2,
learning_rate=1e-5,
warmup_steps=500,
max_steps=4000,
fp16=True,
eval_strategy="steps",
eval_steps=500,
predict_with_generate=True,
metric_for_best_model="wer",
load_best_model_at_end=True,
)
trainer = Seq2SeqTrainer(
model=model, args=args,
train_dataset=ds["train"], eval_dataset=ds["test"],
data_collator=data_collator, compute_metrics=compute_wer,
processing_class=processor,
)
trainer.train()
Typical hyperparameters: learning rate 1e-5 (full fine-tune) or 1e-3-ish for LoRA, a few thousand steps, early stopping on validation WER. On a single A100, fine-tuning large-v3-turbo on ~50 hours of audio takes on the order of half a day; whisper-small on the same data finishes in a couple of hours. That is why iterating on small first, to debug data processing and normalization, before scaling to the large checkpoint saves both time and money. Gradient checkpointing (gradient_checkpointing=True) trades ~20% speed for a large VRAM reduction if you are memory-bound.
Cheap Fine-Tuning with LoRA / PEFT
Full fine-tuning of large-v3 wants serious VRAM. LoRA freezes the base model and trains small low-rank adapter matrices (roughly 1% of parameters), cutting memory enough to fine-tune large-v3 on a single 24 GB GPU (or an 8-bit-quantized base for even less):
from peft import LoraConfig, get_peft_model
lora = LoraConfig(
r=32, lora_alpha=64,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05,
)
model = get_peft_model(model, lora)
model.print_trainable_parameters() # ~1% trainable
Beyond cost, LoRA has a second virtue: because base weights never change, catastrophic forgetting of Whisper's general multilingual ability is much milder, and you can keep separate adapters per domain or language, swapping them at inference. Accuracy usually lands within a fraction of a WER point of full fine-tuning on adaptation tasks.
How Much Data Do You Need?
| Goal | Typical data needed | Notes |
|---|---|---|
| Vocabulary nudging | 0 hrs (use initial_prompt) |
Free baseline; try first |
| Domain adaptation (jargon, acoustics) | 5-50 hrs | Largest gain per hour; LoRA is ideal |
| Accent / dialect adaptation | 50-500 hrs | Needs speaker diversity, not just hours |
| Weakly covered or new language | 500+ hrs | Full fine-tune; consider tokenizer coverage |
These are honest ranges, not guarantees; the curve depends on how far your domain sits from Whisper's training distribution. Two principles hold everywhere. First, diminishing returns: the first 10 matched hours often deliver more than the next 90. Second, match beats mass: conversational target domains need conversational training data, because read-speech corpora barely move call-center WER. For the fuller treatment, see how much audio data you need to train ASR.
Evaluating with WER and jiwer
Hold out a test set of your audio (several hours, speakers disjoint from training) and never touch it during development. Compute WER with jiwer (directly or via evaluate), normalizing both sides identically:
import jiwer
transform = jiwer.Compose([
jiwer.ToLowerCase(),
jiwer.RemovePunctuation(),
jiwer.RemoveMultipleSpaces(),
jiwer.Strip(),
])
wer = jiwer.wer(references, hypotheses,
truth_transform=transform,
hypothesis_transform=transform)
Report the base model and the fine-tuned model on the same set under the same normalization. That delta is your result. For languages where word boundaries are unreliable (Japanese, Mandarin, Thai) or heavily agglutinative, use character error rate (jiwer.cer) instead of WER. Also spot-check a general benchmark (e.g., FLEURS or Common Voice in your language) to quantify any regression outside your domain. A fine-tune that gains 30% relative in-domain but loses 50% everywhere else may still be the wrong trade for a general-purpose product.
Common Pitfalls
- Catastrophic forgetting. Aggressive fine-tuning on a narrow domain degrades everything else: other languages, other domains, even translation. Mitigate with LoRA, low learning rates, few epochs, and mixing a slice of general-domain data into training.
- Overfitting small datasets. On 5-20 hours, validation WER bottoms out fast and then climbs while training loss keeps falling. Evaluate every few hundred steps and keep the best checkpoint, not the last.
- Timestamp degradation. The standard recipe trains without timestamp tokens, so fine-tuned models often lose accurate timestamping. If you need word timings, either include timestamp tokens in training or realign afterwards with WhisperX-style forced alignment.
- Inconsistent transcripts. Mixed normalization conventions in training data put a hard floor under achievable WER. Fix the convention before spending GPU time.
- Testing on training-adjacent data. Same speakers or same calls in train and test will flatter your numbers and betray you in production.
- Wrong language/task tokens. Whisper conditions decoding on special language and task tokens. If training and inference disagree (e.g., you fine-tune with language forced to Hindi but deploy with auto-detection), accuracy quietly drops. Pin the language in
generation_configfor single-language deployments.
Where to Get Quality Audio-Transcript Pairs
The recipe above is a solved problem; sourcing 50-500 hours of accurately transcribed, in-domain, commercially licensed conversational speech is not. Open corpora are mostly read speech, and scraping audio leaves you without consent chains or training rights. SpeechData.ai's datasets are built to drop into this pipeline: dual-channel 16 kHz-compatible conversational audio with time-aligned verbatim transcripts, speaker metadata, and full commercial licensing across 60 languages, with custom collection covering domains and dialects we don't already stock.
Need training data?
Fine-tuning results are only as good as the audio-transcript pairs behind them, and ours ship fine-tuning-ready. Browse datasets in 60 languages or contact us for a sample to run through this exact pipeline.