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.
Files changed (133) hide show
  1. {pyod-3.6.2/pyod.egg-info → pyod-3.6.4}/PKG-INFO +27 -6
  2. {pyod-3.6.2 → pyod-3.6.4}/README.rst +26 -5
  3. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/abod.py +2 -2
  4. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/alad.py +15 -6
  5. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/anogan.py +13 -10
  6. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/auto_encoder.py +1 -1
  7. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/base.py +1 -1
  8. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/base_dl.py +2 -2
  9. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/cblof.py +6 -1
  10. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/cd.py +1 -1
  11. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/cof.py +1 -1
  12. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/copod.py +1 -1
  13. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/deep_svdd.py +109 -39
  14. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/dif.py +9 -10
  15. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ecod.py +1 -1
  16. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/feature_bagging.py +1 -1
  17. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/gmm.py +1 -7
  18. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/hbos.py +1 -1
  19. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/hdbscan.py +1 -1
  20. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/iforest.py +1 -1
  21. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/inne.py +1 -1
  22. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/kde.py +1 -1
  23. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/knn.py +1 -1
  24. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/kpca.py +6 -1
  25. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/lmdd.py +1 -1
  26. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/loci.py +1 -1
  27. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/loda.py +1 -1
  28. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/lof.py +1 -1
  29. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/lscp.py +1 -1
  30. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/lunar.py +9 -5
  31. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/mad.py +5 -1
  32. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/mcd.py +1 -1
  33. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/mo_gaal.py +1 -1
  34. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ocsvm.py +1 -1
  35. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pca.py +1 -1
  36. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/qmcd.py +6 -2
  37. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/rgraph.py +16 -30
  38. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/rod.py +1 -1
  39. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/sampling.py +1 -1
  40. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/so_gaal.py +1 -1
  41. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/so_gaal_new.py +61 -1
  42. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/sod.py +1 -1
  43. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/sos.py +1 -1
  44. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/suod.py +4 -10
  45. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/vae.py +1 -1
  46. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/xgbod.py +8 -9
  47. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/encoders/__init__.py +21 -2
  48. {pyod-3.6.2 → pyod-3.6.4}/pyod/version.py +1 -1
  49. {pyod-3.6.2 → pyod-3.6.4/pyod.egg-info}/PKG-INFO +27 -6
  50. {pyod-3.6.2 → pyod-3.6.4}/LICENSE +0 -0
  51. {pyod-3.6.2 → pyod-3.6.4}/MANIFEST.in +0 -0
  52. {pyod-3.6.2 → pyod-3.6.4}/pyod/__init__.py +0 -0
  53. {pyod-3.6.2 → pyod-3.6.4}/pyod/cli.py +0 -0
  54. {pyod-3.6.2 → pyod-3.6.4}/pyod/mcp_server.py +0 -0
  55. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/__init__.py +0 -0
  56. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/_pyg_utils.py +0 -0
  57. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/_ts_utils.py +0 -0
  58. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ae1svm.py +0 -0
  59. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/audio_ae.py +0 -0
  60. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/combination.py +0 -0
  61. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/devnet.py +0 -0
  62. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/embedding.py +0 -0
  63. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/gaal_base.py +0 -0
  64. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_anomalous.py +0 -0
  65. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_anomalydae.py +0 -0
  66. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_cola.py +0 -0
  67. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_conad.py +0 -0
  68. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_dominant.py +0 -0
  69. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_guide.py +0 -0
  70. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_radar.py +0 -0
  71. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/pyg_scan.py +0 -0
  72. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/sklearn_base.py +0 -0
  73. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/thresholds.py +0 -0
  74. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ts_anomaly_transformer.py +0 -0
  75. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ts_kshape.py +0 -0
  76. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ts_lstm.py +0 -0
  77. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ts_matrix_profile.py +0 -0
  78. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ts_od.py +0 -0
  79. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ts_sand.py +0 -0
  80. {pyod-3.6.2 → pyod-3.6.4}/pyod/models/ts_spectral_residual.py +0 -0
  81. {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/__init__.py +0 -0
  82. {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/SKILL.md +0 -0
  83. {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/__init__.py +0 -0
  84. {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/references/graph.md +0 -0
  85. {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/references/pitfalls.md +0 -0
  86. {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/references/tabular.md +0 -0
  87. {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/references/text_image.md +0 -0
  88. {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/references/time_series.md +0 -0
  89. {pyod-3.6.2 → pyod-3.6.4}/pyod/skills/od_expert/references/workflow.md +0 -0
  90. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/__init__.py +0 -0
  91. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/_detector_factory.py +0 -0
  92. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/_kb_router.py +0 -0
  93. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/_llm.py +0 -0
  94. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/_nl_feedback.py +0 -0
  95. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/_quality_metrics.py +0 -0
  96. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/ad_engine.py +0 -0
  97. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/auto_model_selector.py +0 -0
  98. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/data.py +0 -0
  99. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/encoders/audio.py +0 -0
  100. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/encoders/huggingface.py +0 -0
  101. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/encoders/openai_encoder.py +0 -0
  102. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/encoders/sentence_transformer.py +0 -0
  103. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/example.py +0 -0
  104. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/investigation.py +0 -0
  105. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/knowledge/__init__.py +0 -0
  106. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/knowledge/algorithms.json +0 -0
  107. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/knowledge/benchmarks.json +0 -0
  108. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/knowledge/papers.json +0 -0
  109. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/knowledge/routing_rules.json +0 -0
  110. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/AE1SVM.json +0 -0
  111. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/ALAD.json +0 -0
  112. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/AnoGAN.json +0 -0
  113. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/AutoEncoder.json +0 -0
  114. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/Deep SVDD.json +0 -0
  115. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/DevNet.json +0 -0
  116. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/LUNAR.json +0 -0
  117. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/MO-GAAL.json +0 -0
  118. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/SO-GAAL.json +0 -0
  119. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/VAE.json +0 -0
  120. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/model_analysis_jsons/__init__.py +0 -0
  121. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/persistence.py +0 -0
  122. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/stat_models.py +0 -0
  123. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/torch_utility.py +0 -0
  124. {pyod-3.6.2 → pyod-3.6.4}/pyod/utils/utility.py +0 -0
  125. {pyod-3.6.2 → pyod-3.6.4}/pyod.egg-info/SOURCES.txt +0 -0
  126. {pyod-3.6.2 → pyod-3.6.4}/pyod.egg-info/dependency_links.txt +0 -0
  127. {pyod-3.6.2 → pyod-3.6.4}/pyod.egg-info/entry_points.txt +0 -0
  128. {pyod-3.6.2 → pyod-3.6.4}/pyod.egg-info/requires.txt +0 -0
  129. {pyod-3.6.2 → pyod-3.6.4}/pyod.egg-info/top_level.txt +0 -0
  130. {pyod-3.6.2 → pyod-3.6.4}/pyproject.toml +0 -0
  131. {pyod-3.6.2 → pyod-3.6.4}/requirements.txt +0 -0
  132. {pyod-3.6.2 → pyod-3.6.4}/setup.cfg +0 -0
  133. {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.2
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 / Claude Desktop / Codex — enables the od-expert skill
159
- pyod install skill # Claude Code / Desktop: user-global (~/.claude/skills/)
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:1.0, precision @ rank n:1.0
854
+ KNN ROC:0.9992, precision @ rank n:0.95
836
855
 
837
856
  On Test Data:
838
- KNN ROC:0.9989, precision @ rank n:0.9
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 / Claude Desktop / Codex — enables the od-expert skill
85
- pyod install skill # Claude Code / Desktop: user-global (~/.claude/skills/)
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:1.0, precision @ rank n:1.0
780
+ KNN ROC:0.9992, precision @ rank n:0.95
762
781
 
763
782
  On Test Data:
764
- KNN ROC:0.9989, precision @ rank n:0.9
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=10)
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_ : int, either 0 or 1
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=500)
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.001)
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.001)
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
- preprocessing : bool, optional (default=True)
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=1)
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_ : int, either 0 or 1
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.001)
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=True)
157
+ preprocessing : bool, optional (default=False)
158
158
  If True, apply standardization on the data.
159
159
 
160
- verbose : int, optional (default=1)
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_ : int, either 0 or 1
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_ : int, either 0 or 1
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_ : int, either 0 or 1
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=None)
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=None)
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_ : int, either 0 or 1
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_ : int, either 0 or 1
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_ : int, either 0 or 1
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_ : int, either 0 or 1
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.1)
69
- The regularization strength of activity_regularizer
70
- applied on each layer. By default, l2 regularizer is used. See
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
- intermediate_output = {}
89
- hook_handle = self.model._modules.get(
90
- 'net_output').register_forward_hook(
91
- lambda module, input, output: intermediate_output.update(
92
- {'net_output': output})
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='forwad_nn_pass')
162
- Deep SVDD center, the default will be calculated based on network
163
- initialization first forward pass. To get repeated results set
164
- random_state if c is set to None.
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.1)
197
- The regularization strength of activity_regularizer
198
- applied on each layer. By default, l2 regularizer is used. See
199
- https://keras.io/regularizers/
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_ : int, either 0 or 1
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.1, validation_size=0.1,
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
- self.c = 0.0
320
- self.model_._init_c(X_norm)
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](self.model_.parameters(),
338
- weight_decay=self.l2_regularizer)
339
- w_d = 1e-6 * sum(
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.c) ** 2, dim=-1)
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
- # loss.backward()
416
+ loss.backward()
356
417
  optimizer.step()
357
418
  epoch_loss += loss.item()
358
- if epoch_loss < best_loss:
359
- best_loss = epoch_loss
360
- best_model_dict = self.model_.state_dict()
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.c) ** 2, dim=-1)
467
+ dist = torch.sum((outputs - self.c_) ** 2, dim=-1)
398
468
  anomaly_scores = dist.numpy()
399
469
  return anomaly_scores