Cómo anotar transcripciones de Whisper
Cómo convertir la salida de Whisper o WhisperX en un proyecto de anotación en marcha: qué archivo conservar, cuándo hace falta diarización, cómo asignar hablantes y cómo exportar sin perder las marcas de tiempo.
Whisper te da segmentos con tiempos, y una herramienta de anotación debería leerlos tal cual. Conserva la salida .json en lugar del .txt, ejecuta WhisperX si necesitas etiquetas de hablante y luego apunta Potato a la carpeta. Sin script de conversión ni paso de reformateo. Para el detalle formato por formato, consulta Formatos de transcripción.
Whisper es el modelo de reconocimiento de voz de código abierto de OpenAI, y se ha convertido en el primer paso por defecto para cualquiera que tenga una pila de audio por delante. Lo que produce es un conjunto de segmentos con tiempos, que es casi todo lo que necesita un proyecto de anotación. El hueco está en todo lo que va entre "tengo transcripciones" y "los anotadores están etiquetándolas".
Note: Potato no transcribe. El ASR se ejecuta antes y Potato ingiere su salida. Esta guía cubre qué decisiones previas importan, pero la transcripción ocurre antes de que Potato vea nada.
¿Qué archivo de salida de Whisper debo conservar?
Whisper escribe varios archivos y la elección importa más de lo que parece:
whisper interview_01.mp3 --model medium --output_format json --word_timestamps True| Archivo | Contiene | ¿Usarlo? |
|---|---|---|
.json | Segmentos con tiempos de inicio y fin, opcionalmente tiempos por palabra | Sí, este |
.srt / .vtt | Segmentos con tiempos, sin metadatos | Sí, funciona bien |
.tsv | Inicio y fin en milisegundos más el texto | Sí |
.txt | Solo texto, sin tiempos | No, no hay nada que sincronizar con el audio |
Si el .txt es lo único que conservaste, la alineación se perdió y no la puedes recuperar sin volver a ejecutar el modelo. Esa es la razón más frecuente de que una transcripción llegue a Potato como un único bloque de texto indiferenciado.
¿Whisper etiqueta hablantes?
No, y esto sorprende a mucha gente constantemente. Whisper transcribe; no te dice quién habla. Todos los turnos llegan sin asignar, lo cual está bien para anotar contenido y es doloroso para cualquier cosa relacionada con hablantes.
La diarización de hablantes es un paso aparte. Tres formas de resolverlo:
Ejecuta WhisperX, que envuelve Whisper con diarización de pyannote:
whisperx interview_01.mp3 --model medium --diarize --output_format jsonSu salida lleva un campo speaker por segmento (SPEAKER_00, SPEAKER_01, y así) y Potato lo lee directamente.
Usa una API en la nube con diarización activada. Deepgram con diarize=true, AssemblyAI con speaker_labels, AWS Transcribe y Rev.ai producen etiquetas de hablante, y Potato lee las cuatro de forma nativa.
Los turnos sin diarizar se muestran como Unassigned con un selector de hablante
Deja que lo hagan los anotadores. Cuando los turnos llegan sin diarizar, cada burbuja se muestra como Unassigned con un selector. Con audio ruidoso, habla solapada o una sala con ruido de fondo, una persona escuchando suele ser más precisa que la diarización automática. Para un corpus pequeño esto es un intercambio razonable, no un recurso de emergencia.
¿Cómo construyo el archivo de datos?
Apunta el conversor a tu carpeta de salida de Whisper:
potato transcripts ./whisper_out --media-dir ./audio -o data/interviews.jsonLas transcripciones se emparejan con el audio por nombre base, así que interview_01.json encuentra interview_01.mp3. Los identificadores de ítem salen del nombre de archivo, y la doble extensión interview_01.mp3.json de Whisper se maneja sin producir un id como interview_01.mp3.
Comprueba qué ha entendido antes de darlo por bueno:
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 archivo que informa plain text o cero turnos es tu archivo problemático. Casi siempre es un .txt que se coló entre la salida .json.
¿Puedo saltarme el paso de conversión?
Sí. Un archivo de datos puede apuntar directamente a las transcripciones en disco:
{"id": "int_001", "conversation": {"audio": "media/int_001.mp3",
"transcript": "media/int_001.srt"}}Potato lee el archivo, detecta el formato por su contenido y lo normaliza al renderizar la instancia. Tus transcripciones siguen siendo archivos que puedes comparar y volver a exportar, en lugar de quedar incrustadas en un bloque de datos.
¿Cómo es la configuración?
El archivo de datos generado tiene esta forma:
{
"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."}
]
}
}Y la configuración correspondiente, que potato transcripts --emit-config te imprime:
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
La visualización audio_dialogue, con una pregunta por turno adjunta a cada turno
La visualización audio_dialogue muestra los turnos como burbujas de hablante sincronizadas con el audio, con un botón de reproducción en cada turno que reproduce solo ese turno. span_target: true permite que los tramos crucen fronteras de turno, y los desplazamientos se mantienen estables cuando se reasigna un hablante.
Si prefieres que los anotadores revisen la transcripción en lugar de etiquetar su contenido, speech_transcript te da tarjetas de segmento con etiquetas de error y una caja de corrección. Para trabajo sobre solapamientos e interrupciones, voice_interaction te da una línea de tiempo de doble pista. Ambos leen los mismos archivos.
¿Cómo asignan hablantes los anotadores?
Define una lista para que los hablantes tengan nombres, colores y lados estables:
display_options:
allow_speaker_assignment: auto
speakers:
- id: interviewer
name: "Interviewer"
color: "#7c3aed"
side: left
- id: participant
name: "Participant"
color: "#059669"
side: rightauto activa el selector cuando hay turnos sin diarizar que etiquetar. Ponlo en true para permitir la reasignación incluso cuando la fuente sí etiquetó hablantes, que es lo que quieres si estás corrigiendo errores de diarización en lugar de rellenar huecos.
Las asignaciones se guardan con las anotaciones, ligadas a un identificador de turno estable, así que sobreviven a una recarga.
¿Cómo recupero las anotaciones?
La exportación a JSON, JSONL y CSV funciona como siempre. Cuando la alineación temporal tiene que sobrevivir, exporta las anotaciones por capas a ELAN EAF o Praat TextGrid:
python -m potato.export --config config.yaml --format eaf --output ./out/
python -m potato.export --config config.yaml --format textgrid --output ./out/Ambos hacen ida y vuelta. Potato también lee EAF y TextGrid como entrada, así que puedes anotar en Potato, refinar en ELAN o Praat, y volver a leer el resultado.
Lecturas adicionales
- Formatos de transcripción, todos los formatos admitidos y cómo se detectan
- Cómo anotar subtítulos de YouTube, la versión de este flujo para archivos de subtítulos
- Anotación de audio, para trabajo sobre la forma de onda que no parte de una transcripción
- Ingesta de formatos de transcripción, un diseño de muestra ejecutable con seis formatos en paralelo
- Acuerdo entre anotadores, para medir el acuerdo en etiquetas a nivel de turno