You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGES.txt
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -232,4 +232,4 @@ v<3.6.0>, <06/04/2026> -- Add audio as a first-class anomaly detection modality.
232
232
v<3.6.1>, <06/16/2026> -- Maintenance and contributor PRs since v3.6.0 (no breaking API changes). PyThresh v1 support (#684, Daniel Kulik): the pyod.models.thresholds wrappers and the BaseDetector threshold path use the pythresh v1 API (.fit()/.labels_/.predict() instead of .eval()), and the dependency pin moves to pythresh>=1.0.0. EmbeddingOD air-gapped and pre-instantiated encoder support (#696, Sunny Guntuka): a pre-loaded SentenceTransformer instance can be passed directly as the encoder, and a local filesystem path is loaded with local_files_only=True (no Hub call) for offline use; this also fixes a resolver-order bug where a SentenceTransformer instance was wrapped as a CallableEncoder (calling model(X) instead of model.encode(X)). DataFrame feature-name warning fix (#692, eferhire ugbotu; closes #540): GMM, IForest, LOF, and OCSVM run check_array in decision_function, so scoring a pandas DataFrame after fitting no longer emits the scikit-learn feature-name UserWarning; the predict/predict_proba/predict_confidence paths route through decision_function, so the single-point fix covers them. Audio docs: AudioAE and the audio modality are now in the README and docs algorithm tables with a new pyod.models.audio API page (v3.6.0 shipped AudioAE without a table row). Tests: regression tests for the DataFrame-warning fix (GMM/OCSVM/LOF) and a no-download EmbeddingOD resolver test using SentenceTransformer(modules=[]). Docs: PyPI download figure refreshed to 46M+. Reviewed via /implement-review (Codex gatekeeper): #696 and #692 merged after dual review, with the gatekeeper-flagged test gaps closed in a follow-up. Buildable detector count unchanged at 61.
233
233
v<3.6.2>, <07/20/2026> -- Security hardening for model persistence (CVE-2026-15529): both `pyod.utils.persistence.load()` and `pyod.utils.persistence.compat_load()` now refuse to deserialize pickle/joblib artifacts unless callers pass `trusted=True`, making the trust boundary explicit before `joblib.load()` can execute pickle reducers. This is a breaking change: existing `save()` then `load()` round trips, and any direct `compat_load()` calls, must add `trusted=True`, because both functions now refuse even artifacts you saved yourself until the caller acknowledges the source is trusted. The internal `load()` fall-through to `compat_load()` forwards the acknowledgement automatically, so `load(path, trusted=True)` still recovers legacy dtype-mismatched artifacts in a single call. `strict=True` remains a dependency-version policy and is documented as not making untrusted pickle files safe. Adds regression tests verifying that both the default `load()` path and a direct `compat_load()` call reject before any unpickling side effects occur. Updates the model persistence guide, decision tree, troubleshooting table, and save/load example to pass `trusted=True` for trusted artifacts. Closes #697.
234
234
v<3.6.3>, <08/01/2026> -- DeepSVDD correctness fixes, contributor bug fixes, and project infrastructure. DeepSVDD hypersphere-collapse fix (builds on #704 by Devashish Moghe; closes #606 and #641): the training loop had `loss.backward()` commented out, so no gradient ever reached the weights and scores came from the randomly initialized network. #704 restored the backward pass, recomputing the L2 term `w_d` inside the batch loop so each step gets a fresh graph, moving the best-model bookkeeping out of the batch loop, and deep-copying `state_dict()` (which otherwise aliases the live parameters). Enabling training then exposed three pre-existing defects that the commented-out line had been masking. First, `fit()` assigned the estimator's center `self.c = 0.0` and let `_init_c()` write the computed center to the inner module, which the loss and scoring never read; for a bias-free ReLU network the all-zero weights map every input to 0, so `c = 0` is exactly the trivial solution of Ruff et al., ICML 2018, Proposition 1, and training converged to a collapsed hypersphere. On a 17-dataset ODDS benchmark (3 seeds, 30 epochs) the collapsed configuration returned a single distinct score on 10 of 17 datasets -- a detector that is silently useless while still meeting a ROC floor, because ordering can be induced by floating-point noise as small as 1e-24. The center is now taken from `_init_c()`, detached, and stored where the objective reads it. Second, the optimizer was constructed without a learning rate (Adam's default 1e-3) and passed `l2_regularizer=0.1` as `weight_decay`, five orders of magnitude above the 5e-7 used by the reference implementation; that decay drives the weights toward zero and compounds the collapse. `l2_regularizer` now defaults to 5e-7 and a new `learning_rate` parameter defaults to 1e-4 (both match the reference implementation; the `l2_regularizer` default change is API-visible). Third, `best_model_dict` was stored but never loaded, so scoring used final-epoch weights; the snapshot is now applied before scoring. Fourth, `fit()` wrote the computed center into the constructor parameter `c`, which leaked fitted state through `get_params()`, made `clone()` start pre-seeded, and made a refit train a newly built network against the previous fit's center; the fitted center now lives in `c_`, `c` stays configuration, and a user-supplied center is validated before training: a scalar is expanded to the network output width, a vector must match that width (`hidden_neurons[-1]`, or `n_features` when `use_ae=True`), non-finite values are rejected, an all-zero center is rejected with a `ValueError` because it is exactly the Proposition 1 trivial-solution condition, and the tensor is copied so a later mutation of the caller's array cannot change the fitted center. Measured effect on the 17-dataset benchmark: mean ROC AUC rises from 0.601 (collapsed) to 0.748, with score collapse eliminated on all 17 datasets. Calibration note: 0.748 is level with the 0.746 obtained by the untrained network, so these changes restore DeepSVDD to its baseline rather than improving on it. Deep SVDD assumes a clean one-class training set, while PyOD fits it on contaminated data unsupervised; in a controlled comparison, training on genuinely normal samples only reaches 0.879 and trimming the most anomalous 10% before training reaches 0.800, so a substantial part of the gap tracks the mismatch between the method's one-class assumption and unsupervised use on contaminated data. These two diagnostics do not rule out further defects or tuning gains. Tests: `test_scores_are_not_constant` asserts score diversity on both the standard and autoencoder paths and `test_center_is_valid` asserts the center is a detached, finite, non-scalar tensor; both fail on the collapsed code. `test_fit_changes_parameters` pins the original backward-pass defect only, and does not detect collapse. The LOCI and generate_data contributor PRs remain under review and are not included here. Contributor PRs: local encoder paths now fall back to the HuggingFace backend when sentence-transformers is absent (#701, Sunny Guntuka; follow-up to #696), so a `pyod[huggingface]`-only install no longer raises `ImportError` before the existing fallback can run; save/load round-trip coverage now spans 23 detectors (#708, Jayesh Suryavanshi; closes #269), up from 2, while 21 of them also gain clone assertions for unfitted equivalence and refit score reproduction, strengthening per-detector `test_model_clone` methods that previously only checked that `clone()` did not raise. Test fix (#710): `test_resolve_st_instance_no_download` built its no-download model with `SentenceTransformer(modules=[])`, which sentence-transformers 5.6 rejects; it now passes a single trivial `torch.nn.Identity` module instead, which keeps the test network-free without depending on `sentence_transformers.models` keyword arguments that have been renamed across 5.x. Project infrastructure (#705): pyod.dev website badge in the README (using an absolute image URL so it renders on the PyPI project page), brand assets under `brand/` plus Sphinx `docs/_static/` copies, and a `SECURITY.md` vulnerability-reporting policy.
235
-
v<3.6.4>, <TBD> -- Documentation accuracy pass; no runtime behavior changes. A parallel audit compared every constructor signature against its numpydoc block, attempted all 72 example scripts, and built the Sphinx site; the in-scope findings were then repaired and checked again. Docstrings (83 fixes across 43 modules in `pyod/models/`): removed seven documented constructor parameters that do not exist and raise `TypeError` when passed (`GMM.verbose`, `GMM.verbose_interval`, `RGraph.active_support_params`, `RGraph.random_state`, `SUOD.cost_forecast_loc_fit`, `SUOD.cost_forecast_loc_pred`, and `LUNAR.n_neighbors`, whose real keyword is the British spelling `n_neighbours`; the entry is renamed and notes the alternative spelling); corrected documented defaults that disagreed with the signature (among them `ABOD.n_neighbors` 10 -> 5, `ALAD.epochs` 500 -> 200, `ALAD.preprocessing` True -> False, `ALAD.learning_rate_gen`/`learning_rate_disc` 0.001 -> 0.0001, `AnoGAN.learning_rate_query` 0.001 -> 0.01, `RGraph.transition_steps` 20 -> 10, `RGraph.n_nonzero` 50 -> 10, `SUOD.n_jobs` 1 -> None, and `DIF.hidden_neurons`, documented as [64, 32] while the constructor substitutes [500, 100] for the `None` default); documented public constructor parameters that had no entry (`ALAD.latent_dim`, `add_disc_zz_loss`, `spectral_normalization`; `AnoGAN.latent_dim_G`, `device`); corrected `labels_` in `BaseDetector` and every detector that copied the wording, which described an `int` where the attribute is a numpy array of shape (n_samples,); and corrected `XGBOD.labels_`, documented as `threshold_` applied to `decision_scores_` when `fit()` never sets `threshold_` and the labels come from the fitted XGBoost classifier. An executable-AST comparison against the previous commit confirms every one of those 43 files changed only docstrings. Sphinx (`docs/conf.py`, `docs/pyod.models.tabular.rst`): enabled `sphinx.ext.napoleon`, absent since the numpydoc style was adopted, so every `Parameters`/`Attributes`/`Examples` heading was parsed as an RST section title rather than a field list; a full build goes from 310 warnings and 242 `class="problematic"` spans to 41 and 5, and the `:attr:` links for `decision_scores_` and `labels_` on the landing page and API cheat sheet resolve for the first time. Removed the `pyod.models.auto_encoder_torch` section, dead since the module was deleted in 2024 (a duplicated `:exclude-members:` option made the directive raise `DuplicateOptionError`, which Sphinx stripped from the output, so the page silently rendered a heading with no body), and de-duplicated the `pyod.models.base` automodule so `api_cc.rst` is its canonical home, clearing 13 duplicate-object warnings. Enabling Napoleon also exposed five docstrings it could not parse, which had been inert text before: `AnoGAN` emitted 1 of 16 parameters and `RGraph` 5 of 16 (consecutive blank lines and `name:` headers missing the space before the colon terminated the block), `DIF` emitted 30 fields for an 11-parameter constructor (comma-separated headers such as `hidden_neurons, list` split into bogus names), `XGBOD` turned a commented-out `missing` block into four bogus parameters, and the `SO_GAAL` in `so_gaal_new.py` had an entirely empty `Parameters` section for its 12 arguments. All five are repaired; a sweep over all 62 detectors now parses every documented parameter with no bogus or missing entries. Entry points: the README quick start called `clf.fit(X_train)` without ever defining `X_train` and used `visualize` without importing it, so the block on the GitHub landing page and the PyPI description raised `NameError` when pasted; it now carries the seeded prelude and the sample output measured from it. `docs/install.rst` documented a `pytorch` extra that does not exist (pip treats an unknown extra as a warning, so the command succeeded while installing none of the PyTorch stack), omitted nine real extras and the `audio` extra entirely, and never mentioned `pip install pyod[all]`, a string that appeared nowhere in the repository; the table is now keyed on the extras defined in `pyproject.toml`. The same file claimed the MCP server registers seven tools when it registers ten, omitting the three that perform detection, and contradicted `README.rst`. Removed the claim that `pyod install skill` supports Claude Desktop, which no code path targets, and routed Desktop users to the MCP path. Examples: fixed runtime failures in `mad_example.py`, which generated two features for a detector that requires one, and `qmcd_example.py`, which appended ground-truth labels to the feature matrix before calling `predict` (raising, and leaking test labels). `examples/data/mat_file_conversion.py` now byte-compiles after removal of mid-file Python 2 `__future__` imports; end-to-end conversion still requires its optional dependencies and external source datasets. Flagged for a separate decision, not changed here: `CBLOF.n_jobs` is accepted but unused, `DevNet` has no constructor docstring and exposes arguments that are unused or overridden, and `pyod/cli.py` infers "Claude Code detected" from a directory that `pyod install skill` itself creates.
235
+
v<3.6.4>, <08/02/2026> -- Documentation accuracy pass; no runtime behavior changes. A parallel audit compared every constructor signature against its numpydoc block, attempted all 72 example scripts, and built the Sphinx site; the in-scope findings were then repaired and checked again. Docstrings (83 fixes across 43 modules in `pyod/models/`): removed seven documented constructor parameters that do not exist and raise `TypeError` when passed (`GMM.verbose`, `GMM.verbose_interval`, `RGraph.active_support_params`, `RGraph.random_state`, `SUOD.cost_forecast_loc_fit`, `SUOD.cost_forecast_loc_pred`, and `LUNAR.n_neighbors`, whose real keyword is the British spelling `n_neighbours`; the entry is renamed and notes the alternative spelling); corrected documented defaults that disagreed with the signature (among them `ABOD.n_neighbors` 10 -> 5, `ALAD.epochs` 500 -> 200, `ALAD.preprocessing` True -> False, `ALAD.learning_rate_gen`/`learning_rate_disc` 0.001 -> 0.0001, `AnoGAN.learning_rate_query` 0.001 -> 0.01, `RGraph.transition_steps` 20 -> 10, `RGraph.n_nonzero` 50 -> 10, `SUOD.n_jobs` 1 -> None, and `DIF.hidden_neurons`, documented as [64, 32] while the constructor substitutes [500, 100] for the `None` default); documented public constructor parameters that had no entry (`ALAD.latent_dim`, `add_disc_zz_loss`, `spectral_normalization`; `AnoGAN.latent_dim_G`, `device`); corrected `labels_` in `BaseDetector` and every detector that copied the wording, which described an `int` where the attribute is a numpy array of shape (n_samples,); and corrected `XGBOD.labels_`, documented as `threshold_` applied to `decision_scores_` when `fit()` never sets `threshold_` and the labels come from the fitted XGBoost classifier. An executable-AST comparison against the previous commit confirms every one of those 43 files changed only docstrings. Sphinx (`docs/conf.py`, `docs/pyod.models.tabular.rst`): enabled `sphinx.ext.napoleon`, absent since the numpydoc style was adopted, so every `Parameters`/`Attributes`/`Examples` heading was parsed as an RST section title rather than a field list; a full build goes from 310 warnings and 242 `class="problematic"` spans to 41 and 5, and the `:attr:` links for `decision_scores_` and `labels_` on the landing page and API cheat sheet resolve for the first time. Removed the `pyod.models.auto_encoder_torch` section, dead since the module was deleted in 2024 (a duplicated `:exclude-members:` option made the directive raise `DuplicateOptionError`, which Sphinx stripped from the output, so the page silently rendered a heading with no body), and de-duplicated the `pyod.models.base` automodule so `api_cc.rst` is its canonical home, clearing 13 duplicate-object warnings. Enabling Napoleon also exposed five docstrings it could not parse, which had been inert text before: `AnoGAN` emitted 1 of 16 parameters and `RGraph` 5 of 16 (consecutive blank lines and `name:` headers missing the space before the colon terminated the block), `DIF` emitted 30 fields for an 11-parameter constructor (comma-separated headers such as `hidden_neurons, list` split into bogus names), `XGBOD` turned a commented-out `missing` block into four bogus parameters, and the `SO_GAAL` in `so_gaal_new.py` had an entirely empty `Parameters` section for its 12 arguments. All five are repaired; a sweep over all 62 detectors now parses every documented parameter with no bogus or missing entries. Entry points: the README quick start called `clf.fit(X_train)` without ever defining `X_train` and used `visualize` without importing it, so the block on the GitHub landing page and the PyPI description raised `NameError` when pasted; it now carries the seeded prelude and the sample output measured from it. `docs/install.rst` documented a `pytorch` extra that does not exist (pip treats an unknown extra as a warning, so the command succeeded while installing none of the PyTorch stack), omitted nine real extras and the `audio` extra entirely, and never mentioned `pip install pyod[all]`, a string that appeared nowhere in the repository; the table is now keyed on the extras defined in `pyproject.toml`. The same file claimed the MCP server registers seven tools when it registers ten, omitting the three that perform detection, and contradicted `README.rst`. Removed the claim that `pyod install skill` supports Claude Desktop, which no code path targets, and routed Desktop users to the MCP path. Examples: fixed runtime failures in `mad_example.py`, which generated two features for a detector that requires one, and `qmcd_example.py`, which appended ground-truth labels to the feature matrix before calling `predict` (raising, and leaking test labels). `examples/data/mat_file_conversion.py` now byte-compiles after removal of mid-file Python 2 `__future__` imports; end-to-end conversion still requires its optional dependencies and external source datasets. Flagged for a separate decision, not changed here: `CBLOF.n_jobs` is accepted but unused, `DevNet` has no constructor docstring and exposes arguments that are unused or overridden, and `pyod/cli.py` infers "Claude Code detected" from a directory that `pyod install skill` itself creates.
0 commit comments