File size: 5,246 Bytes
4d50a2c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# ProSDD-ASV5 β€” implementation notes

Plan: `speech-spoof-bench/docs/plans/2026-11-07-eleven-model-batch-arena-submission.md` (model #7).

## Source
- Code: github.com/ProSDD/codes @ `bdb80440cd946368e77f4b51f6ddf26c7443dc33` β€” `model_stage2realfake.py`
  (`ProSDDStage2`), `main_eval.py` (`inference_forward`), `data_utils_eval.py` (`pad`). No LICENSE file
  (β†’ `LICENSE.source`). `Evaluation_scores/ProSDD_*Trained/readme.txt` are empty: no per-utterance author scores.
- Weights: authors' Google Drive folder `1h250-Um5qWo-rpeOE6K_Gdeygy46k3xg`, file `ProSDD_Trained2024` (id `1AyD4f0RVVyDncqYbhi8QaP6-GFZ_ZGZl`),
  fetched with gdown into `benchmarks/_ckpt_dl/ProSDD/`; 1.27 GB, sha256 `c044a402d190f7d4bf75165e56890c21c0d80ffe4164ad741f4ea7d48356c437`. No extension; it is a
  zip-format torch save of a plain `OrderedDict` state_dict (431 tensors, no `module.` prefix) β†’ loads with
  `torch.load(weights_only=True)`; `ProSDD_Trained2024` here is a symlink. No license β†’ NOT re-hosted (meta links Drive).
- Backbone config vendored as `xlsr300m_config.json` (facebook/wav2vec2-xls-r-300m @ 1a640f3, copied from
  Forensics-XLSR-Wild); `Wav2Vec2Model(config)` instead of `from_pretrained` (no Hub download).

## Wrapper (`prosdd_asv5.py:ProSDDASV5`, `_net.py:ProSDDNet`)
- `_net.ProSDDNet.forward` = `inference_forward` verbatim: `ssl.feature_extractor` β†’ transpose β†’
  `ssl.feature_projection` (tuple[0]) β†’ latent truncated / zero-padded to `T_target=200` (64000 samples give 199
  frames, so 1 zero frame is always appended) β†’ `ssl.encoder(attention_mask=None)` β†’ mean over all 200 frames
  β†’ `cls_head` Linear(1024,512)-ReLU-Dropout-Linear(512,2). classifier_pool = mean (checkpoint has no
  `attn`/`attn_q` tensors).
- Preprocessing = `data_utils_eval.pad`: 64000 samples; longer β†’ centre crop `s=(n-64000)//2`; shorter β†’ zero pad
  `left=(64000-n)//2`, rest right. No amplitude normalisation, no resampling. Empty clip β†’ zeros (source would
  also give zeros). FP32, TF32 off (cuDNN + matmul).
- Score = `logits[:,1]` (class 1 = bona fide in the source) β†’ higher = bona fide.
- Strict load: checkpoint keys outside `ssl.*`/`cls_head.*` must equal the allow-list {`mask_embed`,
  `pros_ln.weight/bias`, `final_proj.weight/bias`} (Stage-I masked-prediction token, prosody-target LN,
  1024β†’448 speaker+prosody head; all training-only), asserted; then `load_state_dict(strict=True)`: 0 missing /
  0 unexpected. `ssl.masked_spec_embed` is loaded (it is a Wav2Vec2Model parameter) but unused at inference.
  The source's own `strict=False` load into `ProSDDStage2` reports 0 missing / 0 unexpected.
- Params: 315,964,546 total; **315,963,522 forward path** (minus masked_spec_embed, 1024) β†’ meta 315.963522.
- **Source parity** (`source_parity.py <src_dir>`): authors' ProSDDStage2 (from_pretrained swapped for the vendored
  config) + `main_eval.inference_forward` + `data_utils_eval.pad`, per clip, vs the wrapper batched, on 16 J-SPAW
  clips + a 20000-sample and a 139944-sample clip: max |Ξ” logit| = 5.7e-6 (batching noise).
- Unit tests `test_prosdd_asv5.py`: pad/crop, strict load, eval mode + param count, 199β†’200 frames, batch ==
  single (atol 1e-3), determinism, bona fide > spoof on J-SPAW clips. 8 passed.

## Batch sweep (`sweep.py`, 64000-sample input, 2026-11-07; run with the ASV19 checkpoint, same graph for both)
| card | best | notes |
|---|---|---|
| RTX 4070 Ti SUPER (idx 2) | 126.2 utt/s @24 | plateau 16–64 β†’ `batch_size = 24` |
| RTX 5060 Ti (idx 0) | 47.8 utt/s @4 | ~32 at bs β‰₯ 8 (card shared with another job during the sweep) β†’ 4 on 5060 |

## Sanity (PyTorch, RTX 5060 Ti idx 0)
- Full J-SPAW_LA: EER **5.135%** (2397 trials, 0 skipped). Not ~50 / not inverted.
- ASVspoof2019_LA quick check (`subset_eer.py 2000`, 2000 clips sampled with seed 0 from eval shard 0):
  EER 18.48% (same 2000-clip subset) vs paper 19.04% on full ASVspoof2019 LA eval.
- Training data (paper): Stage I LibriSpeech train-clean-100 + dev (bona fide only); Stage II ASVspoof 2024 (ASVspoof 5) train/dev,
  RawBoost 3. Arena ASVspoof sets are eval partitions β†’ no trained_on entries.

## TensorRT (`trt_prosdd_asv5.py`, sidecar `trt_prosdd-asv5.json`, TRT 10.16.1.11)
Adapted from Forensics-XLSR-Wild's script: `_ExportNet` returns the (B,2) logits; n_classes taken from the traced
output; ONNX file named per card (`prosdd-asv5_<GPU>.onnx`) so the two cards can export concurrently (only the
4070 copy kept). Build parity on 64 InTheWild clips; FP16 kept iff Spearman β‰₯ 0.9999.
| card | precision | best batch | utt/s (sweep) | build Spearman | J-SPAW EER | J-SPAW Spearman vs PT / max Ξ” |
|---|---|---|---|---|---|---|
| RTX 4070 Ti SUPER (idx 2) | FP32 (FP16 build Spearman 0.99977 < 0.9999) | 32 | 186.2 | 1.0 | 5.135 (=PT) | 1.000000 / 0.0109 |
| RTX 5060 Ti (idx 0) | **FP16** (build Spearman 0.99991) | 16 | 376.0 | 0.99991 | 5.197 (Ξ”EER +0.063) | 0.999991 / 0.118 |

Both cards pass (|Ξ”EER| ≀ 0.25, Spearman β‰₯ 0.999). Unused FP16 engines deleted. J-SPAW_LA submitted from PyTorch
scores (`results_pt/` β†’ `results/`); TRT runs in `results_trt2/`, `results_trt0/`; compare with `cmp_scores.py`.
Scripts: `run_pt.sh`, `run_trt_export.sh <gpu>`, `run_trt_jspaw.sh <gpu>`.