2.25.3.14. pycsamt.ai.processing.anomaly#
AnomalyDetector — unsupervised profile-level anomaly detection.
A fully-connected autoencoder is trained on per-site feature vectors derived from a survey profile. Sites whose reconstruction error significantly exceeds the training distribution are flagged as anomalous — they may represent bad data, equipment problems, or genuinely anomalous geology that warrants closer inspection.
Architecture#
where \(f: \mathbb{R}^d \to \mathbb{R}^k\) is the encoder (\(k \ll d\)) and \(g: \mathbb{R}^k \to \mathbb{R}^d\) is the decoder. The anomaly score for site \(i\) is
A site is flagged when \(s_i\) exceeds the
threshold_percentile-th percentile of training scores.
When neither PyTorch nor TensorFlow is available the detector falls back
to PCA-based reconstruction using sklearn.decomposition.PCA.
Working directly with site collections#
AnomalyDetector.apply() accepts a site collection directly and
returns one with every flagged station removed, via
Sites.select() – the
sites-in / sites-out counterpart of transform() /
flag_anomalies(), shaped as a station filter rather than a
per-station mutation.
Classes
|
Profile-level unsupervised anomaly detector. |
- class pycsamt.ai.processing.anomaly.AnomalyDetector(n_features=None, latent_dim=32, *, channels=(128, 64), threshold_percentile=95.0, device=None)[source]
Bases:
BaseEMProcessorProfile-level unsupervised anomaly detector.
- Parameters:
n_features (int or None, default None) – Feature vector length per site (typically
n_freqs × n_components). WhenNone, inferred fromX.shape[1]on the first call tofit().latent_dim (int, default 32) – Bottleneck dimension of the autoencoder.
channels (tuple of int, default (128, 64)) – Hidden layer widths in the encoder (decoder is mirrored).
threshold_percentile (float, default 95.0) – Sites with reconstruction error above this percentile of the training distribution are flagged as anomalous.
device (str or None) – Compute device. Ignored when using the PCA fallback.
Notes
When neither PyTorch nor TensorFlow is installed the detector silently uses a PCA reconstruction model via scikit-learn. The interface is identical; only the accuracy differs.
Examples
>>> import numpy as np >>> X = np.random.randn(100, 120).astype("float32") >>> det = AnomalyDetector(latent_dim=16) >>> det.fit(X, epochs=10, verbose=False) AnomalyDetector(n_features=120, latent_dim=16) >>> scores = det.transform(X) >>> flags = det.flag_anomalies(X)
- fit(X, *, epochs=80, batch_size=32, lr=0.001, val_frac=0.1, seed=None, verbose=True)[source]
Train the anomaly detector on profile data.
- transform(X)[source]
Compute per-site reconstruction error (anomaly score).
- Parameters:
X (ndarray, shape (n_samples, n_features))
- Returns:
scores – Per-site mean squared reconstruction error.
- Return type:
- flag_anomalies(X)[source]
Return a boolean mask of anomalous sites.
- Parameters:
X (ndarray, shape (n_samples, n_features))
- Returns:
flags
- Return type:
- apply(sites, *, n_components=4, freq_ref=None, log_amp=True, threshold=None, recursive=True, on_dup='replace', strict=False, verbose=0)[source]
Drop anomalous stations, sites-in / sites-out.
Builds one feature vector per station with
prepare_z_features(), flattened to(n_sites, n_components * n_freqs)— the same convention used to fit a detector on site data in the first place — scores each station, and returns a new site collection with every flagged station removed viaSites.select().Unlike
EMDenoiser.apply, there is noinplaceoption: dropping stations is a selection, not a per-station mutation, andSiteshas no in-place removal —select()always returns a new container, and so does this method.- Parameters:
sites (SiteCollection or compatible)
n_components (int) – Passed to
prepare_z_features(). Must reproduce the exact feature layout used to build the training data — in particular,n_components * len(freq_ref)must equal the fittedn_features, or aValueErroris raised rather than silently scoring a mismatched feature vector.freq_ref (ndarray | None) – Passed to
prepare_z_features(). Must reproduce the exact feature layout used to build the training data — in particular,n_components * len(freq_ref)must equal the fittedn_features, or aValueErroris raised rather than silently scoring a mismatched feature vector.log_amp (bool) – Passed to
prepare_z_features(). Must reproduce the exact feature layout used to build the training data — in particular,n_components * len(freq_ref)must equal the fittedn_features, or aValueErroris raised rather than silently scoring a mismatched feature vector.threshold (float or None) – Overrides the fitted
threshold_for this call.recursive (bool) – Passed to
ensure_sites().on_dup (str) – Passed to
ensure_sites().strict (bool) – Passed to
ensure_sites().verbose (int) – Passed to
ensure_sites().
- Returns:
corrected – A new site collection with flagged stations removed.
- Return type:
Examples
>>> det = AnomalyDetector(latent_dim=8) >>> det.fit(X, epochs=80) >>> clean_sites = det.apply(sites)
- property history_: dict[str, list][source]
Training history recorded by the last
fit()call.- Returns:
history –
{"train_loss": [...], "val_loss": [...]}, one value per epoch. Empty when the PCA fallback was used (no epoch loop) or beforefit()has been called. Pass directly toplot_training_history().- Return type: