pyod 3.6.2__tar.gz → 3.6.3__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.3}/PKG-INFO +11 -2
- {pyod-3.6.2 → pyod-3.6.3}/README.rst +10 -1
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/deep_svdd.py +105 -38
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/encoders/__init__.py +21 -2
- {pyod-3.6.2 → pyod-3.6.3}/pyod/version.py +1 -1
- {pyod-3.6.2 → pyod-3.6.3/pyod.egg-info}/PKG-INFO +11 -2
- {pyod-3.6.2 → pyod-3.6.3}/LICENSE +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/MANIFEST.in +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/__init__.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/cli.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/mcp_server.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/__init__.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/_pyg_utils.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/_ts_utils.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/abod.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/ae1svm.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/alad.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/anogan.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/audio_ae.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/auto_encoder.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/base.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/base_dl.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/cblof.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/cd.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/cof.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/combination.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/copod.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/devnet.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/dif.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/ecod.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/embedding.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/feature_bagging.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/gaal_base.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/gmm.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/hbos.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/hdbscan.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/iforest.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/inne.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/kde.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/knn.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/kpca.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/lmdd.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/loci.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/loda.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/lof.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/lscp.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/lunar.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/mad.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/mcd.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/mo_gaal.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/ocsvm.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/pca.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/pyg_anomalous.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/pyg_anomalydae.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/pyg_cola.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/pyg_conad.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/pyg_dominant.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/pyg_guide.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/pyg_radar.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/pyg_scan.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/qmcd.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/rgraph.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/rod.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/sampling.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/sklearn_base.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/so_gaal.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/so_gaal_new.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/sod.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/sos.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/suod.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/thresholds.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/ts_anomaly_transformer.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/ts_kshape.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/ts_lstm.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/ts_matrix_profile.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/ts_od.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/ts_sand.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/ts_spectral_residual.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/vae.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/models/xgbod.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/skills/__init__.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/skills/od_expert/SKILL.md +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/skills/od_expert/__init__.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/skills/od_expert/references/graph.md +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/skills/od_expert/references/pitfalls.md +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/skills/od_expert/references/tabular.md +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/skills/od_expert/references/text_image.md +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/skills/od_expert/references/time_series.md +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/skills/od_expert/references/workflow.md +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/__init__.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/_detector_factory.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/_kb_router.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/_llm.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/_nl_feedback.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/_quality_metrics.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/ad_engine.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/auto_model_selector.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/data.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/encoders/audio.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/encoders/huggingface.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/encoders/openai_encoder.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/encoders/sentence_transformer.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/example.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/investigation.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/knowledge/__init__.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/knowledge/algorithms.json +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/knowledge/benchmarks.json +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/knowledge/papers.json +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/knowledge/routing_rules.json +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/model_analysis_jsons/AE1SVM.json +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/model_analysis_jsons/ALAD.json +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/model_analysis_jsons/AnoGAN.json +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/model_analysis_jsons/AutoEncoder.json +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/model_analysis_jsons/Deep SVDD.json +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/model_analysis_jsons/DevNet.json +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/model_analysis_jsons/LUNAR.json +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/model_analysis_jsons/MO-GAAL.json +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/model_analysis_jsons/SO-GAAL.json +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/model_analysis_jsons/VAE.json +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/model_analysis_jsons/__init__.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/persistence.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/stat_models.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/torch_utility.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod/utils/utility.py +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod.egg-info/SOURCES.txt +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod.egg-info/dependency_links.txt +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod.egg-info/entry_points.txt +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod.egg-info/requires.txt +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyod.egg-info/top_level.txt +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/pyproject.toml +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/requirements.txt +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/setup.cfg +0 -0
- {pyod-3.6.2 → pyod-3.6.3}/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.3
|
|
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/
|
|
@@ -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/
|
|
@@ -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.
|
|
@@ -234,15 +250,23 @@ class DeepSVDD(BaseDetector):
|
|
|
234
250
|
The binary labels of the training data. 0 stands for inliers
|
|
235
251
|
and 1 for outliers/anomalies. It is generated by applying
|
|
236
252
|
``threshold_`` on ``decision_scores_``.
|
|
253
|
+
|
|
254
|
+
c_ : torch tensor of shape (output_width,)
|
|
255
|
+
The fitted hypersphere center actually used by the training
|
|
256
|
+
objective and by ``decision_function``. Either the center
|
|
257
|
+
initialized from the data (when ``c`` is None) or a validated
|
|
258
|
+
copy of the user-supplied ``c``.
|
|
237
259
|
"""
|
|
238
260
|
|
|
239
261
|
def __init__(self, n_features, c=None, use_ae=False, hidden_neurons=None,
|
|
240
262
|
hidden_activation='relu',
|
|
241
263
|
output_activation='sigmoid', optimizer='adam', epochs=100,
|
|
242
264
|
batch_size=32,
|
|
243
|
-
dropout_rate=0.2, l2_regularizer=0.
|
|
265
|
+
dropout_rate=0.2, l2_regularizer=0.5e-6,
|
|
266
|
+
validation_size=0.1,
|
|
244
267
|
preprocessing=True,
|
|
245
|
-
verbose=1, random_state=None, contamination=0.1
|
|
268
|
+
verbose=1, random_state=None, contamination=0.1,
|
|
269
|
+
learning_rate=1e-4):
|
|
246
270
|
super(DeepSVDD, self).__init__(contamination=contamination)
|
|
247
271
|
|
|
248
272
|
self.n_features = n_features
|
|
@@ -252,6 +276,7 @@ class DeepSVDD(BaseDetector):
|
|
|
252
276
|
self.hidden_activation = hidden_activation
|
|
253
277
|
self.output_activation = output_activation
|
|
254
278
|
self.optimizer = optimizer
|
|
279
|
+
self.learning_rate = learning_rate
|
|
255
280
|
self.epochs = epochs
|
|
256
281
|
self.batch_size = batch_size
|
|
257
282
|
self.dropout_rate = dropout_rate
|
|
@@ -315,9 +340,40 @@ class DeepSVDD(BaseDetector):
|
|
|
315
340
|
dropout_rate=self.dropout_rate,
|
|
316
341
|
l2_regularizer=self.l2_regularizer)
|
|
317
342
|
X_norm = torch.tensor(X_norm, dtype=torch.float32)
|
|
343
|
+
# ``c`` is a constructor parameter and must stay untouched so that
|
|
344
|
+
# get_params/clone round-trip and a refit re-initializes the center
|
|
345
|
+
# for the freshly built network. The fitted center lives in ``c_``.
|
|
318
346
|
if self.c is None:
|
|
319
|
-
|
|
320
|
-
|
|
347
|
+
center = self.model_._init_c(X_norm)
|
|
348
|
+
else:
|
|
349
|
+
center = torch.as_tensor(self.c, dtype=X_norm.dtype,
|
|
350
|
+
device=X_norm.device)
|
|
351
|
+
|
|
352
|
+
# The center must match the width of whatever ``forward()`` returns:
|
|
353
|
+
# the decoder reconstruction under use_ae, else the final embedding.
|
|
354
|
+
output_width = (self.n_features if self.use_ae
|
|
355
|
+
else self.hidden_neurons[-1])
|
|
356
|
+
if center.ndim == 0:
|
|
357
|
+
center = center.repeat(output_width)
|
|
358
|
+
elif center.ndim != 1 or center.numel() != output_width:
|
|
359
|
+
raise ValueError(
|
|
360
|
+
f'DeepSVDD: c must be a scalar or a vector of length '
|
|
361
|
+
f'{output_width}, got shape {tuple(center.shape)}.')
|
|
362
|
+
if not torch.isfinite(center).all():
|
|
363
|
+
raise ValueError('DeepSVDD: c must contain only finite values.')
|
|
364
|
+
if not torch.any(center != 0):
|
|
365
|
+
# An all-zero center is the trivial solution of the Deep SVDD
|
|
366
|
+
# objective for a bias-free network (Ruff et al., ICML 2018,
|
|
367
|
+
# Proposition 1): all-zero weights map every input to it, so
|
|
368
|
+
# training would converge to a collapsed hypersphere.
|
|
369
|
+
raise ValueError(
|
|
370
|
+
'DeepSVDD: the hypersphere center is all zeros, which makes '
|
|
371
|
+
'the trivial all-zero-weights solution optimal and collapses '
|
|
372
|
+
'the hypersphere. Pass c=None to initialize the center from '
|
|
373
|
+
'the data, or supply a non-zero center.')
|
|
374
|
+
# clone() so a mutable user-supplied array/tensor cannot alias (and
|
|
375
|
+
# later mutate) the fitted center.
|
|
376
|
+
self.c_ = center.detach().clone()
|
|
321
377
|
|
|
322
378
|
# Predict on X itself and calculate the reconstruction error as
|
|
323
379
|
# the outlier scores. Noted X_norm was shuffled has to recreate
|
|
@@ -334,32 +390,43 @@ class DeepSVDD(BaseDetector):
|
|
|
334
390
|
best_loss = float('inf')
|
|
335
391
|
best_model_dict = None
|
|
336
392
|
|
|
337
|
-
optimizer = optimizer_dict[self.optimizer](
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
[torch.linalg.norm(w) for w in self.model_.parameters()])
|
|
341
|
-
|
|
393
|
+
optimizer = optimizer_dict[self.optimizer](
|
|
394
|
+
self.model_.parameters(), lr=self.learning_rate,
|
|
395
|
+
weight_decay=self.l2_regularizer)
|
|
342
396
|
for epoch in range(self.epochs):
|
|
343
397
|
self.model_.train()
|
|
344
398
|
epoch_loss = 0
|
|
345
399
|
for batch_x, _ in dataloader:
|
|
346
400
|
optimizer.zero_grad()
|
|
347
401
|
outputs = self.model_(batch_x)
|
|
348
|
-
dist = torch.sum((outputs - self.
|
|
402
|
+
dist = torch.sum((outputs - self.c_) ** 2, dim=-1)
|
|
403
|
+
# L2 regularization term, recomputed every step so that it
|
|
404
|
+
# is part of the current batch's computation graph
|
|
405
|
+
w_d = 1e-6 * sum(
|
|
406
|
+
[torch.linalg.norm(w) for w in self.model_.parameters()])
|
|
349
407
|
if self.use_ae:
|
|
350
408
|
loss = torch.mean(dist) + w_d + torch.mean(
|
|
351
409
|
torch.square(outputs - batch_x))
|
|
352
410
|
else:
|
|
353
411
|
loss = torch.mean(dist) + w_d
|
|
354
412
|
|
|
355
|
-
|
|
413
|
+
loss.backward()
|
|
356
414
|
optimizer.step()
|
|
357
415
|
epoch_loss += loss.item()
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
416
|
+
# keep the best performing model across epochs (epoch_loss is the
|
|
417
|
+
# accumulated loss over the whole epoch, so this must run after
|
|
418
|
+
# the batch loop, not inside it)
|
|
419
|
+
if epoch_loss < best_loss:
|
|
420
|
+
best_loss = epoch_loss
|
|
421
|
+
# state_dict() returns references to the live parameters, so
|
|
422
|
+
# a later optimizer.step() would mutate this snapshot in
|
|
423
|
+
# place and leave the final weights here instead of the best
|
|
424
|
+
# ones. Copy it.
|
|
425
|
+
best_model_dict = copy.deepcopy(self.model_.state_dict())
|
|
361
426
|
print(f"Epoch {epoch + 1}/{self.epochs}, Loss: {epoch_loss}")
|
|
362
427
|
self.best_model_dict = best_model_dict
|
|
428
|
+
if self.best_model_dict is not None:
|
|
429
|
+
self.model_.load_state_dict(self.best_model_dict)
|
|
363
430
|
|
|
364
431
|
self.decision_scores_ = self.decision_function(X)
|
|
365
432
|
self._process_decision_scores()
|
|
@@ -394,6 +461,6 @@ class DeepSVDD(BaseDetector):
|
|
|
394
461
|
self.model_.eval()
|
|
395
462
|
with torch.no_grad():
|
|
396
463
|
outputs = self.model_(X_norm)
|
|
397
|
-
dist = torch.sum((outputs - self.
|
|
464
|
+
dist = torch.sum((outputs - self.c_) ** 2, dim=-1)
|
|
398
465
|
anomaly_scores = dist.numpy()
|
|
399
466
|
return anomaly_scores
|
|
@@ -382,8 +382,27 @@ def resolve_encoder(encoder):
|
|
|
382
382
|
# with local_files_only=True rather than resolved as a Hub ID.
|
|
383
383
|
import os
|
|
384
384
|
if os.path.exists(encoder):
|
|
385
|
-
|
|
386
|
-
|
|
385
|
+
# Local path: prefer the SentenceTransformer backend, but fall
|
|
386
|
+
# back to HuggingFace when sentence-transformers isn't installed
|
|
387
|
+
# (e.g. a pyod[huggingface]-only environment). Both backends load
|
|
388
|
+
# from a local directory without any network/Hub call.
|
|
389
|
+
try:
|
|
390
|
+
return _create_encoder('sentence_transformer',
|
|
391
|
+
model_name=encoder)
|
|
392
|
+
except ImportError:
|
|
393
|
+
pass
|
|
394
|
+
|
|
395
|
+
try:
|
|
396
|
+
return _create_encoder('huggingface',
|
|
397
|
+
model_name=encoder,
|
|
398
|
+
modality='text')
|
|
399
|
+
except ImportError:
|
|
400
|
+
pass
|
|
401
|
+
|
|
402
|
+
raise ImportError(
|
|
403
|
+
"Loading a local encoder path requires sentence-transformers "
|
|
404
|
+
"or transformers. Install with: pip install pyod[embedding] "
|
|
405
|
+
"or pip install pyod[huggingface].")
|
|
387
406
|
|
|
388
407
|
# Check registry
|
|
389
408
|
if encoder in _ENCODER_REGISTRY:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyod
|
|
3
|
-
Version: 3.6.
|
|
3
|
+
Version: 3.6.3
|
|
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/
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|