Skip to content

Commit df3dded

Browse files
committed
bump version to 3.6.4 + CHANGES.txt (P1 of v3.6.4 release)
1 parent a455b00 commit df3dded

2 files changed

Lines changed: 2 additions & 2 deletions

File tree

CHANGES.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,4 @@ v<3.6.0>, <06/04/2026> -- Add audio as a first-class anomaly detection modality.
232232
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.
233233
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.
234234
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.

pyod/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@
2020
# Dev branch marker is: 'X.Y.dev' or 'X.Y.devN' where N is an integer.
2121
# 'X.Y.dev0' is the canonical version of 'X.Y.dev'
2222
#
23-
__version__ = '3.6.3' # pragma: no cover
23+
__version__ = '3.6.4' # pragma: no cover

0 commit comments

Comments
 (0)