learn-anomaly-detection-with-phoebe / Builder session 8 of 10
Learn Anomaly Detection with Phoebe · Builder track · Session 8 of 10

Deep anomaly detection: a map, honestly drawn

This session is a guided map of the published deep methods, not a from-scratch build. We cover the one idea that unifies them - reconstruction error - and show a small, runnable illustration of it (PCA as a linear autoencoder), plus a clearly-labelled API sketch of a real autoencoder. Training a deep model properly needs a lot of normal data, tuning, and compute, and belongs in a dedicated deep-learning course. What matters tonight is the intuition, the citation trail, and an honest read on when deep methods are worth it.

🔴 Builder track Practitioners: analysts · data scientists · engineers Python 3.10+ · a notebook ~45 min
0-3 · Welcome 3-18 · Reconstruction error + autoencoders 18-42 · PCA sketch + API sketch 42-45 · Q&A
Part 0

The one idea: reconstruction error

Almost every deep anomaly detector rests on a single move. Train a model to compress and then rebuild normal data. Force the information through a narrow bottleneck and the model can only learn to reproduce the patterns it saw most - the normal ones. Feed it a normal point and it rebuilds it well; feed it an anomaly and it rebuilds it badly, because it never learned that shape. The size of that rebuild miss - the reconstruction error - is the anomaly score. That is the whole thesis, and it survives from linear PCA all the way to deep autoencoders.

Live - presented in session Self-study - read after class ★ Run it now (Python) Sources covered (cited)
★ What you walk out with today A clear grip on reconstruction error as the anomaly score, one runnable illustration of it (PCA reconstruction on a windowed Lumen matrix, scored against truth), an API sketch of a Keras autoencoder you can carry into a real project, and an honest map of LSTM-autoencoders, deep one-class, and VAEs - with the frank caveat that on a single low-dimensional series they often do not beat an isolation forest or a good seasonal detector.
Part 1 · the autoencoder

Compress, rebuild, measure the miss 6 min live

An autoencoder is two halves. The encoder squeezes an input into a small latent vector; the decoder expands that vector back toward the original. Train it only on normal data with a reconstruction loss, and the bottleneck forces it to keep just the structure that normal data shares. Anomalies do not share that structure, so they come back distorted - and that distortion is the score.

Encoder squeezes to a latent code; decoder rebuilds it encode latent code decode input x rebuilt x-hat Normal in: rebuilt well - low recon error Anomaly in: rebuilt badly - high error = flagged
🔍 Click to zoom - the autoencoder bottleneck and reconstruction error
LiveThe deep family, in one breath each3 min▶
  • Autoencoder - the base case above. Reconstruction error is the score.
  • LSTM-autoencoder - the same idea for sequences. It reconstructs a whole window of a time series; a window it rebuilds poorly is an anomalous stretch. This is the natural deep cousin of the forecast-residual detector in b7.
  • Deep one-class / VAE - a one-line nod: Deep SVDD learns a tight boundary around normal in a learned space, and a variational autoencoder scores by reconstruction probability. Same spirit, different machinery.
Real world · honest read

Deep is not a free upgrade. Autoencoders need a lot of clean normal data, careful architecture and threshold tuning, and compute - and on a single low-dimensional series they frequently do not beat an isolation forest (b5) or a good seasonal detector (b3, b7). Reach for deep methods when the data is genuinely high-dimensional or complex - images, logs, multivariate sensor streams - where the structure a shallow model cannot capture is exactly where anomalies hide.

Part 2 · error becomes the score

Reconstruction error over time 5 min live

Once trained, the detector is simple: push every point (or window) through, measure how far the rebuild lands from the original, and threshold that error. Plot the error over time and the anomalies announce themselves - normal points hug a low baseline, anomalies spike above the line. The threshold is the usual precision/recall decision, and setting it on a clean stretch of normal errors is the whole game.

Reconstruction error per point = the anomaly score 0 threshold normal - low error error above threshold = anomaly time →
🔍 Click to zoom - reconstruction error as the anomaly score, thresholded
Self-studyThe three ways this bites you2 min read▶

Reconstruction-error detectors fail in predictable ways - know them before you ship one:

  • Contaminated training - if anomalies leak into the "normal" training set, the model learns to rebuild them too, and stops flagging them. Curate the normal sample.
  • Over-capacity - a bottleneck that is too wide (or a model too powerful) learns to rebuild everything, anomalies included, and the error signal collapses. The squeeze is the point.
  • Threshold drift - as the normal regime shifts, yesterday's error threshold turns into today's false-alarm generator. Re-estimate it on fresh normal data.
Demo 1 of 2 · runnable

PCA reconstruction error ★ 12 min · everyone builds

The concept made concrete, and actually runnable. PCA is a linear autoencoder: project each window onto a few components, then project back. Normal windows rebuild well; the anomalous ones rebuild badly, and the squared rebuild error is the anomaly score - no deep-learning library required.

Rebuild the labeled Lumen series (from b1) so the notebook runs standalone.

Turn the series into a feature matrix of overlapping 7-day windows.

Fit PCA, reconstruct, and take the per-window squared error as the score.

Threshold robustly and score precision, recall, and F1 against truth.

★ Run it now (Python) - PCA as a linear autoencoderimport pandas as pd, numpy as np from numpy.lib.stride_tricks import sliding_window_view from sklearn.decomposition import PCA # --- rebuild the labeled Lumen series (from b1) --- idx = pd.date_range("2026-01-01", periods=90, freq="D") season = np.array([30,42,48,40,22,-95,-110]) i = np.arange(90) y = 900 + 1.2*i + season[i % 7] + 10*np.sin(i*1.7) + 7*np.cos(i*0.6) truth = [20, 45, 63, 70, 71, 72] y[20] += 430; y[45] -= 380 y[63] = y[63] - season[63 % 7] + season[6] - 40 y[[70,71,72]] += [175,190,170] s = pd.Series(np.round(y), index=idx, name="sessions") # --- windows -> PCA reconstruction error (a linear autoencoder) --- v = ((s - s.mean()) / s.std()).to_numpy() w = 7 X = sliding_window_view(v, w) # (84, 7) overlapping windows pca = PCA(n_components=2).fit(X) # bottleneck = 2 components Xr = pca.inverse_transform(pca.transform(X)) # encode then decode err = ((X - Xr) ** 2).mean(axis=1) # reconstruction error per window # map each window's error to its last day, then threshold robustly score = np.full(len(s), np.nan); score[w-1:] = err m = np.nanmedian(err); iqr = np.nanpercentile(err,75) - np.nanpercentile(err,25) flags = set(np.where(score > m + 3*iqr)[0]) tp = len(flags & set(truth)); fp = len(flags - set(truth)); fn = len(set(truth) - flags) prec = tp/(tp+fp) if tp+fp else 0; rec = tp/(tp+fn) if tp+fn else 0 f1 = 2*prec*rec/(prec+rec) if prec+rec else 0 print(f"PCA-recon caught {tp}/{len(truth)} precision {prec:.2f} recall {rec:.2f} F1 {f1:.2f}") # a linear autoencoder; a deep one replaces PCA with a nonlinear encoder/decoder
This is the honest bridge to deep PCA reconstruction error is the deep idea stripped to linear algebra - runnable in seconds, no GPU. A real autoencoder swaps the linear projection for a nonlinear network, which helps only when the normal structure is genuinely nonlinear. If PCA-recon already ties your isolation forest here, that is your signal that this series does not need deep at all.
Demo 2 of 2 · sketch, not run

Autoencoder API sketch ○ read-through · not executed

What the deep version looks like in code. This is an API sketch - the shape of a Keras autoencoder so you recognise it in the wild - not a trained model. It needs the library, a lot of clean normal data, and compute, none of which we run here.

Build a symmetric encoder / decoder with a narrow bottleneck.

Train it to reconstruct normal-only data with a reconstruction loss.

Score by per-sample reconstruction error; threshold on a held-out normal set.

○ API sketch - needs the library + data + compute; not run here# SKETCH ONLY - illustrative Keras API, not executed in this notebook. from tensorflow import keras from tensorflow.keras import layers n_features = 7 # e.g. a 7-day window ae = keras.Sequential([ layers.Input(shape=(n_features,)), layers.Dense(16, activation="relu"), # encoder layers.Dense(4, activation="relu"), # bottleneck (the squeeze) layers.Dense(16, activation="relu"), # decoder layers.Dense(n_features), # rebuild ]) ae.compile(optimizer="adam", loss="mse") # train on NORMAL data only, so anomalies stay un-learned # ae.fit(X_normal, X_normal, epochs=50, batch_size=32, validation_split=0.1) # score = per-sample reconstruction error; threshold on held-out normal # recon = ae.predict(X) # err = ((X - recon) ** 2).mean(axis=1) # flags = err > threshold_from_normal(err_normal) # e.g. 99th percentile # # for sequences, swap Dense for an LSTM encoder/decoder (LSTM-autoencoder): # it reconstructs a WINDOW of the series; a poorly-rebuilt window = anomalous stretch.
Real world · re-verify

This area moves fast - treat any survey as a snapshot. The map here follows Pang, Shen, Cao & van den Hengel (2021), "Deep Learning for Anomaly Detection: A Review" (ACM Computing Surveys 54(2); arXiv:2007.02500). Before you commit to a deep method for a real system, re-verify the current state of the art and, just as importantly, benchmark it against the shallow detectors from this course - deep should have to earn its complexity on your data, not win by reputation.

Homework

Try it yourself - this week ◐ 30-45 min total

Source material

Sources covered (cited)

A surveyed map, not a from-scratch build. This session covers:

✓Pang, Shen, Cao & van den Hengel (2021), "Deep Learning for Anomaly Detection: A Review," ACM Computing Surveys 54(2); arXiv:2007.02500Parts 1-2 · reconstruction error, autoencoders, LSTM-AE, deep one-class / VAE
◐scikit-learn PCA · decomposition user guideDemo 1 · PCA as a linear autoencoder (runnable illustration)
Check yourself

Three questions before you go 🎯 ◐ 90 seconds

1 · In a reconstruction-based detector, what is the anomaly score?

The model learns to rebuild normal data through a bottleneck. Anomalies were never learned, so they come back distorted; that distortion (the reconstruction error) is the score.

2 · When are deep methods most worth the extra cost?

On one low-dimensional series, an isolation forest or seasonal detector often matches or beats an autoencoder. Deep pays off where the structure is too complex for a shallow model.

3 · Why does PCA reconstruction error illustrate the deep idea?

Projecting onto a few components then back is exactly encode-then-decode. A deep autoencoder just replaces that linear projection with a nonlinear network.

Builder session 8 cheat sheet · pin this

Reconstruction errortrain to rebuild normal; anomalies rebuild badly. The miss is the score.
Autoencoderencoder squeezes to a latent code, decoder rebuilds. Bottleneck keeps only normal structure.
LSTM-AEsame idea for sequences - reconstruct a window; poorly-rebuilt window = anomalous stretch.
Deep one-class / VAEDeep SVDD boundary; VAE reconstruction probability. Same spirit.
PCA = linear AEproject to few components and back; runnable reconstruction error, no GPU.
Honest readdeep needs lots of clean normal data + tuning + compute; often no better on one low-dim series.
Cite + re-verifyPang et al. (2021), arXiv:2007.02500. This area moves fast - benchmark deep against shallow.
Next sessionb9 - evaluation: precision/recall/F1, PR-AUC, point-adjust, threshold tuning.