🎤 Comment fonctionne un STT

Guide visuel complet — De l'onde sonore à la transcription

📖 Vue d'ensemble — Speech-to-Text

Transforme une onde sonore en texte, mot par mot.

🎯 Fil rouge : "Bonjour, comment ça va ?"

Un extrait audio de 3 secondes qui traverse tout le pipeline.

Durée
3 secondes
Sample Rate
16 kHz
Échantillons
48 000
Modèle
Whisper large-v3
🎵 Audio Onde sonore 📊 Mel Spectrogramme 🔢 Features Log-mel norm. 🏗️ Encoder Transformer 🔮 Decoder Auto-régressif 📝 Texte + Timestamps

💡 L'essentiel

Un STT moderne (Whisper, Canary) utilise une architecture encoder-decoder : l'encoder compresse l'audio en représentations, le decoder génère le texte token par token. Tout repose sur des Transformers, comme les LLM.

📚 Les 12 sections

SectionCe que tu vas apprendre
🎵 AudioOnde sonore, échantillonnage, sample rate
📊 Mel SpectrogramFFT, fréquences, perception humaine
🔢 FeaturesLog-mel, normalisation, format d'entrée
🏗️ EncoderArchitecture Transformer, convolutions
🔮 DecoderGénération auto-régressive, cross-attention
⏱️ TimestampsWord-level, segment-level, alignement
🌍 LanguesDétection automatique, multilingual
📏 Chunking30 secondes, overlap, VAD
⚡ MétriquesWER, RTFx, latence
🔧 OptimisationsCTranslate2, int8, batching
🆚 ModèlesWhisper vs Canary vs Parakeet

🎵 Audio Input — L'onde sonore

Tout commence par une vibration de l'air, capturée par un microphone.

📍 Fil rouge — Étape 1/12
"Bonjour, comment ça va ?" → 48 000 échantillons (3s × 16 000 Hz)
Forme d'onde (Waveform) "Bon-" "-jour" "comment" "ça" "va" Échantillonnage (Sampling) Sample Rate : 16 000 Hz = 16 000 mesures par seconde Valeur : -32768 à +32767 (int16) Format requis par Whisper • Mono (1 canal) • 16 kHz sample rate • Float32 normalisé [-1.0, +1.0]

💡 L'essentiel

L'audio est une suite de nombres représentant l'amplitude du son à chaque instant. Whisper attend du mono 16 kHz. Un audio de 3 secondes = 48 000 valeurs flottantes entre -1 et +1.

Sample Rate

Nombre d'échantillons par seconde. 16 kHz = standard STT (suffisant pour la voix humaine).

Bit Depth

Précision de chaque échantillon. 16-bit = 65 536 niveaux possibles.

Nyquist

Sample rate = 2× la fréquence max. 16 kHz capture jusqu'à 8 kHz (voix = 300-3400 Hz).

Mono vs Stereo

STT = mono. Stereo est downmixé (moyenne des canaux).

Aller plus loin

Pourquoi 16 kHz et pas 44.1 kHz ?

La voix humaine intelligible est concentrée entre 300 Hz et 3400 Hz (bande téléphonique). 16 kHz capture jusqu'à 8 kHz, largement suffisant. Plus de samples = plus de calcul pour rien.


Preprocessing courant :

• Resampling (ffmpeg, librosa) si source ≠ 16 kHz
• Normalisation amplitude (peak ou RMS)
• Conversion stereo → mono
• Suppression du silence (VAD)

📊 Mel Spectrogram — L'image du son

Transformer l'onde temporelle en représentation temps-fréquence, adaptée à l'oreille humaine.

📍 Fil rouge — Étape 2/12
48 000 échantillons → Matrice 80 × 300 (80 bandes mel × 300 frames)
Waveform STFT Fenêtre glissante 25ms / hop 10ms Mel Filterbank 80 filtres triangulaires Mel Spectrogram 🎵 Échelle Mel — Perception humaine L'oreille perçoit les fréquences de façon logarithmique : • 100 Hz → 200 Hz : grand écart perçu • 5000 Hz → 5100 Hz : quasi identique Formule : mel = 2595 × log10(1 + f/700) Les filtres mel sont espacés linéairement en mel, pas en Hz. 📐 Dimensions pour Whisper Pour 30 secondes d'audio : • Frames : 3000 (30s × 100 fps) • Mel bins : 80 (ou 128 pour large-v3) Matrice finale : (80, 3000) Notre exemple 3s : (80, 300) → 24 000 valeurs

💡 L'essentiel

Le Mel Spectrogram transforme l'audio en "image" temps-fréquence. L'axe X = temps (frames de 10ms), l'axe Y = fréquences (80 bandes mel). Chaque pixel = énergie dans cette bande à cet instant. C'est ce que "voit" l'encoder.

STFT

Short-Time Fourier Transform. FFT sur fenêtres glissantes (25ms, hop 10ms).

Frame

Une "colonne" du spectrogramme = 10ms d'audio analysé.

Mel bin

Une bande de fréquences. 80 bins = 80 "hauteurs" de son distinctes.

Hop length

Décalage entre fenêtres. 10ms = 100 frames/seconde.

Aller plus loin

Pourquoi pas spectrogramme linéaire ?

Un spectrogramme linéaire aurait autant de bins pour 0-1000 Hz que pour 7000-8000 Hz. Or l'information vocale est concentrée dans les basses fréquences. L'échelle mel compresse les hautes fréquences = représentation plus efficace.


Paramètres Whisper :

• n_fft : 400 (fenêtre de 25ms à 16kHz)
• hop_length : 160 (10ms)
• n_mels : 80 (ou 128 pour large-v3)
• fmin : 0 Hz, fmax : 8000 Hz

🔢 Feature Extraction — Préparer l'entrée

Normaliser et formater le spectrogramme pour le réseau de neurones.

📍 Fil rouge — Étape 3/12
Mel (80, 300) → Log-mel normalisé → Tensor (1, 80, 300)
Mel Spectrogram Valeurs : 0 → ~1000 (énergie brute) Log Transform log10(mel + 1e-10) Compresse la dynamique Normalisation (x - mean) / std Valeurs : -4 → +4 Tensor Input (batch, 80, 3000) Prêt pour encoder 📊 Pourquoi le log ? L'énergie audio varie sur plusieurs ordres de grandeur. Chuchotement : ~10 | Voix normale : ~100 | Cri : ~1000 Le log compresse : log(10)=1, log(100)=2, log(1000)=3 ⚙️ Spécificités Whisper • Normalisation globale (stats du training set) • Padding à 30s (3000 frames) si audio < 30s • Float32 ou Float16 selon compute_type

💡 L'essentiel

Le log compresse la dynamique audio (perception humaine = logarithmique). La normalisation centre les valeurs autour de 0 avec écart-type ~1, ce qui aide la convergence du réseau. Le tensor final est paddé à 30 secondes (3000 frames).

Log-mel

Spectrogramme mel passé au log. Standard en speech recognition.

Padding

Ajout de zéros pour atteindre 30s. L'attention mask ignore ces frames.

Batch dimension

Permet de traiter plusieurs audios en parallèle sur GPU.

dB (décibels)

Souvent on utilise 10×log10 pour avoir des dB. Même idée que log.

Aller plus loin

Code simplifié (faster-whisper) :

mel = librosa.feature.melspectrogram(y=audio, sr=16000, n_mels=80)
log_mel = np.log10(np.maximum(mel, 1e-10))
normalized = (log_mel - MEAN) / STD # stats globales Whisper
tensor = torch.tensor(normalized).unsqueeze(0) # (1, 80, T)

Alternative : MFCC

Les MFCC (Mel-Frequency Cepstral Coefficients) appliquent une DCT en plus. Utilisés historiquement, mais les réseaux modernes préfèrent les log-mel bruts.

🏗️ Encoder — Comprendre l'audio

Transformer le spectrogramme en représentations sémantiques exploitables par le decoder.

📍 Fil rouge — Étape 4/12
Tensor (1, 80, 300) → Encoder embeddings (1, 75, 1280) pour large-v3
Log-mel Input (batch, 80, 3000) Conv1D Stem 2 couches convolution Downsample 4× → 750 frames + Positional Encoding Sinusoidal (fixe) Transformer Encoder 32 couches (large-v3) Self-attention + FFN 🔍 Détail Conv Stem Conv1 : 80 → 1280 channels kernel=3, stride=1, GELU Conv2 : 1280 → 1280 channels kernel=3, stride=2, GELU Résultat : 3000 frames → 750 frames Compression temporelle 4× 📐 Dimensions Whisper Modèle Layers d_model tiny 4 384 base 6 512 medium 24 1024 large-v3 32 1280 turbo 32 1280 🧠 Ce que l'encoder apprend Couches basses (1-8) • Phonèmes, formants • Transitions acoustiques Couches moyennes (9-20) • Syllabes, mots • Prosodie, intonation Couches hautes (21-32) • Contexte sémantique • Détection de langue • Structure de phrase Output final Chaque frame (10ms × 4 = 40ms) devient un vecteur de 1280 dims encodant "ce qui est dit ici"

💡 L'essentiel

L'encoder Whisper est un Transformer qui traite le spectrogramme. Les convolutions initiales réduisent la résolution temporelle (3000→750 frames). Les 32 couches de self-attention permettent à chaque frame de "voir" tout l'audio. Output : une représentation dense par frame de 40ms.

Conv stem

Couches convolutionnelles initiales. Réduisent la séquence et projettent vers d_model.

Self-attention

Chaque position attend à toutes les autres. Capture les dépendances longues.

d_model

Dimension des embeddings internes. 1280 pour large = vecteurs de 1280 nombres.

Downsample

Réduction temporelle. 4× = 1 frame encoder représente 40ms d'audio.

Aller plus loin

Pourquoi des convolutions avant le Transformer ?

Le spectrogramme a 3000 frames pour 30s. L'attention a une complexité O(n²). Avec 3000 positions, ça fait 9 millions d'opérations par couche. Les convolutions réduisent à 750, soit 562 500 opérations. Gain de 16×.


Différence avec un encoder LLM :

• LLM encoder (BERT) : tokens discrets en entrée
• STT encoder : signal continu (spectrogramme)
• Les deux utilisent self-attention, mais le preprocessing diffère

🔮 Decoder — Générer le texte

Transformer les représentations audio en tokens texte, un par un.

📍 Fil rouge — Étape 5/12
Encoder output (1, 75, 1280) → Decoder → "Bonjour, comment ça va ?"
Encoder Output (1, 750, 1280) Cross-attention K, V Start Tokens <|startoftranscript|> <|fr|> <|transcribe|> Transformer Decoder 32 couches (large-v3) Masked Self-Attention Tokens ne voient que le passé Cross-Attention Q: decoder, K/V: encoder output 🔄 Génération auto-régressive Étape 1: [START, fr, transcribe] → prédire → "Bon" Étape 2: [START, fr, transcribe, Bon] → prédire → "jour" Étape 3: [..., Bon, jour] → prédire → "," Étape 4: [..., jour, ,] → prédire → "comment" ... Étape N: [..., va, ?] → prédire → <|endoftext|> 💾 KV Cache Les K/V des tokens passés sont cachés → pas recalculés à chaque étape

💡 L'essentiel

Le decoder génère le texte token par token (auto-régressif). À chaque étape : (1) self-attention sur les tokens déjà générés, (2) cross-attention vers l'encoder pour "regarder" l'audio, (3) prédiction du prochain token. Le KV cache évite de recalculer les tokens passés.

Auto-régressif

Chaque token dépend des précédents. Génération séquentielle, pas parallèle.

Cross-attention

Le decoder "interroge" l'encoder : Q vient du decoder, K/V de l'encoder.

Causal mask

Empêche de voir les tokens futurs. Position N ne voit que 1...N-1.

Special tokens

<|startoftranscript|>, <|fr|>, <|transcribe|>, <|endoftext|>

Aller plus loin

Pourquoi encoder-decoder et pas encoder-only ?

L'audio et le texte ont des longueurs différentes (750 frames → ~20 tokens). Le decoder permet une correspondance flexible. Un CTC (encoder-only) force un alignement monotone.


Beam search vs Greedy :

• Greedy : prend le token le plus probable à chaque étape
• Beam search : garde les N meilleures hypothèses, meilleure qualité mais plus lent
• faster-whisper utilise beam_size=5 par défaut


Température :

Whisper utilise temperature=0 par défaut (greedy). Si échec (trop de répétitions), fallback à temperature=[0.2, 0.4, 0.6, 0.8, 1.0]

⏱️ Timestamps — Positionner les mots

Associer chaque mot ou segment à son instant dans l'audio.

📍 Fil rouge — Étape 6/12
"Bonjour" → [0.00s - 0.52s] | "comment" → [0.60s - 1.10s] | ...
Timeline audio — 3 secondes 0.0s 1.0s 2.0s 3.0s Bonjour , comment ça va ? 📝 Segment-level timestamps Par défaut dans Whisper • Segments de ~30s max • Timestamp = tokens spéciaux <|0.00|> • Résolution : 20ms Exemple output : [0.00 - 2.50] "Bonjour, comment ça va ?" 🔤 Word-level timestamps Nécessite word_timestamps=True • Cross-attention alignment • DTW (Dynamic Time Warping) • Résolution : ~40ms Exemple output : "Bonjour" [0.00-0.52] "comment" [0.60-1.10] ...

💡 L'essentiel

Whisper génère des timestamps de 2 façons : (1) segment-level via des tokens spéciaux <|0.00|> intégrés au vocabulaire, (2) word-level via l'alignement des poids de cross-attention entre decoder et encoder. Le word-level est plus lent mais indispensable pour le sous-titrage précis.

Cross-attention weights

Indiquent où le decoder "regarde" dans l'audio pour chaque token généré.

DTW

Dynamic Time Warping. Aligne séquences de longueurs différentes.

Résolution 20ms

Whisper a 1500 timestamps possibles pour 30s (tokens <|0.00|> à <|30.00|>).

WhisperX

Outil qui améliore les word timestamps avec forced alignment (wav2vec2).

Aller plus loin

Comment faster-whisper calcule les word timestamps ?

1. Extrait les poids de cross-attention (decoder → encoder)
2. Pour chaque token texte, identifie les frames audio avec le plus de poids
3. Applique DTW pour lisser l'alignement
4. Convertit frame index → secondes


Problèmes courants :

• Mots très courts (articles) : timestamps imprécis
• Musique/bruit : alignement dérive
• Répétitions : Whisper peut "sauter" ou répéter des segments

🌍 Langues — Détection et multilingual

Comment Whisper identifie et transcrit 99+ langues.

📍 Fil rouge — Étape 7/12
Audio français → Détection auto : <|fr|> → Transcription en français
Audio Input (langue inconnue) Encoder 30 premières secondes Language Head Softmax sur 99 langues <|fr|> Token langue Tokens de langue Token Langue ID vocab <|en|> English 50259 <|fr|> French 50265 <|de|> German 50261 <|es|> Spanish 50262 ... 99 langues au total Tokens de tâche <|transcribe|> Transcrire dans la langue source <|translate|> Traduire vers anglais <|notimestamps|> Désactiver timestamps Prompt complet : <|startoftranscript|><|fr|><|transcribe|>

💡 L essentiel

Whisper détecte la langue en analysant les 30 premières secondes via l encoder, puis génère le token de langue approprié. Le modèle peut transcrire (même langue) ou traduire (vers anglais). La détection fonctionne bien pour les langues avec beaucoup de données d entraînement.

Multilingual

Les modèles .en sont anglais-only. Les autres supportent 99 langues.

Code-switching

Changement de langue mid-phrase. Whisper gère mal, détecte une langue dominante.

Translate

Mode speech-to-English. Transcrit ET traduit vers anglais en un pass.

language=None

Détection automatique. Spécifier la langue évite les erreurs de détection.

Aller plus loin

Qualité par langue (WER approximatif) :

• Anglais : ~3-5% (excellent)
• Français, Allemand, Espagnol : ~5-8% (très bon)
• Langues asiatiques : ~10-15% (correct)
• Langues rares : ~20%+ (variable)


Forcer la langue :

Si tu sais que l audio est en français, utilise language="fr" pour éviter les erreurs de détection et accélérer légèrement le traitement.

📏 Chunking — Découper l audio

Pourquoi et comment diviser les longs audios en segments de 30 secondes.

📍 Fil rouge — Étape 8/12
Notre audio de 3s tient dans un seul chunk. Un appel de 5 minutes → 10 chunks de 30s
Audio original — 5 minutes (300 secondes) Découpage 10 chunks de 30 secondes 0-30s 30-60s 60-90s 90-120s ... 270-300s Pourquoi 30 secondes ? Whisper entraine sur segments de 30s. Au-dela = hallucinations Traitement parallele Batching GPU : plusieurs chunks en simultane

💡 L essentiel

Whisper a ete entraine sur des segments de 30 secondes max. Au-dela, le positional encoding ne generalise pas et le modele hallucine ou repete. Pour les longs audios : decouper en chunks, traiter, concatener. Le chevauchement (overlap) evite de couper les mots.

Chunk

Segment de 30s. Spectrogramme de (80, 3000) frames.

VAD

Voice Activity Detection. Detecte les silences pour couper intelligemment.

Overlap

Chevauchement entre chunks pour eviter de couper mid-word.

Seek

Position actuelle dans l audio. Avance apres chaque segment transcrit.

Aller plus loin

Strategie faster-whisper :

1. Transcrit 30s, detecte le dernier timestamp valide
2. Avance (seek) jusqu a ce timestamp
3. Recommence avec un nouveau chunk
4. VAD optionnel pour skipper les silences


Probleme des repetitions :

Si Whisper genere "merci merci merci merci...", c est souvent un chunk mal decoupe ou du silence. Solutions : VAD, condition_on_previous_text=False, compression_ratio_threshold.

⚡ Metriques — Mesurer la performance

WER pour la qualite, RTFx pour la vitesse.

📍 Fil rouge — Étape 9/12
"Bonjour, comment ca va ?" transcrit en 5ms → RTFx = 600 (3s / 0.005s)
WER — Word Error Rate Formule : WER = (S + D + I) / N × 100% S = Substitutions (mots remplaces) D = Deletions (mots manquants) I = Insertions (mots en trop) N = Nombre total de mots reference Exemple : Ref: "Bonjour comment ca va" Hyp: "Bonjour comme ca va bien" → 1 sub (comment→comme) + 1 ins (bien) WER = (1+0+1) / 4 = 50% Bon WER : <5% excellent, <10% acceptable RTFx — Real-Time Factor Formule : RTFx = duree_audio / temps_traitement RTFx = 1 → temps reel RTFx = 100 → 100x plus rapide RTFx = 0.5 → 2x plus lent que temps reel Benchmarks typiques : CPU (i7) + large-v3 RTFx ~5-10 RTX 3090 + large-v3 RTFx ~50-100 H100 + large-v3 (batch) RTFx ~600 H100 + Canary-1B-Flash RTFx ~1000+ Ton objectif H100 : RTFx 600 ✓

💡 L essentiel

WER mesure la qualite (% d erreurs par rapport a une reference humaine). RTFx mesure la vitesse (combien de fois plus rapide que le temps reel). Sur H100 avec 60 conversations de 5 min traitees en batch, tu atteins RTFx ~600 = excellent.

CER

Character Error Rate. Comme WER mais au niveau caractere. Utile pour langues sans espaces.

Latence

Temps entre fin de parole et debut de transcription. Critique en streaming.

Throughput

Heures d audio traitees par heure. RTFx × utilisation GPU.

Time to First Token

Temps avant le premier mot. Encoder (prefill) domine.

Aller plus loin

WER vs WER normalise :

Le WER brut est sensible a la ponctuation et la casse. Le WER normalise (lowercase, sans ponctuation) est plus juste pour comparer des systemes.


Calculer le RTFx :

Si tu traites 300 minutes d audio en 30 secondes :
RTFx = (300 × 60) / 30 = 600
= 600 secondes d audio par seconde de calcul

🔧 Optimisations — Aller plus vite

CTranslate2, quantization, batching et autres techniques.

📍 Fil rouge — Étape 10/12
Whisper OpenAI → faster-whisper (CTranslate2) → int8 → batch de 60 → RTFx 600
CTranslate2 Backend de faster-whisper • C++ optimise (pas Python) • Kernels CUDA customs • Support int8/float16 • Batched inference Gain : ~4x vs Whisper HF Quantization Reduire la precision des poids float32 → float16 : ~2x faster float16 → int8 : ~1.5x faster compute_type options : float32, float16, int8_float16, int8 int8 : +30-50% speed, ~0.5% WER Batching Traiter plusieurs audios ensemble • Sature le GPU • Amortit le cout encoder • Ton cas : batch_size=60 BatchedInferencePipeline : faster-whisper ≥1.0 Gain : throughput lineaire VAD (Silero) Detecter les zones de parole • Skip les silences • Decoupe intelligente • Evite les hallucinations vad_filter=True Gain : variable (selon silences) Modeles optimises Alternatives a large-v3 : large-v3-turbo : 2x faster distil-large-v3 : 2x faster Canary-1B-Flash : 3x faster Parakeet TDT (EN only) : 5x+ Trade-off vitesse/qualite Autres techniques • TensorRT-LLM : kernels NVIDIA • Flash Attention : O(n) memoire • Speculative decoding • KV cache int8 • Multi-GPU (tensor parallel) Gains cumulables

💡 L essentiel

faster-whisper (CTranslate2) est ~4x plus rapide que Whisper HF. int8 ajoute ~30-50% sans perte notable. Batching sature le GPU pour maximiser le throughput. Combine les trois = RTFx 600 sur H100.

CTranslate2

Moteur d inference C++ optimise pour les Transformers. Backend de faster-whisper.

compute_type

Precision des calculs. int8_float16 = bon compromis speed/quality.

Silero VAD

Modele leger de Voice Activity Detection. Detecte parole vs silence.

TensorRT

SDK NVIDIA pour optimiser les modeles. Compile des kernels specifiques au GPU.

Aller plus loin

Config optimale faster-whisper sur H100 :

model = WhisperModel("large-v3", device="cuda", compute_type="int8_float16")
batched = BatchedInferencePipeline(model, batch_size=32)


Pourquoi pas vLLM pour Whisper ?

vLLM supporte Whisper depuis peu mais moins mature que faster-whisper. Avantage : continuous batching natif. A tester si tu scales davantage.

🆚 Modeles — Whisper vs Alternatives

Comparatif des principaux modeles STT en 2024-2025.

📍 Fil rouge — Étape 11/12
Ton choix : Whisper large-v3 via faster-whisper (FR requis, word timestamps, ponctuation)
Comparatif des modeles STT Modele Params WER (EN) RTFx Langues Streaming Timestamps Note Whisper large-v3 1.55B ~4% ~50-600 99 Non Word Reference Whisper large-v3-turbo 809M ~5% ~100-1200 99 Non Word 2x faster Canary-1B-Flash 1B ~1.5% ~1000+ 4 Oui Word NVIDIA NeMo Parakeet TDT 0.6B 600M ~2% ~3400 EN only Oui Word Ultrarapide distil-whisper-large-v3 756M ~5% ~100-600 99 Non Segment Distillation Moonshine ~200M ~8% ~50 (CPU) EN only Oui Word Edge/CPU Ton cas d usage ✓ Francais requis ✓ Word timestamps ✓ Ponctuation / capitalisation → Whisper large-v3 optimal Si tu veux accelerer • large-v3-turbo : ~2x faster, FR OK • Canary-1B-v2 : FR/EN/DE/ES, streaming • Si EN only : Parakeet TDT (5x+) → Test large-v3-turbo en premier

💡 L essentiel

Pour le francais + word timestamps + ponctuation, Whisper large-v3 reste la reference. Turbo est 2x plus rapide avec qualite quasi identique. Canary (NVIDIA) excelle en streaming. Parakeet ecrase tout en anglais only.

NeMo

Framework NVIDIA pour speech AI. Canary et Parakeet en font partie.

Distillation

Entrainer un petit modele a imiter un gros. distil-whisper = eleve de large-v3.

TDT

Token-and-Duration Transducer. Architecture NVIDIA pour timestamps precis.

CTC

Connectionist Temporal Classification. Alternative au decoder, plus simple mais moins flexible.

Aller plus loin

Migration vers Canary :

Si tu veux passer de Whisper a Canary :
• Installer NeMo toolkit
• Convertir pipeline audio (meme format 16kHz mono)
• API differente mais concepts similaires


Whisper-large-v3-turbo-ct2 :

Version CTranslate2 du turbo, directement utilisable avec faster-whisper :
model = WhisperModel("deepdml/faster-whisper-large-v3-turbo-ct2")