# Parametres Papermill - JAMAIS modifier ce commentaire
# Configuration notebook
notebook_mode = "interactive" # "interactive" ou "batch"
skip_widgets = False # True pour mode batch MCP
debug_level = "INFO"
# Parametres Whisper local
model_size = "large-v3-turbo" # "tiny", "base", "small", "medium", "large-v3", "large-v3-turbo"
device = "cuda" # "cuda" ou "cpu"
compute_type = "float16" # "float16", "int8", "int8_float16"
# Configuration
generate_test_audio = True # Generer des echantillons via TTS
compare_model_sizes = True # Comparer differentes tailles de modeles
compare_with_api = True # Comparer avec l'API OpenAI
batch_transcribe = True # Tester la transcription batch
monitor_vram = True # Surveiller l'utilisation VRAM
save_results = True # Sauvegarder les resultatsWhisper Local - Transcription GPU avec faster-whisper
Module : 01-Audio-Foundation
Niveau : Intermediaire
Technologies : faster-whisper (CTranslate2), transformers
Duree estimee : 45 minutes
VRAM : ~10 GB
Objectifs d’Apprentissage
Prerequis
- GPU NVIDIA avec au moins 4 GB VRAM (10 GB recommande pour large-v3)
- CUDA Toolkit installe
pip install faster-whisper- Echantillons audio (generes dans les notebooks précédents ou fournis)
Navigation : Index | << Précédent | Suivant >>
# Parameters
notebook_mode = "batch"
skip_widgets = TrueLes paramètres Papermill etant définis, nous importons les bibliotheques necessaires, notamment faster-whisper pour la transcription GPU locale et les utilitaires de gestion des fichiers audio.
# Setup environnement et imports
import os
import sys
import json
import time
import gc
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Any, Optional
import logging
import numpy as np
from IPython.display import Audio, display
# Import helpers GenAI
GENAI_ROOT = Path.cwd()
while GENAI_ROOT.name != 'GenAI' and len(GENAI_ROOT.parts) > 1:
GENAI_ROOT = GENAI_ROOT.parent
HELPERS_PATH = GENAI_ROOT / 'shared' / 'helpers'
if HELPERS_PATH.exists():
sys.path.insert(0, str(HELPERS_PATH.parent))
try:
from helpers.audio_helpers import transcribe_local, load_audio, play_audio_file
print("Helpers audio importes")
except ImportError:
print("Helpers audio non disponibles - mode autonome")
# Repertoires
OUTPUT_DIR = GENAI_ROOT / 'outputs' / 'audio' / 'whisper_local'
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
SAMPLES_DIR = GENAI_ROOT / 'outputs' / 'audio' / 'samples'
SAMPLES_DIR.mkdir(parents=True, exist_ok=True)
# Configuration logging
logging.basicConfig(level=getattr(logging, debug_level))
logger = logging.getLogger('whisper_local')
# Verification GPU
try:
import torch
gpu_available = torch.cuda.is_available()
if gpu_available:
gpu_name = torch.cuda.get_device_name(0)
gpu_vram = torch.cuda.get_device_properties(0).total_memory / (1024**3)
print(f"GPU : {gpu_name} ({gpu_vram:.1f} GB VRAM)")
else:
print("GPU non disponible - utilisation CPU")
device = "cpu"
compute_type = "int8"
except ImportError:
print("torch non disponible - verification GPU ignoree")
gpu_available = False
print(f"\nWhisper Local - Transcription GPU")
print(f"Date : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Modele : {model_size}, Device : {device}, Compute : {compute_type}")
print(f"Sortie : GenAI/{OUTPUT_DIR.relative_to(GENAI_ROOT)}")GPU : NVIDIA GeForce RTX 3090 (24.0 GB VRAM)
Whisper Local - Transcription GPU
Date : 2026-08-18 00:43:25
Modele : large-v3-turbo, Device : cuda, Compute : float16
Sortie : GenAI/outputs\audio\whisper_local
L’environnement Python etant configure, nous chargeons les variables d’environnement pour permettre l’acces a l’API OpenAI lors des comparaisons avec faster-whisper.
# Chargement robuste de la configuration .env (Pattern A - Papermill-safe)
from dotenv import load_dotenv
import os
from pathlib import Path
current_path = Path.cwd()
genai_path = None
# Remonter jusqu'au dossier GenAI
while len(current_path.parts) > 1:
if current_path.name == "GenAI":
genai_path = current_path
break
current_path = current_path.parent
if genai_path:
env_path = genai_path / ".env"
if env_path.exists():
load_dotenv(env_path)
print(f".env charge depuis: {env_path.parent.name}/{env_path.name}")
else:
print(f"WARNING: .env non trouve a {env_path.parent.name}/{env_path.name}")
else:
print("WARNING: Dossier GenAI non trouve, utilisation variables environnement")WARNING: Dossier GenAI non trouve, utilisation variables environnement
La configuration .env est chargee : les cles API et les chemins de repertoires sont disponibles. La cellule suivante prepare les echantillons audio de test en plusieurs langues (francais, anglais) qui serviront a evaluer la precision de transcription du modèle Whisper.
# Generation ou chargement des echantillons audio de test
print("PREPARATION DES ECHANTILLONS AUDIO")
print("=" * 45)
test_files = {}
# Verifier les fichiers existants
existing_samples = {
'en': SAMPLES_DIR / "sample_en.mp3",
'fr': SAMPLES_DIR / "sample_fr.mp3",
'multi': SAMPLES_DIR / "sample_multi.mp3"
}
for lang_key, filepath in existing_samples.items():
if filepath.exists():
test_files[lang_key] = filepath
print(f" {lang_key}: {filepath.name} (existant)")
# Generer les fichiers manquants via TTS si OPENAI_API_KEY disponible
if generate_test_audio and len(test_files) < 3:
openai_key = os.environ.get('OPENAI_API_KEY')
if openai_key:
from openai import OpenAI
client = OpenAI(api_key=openai_key)
test_texts = {
'en': "Speech recognition converts spoken language into written text. Modern systems use deep neural networks trained on thousands of hours of audio data to achieve human level accuracy.",
'fr': "La reconnaissance vocale permet de convertir la parole en texte. Cette technologie est utilisee dans les assistants virtuels et la transcription automatique.",
'multi': "Bonjour, je parle français. Now I switch to English. Et je reviens au français pour finir."
}
for lang_key, text in test_texts.items():
if lang_key not in test_files:
output_path = SAMPLES_DIR / f"sample_{lang_key}.mp3"
print(f" Generation {lang_key} via TTS...")
try:
response = client.audio.speech.create(
model="tts-1",
voice="alloy" if lang_key != 'fr' else "nova",
input=text
)
response.stream_to_file(str(output_path))
test_files[lang_key] = output_path
print(f" -> {output_path.name}")
except Exception as e:
print(f" Erreur TTS: {e}")
else:
print(" OPENAI_API_KEY non disponible - generation TTS impossible")
# Generer signal synthetique si aucun fichier disponible
if not test_files:
import soundfile as sf
print(" Aucun fichier audio disponible - generation synthetique...")
# Generer un fichier synthetique simple
sample_rate = 16000
duration = 5
t = np.linspace(0, duration, int(sample_rate * duration), False)
audio_synth = 0.5 * np.sin(2 * np.pi * 440 * t)
synth_path = SAMPLES_DIR / "sample_synth.wav"
sf.write(str(synth_path), audio_synth, sample_rate)
test_files['synth'] = synth_path
print(f" -> sample_synth.wav (signal 440 Hz)")
print(f"\nEchantillons disponibles: {list(test_files.keys())}")PREPARATION DES ECHANTILLONS AUDIO
=============================================
en: sample_en.mp3 (existant)
fr: sample_fr.mp3 (existant)
multi: sample_multi.mp3 (existant)
Echantillons disponibles: ['en', 'fr', 'multi']
Interpretation : Preparation des echantillons
| Scénario | Résultat | Solution |
|---|---|---|
| API TTS disponible (cle valide) | Generation d’echantillons en francais, anglais, multi-langue | Utilisation de OpenAI TTS |
| API TTS indisponible (cle invalide) | Generation d’un signal synthetique 440 Hz | Fallback pour demonstration |
Points cles : 1. Les echantillons sont stockes dans outputs/audio/samples/ pour reutilisation 2. Le signal synthetique (440 Hz) est un ton pur - Whisper ne le transcrira pas correctement 3. Pour des tests significatifs, utiliser des vrais fichiers audio avec de la parole 4. La detection de langue fonctionne mieux avec des phrases completes qu’avec des signaux synthetiques
Note technique : Dans un environnement de production, preferez des enregistrements reels ou des fichiers audio de reference pour evaluer la qualite de transcription. Les signaux synthetiques ne testent que la pipeline, pas la precision du modèle.
Section 1 : Modèles Whisper et compromis
faster-whisper utilise CTranslate2 pour une inference optimisee des modèles Whisper.
Tailles de modèles
| Modèle | Paramètres | VRAM (float16) | Vitesse relative | Qualite |
|---|---|---|---|---|
tiny |
39 M | ~1 GB | 32x | Basique |
base |
74 M | ~1 GB | 16x | Correcte |
small |
244 M | ~2 GB | 6x | Bonne |
medium |
769 M | ~5 GB | 2x | Très bonne |
large-v3 |
1.55 B | ~10 GB | 1x (reference) | Excellente |
large-v3-turbo |
809 M | ~6 GB | 3x | Excellente |
Types de calcul
| Type | Precision | VRAM | Vitesse |
|---|---|---|---|
float16 |
Haute | Standard | Standard |
int8_float16 |
Bonne | Reduite ~50% | Plus rapide |
int8 |
Acceptable | Minimale | Plus rapide |
# Chargement du modele Whisper
print("CHARGEMENT DU MODELE WHISPER")
print("=" * 45)
from faster_whisper import WhisperModel
# Mesure VRAM avant chargement
vram_before = 0
if monitor_vram and gpu_available:
vram_before = torch.cuda.memory_allocated(0) / (1024**3)
print(f"VRAM avant chargement : {vram_before:.2f} GB")
print(f"\nChargement du modele '{model_size}' sur '{device}'...")
print(f"Type de calcul : {compute_type}")
start_time = time.time()
model = WhisperModel(model_size, device=device, compute_type=compute_type)
load_time = time.time() - start_time
print(f"Modele charge en {load_time:.1f}s")
# Mesure VRAM apres chargement
if monitor_vram and gpu_available:
vram_after = torch.cuda.memory_allocated(0) / (1024**3)
vram_used = vram_after - vram_before
print(f"VRAM apres chargement : {vram_after:.2f} GB")
print(f"VRAM utilisee par le modele : {vram_used:.2f} GB")CHARGEMENT DU MODELE WHISPER
=============================================
VRAM avant chargement : 0.00 GB
Chargement du modele 'large-v3-turbo' sur 'cuda'...
Type de calcul : float16
Modele charge en 3.0s
VRAM apres chargement : 0.00 GB
VRAM utilisee par le modele : 0.00 GB
Interpretation : Chargement du modèle
| Aspect | Valeur observee | Signification |
|---|---|---|
| Temps de chargement | runtime machine-dep : 5.6s (CPU) | Temps necessaire pour charger les poids depuis HuggingFace |
| VRAM utilisee | ~6 GB (GPU) | Pour large-v3-turbo en float16 |
Points cles : 1. Le premier telechargement depuis HuggingFace peut prendre plusieurs secondes 2. Les modèles sont mis en cache localement (~3 GB pour large-v3-turbo) 3. Sur CPU, le chargement est plus rapide mais la transcription est plus lente 4. La VRAM est allouee une seule fois au chargement - pas de re-allocation pendant la transcription
Note technique : faster-whisper utilise CTranslate2 qui optimise le modèle pour l’inference (quantization, fusion d’opérations). C’est ce qui le rend beaucoup plus rapide que l’implementation OpenAI d’origine.
Section 2 : Transcription detaillee
faster-whisper retourne deux objets : - segments : générateur de segments avec timestamps, texte et confiance - info : metadonnees (langue detectee, probabilite, duree)
# Transcription detaillee
print("TRANSCRIPTION DETAILLEE")
print("=" * 45)
if test_files:
# Transcription du premier echantillon
lang_key = list(test_files.keys())[0]
sample_path = test_files[lang_key]
print(f"Fichier : {sample_path.name}")
# Ecouter l'echantillon source avant de verifier la transcription
display(Audio(str(sample_path)))
print(f"\nTranscription en cours...")
start_time = time.time()
segments, info = model.transcribe(
str(sample_path),
beam_size=5,
word_timestamps=True
)
# Collecter les segments (le generateur est consomme une seule fois)
segments_list = list(segments)
transcribe_time = time.time() - start_time
# Informations de detection
print(f"\n--- Metadonnees ---")
print(f" Langue detectee : {info.language} (probabilite : {info.language_probability:.2%})")
print(f" Duree audio : {info.duration:.1f}s")
print(f" Temps de transcription : {transcribe_time:.2f}s")
print(f" Ratio temps reel : {info.duration / transcribe_time:.1f}x")
# Texte complet
full_text = " ".join([s.text.strip() for s in segments_list])
print(f"\n--- Texte transcrit ---")
print(f" {full_text}")
# Segments detailles
print(f"\n--- Segments ({len(segments_list)}) ---")
for seg in segments_list:
print(f" [{seg.start:.2f}s - {seg.end:.2f}s] (conf: {seg.avg_logprob:.3f}) {seg.text.strip()}")
# Mots avec timestamps
all_words = []
for seg in segments_list:
if seg.words:
all_words.extend(seg.words)
if all_words:
print(f"\n--- Mots avec timestamps ({len(all_words)}) ---")
for w in all_words[:10]:
print(f" [{w.start:.2f}s - {w.end:.2f}s] (p={w.probability:.2f}) {w.word}")
if len(all_words) > 10:
print(f" ... ({len(all_words) - 10} mots supplementaires)")
# Sauvegarde
if save_results:
result = {
"model": model_size,
"device": device,
"compute_type": compute_type,
"language": info.language,
"language_probability": info.language_probability,
"duration": info.duration,
"transcription_time": transcribe_time,
"text": full_text,
"segments": [
{"start": s.start, "end": s.end, "text": s.text.strip(),
"avg_logprob": s.avg_logprob}
for s in segments_list
]
}
result_file = OUTPUT_DIR / f"transcription_{lang_key}_{model_size}.json"
with open(result_file, 'w', encoding='utf-8') as f:
json.dump(result, f, indent=2, ensure_ascii=False)
print(f"\nResultat sauvegarde : {result_file.name}")
else:
print("Aucun echantillon audio disponible")TRANSCRIPTION DETAILLEE
=============================================
Fichier : sample_en.mp3
Transcription en cours...
--- Metadonnees ---
Langue detectee : en (probabilite : 99.95%)
Duree audio : 11.4s
Temps de transcription : 1.02s
Ratio temps reel : 11.1x
--- Texte transcrit ---
Speech recognition converts spoken language into written text. Modern systems use deep neural networks trained on thousands of hours of audio data to achieve human-level accuracy.
--- Segments (3) ---
[0.00s - 3.30s] (conf: -0.116) Speech recognition converts spoken language into written text.
[3.68s - 8.68s] (conf: -0.116) Modern systems use deep neural networks trained on thousands of hours of audio data
[8.68s - 10.80s] (conf: -0.116) to achieve human-level accuracy.
--- Mots avec timestamps (27) ---
[0.00s - 0.34s] (p=0.88) Speech
[0.34s - 0.82s] (p=0.97) recognition
[0.82s - 1.26s] (p=0.99) converts
[1.26s - 1.72s] (p=1.00) spoken
[1.72s - 2.16s] (p=1.00) language
[2.16s - 2.50s] (p=1.00) into
[2.50s - 2.78s] (p=1.00) written
[2.78s - 3.30s] (p=1.00) text.
[3.68s - 4.40s] (p=1.00) Modern
[4.40s - 4.88s] (p=1.00) systems
... (17 mots supplementaires)
Resultat sauvegarde : transcription_en_large-v3-turbo.json
Interpretation : Transcription locale
| Aspect | Valeur | Signification |
|---|---|---|
| Ratio temps reel | >1x = plus rapide que le temps reel | Plus le ratio est eleve, meilleure est la performance |
| avg_logprob | Proche de 0 = haute confiance | Valeurs < -1.0 indiquent une incertitude |
| language_probability | Proche de 1.0 | Detection de langue fiable |
Points cles : 1. word_timestamps=True active l’alignement mot par mot 2. beam_size=5 ameliore la qualite au prix d’une latence accrue 3. Le générateur segments est consomme une seule fois - le convertir en liste pour reutilisation
Exercice 1 : Filtrage de segments par confiance
Contexte : Lors d’une transcription automatique, certains segments ont une faible confiance (avg_logprob très negatif), souvent dus a du bruit, des silences ou des artefacts audio. Vous souhaitez filtrer ces segments pour ne garder que les transcriptions fiables.
Objectif : Créer une fonction filter_segments_by_confidence() qui prend une liste de segments detailles et retourne uniquement ceux dont la confiance depasse un seuil, avec des statistiques sur les segments filtres.
Étapes : 1. Ecrire la fonction avec les paramètres : segments_list (liste de segments), threshold (seuil logprob, par defaut -0.5) 2. Filtrer les segments dont avg_logprob >= threshold 3. Calculer les statistiques : nombre total, nombre filtre, pourcentage retenu, texte moyen par segment 4. Afficher un resume formatte des segments valides et rejetes
Indices : - seg.avg_logprob est le score de confiance d’un segment (proche de 0 = haute confiance) - Un seuil typique est entre -0.3 (strict) et -1.0 (permissif) - Les segments courts (< 1 mot) ont souvent un score faible - Utilisez les segments segments_list generes dans la Section 2 comme données de test
def filter_segments_by_confidence(segments_list, threshold=-0.5):
"""
Filtre les segments de transcription par confiance.
Parametres :
segments_list : liste de segments (objets faster-whisper)
threshold : seuil avg_logprob minimum (defaut: -0.5)
Retourne :
dict : segments_valides, segments_rejetes, statistiques
"""
# TODO etudiant : implementer le filtrage
# Etape 1 : Separer les segments valides et rejetes
valid_segments = [] # segments avec avg_logprob >= threshold
rejected_segments = [] # segments avec avg_logprob < threshold
# Indice : iterer sur segments_list et verifier seg.avg_logprob
# Etape 2 : Calculer les statistiques
stats = {
"total": len(segments_list),
"valid": len(valid_segments),
"rejected": len(rejected_segments),
"retention_pct": None # pourcentage de segments valides
}
# Etape 3 : Afficher le resume
# Indice : utiliser print() avec formatage pour chaque segment valide
return {
"valid_segments": valid_segments,
"rejected_segments": rejected_segments,
"stats": stats
}
# Test avec les segments de la Section 2 (si disponibles)
# Indice : remplacer [] par segments_list de la cellule cell-transcription
test_segments = []
if test_segments:
result = filter_segments_by_confidence(test_segments, threshold=-0.5)
print(f"Segments valides : {result['stats']['valid']}/{result['stats']['total']}")
else:
print("Exercice a completer - utilisez les segments de la Section 2")Exercice a completer - utilisez les segments de la Section 2
Section 3 : Transcription batch
Pour traiter plusieurs fichiers audio, on itere sur les fichiers tout en conservant le modèle en memoire. Le cout marginal d’une transcription supplementaire est minime une fois le modèle charge.
# Transcription batch
print("TRANSCRIPTION BATCH")
print("=" * 45)
if batch_transcribe and len(test_files) > 0:
batch_results = {}
for lang_key, filepath in test_files.items():
print(f"\nTranscription '{lang_key}' : {filepath.name}")
start_time = time.time()
segments, info = model.transcribe(str(filepath))
segments_list = list(segments)
elapsed = time.time() - start_time
text = " ".join([s.text.strip() for s in segments_list])
batch_results[lang_key] = {
"text": text,
"language": info.language,
"probability": info.language_probability,
"duration": info.duration,
"time": elapsed
}
print(f" Langue : {info.language} ({info.language_probability:.0%})")
print(f" Texte : {text[:80]}...")
print(f" Temps : {elapsed:.2f}s (ratio : {info.duration/elapsed:.1f}x)")
# Tableau recapitulatif
print(f"\nRecapitulatif batch :")
print(f"{'Fichier':<12} {'Langue':<8} {'Proba':<8} {'Duree':<8} {'Temps':<8} {'Ratio':<8}")
print("-" * 52)
for lang_key, data in batch_results.items():
ratio = data['duration'] / data['time'] if data['time'] > 0 else 0
print(f"{lang_key:<12} {data['language']:<8} {data['probability']:<8.0%} "
f"{data['duration']:<8.1f} {data['time']:<8.2f} {ratio:<8.1f}")
total_duration = sum(d['duration'] for d in batch_results.values())
total_time = sum(d['time'] for d in batch_results.values())
print(f"\nTotal : {total_duration:.1f}s d'audio transcrits en {total_time:.1f}s")
else:
print("Transcription batch desactivee ou pas d'echantillons")TRANSCRIPTION BATCH
=============================================
Transcription 'en' : sample_en.mp3
Langue : en (100%)
Texte : Speech recognition converts spoken language into written text. Modern systems us...
Temps : 0.50s (ratio : 22.8x)
Transcription 'fr' : sample_fr.mp3
Langue : fr (100%)
Texte : La reconnaissance vocale permet de convertir la parole en texte. Cette technolog...
Temps : 0.55s (ratio : 21.5x)
Transcription 'multi' : sample_multi.mp3
Langue : fr (78%)
Texte : Bonjour, je parle français, now I switch to English, et je reviens au franquet p...
Temps : 0.45s (ratio : 12.3x)
Recapitulatif batch :
Fichier Langue Proba Duree Temps Ratio
----------------------------------------------------
en en 100% 11.4 0.50 22.8
fr fr 100% 11.8 0.55 21.5
multi fr 78% 5.5 0.45 12.3
Total : 28.7s d'audio transcrits en 1.5s
Interpretation : Transcription batch
| Aspect | Valeur observee | Signification |
|---|---|---|
| Ratio temps reel | runtime machine-dep : 0.2x (CPU) | Transcription plus lente que le temps reel sur CPU, mais >1x sur GPU |
| Segments par fichier | Variable | Depend de la complexite audio et des pauses |
| Probabilite de langue | >80% | Detection fiable de la langue |
Points cles : 1. Le modèle reste en memoire entre les fichiers - cout marginal minimal 2. La detection de langue est automatique (pas besoin de la specifier) 3. Sur CPU, le ratio temps reel est inferieur a 1 (plus lent que le temps reel) 4. Sur GPU avec large-v3-turbo, on atteint typiquement runtime machine-dep : 3-5x le temps reel
Note technique : Pour des traitements batch intensifs, prefer
large-v3-turboalarge-v3- qualite equivalente avec une vitesse 3x superieure.
Section 4 : Comparaison local vs API
Analyse comparative entre la transcription locale (faster-whisper) et l’API OpenAI Whisper.
| Critere | Local (faster-whisper) | API (OpenAI) |
|---|---|---|
| Cout | Gratuit (hardware) | $0.006/minute |
| Latence | runtime machine-dep : Depend du GPU | runtime machine-dep : ~1-3s par requête |
| Confidentialite | Données locales | Envoi a OpenAI |
| Disponibilite | Pas de connexion requise | Internet requis |
| Maintenance | Mises a jour manuelles | Geree par OpenAI |
Transition : Local vs API
Après avoir experimente la transcription locale et batch, nous allons maintenant comparer faster-whisper avec l’API OpenAI Whisper. Cette comparaison vous aidera a choisir la solution adaptee a votre cas d’usage : production locale (cout, confidentialite) ou API cloud (commodite, scalabilite).
Critères de decision : - Volume : Moins de 100h/mois → API, plus → local - Confidentialite : Données sensibles → local obligatoire - Infrastructure : GPU disponible → local, sinon → API - Maintenance : Preferer ne pas gerer les mises a jour → API
# Comparaison local vs API
print("COMPARAISON LOCAL VS API")
print("=" * 45)
if compare_with_api and len(test_files) > 0:
openai_key = os.getenv('OPENAI_API_KEY')
if openai_key:
from openai import OpenAI
client_api = OpenAI(api_key=openai_key)
lang_key = list(test_files.keys())[0]
filepath = test_files[lang_key]
# --- Transcription locale ---
print(f"\n--- Local ({model_size}) ---")
start_local = time.time()
segments, info = model.transcribe(str(filepath))
text_local = " ".join([s.text.strip() for s in segments])
time_local = time.time() - start_local
print(f" Texte : {text_local[:80]}...")
print(f" Temps : {time_local:.2f}s")
# --- Transcription API ---
print(f"\n--- API (whisper-1) ---")
try:
start_api = time.time()
with open(filepath, 'rb') as audio_file:
transcript = client_api.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
response_format="json"
)
text_api = transcript.text
time_api = time.time() - start_api
print(f" Texte : {text_api[:80]}...")
print(f" Temps : {time_api:.2f}s")
# --- Analyse ---
print(f"\n--- Analyse comparative ---")
print(f"{'Critere':<25} {'Local':<20} {'API':<20}")
print("-" * 65)
print(f"{'Temps':<25} {time_local:<20.2f} {time_api:<20.2f}")
print(f"{'Longueur texte':<25} {len(text_local):<20} {len(text_api):<20}")
cost_api = info.duration / 60 * 0.006
print(f"{'Cout':<25} {'$0.00 (local)':<20} {f'${cost_api:.4f}':<20}")
# Estimation cout pour 1 heure
cost_1h = 60 * 0.006
print(f"\nCout pour 1 heure d'audio :")
print(f" Local : $0.00 (cout electricite uniquement)")
print(f" API : ${cost_1h:.2f}")
except Exception as e:
print(f" API indisponible: {type(e).__name__}: {str(e)[:100]}")
print(" Comparaison partielle (local uniquement)")
else:
print("OPENAI_API_KEY non disponible pour la comparaison")
else:
print("Comparaison desactivee ou pas d'echantillons")COMPARAISON LOCAL VS API
=============================================
OPENAI_API_KEY non disponible pour la comparaison
Exercice 2 : Estimateur de couts STT pour un projet
Contexte : Vous devez conseiller un client sur le choix entre transcription locale (faster-whisper) et API (OpenAI Whisper) pour son projet. Le client traite des volumes variables d’audio chaque mois et souhaite un estimateur de couts.
Objectif : Créer une fonction estimate_stt_costs() qui, etant donne un volume horaire mensuel et une configuration hardware, retourne un tableau comparatif des couts et performances pour chaque solution.
Étapes : 1. Définir la fonction avec les paramètres : hours_per_month, gpu_available, gpu_vram_gb, model_size 2. Calculer le cout API : $0.006/minute (OpenAI Whisper) 3. Estimer le temps de traitement local en fonction du modèle et du GPU (utiliser les ratios du tableau Section 1) 4. Calculer le seuil de rentabilite : a partir de combien d’heures le local devient-il plus economique ? 5. Retourner un dictionnaire structure avec la recommandation
Indices : - Le cout API = hours * 60 * 0.006 dollars par mois - Le ratio temps reel depend du modèle : tiny=32x, base=16x, small=6x, medium=2x, large-v3=1x, large-v3-turbo=3x - Sur CPU, diviser le ratio par ~5 - Le seuil de rentabilite GPU (~$1000) se compare au cout cumule API sur la duree du projet
def estimate_stt_costs(hours_per_month, gpu_available=True, gpu_vram_gb=10, model_size="large-v3-turbo"):
"""
Estime les couts et performances pour la transcription STT.
Parametres :
hours_per_month (float) : volume d'audio mensuel en heures
gpu_available (bool) : presence d'un GPU
gpu_vram_gb (float) : VRAM disponible en GB
model_size (str) : taille du modele faster-whisper
Retourne :
dict : comparatif couts, performances, recommandation
"""
# TODO etudiant : implementer l'estimation
# Etape 1 : Definir le ratio temps reel selon le modele
speed_ratios = None # dictionnaire modele -> ratio
# Etape 2 : Calculer le cout mensuel API
api_cost_monthly = None # dollars/mois
# Etape 3 : Estimer le temps de traitement local
local_processing_hours = None # heures de calcul
# Etape 4 : Calculer le seuil de rentabilite GPU
gpu_cost = 1000 # dollars (estimation RTX 3090)
break_even_months = None # mois
# Etape 5 : Construire la recommandation
recommendation = None # "local" ou "api"
return {
"hours_per_month": hours_per_month,
"api_cost_monthly": api_cost_monthly,
"local_processing_hours": local_processing_hours,
"break_even_months": break_even_months,
"recommendation": recommendation
}
# Test avec un exemple
result = estimate_stt_costs(hours_per_month=50, gpu_available=True, gpu_vram_gb=10)
print("Exercice a completer")Exercice a completer
Exercice 3 : Conversion de format audio et impact sur la transcription
Objectif : Créer une fonction qui convertit un fichier audio entre différents formats (WAV, MP3, FLAC) et analyse l’impact du format et du sample rate sur la qualite de transcription Whisper.
Les modèles Whisper sont sensibles a la qualite de l’audio en entree. Les compressions avec perte (MP3 a bas bitrate) peuvent degrader la precision de transcription, tandis que les formats sans perte (WAV, FLAC) preservent la fidelite. Le sample rate affecte également la detection de la langue et la precision des timestamps.
Indices : - Whisper fonctionne en interne a 16 kHz mono ; les fichiers a 44.1 kHz stereo sont automatiquement resample - librosa.load(path, sr=16000) force le resampling a 16 kHz - soundfile.write() peut sauvegarder en WAV, FLAC et OGG - Comparez la confiance moyenne (avg_logprob) entre les versions - # Étape 1 : Charger un echantillon audio existant (utilisez test_files) - # Étape 2 : Convertir en WAV 16kHz mono, MP3 128kbps, et FLAC - # Étape 3 : Transcrire chaque version avec le même modèle - # Étape 4 : Comparer les metriques (texte, confiance, temps) - # Indice : sf.write() accepte format="FLAC" ou format="WAV" comme paramètre
# Exercice 4 : Conversion de format audio et impact sur la transcription
# TODO etudiant : Analyser l'impact du format audio sur la qualite Whisper
def compare_formats_for_transcription(audio_path, model_whisper, formats=None):
"""
Convertit un fichier audio en plusieurs formats et compare les transcriptions.
Args:
audio_path: Chemin du fichier audio source
model_whisper: Modele faster-whisper charge
formats: Liste de tuples (nom, sample_rate, format)
ex: [("wav_16k", 16000, "WAV"), ("flac_44k", 44100, "FLAC")]
Returns:
dict: Resultats comparatifs par format
"""
if formats is None:
formats = [] # TODO etudiant : definir 3 formats a tester
results = {}
# TODO etudiant : Pour chaque format :
# 1. Charger l'audio avec librosa.load(path, sr=target_sr)
# 2. Sauvegarder au format cible avec sf.write()
# 3. Transcrire avec model_whisper.transcribe()
# 4. Collecter : texte, confiance moyenne, temps, taille fichier
return results
# Test (decommenter quand le modele est charge)
# comparison = compare_formats_for_transcription(test_files["en"], model)
print("Exercice a completer")Exercice a completer
Interpretation : Comparaison local vs API
| Aspect | Local (faster-whisper) | API (OpenAI) | Gagnant |
|---|---|---|---|
| Cout initial | $0 (hardware) | $0 | Local |
| cout recurrent | $0 | $0.36/heure | Local (volume >60h) |
| Latence | runtime machine-dep : 30s (CPU) / 5s (GPU) | runtime machine-dep : 2-3s + reseau | API (si GPU indispo) |
| Confidentialite | 100% locale | Envoi a OpenAI | Local |
| Disponibilite | Offline | Internet requis | Local |
| Maintenance | Mises a jour manuelles | Geree par OpenAI | API |
Analyse cout : - 1 heure d’audio : API = $0.36 - 100 heures/mois : API = $36/mois - GPU RTX 3090 (24GB) : ~$1000 (amortissable sur 2-3 ans) - Seuil de rentabilite : ~200-300 heures d’audio
Recommandation : - Local : Production, données sensibles, volume >100h/mois, GPU disponible - API : Prototypage, volume faible, pas de GPU, commodite prioritaire
Note technique : L’API OpenAI Whisper utilise le même modèle large-v3 mais avec une infrastructure optimisee. La qualite de transcription est identique. La différence est purement operationnelle (latence, cout, confidentialite).
La transcription batch etant experimentee, nous comparons maintenant faster-whisper en mode local avec l’API OpenAI Whisper pour evaluer les compromis de performance, cout et confidentialite.
# Mode interactif
if notebook_mode == "interactive" and not skip_widgets:
print("MODE INTERACTIF")
print("=" * 50)
print("\nEntrez le chemin d'un fichier audio a transcrire :")
print("(Laissez vide pour passer a la suite)")
try:
user_path = input("\nChemin du fichier audio : ")
if user_path.strip():
user_file = Path(user_path.strip())
if user_file.exists():
print(f"\nTranscription de {user_file.name}...")
start_time = time.time()
segments, info = model.transcribe(
str(user_file), word_timestamps=True
)
segments_list = list(segments)
elapsed = time.time() - start_time
text = " ".join([s.text.strip() for s in segments_list])
print(f"\nLangue : {info.language} ({info.language_probability:.0%})")
print(f"Duree : {info.duration:.1f}s")
print(f"Temps : {elapsed:.2f}s")
print(f"\nTexte : {text}")
else:
print(f"Fichier non trouve : {user_file}")
else:
print("Mode interactif ignore")
except (KeyboardInterrupt, EOFError):
print("Mode interactif interrompu")
except Exception as e:
error_type = type(e).__name__
if "StdinNotImplemented" in error_type or "input" in str(e).lower():
print("Mode interactif non disponible (execution automatisee)")
else:
print(f"Erreur : {error_type} - {str(e)[:100]}")
else:
print("Mode batch - Interface interactive desactivee")Mode batch - Interface interactive desactivee
Bonnes pratiques et optimisation
Choix du modèle
| Scénario | Modèle recommande | Raison |
|---|---|---|
| Prototypage rapide | small ou base |
Chargement rapide, VRAM minimale |
| Production | large-v3-turbo |
Meilleur compromis qualite/vitesse |
| Qualite maximale | large-v3 |
Precision maximale, plus lent |
| GPU limite (4 GB) | small + int8 |
Fonctionne sur la plupart des GPUs |
Optimisation des performances
| Technique | Impact | Description |
|---|---|---|
int8_float16 |
VRAM -50% | Quantification mixte |
beam_size=1 |
Vitesse +50% | Perte de qualite minime |
| Specifier la langue | Vitesse +20% | Evite la detection |
condition_on_previous_text=False |
Evite les repetitions | Utile pour longs fichiers |
# Statistiques de session et prochaines etapes
print("STATISTIQUES DE SESSION")
print("=" * 45)
print(f"Date : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Modele : {model_size}")
print(f"Device : {device}, Compute : {compute_type}")
print(f"Fichiers transcrits : {len(test_files)}")
if monitor_vram and gpu_available:
vram_current = torch.cuda.memory_allocated(0) / (1024**3)
vram_peak = torch.cuda.max_memory_allocated(0) / (1024**3)
print(f"VRAM actuelle : {vram_current:.2f} GB")
print(f"VRAM pic : {vram_peak:.2f} GB")
if save_results:
saved = list(OUTPUT_DIR.glob('*'))
print(f"Fichiers sauvegardes : {len(saved)} dans GenAI/{OUTPUT_DIR.relative_to(GENAI_ROOT)}")
# Liberation memoire
print(f"\nLiberation du modele...")
del model
gc.collect()
if gpu_available:
torch.cuda.empty_cache()
print(f"Memoire liberee")
print(f"\nPROCHAINES ETAPES")
print(f"1. Essayer le TTS local avec Kokoro (01-5-Kokoro-TTS-Local)")
print(f"2. Decouvrir le voice cloning avec XTTS (02-2)")
print(f"3. Comparer tous les modeles STT (03-1)")
print(f"4. Construire un pipeline STT->LLM->TTS (03-2)")
print(f"\nNotebook Whisper Local termine - {datetime.now().strftime('%H:%M:%S')}")STATISTIQUES DE SESSION
=============================================
Date : 2026-08-18 00:43:39
Modele : large-v3-turbo
Device : cuda, Compute : float16
Fichiers transcrits : 3
VRAM actuelle : 0.00 GB
VRAM pic : 0.00 GB
Fichiers sauvegardes : 1 dans GenAI/outputs\audio\whisper_local
Liberation du modele...
Memoire liberee
PROCHAINES ETAPES
1. Essayer le TTS local avec Kokoro (01-5-Kokoro-TTS-Local)
2. Decouvrir le voice cloning avec XTTS (02-2)
3. Comparer tous les modeles STT (03-1)
4. Construire un pipeline STT->LLM->TTS (03-2)
Notebook Whisper Local termine - 00:43:39
Exemple guide - Pipeline de Transcription avec Segmentation Intelligente
Duree estimee : 15-20 minutes
Objectif
Créer un pipeline de transcription qui detecte automatiquement les silences longs (>2s) pour segmenter l’audio en chapitres, puis transcrit chaque segment avec des metadonnees.
Contexte
Dans la Section 3, vous avez vu la transcription batch de fichiers multiples. Maintenant, vous allez transcrire un seul fichier mais en y appliquant une segmentation intelligente basee sur les silences.
Cette approche est utile pour : - Podcasts avec plusieurs intervenants - Enregistrements de conferences avec pauses - Generation de chapitres automatiques
Instructions
- Charger un echantillon audio existant (utilisez
test_files['en']outest_files['fr']du notebook) - Utiliser
librosa.effects.split()pour detecter les silences > 2 secondes- Indice :
top_db=20est un bon point de depart pour le seuil de silence
- Indice :
- Pour chaque segment non-silent :
- Transcrire avec
model.transcribe()(cf. Section 2) - Extraire : texte, langue, timestamps debut/fin, confiance moyenne
- Transcrire avec
- Créer un tableau recapitulatif avec pandas ou un dictionnaire structure
Indices :
librosa.effects.split(y, top_db=20)retourne les indices des segments non-silents- Convertir les indices en temps :
start_sample / srdonne le temps en secondes - La confiance moyenne peut etre calculee depuis
seg.avg_logprobpour chaque segment - Pour isoler un segment audio :
y[start:end]
Synthese du notebook
Dans ce notebook, vous avez appris a :
- Charger et utiliser faster-whisper pour la transcription STT locale
- Comprendre les compromis entre les différentes tailles de modèles (tiny/base/small/medium/large-v3/large-v3-turbo)
- Transcrire avec des metadonnees detaillees (timestamps, confiance, detection de langue)
- Effectuer des traitements batch en conservant le modèle en memoire
- Comparer local vs API pour choisir la solution adapttee a votre cas d’usage
Tableau recapitulatif des modèles
| Modèle | VRAM | Vitesse | Qualite | Usage recommande |
|---|---|---|---|---|
| tiny | 1 GB | 32x | Basique | Prototypage rapide |
| base | 1 GB | 16x | Correcte | Tests preliminaires |
| small | 2 GB | 6x | Bonne | Equilibrage CPU/GPU |
| medium | 5 GB | 2x | Très bonne | Production GPU limite |
| large-v3-turbo | 6 GB | 3x | Excellente | Production standard |
| large-v3 | 10 GB | 1x | Excellente | Qualite maximale |
Prochaine étape
Le notebook suivant (01-5-Kokoro-TTS-Local) vous montrera comment faire l’inverse : generer de l’audio depuis du texte avec un modèle TTS local.
import librosa
import pandas as pd
from faster_whisper import WhisperModel
# Exercice: Charger l'echantillon audio
# Indice : utiliser test_files deja disponibles dans le notebook
if len(test_files) > 0:
# Charger le modele Whisper (si pas deja charge)
if 'model' not in locals() and 'model' not in globals():
print("Chargement du modele Whisper...")
model_exo = WhisperModel(model_size, device=device, compute_type=compute_type)
else:
model_exo = model
audio_path = test_files[list(test_files.keys())[0]]
# Exercice: Charger l'audio avec librosa
# Indice : sr=None pour preserver le sample rate original
y, sr = librosa.load(str(audio_path), sr=None)
# Exercice: Detecter les segments non-silents
# Indice : top_db=20, frame_length=2048, hop_length=512
segments = librosa.effects.split(y, top_db=20, frame_length=2048, hop_length=512)
print(f"Segments detectes : {len(segments)}")
# Exercice: Pour chaque segment, transcrire et collecter les metadonnees
results = []
for i, (start, end) in enumerate(segments[:3]): # Limiter a 3 segments pour l'exercice
# Exercice: Extraire le segment audio
# Indice: y[start:end]
segment_audio = y[start:end]
# Exercice: Sauvegarder temporairement le segment pour Whisper
# Indice: utiliser sf.write() avec un fichier temporaire
import soundfile as sf
temp_path = OUTPUT_DIR / f"segment_{i}.wav"
sf.write(str(temp_path), segment_audio, sr)
# Exercice: Transcrire le segment
# Indice: model.transcribe(str(temp_path), beam_size=5)
segments_list, info = model_exo.transcribe(str(temp_path), beam_size=5)
segments_list = list(segments_list)
# Exercice: Collecter les metadonnees
text = " ".join([s.text.strip() for s in segments_list])
confidence = sum([s.avg_logprob for s in segments_list]) / len(segments_list) if segments_list else 0
result = {
"segment": i,
"start_s": start / sr,
"end_s": end / sr,
"duration_s": (end - start) / sr,
"language": info.language,
"text": text[:50] + "..." if len(text) > 50 else text,
"confidence": confidence
}
results.append(result)
# Nettoyer le fichier temporaire
temp_path.unlink()
# Exercice: Creer un DataFrame pandas pour afficher les resultats
df = pd.DataFrame(results)
# Afficher le resultat
print("\nResultats de transcription par segment:")
print(df[['segment', 'start_s', 'duration_s', 'language', 'text']])
else:
print("EXERCICE IGNORE: Pas d'echantillons audio disponibles")Chargement du modele Whisper...
Segments detectes : 18
Resultats de transcription par segment:
segment start_s duration_s language text
0 0 0.064000 0.448000 en Speech recognition.
1 1 0.533333 0.469333 en ignition.
2 2 1.024000 0.256000 en convert.
Interpretation : Exemple guide de segmentation
Le résultat de l’exercice montre que la segmentation par silence (librosa.effects.split) permet de diviser un fichier audio en segments coherents avant transcription. Chaque segment peut ensuite etre transcrit independamment avec ses metadonnees.
Avantages de cette approche : 1. Chapitrage automatique : Les silences longs identifient naturellement les changements de sujet 2. Parallelisation : Chaque segment peut etre transcrit en parallele sur plusieurs GPUs 3. Metadonnees riches : Timestamps précis pour chaque segment (utile pour sous-titres) 4. Robustesse : Si un segment echoue, les autres ne sont pas affectes
Limites : 1. Faux positifs : Les pauses dans une phrase peuvent etre interprutees comme des silences 2. Faux negatifs : Les chevauchements de parole (overlaps) ne sont pas separes 3. Surcout : Transcription N segments plus lente que transcription unique
Note technique : Pour une production robuste, combinez la detection de silence avec une detection de changement de locuteur (speaker diarization) utilisant des modèles comme pyannote.audio.
Critères de succes
Extension (bonus)
Soumission : PR avec titre “Exercice #1 - [Votre Nom]” sur le repo GenAI_Series
Conclusion
Ce notebook a permis d explorer les aspects essentiels de 01 4 whisper local. Les points cles :
- Les concepts fondamentaux ont ete presentes et illustres
- Les exercices proposent une mise en pratique progressive
- Les résultats obtenus permettent de valider la comprehension
Pour aller plus loin : approfondir les aspects avances du sujet et explorer les liens avec d autres domaines.