voice-detect.cpp - Parity report

June 29, 2026 ยท View on GitHub

This document records the numerical and end-to-end parity of the C++/ggml speaker-recognition path against the reference implementations (SpeechBrain for ECAPA-TDNN; the ONNX runtime graph for WeSpeaker / 3D-Speaker encoders). All comparisons are CPU, deterministic (dither off).

Status: scaffolding. The encoder graphs are not implemented yet, so the matrix below is the target each model must hit before it is marked PASS.

Parity gates

A stage or model passes when it meets ALL of:

  1. Embedding cosine similarity >= 0.9999 vs the reference L2-normalized speaker embedding for the same clip.
  2. Max absolute difference <= 1e-3 on every dumped intermediate tensor (fbank, encoder_out, embedding) vs scripts/gen_baseline.py.
  3. Identical verification verdict vs the reference on a fixed enroll/probe set: for every (clip_a, clip_b, threshold) triple the C++ verified flag matches the reference decision (same-speaker / different-speaker).

The per-stage golden compare is tests/parity.hpp (vdtest::compare, vdtest::cosine); tests/test_fbank.cpp is the first gate. Tests SKIP (exit 77) when the baseline GGUF env vars are unset, so CI without the reference venv never fails on them.

Model coverage matrix (target)

CheckpointFamilyarchembeddingFBankEmbedding cosineVerdictStatus
speechbrain/spkrec-ecapa-voxcelebECAPA-TDNNecapa_tdnn19280--TODO
WeSpeaker ResNet34ResNet34wespeaker_resnet3425680--TODO
3D-Speaker ERes2Net (base 200k)ERes2Neteres2net512801.000000-GREEN
3D-Speaker CAM++ (zh-cn 16k-common)CAM++campplus192801.000000-GREEN
superb/wav2vec2-base-superb-erwav2vec2 emotionwav2vec2_emotion--emotion_probs max|d| 4.8e-7dominant matchesGREEN
audeering/wav2vec2-large-robust-24-ft-age-genderwav2vec2 age/genderwav2vec2_age_gender--age 72.12==72.12; gender_probs max|d| 6e-8dominant matchesGREEN

Stage parity (target)

StageReferenceTensorGate
FBank front endtorchaudio.compliance.kaldi.fbank + CMNfbank [n_mels, T]max|d| <= 1e-3
Speaker encoderSpeechBrain / ONNX forwardencoder_out [d, T']max|d| <= 1e-3
EmbeddingL2-normalized speaker vectorembedding [embedding_dim]cosine >= 0.9999

The FBank stage is the load-bearing one: any deviation from the reference Kaldi options (POVEY window, 0.97 pre-emphasis, dither off, snip_edges, mel band edges, log floor, per-utterance CMN) propagates into a verification-threshold drift. See src/fbank.cpp.