Comment annoter des transcriptions Whisper
Comment transformer une sortie Whisper ou WhisperX en projet d'annotation opérationnel : quel fichier conserver, quand la diarisation est nécessaire, comment attribuer les locuteurs et comment exporter sans perdre l'alignement temporel.
Whisper vous donne des segments horodatés, et un outil d'annotation devrait les lire tels quels. Gardez la sortie .json plutôt que le .txt, lancez WhisperX s'il vous faut des étiquettes de locuteur, puis pointez Potato vers le dossier. Pas de script de conversion, pas d'étape de reformatage. Pour le détail format par format, voir Formats de transcription.
Whisper est le modèle open source de reconnaissance automatique de la parole d'OpenAI, devenu la première étape par défaut pour quiconque a une pile d'audio à traiter. Il produit un ensemble de segments horodatés, c'est-à-dire l'essentiel de ce dont un projet d'annotation a besoin. Ce qui manque, c'est tout ce qui sépare « j'ai des transcriptions » de « les annotateurs les étiquettent ».
Note: Potato ne transcrit pas. La reconnaissance vocale tourne en amont et Potato ingère sa sortie. Ce guide indique quels choix en amont comptent, mais la transcription se fait avant que Potato ne voie quoi que ce soit.
Quel fichier de sortie Whisper faut-il garder ?
Whisper écrit plusieurs fichiers, et le choix compte plus qu'il n'y paraît :
whisper interview_01.mp3 --model medium --output_format json --word_timestamps True| Fichier | Contient | À utiliser ? |
|---|---|---|
.json | Segments avec temps de début et de fin, éventuellement des temps par mot | Oui, celui-ci |
.srt / .vtt | Segments horodatés, sans métadonnées | Oui, fonctionne très bien |
.tsv | Début et fin en millisecondes, plus le texte | Oui |
.txt | Texte seul, aucun temps | Non, rien à synchroniser avec l'audio |
Si le .txt est tout ce que vous avez gardé, l'alignement est perdu et vous ne pouvez pas le récupérer sans relancer le modèle. C'est la raison la plus fréquente pour laquelle une transcription arrive dans Potato sous forme d'un unique bloc de texte indifférencié.
Whisper étiquette-t-il les locuteurs ?
Non, et cela surprend beaucoup de monde. Whisper transcrit ; il ne dit pas qui parle. Tous les tours arrivent non attribués, ce qui convient pour annoter du contenu et devient pénible dès qu'il s'agit des locuteurs.
La diarisation est une étape distincte. Trois façons de la traiter :
Lancez WhisperX, qui enveloppe Whisper avec la diarisation de pyannote :
whisperx interview_01.mp3 --model medium --diarize --output_format jsonSa sortie contient un champ speaker par segment (SPEAKER_00, SPEAKER_01, etc.) que Potato lit directement.
Utilisez une API cloud avec la diarisation activée. Deepgram avec diarize=true, AssemblyAI avec speaker_labels, AWS Transcribe et Rev.ai produisent des étiquettes de locuteur, et Potato lit les quatre nativement.
Les tours non diarisés apparaissent comme Unassigned avec un sélecteur de locuteur
Laissez les annotateurs le faire. Quand les tours arrivent non diarisés, chaque bulle apparaît comme Unassigned avec un sélecteur. Sur un audio difficile, avec de la parole superposée ou du bruit de fond, une personne qui écoute est souvent plus juste que la diarisation automatique. Pour un petit corpus, c'est un arbitrage raisonnable, pas un pis-aller.
Comment construire le fichier de données ?
Pointez le convertisseur vers votre dossier de sortie Whisper :
potato transcripts ./whisper_out --media-dir ./audio -o data/interviews.jsonLes transcriptions sont appariées aux médias par nom de base, donc interview_01.json trouve interview_01.mp3. Les identifiants d'item viennent du nom de fichier, et la double extension interview_01.mp3.json de Whisper est gérée sans produire un identifiant interview_01.mp3.
Vérifiez ce qu'il a compris avant de vous engager :
potato transcripts ./whisper_out --dry-runScanned 3 file(s):
interview_01.json Whisper JSON 42 turns 891.4s undiarized
interview_02.json WhisperX JSON 51 turns 1120.8s 2 speaker(s): SPEAKER_00, SPEAKER_01
interview_03.json Whisper JSON 38 turns 754.2s undiarized
3 item(s), 131 turn(s).
Un fichier qui annonce plain text ou zéro tour est votre fichier à problème. C'est presque toujours un .txt qui s'est glissé au milieu des .json.
Puis-je sauter l'étape de conversion ?
Oui. Un fichier de données peut pointer directement vers les transcriptions sur le disque :
{"id": "int_001", "conversation": {"audio": "media/int_001.mp3",
"transcript": "media/int_001.srt"}}Potato lit le fichier, détecte le format d'après son contenu et le normalise au moment du rendu. Vos transcriptions restent des fichiers que vous pouvez comparer et réexporter, au lieu d'être figées dans un bloc de données.
À quoi ressemble la configuration ?
Le fichier de données généré a cette forme :
{
"id": "interview_01",
"conversation": {
"audio": "audio/interview_01.mp3",
"turns": [
{"turn_id": "t0", "speaker": "SPEAKER_00", "start": 0.0, "end": 6.5,
"text": "Welcome back."}
]
}
}Et la configuration correspondante, que potato transcripts --emit-config imprime pour vous :
annotation_task_name: "Interview Annotation"
task_dir: .
data_files:
- data/interviews.json
item_properties:
id_key: id
text_key: conversation
instance_display:
fields:
- key: conversation
type: audio_dialogue
label: "Transcript"
span_target: true
display_options:
show_timestamps: true
allow_speaker_assignment: auto
annotation_schemes:
- annotation_type: span
name: topics
description: "Highlight topic mentions"
target_field: conversation
labels:
- name: policy
- name: personal
L'affichage audio_dialogue, avec une question par tour rattachée à chaque tour
L'affichage audio_dialogue présente les tours sous forme de bulles de locuteur synchronisées avec l'audio, chaque tour ayant un bouton de lecture qui ne joue que ce tour. span_target: true permet aux empans de franchir les frontières de tour, et les décalages restent stables quand un locuteur est réattribué.
Si vous préférez faire vérifier la transcription plutôt qu'étiqueter son contenu, speech_transcript fournit des fiches de segment avec des étiquettes d'erreur et un champ de correction. Pour le travail sur les chevauchements et les interruptions, voice_interaction fournit une frise à deux pistes. Les deux lisent les mêmes fichiers.
Comment les annotateurs attribuent-ils les locuteurs ?
Définissez une liste pour que les locuteurs aient des noms, des couleurs et des côtés stables :
display_options:
allow_speaker_assignment: auto
speakers:
- id: interviewer
name: "Interviewer"
color: "#7c3aed"
side: left
- id: participant
name: "Participant"
color: "#059669"
side: rightauto active le sélecteur dès qu'il y a des tours non diarisés à étiqueter. Passez à true pour autoriser la réattribution même quand la source avait bien étiqueté les locuteurs, ce qui est utile si vous corrigez des erreurs de diarisation plutôt que de remplir des vides.
Les attributions sont enregistrées avec les annotations, rattachées à un identifiant de tour stable, donc elles survivent à un rechargement.
Comment récupérer les annotations ?
L'export JSON, JSONL et CSV fonctionne comme d'habitude. Quand l'alignement temporel doit survivre, exportez les annotations en couches vers ELAN EAF ou Praat TextGrid :
python -m potato.export --config config.yaml --format eaf --output ./out/
python -m potato.export --config config.yaml --format textgrid --output ./out/Les deux font l'aller-retour. Potato lit aussi EAF et TextGrid en entrée, donc vous pouvez annoter dans Potato, affiner dans ELAN ou Praat, puis relire le résultat.
Pour aller plus loin
- Formats de transcription, tous les formats pris en charge et leur détection
- Comment annoter des sous-titres YouTube, la version « fichiers de sous-titres » de ce flux
- Annotation audio, pour le travail sur forme d'onde qui ne part pas d'une transcription
- Ingestion de formats de transcription, une conception exécutable avec six formats côte à côte
- Accord inter-annotateurs, pour mesurer l'accord sur des étiquettes au niveau du tour