Anomaly Detection

Anomaly detection ranks or flags observations that look unusual relative to a reference distribution. It is often unsupervised learning, but evaluation usually becomes supervised once analysts label true incidents. Compared with clustering, the goal is not to assign every point to a group; compared with class imbalance, the rare class may not be labeled at training time.

What counts as an anomaly

An anomaly is not just a rare point. It is rare under the model of normal behavior and relevant to the operational question. A new legitimate customer segment can look anomalous, while a common failure pattern may stop looking unusual after enough incidents accumulate. Methods differ in how they define “unusual”:

FamilyIdeaExample method
Densityflag points in low-probability regionsGaussian model, kernel density
Distance / neighborhoodflag points far from their neighborskNN distance, local outlier factor
Isolationanomalies are easy to separate offIsolation Forest
Reconstructionflag points the model rebuilds poorlyPCA or autoencoder error

Scoring and flagging

Many methods learn an anomaly score where larger (or smaller) means more anomalous, then flag a point when the score crosses a threshold : , the indicator being when the rule fires. Density methods instead flag points of low estimated density , using . Isolation Forest isolates points by random partitioning; anomalies tend to have shorter average path lengths.

Worked example

This example creates a normal two-dimensional cloud plus a small separated outlier cluster, then checks whether Isolation Forest flags the separated points at the requested contamination rate.

import numpy as np
from sklearn.ensemble import IsolationForest
 
rng = np.random.default_rng(22)
normal = rng.normal(0, 1, size=(120, 2))
outliers = rng.normal(6, .5, size=(6, 2))
X = np.vstack([normal, outliers])
iso = IsolationForest(contamination=6/126, random_state=22).fit(X)
pred = iso.predict(X)
print("flagged_total", int((pred == -1).sum()))
print("outliers_flagged", int((pred[-6:] == -1).sum()), "of", len(outliers))
print("normal_flagged", int((pred[:-6] == -1).sum()), "of", len(normal))

Observed output:

flagged_total 6
outliers_flagged 6 of 6
normal_flagged 0 of 120

The synthetic outliers are far from the normal cloud, so the fitted contamination threshold flags exactly those six points.

Caveats

The contamination parameter bakes in an expected alert rate. High-dimensional distance concentration can make “far away” less meaningful. Concept drift changes normality, so anomaly systems need monitoring and analyst feedback loops.

References