|
Download implementation-notes.md from SpeechAntiSpoofingBenchmarks/LaST-Net: direct link, hf CLI and curl.
- Browser
- Download file 6.08 kB
-
https://huggingface.co/SpeechAntiSpoofingBenchmarks/LaST-Net/resolve/main/implementation-notes.md
- Command line
-
hf download hf://SpeechAntiSpoofingBenchmarks/LaST-Net/implementation-notes.md
-
curl -L -o implementation-notes.md https://huggingface.co/SpeechAntiSpoofingBenchmarks/LaST-Net/resolve/main/implementation-notes.md
6.08 kB
| # LaST-Net β implementation notes | |
| Plan: `speech-spoof-bench/docs/plans/2026-11-09-thirteen-model-batch-arena-submission.md` (model #3). | |
| ## Source | |
| - Code: github.com/JunXue-tech/LaST-Net @ `3838f0c7108313f771f6c1e208ac8f1e3bde60b0` (MIT), clone at | |
| `benchmarks/_ckpt_dl/gh__JunXue-tech__LaST-Net/`: `model.py` (LaSTNet), `infer.py` (single-file scoring), | |
| `eval.py` (dataset eval + `load_model`), `load_dataset.py` (`read_audio`, `crop_or_repeat`). | |
| - Weights: HF JunXueTech/LaST-Net @ `7b18ff0abff078c60745aaaaf5165e3f9542fa1b` `best.pt` (3.8 GB, sha256 | |
| `8fee6178d19b398fac9e1b1bfcf13720c4eb5ff722b5b52e4bc698e0de0fa28b`): epoch 52, keys model / optimizer / epoch / | |
| global_step / best_mean_dev_eer (1.383) / args / rng_states / initialization (`xlsr_only`) / pretrained_path / | |
| architecture (`LCF24_LengthSelectiveKernelResidualBiFPN_v18b`) / training_view / consistency. No license β linked. | |
| - Extraction (`extract_state.py`, run once with `python -I`): plain `weights_only=True` fails on the numpy RNG states | |
| (`numpy.dtype[uint32]`); loaded with `weights_only=True` + `torch.serialization.add_safe_globals` limited to | |
| `numpy.core.multiarray._reconstruct`, `numpy.ndarray`, `numpy.dtype`, `_codecs.encode` and the uint32/float64/int64/ | |
| float32 dtype classes (the pickle's only globals, checked with `pickletools` first). `ck["model"]` (522 fp32 tensors) | |
| β `_ckpt_dl/JunXueTech__LaST-Net/last_net_model.safetensors` (sha256 | |
| `3ec3ff415654931474fe0949b5ed1030a84b8c0b2ee28a4fce37f238e952b264`, metadata: train args), symlinked here. | |
| Train args: router_temperature 0.8, uniform_floor 0.05, layer_dropout 0.1 (= constructor defaults). | |
| - XLS-R architecture: `xlsr2_300m_model_cfg.json` copied from DeepFense-CodecFake-W2V2-MLP (model cfg of fairseq | |
| `xlsr2_300m.pt`); `Wav2Vec2Model.build_model(merge_with_parent(...))` with fairseq 0.12.2 β the 3.6 GB base pickle | |
| is not needed (all its tensors are replaced by best.pt anyway, as the authors' README says). | |
| - Paper: none found (web search "LaST-Net Length-Aware Layer and Scale-Adaptive Temporal Network", 2026-11-09) β | |
| unpublished tier. | |
| ## Wrapper (`last_net.py:LaSTNetModel`, `_net.py:LaSTNet`) | |
| - `_net.py` = model.py verbatim except: SSLModel built from the vendored cfg; forward returns logits only; | |
| `nn.init.zeros_` calls dropped. extract_feat = `model(wav, mask=False, features_only=True, layer=23)["layer_results"]`. | |
| - **Length-aware input**: the model takes `available_seconds` (B,) besides the waveform; `duration_features` builds | |
| [requested/6, log1p(requested)/log 7, available/requested] with requested = 4.0. As infer.py/eval.py: | |
| available = min(len, 64000)/16000 (so a repeated short clip is scored differently from the same tiled audio | |
| given as a full 4 s clip β unit-tested). | |
| - **Window**: authors' 4 s = `seconds * 16000` = **64000** samples (not 64600). Prefix crop; shorter β `np.tile` to | |
| ceil(64000/len) copies, cropped. No amplitude normalisation, no resampling. Empty clip β zeros, available 0 | |
| (clamped to 1 ms in the model; the source raises instead). | |
| - Score = `log_softmax(logits)[:, 1]` (= `bonafide_log_score`; labels spoof 0 / bona fide 1) β higher = bona fide. | |
| - FP32, TF32 off for cuDNN + matmul (authors' eval.py turns TF32 matmuls on; strict FP32 keeps scores batch-independent). | |
| - Strict load: all 522 tensors, 0 missing / 0 unexpected. Inference-path check (backward on a forward pass): exactly | |
| 10 tensors get no gradient = `UNUSED_ON_FORWARD` (mask_emb; quantizer.vars / weight_proj.*; project_q.*; | |
| final_proj.*; encoder.layer_norm.* β skipped by fairseq when `layer` is given). Asserted in the tests. | |
| - Params: 318,910,608 loaded; **316,955,664 on the inference path** β meta 316.955664. | |
| ## Checks | |
| - `test_last_net.py`: crop/repeat + available seconds, empty clip, duration-feature formula, strict load, | |
| eval mode + param counts, unused set == allow-list, available-duration effect, batch == single (atol 1e-4), | |
| determinism, bona fide > spoof on J-SPAW clips. **11 passed** (idx 3). | |
| - **Source parity** (`source_parity.py <clone>` β `logs_source_parity.txt`): authors' `model.LaSTNet` (fairseq | |
| loader patched to the vendored cfg; same safetensors tensors, strict=True) + `read_audio` / `crop_or_repeat` + the | |
| infer.py loop per clip, vs the wrapper batched, on 16 J-SPAW_LA clips (8 bona fide / 8 spoof) + a 20000, a 5000 and | |
| a 102912-sample clip: max |Ξ| **1.57e-5 at 4 s**, 1.78e-5 at 6 s (authors' default). (Whole-utterance "window off" | |
| is not an authors' mode: infer/eval only accept 1β6 s.) | |
| - Batch sweep (`sweep.py`, 64000 samples, FP32): RTX 4070 Ti SUPER (idx 2) 117.8 utt/s @24 (plateau 16β64, 118.2 @32) | |
| β `batch_size = 24`; RTX 5060 Ti (idx 3) 41.9 utt/s @4, ~32 at bs β₯ 8 β 4. | |
| - Full J-SPAW_LA, PyTorch, RTX 5060 Ti idx 3 (`run_pt.sh`): **EER 14.500%** (2397 trials, 0 skipped). Sane. | |
| ## TensorRT (`trt_last_net.py`, sidecar `trt_last-net.json`) | |
| Two-input engine (wav (B,64000) + available (B,)), FP32 with TF32 cleared, profile 1/16/32. Export-only patches: | |
| fairseq `pad_to_multiple` β static pad (199 β 200 frames + mask, as eager); `_net.duration_features` β a trace-safe copy | |
| (`full_like(available, T/16000)` instead of `new_full((len(wav),))`, which would bake the batch size). Build parity on | |
| 64 InTheWild clips + 3 truncated (repeat-path) clips. | |
| | card | precision | best batch | utt/s (sweep) | build Spearman / max Ξ | J-SPAW EER | J-SPAW Spearman vs PT / max Ξ | | |
| |---|---|---|---|---|---|---| | |
| | RTX 4070 Ti SUPER (idx 2) | FP32 (no TF32) | 16 | 135.6 | 1.0 / 6.1e-5 | 14.500 (=PT) | 1.000000 / 2.7e-4 | | |
| | RTX 5060 Ti (idx 3) | FP32 (no TF32) | 16 | 88.8 | 1.0 / 3.7e-5 | 14.500 (=PT) | 1.000000 / 3.5e-4 | | |
| Both cards pass (|ΞEER| = 0 β€ 0.25, Spearman β₯ 0.999) β TRT accepted on both card types. Engines | |
| `engine_<GPU>_fp32_b1-16-32.plan` (gitignored); only the 4070 ONNX is kept. TRT runs in `results_trt2/`, `results_trt3/` | |
| (`cmp_scores.py`, `logs_trt_done.txt`). FP16 not tried (plan: FP32). | |
| Scripts: `run_pt.sh`, `run_trt_export.sh <gpu>`, `run_trt_jspaw.sh <gpu>`. | |