Speaker Verification Quickstart
This quickstart shows the smallest complete speaker-verification evaluation workflow in Hyperion. It creates deterministic synthetic embeddings, scores every enrollment/test pair with cosine similarity, represents the labeled trials with Hyperion tables, and computes equal error rate (EER).
It is intentionally model- and audio-free. In a real system, replace the synthetic embeddings with vectors extracted by an embedding model; the trial, score, and evaluation steps stay the same.
Requirements
Install Hyperion and its regular Python dependencies as described in
Getting Started. No GPU, dataset download, or egs/ recipe is needed
for this example.
Create embeddings and trials
The code below creates four speakers. Each speaker has one enrollment embedding and three test embeddings. Test embeddings are generated near their speaker’s enrollment representation, so target trials should score higher than non-target trials.
import numpy as np
from hyperion.np.metrics import compute_eer
from hyperion.utils import TrialKey, TrialScores
from hyperion.utils.math_funcs import cosine_scoring
rng = np.random.default_rng(1234)
num_speakers = 4
embedding_dim = 192
tests_per_speaker = 3
model_ids = np.array([f"spk-{i}" for i in range(num_speakers)])
speaker_centers = rng.normal(size=(num_speakers, embedding_dim))
enroll_embeddings = speaker_centers + 0.10 * rng.normal(
size=speaker_centers.shape
)
test_speaker_ids = np.repeat(model_ids, tests_per_speaker)
test_embeddings = np.repeat(speaker_centers, tests_per_speaker, axis=0)
test_embeddings += 0.10 * rng.normal(size=test_embeddings.shape)
segment_ids = np.array([f"test-{i}" for i in range(len(test_embeddings))])
# Rows are enrollment models; columns are test segments.
is_target = model_ids[:, None] == test_speaker_ids[None, :]
key = TrialKey(
model_set=model_ids,
seg_set=segment_ids,
tar=is_target,
non=~is_target,
)
Score and evaluate
cosine_scoring L2-normalizes each input vector internally and produces a
score matrix of shape (num_models, num_test_segments). TrialScores
keeps that matrix aligned with the trial key, then get_tar_non extracts
the target and non-target score vectors needed by the metric.
score_matrix = cosine_scoring(enroll_embeddings, test_embeddings)
scores = TrialScores(
model_set=model_ids,
seg_set=segment_ids,
scores=score_matrix,
score_mask=np.ones(score_matrix.shape, dtype=bool),
)
target_scores, non_target_scores = scores.get_tar_non(key)
eer = compute_eer(target_scores, non_target_scores)
print(f"Score matrix: {scores.scores.shape}")
print(f"Target trials: {len(target_scores)}")
print(f"Non-target trials: {len(non_target_scores)}")
print(f"EER: {eer:.2%}")
The score matrix has four rows and twelve columns: one row per enrollment speaker and one column per test segment. There are twelve target trials and thirty-six non-target trials. With this deliberately well-separated synthetic data, the EER should be close to zero.
What the tables mean
TrialKeyidentifies every valid trial and labels it as target or non-target. Its row order ismodel_setand its column order isseg_set.TrialScoresstores a score for the same ordered model/segment grid. Itsscore_masksays which entries are present.compute_eercompares the target-score distribution with the non-target-score distribution. Lower EER is better.
Next steps
Learn the full vocabulary and alignment rules in Hyperion Data Model.
Read Working With Trial Tables for dense/sparse trial tables and on-disk formats.
Replace the synthetic vectors with real embeddings in the extraction and scoring guides added in the next documentation phase.