Keras
English
audio
anomaly-detection
industrial
predictive-maintenance
acoustic-monitoring
dcase2020
arcface
transformer
tensorflow
Instructions to use Jvachier/dcase2020-task2-anomaly-detection with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Keras
How to use Jvachier/dcase2020-task2-anomaly-detection with Keras:
# Available backend options are: "jax", "torch", "tensorflow". import os os.environ["KERAS_BACKEND"] = "jax" import keras model = keras.saving.load_model("hf://Jvachier/dcase2020-task2-anomaly-detection") - Notebooks
- Google Colab
- Kaggle
| license: apache-2.0 | |
| language: en | |
| library_name: keras | |
| tags: | |
| - audio | |
| - anomaly-detection | |
| - industrial | |
| - predictive-maintenance | |
| - acoustic-monitoring | |
| - dcase2020 | |
| - arcface | |
| - transformer | |
| - keras | |
| - tensorflow | |
| metrics: | |
| - roc_auc | |
| # DCASE 2020 Task 2 — Valve Anomaly Detection (ArcFace Embedding + Mahalanobis) | |
| Unsupervised acoustic anomaly detection for industrial valves. Trained on **normal | |
| operating sounds only** (no anomalous examples seen during training); anomalies are | |
| detected as embeddings that fall far from the learned distribution of normal acoustic | |
| signatures. | |
| - **Task**: unsupervised anomaly detection (DCASE 2020 Challenge Task 2 formulation) | |
| - **Equipment type**: valve | |
| - **Metric**: pooled Mahalanobis distance AUC-ROC = **0.947 ± 0.006** (mean ± std over 3 seeds) | |
| - **Deployment**: no machine ID required at inference (pooled scoring) | |
| - **Code**: [github.com/jvachier/industrial-audio-anomaly-detection](https://github.com/jvachier/industrial-audio-anomaly-detection) | |
| - **Demo**: Gradio App | |
| --- | |
| ## Model description | |
| The model is a multiscale Transformer encoder trained on a **pretext task**: given a | |
| log-mel spectrogram patch, predict which physical valve unit (machine ID) it came from. | |
| This forces the encoder to learn fine-grained acoustic signatures characteristic of each | |
| unit's normal operation. An **ArcFace additive angular margin loss** (`s=32`, `m=0.10`) | |
| tightens the per-ID clusters on the embedding hypersphere during training. | |
| After training, the classification head is discarded. What remains is a 128-dimensional | |
| embedding function. At inference, the anomaly score is the **Mahalanobis distance** from | |
| a clip's embedding to a single LedoitWolf-estimated Gaussian fitted over *all* training | |
| embeddings pooled across machine IDs — this is what makes the deployed model usable | |
| without knowing which physical unit produced a given recording. | |
| **Architecture**: | |
| ``` | |
| Input: log-mel spectrogram (64 frames x 128 mel bins) | |
| -> multiscale patch tokenisation: patch sizes [8, 4] frames, each scale gets its own | |
| linear projection to 128-d + sinusoidal positional encoding, then concatenated | |
| -> 2 x Transformer encoder block (4 heads, d_model=128) | |
| -> mean pooling -> 128-d embedding | |
| -> (training only) ArcFace head, s=32, m=0.10, over 4 machine-ID classes | |
| ``` | |
| Trained independently for 3 random seeds (0, 1, 2); the reference implementation | |
| z-score-ensembles all three at inference for a more robust score (see Usage below). | |
| --- | |
| ## Intended use | |
| - **Primary use case**: screening industrial valve acoustic recordings for anomalous | |
| operating conditions (e.g. predictive maintenance), as a proof-of-concept / benchmark | |
| model on the DCASE 2020 Task 2 dataset. | |
| - **Not validated for**: real-world deployment audio outside the DCASE 2020 / MIMII | |
| valve distribution (different valve types, microphone placement, background noise | |
| profiles, or SNR than the benchmark's synthetic 0 dB mixing). Treat scores from | |
| out-of-distribution audio as unreliable without re-calibration. | |
| - **Not included in this repo**: fan and pump models. This repository currently ships | |
| the valve model only. | |
| --- | |
| ## How to use | |
| This model uses custom Keras layers (`ArcFaceLayer`, multiscale patch tokenizer) that | |
| are registered via `@keras.saving.register_keras_serializable` in the companion | |
| `iaad` Python package — loading the `.keras` file with bare `keras.models.load_model()` | |
| without that package installed will fail to resolve the custom layers. Install the | |
| package from GitHub (not yet published to PyPI): | |
| ```bash | |
| pip install git+https://github.com/jvachier/industrial-audio-anomaly-detection.git | |
| ``` | |
| **Single seed:** | |
| ```python | |
| from iaad.inference import AnomalyDetector | |
| det = AnomalyDetector.from_hub(machine_type="valve", seed=0) | |
| result = det.score_file("path/to/valve_clip.wav", score="mahalanobis") | |
| print(result.is_anomalous, result.percentile) | |
| ``` | |
| **Ensemble of all 3 seeds (recommended — more robust score):** | |
| ```python | |
| from iaad.inference import EnsembleDetector | |
| ens = EnsembleDetector.from_hub(machine_type="valve") | |
| result = ens.score_file("path/to/valve_clip.wav", score="mahalanobis") | |
| print(result["is_anomalous"], result["percentile"]) | |
| ``` | |
| Input audio should be a 16 kHz mono WAV clip (10 s recommended, matching training | |
| clip length); other sample rates are auto-resampled with a warning. | |
| --- | |
| ## Training data | |
| [DCASE 2020 Challenge Task 2](https://dcase.community/challenge2020/task2-unsupervised-detection-of-anomalous-sounds) | |
| (MIMII-based), valve subset, mirrored on | |
| [Kaggle: daisukelab/dc2020task2](https://www.kaggle.com/datasets/daisukelab/dc2020task2). | |
| - 4 machine IDs: `id_00`, `id_02`, `id_04`, `id_06` | |
| - Condition: 0 dB SNR (target machine signal + factory background noise at equal power) | |
| - ~1000 ten-second normal-only training clips per machine ID; a labelled normal + | |
| anomaly test split is used only for evaluation, never for training | |
| - 85/15 train/validation split (validation is normal-only, used for early stopping) | |
| **Preprocessing** (`src/iaad/features.py`, no external audio library): | |
| log-mel spectrogram, 128 mel bins, 50–8000 Hz, 512-sample FFT window / 256-sample hop, | |
| 64-frame (~1.02 s) non-overlapping patches, per-clip CMVN (mean-normalised per mel bin). | |
| SpecAugment is disabled for valve specifically (empirically best AUC without it; fan/pump | |
| use frequency + time masking). | |
| --- | |
| ## Training procedure | |
| | Hyperparameter | Value | | |
| |---|---| | |
| | Optimizer | Adam, lr=1e-3 | | |
| | Batch size | 64 | | |
| | Epochs | up to 120, early stopping (patience=10, restore best weights) | | |
| | Loss | ArcFace-modified softmax cross-entropy over 4 machine-ID classes | | |
| | ArcFace scale / margin | s=32, m=0.10 | | |
| | Seeds | 0, 1, 2 (independent runs, deterministic — `tf.config.experimental.enable_op_determinism()`) | | |
| | Hardware | single GPU (Kaggle P100/T4), no mixed precision | | |
| After training, a calibration pass fits a LedoitWolf covariance estimator over all | |
| training-set embeddings (pooled across machine IDs) and records percentile thresholds | |
| of the resulting Mahalanobis distances — this is what ships in `valve_calib_s{seed}.npz` | |
| and lets inference flag a clip as anomalous (e.g. above the 95th percentile of normal) | |
| without any refitting. | |
| --- | |
| ## Evaluation results | |
| Per-machine-ID mean AUC-ROC, averaged over 3 random seeds (chance = 0.500): | |
| | Machine | EMB pooled Mahalanobis | | |
| |---------|:----------------------:| | |
| | **valve** | **0.947 ± 0.006** | | |
| Pooled scoring is the deployment-safe metric (no machine ID needed at inference). See | |
| the [GitHub repo's evaluation docs](https://github.com/jvachier/industrial-audio-anomaly-detection/blob/main/docs/evaluation.md) | |
| for the full comparison against earlier detector families (dense autoencoder, | |
| Transformer-VAE) and the oracle (per-ID, ID-known) scoring variant. | |
| --- | |
| ## Files in this repository | |
| | File | Description | | |
| |---|---| | |
| | `version.json` | `{"emb_version": "v5", "run_tag": "v5.4"}` — model family marker read by `AnomalyDetector._load()` | | |
| | `valve_emb_s{0,1,2}.keras` | Trained `EMBClassifier` weights, one per seed | | |
| | `valve_norm_s{0,1,2}.npz` | Per-mel-bin normalisation stats (mean, std) fit on that seed's training split | | |
| | `valve_calib_s{0,1,2}.npz` | Calibration: pooled LedoitWolf location/precision + percentile thresholds | | |
| --- | |
| ## Limitations and biases | |
| - Trained and evaluated exclusively on the DCASE 2020 Task 2 synthetic 0 dB SNR mixing; | |
| performance on cleaner or noisier real-world recordings is unverified. | |
| - Unsupervised training means the model has never seen a true anomaly label; its notion | |
| of "anomalous" is entirely defined by the DCASE benchmark's specific injected fault | |
| types, which may not cover all real failure modes. | |
| - Anomaly threshold (95th percentile by default) is a design choice trading recall for | |
| precision — recalibrate on your own normal-operation data before deploying to a | |
| different valve installation. | |
| --- | |
| ## License | |
| Apache 2.0 — see the [GitHub repository's LICENSE](https://github.com/jvachier/industrial-audio-anomaly-detection/blob/main/LICENSE). | |
| ## Citation | |
| ```bibtex | |
| @misc{vachier2025iaad, | |
| author = {Vachier, Jeremy}, | |
| title = {Industrial Audio Anomaly Detection -- DCASE 2020 Task 2 Baseline}, | |
| year = {2026}, | |
| url = {https://github.com/jvachier/industrial-audio-anomaly-detection}, | |
| } | |
| @inproceedings{koizumi2020dcase, | |
| author = {Koizumi, Yuma and Saito, Shoichiro and Uematsu, Hisashi and Harada, Noboru and Imoto, Keisuke}, | |
| title = {ToyADMOS: A Dataset of Miniature-Machine Operating Sounds for Anomalous Sound Detection}, | |
| booktitle = {Proceedings of the Detection and Classification of Acoustic Scenes and Events 2020 Workshop (DCASE2020)}, | |
| year = {2020}, | |
| } | |
| @misc{huggingface_hub, | |
| author = {Hugging Face}, | |
| title = {Hugging Face Hub}, | |
| year = {2026}, | |
| url = {https://github.com/huggingface/huggingface_hub}, | |
| } | |
| ``` |