TAI BUI
Back to curriculum
BuildPython

Speaker Recognition & Verification

ASR asks "what did they say?" Speaker recognition asks "who said it?" The math is nearly identical: embeddings plus cosine similarity. Every production decision, though, comes down to a single EER number.

TIME: ~45 minutesPREREQUISITES: Phase 6 · 02 (Spectrograms & Mel), Phase 5 · 22 (Embedding Models)

Learning Objectives

  • Distinguish speaker verification (1:1) from identification (1:N) and open-set recognition, and state what Equal Error Rate (EER) measures.
  • Build the enroll → embed → cosine-similarity → threshold pipeline, and explain why embeddings must be L2-normalized before scoring.
  • Compare x-vector, ECAPA-TDNN, and WavLM-SV on architecture, AAM-softmax training loss, and the speed/accuracy tradeoff, and pick a scoring method (cosine, PLDA, or AS-norm) for a given deployment target.
  • Run a diarization pipeline (VAD, segmentation, embedding, clustering) to answer "who spoke when," and read a Diarization Error Rate (DER) figure.
  • Avoid the production failure modes covered in this lesson: channel mismatch, short utterances, noisy enrollment, fixed thresholds, and setting a production threshold from EER instead of your own cost of false accepts versus false rejects.

The Problem

A user says a passphrase. You want to know one of three things: is this the person they claim to be (verification, 1:1)? Is it the first match in your enrollment bank (identification, 1:N)? Or is this someone you've never enrolled at all (open-set)?

The field got here in three hops. Pre-2018: GMM-UBM plus i-vectors, decent in the lab but fragile to channel shift (phone vs laptop) and emotion. 2018–2022: x-vectors, a TDNN (time-delay neural network) backbone trained with angular margin. 2022 onward: ECAPA-TDNN and WavLM-large embeddings took over. By 2026, three models and one metric dominate everything that matters.

That metric is EER, Equal Error Rate. Set your decision threshold so the False Accept Rate equals the False Reject Rate; the point where those two curves cross is EER. It shows up in every paper, every leaderboard, and every procurement call you'll ever sit through.

Warm-up0/3
1. Two embedding vectors point in nearly the same direction, but one is ten times longer than the other. Which similarity measure ignores that length difference?Warm-up
Two arrows from a common origin pointing in nearly the same direction, a short one labelled a and one ten times longer labelled b.
Schematic drawn to scale: b is ten times as long as a, and the angle between them is small.
2. A detector accepts an input when its score is above a threshold. What happens to the two error types if you raise that threshold?Warm-up
Two overlapping score curves, one for inputs that should be rejected and one for inputs that should be accepted, with a vertical threshold line and a dashed copy to its right with an arrow labelled raised.
Illustrative score curves, not measured data.
3. You have thousands of unlabeled feature vectors and want to group the ones that are similar, without any labels. Which family of methods fits?Warm-up
A scatter of 72 unlabeled grey points in a square panel, with a note that the goal is to group the similar ones.
Synthetic 2-D points for illustration; real feature vectors have many more dimensions.
Check: The Problem0/6
1. What does Equal Error Rate (EER) measure?Define
Two overlapping score curves, one for different-speaker trials and one for same-speaker trials, over a score axis from 0 to 1, with a note that the reported metric EER is unknown.
Illustrative score curves, not measured data.
2. What is speaker verification?Define
A login screen showing the typed user ID alice and a recorded passphrase clip, an arrow into a voice system box, and an arrow to a result box marked with a question mark.
Example scenario; the name is a placeholder.
3. What is speaker identification?Define
A test clip waveform with speaker unknown goes into a scoring box and an output box marked with a question mark, above a row of five enrolled names: alice, bob, carol, dave, eve.
The five names are the synthetic speakers in the lesson's main.py.
4. What makes a recognition task open-set?Define
Three enrolled names, alice, bob and carol, above four incoming test clips whose speakers are marked with question marks.
Schematic; names are placeholders.
5. Why did x-vectors and later ECAPA-TDNN replace the older GMM-UBM plus i-vector approach?Explain
Three stacked rows: pre-2018 GMM-UBM plus i-vectors, 2018 to 2022 x-vectors, and 2022 onward ECAPA-TDNN and WavLM-large embeddings.
Eras as given in the lesson's Problem section.
6. Why can EER serve as a single number for comparing models, even though error rates change as the threshold moves?Explain
A table of false accept and false reject rates for model A and model B at thresholds 0.4, 0.5 and 0.6, where each rate changes with the threshold.
Illustrative Gaussian score models, not real systems.

The Concept

Enrollment + verification pipeline with embedding + cosine + EER

The pipeline. Enrollment: record 5–30 seconds of the target speaker, then compute a fixed-dimension embedding (192-d for ECAPA-TDNN, 256-d for WavLM-large). Verification: embed the test utterance, compute cosine similarity against the enrolled embedding, and compare the result to a threshold.

ECAPA-TDNN (2020, still dominant 2026). Emphasized Channel Attention, Propagation and Aggregation - Time-Delay Neural Network. 1D conv blocks with squeeze-excitation, multi-head attention pooling, followed by a linear layer to 192-d. Trained on VoxCeleb 1+2 (2,700 speakers, 1.1M utterances) with Additive Angular Margin loss (AAM-softmax).

WavLM-SV (2022+). Fine-tunes a pretrained WavLM-large SSL (self-supervised learning) backbone with AAM loss. Higher quality, but slower: 300+ MB versus 15 MB.

x-vector (baseline). TDNN plus statistics pooling. The classic approach, and still useful on CPU or at the edge.

AAM-softmax. Standard softmax with an added margin m in the angular space: cos(θ + m) for the correct class. This forces inter-class angular separation. Typical values are m=0.2 and scale s=30.

Scoring

  • Cosine between enrollment and test embeddings. A threshold-based decision, nothing fancier.
  • PLDA (Probabilistic LDA). Projects embeddings into a latent space where same-speaker vs different-speaker has a closed-form likelihood ratio. Layered on top of cosine, it buys a +10–20% EER reduction. Standard before 2020; now used only in closed-set setups.
  • Score normalization. S-norm or AS-norm normalize each score against a cohort of imposter means and stds, essential once you're evaluating across domains.

Numbers you should know (2026)

ModelVoxCeleb1-O EERParamsThroughput (A100)
x-vector (classic)3.10%5 M400× RT
ECAPA-TDNN0.87%15 M200× RT
WavLM-SV large0.42%316 M20× RT
Pyannote 3.1 segmentation + embedding0.65%6 M100× RT
ReDimNet (2024)0.39%24 M100× RT

Diarization

"Who spoke when" in a multi-speaker clip. Pipeline: VAD (voice activity detection, trims silence before anything else runs) → segment → embed each segment → cluster (agglomerative or spectral) → smooth boundaries. The modern stack is pyannote.audio 3.1, which bundles speaker segmentation, embedding, and clustering behind one call. 2026 SOTA DER (Diarization Error Rate: missed speech plus false alarms plus speaker confusion) on the AMI meeting-corpus benchmark sits around 15%, down from 23% in 2022.

Check: The Concept0/14
1. What is enrollment in a speaker verification system?Define
A waveform of alice speaking for 5 to 30 seconds goes into an enrollment box, then into a table with a user column reading alice and a stored column marked with a question mark.
Schematic; alice is a placeholder name.
2. What is AAM-softmax?Define
A circle with two class directions labelled A and B from the center, an embedding arrow x between them, and an arc marking the angle theta between x and class A, beside a note naming AAM-softmax as the loss in use.
Schematic; angles are drawn for illustration.
3. What is PLDA in speaker verification?Define
Two embedding arrows e1, the enrollment embedding, and e2, the test embedding, feed a PLDA box whose output is marked with a question mark.
Schematic.
4. What does Diarization Error Rate (DER) count?Define
Two timelines over 30 seconds, a reference with speakers A and B and a system output whose segments differ slightly, with the DER value shown as unknown.
Illustrative timelines with made-up times, not real system output.
5. Which loss function are ECAPA-TDNN and WavLM-SV trained with?Define
Two boxes: ECAPA-TDNN trained on VoxCeleb 1+2 with 2,700 speakers and 1.1M utterances, and WavLM-SV fine-tuned from a self-supervised WavLM-large, above a note that their training loss is unknown.
Training facts from the lesson's Concept section.
6. Which pair of embedding sizes does the lesson give for ECAPA-TDNN and WavLM-large?Define
Two boxes, ECAPA-TDNN with a linear layer and WavLM-large, each followed by an embedding-size label with an unknown dimension.
Model names from the lesson's Concept section.
7. On the AMI meeting benchmark, what DER does the lesson give for 2026, and what was it in 2022?Define
An empty bar chart axis from 0 to 40 percent with two dashed bars for 2022 and 2026 on the AMI benchmark, both unknown.
Bar heights hidden; the filled chart is in the explanation.
8. What does the margin m in AAM-softmax do during training?Explain
A number line of angles in radians marking a training sample at 0.55 from its own speaker class and 0.80 from another class, with the margin hyperparameter m, typical value 0.2 and scale 30, noted and its effect unknown.
Angles are illustrative values for one imagined sample.
9. Why is the x-vector still useful when ECAPA-TDNN and WavLM-SV reach lower EER?Explain
Three model cards, x-vector with TDNN plus statistics pooling, ECAPA-TDNN with attention pooling and WavLM-SV on a WavLM-large backbone, above a table of their EERs: 3.10, 0.87 and 0.42 percent.
Descriptions and EERs follow the lesson's Concept section and numbers table.
10. What does PLDA scoring add on top of cosine similarity?Explain
A chain of boxes: enrollment and test embeddings, cosine, PLDA layered on top, then a threshold.
Layering as described in the lesson's Scoring section.
11. What do S-norm and AS-norm do to a verification score?Explain
A raw score from cosine enters a box named S-norm or AS-norm, and the output is marked with a question mark.
Schematic.
12. Why does diarization run voice activity detection (VAD) before segmenting and embedding?Explain
A synthetic waveform with three bursts and quiet gaps between them, above a row of three boxes VAD, segment, embed joined by arrows.
Synthetic envelope for illustration, not real audio.
13. Comparing ECAPA-TDNN with WavLM-SV large in the numbers table, how do accuracy and speed change?Apply
A table with rows ECAPA-TDNN and WavLM-SV large and columns EER, params and throughput on an A100, every value unknown.
Values hidden here; the solved table is in the explanation.
14. A diarizer finds every stretch of speech and raises no false alarms, yet its DER is still high. Which DER component explains it?Analyze
A reference timeline with speakers A and B alternating across 30 seconds and a system timeline of grey blocks covering the same speech spans with speaker labels shown as question marks.
Illustrative timelines with made-up times.

Build It

Step 1: toy embedding from MFCC statistics

Before reaching for a pretrained embedder, build the pipeline shape with something you can trace by hand. This toy embedding isn't SOTA by any measure, it exists purely to teach the shape of the pipeline, and code/main.py uses it as a proof of concept on synthetic speaker data.

python
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

Step 2: cosine similarity + threshold

With an enrollment embedding and a test embedding in hand, score how alike they are and turn that score into an accept-or-reject call:

python
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

Step 3: EER from similarity pairs

Collect same-speaker and different-speaker scores from a trial list, then sweep every possible threshold to find where false accepts and false rejects cross:

python
def eer(same_scores, diff_scores):
    thresholds = sorted(set(same_scores + diff_scores))
    best_gap = float("inf")
    best_fa, best_fr, best_t = 1.0, 0.0, thresholds[0] if thresholds else 0.0
    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)
        gap = abs(fa - fr)
        if gap < best_gap:
            best_gap = gap
            best_fa, best_fr, best_t = fa, fr, t
    return (best_fa + best_fr) / 2, best_t

Returns (eer, threshold_at_eer). Report both.

Step 4: production with SpeechBrain

The toy pipeline above proves the shape works. For anything production-bound, swap in a real pretrained embedder instead of hand-rolled MFCC stats:

python
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

Step 5: diarize with pyannote

None of the above helps once a clip has more than one speaker talking at once. For that, reach for a diarization pipeline instead of per-utterance verification:

python
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}")
Check: Build It0/5
1. The toy verify() has threshold=0.75, and the SpeechBrain snippet uses score > 0.25. Why do the two cutoffs differ?Apply
Two code lines: a toy verify function with threshold=0.75 and a SpeechBrain snippet deciding verdict = score > 0.25.
Lines copied from the lesson's Build It code.
2. What does cosine([3, 4], [6, 8]) return in the lesson's cosine() function?Apply
The lesson's cosine function in five lines and a call cosine([3, 4], [6, 8]) whose result is marked with a question mark.
Code copied from the lesson's Build It Step 2.
3. embed_mfcc_stats uses 13 MFCCs per frame and returns mean + std. How many dimensions does the embedding of one clip have?Apply
Two grids of MFCC values, 13 columns wide, one with 8 frame rows for a short clip and one with 18 for a longer clip, above a note that the embedding length of one clip is unknown.
Schematic grids; frame counts are illustrative.
4. same_scores=[0.9, 0.8, 0.4] and diff_scores=[0.5, 0.3, 0.2]. Using the lesson's eer() definitions of fa and fr, what are the rates at threshold t=0.5?Apply
A score line from 0 to 1 with three same-speaker scores 0.9, 0.8 and 0.4 as filled dots, three different-speaker scores 0.5, 0.3 and 0.2 as open dots, a dashed threshold at 0.5, and the lesson's two lines that define fr and fa.
Scores from the question; code copied from the lesson's eer().
5. The SpeechBrain enrollment line stacks the embeddings of several clean clips and calls .mean(0). What does enroll hold afterwards?Apply
Four rows of bars, one per clean clip's embedding, above the SpeechBrain enrollment code that stacks them and calls mean(0), with what enroll holds marked unknown.
Bar heights are random illustration; code adapted from the lesson with the original comment left out.

Use It

Match the situation to the model. Here's the 2026 stack:

SituationPick
Closed-set 1:1 verification, edgeECAPA-TDNN + cosine threshold
Open-set verification, cloudWavLM-SV + AS-norm
Diarization (meetings, podcasts)pyannote/speaker-diarization-3.1
Anti-spoofing (replay / deepfake detection)AASIST or RawNet2
Tiny embedded (KWS + enrollment)Titanet-Small (NeMo)
Check: Use It0/4
1. You need open-set verification in the cloud, and separately a tiny embedded device doing keyword spotting plus enrollment. Which picks does the lesson's table give?Apply
A two-row table with situations open-set verification in the cloud and tiny embedded keyword spotting plus enrollment, whose picks are unknown.
Situation wording from the lesson's Use It table; picks hidden.
2. A product team wants to detect replayed recordings and deepfake voices. Which model family does the lesson pick?Apply
Three audio sources, a live speaker, a replayed recording and a synthetic deepfake voice, feeding one box whose model family is marked with a question mark.
Schematic.
3. You must run closed-set 1:1 verification on a low-power edge device, and CPU speed matters more than the last bit of accuracy. Which design fits?Design
Three boxes: a low-power edge device, closed-set 1:1 verification as the task, and a priority of CPU speed above the last bit of accuracy.
Constraints from the question.
4. A cloud service verifies users on phone and laptop mics, and some test callers were never enrolled. You must report scores across domains. Which design fits?Design
Two caller sources, a phone mic and a laptop mic, each with enrolled and never-enrolled callers, feeding a box marked with a question mark for the design, with a note that scores must be reported across both domains.
Scenario from the question.

Pitfalls

  • Channel mismatch. A model trained on VoxCeleb (web video) ≠ phone-call audio. Always evaluate on the channel you'll actually deploy on.
  • Short utterances. EER degrades sharply below 3 seconds of test audio.
  • Enrollment with noise. One noisy enrollment sample poisons the whole anchor. Use ≥3 clean samples and average them.
  • Fixed threshold across conditions. Always tune the threshold on a held-out dev set drawn from the target domain.
  • Cosine on non-normalized embeddings. L2-normalize first, or the magnitude will dominate the score instead of the direction.
Check: Pitfalls0/9
1. Why must embeddings be L2-normalized before cosine scoring?Explain
A pipeline of boxes: embed, L2-normalize marked as required first, cosine, then an accept decision.
Order as given in the lesson's Pitfalls section.
2. Why does the lesson say to evaluate on the channel you will actually deploy on?Explain
Two boxes joined by an arrow: training data of one kind of audio and deployment on the audio the product gets, each with an unknown EER measured there.
Schematic.
3. What do short test utterances and a noisy enrollment sample do, and what is the fix?Explain
Two panels: a test utterance 2 seconds long on a 0 to 4 second axis, and an enrollment made from a single noisy clip, above a note that the effect and safeguard are unknown.
Illustrative 2 s clip length; the clip waveform is synthetic.
4. What does the lesson recommend instead of one fixed similarity threshold for all conditions?Explain
Three rows of overlapping green and red score curves labelled quiet room, noisy street and phone call, each with a question mark where the threshold would go.
Illustrative curves, not measured data.
5. A model scores 0.9% EER on VoxCeleb but misses many real customers on a call-center phone line. Which pitfall is the most likely cause?Analyze
Two result rows for one model: a short bar for 0.9 percent EER on the VoxCeleb test set, and a dashed bar for many real customers rejected on a call-center phone line.
Dashed bar is qualitative: the stem gives no number for it.
6. A user enrolled with one clip recorded in a loud cafe. Now they are rejected often at home, even on clear speech. What is the most likely cause?Analyze
Two boxes joined by an arrow: an enrollment day with one clip recorded in a loud cafe, and later days at home with clear speech, where the user is often rejected.
Scenario from the question.
7. Scores from a cosine verifier look inflated for loud, long clips from any speaker, so impostors get accepted. What was most likely skipped?Analyze
Two bars of verifier scores for impostor clips against a dashed accept threshold: a typical impostor clip below it and a loud, long impostor clip above it.
Schematic; bar heights are not measured.
8. A bank's KYC flow and a phone's voice unlock use the same model with the same published EER. Why do they set different thresholds?Analyze
Two cards, a bank KYC flow and a phone voice unlock, each using the same model and the same published EER, with the threshold unknown.
Scenario from the question and the lesson's Author's Note.
9. You are launching voice unlock for a phone and must choose a decision threshold. Which approach follows the lesson's advice?Design
A 2 by 2 grid of outcomes for a voice unlock attempt: owner unlocks is correct, owner locked out is a false reject, stranger unlocks is a false accept, stranger locked out is correct, with the threshold and cost of each error unknown.
Outcome grid; no costs are given.

Ship It

Save it as outputs/skill-speaker-verifier.md. Nail down the model choice, the enrollment protocol, the threshold-tuning plan, and the fraud safeguards.

Author's Note

The number every paper reports is EER, but it's rarely the number you should ship. EER is the point where a false accept costs exactly as much as a false reject, and outside of a leaderboard, that's almost never true. Take a banking KYC flow: it would rather turn away ten legitimate customers than let one impostor through, which pushes the threshold well past EER toward zero false accepts, friction and all. A voice-unlock feature on a phone runs the opposite calculation. Annoying the owner every morning costs more than the rare false accept, so the threshold sits lower and the experience stays smooth. Use EER to compare models against each other, not to set your production cutoff. Pick that cutoff from your own cost of a false accept versus a false reject, measured on your own channel, not VoxCeleb's.

Exercises

  1. Easy. Run code/main.py. Builds synthetic "speakers" (different tone profiles), enrolls, computes EER on a 100-pair trial list.
  2. Medium. Use SpeechBrain ECAPA on 30 VoxCeleb1 utterances (5 speakers × 6 each). Compute EER with cosine vs PLDA.
  3. Hard. Build the full enroll → diarize → verify pipeline with pyannote.audio. Evaluate DER on AMI dev set.

Key Terms

TermWhat people sayWhat it actually means
EERThe headline metricThreshold where False Accept = False Reject.
Verification1:1"Is this Alice?"
Identification1:N"Who is speaking?"
Open-setUnknown possibleTest set can contain unenrolled speakers.
EnrollmentRegisteringComputing a speaker's reference embedding.
AAM-softmaxThe lossSoftmax with additive angular margin; forces cluster separation.
PLDAClassic scoringProbabilistic LDA; likelihood-ratio scoring on top of embeddings.
DERDiarization metricDiarization Error Rate — miss + false alarm + confusion.

Further Reading

Code

Ships With