pyod 3.6.2__tar.gz → 3.6.4__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.6.2/pyod.egg-info → pyod-3.6.4}/PKG-INFO +27 -6
- {pyod-3.6.2 → pyod-3.6.4}/README.rst +26 -5
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/abod.py +2 -2
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/alad.py +15 -6
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/anogan.py +13 -10
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/auto_encoder.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/base.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/base_dl.py +2 -2
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/cblof.py +6 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/cd.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/cof.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/copod.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/deep_svdd.py +109 -39
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/dif.py +9 -10
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ecod.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/feature_bagging.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/gmm.py +1 -7
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/hbos.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/hdbscan.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/iforest.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/inne.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/kde.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/knn.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/kpca.py +6 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/lmdd.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/loci.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/loda.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/lof.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/lscp.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/lunar.py +9 -5
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/mad.py +5 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/mcd.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/mo_gaal.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ocsvm.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pca.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/qmcd.py +6 -2
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/rgraph.py +16 -30
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/rod.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/sampling.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/so_gaal.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/so_gaal_new.py +61 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/sod.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/sos.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/suod.py +4 -10
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/vae.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/xgbod.py +8 -9
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/encoders/__init__.py +21 -2
- {pyod-3.6.2 → pyod-3.6.4}/pyod/version.py +1 -1
- {pyod-3.6.2 → pyod-3.6.4/pyod.egg-info}/PKG-INFO +27 -6
- {pyod-3.6.2 → pyod-3.6.4}/LICENSE +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/MANIFEST.in +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/__init__.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/cli.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/mcp_server.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/__init__.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/_pyg_utils.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/_ts_utils.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ae1svm.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/audio_ae.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/combination.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/devnet.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/embedding.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/gaal_base.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_anomalous.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_anomalydae.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_cola.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_conad.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_dominant.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_guide.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_radar.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_scan.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/sklearn_base.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/thresholds.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ts_anomaly_transformer.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ts_kshape.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ts_lstm.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ts_matrix_profile.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ts_od.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ts_sand.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ts_spectral_residual.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/__init__.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/SKILL.md +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/__init__.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/references/graph.md +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/references/pitfalls.md +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/references/tabular.md +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/references/text_image.md +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/references/time_series.md +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/references/workflow.md +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/__init__.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/_detector_factory.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/_kb_router.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/_llm.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/_nl_feedback.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/_quality_metrics.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/ad_engine.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/auto_model_selector.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/data.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/encoders/audio.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/encoders/huggingface.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/encoders/openai_encoder.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/encoders/sentence_transformer.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/example.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/investigation.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/knowledge/__init__.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/knowledge/algorithms.json +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/knowledge/benchmarks.json +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/knowledge/papers.json +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/knowledge/routing_rules.json +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/AE1SVM.json +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/ALAD.json +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/AnoGAN.json +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/AutoEncoder.json +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/Deep SVDD.json +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/DevNet.json +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/LUNAR.json +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/MO-GAAL.json +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/SO-GAAL.json +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/VAE.json +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/__init__.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/persistence.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/stat_models.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/torch_utility.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/utility.py +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod.egg-info/SOURCES.txt +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod.egg-info/dependency_links.txt +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod.egg-info/entry_points.txt +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod.egg-info/requires.txt +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyod.egg-info/top_level.txt +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/pyproject.toml +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/requirements.txt +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/setup.cfg +0 -0
- {pyod-3.6.2 → pyod-3.6.4}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyod
|
|
3
|
-
Version: 3.6.
|
|
3
|
+
Version: 3.6.4
|
|
4
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
|
|
@@ -72,12 +72,21 @@ Requires-Dist: librosa>=0.10; extra == "all"
|
|
|
72
72
|
Requires-Dist: soundfile; extra == "all"
|
|
73
73
|
Dynamic: license-file
|
|
74
74
|
|
|
75
|
+
.. image:: https://raw.githubusercontent.com/yzhao062/pyod/master/brand/pyod-icon.svg
|
|
76
|
+
:target: https://pyod.dev
|
|
77
|
+
:alt: PyOD Ecosystem
|
|
78
|
+
:width: 84px
|
|
79
|
+
|
|
75
80
|
Python Outlier Detection (PyOD) 3
|
|
76
81
|
==================================
|
|
77
82
|
|
|
78
83
|
**PyOD 3: Agentic Anomaly Detection At Scale**
|
|
79
84
|
|
|
80
|
-
|badge_pypi| |badge_anaconda| |badge_docs| |badge_stars| |badge_forks| |badge_downloads| |badge_testing| |badge_coverage| |badge_maintainability| |badge_license| |badge_benchmark|
|
|
85
|
+
|badge_website| |badge_pypi| |badge_anaconda| |badge_docs| |badge_stars| |badge_forks| |badge_downloads| |badge_testing| |badge_coverage| |badge_maintainability| |badge_license| |badge_benchmark|
|
|
86
|
+
|
|
87
|
+
.. |badge_website| image:: https://img.shields.io/badge/website-pyod.dev-990000
|
|
88
|
+
:target: https://pyod.dev
|
|
89
|
+
:alt: Website
|
|
81
90
|
|
|
82
91
|
.. |badge_pypi| image:: https://img.shields.io/pypi/v/pyod.svg?color=brightgreen
|
|
83
92
|
:target: https://pypi.org/project/pyod/
|
|
@@ -155,8 +164,8 @@ Then pick the activation path that matches your agent stack:
|
|
|
155
164
|
|
|
156
165
|
.. code-block:: bash
|
|
157
166
|
|
|
158
|
-
# 1. Claude Code /
|
|
159
|
-
pyod install skill # Claude Code
|
|
167
|
+
# 1. Claude Code / Codex — enables the od-expert skill
|
|
168
|
+
pyod install skill # Claude Code: user-global (~/.claude/skills/)
|
|
160
169
|
pyod install skill --project # Codex: project-local (./skills/, Codex has no user-global dir)
|
|
161
170
|
|
|
162
171
|
# 2. Any MCP-compatible LLM — requires the optional mcp extra
|
|
@@ -795,6 +804,16 @@ More detailed instructions for running examples can be found in `examples direct
|
|
|
795
804
|
|
|
796
805
|
|
|
797
806
|
from pyod.models.knn import KNN # kNN detector
|
|
807
|
+
from pyod.utils.data import generate_data
|
|
808
|
+
|
|
809
|
+
contamination = 0.1 # percentage of outliers
|
|
810
|
+
n_train = 200 # number of training points
|
|
811
|
+
n_test = 100 # number of testing points
|
|
812
|
+
|
|
813
|
+
# generate sample data
|
|
814
|
+
X_train, X_test, y_train, y_test = generate_data(
|
|
815
|
+
n_train=n_train, n_test=n_test, n_features=2,
|
|
816
|
+
contamination=contamination, random_state=42)
|
|
798
817
|
|
|
799
818
|
# train kNN detector
|
|
800
819
|
clf_name = 'KNN'
|
|
@@ -832,14 +851,16 @@ More detailed instructions for running examples can be found in `examples direct
|
|
|
832
851
|
|
|
833
852
|
|
|
834
853
|
On Training Data:
|
|
835
|
-
KNN ROC:
|
|
854
|
+
KNN ROC:0.9992, precision @ rank n:0.95
|
|
836
855
|
|
|
837
856
|
On Test Data:
|
|
838
|
-
KNN ROC:0
|
|
857
|
+
KNN ROC:1.0, precision @ rank n:1.0
|
|
839
858
|
|
|
840
859
|
.. code-block:: python
|
|
841
860
|
|
|
842
861
|
|
|
862
|
+
from pyod.utils.example import visualize
|
|
863
|
+
|
|
843
864
|
visualize(clf_name, X_train, y_train, X_test, y_test, y_train_pred,
|
|
844
865
|
y_test_pred, show_figure=True, save_figure=False)
|
|
845
866
|
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
.. image:: https://raw.githubusercontent.com/yzhao062/pyod/master/brand/pyod-icon.svg
|
|
2
|
+
:target: https://pyod.dev
|
|
3
|
+
:alt: PyOD Ecosystem
|
|
4
|
+
:width: 84px
|
|
5
|
+
|
|
1
6
|
Python Outlier Detection (PyOD) 3
|
|
2
7
|
==================================
|
|
3
8
|
|
|
4
9
|
**PyOD 3: Agentic Anomaly Detection At Scale**
|
|
5
10
|
|
|
6
|
-
|badge_pypi| |badge_anaconda| |badge_docs| |badge_stars| |badge_forks| |badge_downloads| |badge_testing| |badge_coverage| |badge_maintainability| |badge_license| |badge_benchmark|
|
|
11
|
+
|badge_website| |badge_pypi| |badge_anaconda| |badge_docs| |badge_stars| |badge_forks| |badge_downloads| |badge_testing| |badge_coverage| |badge_maintainability| |badge_license| |badge_benchmark|
|
|
12
|
+
|
|
13
|
+
.. |badge_website| image:: https://img.shields.io/badge/website-pyod.dev-990000
|
|
14
|
+
:target: https://pyod.dev
|
|
15
|
+
:alt: Website
|
|
7
16
|
|
|
8
17
|
.. |badge_pypi| image:: https://img.shields.io/pypi/v/pyod.svg?color=brightgreen
|
|
9
18
|
:target: https://pypi.org/project/pyod/
|
|
@@ -81,8 +90,8 @@ Then pick the activation path that matches your agent stack:
|
|
|
81
90
|
|
|
82
91
|
.. code-block:: bash
|
|
83
92
|
|
|
84
|
-
# 1. Claude Code /
|
|
85
|
-
pyod install skill # Claude Code
|
|
93
|
+
# 1. Claude Code / Codex — enables the od-expert skill
|
|
94
|
+
pyod install skill # Claude Code: user-global (~/.claude/skills/)
|
|
86
95
|
pyod install skill --project # Codex: project-local (./skills/, Codex has no user-global dir)
|
|
87
96
|
|
|
88
97
|
# 2. Any MCP-compatible LLM — requires the optional mcp extra
|
|
@@ -721,6 +730,16 @@ More detailed instructions for running examples can be found in `examples direct
|
|
|
721
730
|
|
|
722
731
|
|
|
723
732
|
from pyod.models.knn import KNN # kNN detector
|
|
733
|
+
from pyod.utils.data import generate_data
|
|
734
|
+
|
|
735
|
+
contamination = 0.1 # percentage of outliers
|
|
736
|
+
n_train = 200 # number of training points
|
|
737
|
+
n_test = 100 # number of testing points
|
|
738
|
+
|
|
739
|
+
# generate sample data
|
|
740
|
+
X_train, X_test, y_train, y_test = generate_data(
|
|
741
|
+
n_train=n_train, n_test=n_test, n_features=2,
|
|
742
|
+
contamination=contamination, random_state=42)
|
|
724
743
|
|
|
725
744
|
# train kNN detector
|
|
726
745
|
clf_name = 'KNN'
|
|
@@ -758,14 +777,16 @@ More detailed instructions for running examples can be found in `examples direct
|
|
|
758
777
|
|
|
759
778
|
|
|
760
779
|
On Training Data:
|
|
761
|
-
KNN ROC:
|
|
780
|
+
KNN ROC:0.9992, precision @ rank n:0.95
|
|
762
781
|
|
|
763
782
|
On Test Data:
|
|
764
|
-
KNN ROC:0
|
|
783
|
+
KNN ROC:1.0, precision @ rank n:1.0
|
|
765
784
|
|
|
766
785
|
.. code-block:: python
|
|
767
786
|
|
|
768
787
|
|
|
788
|
+
from pyod.utils.example import visualize
|
|
789
|
+
|
|
769
790
|
visualize(clf_name, X_train, y_train, X_test, y_test, y_train_pred,
|
|
770
791
|
y_test_pred, show_figure=True, save_figure=False)
|
|
771
792
|
|
|
@@ -106,7 +106,7 @@ class ABOD(BaseDetector):
|
|
|
106
106
|
the proportion of outliers in the data set. Used when fitting to
|
|
107
107
|
define the threshold on the decision function.
|
|
108
108
|
|
|
109
|
-
n_neighbors : int, optional (default=
|
|
109
|
+
n_neighbors : int, optional (default=5)
|
|
110
110
|
Number of neighbors to use by default for k neighbors queries.
|
|
111
111
|
|
|
112
112
|
method: str, optional (default='fast')
|
|
@@ -155,7 +155,7 @@ class ABOD(BaseDetector):
|
|
|
155
155
|
``decision_scores_``. The threshold is calculated for generating
|
|
156
156
|
binary outlier labels.
|
|
157
157
|
|
|
158
|
-
labels_ :
|
|
158
|
+
labels_ : numpy array of shape (n_samples,)
|
|
159
159
|
The binary labels of the training data. 0 stands for inliers
|
|
160
160
|
and 1 for outliers/anomalies. It is generated by applying
|
|
161
161
|
``threshold_`` on ``decision_scores_``.
|
|
@@ -43,7 +43,7 @@ class ALAD(BaseDetector):
|
|
|
43
43
|
Activation function to use for hidden layers in encoder and decoder
|
|
44
44
|
(i.e. generator).
|
|
45
45
|
|
|
46
|
-
epochs : int, optional (default=
|
|
46
|
+
epochs : int, optional (default=200)
|
|
47
47
|
Number of epochs to train the model.
|
|
48
48
|
|
|
49
49
|
batch_size : int, optional (default=32)
|
|
@@ -52,6 +52,9 @@ class ALAD(BaseDetector):
|
|
|
52
52
|
dropout_rate : float in (0., 1), optional (default=0.2)
|
|
53
53
|
The dropout to be used across all layers.
|
|
54
54
|
|
|
55
|
+
latent_dim : int, optional (default=2)
|
|
56
|
+
Dimensionality of the latent space.
|
|
57
|
+
|
|
55
58
|
dec_layers : list, optional (default=[5,10,25])
|
|
56
59
|
List that indicates the number of nodes per hidden layer for the d
|
|
57
60
|
ecoder network.
|
|
@@ -77,25 +80,31 @@ class ALAD(BaseDetector):
|
|
|
77
80
|
discriminator_xz.
|
|
78
81
|
Thus, [10,10] indicates 2 hidden layers having each 10 nodes.
|
|
79
82
|
|
|
80
|
-
learning_rate_gen: float in (0., 1), optional (default=0.
|
|
83
|
+
learning_rate_gen: float in (0., 1), optional (default=0.0001)
|
|
81
84
|
learning rate of training the encoder and decoder
|
|
82
85
|
|
|
83
|
-
learning_rate_disc: float in (0., 1), optional (default=0.
|
|
86
|
+
learning_rate_disc: float in (0., 1), optional (default=0.0001)
|
|
84
87
|
learning rate of training the discriminators
|
|
85
88
|
|
|
86
89
|
add_recon_loss: bool optional (default=False)
|
|
87
90
|
add an extra loss for encoder and decoder based on the reconstruction
|
|
88
91
|
error
|
|
89
92
|
|
|
93
|
+
add_disc_zz_loss : bool, optional (default=True)
|
|
94
|
+
If True, include the latent-space discriminator loss.
|
|
95
|
+
|
|
90
96
|
lambda_recon_loss: float in (0., 1), optional (default=0.1)
|
|
91
97
|
if ``add_recon_loss= True``, the reconstruction loss gets multiplied
|
|
92
98
|
by ``lambda_recon_loss`` and added to the total loss for the generator
|
|
93
99
|
(i.e. encoder and decoder).
|
|
94
100
|
|
|
95
|
-
|
|
101
|
+
spectral_normalization : bool, optional (default=False)
|
|
102
|
+
If True, apply spectral normalization to discriminator layers.
|
|
103
|
+
|
|
104
|
+
preprocessing : bool, optional (default=False)
|
|
96
105
|
If True, apply standardization on the data.
|
|
97
106
|
|
|
98
|
-
verbose : int, optional (default=
|
|
107
|
+
verbose : int, optional (default=0)
|
|
99
108
|
Verbosity mode.
|
|
100
109
|
- 0 = silent
|
|
101
110
|
- 1 = progress bar
|
|
@@ -124,7 +133,7 @@ class ALAD(BaseDetector):
|
|
|
124
133
|
``decision_scores_``. The threshold is calculated for generating
|
|
125
134
|
binary outlier labels.
|
|
126
135
|
|
|
127
|
-
labels_ :
|
|
136
|
+
labels_ : numpy array of shape (n_samples,)
|
|
128
137
|
The binary labels of the training data. 0 stands for inliers
|
|
129
138
|
and 1 for outliers/anomalies. It is generated by applying
|
|
130
139
|
``threshold_`` on ``decision_scores_``.
|
|
@@ -114,7 +114,6 @@ class AnoGAN(BaseDetector):
|
|
|
114
114
|
output_activation : str, optional (default=None)
|
|
115
115
|
Activation function to use for output layer.
|
|
116
116
|
|
|
117
|
-
|
|
118
117
|
activation_hidden : str, optional (default='tanh')
|
|
119
118
|
Activation function to use for output layer.
|
|
120
119
|
|
|
@@ -127,6 +126,9 @@ class AnoGAN(BaseDetector):
|
|
|
127
126
|
dropout_rate : float in (0., 1), optional (default=0.2)
|
|
128
127
|
The dropout to be used across all layers.
|
|
129
128
|
|
|
129
|
+
latent_dim_G : int, optional (default=2)
|
|
130
|
+
Dimensionality of the generator's latent space.
|
|
131
|
+
|
|
130
132
|
G_layers : list, optional (default=[20,10,3,10,20])
|
|
131
133
|
List that indicates the number of nodes per hidden layer for the
|
|
132
134
|
generator. Thus, [10,10] indicates 2 hidden layers having each 10 nodes.
|
|
@@ -136,28 +138,26 @@ class AnoGAN(BaseDetector):
|
|
|
136
138
|
discriminator. Thus, [10,10] indicates 2 hidden layers having each 10
|
|
137
139
|
nodes.
|
|
138
140
|
|
|
139
|
-
|
|
140
|
-
learning_rate: float in (0., 1), optional (default=0.001)
|
|
141
|
+
learning_rate : float in (0., 1), optional (default=0.001)
|
|
141
142
|
learning rate of training the network
|
|
142
143
|
|
|
143
|
-
index_D_layer_for_recon_error: int, optional (default = 1)
|
|
144
|
+
index_D_layer_for_recon_error : int, optional (default = 1)
|
|
144
145
|
This is the index of the hidden layer in the discriminator for which
|
|
145
146
|
the reconstruction error will be determined between query sample and
|
|
146
147
|
the sample created from the latent space.
|
|
147
148
|
|
|
148
|
-
learning_rate_query: float in (0., 1), optional (default=0.
|
|
149
|
+
learning_rate_query : float in (0., 1), optional (default=0.01)
|
|
149
150
|
learning rate for the backpropagation steps needed to find a point in
|
|
150
151
|
the latent space of the generator that approximate the query sample
|
|
151
152
|
|
|
152
|
-
|
|
153
|
-
epochs_query: int, optional (default=20)
|
|
153
|
+
epochs_query : int, optional (default=20)
|
|
154
154
|
Number of epochs to approximate the query sample in the latent space
|
|
155
155
|
of the generator
|
|
156
156
|
|
|
157
|
-
preprocessing : bool, optional (default=
|
|
157
|
+
preprocessing : bool, optional (default=False)
|
|
158
158
|
If True, apply standardization on the data.
|
|
159
159
|
|
|
160
|
-
verbose : int, optional (default=
|
|
160
|
+
verbose : int, optional (default=0)
|
|
161
161
|
Verbosity mode.
|
|
162
162
|
- 0 = silent
|
|
163
163
|
- 1 = progress bar
|
|
@@ -167,6 +167,9 @@ class AnoGAN(BaseDetector):
|
|
|
167
167
|
the proportion of outliers in the data set. When fitting this is used
|
|
168
168
|
to define the threshold on the decision function.
|
|
169
169
|
|
|
170
|
+
device : str, torch.device or None, optional (default=None)
|
|
171
|
+
Device used for computation. If None, use the PyTorch default device.
|
|
172
|
+
|
|
170
173
|
Attributes
|
|
171
174
|
----------
|
|
172
175
|
|
|
@@ -182,7 +185,7 @@ class AnoGAN(BaseDetector):
|
|
|
182
185
|
``decision_scores_``. The threshold is calculated for generating
|
|
183
186
|
binary outlier labels.
|
|
184
187
|
|
|
185
|
-
labels_ :
|
|
188
|
+
labels_ : numpy array of shape (n_samples,)
|
|
186
189
|
The binary labels of the training data. 0 stands for inliers
|
|
187
190
|
and 1 for outliers/anomalies. It is generated by applying
|
|
188
191
|
``threshold_`` on ``decision_scores_``.
|
|
@@ -111,7 +111,7 @@ class AutoEncoder(BaseDeepLearningDetector):
|
|
|
111
111
|
``decision_scores_``. The threshold is calculated for generating
|
|
112
112
|
binary outlier labels.
|
|
113
113
|
|
|
114
|
-
labels_ :
|
|
114
|
+
labels_ : numpy array of shape (n_samples,)
|
|
115
115
|
The binary labels of the training data. 0 stands for inliers
|
|
116
116
|
and 1 for outliers/anomalies. It is generated by applying
|
|
117
117
|
``threshold_`` on ``decision_scores_``.
|
|
@@ -50,7 +50,7 @@ class BaseDetector(BaseEstimator, metaclass=abc.ABCMeta):
|
|
|
50
50
|
``decision_scores_``. The threshold is calculated for generating
|
|
51
51
|
binary outlier labels.
|
|
52
52
|
|
|
53
|
-
labels_ :
|
|
53
|
+
labels_ : numpy array of shape (n_samples,)
|
|
54
54
|
The binary labels of the training data. 0 stands for inliers
|
|
55
55
|
and 1 for outliers/anomalies. It is generated by applying
|
|
56
56
|
``threshold_`` on ``decision_scores_``.
|
|
@@ -92,11 +92,11 @@ class BaseDeepLearningDetector(BaseDetector):
|
|
|
92
92
|
- 1 = progress bar
|
|
93
93
|
- 2 = one line per epoch.
|
|
94
94
|
|
|
95
|
-
optimizer_params : dict, optional (default=
|
|
95
|
+
optimizer_params : dict, optional (default={})
|
|
96
96
|
Additional parameters for the optimizer.
|
|
97
97
|
For example, `optimizer_params={'weight_decay': 1e-4}`.
|
|
98
98
|
|
|
99
|
-
criterion_params : dict, optional (default=
|
|
99
|
+
criterion_params : dict, optional (default={})
|
|
100
100
|
Additional parameters for the criterion.
|
|
101
101
|
For example, `criterion_params={'reduction': 'sum'}`.
|
|
102
102
|
"""
|
|
@@ -91,6 +91,11 @@ class CBLOF(BaseDetector):
|
|
|
91
91
|
number generator; If None, the random number generator is the
|
|
92
92
|
RandomState instance used by `np.random`.
|
|
93
93
|
|
|
94
|
+
n_jobs : int, optional (default=1)
|
|
95
|
+
Accepted for API compatibility but currently unused: the value is
|
|
96
|
+
neither stored on the estimator nor forwarded to the clustering
|
|
97
|
+
step, so ``get_params()`` reports ``None`` for it and ``clone()``
|
|
98
|
+
does not carry it over. See issue #713.
|
|
94
99
|
|
|
95
100
|
Attributes
|
|
96
101
|
----------
|
|
@@ -126,7 +131,7 @@ class CBLOF(BaseDetector):
|
|
|
126
131
|
``decision_scores_``. The threshold is calculated for generating
|
|
127
132
|
binary outlier labels.
|
|
128
133
|
|
|
129
|
-
labels_ :
|
|
134
|
+
labels_ : numpy array of shape (n_samples,)
|
|
130
135
|
The binary labels of the training data. 0 stands for inliers
|
|
131
136
|
and 1 for outliers/anomalies. It is generated by applying
|
|
132
137
|
``threshold_`` on ``decision_scores_``.
|
|
@@ -150,7 +150,7 @@ class CD(BaseDetector):
|
|
|
150
150
|
a modified z-score (based on the median absolute deviation) greater
|
|
151
151
|
than this value will be classified as outliers.
|
|
152
152
|
|
|
153
|
-
labels_ :
|
|
153
|
+
labels_ : numpy array of shape (n_samples,)
|
|
154
154
|
The binary labels of the training data. 0 stands for inliers
|
|
155
155
|
and 1 for outliers/anomalies. It is generated by applying
|
|
156
156
|
``threshold_`` on ``decision_scores_``.
|
|
@@ -68,7 +68,7 @@ class COF(BaseDetector):
|
|
|
68
68
|
``decision_scores_``. The threshold is calculated for generating
|
|
69
69
|
binary outlier labels.
|
|
70
70
|
|
|
71
|
-
labels_ :
|
|
71
|
+
labels_ : numpy array of shape (n_samples,)
|
|
72
72
|
The binary labels of the training data. 0 stands for inliers
|
|
73
73
|
and 1 for outliers/anomalies. It is generated by applying
|
|
74
74
|
``threshold_`` on ``decision_scores_``.
|
|
@@ -79,7 +79,7 @@ class COPOD(BaseDetector):
|
|
|
79
79
|
``n_samples * contamination`` most abnormal samples in
|
|
80
80
|
``decision_scores_``. The threshold is calculated for generating
|
|
81
81
|
binary outlier labels.
|
|
82
|
-
labels_ :
|
|
82
|
+
labels_ : numpy array of shape (n_samples,)
|
|
83
83
|
The binary labels of the training data. 0 stands for inliers
|
|
84
84
|
and 1 for outliers/anomalies. It is generated by applying
|
|
85
85
|
``threshold_`` on ``decision_scores_``.
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
# License: BSD 2 clause
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
import copy
|
|
10
|
+
|
|
9
11
|
import numpy as np
|
|
10
12
|
|
|
11
13
|
try:
|
|
@@ -65,9 +67,11 @@ class InnerDeepSVDD(nn.Module):
|
|
|
65
67
|
dropout_rate : float in (0., 1), optional (default=0.2)
|
|
66
68
|
The dropout to be used across all layers.
|
|
67
69
|
|
|
68
|
-
l2_regularizer : float in (0., 1), optional (default=0.
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
l2_regularizer : float in (0., 1), optional (default=0.5e-6)
|
|
71
|
+
Weight decay passed to the optimizer (L2 regularization on the
|
|
72
|
+
network weights). The default matches the reference Deep SVDD
|
|
73
|
+
implementation; larger values push the weights toward zero and can
|
|
74
|
+
collapse the hypersphere.
|
|
71
75
|
"""
|
|
72
76
|
|
|
73
77
|
def __init__(self, n_features, use_ae,
|
|
@@ -85,20 +89,14 @@ class InnerDeepSVDD(nn.Module):
|
|
|
85
89
|
self.model = self._build_model()
|
|
86
90
|
|
|
87
91
|
def _init_c(self, X_norm, eps=0.1):
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
)
|
|
94
|
-
output = self.model(X_norm)
|
|
95
|
-
|
|
96
|
-
out = intermediate_output['net_output']
|
|
97
|
-
hook_handle.remove()
|
|
98
|
-
|
|
99
|
-
self.c = torch.mean(out, dim=0)
|
|
92
|
+
# The center must live in the same representation space used by the
|
|
93
|
+
# loss. In particular, include the activation after ``net_output``.
|
|
94
|
+
self.eval()
|
|
95
|
+
with torch.no_grad():
|
|
96
|
+
self.c = torch.mean(self.model(X_norm), dim=0)
|
|
100
97
|
self.c[(torch.abs(self.c) < eps) & (self.c < 0)] = -eps
|
|
101
98
|
self.c[(torch.abs(self.c) < eps) & (self.c > 0)] = eps
|
|
99
|
+
return self.c
|
|
102
100
|
|
|
103
101
|
def _build_model(self):
|
|
104
102
|
layers = nn.Sequential()
|
|
@@ -153,15 +151,29 @@ class DeepSVDD(BaseDetector):
|
|
|
153
151
|
data by calculating the distance from center
|
|
154
152
|
See :cite:`ruff2018deepsvdd` for details.
|
|
155
153
|
|
|
154
|
+
This one-class objective works best when the fitting samples are
|
|
155
|
+
predominantly normal; PyOD fits it unsupervised on contaminated data,
|
|
156
|
+
so accuracy can trail a clean one-class setup. With ``use_ae=True``
|
|
157
|
+
the hypersphere distance and the anomaly scores are computed from the
|
|
158
|
+
decoder reconstructions; the encoder's latent layer remains an
|
|
159
|
+
intermediate activation.
|
|
160
|
+
|
|
156
161
|
Parameters
|
|
157
162
|
----------
|
|
158
163
|
n_features: int,
|
|
159
164
|
Number of features in the input data.
|
|
160
165
|
|
|
161
|
-
c: float, optional (default=
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
166
|
+
c: float, numpy array, or torch tensor, optional (default=None)
|
|
167
|
+
Hypersphere center, kept as configuration and never overwritten by
|
|
168
|
+
``fit``. When None (the default) the center is initialized from the
|
|
169
|
+
first forward pass of the freshly built network; set
|
|
170
|
+
``random_state`` for repeatable results. A scalar is expanded to
|
|
171
|
+
the network output width; a vector must match that width, which is
|
|
172
|
+
``hidden_neurons[-1]`` normally and ``n_features`` when
|
|
173
|
+
``use_ae=True``. The value must be finite and not all zeros: an
|
|
174
|
+
all-zero center is the trivial solution of the Deep SVDD objective
|
|
175
|
+
for a bias-free network and collapses the hypersphere. The fitted
|
|
176
|
+
center is exposed as ``c_``.
|
|
165
177
|
|
|
166
178
|
use_ae: bool, optional (default=False)
|
|
167
179
|
The AutoEncoder type of DeepSVDD it reverse neurons from hidden_neurons
|
|
@@ -184,6 +196,9 @@ class DeepSVDD(BaseDetector):
|
|
|
184
196
|
String (name of optimizer) or optimizer instance.
|
|
185
197
|
See https://keras.io/optimizers/
|
|
186
198
|
|
|
199
|
+
learning_rate : float, optional (default=1e-4)
|
|
200
|
+
Learning rate used by the optimizer.
|
|
201
|
+
|
|
187
202
|
epochs : int, optional (default=100)
|
|
188
203
|
Number of epochs to train the model.
|
|
189
204
|
|
|
@@ -193,10 +208,11 @@ class DeepSVDD(BaseDetector):
|
|
|
193
208
|
dropout_rate : float in (0., 1), optional (default=0.2)
|
|
194
209
|
The dropout to be used across all layers.
|
|
195
210
|
|
|
196
|
-
l2_regularizer : float in (0., 1), optional (default=0.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
211
|
+
l2_regularizer : float in (0., 1), optional (default=0.5e-6)
|
|
212
|
+
Weight decay passed to the optimizer (L2 regularization on the
|
|
213
|
+
network weights). The default matches the reference Deep SVDD
|
|
214
|
+
implementation; larger values push the weights toward zero and
|
|
215
|
+
can collapse the hypersphere.
|
|
200
216
|
|
|
201
217
|
validation_size : float in (0., 1), optional (default=0.1)
|
|
202
218
|
The percentage of data to be used for validation.
|
|
@@ -204,6 +220,9 @@ class DeepSVDD(BaseDetector):
|
|
|
204
220
|
preprocessing : bool, optional (default=True)
|
|
205
221
|
If True, apply standardization on the data.
|
|
206
222
|
|
|
223
|
+
verbose : int, optional (default=1)
|
|
224
|
+
Verbosity mode.
|
|
225
|
+
|
|
207
226
|
random_state : random_state: int, RandomState instance or None, optional
|
|
208
227
|
(default=None)
|
|
209
228
|
If int, random_state is the seed used by the random
|
|
@@ -230,19 +249,27 @@ class DeepSVDD(BaseDetector):
|
|
|
230
249
|
``decision_scores_``. The threshold is calculated for generating
|
|
231
250
|
binary outlier labels.
|
|
232
251
|
|
|
233
|
-
labels_ :
|
|
252
|
+
labels_ : numpy array of shape (n_samples,)
|
|
234
253
|
The binary labels of the training data. 0 stands for inliers
|
|
235
254
|
and 1 for outliers/anomalies. It is generated by applying
|
|
236
255
|
``threshold_`` on ``decision_scores_``.
|
|
256
|
+
|
|
257
|
+
c_ : torch tensor of shape (output_width,)
|
|
258
|
+
The fitted hypersphere center actually used by the training
|
|
259
|
+
objective and by ``decision_function``. Either the center
|
|
260
|
+
initialized from the data (when ``c`` is None) or a validated
|
|
261
|
+
copy of the user-supplied ``c``.
|
|
237
262
|
"""
|
|
238
263
|
|
|
239
264
|
def __init__(self, n_features, c=None, use_ae=False, hidden_neurons=None,
|
|
240
265
|
hidden_activation='relu',
|
|
241
266
|
output_activation='sigmoid', optimizer='adam', epochs=100,
|
|
242
267
|
batch_size=32,
|
|
243
|
-
dropout_rate=0.2, l2_regularizer=0.
|
|
268
|
+
dropout_rate=0.2, l2_regularizer=0.5e-6,
|
|
269
|
+
validation_size=0.1,
|
|
244
270
|
preprocessing=True,
|
|
245
|
-
verbose=1, random_state=None, contamination=0.1
|
|
271
|
+
verbose=1, random_state=None, contamination=0.1,
|
|
272
|
+
learning_rate=1e-4):
|
|
246
273
|
super(DeepSVDD, self).__init__(contamination=contamination)
|
|
247
274
|
|
|
248
275
|
self.n_features = n_features
|
|
@@ -252,6 +279,7 @@ class DeepSVDD(BaseDetector):
|
|
|
252
279
|
self.hidden_activation = hidden_activation
|
|
253
280
|
self.output_activation = output_activation
|
|
254
281
|
self.optimizer = optimizer
|
|
282
|
+
self.learning_rate = learning_rate
|
|
255
283
|
self.epochs = epochs
|
|
256
284
|
self.batch_size = batch_size
|
|
257
285
|
self.dropout_rate = dropout_rate
|
|
@@ -315,9 +343,40 @@ class DeepSVDD(BaseDetector):
|
|
|
315
343
|
dropout_rate=self.dropout_rate,
|
|
316
344
|
l2_regularizer=self.l2_regularizer)
|
|
317
345
|
X_norm = torch.tensor(X_norm, dtype=torch.float32)
|
|
346
|
+
# ``c`` is a constructor parameter and must stay untouched so that
|
|
347
|
+
# get_params/clone round-trip and a refit re-initializes the center
|
|
348
|
+
# for the freshly built network. The fitted center lives in ``c_``.
|
|
318
349
|
if self.c is None:
|
|
319
|
-
|
|
320
|
-
|
|
350
|
+
center = self.model_._init_c(X_norm)
|
|
351
|
+
else:
|
|
352
|
+
center = torch.as_tensor(self.c, dtype=X_norm.dtype,
|
|
353
|
+
device=X_norm.device)
|
|
354
|
+
|
|
355
|
+
# The center must match the width of whatever ``forward()`` returns:
|
|
356
|
+
# the decoder reconstruction under use_ae, else the final embedding.
|
|
357
|
+
output_width = (self.n_features if self.use_ae
|
|
358
|
+
else self.hidden_neurons[-1])
|
|
359
|
+
if center.ndim == 0:
|
|
360
|
+
center = center.repeat(output_width)
|
|
361
|
+
elif center.ndim != 1 or center.numel() != output_width:
|
|
362
|
+
raise ValueError(
|
|
363
|
+
f'DeepSVDD: c must be a scalar or a vector of length '
|
|
364
|
+
f'{output_width}, got shape {tuple(center.shape)}.')
|
|
365
|
+
if not torch.isfinite(center).all():
|
|
366
|
+
raise ValueError('DeepSVDD: c must contain only finite values.')
|
|
367
|
+
if not torch.any(center != 0):
|
|
368
|
+
# An all-zero center is the trivial solution of the Deep SVDD
|
|
369
|
+
# objective for a bias-free network (Ruff et al., ICML 2018,
|
|
370
|
+
# Proposition 1): all-zero weights map every input to it, so
|
|
371
|
+
# training would converge to a collapsed hypersphere.
|
|
372
|
+
raise ValueError(
|
|
373
|
+
'DeepSVDD: the hypersphere center is all zeros, which makes '
|
|
374
|
+
'the trivial all-zero-weights solution optimal and collapses '
|
|
375
|
+
'the hypersphere. Pass c=None to initialize the center from '
|
|
376
|
+
'the data, or supply a non-zero center.')
|
|
377
|
+
# clone() so a mutable user-supplied array/tensor cannot alias (and
|
|
378
|
+
# later mutate) the fitted center.
|
|
379
|
+
self.c_ = center.detach().clone()
|
|
321
380
|
|
|
322
381
|
# Predict on X itself and calculate the reconstruction error as
|
|
323
382
|
# the outlier scores. Noted X_norm was shuffled has to recreate
|
|
@@ -334,32 +393,43 @@ class DeepSVDD(BaseDetector):
|
|
|
334
393
|
best_loss = float('inf')
|
|
335
394
|
best_model_dict = None
|
|
336
395
|
|
|
337
|
-
optimizer = optimizer_dict[self.optimizer](
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
[torch.linalg.norm(w) for w in self.model_.parameters()])
|
|
341
|
-
|
|
396
|
+
optimizer = optimizer_dict[self.optimizer](
|
|
397
|
+
self.model_.parameters(), lr=self.learning_rate,
|
|
398
|
+
weight_decay=self.l2_regularizer)
|
|
342
399
|
for epoch in range(self.epochs):
|
|
343
400
|
self.model_.train()
|
|
344
401
|
epoch_loss = 0
|
|
345
402
|
for batch_x, _ in dataloader:
|
|
346
403
|
optimizer.zero_grad()
|
|
347
404
|
outputs = self.model_(batch_x)
|
|
348
|
-
dist = torch.sum((outputs - self.
|
|
405
|
+
dist = torch.sum((outputs - self.c_) ** 2, dim=-1)
|
|
406
|
+
# L2 regularization term, recomputed every step so that it
|
|
407
|
+
# is part of the current batch's computation graph
|
|
408
|
+
w_d = 1e-6 * sum(
|
|
409
|
+
[torch.linalg.norm(w) for w in self.model_.parameters()])
|
|
349
410
|
if self.use_ae:
|
|
350
411
|
loss = torch.mean(dist) + w_d + torch.mean(
|
|
351
412
|
torch.square(outputs - batch_x))
|
|
352
413
|
else:
|
|
353
414
|
loss = torch.mean(dist) + w_d
|
|
354
415
|
|
|
355
|
-
|
|
416
|
+
loss.backward()
|
|
356
417
|
optimizer.step()
|
|
357
418
|
epoch_loss += loss.item()
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
419
|
+
# keep the best performing model across epochs (epoch_loss is the
|
|
420
|
+
# accumulated loss over the whole epoch, so this must run after
|
|
421
|
+
# the batch loop, not inside it)
|
|
422
|
+
if epoch_loss < best_loss:
|
|
423
|
+
best_loss = epoch_loss
|
|
424
|
+
# state_dict() returns references to the live parameters, so
|
|
425
|
+
# a later optimizer.step() would mutate this snapshot in
|
|
426
|
+
# place and leave the final weights here instead of the best
|
|
427
|
+
# ones. Copy it.
|
|
428
|
+
best_model_dict = copy.deepcopy(self.model_.state_dict())
|
|
361
429
|
print(f"Epoch {epoch + 1}/{self.epochs}, Loss: {epoch_loss}")
|
|
362
430
|
self.best_model_dict = best_model_dict
|
|
431
|
+
if self.best_model_dict is not None:
|
|
432
|
+
self.model_.load_state_dict(self.best_model_dict)
|
|
363
433
|
|
|
364
434
|
self.decision_scores_ = self.decision_function(X)
|
|
365
435
|
self._process_decision_scores()
|
|
@@ -394,6 +464,6 @@ class DeepSVDD(BaseDetector):
|
|
|
394
464
|
self.model_.eval()
|
|
395
465
|
with torch.no_grad():
|
|
396
466
|
outputs = self.model_(X_norm)
|
|
397
|
-
dist = torch.sum((outputs - self.
|
|
467
|
+
dist = torch.sum((outputs - self.c_) ** 2, dim=-1)
|
|
398
468
|
anomaly_scores = dist.numpy()
|
|
399
469
|
return anomaly_scores
|