Phase 06 - Lesson 06
Reconhecimento e Verificação de Locutor
This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.
O ASR pergunta "o que foi dito?". O reconhecimento de locutor pergunta "quem disse?". A matemática parece a mesma — embeddings mais cosseno — mas toda decisão de produção depende de um único número de EER.
Tipo: Build Linguagens: Python Pré-requisitos: Fase 6 · 02 (Espectrogramas e Mel), Fase 5 · 22 (Modelos de Embedding) Tempo: ~45 minutos
O Problema
Um usuário fala uma frase secreta. Você quer saber: esta é a pessoa que ela afirma ser (verificação, 1:1), ou é a primeira pessoa no seu banco de cadastro (identificação, 1:N)? Ou nenhuma das duas — este é um locutor desconhecido (open-set)?
Antes de 2018: GMM-UBM + i-vectors. EER razoável, mas frágil a mudança de canal (telefone vs laptop) e emoção. 2018–2022: x-vectors (backbone TDNN treinado com margem angular). 2022+: embeddings ECAPA-TDNN e WavLM-large. Em 2026 o campo é dominado por três modelos e uma métrica.
A métrica é o EER — Equal Error Rate (taxa de erro igual). Ajuste o limiar de decisão de modo que a Taxa de Falsa Aceitação = Taxa de Falsa Rejeição. O ponto de cruzamento é o EER. Usado em todo artigo, todo leaderboard, toda licitação.
O Conceito
O pipeline. Cadastro: grave de 5 a 30 segundos do locutor-alvo; calcule um embedding de dimensão fixa (192-d para ECAPA-TDNN, 256-d para WavLM-large). Verificação: obtenha o embedding da fala de teste; calcule a similaridade de cosseno; compare com um limiar.
ECAPA-TDNN (2020, ainda dominante em 2026). Emphasized Channel Attention, Propagation and Aggregation - Time-Delay Neural Network. Blocos de convolução 1D com squeeze-excitation, pooling por atenção multi-cabeça, seguidos por uma camada linear para 192-d. Treinado no VoxCeleb 1+2 (2.700 locutores, 1,1 M de falas) com a perda Additive Angular Margin (AAM-softmax).
WavLM-SV (2022+). Faça fine-tuning de um backbone SSL WavLM-large pré-treinado com perda AAM. Qualidade maior, porém mais lento — 300+ MB vs 15 MB.
x-vector (baseline). TDNN + statistics pooling. Clássico; ainda útil em CPU / edge.
AAM-softmax. Softmax padrão com margem m adicionada no espaço angular: cos(θ + m) para a classe correta. Força a separação angular entre classes. m=0.2 típico, escala s=30.
Pontuação
- Cosseno entre os embeddings de cadastro e de teste. Decisão baseada em limiar.
- PLDA (LDA Probabilístico). Projeta os embeddings em um espaço latente onde mesmo-locutor vs locutor-diferente tem uma razão de verossimilhança em forma fechada. Adicionado sobre o cosseno para uma redução de EER de +10–20%. Padrão antes de 2020; agora usado apenas em configurações closed-set.
- Normalização de score.
S-normouAS-norm: normalize cada score contra um cohort de médias e desvios-padrão de impostores. Essencial para avaliação cross-domain.
Números que você deveria conhecer (2026)
| Modelo | VoxCeleb1-O EER | Parâmetros | Throughput (A100) |
|---|---|---|---|
| x-vector (clássico) | 3.10% | 5 M | 400× RT |
| ECAPA-TDNN | 0.87% | 15 M | 200× RT |
| WavLM-SV large | 0.42% | 316 M | 20× RT |
| Pyannote 3.1 segmentação + embedding | 0.65% | 6 M | 100× RT |
| ReDimNet (2024) | 0.39% | 24 M | 100× RT |
Diarização
"Quem falou quando" em um clipe com múltiplos locutores. Pipeline: VAD → segmentar → embedar cada segmento → clusterizar (aglomerativo ou espectral) → suavizar fronteiras. Stack moderno: pyannote.audio 3.1, que reúne segmentação de locutor + embedding + clustering por trás de uma única chamada. O DER SOTA de 2026 no AMI é ~15% (queda dos 23% de 2022).
Construa
Passo 1: embedding de brinquedo a partir de estatísticas de MFCC
def embed_mfcc_stats(signal, sr):
frames = featurize_mfcc(signal, sr, n_mfcc=13)
mean = [sum(f[i] for f in frames) / len(frames) for i in range(13)]
std = [
math.sqrt(sum((f[i] - mean[i]) ** 2 for f in frames) / len(frames))
for i in range(13)
]
return mean + std # 26-d
Longe de SOTA — apenas para fins didáticos. code/main.py usa isto como prova de conceito em dados sintéticos de locutores.
Passo 2: similaridade de cosseno + limiar
def cosine(a, b):
dot = sum(x * y for x, y in zip(a, b))
na = math.sqrt(sum(x * x for x in a))
nb = math.sqrt(sum(x * x for x in b))
return dot / (na * nb) if na and nb else 0.0
def verify(enroll, test, threshold=0.75):
return cosine(enroll, test) >= threshold
Passo 3: EER a partir de pares de similaridade
def eer(same_scores, diff_scores):
thresholds = sorted(set(same_scores + diff_scores))
best = (1.0, 1.0, 0.0) # (fa, fr, threshold)
for t in thresholds:
fr = sum(1 for s in same_scores if s < t) / len(same_scores)
fa = sum(1 for s in diff_scores if s >= t) / len(diff_scores)
if abs(fa - fr) < abs(best[0] - best[1]):
best = (fa, fr, t)
return (best[0] + best[1]) / 2, best[2]
Retorna (eer, threshold_at_eer). Reporte ambos.
Passo 4: produção com SpeechBrain
from speechbrain.pretrained import EncoderClassifier
clf = EncoderClassifier.from_hparams(source="speechbrain/spkrec-ecapa-voxceleb")
# enroll: average the embeddings of 3-5 clean samples
enroll = torch.stack([clf.encode_batch(load(x)) for x in enrollment_clips]).mean(0)
# verify
score = clf.similarity(enroll, clf.encode_batch(load("test.wav"))).item()
verdict = score > 0.25 # ECAPA typical threshold; tune on your data
Passo 5: diarize com pyannote
from pyannote.audio import Pipeline
pipe = Pipeline.from_pretrained("pyannote/speaker-diarization-3.1")
diarization = pipe("meeting.wav", num_speakers=None)
for turn, _, speaker in diarization.itertracks(yield_label=True):
print(f"{turn.start:.1f}–{turn.end:.1f} {speaker}")
Use
O stack de 2026:
| Situação | Escolha |
|---|---|
| Verificação 1:1 closed-set, edge | ECAPA-TDNN + limiar de cosseno |
| Verificação open-set, nuvem | WavLM-SV + AS-norm |
| Diarização (reuniões, podcasts) | pyannote/speaker-diarization-3.1 |
| Anti-spoofing (detecção de replay / deepfake) | AASIST ou RawNet2 |
| Embarcado minúsculo (KWS + cadastro) | Titanet-Small (NeMo) |
Armadilhas
- Incompatibilidade de canal. Modelo treinado no VoxCeleb (vídeo da web) ≠ áudio de chamada telefônica. Sempre avalie no canal-alvo.
- Falas curtas. O EER degrada bruscamente abaixo de 3 segundos de áudio de teste.
- Cadastro com ruído. Um único cadastro ruidoso envenena a âncora. Use ≥3 amostras limpas e faça a média.
- Limiar fixo entre condições. Sempre ajuste o limiar em um conjunto de dev separado do domínio-alvo.
- Cosseno sobre embeddings não normalizados. Normalize por L2 primeiro; caso contrário, a magnitude domina.
Entregue
Salve como outputs/skill-speaker-verifier.md. Escolha o modelo, o protocolo de cadastro, o plano de ajuste de limiar e as salvaguardas contra fraude.
Exercícios
- Fácil. Execute
code/main.py. Constrói "locutores" sintéticos (perfis de tom diferentes), faz o cadastro, calcula o EER em uma lista de teste de 100 pares. - Médio. Use o ECAPA do SpeechBrain em 30 falas do VoxCeleb1 (5 locutores × 6 cada). Calcule o EER com cosseno vs PLDA.
- Difícil. Construa o pipeline completo cadastro → diarização → verificação com
pyannote.audio. Avalie o DER no conjunto de dev do AMI.
Termos-chave
| Termo | O que as pessoas dizem | O que realmente significa |
|---|---|---|
| EER | A métrica principal | Limiar onde Falsa Aceitação = Falsa Rejeição. |
| Verificação | 1:1 | "Esta é a Alice?" |
| Identificação | 1:N | "Quem está falando?" |
| Open-set | Desconhecido possível | O conjunto de teste pode conter locutores não cadastrados. |
| Cadastro | Registrar | Calcular o embedding de referência de um locutor. |
| AAM-softmax | A perda | Softmax com margem angular aditiva; força a separação de clusters. |
| PLDA | Pontuação clássica | LDA Probabilístico; pontuação por razão de verossimilhança sobre os embeddings. |
| DER | Métrica de diarização | Diarization Error Rate — miss + falso alarme + confusão. |
Leitura Complementar
- Snyder et al. (2018). X-Vectors: Robust DNN Embeddings for Speaker Recognition — o clássico artigo de embedding profundo.
- Desplanques et al. (2020). ECAPA-TDNN — arquitetura dominante 2020–2026.
- Chen et al. (2022). WavLM: Large-Scale Self-Supervised Pre-Training for Full Stack Speech Processing — backbone SSL para SV e diarização.
- Bredin et al. (2023). pyannote.audio 3.1 — stack de diarização + embedding de produção.
- VoxCeleb leaderboard (atualizado em 2026) — classificação atual de EER entre modelos.