Como anotar transcrições do Whisper
Como transformar a saída do Whisper ou do WhisperX num projeto de anotação em funcionamento: que ficheiro guardar, quando é preciso diarização, como atribuir falantes e como exportar sem perder o alinhamento temporal.
O Whisper dá-lhe segmentos com tempos, e uma ferramenta de anotação deveria lê-los tal como estão. Guarde a saída .json em vez do .txt, execute o WhisperX se precisar de etiquetas de falante e depois aponte o Potato para a pasta. Sem script de conversão, sem passo de reformatação. Para o detalhe formato a formato, consulte Formatos de transcrição.
O Whisper é o modelo de reconhecimento de fala de código aberto da OpenAI, e tornou-se o primeiro passo por omissão para quem tem uma pilha de áudio pela frente. O que produz é um conjunto de segmentos com tempos, que é quase tudo o que um projeto de anotação precisa. A lacuna está em tudo o que separa "tenho transcrições" de "os anotadores estão a etiquetá-las".
Note: O Potato não transcreve. O reconhecimento de fala corre a montante e o Potato ingere a sua saída. Este guia cobre que escolhas anteriores importam, mas a transcrição acontece antes de o Potato ver seja o que for.
Que ficheiro de saída do Whisper devo guardar?
O Whisper escreve vários ficheiros e a escolha importa mais do que parece:
whisper interview_01.mp3 --model medium --output_format json --word_timestamps True| Ficheiro | Contém | Usar? |
|---|---|---|
.json | Segmentos com tempos de início e fim, opcionalmente tempos por palavra | Sim, este |
.srt / .vtt | Segmentos com tempos, sem metadados | Sim, funciona bem |
.tsv | Início e fim em milissegundos mais o texto | Sim |
.txt | Apenas texto, sem tempos | Não, não há nada para sincronizar com o áudio |
Se o .txt foi tudo o que guardou, o alinhamento perdeu-se e não o consegue recuperar sem voltar a correr o modelo. É a razão mais comum para uma transcrição chegar ao Potato como um único bloco de texto indiferenciado.
O Whisper etiqueta falantes?
Não, e isto apanha muita gente de surpresa. O Whisper transcreve; não diz quem está a falar. Todos os turnos chegam por atribuir, o que é aceitável para anotar conteúdo e penoso para qualquer coisa relacionada com falantes.
A diarização de falantes é um passo à parte. Três formas de a resolver:
Execute o WhisperX, que envolve o Whisper com a diarização do pyannote:
whisperx interview_01.mp3 --model medium --diarize --output_format jsonA sua saída traz um campo speaker por segmento (SPEAKER_00, SPEAKER_01, e assim por diante) e o Potato lê-o diretamente.
Use uma API na nuvem com diarização ativada. Deepgram com diarize=true, AssemblyAI com speaker_labels, AWS Transcribe e Rev.ai produzem etiquetas de falante, e o Potato lê os quatro nativamente.
Os turnos não diarizados aparecem como Unassigned com um seletor de falante
Deixe que os anotadores o façam. Quando os turnos chegam sem diarização, cada balão aparece como Unassigned com um seletor. Com áudio difícil, fala sobreposta ou ruído de fundo, uma pessoa a ouvir costuma ser mais precisa do que a diarização automática. Para um corpus pequeno é uma troca razoável, não um remendo.
Como construo o ficheiro de dados?
Aponte o conversor para a sua pasta de saída do Whisper:
potato transcripts ./whisper_out --media-dir ./audio -o data/interviews.jsonAs transcrições são emparelhadas com o áudio pelo nome base, portanto interview_01.json encontra interview_01.mp3. Os identificadores de item vêm do nome do ficheiro, e a extensão dupla interview_01.mp3.json do Whisper é tratada sem produzir um id interview_01.mp3.
Verifique o que ele percebeu antes de avançar:
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).
Um ficheiro que reporta plain text ou zero turnos é o seu ficheiro problemático. Quase sempre é um .txt que se misturou com a saída .json.
Posso saltar o passo de conversão?
Sim. Um ficheiro de dados pode apontar diretamente para as transcrições em disco:
{"id": "int_001", "conversation": {"audio": "media/int_001.mp3",
"transcript": "media/int_001.srt"}}O Potato lê o ficheiro, deteta o formato pelo conteúdo e normaliza-o ao apresentar a instância. As suas transcrições continuam a ser ficheiros que pode comparar e voltar a exportar, em vez de ficarem presas dentro de um bloco de dados.
Como é a configuração?
O ficheiro de dados gerado tem 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."}
]
}
}E a configuração correspondente, que potato transcripts --emit-config imprime por si:
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
A visualização audio_dialogue, com uma pergunta por turno associada a cada turno
A visualização audio_dialogue mostra os turnos como balões de falante sincronizados com o áudio, com um botão de reprodução em cada turno que toca apenas esse turno. span_target: true permite que os intervalos atravessem fronteiras de turno, e os deslocamentos mantêm-se estáveis quando um falante é reatribuído.
Se preferir que os anotadores verifiquem a própria transcrição em vez de etiquetar o seu conteúdo, speech_transcript dá-lhe cartões de segmento com etiquetas de erro e uma caixa de correção. Para trabalho sobre sobreposições e interrupções, voice_interaction dá-lhe uma linha temporal de duas pistas. Ambos leem os mesmos ficheiros.
Como é que os anotadores atribuem falantes?
Defina uma lista para que os falantes tenham nomes, cores e lados estáveis:
display_options:
allow_speaker_assignment: auto
speakers:
- id: interviewer
name: "Interviewer"
color: "#7c3aed"
side: left
- id: participant
name: "Participant"
color: "#059669"
side: rightauto liga o seletor quando há turnos não diarizados para etiquetar. Ponha true para permitir a reatribuição mesmo quando a fonte etiquetou falantes, que é o que quer se estiver a corrigir erros de diarização em vez de preencher lacunas.
As atribuições ficam guardadas com as anotações, ligadas a um identificador de turno estável, por isso sobrevivem a um recarregamento.
Como recupero as anotações?
A exportação para JSON, JSONL e CSV funciona como sempre. Quando o alinhamento temporal tem de sobreviver, exporte as anotações em camadas para 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/Ambos fazem ida e volta. O Potato também lê EAF e TextGrid como entrada, portanto pode anotar no Potato, refinar no ELAN ou no Praat, e voltar a ler o resultado.
Leituras adicionais
- Formatos de transcrição, todos os formatos suportados e como são detetados
- Como anotar legendas do YouTube, a versão deste fluxo para ficheiros de legendas
- Anotação de áudio, para trabalho sobre a forma de onda que não parte de uma transcrição
- Ingestão de formatos de transcrição, um design executável com seis formatos lado a lado
- Concordância entre anotadores, para medir a concordância em etiquetas ao nível do turno