# Forensics-WavLM-Base — implementation notes Plan: `speech-spoof-bench/docs/plans/2026-11-07-eleven-model-batch-arena-submission.md` (model #1). Near-copy of `benchmarks/Forensics-XLSR-Wild/` (same author, same model.py except the backbone). ## Source - HF `eliya/forensics_0.3B_base_deepfake_classifier` @ `cfd11365b02167214dc3b392378809fef6ca3cfa`, CC-BY-NC-4.0 (model-card metadata; no LICENSE file → `LICENSE.source`). Downloaded `checkpoint_epoch_5.safetensors` (sha256 `803d2c57e705395d007b9deaf5b1a110965950f207df223e6dff5b6d298490fb`), `model.py`, `inference.py`, `config.json`, `README.md` into `benchmarks/_ckpt_dl/eliya__forensics_0.3B_base_deepfake_classifier/`; `checkpoint_epoch_5.safetensors` here is a hard link to it (pickled `.pt` not fetched). - `model.py` is byte-identical to the OC-Softmax repo's; vs XLSR-Wild only `WavLMModel.from_pretrained("microsoft/wavlm-large")` and the defaults `hidden_dim=320`, `aasist_gat_dims=(128, 64)` differ. `inference.py` identical to XLSR-Wild's apart from names. - `model.py` → `_net.py` (edits listed in its header). Backbone config vendored as `wavlm_large_config.json` (`microsoft/wavlm-large` @ `c1423ed94bb01d80a3f5ce5bc39f6026a0f4828c`, config only; no Hub weights loaded). - Upload commits are titled "Upload 3rd_ce_full_0304" (no ITW-selection hint, unlike XLSR-Wild). Card revisions: first lists SpeechFake, MD-CommonVoice, DFADD, CodecFake, ASVspoof2019-LA, EnvSDD; 2026-09-03 lists SpeechFake, ASVspoof2021, CommonVoice, Aishell; current says "proprietary and public". ## Wrapper decisions (`forensics_wavlm_base.py:ForensicsWavLMBase`) - Input = `inference.py:load_audio` after mono/16 kHz: peak-normalise `wav/(max|wav|+1e-8)`, centre crop `s=(cur-n)//2` if > 80000, repeat-pad if shorter. Empty clip → zeros (not in source). No resampling. FP32, cuDNN TF32 off. - Score = the single logit (label 1 = real, sigmoid = P(real)); higher = bona fide. - Load: 655 checkpoint tensors; the 4 `projection.{0,2}.{weight,bias}` (training-only head, not in model.py) are dropped via an asserted allow-list, then `strict=True` (0 missing / 0 unexpected; 651 tensors all `torch.equal` to the checkpoint). The pos_conv weight-norm is stored as `parametrizations.weight.original0/1`, which is the transformers 5.3 module layout → loaded strictly (test `test_pos_conv_weight_norm_loaded`), not left at init. - Params: 317,257,863 (317.26 M). - **Source parity** (`source_parity.py`, log `logs_source_parity.txt`): the authors' `model.py` with its real `WavLMModel.from_pretrained` path redirected to a local dir (vendored config + the checkpoint's own `wavlm.*` tensors), then the full checkpoint loaded `strict=False` like `inference.py` (reports exactly the 4 `projection.*` unexpected, 0 missing), input via `inference.py:load_audio` (torchaudio) vs our wrapper on 20 In-the-Wild clips (10 bona fide / 10 spoof): max |Δ| = 0.0 (bit-identical), 5060 Ti. ## Batch sweep (`sweep.py`, 80000-sample input, 2026-11-07; `logs_sweep_g{2,0}.txt`) | card | best | notes | |---|---|---| | RTX 4070 Ti SUPER (idx 2) | 84.7 utt/s @24 | plateau 8–64 | | RTX 5060 Ti (idx 0) | 26.4 utt/s @24 | flat 4–64 (21.8 @1) → no per-card override | ## Sanity (PyTorch, 5060 Ti idx 0) - Full J-SPAW_LA: EER **0.125%** (2397 trials, 0 skipped), ~25.6 utt/s. Not inverted. ## TensorRT (`trt_forensics_wavlm_base.py`, sidecar `trt_forensics-wavlm-base.json`, TRT 10.16.1.11) Copied from XLSR-Wild's script (only names changed). FP16 engine outputs NaN (parity Spearman −0.11, WavLM attention / relative-position bias overflows in half precision) → FP32 on both cards. | card | precision | best batch | utt/s (sweep) | build parity Spearman / max Δ (64 ITW) | J-SPAW EER | J-SPAW Spearman vs PT / max Δ | |---|---|---|---|---|---|---| | RTX 4070 Ti SUPER (idx 2) | FP32 (FP16 NaN) | 32 | 124.7 | 0.99991 / 0.0078 | 0.125 (=PT) | 0.999187 / 0.0424 | | RTX 5060 Ti (idx 0) | FP32 (FP16 NaN) | 32 | 80.4 | 0.99977 / 0.0018 | 0.125 (=PT) | 0.999831 / 0.0121 | Both cards pass (|ΔEER| = 0 ≤ 0.25, Spearman ≥ 0.999; the 4070 J-SPAW Spearman 0.99919 is closer to the bar than XLSR-Wild's 0.99999 — WavLM's gated relative-position bias drifts more in TRT FP32). End-to-end J-SPAW runner throughput: PT 5060 ~25.6, TRT 4070 ~104, TRT 5060 ~72 utt/s. FP16 engines deleted. J-SPAW_LA is submitted from PyTorch scores (`results_pt/` → `results/`); TRT runs in `results_trt2/`, `results_trt0/`; `cmp_scores.py` compares. Scripts: `run_pt.sh `, `run_trt_export.sh`, `run_trt_jspaw.sh `. ## meta.yaml - No paper block → Unpublished tier (as XLSR-Wild). `checkpoint` holds a `REHOST_COMMIT` placeholder: fill with the commit of the re-hosted `SpeechAntiSpoofingBenchmarks/Forensics-WavLM-Base` repo. - `trained_on`: DFADD, ASVspoof2021_DF, ASVspoof2019_LA, InTheWild (plan: same set as XLSR-Wild). Unlike XLSR-Wild there is no "itw_winner" hint in the upload commits; ITW is kept per the plan (undisclosed mix).