B4B33RPZ · Pattern Recognition and Machine Learning

Parzen windows

Lab 4: estimating a probability density from samples: histogram, k-NN and Parzen windows (kernel density estimation)
Klára JanouškováFEE CTU, Prague
Part I

Recap

What have we assumed so far?

p(x∣k)p(x \mid k)priors p(k)p(k)loss WW
Bayesian decisionknownknownknown
Neyman–Pearson, minimax, Waldknownunknownunknown
Todayunknownwe only have samples x1,…,xnx_1, \dots, x_n
Probability density estimation: from samples x1,…,xnx_1, \dots, x_n drawn from p(x)p(x), build an estimate p^(x)\hat p(x). For a classifier we do it once per class: the samples of class kk give p^(x∣k)\hat p(x \mid k).

How can we estimate a density?

Parametric (next lab)

Assume a shape, e.g. a Gaussian; estimate its few parameters (μ\mu, σ\sigma).

MLE, MAP, Bayesian estimation

Non-parametric (today)

No fixed shape: the samples themselves define p^\hat p.

histogram, k-NN, Parzen windows = kernel density estimation

Mixtures

A sum of a few simple densities: in between the two.

mixture of Gaussians, deep neural networks

Parametric: few numbers to learn, but if the true density has a different shape (two bumps, one Gaussian fitted), the error stays however many samples we get. Non-parametric: follows any shape given enough data, but needs more samples and keeps all of them to evaluate p^(x)\hat p(x).

Part II

Histogram

We have samples. What is p(x)p(x)?

We have: {x1,x2,…,xn}\{x_1, x_2, \dots, x_n\}, each xi∼p(x)x_i \sim p(x), independent

We want: p(x)p(x)

Where is p(x)p(x) high? Where is it zero? How would you decide?

Count: many samples close together → p(x)p(x) high; no samples → p(x)≈0p(x) \approx 0.

Simplest way to count: cut the axis into bins and count the samples in each bin.

Counts as bars = a histogram. But a count is not a density: how tall should a bar be?

21393841−3−2−101234x

How tall should a histogram bar be?

The same bins as before, now as bars. A bin BB holds NBN_B of the NN samples and has width wBw_B. Which height p^\hat p?

↓ what the data say: the fraction of samples that landed in the bin

fraction in B=NBN\text{fraction in } B = \frac{\textcolor{blue}{N_B}}{N}

The bin has width wBw_B.

↓ what our bar says: probability of the bin = area of the bar = height × width

area of the bar=p^(x) wB\text{area of the bar} = \textcolor{purple}{\hat p(x)}\, \textcolor{orange}{w_B}

↓ we choose the height so that the two agree

p^(x) wB=NBN\textcolor{purple}{\hat p(x)}\, \textcolor{orange}{w_B} = \frac{\textcolor{blue}{N_B}}{N}

↓ divide both sides by wB\textcolor{orange}{w_B}

p^(x)=NBN wB\textcolor{purple}{\hat p(x)} = \frac{\textcolor{blue}{N_B}}{N\, \textcolor{orange}{w_B}}
−3−2−1012340.10.20.30.4xN = 40 samples in totalNB  = 13wB  = 1p̂area = p̂ · wB p̂ = 0.325
In words: take each bin's relative frequency NBN\frac{\textcolor{blue}{N_B}}{N} and divide it by the bin width wB\textcolor{orange}{w_B}. Bin [−2,−1)[-2, -1): 1340÷1=0.325\frac{\textcolor{blue}{13}}{40} \div \textcolor{orange}{1} = \textcolor{purple}{0.325}

Histogram: pros and cons?

Discuss in pairs for a minute, then we collect.

✓ Pros
  • ✓ simple, easy to visualise
  • ✓ we don't need to keep the xix_i: the bin counts are enough
  • ✓ fast to evaluate: find the bin, read the height
✗ Cons
  • ✗ step-like, not smooth
  • ✗ we must choose wBw_B: too small → empty bins, too large → no detail
  • ✗ depends on where the bins start
  • ✗ sparse for dimension >2> 2: mm bins per axis → mDm^D cells

Try it: bin width and bin origin

Part III

More dimensions: KK, NN and VV

More dimensions?

1D: p^=NB/(N wB)\hat p = \textcolor{blue}{N_B} / (N\, \textcolor{orange}{w_B}). In 2D the bin is a square region RR around xx. Rename: count NB→K\textcolor{blue}{N_B} \to \textcolor{blue}{K}, size wB→V\textcolor{orange}{w_B} \to \textcolor{orange}{V} (area s2s^2; a volume in DD dims).

s = 3.3region RV = s²rball RV = πr²K = 5insidexN = 21 samples

↓ what our estimate says: p^\textcolor{purple}{\hat p} is constant over RR, so probability = height × volume

probability of R=p^(x) V\text{probability of } R = \textcolor{purple}{\hat p(x)}\, \textcolor{orange}{V}

↓ what the data say: K\textcolor{blue}{K} of the NN samples fell into RR

fraction in R=KN\text{fraction in } R = \frac{\textcolor{blue}{K}}{N}

↓ we choose p^\textcolor{purple}{\hat p} so that the two agree, then divide by V\textcolor{orange}{V}

p^(x) V=KN  ⇒  p^(x)=KN V\textcolor{purple}{\hat p(x)}\, \textcolor{orange}{V} = \frac{\textcolor{blue}{K}}{N} \;\Rightarrow\; \textcolor{purple}{\hat p(x)} = \frac{\textcolor{blue}{K}}{N\, \textcolor{orange}{V}}
Two ways to use it (NN fixed):
fix VV, count KK: histogram, Parzen windows
fix KK, find VV: k-nearest neighbours

From now on RR is a ball around xx (a circle in 2D): one number, the radius r\textcolor{orange}{r}; V=πr2\textcolor{orange}{V = \pi r^2}. k-NN grows r\textcolor{orange}{r} until K\textcolor{blue}{K} samples are inside.

Fix KK, find VV: k-nearest neighbours

q1q2123123samplequerycounted: Kcircle: radius r, volume V

RR is now a circle around the query. N=21N = 21 samples, K=3K = 3. Question: is p^\hat p higher at q1 or at q2?

q1: grow a circle until it holds 3 points. The 3rd nearest point sets the radius: r=2.42r = 2.42

q2: the 3 nearest are much closer: r=1.12r = 1.12

↓ in 2D the "volume" of the circle is its area

p^(q)=KN V,V=πr2\textcolor{purple}{\hat p(q)} = \frac{\textcolor{blue}{K}}{N\, \textcolor{orange}{V}}, \qquad \textcolor{orange}{V = \pi r^2}
q1: p^=321⋅π⋅2.422=321⋅18.3=0.0078\textcolor{purple}{\hat p} = \frac{\textcolor{blue}{3}}{21 \cdot \textcolor{orange}{\pi \cdot 2.42^2}} = \frac{\textcolor{blue}{3}}{21 \cdot \textcolor{orange}{18.3}} = \textcolor{purple}{0.0078}
q2: p^=321⋅π⋅1.122=321⋅3.9=0.0364\textcolor{purple}{\hat p} = \frac{\textcolor{blue}{3}}{21 \cdot \textcolor{orange}{\pi \cdot 1.12^2}} = \frac{\textcolor{blue}{3}}{21 \cdot \textcolor{orange}{3.9}} = \textcolor{purple}{0.0364}
Same KK, smaller circle VV → higher density p^\hat p at q2.

More: k-nearest neighbours

Fix VV, count KK: a window

q1q2112345samplequerycounted: Kcircle: radius r, volume V

Same 21 samples. Now every query gets a circle of the same radius r=2r = 2. Count the points inside.

q1: K=1K = 1

q2: K=5K = 5

↓ now V\textcolor{orange}{V} is the same for both queries, only K\textcolor{blue}{K} changes

p^(q)=KN V,V=π⋅22=12.6\textcolor{purple}{\hat p(q)} = \frac{\textcolor{blue}{K}}{N\, \textcolor{orange}{V}}, \qquad \textcolor{orange}{V = \pi \cdot 2^2 = 12.6}
q1: p^=121⋅12.6=0.0038\textcolor{purple}{\hat p} = \dfrac{\textcolor{blue}{1}}{21 \cdot \textcolor{orange}{12.6}} = \textcolor{purple}{0.0038}
q2: p^=521⋅12.6=0.0189\textcolor{purple}{\hat p} = \dfrac{\textcolor{blue}{5}}{21 \cdot \textcolor{orange}{12.6}} = \textcolor{purple}{0.0189}
Both ways agree: q2 lies in the denser region. A window around xx that counts points is the idea behind Parzen.

Quick check: fixed KK or fixed VV?

We have N≥KN \ge K samples. With which estimate can some xx get p^(x)=0\hat p(x) = 0?

p^=K/(NV)\hat p = K/(NV). A fixed window in an empty area holds K=0K = 0, so p^=0\hat p = 0. A k-NN circle grows until it holds KK samples, so p^>0\hat p > 0 everywhere (with fewer than KK samples k-NN is not defined at all).

In the k-NN estimate, a query far from all points gets…

KK is fixed, so VV grows with the distance: p^∼1/V\hat p \sim 1/V is small but never 0.

k-NN density in 1D: what does KK do?

−20240.20.4K = 1−2024K = 5−2024K = 20k-NN estimate (clipped at 0.6)true p(x)samples

K=1K = 1: a spike wherever two samples are close: overfits.

K=5K = 5: follows both bumps, still noisy.

K=20K = 20: smooth, but flat and with heavy tails.

Notice the tails: far from the data the k-NN estimate stays much too high. Why, and what it means: next slide.

What happens far from the data?

A query right of all samples, x=6x = 6. Its K=5\textcolor{blue}{K = 5} nearest samples are the rightmost ones.

↓ in 1D the "ball" is an interval of half-width r\textcolor{orange}{r}; it must reach back to the data, so r\textcolor{orange}{r} grows with the distance from the data

V=2r,r=x−2.21  ≈  ∣x∣ for large ∣x∣\textcolor{orange}{V} = 2\textcolor{orange}{r}, \quad \textcolor{orange}{r} = x - 2.21 \;\approx\; |x| \text{ for large } |x|

here r=6−2.21=3.79\textcolor{orange}{r} = 6 - 2.21 = 3.79, V=7.58\textcolor{orange}{V} = 7.58 (2.21: the 5th sample from the right)

↓ into K/(NV)\textcolor{blue}{K}/(N\textcolor{orange}{V}): far out, the estimate falls only like 1/∣x∣1/|x|

p^(x)≈K2N∣x∣\textcolor{purple}{\hat p(x)} \approx \frac{\textcolor{blue}{K}}{2N|x|}

At x=6x = 6: p^=0.016\textcolor{purple}{\hat p} = 0.016, true p=0.00005p = 0.00005: about 308× too high. A Gaussian tail falls like e−x2/2e^{-x^2/2}.

↓ total area of the right tail, from aa to bb

∫abK2Nx dx=K2Nln⁡ba→  b→∞  ∞\int_a^b \frac{\textcolor{blue}{K}}{2N x}\,dx = \frac{\textcolor{blue}{K}}{2N} \ln\frac{b}{a} \xrightarrow{\;b \to \infty\;} \infty
−20246810V = 2r = 7.58K = 5 nearestquery x = 610⁻⁶10⁻⁴10⁻²1−20246810k-NN: 0.016true: 0.00005k-NN estimate, K = 5true p(x)sampleslog scale
TL;DR: k-NN can never say "nothing here": it always stretches until it finds K\textcolor{blue}{K} samples, so the tails never die out and the total area is infinite. Parzen: each bump has area 1n\frac{1}{n}, total exactly 1.
Part IV

Parzen windows

A window around xx

The fixed circle from before, in 1D: a window of width hh centred at xx. Five samples, h=1.4h = 1.4.

At x=0.6x = 0.6: how many samples fall into the window?

K=2\textcolor{blue}{K = 2}

↓ K/(NV)\textcolor{blue}{K}/(N\textcolor{orange}{V}) with V=h\textcolor{orange}{V = h}

p^(x)=Kn h\textcolor{purple}{\hat p(x)} = \frac{\textcolor{blue}{K}}{n\,\textcolor{orange}{h}}
p^(0.6)=25⋅1.4=0.29\textcolor{purple}{\hat p(0.6)} = \dfrac{\textcolor{blue}{2}}{5 \cdot \textcolor{orange}{1.4}} = \textcolor{purple}{0.29}

Seen from one sample xix_i: it adds 1nh\frac{1}{nh} to every xx within h/2h/2 of it: a box of width hh, height 1nh\frac{1}{nh}, so area 1n\frac{1}{n}.

Every sample adds its box: n⋅1nn \cdot \frac{1}{n} = total area 1, and p^≥0\hat p \ge 0: a proper probability density.

−4−20240.10.20.3xwindow of width h around x = 0.6p̂ = 0.29xi area= 1/none sample:a box of height 1/(nh)sum of the boxestotal area = 5 · 1/5 = 1samplesbox per samplesump̂(0.6)

↓ the estimate at xx = the boxes that cover xx; [⋅][\cdot] is 1 if true, else 0

p^(x)=1n∑i=1n1h [ ∣x−xi∣<h/2 ]\hat p(x) = \frac{1}{n} \sum_{i=1}^{n} \frac{1}{h}\, [\, |x - x_i| < h/2 \,]

Smooth bumps instead of boxes

With boxes a sample counts fully or not at all, so p^\hat p jumps. Let its weight fade with the distance instead.

boxes (before)bumps (now)
one sample xix_i adds1nh [ ∣x−xi∣<h2 ]\dfrac{1}{n\textcolor{orange}{h}}\,[\,|x - x_i| < \tfrac{\textcolor{orange}{h}}{2}\,]1nh φ ⁣(x−xih)\dfrac{1}{n\textcolor{orange}{h}}\,\varphi\!\left(\dfrac{x - x_i}{\textcolor{orange}{h}}\right)
h\textcolor{orange}{h} is thewidth of the boxstd. deviation
area of one1/n\textcolor{purple}{1/n}1/n\textcolor{purple}{1/n}
all nn samplesstaircase, area 1\textcolor{purple}{1}smooth, area 1\textcolor{purple}{1}

Every sample gets its bump; φ(u)=12πe−u2/2\varphi(u) = \frac{1}{\sqrt{2\pi}} e^{-u^2/2} is the standard normal density, and 1hφ(uh)=Kh(u)\frac{1}{\textcolor{orange}{h}}\varphi(\frac{u}{\textcolor{orange}{h}}) = K_h(u), a normal with std. h\textcolor{orange}{h}.

−4−20240.10.20.3xbox (before) andbump (now): botharea 1/nxi sum of the bumps = p̂(x)samplesone bumpsumboxes (before)
p^h(x)=1n∑i=1nKh(x−xi)\hat p_h(x) = \frac{1}{n} \sum_{i=1}^{n} K_h(x - x_i)

Parzen windows: the general recipe

Given samples x1,…,xnx_1, \dots, x_n, a kernel KK and a bandwidth h>0\textcolor{orange}{h} > 0:

p^h(x)=1n∑i=1n1h K ⁣(x−xih)\textcolor{purple}{\hat p_h(x)} = \frac{1}{n} \sum_{i=1}^{n} \frac{1}{\textcolor{orange}{h}}\, K\!\left(\frac{x - x_i}{\textcolor{orange}{h}}\right)

KK sets the shape of one bump, h\textcolor{orange}{h} its width; the 1h\frac{1}{\textcolor{orange}{h}} keeps its area unchanged when we stretch it. In DD dimensions: 1hD\frac{1}{\textcolor{orange}{h}^D}.

Box: K(u)=[ ∣u∣<12 ]K(u) = [\,|u| < \frac12\,]. Gaussian: K(u)=φ(u)K(u) = \varphi(u), as in the assignment. More shapes in the Optional section.

What must KK look like?
  • K(u)≥0K(u) \ge 0: no negative density
  • ∫K(u) du=1\int K(u)\,du = 1: total area 1
  • symmetric, K(u)=K(−u)K(u) = K(-u)

The first two are needed for a density; the third is a sensible choice.

More: Kernel density estimation

Kernel symmetry

a bump on every data pointread at xx1x2x3xone bump on the query xxx1x2x3samplesGaussian bumpheight read off

↓ the Gaussian is symmetric: Kh(u)=Kh(−u)K_h(u) = K_h(-u)

Kh(x−xi)=Kh(xi−x)K_h(x - x_i) = K_h(x_i - x)

So p^h(x)\hat p_h(x) is also one bump on xx, averaged over the samples: a soft count of the samples near xx, like the fixed-radius circle.

Try it: the bandwidth hh

Part V

Choosing hh

How big should hh be?

−4−20240.20.40.6h = 0.08too small: overfitting−4−2024h = 0.57about right−4−2024h = 2too large: underfittingParzen estimatetrue p(x)samples

The same trade-off as in every model: too flexible fits the noise in the samples, too rigid misses the structure.

Can the training data choose hh?

Idea: pick the hh under which our samples are most probable (their log-likelihood; why the log: next slide)

Ltrn(h)=∑j=1nlog⁡p^h(xj)L_{\text{trn}}(h) = \sum_{j=1}^{n} \log \hat p_h(x_j)

As h→0h \to 0, Ltrn(h)L_{\text{trn}}(h)…

Every training sample sits under its own bump, whose height 1nhφ(0)\frac{1}{nh}\varphi(0) grows without limit.

↓ xjx_j is itself a sample: its own bump sits on it, with height 1n1hφ(0)\frac{1}{n}\frac{1}{h}\varphi(0)

p^h(xj) ≥ 1n h φ(0) → h→0  ∞\hat p_h(x_j) \ \ge\ \frac{1}{n\,h}\,\varphi(0) \ \xrightarrow{\ h \to 0\ }\ \infty

The "best" hh would be 0: one spike per sample, a perfect score on these samples, useless for new ones.

Score p^h\hat p_h on samples it has not seen.

More: likelihood function

How do we score p^\hat p on new samples?

A good estimate puts high density where new samples actually land. Two estimates, three held-out samples:

x1′x'_1x2′x'_2x3′x'_3productsum of logs
p^A\hat p_A0.300.250.200.015−4.2
p^B\hat p_B0.500.010.400.002−6.2

BB is higher at two samples, but nearly misses x2′x'_2: the product punishes that. AA wins.

↓ samples are independent, so the probability of seeing all of them is a product: the likelihood

∏x∈Xtstp^(x)\prod_{x \in X_{\text{tst}}} \hat p(x)

↓ take the log: products of many small numbers underflow (100 samples at 0.05: 10−13010^{-130}); the log turns the product into a sum and keeps the same order, so the same hh wins

log⁡∏p^(x)=∑x∈Xtstlog⁡p^(x)\log \prod \hat p(x) = \sum_{x \in X_{\text{tst}}} \log \hat p(x)
Higher log-likelihood on held-out samples = better estimate. A sample where p^≈0\hat p \approx 0 gives log⁡p^→−∞\log \hat p \to -\infty: a strong penalty for spikes and gaps.

More: likelihood and log-likelihood

Choosing hh with 10-fold cross-validation

fold 1testfold 2testfold 3testfold 10test⋮training partheld-out test part
In the assignment
  • crossval(num_data, 10): index sets itrn[i] (Xtrni\textcolor{blue}{X^i_{\text{trn}}}), itst[i] (Xtsti\textcolor{orange}{X^i_{\text{tst}}})
  • compute_Lh: L(h)L(h) for one hh; fminbound: h∗h^* (it minimises, so pass −L-L)

Split the nn samples into 10 equal parts. Fold ii holds out part ii:

Xtsti\textcolor{orange}{X^i_{\text{tst}}}: the samples in part ii;   Xtrni\;\textcolor{blue}{X^i_{\text{trn}}}: the other 9 parts

↓ Parzen built from Xtrni\textcolor{blue}{X^i_{\text{trn}}} only (notebook: p^h(x∣Xtrni)\hat p_h(x \mid X^i_{\text{trn}}), "∣\mid" = "built from")

p^h(i)(x)=1∣Xtrni∣∑xj∈XtrniKh(x−xj)\hat p^{(i)}_h(x) = \frac{1}{|\textcolor{blue}{X^i_{\text{trn}}}|} \sum_{x_j \in \textcolor{blue}{X^i_{\text{trn}}}} K_h(x - x_j)

↓ score it on the held-out samples it never saw

Li(h)=∑x∈Xtstilog⁡p^h(i)(x)L^i(h) = \sum_{x \in \textcolor{orange}{X^i_{\text{tst}}}} \log \hat p^{(i)}_h(x)

↓ average over the 10 folds: every sample is held out exactly once

L(h)=110∑i=110Li(h)L(h) = \frac{1}{10} \sum_{i=1}^{10} L^i(h)

↓ choose the hh that predicts unseen samples best

h∗=argmax⁡hL(h)h^* = \operatorname*{argmax}_h L(h)

More: Cross-validation · scikit-learn: cross-validation · scipy fminbound

Cross-validation, one fold at a time

Try it: training vs. cross-validated log-likelihood

Part VI

Assignment

From densities to a classifier

Images of letters A and C, one measurement xx per image. We don't know p(x∣A)p(x \mid A) or p(x∣C)p(x \mid C).

↓ one Parzen estimate per class, from that class's training samples, each with its own hh from cross-validation

p^(x∣A)=p^hA(x∣xA),p^(x∣C)=p^hC(x∣xC)\hat p(x \mid A) = \hat p_{h_A}(x \mid x_A), \quad \hat p(x \mid C) = \hat p_{h_C}(x \mid x_C)

↓ priors: the fraction of training samples in each class

pK(A)=NANA+NC,pK(C)=NCNA+NCp_K(A) = \frac{N_A}{N_A + N_C}, \quad p_K(C) = \frac{N_C}{N_A + N_C}

↓ 0/1 loss: the most probable class (lab 2), with p^\hat p in place of pp

q(x)=argmax⁡k∈{A,C}pK(k) p^(x∣k)q(x) = \operatorname*{argmax}_{k \in \{A, C\}} p_K(k)\, \hat p(x \mid k)

Then: classify the test images and compute the test error. Different hAh_A and hCh_C are fine: each class has its own spread.

The assignment in three parts

1 · Parzen estimate
  • parzen(x, trn_x, h); xx scalar or vector
  • unit test: the hand exercise
  • plots for h∈{100,500,1000,2000}h \in \{100, 500, 1000, 2000\} vs. a normalised histogram
2 · Best hh
  • compute_Lh(itrn, itst, x, h): 10-fold L(h)L(h)
  • h_range = linspace(100, 1000, 19)
  • np.random.seed(42) before crossval
  • fminbound for hAh_A, hCh_C
3 · Classifier
  • classify_bayes_parzen(x_test, xA, xC, pA, pC, h_bestA, h_bestC)
  • test error, show_classification

Upload to BRUTE (zip, no folders): parzen.py, parzen.ipynb, parzen_estimates.png, optimal_h_classA.png, optimal_h_classC.png, parzen_classif.png. Bonus: 2D measurements (Optional section).

Optional

Extra material

Other kernels

boxtriangleEpanechnikovGaussian

Any symmetric bump with area 1 works: p^h(x)=1n∑iKh(x−xi)\hat p_h(x) = \frac{1}{n} \sum_i K_h(x - x_i) with Kh(u)=1hK ⁣(uh)K_h(u) = \frac{1}{h} K\!\left(\frac{u}{h}\right).

  • The box gives the window count from before (steps); the Gaussian is smooth everywhere.
  • "Epanechnikov is optimal": with the best hh for each kernel, it gives the smallest error (the AMISE from the rules-of-thumb slide) of all non-negative kernels. The others are close: for the same error the Gaussian needs only about 5% more samples, the box about 7%.
  • So in practice hh matters far more than the shape of the kernel.

More: Kernel (statistics): common kernels and their efficiency

Where do rules of thumb for hh come from?

The error of p^h\hat p_h has two parts that pull hh in opposite directions.

↓ bias: a wide kernel flattens peaks and fills valleys, by about 12h2p′′(x)\frac{1}{2} h^2 p''(x)

∫bias2≈h44∫p′′(x)2 dx\int \text{bias}^2 \approx \frac{h^4}{4} \int p''(x)^2\,dx

↓ variance: a narrow window holds few samples, about nh p(x)n h\, p(x) of them

∫var≈1nh∫K(u)2 du\int \text{var} \approx \frac{1}{n h} \int K(u)^2\,du

↓ add them, set the derivative in hh to 0

h∗5=∫K2n∫p′′2h^{*5} = \frac{\int K^2}{n \int p''^2}
0.10.20.5120.010.020.030.04hmin at h = 0.43bias² ∝ h⁴variance ∝ 1/(nh)total error

More: Kernel density estimation: bandwidth selection, AMISE

Silverman's rule: pretend pp is Gaussian

h∗h^* needs ∫p′′2\int p''^2, which depends on the unknown pp.

↓ plug in a Gaussian with the sample's standard deviation σ\sigma, and a Gaussian kernel

∫p′′2=38π σ5,∫K2=12π\int p''^2 = \frac{3}{8\sqrt{\pi}\,\sigma^5}, \qquad \int K^2 = \frac{1}{2\sqrt{\pi}}

↓ into h∗5=∫K2/(n∫p′′2)h^{*5} = \int K^2 / (n \int p''^2)

h=(43)1/5σ n−1/5≈1.06 σ n−1/5h = \left(\tfrac{4}{3}\right)^{1/5} \sigma\, n^{-1/5} \approx 1.06\, \sigma\, n^{-1/5}

n−1/5n^{-1/5}: 10× more data → hh only 0.63× smaller.

running example, n=40n = 40hh
Silverman, 1.06 σn−1/51.06\,\sigma n^{-1/5} (σ=1.62\sigma = 1.62)0.82
Scott (scipy default), σn−1/5\sigma n^{-1/5}0.77
robust: 0.9min⁡(σ,IQR/1.34) n−1/50.9 \min(\sigma, \text{IQR}/1.34)\, n^{-1/5}0.70
cross-validation0.57
best, using the true pp0.43

The rules oversmooth here: two bumps curve more than one Gaussian with the same σ\sigma. Fine as a quick default, or as a starting point for CV.

More: Silverman's rule · scipy gaussian_kde (Scott, Silverman) · interquartile range

Adaptive bandwidth: one width per sample

One hh is a compromise: too wide where samples are crowded, too narrow where they are sparse. Idea: every sample xix_i gets its own width hi\textcolor{orange}{h_i}.

hh: one global width, as before (Silverman or cross-validation).

Pilot p~\textcolor{blue}{\tilde p}: ordinary Parzen with that hh. p~(xi)\textcolor{blue}{\tilde p(x_i)} says how crowded it is around xix_i.

g\textcolor{purple}{g}: a typical pilot value (geometric mean of all p~(xi)\tilde p(x_i)). Ratio p~(xi)/g\textcolor{blue}{\tilde p(x_i)}/\textcolor{purple}{g}: >1>1 crowded, <1<1 sparse.

hi=h(p~(xi)g)−1/2\textcolor{orange}{h_i} = h \left(\frac{\textcolor{blue}{\tilde p(x_i)}}{\textcolor{purple}{g}}\right)^{-1/2}

4× more crowded than typical → half the width; 4× sparser → twice the width (Abramson's square-root law)

p^(x)=1n∑iKhi(x−xi)\hat p(x) = \frac{1}{n}\sum_i K_{\textcolor{orange}{h_i}}(x - x_i): the same recipe; every bump still has area 1n\frac1n, so p^\hat p is still a density.

−10123450.10.20.30.4xhhp̃(xi ) = 0.46: crowdedp̃(xi ) = 0.12: sparseg = 0.28hi  = 0.39hi  = 0.77samples xi pilot p̃typical gown kerneladaptive p̂

Adaptive bandwidth on a skewed sample

60 samples from a skewed density: a sharp peak near 1 and a long thin tail to the right.

Fixed hh (from CV): a compromise. The peak comes out too low, and lonely tail samples still make small bumps (around 4.3).

Adaptive hih_i: narrow bumps at the peak, so it is closer to the truth; wide bumps in the tail, so it is smooth. Grey bars: the widths hih_i of the far samples.

Used in: t-SNE (each point's Gaussian width σi\sigma_i is set by the perplexity, i.e. by its neighbours) · particle physics, Cranmer 2001, in ROOT as RooKeysPdf · Abramson 1982

0123450.40.81.2xtrue p(x)fixed h = 0.429adaptive hi samples

The curse of dimensionality, numerically

Standard Gaussian in DD dimensions. Window: a cube of side 1 around the peak. How many samples land in it?

DDprobability insidenn for K≈20K \approx 20 inside
10.3852
20.15136
30.056356
50.00822 400
100.000068295 000
204.6·10⁻⁹4.4·10⁹

↓ the coordinates are independent; each one is inside [−12,12][-\frac12, \frac12] with probability 0.383

P(inside)=0.383D,n≈200.383DP(\text{inside}) = 0.383^D, \qquad n \approx \frac{20}{0.383^D}

A 28×28 image has D=784D = 784. Parzen (and histograms) work in 1–3 dimensions; beyond that we need models that assume structure.

More: Curse of dimensionality

Try it: the same window in 1, 2 and 3 dimensions

Bonus: two measurements

xx = (left half) − (right half) intensity
yy = (top half) − (bottom half) intensity

↓ a round 2D Gaussian bump of width hh on every sample, covariance h2Ih^2 I

Kh(u)=12πh2 e−∥u∥2/(2h2)K_h(\mathbf{u}) = \frac{1}{2\pi h^2}\, e^{-\|\mathbf{u}\|^2 / (2h^2)}

↓ the same average as in 1D

p^h(x)=1n∑i=1nKh(x−xi)\hat p_h(\mathbf{x}) = \frac{1}{n} \sum_{i=1}^{n} K_h(\mathbf{x} - \mathbf{x}_i)

Watch the scales: one hh for both axes only makes sense if xx and yy have similar ranges. Rescale them first (e.g. divide each by its standard deviation), or use a separate hh per axis.

In DD dimensions V∼hDV \sim h^D: to keep KK samples in the window we need far more data. Parzen works well only in low dimensions.

Bonus task: show the estimated p^(x∣k)\hat p(\mathbf{x} \mid k) for both letters, e.g. as contour plots.

More: scipy multivariate_normal

Optional

Where Parzen windows show up

You have seen kernel density estimates before

−4−2024mirrored p̂(x)samples

A violin plot is a Parzen estimate drawn on both sides of an axis: the running example, h=0.57h = 0.57.

  • seaborn: sns.kdeplot(x), sns.violinplot(…); bw_adjust scales the default (Scott's) hh
  • pandas: df.plot.kde()
  • maps of hotspots (accidents, crime, GPS traces): a 2D Parzen estimate on a map
Next time a plot looks suspiciously smooth or bumpy: ask which hh was used.

More: seaborn kdeplot · violin plot

Background subtraction in video

A static camera. Which pixels belong to a moving object?

Per pixel: keep its last NN values, fit a 1D Parzen estimate. A new value with low p^\hat p is foreground.

Why non-parametric: a pixel behind a swaying tree sees leaf or sky: two bumps. One Gaussian would call both "unusual".

Elgammal, Harwood, Davis: Non-parametric model for background subtraction, ECCV 2000

050100150200250new value 146: p̂ = 0.014 → backgroundnew value 215: p̂ ≈ 0 → foreground!intensity of one pixelp̂ from the last 50 framespast valuesnew frame

Mean shift: climb p^\hat p to a mode

x←∑iKh(x−xi) xi∑iKh(x−xi)x \leftarrow \frac{\sum_i K_h(x - x_i)\, x_i}{\sum_i K_h(x - x_i)}

Move to the kernel-weighted mean of the samples. Every step goes uphill on p^h\hat p_h.

Comaniciu & Meer 2002: clustering, segmentation, tracking · Wikipedia

Parzen windows inside transformers

Parzen regression: to predict yy at a new xx, average the training outputs yiy_i, weighting each by how close its xix_i is to xx.

y^(x)=∑iwi yi,wi=Kh(x−xi)∑jKh(x−xj)\hat y(x) = \sum_i w_i\, y_i, \qquad w_i = \frac{K_h(x - x_i)}{\sum_j K_h(x - x_j)}

The weights are ≥0\ge 0 and sum to 1: close samples count more.

Attention in a transformer does the same: each token's output is a weighted average of the other tokens' values, with larger weights for tokens that are more "similar" to it.

The difference: "similar" is not a fixed kernel with a bandwidth hh, but learned during training.

Explained step by step, from this exact formula to attention: Dive into Deep Learning, 11.2: Attention pooling by similarity · the kernel view of attention: Tsai et al. 2019, Transformer dissection

Cautionary tale: Parzen for generative models

2014: the original GAN paper could not compute the likelihood of its samples. So it fitted a Parzen estimate to generated images, chose hh by validation, and reported the log-likelihood of the test images.

2016: Theis, van den Oord, Bethge showed that in image dimensions this number is far from the true log-likelihood even with very many samples, and can rank models in the wrong order.

Why: images have hundreds of dimensions. The curse of dimensionality from the Optional section: no sample lands near a test image, so p^\hat p measures distance to the nearest sample, not probability.

The metric was dropped. Lesson: a density estimate is only as good as the dimension it lives in.

Further reading