Phase 06 - Lesson 06

Reconocimiento y Verificación de Hablante

This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.

El ASR pregunta "¿qué dijeron?". El reconocimiento de hablante pregunta "¿quién lo dijo?". La matemática parece la misma — embeddings más coseno — pero cada decisión de producción depende de un único número de EER.

Tipo: Build Lenguajes: Python Prerrequisitos: Fase 6 · 02 (Espectrogramas y Mel), Fase 5 · 22 (Modelos de Embedding) Tiempo: ~45 minutos

El Problema

Un usuario dice una frase secreta. Quieres saber: ¿es esta la persona que dice ser (verificación, 1:1), o es la primera persona en tu banco de inscripción (identificación, 1:N)? ¿O ninguna de las dos — es un hablante desconocido (open-set)?

Antes de 2018: GMM-UBM + i-vectors. EER razonable pero frágil ante el cambio de canal (teléfono vs laptop) y la emoción. 2018–2022: x-vectors (backbone TDNN entrenado con margen angular). 2022+: embeddings ECAPA-TDNN y WavLM-large. Para 2026 el campo está dominado por tres modelos y una métrica.

La métrica es el EER — Equal Error Rate (tasa de error igual). Ajusta tu umbral de decisión de modo que la Tasa de Falsa Aceptación = Tasa de Falso Rechazo. El punto de cruce es el EER. Se usa en cada artículo, cada leaderboard, cada licitación.

El Concepto

Pipeline de inscripción + verificación con embedding + coseno + EER

El pipeline. Inscripción: graba de 5 a 30 segundos del hablante objetivo; calcula un embedding de dimensión fija (192-d para ECAPA-TDNN, 256-d para WavLM-large). Verificación: obtén el embedding de la locución de prueba; calcula la similitud de coseno; compara contra un umbral.

ECAPA-TDNN (2020, todavía dominante en 2026). Emphasized Channel Attention, Propagation and Aggregation - Time-Delay Neural Network. Bloques de convolución 1D con squeeze-excitation, pooling por atención multi-cabeza, seguidos de una capa lineal a 192-d. Entrenado en VoxCeleb 1+2 (2.700 hablantes, 1,1 M de locuciones) con la pérdida Additive Angular Margin (AAM-softmax).

WavLM-SV (2022+). Haz fine-tuning de un backbone SSL WavLM-large preentrenado con pérdida AAM. Mayor calidad pero más lento — 300+ MB vs 15 MB.

x-vector (baseline). TDNN + statistics pooling. Clásico; aún útil en CPU / edge.

AAM-softmax. Softmax estándar con un margen m agregado en el espacio angular: cos(θ + m) para la clase correcta. Fuerza la separación angular entre clases. m=0.2 típico, escala s=30.

Puntuación

  • Coseno entre los embeddings de inscripción y de prueba. Decisión basada en umbral.
  • PLDA (LDA Probabilístico). Proyecta los embeddings en un espacio latente donde mismo-hablante vs hablante-diferente tiene una razón de verosimilitud en forma cerrada. Se agrega sobre el coseno para una reducción de EER de +10–20%. Estándar antes de 2020; ahora se usa solo en configuraciones closed-set.
  • Normalización de score. S-norm o AS-norm: normaliza cada score contra un cohort de medias y desviaciones estándar de impostores. Esencial para evaluación cross-domain.

Números que deberías conocer (2026)

Modelo VoxCeleb1-O EER Parámetros Throughput (A100)
x-vector (clásico) 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 segmentación + embedding 0.65% 6 M 100× RT
ReDimNet (2024) 0.39% 24 M 100× RT

Diarización

"Quién habló cuándo" en un clip con múltiples hablantes. Pipeline: VAD → segmentar → embedar cada segmento → clusterizar (aglomerativo o espectral) → suavizar fronteras. Stack moderno: pyannote.audio 3.1, que reúne segmentación de hablante + embedding + clustering detrás de una sola llamada. El DER SOTA de 2026 en AMI es ~15% (a la baja desde el 23% de 2022).

Constrúyelo

Paso 1: embedding de juguete a partir de estadí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

Lejos de SOTA — solo con fines didácticos. code/main.py lo usa como prueba de concepto sobre datos sintéticos de hablantes.

Paso 2: similitud de coseno + umbral

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

Paso 3: EER a partir de pares de similitud

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]

Devuelve (eer, threshold_at_eer). Reporta ambos.

Paso 4: producción con 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

Paso 5: diariza con 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}")

Úsalo

El stack de 2026:

Situación Elección
Verificación 1:1 closed-set, edge ECAPA-TDNN + umbral de coseno
Verificación open-set, nube WavLM-SV + AS-norm
Diarización (reuniones, podcasts) pyannote/speaker-diarization-3.1
Anti-spoofing (detección de replay / deepfake) AASIST o RawNet2
Embebido diminuto (KWS + inscripción) Titanet-Small (NeMo)

Trampas

  • Desajuste de canal. Modelo entrenado en VoxCeleb (video web) ≠ audio de llamada telefónica. Evalúa siempre en el canal objetivo.
  • Locuciones cortas. El EER se degrada bruscamente por debajo de 3 segundos de audio de prueba.
  • Inscripción con ruido. Una sola inscripción ruidosa envenena el anclaje. Usa ≥3 muestras limpias y promedia.
  • Umbral fijo entre condiciones. Ajusta siempre el umbral en un conjunto de dev separado del dominio objetivo.
  • Coseno sobre embeddings no normalizados. Normaliza por L2 primero; de lo contrario, la magnitud domina.

Entrégalo

Guarda como outputs/skill-speaker-verifier.md. Elige el modelo, el protocolo de inscripción, el plan de ajuste de umbral y las salvaguardas contra fraude.

Ejercicios

  1. Fácil. Ejecuta code/main.py. Construye "hablantes" sintéticos (perfiles de tono diferentes), inscribe, calcula el EER sobre una lista de prueba de 100 pares.
  2. Medio. Usa el ECAPA de SpeechBrain en 30 locuciones de VoxCeleb1 (5 hablantes × 6 cada uno). Calcula el EER con coseno vs PLDA.
  3. Difícil. Construye el pipeline completo inscripción → diarización → verificación con pyannote.audio. Evalúa el DER en el conjunto de dev de AMI.

Términos Clave

Término Lo que dice la gente Lo que realmente significa
EER La métrica principal Umbral donde Falsa Aceptación = Falso Rechazo.
Verificación 1:1 "¿Es esta Alice?"
Identificación 1:N "¿Quién está hablando?"
Open-set Desconocido posible El conjunto de prueba puede contener hablantes no inscritos.
Inscripción Registrar Calcular el embedding de referencia de un hablante.
AAM-softmax La pérdida Softmax con margen angular aditivo; fuerza la separación de clusters.
PLDA Puntuación clásica LDA Probabilístico; puntuación por razón de verosimilitud sobre los embeddings.
DER Métrica de diarización Diarization Error Rate — miss + falsa alarma + confusión.

Lecturas Adicionales

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).