Part III: State Estimation and Classical Inference
Chapter 12: Classical Anomaly and Change Detection

Isolation Forest, LOF, one-class SVM

"You spent a week teaching me what a fault looks like. I found it faster by learning only what normal looks like, and flinching at everything else."

An Unsupervised AI Agent

Why this section matters

The earlier sections of this chapter assumed you could write down what an anomaly is: a residual past a threshold, a control chart out of limits, a change point in a mean. That works with a physics model or a stable univariate signal. Real sensor deployments rarely oblige. You get a 40-channel vibration-and-thermal feature vector from a compressor, no labeled faults, and a question posed the wrong way round: not "is this the known failure mode?" but "is this unlike anything I have seen this machine do while healthy?" This section covers three workhorse unsupervised detectors that answer exactly that question, each from a different first principle: Isolation Forest (anomalies are easy to separate), Local Outlier Factor (anomalies sit in thinner neighborhoods than their neighbors), and one-class SVM (anomalies fall outside a learned boundary around normal). Knowing which principle matches your data is the difference between a detector that pages an engineer at 3 a.m. for real and one that cries wolf nightly.

These three methods share one framing: none of them knows what an anomaly looks like, only what normal looks like, and each turns the distance from normal into a continuous anomaly score rather than a hard label. You still need a threshold, and choosing it well is its own discipline covered in Section 12.7. Throughout, \(x \in \mathbb{R}^d\) is a feature vector, usually the windowed statistical and spectral features of Chapter 8, drawn from routine operation for training. Because all three learn "normal" from data, they inherit every leakage hazard of Chapter 5: a fault that sneaks into the training window gets learned as normal.

Isolation Forest: anomalies are cheap to isolate

Isolation Forest (Liu, Ting, and Zhou, 2008) inverts the usual density-estimation instinct: instead of modeling where the data is dense, it asks how hard each point is to isolate by random partitioning. Build a tree by repeatedly picking a random feature and a random split value between that feature's current min and max, recursing until every point sits alone in a leaf. A point in a dense cluster needs many splits to be cornered; an outlier far out on some axis gets carved off after only a few. The path length \(h(x)\), the number of edges from root to \(x\)'s leaf, is therefore short for anomalies and long for inliers; average over an ensemble of random trees for a stable estimate \(E[h(x)]\).

The score normalizes path length against \(c(n)\), the average path length of an unsuccessful search in a binary tree of \(n\) points, so that scores are comparable across sample sizes:

$$s(x) = 2^{-\frac{E[h(x)]}{c(n)}}, \qquad c(n) = 2H(n-1) - \frac{2(n-1)}{n},$$

where \(H(i)\) is the \(i\)-th harmonic number. A score near 1 means "isolated almost immediately," a strong anomaly; near 0.5 means "as hard to isolate as a typical point." Isolation Forest's appeal for sensor fleets is practical: it is near-linear in \(n\), trivially parallel across trees, needs no distance metric or feature scaling (splits are axis-aligned and scale-invariant), and handles high-dimensional vibration and current-signature feature vectors without choking. Its blind spot is the flip side of axis-aligned splits: it struggles with anomalies defined only by a correlation between features rather than an extreme value on any single axis, a gap the Extended Isolation Forest (Hariri et al., 2019) closes with random-slope hyperplane cuts.

Three principles, one score

Isolation Forest scores separability, LOF scores relative density, one-class SVM scores distance to a learned boundary. They disagree most where it matters: a point globally rare but locally consistent (a whole machine running hot uniformly) versus a point globally ordinary but locally out of place (one bearing warmer than its identical neighbors). Pick the principle whose notion of "weird" matches your failure physics, or ensemble all three and let disagreement itself become the signal.

Local Outlier Factor: weird relative to your neighbors

Isolation Forest and most statistical detectors judge a point against the global distribution. That fails when normal operation has regions of genuinely different density: an HVAC chiller idling, ramping, and running at full load produce three legitimate clusters of very different tightness. A point on the sparse edge of the idle cluster might be perfectly healthy, while a point of identical global rarity inside the dense full-load cluster is a real fault. Local Outlier Factor (Breunig et al., 2000) makes rarity local. For each point it finds the \(k\) nearest neighbors and defines a smoothed local reachability density \(\mathrm{lrd}_k(x)\), the inverse of the average reachability distance to those neighbors. Reachability distance floors the raw distance \(d(x,o)\) at the neighbor's own \(k\)-distance, so a near-duplicate cannot inherit an artificially inflated density; that floor matters on sensor data, where back-to-back windows from a steady idle state sit almost on top of each other, and unfloored near-zero distances would otherwise destabilize the ratio below. LOF then compares your density to theirs:

$$\mathrm{LOF}_k(x) = \frac{1}{|N_k(x)|} \sum_{o \in N_k(x)} \frac{\mathrm{lrd}_k(o)}{\mathrm{lrd}_k(x)}.$$

An \(\mathrm{LOF}\) near 1 means you are about as dense as your neighbors: an inlier. Substantially above 1 means your neighborhood is much denser than you are, so you sit in a relative void: an outlier. Because the comparison is only ever local, LOF adapts to varying density automatically, which is its whole reason to exist. The costs are equally characteristic: it demands careful feature scaling (millivolts will drown kilohertz) and degrades in high dimensions where distances concentrate, pushing you toward the dimensionality reduction of Chapter 8 first. Naive LOF is also \(O(n^2)\) to score, and \(k\) matters: too small chases noise, too large washes out local structure.

One-class SVM: draw a boundary around normal

The third principle is geometric. A one-class SVM (Schölkopf et al., 2001) maps the data into a high-dimensional feature space through a kernel, usually the radial basis function \(k(x, x') = \exp(-\gamma \lVert x - x' \rVert^2)\), and finds the hyperplane that separates the data from the origin with maximum margin; points on the origin side score as anomalies. An equivalent, often more intuitive formulation, Support Vector Data Description (Tax and Duin, 2004), fits the smallest hypersphere enclosing the normal data, with anything outside anomalous. The key knob is \(\nu \in (0, 1]\), which simultaneously upper-bounds the fraction of training points allowed outside the boundary and lower-bounds the fraction of support vectors: set \(\nu = 0.05\) and you are asserting at most about 5 percent of training data are contaminants, a direct handle on your assumed anomaly rate.

One-class SVM shines when normal operation occupies a compact, curved region a good kernel can wrap tightly, producing a genuine decision boundary you can reason about. Its weaknesses are real: the RBF bandwidth \(\gamma\) is finicky and interacts with \(\nu\), training scales poorly past tens of thousands of points, and it is sensitive to feature scaling and training-set contamination. It is often the least robust of the three on messy, high-volume fleet telemetry, better kept as a boundary-aware complement than the primary detector.

A refrigeration compressor fleet that only knew "healthy"

A cold-chain logistics operator instrumented 300 rooftop refrigeration compressors with suction/discharge pressure, motor current, and a case-mounted accelerometer, seeking early warning of failing valves and low refrigerant with zero labeled failures at launch. The team built 28 features per 10-minute window (band powers, current crest factor, pressure-ratio statistics) and trained an Isolation Forest per compressor on each unit's first three healthy weeks. It caught gross faults, a seized fan, a flooded start, within days, but missed a subtler mode: one compressor running slightly hot and rough in a way that was globally unremarkable across the fleet yet clearly abnormal for that unit's own history. Adding LOF over a sliding reference window caught it, since LOF judges a point against its local neighborhood, not the whole fleet. The one-class SVM agreed on the gross faults but false-alarmed on every ambient-temperature swing, until an outdoor-temperature feature stopped the boundary from treating a hot afternoon as novel. The shipped system alerted only when at least two of the three agreed, halving nuisance pages. This condition-monitoring pattern anchors Chapter 37.

The comparison code below fits all three on the same feature matrix and lines up their scores, which is exactly how you should start any new deployment: run all three, look at where they agree and disagree, and let that structure the investigation.

import numpy as np
from sklearn.ensemble import IsolationForest
from sklearn.neighbors import LocalOutlierFactor
from sklearn.svm import OneClassSVM
from sklearn.preprocessing import StandardScaler

# X_train: healthy windows (n, d).  X_test: recent windows to score.
scaler = StandardScaler().fit(X_train)          # matters for LOF and OCSVM
Xtr, Xte = scaler.transform(X_train), scaler.transform(X_test)

iforest = IsolationForest(n_estimators=200, contamination=0.02,
                          random_state=0).fit(Xtr)
ocsvm   = OneClassSVM(kernel="rbf", gamma="scale", nu=0.02).fit(Xtr)
lof     = LocalOutlierFactor(n_neighbors=20, novelty=True).fit(Xtr)

# Higher = more anomalous for all three (sign-flipped so they align).
scores = {
    "iforest": -iforest.score_samples(Xte),
    "ocsvm":   -ocsvm.score_samples(Xte),
    "lof":     -lof.score_samples(Xte),
}
consensus = np.mean([(s > np.quantile(s, 0.98)) for s in scores.values()], axis=0)
flagged = np.where(consensus >= 2/3)[0]          # >=2 of 3 detectors agree
Fitting Isolation Forest, one-class SVM, and LOF (in novelty=True mode so it scores unseen points) on the same standardized healthy features, then flagging test windows where at least two of the three exceed their 98th-percentile score. Note that only LOF and the SVM need the StandardScaler; the forest is scale-invariant.

novelty=True is not cosmetic

It is easy to read novelty=True above as a formality; it is not. LOF's default, novelty=False, only supports fit_predict on the training set, assuming some training points are already anomalies to flag in place (outlier detection). novelty=True assumes the training set is clean and unlocks score_samples on new points instead (novelty detection), the mode a deployed system needs. IsolationForest and OneClassSVM default to novelty-style scoring already, so LOF is the one estimator where forgetting the flag silently breaks your training-serving split.

scikit-learn collapses each detector to two lines

A from-scratch Isolation Forest (split trees, path-length averaging, \(c(n)\) normalization) is roughly 120 lines; a correct LOF with a k-d tree search and reachability smoothing is 80 or more; a one-class SVM needs a full quadratic-program solver. scikit-learn's IsolationForest, LocalOutlierFactor, and OneClassSVM each reduce to a fit plus a score_samples call, so the two lines above replace several hundred and hand you validated tree ensembles, neighbor structures, and QP solvers for free. Spend the saved effort on features and thresholds, where sensor-anomaly performance actually lives.

Which one, when

Reach for Isolation Forest first on high-dimensional fleet telemetry: fast, scale-free, needs almost no tuning. Add LOF when normal operation has multiple regimes of differing density and per-unit rarity matters. Keep one-class SVM for lower-dimensional, well-scaled features where a tight curved boundary is meaningful and training volume is modest. None outputs a calibrated probability, so pair any of them with the conformal machinery of Chapter 18 for a controllable false-alarm rate instead of an arbitrary cutoff.

Learned features instead of hand-built ones

All three detectors are only as good as the feature vector \(x\) they score. An active research line, Deep Isolation Forest and related deep one-class methods, feeds them a self-supervised encoder's representations (see Chapter 17) instead of hand-built statistics, separating faults invisible to any single engineered feature.

Exercise: build the failure each detector cannot see

Synthesize a 2-D normal set as two Gaussian blobs of very different variance (a tight cluster at the origin, a diffuse cluster at \((6,6)\)). Craft three test points: (a) far out on one axis from both blobs; (b) low global density just outside the tight cluster, but at a distance ordinary for the diffuse cluster; (c) anomalous only because it violates the positive correlation within a blob. Score all three with Isolation Forest, LOF, and one-class SVM. Predict beforehand which detector misses which point and why, then confirm it. Which single detector, if any, catches all three, and what does that tell you about ensembling?

Self-check

  1. Isolation Forest needs no feature scaling but LOF and one-class SVM do. What property of each algorithm causes that difference?
  2. You set \(\nu = 0.05\) on a one-class SVM but your training window secretly contains 15 percent faulty samples. What happens to the learned boundary, and how does this connect to the leakage warnings of Chapter 5?
  3. Give a concrete sensor scenario where a point has ordinary global density but a high LOF, and explain why a global detector would wave it through.

What's Next

In Section 12.6, we confront the uncomfortable truth lurking under every score in this section: with few or no labels, how do you even know your detector is any good? We will build honest evaluation protocols for partially labeled anomaly data and dismantle the point-adjust trap, a scoring convention that has quietly inflated a decade of time-series anomaly-detection results.