DPDA-GA-TCM / implementation-notes.md
korallll's picture
Wrapper code (speech-spoof-bench)
552e29f verified
|
Raw History Blame Contribute Delete
3.93 kB

DPDA-GA-TCM — implementation notes

Plan: speech-spoof-bench/docs/plans/2026-10-29-mamba-batch-arena-submission.md (model #2).

Source

  • Code: github.com/ductuantruong/dpda_ga @ aab89614ee90252e10e5df35ec6aaa5ee2e179a7 (MIT, LICENSE.source): nets/conformer.py + nets/conformer_tcm.py → _net.py (edits listed in its header).
  • Weights: HF tuantd/dpda_ga @ 531554e0a1c28d6a6ccf22a355281e3d3acefeb5, pcgrad/conformer_tcm/best/best_{0..4}.pth (downloaded by benchmarks/_ckpt_dl/dl.py).
  • Hyper-parameters = main.py defaults: emb 144, heads 4, kernel 31, 4 encoders; checkpoint shapes confirm (LL 144×1024, ht_proj 144×36, depthwise conv k=31, encoder_blocks 0..3).
  • XLS-R 300M architecture built from xlsr2_300m.pt cfg (symlink to ../W2V2-AASIST/) via merge_with_parent; base weights not loaded (the checkpoint overwrites all 588 tensors).

Checkpoint averaging (make_avg.py → dpda_ga_tcm_avg4.pth)

  • best_4.pth is truncated on HF: 547,680,256 bytes vs 1,285,107,746 for the others, no ZIP central directory; torch.load → "failed finding central directory". Confirmed locally.
  • main.py eval averages n_average_model (default 5) n-best checkpoints, but sd = model.state_dict() aliases the live parameters, so the following model.load_state_dict(best_1) overwrites sd in place. Net effect: (2*b1 + b2 + … + b_{n-1}) / n; best_0 never contributes. Maintainer decision (2026-10-30): reproduce main.py exactly with the 4 intact files → (2*best_1 + best_2 + best_3) / 4, float32, main.py's summation order; verified bit-identical to literally running main.py's loop on a module holding the tensors. Int buffers (num_batches_tracked) get the truncated float quotient, as main.py's copy into a long buffer does (unused at eval).
  • The paper's numbers come from the 5-file version (2*b1+b2+b3+b4)/5, so small deviations from the paper are expected.

Wrapper decisions

  • Input: first 66,800 samples; shorter clips repeat-tiled (utils.pad). No resampling/normalisation.
  • Score = logits[:, 1] (label 1 = bonafide in utils.read_metadata; produce_evaluation_file).
  • .to(device) then .eval(); extract_feat's self.model.train() removed.
  • cuDNN TF32 disabled in load(): with it on, batched vs single scores differed by up to 2e-4 (XLS-R conv front-end); off → 1.7e-6. 4070 Ti SUPER speed unchanged (121.7 utt/s @24 both ways); 5060 Ti PyTorch drops 47.5 → 22.2 utt/s (bs 4), but that card runs TensorRT.
  • Params: 321,213,220 (incl. the frozen 10000×144 positional table, 1.44 M).

Batch sweep (sweep.py, 66,800-sample random input, 2026-10-30)

card TF32 off best TF32 on best
RTX 4070 Ti SUPER (idx 2) 121.7 utt/s @24 121.7 @24
RTX 5060 Ti (idx 3) 22.2 @4 47.5 @4
Raw: logs_sweep_g{2,3}_tf{0,1}.txt.

Sanity (full J-SPAW_LA, PyTorch, RTX 4070 Ti SUPER)

  • EER 12.90% (2397 trials, 0 skipped), ~110 utt/s. Comparable XLS-R models: DPDA-GA-AASIST 13.65, XLSR-SLS 8.70, W2V2-AASIST 26.17. Not flipped.

TensorRT (trt_dpda_ga_tcm.py, sidecar trt_dpda-ga-tcm.json, TRT 10.16)

Adapted from XLSR-MamBo's script (config block only). The class-token loop rewrite (_net.py edit 4) keeps the batch axis dynamic in the traced ONNX.

card precision best batch utt/s (sweep) build parity Spearman (64 ITW) J-SPAW EER J-SPAW Spearman vs PT / max Δ
RTX 4070 Ti SUPER (idx 2) FP32 (FP16 Spearman 0.99963 < 0.9999) 8 173.3 1.0000 12.8992 (=PT) 0.999998 / 0.031
RTX 5060 Ti (idx 3) FP16 16 362.2 0.99991 12.8992 (=PT) 0.999917 / 0.287
Both cards pass ( ΔEER = 0 ≤ 0.25, Spearman ≥ 0.999). J-SPAW_LA is submitted from PyTorch scores
(results_pt/); TRT runs in results_trt/ (idx 2) and results_trt3/ (idx 3). cmp_scores.py
does the comparison. The unused 4070 FP16 engine was deleted.