pyod 3.5.2__tar.gz → 3.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyod-3.5.2/pyod.egg-info → pyod-3.6.0}/PKG-INFO +12 -7
- {pyod-3.5.2 → pyod-3.6.0}/README.rst +5 -5
- {pyod-3.5.2 → pyod-3.6.0}/pyod/cli.py +8 -2
- pyod-3.6.0/pyod/models/audio_ae.py +235 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/embedding.py +49 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/skills/od_expert/SKILL.md +123 -123
- {pyod-3.5.2 → pyod-3.6.0}/pyod/skills/od_expert/references/graph.md +2 -2
- {pyod-3.5.2 → pyod-3.6.0}/pyod/skills/od_expert/references/tabular.md +3 -4
- {pyod-3.5.2 → pyod-3.6.0}/pyod/skills/od_expert/references/text_image.md +2 -3
- {pyod-3.5.2 → pyod-3.6.0}/pyod/skills/od_expert/references/time_series.md +1 -1
- {pyod-3.5.2 → pyod-3.6.0}/pyod/skills/od_expert/references/workflow.md +237 -237
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/_detector_factory.py +2 -0
- pyod-3.6.0/pyod/utils/_llm.py +267 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/_quality_metrics.py +505 -500
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/ad_engine.py +354 -8
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/encoders/__init__.py +6 -0
- pyod-3.6.0/pyod/utils/encoders/audio.py +175 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/knowledge/algorithms.json +30 -3
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/knowledge/routing_rules.json +12 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/version.py +23 -23
- {pyod-3.5.2 → pyod-3.6.0/pyod.egg-info}/PKG-INFO +12 -7
- {pyod-3.5.2 → pyod-3.6.0}/pyod.egg-info/SOURCES.txt +3 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod.egg-info/requires.txt +6 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyproject.toml +4 -1
- {pyod-3.5.2 → pyod-3.6.0}/LICENSE +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/MANIFEST.in +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/__init__.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/mcp_server.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/__init__.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/_pyg_utils.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/_ts_utils.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/abod.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/ae1svm.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/alad.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/anogan.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/auto_encoder.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/base.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/base_dl.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/cblof.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/cd.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/cof.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/combination.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/copod.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/deep_svdd.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/devnet.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/dif.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/ecod.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/feature_bagging.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/gaal_base.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/gmm.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/hbos.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/hdbscan.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/iforest.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/inne.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/kde.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/knn.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/kpca.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/lmdd.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/loci.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/loda.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/lof.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/lscp.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/lunar.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/mad.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/mcd.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/mo_gaal.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/ocsvm.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/pca.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/pyg_anomalous.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/pyg_anomalydae.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/pyg_cola.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/pyg_conad.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/pyg_dominant.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/pyg_guide.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/pyg_radar.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/pyg_scan.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/qmcd.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/rgraph.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/rod.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/sampling.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/sklearn_base.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/so_gaal.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/so_gaal_new.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/sod.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/sos.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/suod.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/thresholds.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/ts_anomaly_transformer.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/ts_kshape.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/ts_lstm.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/ts_matrix_profile.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/ts_od.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/ts_sand.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/ts_spectral_residual.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/vae.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/models/xgbod.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/skills/__init__.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/skills/od_expert/__init__.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/skills/od_expert/references/pitfalls.md +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/__init__.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/_kb_router.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/_nl_feedback.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/auto_model_selector.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/data.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/encoders/huggingface.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/encoders/openai_encoder.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/encoders/sentence_transformer.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/example.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/investigation.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/knowledge/__init__.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/knowledge/benchmarks.json +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/knowledge/papers.json +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/model_analysis_jsons/AE1SVM.json +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/model_analysis_jsons/ALAD.json +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/model_analysis_jsons/AnoGAN.json +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/model_analysis_jsons/AutoEncoder.json +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/model_analysis_jsons/Deep SVDD.json +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/model_analysis_jsons/DevNet.json +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/model_analysis_jsons/LUNAR.json +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/model_analysis_jsons/MO-GAAL.json +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/model_analysis_jsons/SO-GAAL.json +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/model_analysis_jsons/VAE.json +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/model_analysis_jsons/__init__.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/persistence.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/stat_models.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/torch_utility.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod/utils/utility.py +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod.egg-info/dependency_links.txt +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod.egg-info/entry_points.txt +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/pyod.egg-info/top_level.txt +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/requirements.txt +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/setup.cfg +0 -0
- {pyod-3.5.2 → pyod-3.6.0}/setup.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyod
|
|
3
|
-
Version: 3.
|
|
4
|
-
Summary: A Python library for anomaly detection across tabular, time series, graph, text, and
|
|
3
|
+
Version: 3.6.0
|
|
4
|
+
Summary: A Python library for anomaly detection across tabular, time series, graph, text, image, and audio data. 61 detectors, benchmark-backed ADEngine orchestration, and an agentic workflow for AI agents.
|
|
5
5
|
Author-email: Yue Zhao <yzhao062@gmail.com>
|
|
6
6
|
License-Expression: BSD-2-Clause
|
|
7
7
|
Project-URL: Homepage, https://github.com/yzhao062/pyod
|
|
@@ -53,6 +53,9 @@ Requires-Dist: torch>=2.0; extra == "graph"
|
|
|
53
53
|
Requires-Dist: torch_geometric>=2.0; extra == "graph"
|
|
54
54
|
Provides-Extra: mcp
|
|
55
55
|
Requires-Dist: mcp>=1.0; extra == "mcp"
|
|
56
|
+
Provides-Extra: audio
|
|
57
|
+
Requires-Dist: librosa>=0.10; extra == "audio"
|
|
58
|
+
Requires-Dist: soundfile; extra == "audio"
|
|
56
59
|
Provides-Extra: all
|
|
57
60
|
Requires-Dist: torch>=2.0; extra == "all"
|
|
58
61
|
Requires-Dist: suod; extra == "all"
|
|
@@ -65,6 +68,8 @@ Requires-Dist: transformers>=4.25.1; extra == "all"
|
|
|
65
68
|
Requires-Dist: torch_geometric>=2.0; extra == "all"
|
|
66
69
|
Requires-Dist: Pillow; extra == "all"
|
|
67
70
|
Requires-Dist: mcp>=1.0; extra == "all"
|
|
71
|
+
Requires-Dist: librosa>=0.10; extra == "all"
|
|
72
|
+
Requires-Dist: soundfile; extra == "all"
|
|
68
73
|
Dynamic: license-file
|
|
69
74
|
|
|
70
75
|
Python Outlier Detection (PyOD) 3
|
|
@@ -131,7 +136,7 @@ PyOD 3 is the most comprehensive Python library for anomaly detection. Four pill
|
|
|
131
136
|
=========================== ========================================================================================
|
|
132
137
|
Pillar What it means
|
|
133
138
|
=========================== ========================================================================================
|
|
134
|
-
Multi-Modal
|
|
139
|
+
Multi-Modal 61 detectors across **tabular, time series, graph, text, image, and audio** data, one API
|
|
135
140
|
Full Lifecycle From raw data to explained anomalies and next-step guidance in a single call
|
|
136
141
|
Agentic ``od-expert`` turns natural-language requests into ADEngine workflows; MCP exposes structured tools for other agents
|
|
137
142
|
Most Used 38+ million downloads; benchmark-backed routing (ADBench, TSB-AD, BOND, NLP-ADBench)
|
|
@@ -191,7 +196,7 @@ Layer Name When to use
|
|
|
191
196
|
3 Agentic Investigation You want an AI agent to drive OD through natural conversation `Layer 3 walkthrough <https://pyod.readthedocs.io/en/latest/examples/agentic.html>`__
|
|
192
197
|
========= ===================== ====================================================================== =======================================
|
|
193
198
|
|
|
194
|
-
Layers 2 and 3 are powered by ``ADEngine``, PyOD's
|
|
199
|
+
Layers 2 and 3 are powered by ``ADEngine``, PyOD's lifecycle orchestration core. The full multi-turn Layer 3 investigation flow is available through the ``od-expert`` skill for Claude Code and Codex. The MCP server (``python -m pyod.mcp_server``) exposes ten stateless tools for MCP-compatible LLMs, spanning knowledge queries (``list_detectors``, ``explain_detector``, ``compare_detectors``, ``get_benchmarks``), planning (``profile_data``, ``plan_detection``, ``build_detector``), and detection (``run_detection``, ``analyze_results``, ``explain_findings``); stateful ``investigate`` / ``iterate`` MCP tools are deferred.
|
|
195
200
|
|
|
196
201
|
.. image:: https://raw.githubusercontent.com/yzhao062/pyod/development/docs/figs/agentic-demo.png
|
|
197
202
|
:alt: PyOD 3 agentic investigation demo on cardiotocography dataset
|
|
@@ -211,7 +216,7 @@ About PyOD
|
|
|
211
216
|
|
|
212
217
|
PyOD, established in 2017, is the longest-running and most widely used Python library for anomaly detection. With `38+ million downloads <https://pepy.tech/project/pyod>`__, it serves both academic research (featured in `Analytics Vidhya <https://www.analyticsvidhya.com/blog/2019/02/outlier-detection-python-pyod/>`__, `KDnuggets <https://www.kdnuggets.com/2019/02/outlier-detection-methods-cheat-sheet.html>`__, and `Towards Data Science <https://towardsdatascience.com/anomaly-detection-for-dummies-15f148e559c1>`__) and commercial products.
|
|
213
218
|
|
|
214
|
-
V3 extends the library with ``ADEngine`` (
|
|
219
|
+
V3 extends the library with ``ADEngine`` (lifecycle orchestration) and the ``od-expert`` skill (agentic workflow), while keeping the classic ``fit``/``predict`` API fully backward-compatible. V3 is built on SUOD [#Zhao2021SUOD]_ for fast parallel training and numba JIT for per-model speedups.
|
|
215
220
|
|
|
216
221
|
**Impact & Recognition**:
|
|
217
222
|
|
|
@@ -322,7 +327,7 @@ Additional Topics
|
|
|
322
327
|
Implemented Algorithms
|
|
323
328
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
324
329
|
|
|
325
|
-
PyOD is organized into two functional groups: **(i) Detection Algorithms**, with dedicated subsections for tabular, time series, and graph data (EmbeddingOD inside the tabular table adds multi-modal support for text and
|
|
330
|
+
PyOD is organized into two functional groups: **(i) Detection Algorithms**, with dedicated subsections for tabular, time series, and graph data (EmbeddingOD inside the tabular table adds multi-modal support for text, image, and audio via foundation model or handcrafted encoders); and **(ii) Utility Functions** for data generation, evaluation, and lifecycle orchestration.
|
|
326
331
|
|
|
327
332
|
**(i-a) Tabular & Multi-Modal Detection Algorithms** :
|
|
328
333
|
|
|
@@ -577,7 +582,7 @@ PyOD is organized into two functional groups: **(i) Detection Algorithms**, with
|
|
|
577
582
|
- [#Goodge2022Lunar]_
|
|
578
583
|
* - Embedding-based
|
|
579
584
|
- EmbeddingOD
|
|
580
|
-
- Multi-modal anomaly detection via foundation model embeddings, text and
|
|
585
|
+
- Multi-modal anomaly detection via foundation model embeddings, text, image, and audio (`example <https://github.com/yzhao062/pyod/blob/development/examples/embedding_od_example.py>`__)
|
|
581
586
|
- 2025
|
|
582
587
|
- [#Li2024NLPADBench]_
|
|
583
588
|
|
|
@@ -62,7 +62,7 @@ PyOD 3 is the most comprehensive Python library for anomaly detection. Four pill
|
|
|
62
62
|
=========================== ========================================================================================
|
|
63
63
|
Pillar What it means
|
|
64
64
|
=========================== ========================================================================================
|
|
65
|
-
Multi-Modal
|
|
65
|
+
Multi-Modal 61 detectors across **tabular, time series, graph, text, image, and audio** data, one API
|
|
66
66
|
Full Lifecycle From raw data to explained anomalies and next-step guidance in a single call
|
|
67
67
|
Agentic ``od-expert`` turns natural-language requests into ADEngine workflows; MCP exposes structured tools for other agents
|
|
68
68
|
Most Used 38+ million downloads; benchmark-backed routing (ADBench, TSB-AD, BOND, NLP-ADBench)
|
|
@@ -122,7 +122,7 @@ Layer Name When to use
|
|
|
122
122
|
3 Agentic Investigation You want an AI agent to drive OD through natural conversation `Layer 3 walkthrough <https://pyod.readthedocs.io/en/latest/examples/agentic.html>`__
|
|
123
123
|
========= ===================== ====================================================================== =======================================
|
|
124
124
|
|
|
125
|
-
Layers 2 and 3 are powered by ``ADEngine``, PyOD's
|
|
125
|
+
Layers 2 and 3 are powered by ``ADEngine``, PyOD's lifecycle orchestration core. The full multi-turn Layer 3 investigation flow is available through the ``od-expert`` skill for Claude Code and Codex. The MCP server (``python -m pyod.mcp_server``) exposes ten stateless tools for MCP-compatible LLMs, spanning knowledge queries (``list_detectors``, ``explain_detector``, ``compare_detectors``, ``get_benchmarks``), planning (``profile_data``, ``plan_detection``, ``build_detector``), and detection (``run_detection``, ``analyze_results``, ``explain_findings``); stateful ``investigate`` / ``iterate`` MCP tools are deferred.
|
|
126
126
|
|
|
127
127
|
.. image:: https://raw.githubusercontent.com/yzhao062/pyod/development/docs/figs/agentic-demo.png
|
|
128
128
|
:alt: PyOD 3 agentic investigation demo on cardiotocography dataset
|
|
@@ -142,7 +142,7 @@ About PyOD
|
|
|
142
142
|
|
|
143
143
|
PyOD, established in 2017, is the longest-running and most widely used Python library for anomaly detection. With `38+ million downloads <https://pepy.tech/project/pyod>`__, it serves both academic research (featured in `Analytics Vidhya <https://www.analyticsvidhya.com/blog/2019/02/outlier-detection-python-pyod/>`__, `KDnuggets <https://www.kdnuggets.com/2019/02/outlier-detection-methods-cheat-sheet.html>`__, and `Towards Data Science <https://towardsdatascience.com/anomaly-detection-for-dummies-15f148e559c1>`__) and commercial products.
|
|
144
144
|
|
|
145
|
-
V3 extends the library with ``ADEngine`` (
|
|
145
|
+
V3 extends the library with ``ADEngine`` (lifecycle orchestration) and the ``od-expert`` skill (agentic workflow), while keeping the classic ``fit``/``predict`` API fully backward-compatible. V3 is built on SUOD [#Zhao2021SUOD]_ for fast parallel training and numba JIT for per-model speedups.
|
|
146
146
|
|
|
147
147
|
**Impact & Recognition**:
|
|
148
148
|
|
|
@@ -253,7 +253,7 @@ Additional Topics
|
|
|
253
253
|
Implemented Algorithms
|
|
254
254
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
255
255
|
|
|
256
|
-
PyOD is organized into two functional groups: **(i) Detection Algorithms**, with dedicated subsections for tabular, time series, and graph data (EmbeddingOD inside the tabular table adds multi-modal support for text and
|
|
256
|
+
PyOD is organized into two functional groups: **(i) Detection Algorithms**, with dedicated subsections for tabular, time series, and graph data (EmbeddingOD inside the tabular table adds multi-modal support for text, image, and audio via foundation model or handcrafted encoders); and **(ii) Utility Functions** for data generation, evaluation, and lifecycle orchestration.
|
|
257
257
|
|
|
258
258
|
**(i-a) Tabular & Multi-Modal Detection Algorithms** :
|
|
259
259
|
|
|
@@ -508,7 +508,7 @@ PyOD is organized into two functional groups: **(i) Detection Algorithms**, with
|
|
|
508
508
|
- [#Goodge2022Lunar]_
|
|
509
509
|
* - Embedding-based
|
|
510
510
|
- EmbeddingOD
|
|
511
|
-
- Multi-modal anomaly detection via foundation model embeddings, text and
|
|
511
|
+
- Multi-modal anomaly detection via foundation model embeddings, text, image, and audio (`example <https://github.com/yzhao062/pyod/blob/development/examples/embedding_od_example.py>`__)
|
|
512
512
|
- 2025
|
|
513
513
|
- [#Li2024NLPADBench]_
|
|
514
514
|
|
|
@@ -46,10 +46,16 @@ def _cmd_info(args: argparse.Namespace) -> int:
|
|
|
46
46
|
from pyod.utils.ad_engine import ADEngine
|
|
47
47
|
engine = ADEngine()
|
|
48
48
|
counts: Counter = Counter()
|
|
49
|
-
|
|
49
|
+
# Count only buildable detectors: a `planned` entry (e.g., LLMAD) has
|
|
50
|
+
# no backing module, so it must not inflate the reported total.
|
|
51
|
+
buildable = {
|
|
52
|
+
name: algo for name, algo in engine.kb.algorithms.items()
|
|
53
|
+
if algo.get("status") != "planned"
|
|
54
|
+
}
|
|
55
|
+
for algo in buildable.values():
|
|
50
56
|
for dt in algo.get("data_types", []):
|
|
51
57
|
counts[dt] += 1
|
|
52
|
-
total = len(
|
|
58
|
+
total = len(buildable)
|
|
53
59
|
ad_ok = True
|
|
54
60
|
except Exception:
|
|
55
61
|
counts = Counter()
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""AudioAE: a log-mel reconstruction autoencoder for audio anomaly detection.
|
|
3
|
+
|
|
4
|
+
Each clip is turned into overlapping log-mel context windows; a dense
|
|
5
|
+
autoencoder is fit on the windows of the (mostly normal) training clips,
|
|
6
|
+
and each clip is scored by its mean per-window reconstruction error. This
|
|
7
|
+
is the DCASE-style audio anomaly detection baseline, expressed through
|
|
8
|
+
PyOD's ``AutoEncoder`` so the training loop and preprocessing are shared
|
|
9
|
+
with the rest of the library.
|
|
10
|
+
"""
|
|
11
|
+
# Author: Yue Zhao <yzhao062@gmail.com>
|
|
12
|
+
# License: BSD 2 clause
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
from sklearn.utils.validation import check_is_fitted
|
|
16
|
+
|
|
17
|
+
from .base import BaseDetector
|
|
18
|
+
from ..utils.encoders.audio import _to_mono_waveform
|
|
19
|
+
|
|
20
|
+
_DEFAULT_SR = 22050
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _logmel_windows(y, sr, n_mels, context, hop_length):
|
|
24
|
+
"""Return overlapping log-mel context windows for one waveform.
|
|
25
|
+
|
|
26
|
+
Output shape is ``(n_windows, n_mels * context)``. Clips shorter than
|
|
27
|
+
one context window are padded so at least one window is produced.
|
|
28
|
+
"""
|
|
29
|
+
import librosa
|
|
30
|
+
|
|
31
|
+
spec = librosa.power_to_db(
|
|
32
|
+
librosa.feature.melspectrogram(
|
|
33
|
+
y=y, sr=sr, n_mels=n_mels, hop_length=hop_length))
|
|
34
|
+
n_frames = spec.shape[1]
|
|
35
|
+
if n_frames < context:
|
|
36
|
+
pad = np.zeros((n_mels, context - n_frames), dtype=spec.dtype)
|
|
37
|
+
spec = np.concatenate([spec, pad], axis=1)
|
|
38
|
+
n_frames = context
|
|
39
|
+
windows = [spec[:, t:t + context].T.reshape(-1)
|
|
40
|
+
for t in range(n_frames - context + 1)]
|
|
41
|
+
return np.stack(windows).astype(np.float32)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class AudioAE(BaseDetector):
|
|
45
|
+
"""Log-mel reconstruction autoencoder for audio anomaly detection.
|
|
46
|
+
|
|
47
|
+
The detector extracts overlapping log-mel context windows from each
|
|
48
|
+
clip, fits a dense autoencoder (PyOD's :class:`AutoEncoder`) on the
|
|
49
|
+
windows of the training clips, and scores each clip by its mean
|
|
50
|
+
per-window reconstruction error. Training assumes the input is mostly
|
|
51
|
+
normal, the usual unsupervised setting.
|
|
52
|
+
|
|
53
|
+
Requires ``torch`` (for the autoencoder) and ``pyod[audio]``
|
|
54
|
+
(``librosa``, ``soundfile``).
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
n_mels : int, optional (default=64)
|
|
59
|
+
Number of mel bands in the spectrogram.
|
|
60
|
+
|
|
61
|
+
context : int, optional (default=5)
|
|
62
|
+
Number of consecutive frames stacked into one autoencoder input
|
|
63
|
+
window. The window dimensionality is ``n_mels * context``.
|
|
64
|
+
|
|
65
|
+
hop_length : int, optional (default=512)
|
|
66
|
+
STFT hop length in samples.
|
|
67
|
+
|
|
68
|
+
sr : int, optional (default=22050)
|
|
69
|
+
Target sample rate. File inputs are loaded at this rate;
|
|
70
|
+
``(waveform, sample_rate)`` tuples are resampled to it.
|
|
71
|
+
|
|
72
|
+
contamination : float, optional (default=0.1)
|
|
73
|
+
Expected proportion of outliers, used for the clip-level
|
|
74
|
+
threshold and labels.
|
|
75
|
+
|
|
76
|
+
epoch_num : int, optional (default=40)
|
|
77
|
+
Autoencoder training epochs.
|
|
78
|
+
|
|
79
|
+
batch_size : int, optional (default=1024)
|
|
80
|
+
Autoencoder mini-batch size (over frames, not clips).
|
|
81
|
+
|
|
82
|
+
lr : float, optional (default=1e-3)
|
|
83
|
+
Learning rate.
|
|
84
|
+
|
|
85
|
+
hidden_neuron_list : list of int or None, optional (default=None)
|
|
86
|
+
Encoder hidden sizes. ``None`` uses ``[128, 32, 8]``, which gives
|
|
87
|
+
the DCASE-style 320-128-32-8 contraction for the default
|
|
88
|
+
320-dimensional window (``n_mels=64``, ``context=5``).
|
|
89
|
+
|
|
90
|
+
device : str or None, optional (default=None)
|
|
91
|
+
Torch device. ``None`` auto-selects.
|
|
92
|
+
|
|
93
|
+
random_state : int, optional (default=42)
|
|
94
|
+
Seed forwarded to the autoencoder.
|
|
95
|
+
|
|
96
|
+
verbose : int, optional (default=0)
|
|
97
|
+
Autoencoder verbosity.
|
|
98
|
+
|
|
99
|
+
Attributes
|
|
100
|
+
----------
|
|
101
|
+
decision_scores_ : numpy array of shape (n_clips,)
|
|
102
|
+
Clip-level outlier scores of the training data.
|
|
103
|
+
|
|
104
|
+
threshold_ : float
|
|
105
|
+
Score threshold based on ``contamination``.
|
|
106
|
+
|
|
107
|
+
labels_ : numpy array of shape (n_clips,)
|
|
108
|
+
Binary labels of training clips (0: inlier, 1: outlier).
|
|
109
|
+
|
|
110
|
+
ae_ : AutoEncoder
|
|
111
|
+
The fitted frame-level autoencoder.
|
|
112
|
+
|
|
113
|
+
Examples
|
|
114
|
+
--------
|
|
115
|
+
>>> import numpy as np
|
|
116
|
+
>>> from pyod.models.audio_ae import AudioAE
|
|
117
|
+
>>> clips = [np.random.RandomState(s).randn(22050) for s in range(20)]
|
|
118
|
+
>>> clf = AudioAE(epoch_num=5)
|
|
119
|
+
>>> clf.fit(clips) # doctest: +SKIP
|
|
120
|
+
>>> scores = clf.decision_function(clips) # doctest: +SKIP
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
def __init__(self, n_mels=64, context=5, hop_length=512, sr=_DEFAULT_SR,
|
|
124
|
+
contamination=0.1, epoch_num=40, batch_size=1024, lr=1e-3,
|
|
125
|
+
hidden_neuron_list=None, device=None, random_state=42,
|
|
126
|
+
verbose=0):
|
|
127
|
+
super(AudioAE, self).__init__(contamination=contamination)
|
|
128
|
+
self.n_mels = n_mels
|
|
129
|
+
self.context = context
|
|
130
|
+
self.hop_length = hop_length
|
|
131
|
+
self.sr = sr
|
|
132
|
+
self.epoch_num = epoch_num
|
|
133
|
+
self.batch_size = batch_size
|
|
134
|
+
self.lr = lr
|
|
135
|
+
self.hidden_neuron_list = hidden_neuron_list
|
|
136
|
+
self.device = device
|
|
137
|
+
self.random_state = random_state
|
|
138
|
+
self.verbose = verbose
|
|
139
|
+
|
|
140
|
+
def _extract(self, X):
|
|
141
|
+
"""Return (frames, clip_idx) over all clips in X."""
|
|
142
|
+
try:
|
|
143
|
+
import librosa # noqa: F401
|
|
144
|
+
import soundfile # noqa: F401
|
|
145
|
+
except ImportError:
|
|
146
|
+
raise ImportError(
|
|
147
|
+
"AudioAE requires 'librosa' and 'soundfile'. "
|
|
148
|
+
"Install with: pip install pyod[audio]")
|
|
149
|
+
if len(X) == 0:
|
|
150
|
+
raise ValueError("AudioAE received an empty input.")
|
|
151
|
+
frames_list, clip_idx = [], []
|
|
152
|
+
for i, item in enumerate(X):
|
|
153
|
+
y = _to_mono_waveform(item, self.sr)
|
|
154
|
+
windows = _logmel_windows(y, self.sr, self.n_mels,
|
|
155
|
+
self.context, self.hop_length)
|
|
156
|
+
frames_list.append(windows)
|
|
157
|
+
clip_idx.append(np.full(len(windows), i, dtype=np.int64))
|
|
158
|
+
return np.concatenate(frames_list, axis=0), np.concatenate(clip_idx)
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def _aggregate(frame_scores, clip_idx, n_clips):
|
|
162
|
+
"""Mean per-frame score within each clip."""
|
|
163
|
+
out = np.zeros(n_clips, dtype=np.float64)
|
|
164
|
+
for i in range(n_clips):
|
|
165
|
+
mask = clip_idx == i
|
|
166
|
+
if mask.any():
|
|
167
|
+
out[i] = float(frame_scores[mask].mean())
|
|
168
|
+
return out
|
|
169
|
+
|
|
170
|
+
def fit(self, X, y=None):
|
|
171
|
+
"""Fit the frame autoencoder and score the training clips.
|
|
172
|
+
|
|
173
|
+
Parameters
|
|
174
|
+
----------
|
|
175
|
+
X : list
|
|
176
|
+
Audio clips as file paths, waveform arrays, or
|
|
177
|
+
``(waveform, sample_rate)`` tuples.
|
|
178
|
+
|
|
179
|
+
y : Ignored
|
|
180
|
+
Not used, present for API consistency.
|
|
181
|
+
|
|
182
|
+
Returns
|
|
183
|
+
-------
|
|
184
|
+
self : object
|
|
185
|
+
"""
|
|
186
|
+
try:
|
|
187
|
+
import torch # noqa: F401
|
|
188
|
+
except ImportError:
|
|
189
|
+
raise ImportError(
|
|
190
|
+
"AudioAE requires torch (for the autoencoder) and "
|
|
191
|
+
"pyod[audio] (librosa, soundfile). Install with: "
|
|
192
|
+
"pip install pyod[torch,audio]")
|
|
193
|
+
from .auto_encoder import AutoEncoder
|
|
194
|
+
|
|
195
|
+
frames, clip_idx = self._extract(X)
|
|
196
|
+
dim = frames.shape[1]
|
|
197
|
+
hidden = self.hidden_neuron_list or [128, 32, 8]
|
|
198
|
+
# Drop hidden layers that are not smaller than the input so the
|
|
199
|
+
# autoencoder stays a contraction for unusually small windows.
|
|
200
|
+
hidden = [h for h in hidden if h < dim] or [max(dim // 2, 2)]
|
|
201
|
+
|
|
202
|
+
# Cap the batch size to the frame count. PyOD's AutoEncoder drops
|
|
203
|
+
# the last incomplete batch, so a batch larger than the dataset
|
|
204
|
+
# would drop every frame and leave the training loop with nothing.
|
|
205
|
+
batch_size = max(1, min(self.batch_size, frames.shape[0]))
|
|
206
|
+
|
|
207
|
+
self.ae_ = AutoEncoder(
|
|
208
|
+
contamination=self.contamination, epoch_num=self.epoch_num,
|
|
209
|
+
batch_size=batch_size, lr=self.lr,
|
|
210
|
+
hidden_neuron_list=hidden, device=self.device,
|
|
211
|
+
random_state=self.random_state, verbose=self.verbose)
|
|
212
|
+
self.ae_.fit(frames)
|
|
213
|
+
|
|
214
|
+
frame_scores = self.ae_.decision_function(frames)
|
|
215
|
+
self._set_n_classes(y)
|
|
216
|
+
self.decision_scores_ = self._aggregate(frame_scores, clip_idx, len(X))
|
|
217
|
+
self._process_decision_scores()
|
|
218
|
+
return self
|
|
219
|
+
|
|
220
|
+
def decision_function(self, X):
|
|
221
|
+
"""Predict clip-level anomaly scores for X.
|
|
222
|
+
|
|
223
|
+
Parameters
|
|
224
|
+
----------
|
|
225
|
+
X : list
|
|
226
|
+
Audio clips in the same formats accepted by ``fit``.
|
|
227
|
+
|
|
228
|
+
Returns
|
|
229
|
+
-------
|
|
230
|
+
anomaly_scores : numpy array of shape (n_clips,)
|
|
231
|
+
"""
|
|
232
|
+
check_is_fitted(self, ['decision_scores_', 'threshold_', 'labels_'])
|
|
233
|
+
frames, clip_idx = self._extract(X)
|
|
234
|
+
frame_scores = self.ae_.decision_function(frames)
|
|
235
|
+
return self._aggregate(frame_scores, clip_idx, len(X))
|
|
@@ -431,6 +431,55 @@ class EmbeddingOD(BaseDetector):
|
|
|
431
431
|
config = {**presets[quality], **kwargs}
|
|
432
432
|
return cls(**config)
|
|
433
433
|
|
|
434
|
+
@classmethod
|
|
435
|
+
def for_audio(cls, quality='balanced', **kwargs):
|
|
436
|
+
"""Create an EmbeddingOD configured for audio anomaly detection.
|
|
437
|
+
|
|
438
|
+
Uses a handcrafted 74-dim acoustic feature encoder (20 MFCC,
|
|
439
|
+
12 chroma, and 5 spectral descriptors, each as mean and standard
|
|
440
|
+
deviation over frames) followed by a classical PyOD detector. This
|
|
441
|
+
embed-then-detect pattern with classical detectors is competitive
|
|
442
|
+
on standard audio anomaly detection benchmarks and needs no GPU.
|
|
443
|
+
Requires ``pyod[audio]`` (librosa, soundfile).
|
|
444
|
+
|
|
445
|
+
Input clips may be file paths, waveform arrays, or
|
|
446
|
+
``(waveform, sample_rate)`` tuples.
|
|
447
|
+
|
|
448
|
+
Parameters
|
|
449
|
+
----------
|
|
450
|
+
quality : str, optional (default='balanced')
|
|
451
|
+
- 'fast': handcrafted features + IForest.
|
|
452
|
+
- 'balanced': handcrafted features + KNN.
|
|
453
|
+
- 'best': handcrafted features + LUNAR (requires torch).
|
|
454
|
+
|
|
455
|
+
**kwargs
|
|
456
|
+
Override any EmbeddingOD parameter.
|
|
457
|
+
|
|
458
|
+
Returns
|
|
459
|
+
-------
|
|
460
|
+
clf : EmbeddingOD
|
|
461
|
+
"""
|
|
462
|
+
presets = {
|
|
463
|
+
'fast': {
|
|
464
|
+
'encoder': 'audio-mfcc',
|
|
465
|
+
'detector': 'IForest',
|
|
466
|
+
},
|
|
467
|
+
'balanced': {
|
|
468
|
+
'encoder': 'audio-mfcc',
|
|
469
|
+
'detector': 'KNN',
|
|
470
|
+
},
|
|
471
|
+
'best': {
|
|
472
|
+
'encoder': 'audio-mfcc',
|
|
473
|
+
'detector': 'LUNAR',
|
|
474
|
+
},
|
|
475
|
+
}
|
|
476
|
+
if quality not in presets:
|
|
477
|
+
raise ValueError(
|
|
478
|
+
"quality must be 'fast', 'balanced', or 'best', "
|
|
479
|
+
"got '%s'" % quality)
|
|
480
|
+
config = {**presets[quality], **kwargs}
|
|
481
|
+
return cls(**config)
|
|
482
|
+
|
|
434
483
|
|
|
435
484
|
class MultiModalOD(BaseDetector):
|
|
436
485
|
"""Multi-modal anomaly detection via score fusion.
|