Lesson 23: The Multivariate Gaussian

USAAIO Lesson 23, from Week 8, fully worked. It builds the N(mu, Sigma) density term by term from the one-dimensional bell curve and proves from Cov(a^T x) why Sigma must be symmetric and PSD - and positive definite if it is to be inverted. It then covers the covariance ellipse from the eigendecomposition, derives and hand-computes the Mahalanobis distance, and covers the normalizing constant and the determinant. It derives the marginals and the bivariate conditional entry by entry, proves that a diagonal Sigma implies independence by factorizing the density, and gives the X^2 counterexample. It closes with Cholesky sampling, x = mu + Lz, proving Cov(mu + Lz) = Sigma line by line, verifying against 200k samples, and tying it to the VAE reparameterization trick. Every snippet runs standalone, and every number was produced by real execution. The lesson runs to 63 slides.

Subject: Machine Learning · 115 slides · code lesson

Open the interactive version of this deck · Homework for this lesson

What this lesson covers

The lesson, slide by slide

1. The Multivariate Gaussian

Title

USAAIO · Lesson 23 · Week 8

The bell curve in many dimensions - and the distribution at the heart of the VAE. We build N(mu, Sigma) from the 1-D Gaussian term by term, prove why Sigma must be PSD, derive Mahalanobis distance, marginals, conditionals, and independence, then sample by Cholesky and prove x = mu + Lz has covariance Sigma.

2. By the end of this lesson you can

Objectives

  1. Build the N(mu, Sigma) density from the 1-D bell curve and explain every factor - the quadratic form, the |Sigma|, and the (2pi) power
  2. Prove Sigma must be symmetric PSD (and PD to be invertible) from Cov(a^T x) = a^T Sigma a >= 0
  3. Read the covariance ellipse off the eigen-decomposition and compute Mahalanobis distance by hand, saying exactly why it beats Euclidean
  4. Derive the marginal and the bivariate conditional x_2 | x_1 entry by entry, and prove diagonal Sigma => independent by factorizing the density
  5. Sample x = mu + Lz with L = chol(Sigma), prove Cov(x) = Sigma, recover it from 200k draws, and connect it to the VAE reparameterization trick

3. What survived from Affine Transforms?

Warm-up

Discussion prompt

Before we open Lesson 23: The Multivariate Gaussian: without looking back, what was the main idea of Affine Transforms, and what could you do by the end of it that you could not do before?

Hint: One sentence for the idea, one for the skill. If the second one is blank, that is the part to revisit.

Answer:

affine maps T(x)=Wx+b, why the composition of affine maps is affine, the resulting collapse of linear networks into a single layer, and why nonlinearity is essential — proven on XOR. Verify the collapse and watch a linear model fail XOR while an MLP solves it.

4. From one bell curve to many

Section

Part 1 of 7 - the density

5. Start from the 1-D Gaussian

Concept

You already know the scalar bell curve: a random score x with mean mu and variance sigma^2 has density

\[ p(x) = \frac{1}{\sqrt{2\pi\,\sigma^2}}\,\exp\!\left(-\frac{(x-\mu)^2}{2\sigma^2}\right) \]

Two moving parts: a front constant that makes the area integrate to 1, and an exponent that is a squared, scaled distance from the center mu. Every piece of the multivariate version is one of these two, generalized.

6. Break it if you can: Start from the 1-D Gaussian

Counterexample

Discussion prompt

You already know the scalar bell curve: a random score x with mean mu and variance sigma^2 has density

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

7. Our running example: two correlated features

Concept

We carry ONE example through the whole lesson: a 2-D vector x = [x_1, x_2] - say a student's standardized reading and math scores - with center mu and covariance Sigma.

\[ \mu = \begin{bmatrix} 1 \\ -1 \end{bmatrix}, \qquad \Sigma = \begin{bmatrix} 2 & 0.8 \\ 0.8 & 1 \end{bmatrix} \]

symbolmeaningvalue
mu_1mean of x_11
mu_2mean of x_2-1
Sigma_11variance of x_12
Sigma_22variance of x_21
Sigma_12 = Sigma_21covariance of x_1, x_20.8

8. Fill in: meaning for Our running example: two correlated features

Comparison

Comparison matrix

From Our running example: two correlated features: refill the meaning column from what you know. The rest of the table is as it appeared.

symbolmeaningvalue
mu_1mean of x_11
mu_2mean of x_2-1
Sigma_11variance of x_12
Sigma_22variance of x_21
Sigma_12 = Sigma_21covariance of x_1, x_20.8

9. The covariance matrix, entry by entry

Concept

Sigma collects every pairwise covariance. Entry (i, j) is Cov(x_i, x_j) = E[(x_i - mu_i)(x_j - mu_j)]. The diagonal holds the variances; the off-diagonal holds the coupling.

\[ \Sigma = \mathbb{E}\big[(x-\mu)(x-\mu)^\top\big] = \begin{bmatrix} \operatorname{Var}(x_1) & \operatorname{Cov}(x_1,x_2) \\ \operatorname{Cov}(x_2,x_1) & \operatorname{Var}(x_2) \end{bmatrix} \]

Sigma is symmetric because Cov(x_i, x_j) = Cov(x_j, x_i)

Why: Swapping the two factors inside the expectation changes nothing, so Sigma_ij = Sigma_ji. Symmetry is not optional - it falls straight out of the definition, and we will lean on it constantly.

10. By analogy: The covariance matrix, entry by entry

Analogy

Discussion prompt

Explain The covariance matrix, entry by entry by analogy to something with no Machine Learning in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

Sigma collects every pairwise covariance. Entry (i, j) is Cov(x_i, x_j) = E[(x_i - mu_i)(x_j - mu_j)]. The diagonal holds the variances; the off-diagonal holds the coupling.

11. What Sigma^{-1} is doing in the exponent

Intuition

In 1-D the exponent divides by sigma^2 - it rescales the squared miss into variance units. A miss of 2 is a big deal if sigma^2 = 0.1 and trivial if sigma^2 = 100.

In many dimensions the single number 1/sigma^2 becomes the whole matrix Sigma^{-1}. It rescales the miss vector x - mu by variance and un-tilts the correlation, so the exponent measures distance the way the data actually spreads - not with a naive ruler.

12. Teach it back: What Sigma^{-1} is doing in the exponent

Explain it

Discussion prompt

Explain What Sigma^{-1} is doing in the exponent to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

In 1-D the exponent divides by sigma^2 - it rescales the squared miss into variance units. A miss of 2 is a big deal if sigma^2 = 0.1 and trivial if sigma^2 = 100.

13. The multivariate Gaussian density N(mu, Sigma)

Concept

Promote each 1-D piece to its matrix form: (x - mu)^2 / sigma^2 becomes the quadratic form (x - mu)^T Sigma^{-1} (x - mu), and sqrt(2 pi sigma^2) becomes (2 pi)^{d/2} |Sigma|^{1/2} for d dimensions.

\[ p(x) = \frac{1}{(2\pi)^{d/2}\,|\Sigma|^{1/2}}\,\exp\!\left(-\tfrac{1}{2}(x-\mu)^\top \Sigma^{-1}(x-\mu)\right) \]

1-D piecegeneralizes torole
(x - mu)^2 / sigma^2(x - mu)^T Sigma^{-1} (x - mu)scaled squared distance
sqrt(2 pi)(2 pi)^{d/2}dimension count in the constant
sqrt(sigma^2)|Sigma|^{1/2}total spread (volume)

14. What each one costs: The multivariate Gaussian density N(mu, Sigma)

Trade off

Comparison matrix

From The multivariate Gaussian density N(mu, Sigma): every row here is a choice with a cost. Fill the generalizes to column, then say which row you would actually pick and what you give up for it.

1-D piecegeneralizes torole
(x - mu)^2 / sigma^2(x - mu)^T Sigma^{-1} (x - mu)scaled squared distance
sqrt(2 pi)(2 pi)^{d/2}dimension count in the constant
sqrt(sigma^2)|Sigma|^{1/2}total spread (volume)

15. Every factor must be well-defined

Concept

Look at what the formula demands: it uses Sigma^{-1} (so Sigma must be invertible) and |Sigma|^{1/2} (so |Sigma| must be positive, or the square root is imaginary).

It also needs the exponent to be <= 0 for every x, so the quadratic form (x - mu)^T Sigma^{-1} (x - mu) must never go negative. All three requirements point at one property of Sigma: positive definiteness. We prove that next.

16. Why Sigma is symmetric PSD

Section

Part 2 of 7 - the constraint

17. The claim, stated precisely

Concept

Every covariance matrix is symmetric positive semidefinite (PSD): symmetric, and a^T Sigma a >= 0 for every vector a. For the density to be non-degenerate (invertible Sigma), we need positive definite (PD): a^T Sigma a > 0 for every a != 0.

\[ \Sigma = \Sigma^\top \quad\text{and}\quad a^\top \Sigma\, a \ge 0 \;\; \forall a \qquad (\text{PD: } > 0 \;\forall\, a \neq 0) \]

18. What has to happen first: Prove a^T Sigma a >= 0, one line at a time

Ranking

Put in order

Put the moves of Prove a^T Sigma a >= 0, one line at a time into the order they have to happen.

  1. Pick any direction a and project the data onto it: define scalar y = a^T (x - mu)
  2. Write Var(y) = E[y^2] since E[y] = 0
  3. Expand the square as (a^T u)(u^T a) with u = x - mu
  4. Pull the constant a outside the expectation
  5. Conclude a^T Sigma a = Var(y) >= 0

Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. y is a single random number - the component of the centered vector along a.

19. Prove a^T Sigma a >= 0, one line at a time

Worked example

Pick any direction a and project the data onto it: define scalar y = a^T (x - mu)

Why: y is a single random number - the component of the centered vector along a. Its variance cannot be negative, and we will show that variance IS a^T Sigma a.

Write Var(y) = E[y^2] since E[y] = 0

Why: y is a^T times a centered vector, so E[y] = a^T E[x - mu] = 0. For a zero-mean quantity, variance is just the mean square.

\[ \operatorname{Var}(y) = \mathbb{E}[\,y^2\,] = \mathbb{E}\big[(a^\top(x-\mu))^2\big] \]

Expand the square as (a^T u)(u^T a) with u = x - mu

Why: A scalar equals itself transposed: a^T u = (a^T u)^T = u^T a. So y^2 = (a^T u)(u^T a) = a^T (u u^T) a.

\[ = \mathbb{E}\big[a^\top (x-\mu)(x-\mu)^\top a\big] \]

Pull the constant a outside the expectation

Why: a is not random, so E[a^T M a] = a^T E[M] a. The middle expectation is exactly Sigma by definition.

\[ = a^\top\,\mathbb{E}[(x-\mu)(x-\mu)^\top]\,a = a^\top \Sigma\, a \]

Conclude a^T Sigma a = Var(y) >= 0

Why: A real variance is never negative, and it equals a^T Sigma a for EVERY a. So Sigma is PSD. This is the whole reason covariance matrices are special - it is not an assumption, it is forced.

20. Decode the notation: Prove a^T Sigma a >= 0, one line at a time

Notation

Annotate

From Prove a^T Sigma a >= 0, one line at a time — read this one piece at a time. What is each part doing?

On: \( \operatorname{Var}(y) = \mathbb{E}[\,y^2\,] = \mathbb{E}\big[(a^\top(x-\mu))^2\big] \)

  • y is a single random number - the component of the centered vector along a. Its variance cannot be negative, and we will show that variance IS a^T Sigma a.
  • y is a^T times a centered vector, so E[y] = a^T E[x - mu] = 0. For a zero-mean quantity, variance is just the mean square.
  • A scalar equals itself transposed: a^T u = (a^T u)^T = u^T a. So y^2 = (a^T u)(u^T a) = a^T (u u^T) a.

21. Guess the shape of the answer: Our Sigma is PD: check the quadratic form…

Estimation

Predict first

PSD is guaranteed. For our density we need strictly PD - invertible. Confirm it three equivalent ways in one runnable block:

Commit before you compute: what does Our Sigma is PD: check the quadratic form and eigenvalues come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.

Correct: Eigenvalues {0.5566, 2.4434} are both positive -> PD

Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. A symmetric matrix is PD exactly when every eigenvalue is > 0.

22. Our Sigma is PD: check the quadratic form and eigenvalues

Worked example

PSD is guaranteed. For our density we need strictly PD - invertible. Confirm it three equivalent ways in one runnable block:

import numpy as np
Sigma = np.array([[2., 0.8], [0.8, 1.]])
print(np.allclose(Sigma, Sigma.T))       # symmetric?
print(np.linalg.eigvalsh(Sigma).round(4)) # all eigenvalues > 0?
print(round(np.linalg.det(Sigma), 4))     # det > 0?
a = np.array([3., -1.])
print(round(a @ Sigma @ a, 4))            # a^T Sigma a > 0?

Eigenvalues {0.5566, 2.4434} are both positive -> PD

Why: A symmetric matrix is PD exactly when every eigenvalue is > 0. Both are, det = 1.36 > 0, and the sample direction gives a^T Sigma a = 14.2 > 0. All three tests agree.

testvalue (verified)verdict
symmetricTrueok
eigenvalues[0.5566, 2.4434]both > 0 -> PD
det(Sigma)1.36> 0
a^T Sigma a at a=[3,-1]14.2> 0

23. Which is which, by verdict

Discrimination

Sort into buckets

Sort these by verdict, from memory, without looking back at Our Sigma is PD: check the quadratic form and…. Telling them apart on the spot is the skill; the table is only where the answer happens to be written down.

ok
symmetric
both > 0 -> PD
eigenvalues
> 0
det(Sigma); a^T Sigma a at a=[3,-1]
g1
verdict is "ok" for symmetric — that is what the table on "Our Sigma is PD: check the quadratic…" records, and it is the single property separating this group from the rest.
g2
verdict is "both > 0 -> PD" for eigenvalues — that is what the table on "Our Sigma is PD: check the quadratic…" records, and it is the single property separating this group from the rest.
g3
verdict is "> 0" for det(Sigma), a^T Sigma a at a=[3,-1] — that is what the table on "Our Sigma is PD: check the quadratic…" records, and it is the single property separating this group from the rest.

24. Something is wrong here: assuming any symmetric matrix is a covariance

Anomaly

Predict first

A student writes this, and it looks reasonable:

It is symmetric and has ones on the diagonal, so [[1, 2], [2, 1]] is a fine covariance matrix.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: Its eigenvalues are -1 and 3. Take a = [1, -1]: a^T Sigma a = 1 - 2 - 2 + 1 = -2 < 0.

Require every eigenvalue >= 0 (PSD), and > 0 (PD) before trusting it as a Sigma.

Why: Its eigenvalues are -1 and 3. Take a = [1, -1]: a^T Sigma a = 1 - 2 - 2 + 1 = -2 < 0. That would be a NEGATIVE variance along direction [1, -1] - impossible. The matrix is indefinite, not a covariance.

25. Trap: assuming any symmetric matrix is a covariance

Trap

The trap

It is symmetric and has ones on the diagonal, so [[1, 2], [2, 1]] is a fine covariance matrix.

Treat [[1, 2], [2, 1]] as Sigma

Why: Its eigenvalues are -1 and 3. Take a = [1, -1]: a^T Sigma a = 1 - 2 - 2 + 1 = -2 < 0. That would be a NEGATIVE variance along direction [1, -1] - impossible. The matrix is indefinite, not a covariance.

The fix

Require every eigenvalue >= 0 (PSD), and > 0 (PD) before trusting it as a Sigma.

Check eigvalsh >= 0 (or attempt a Cholesky)

Why: Our [[2, 0.8], [0.8, 1]] has eigenvalues 0.5566 and 2.4434, both > 0. A failed np.linalg.cholesky is the fastest PD test - it raises the instant a matrix is not positive definite.

26. PSD vs PD: the degenerate case

Concept

A PSD but not PD Sigma has a zero eigenvalue. Then |Sigma| = 0, Sigma^{-1} does not exist, and the standard density formula breaks - the distribution collapses onto a lower-dimensional line or plane (all its mass on a subspace).

That is still a valid Gaussian in a degenerate sense, but you cannot write its density with Sigma^{-1}. For everything below we assume Sigma is strictly PD, so |Sigma| > 0 and Sigma^{-1} exists.

27. The ellipse and Mahalanobis

Section

Part 3 of 7 - geometry & distance

28. Picture it first: Level sets are ellipses

Picture it

Figure (svg): Nested ellipses centered at a point mu, tilted along a diagonal direction, with a longer axis and a shorter axis drawn through the center.

Contours of N(mu, Sigma): concentric tilted ellipses. Long axis = large-eigenvalue direction, short axis = small-eigenvalue direction.

Discussion prompt

Read the picture before the words. What is this showing, and what is the one thing it is built to make obvious? Commit to an answer, then read on.

Hint: Name the parts, then say what changes between them — and if nothing changes, say what is being held still.

Answer:

The density depends on x only through the exponent (x - mu)^T Sigma^{-1} (x - mu). Fixing that quadratic form to a constant c traces out all the equally-likely points - and that set is an ellipse centered at mu.

29. Level sets are ellipses

Intuition

The density depends on x only through the exponent (x - mu)^T Sigma^{-1} (x - mu). Fixing that quadratic form to a constant c traces out all the equally-likely points - and that set is an ellipse centered at mu.

Bigger c = further from center = lower density. So the density is a smooth hill over the plane whose contour lines are nested ellipses, tallest at mu.

Figure (svg): Nested ellipses centered at a point mu, tilted along a diagonal direction, with a longer axis and a shorter axis drawn through the center.

Contours of N(mu, Sigma): concentric tilted ellipses. Long axis = large-eigenvalue direction, short axis = small-eigenvalue direction.

30. The eigen-decomposition gives the axes

Concept

Sigma = Q Lambda Q^T (symmetric eigen-decomposition, Lesson 10). The eigenvectors (columns of Q) point along the ellipse's axes; the eigenvalues in Lambda are the variances along those axes.

A 1-standard-deviation contour reaches sqrt(lambda_k) along axis k. Large eigenvalue = long axis (high spread); small eigenvalue = short axis. Off-diagonal terms in Sigma are exactly what rotate the axes off the coordinate lines.

31. What has to be given first: Compute our ellipse axes

Missing information

Discussion prompt

Eigen-decompose our Sigma to get the axis directions and lengths. np.linalg.eigh is the symmetric solver - real eigenvalues, orthonormal eigenvectors, ascending order:

What do you need to know — or decide — before the first line can be written? List everything the problem has to hand you.

Hint: Anything you would have to invent to get started is a thing the problem must supply.

Answer:

trace = 2 + 1 = 3 and det = 1.36, so eigenvalues solve l^2 - (trace) l + det = 0. sqrt(3.56) = 1.8868, giving l = 0.5566 and 2.4434 - matching eigh exactly.

32. Compute our ellipse axes

Worked example

Eigen-decompose our Sigma to get the axis directions and lengths. np.linalg.eigh is the symmetric solver - real eigenvalues, orthonormal eigenvectors, ascending order:

import numpy as np
Sigma = np.array([[2., 0.8], [0.8, 1.]])
vals, vecs = np.linalg.eigh(Sigma)   # ascending; symmetric -> real
print(vals.round(4))                  # axis variances
print(np.sqrt(vals).round(4))         # 1-sigma semi-axis lengths
print(vecs.round(4))                  # columns = axis directions

Char. poly l^2 - 3l + 1.36 = 0 -> l = (3 +- sqrt(3.56))/2

Why: trace = 2 + 1 = 3 and det = 1.36, so eigenvalues solve l^2 - (trace) l + det = 0. sqrt(3.56) = 1.8868, giving l = 0.5566 and 2.4434 - matching eigh exactly.

axiseigenvalue (variance)semi-axis sqrt(lambda)
short0.55660.7461
long2.44341.5631

33. Work backwards from the answer: Compute our ellipse axes

Reverse engineer

Discussion prompt

Work backwards. The example finished here:

Char. poly l^2 - 3l + 1.36 = 0 -> l = (3 +- sqrt(3.56))/2

What was it asked to do, and what must it have been given? Reconstruct the problem from its answer.

Hint: Every quantity in the result had to enter somewhere. Account for each one.

Answer:

Eigen-decompose our Sigma to get the axis directions and lengths. np.linalg.eigh is the symmetric solver - real eigenvalues, orthonormal eigenvectors, ascending order:

34. Mahalanobis distance: the exponent IS a distance

Concept

The quadratic form in the exponent has a name - the squared Mahalanobis distance from x to the center:

\[ d_M^2(x) = (x-\mu)^\top \Sigma^{-1}(x-\mu) \]

It measures how far x is from mu in standard-deviation units along the ellipse axes. Points on the same contour ellipse are all the same Mahalanobis distance from the center - equally surprising - even though their raw Euclidean distances differ.

35. Euclidean lies when data is correlated

Intuition

Euclidean distance treats every direction the same, as if the cloud were a perfect circle. But our cloud is a tilted, stretched ellipse.

A point 2 units out along the long axis is still deep inside the cloud - totally typical. The same 2 units across the short axis pokes out the side - a genuine outlier. Mahalanobis divides by the spread in each direction, so it calls the first point close and the second far. Euclidean cannot tell them apart.

36. Predict the next row: Invert Sigma by hand

Pattern

Predict first

The table runs: det(Sigma) | 1.36 · (Sigma^{-1})_11 | 0.7353 · (Sigma^{-1})_12 = _21 | -0.5882

In Invert Sigma by hand, given the rows so far: what is the next one — the row where entry is (Sigma^{-1})_22?

Correct: (Sigma^{-1})_22 | 1.4706

entryvalue (verified)
det(Sigma)1.36
(Sigma^{-1})_110.7353
(Sigma^{-1})_12 = _21-0.5882
(Sigma^{-1})_221.4706

Why: The relationship between the columns, not the individual numbers, is what generates the next row. ad - bc with a=2, b=c=0.8, d=1.

37. Invert Sigma by hand

Worked example

To compute Mahalanobis we need Sigma^{-1}. For a 2x2 [[a, b], [c, d]], the inverse is 1/det * [[d, -b], [-c, a]]. First the determinant:

det = 21 - 0.80.8 = 2 - 0.64 = 1.36

Why: ad - bc with a=2, b=c=0.8, d=1. Nonzero, so the inverse exists (PD confirmed again).

\[ \det \Sigma = 2\cdot 1 - 0.8\cdot 0.8 = 1.36 \]

Swap the diagonal, negate the off-diagonal, divide by 1.36

Why: The 2x2 inverse formula. Divide each entry of [[1, -0.8], [-0.8, 2]] by 1.36.

\[ \Sigma^{-1} = \frac{1}{1.36}\begin{bmatrix} 1 & -0.8 \\ -0.8 & 2 \end{bmatrix} = \begin{bmatrix} 0.7353 & -0.5882 \\ -0.5882 & 1.4706 \end{bmatrix} \]

entryvalue (verified)
det(Sigma)1.36
(Sigma^{-1})_110.7353
(Sigma^{-1})_12 = _21-0.5882
(Sigma^{-1})_221.4706

38. Draw the shape of it: Invert Sigma by hand

Blank canvas

Draw it

Draw what Invert Sigma by hand just did — the shape of it, not the line-by-line working. One picture, labels only where you need them. Then check it against the steps: anything you could not draw is a step you followed rather than understood.

39. Restore the missing line: Mahalanobis vs Euclidean to x = [3, 0]

Fill the middle

Fill in the blanks

From Mahalanobis vs Euclidean to x = [3, 0] — one line has had its right-hand side removed. Put it back.

import numpy as np
mu = np.array([1., -1.])
Sigma = np.array([[2., 0.8], [0.8, 1.]])
x = np.array([3., 0.])
diff = x - mu
d2_M = diff @ np.linalg.inv(Sigma) @ diff
d2_E = (diff**2).sum()
print(round(d2_M, 4), round(d2_E, 4))

Why: diff is what everything below it consumes, so the wrong expression here fails later and somewhere else. Row 1: 12 + (-0.8)1 = 1.2. Row 2: (-0.8)2 + 21 = 0.4.

40. Mahalanobis vs Euclidean to x = [3, 0]

Worked example

Now measure the distance from x = [3, 0] to mu = [1, -1] both ways. The miss vector is x - mu = [2, 1].

Sigma^{-1}(x - mu) = (1/1.36)[[1,-0.8],[-0.8,2]] [2,1] = (1/1.36)[1.2, 0.4]

Why: Row 1: 12 + (-0.8)1 = 1.2. Row 2: (-0.8)2 + 21 = 0.4. Then divide by 1.36 -> [0.8824, 0.2941].

d_M^2 = (x-mu)^T [that] = 20.8824 + 10.2941 = 2.0588

Why: Equivalently (21.2 + 10.4)/1.36 = 2.8/1.36 = 2.0588. Verified in code below.

\[ d_M^2 = \frac{2\cdot 1.2 + 1\cdot 0.4}{1.36} = \frac{2.8}{1.36} = 2.0588 \]

import numpy as np
mu = np.array([1., -1.])
Sigma = np.array([[2., 0.8], [0.8, 1.]])
x = np.array([3., 0.])
diff = x - mu
d2_M = diff @ np.linalg.inv(Sigma) @ diff
d2_E = (diff**2).sum()
print(round(d2_M, 4), round(d2_E, 4))
distancevaluereading
Euclidean^25.0looks far
Mahalanobis^22.0588actually typical
ratio5.0 / 2.06 = 2.4xEuclidean overstates by 2.4x

41. Inspect it line by line: Mahalanobis vs Euclidean to x = [3, 0]

Error analysis

Annotate

Walk the callouts on Mahalanobis vs Euclidean to x = [3, 0]. Each one is a place this is easy to get subtly wrong.

  • Row 1: 12 + (-0.8)1 = 1.2. Row 2: (-0.8)2 + 21 = 0.4. Then divide by 1.36 -> [0.8824, 0.2941].
  • Equivalently (21.2 + 10.4)/1.36 = 2.8/1.36 = 2.0588. Verified in code below.

42. When Sigma = I, Mahalanobis = Euclidean

Concept

Set Sigma = I (unit variance, no correlation). Then Sigma^{-1} = I and d_M^2 = (x - mu)^T (x - mu) = ||x - mu||^2 - plain squared Euclidean distance.

So Euclidean is the special case of Mahalanobis for an isotropic (spherical) Gaussian. Mahalanobis is the honest generalization once the cloud stops being a perfect ball.

43. Something is wrong here: flagging outliers with Euclidean distance

Anomaly

Predict first

A student writes this, and it looks reasonable:

To find anomalies in correlated data, rank points by raw distance from the mean, ||x - mu||.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: Ignores Sigma entirely. Our x = [3, 0] scores Euclidean^2 = 5.0 and looks like a far outlier, but along the high-variance tilt it is only 2.06 in Mahalanobis - a perfectly ordinary point.

Rank by Mahalanobis distance, which rescales by Sigma^{-1}.

Why: Ignores Sigma entirely. Our x = [3, 0] scores Euclidean^2 = 5.0 and looks like a far outlier, but along the high-variance tilt it is only 2.06 in Mahalanobis - a perfectly ordinary point. You would flag typical points and miss real ones.

44. Trap: flagging outliers with Euclidean distance

Trap

The trap

To find anomalies in correlated data, rank points by raw distance from the mean, ||x - mu||.

Flag x by Euclidean ||x - mu||

Why: Ignores Sigma entirely. Our x = [3, 0] scores Euclidean^2 = 5.0 and looks like a far outlier, but along the high-variance tilt it is only 2.06 in Mahalanobis - a perfectly ordinary point. You would flag typical points and miss real ones.

The fix

Rank by Mahalanobis distance, which rescales by Sigma^{-1}.

Flag x by d_M^2 = (x-mu)^T Sigma^{-1} (x-mu)

Why: Distance in standard-deviation units along the ellipse axes - the correct notion of 'far' for correlated data. Under a Gaussian, d_M^2 follows a chi-square(d) law, so you can even set a principled threshold (e.g. 5.991 for 95% in 2-D).

45. Break it on purpose: flagging outliers with Euclidean distance

Break the constraint

Discussion prompt

The rule this trap just fixed:

Rank by Mahalanobis distance, which rescales by Sigma^{-1}.

Now break it on purpose. Build a case that violates it and follow the consequences until something visibly fails. Where does the failure first show up — and would you have noticed it if you had not been looking?

Hint: The dangerous rules are the ones whose violation still produces an answer. If yours fails loudly, try to find one that fails quietly.

Answer:

Ignores Sigma entirely. Our x = [3, 0] scores Euclidean^2 = 5.0 and looks like a far outlier, but along the high-variance tilt it is only 2.06 in Mahalanobis - a perfectly ordinary point. You would flag typical points and miss real ones.

46. The normalizing constant

Section

Part 4 of 7 - determinant & PDF

47. Why |Sigma| appears out front

Concept

The front constant makes the total probability integrate to 1. In 1-D that job needs 1/sqrt(2 pi sigma^2); the sigma^2 accounts for how wide the bump is.

In d dimensions the 'width' is a volume, and the volume of the covariance ellipse scales with sqrt(|Sigma|) - the product of the axis lengths sqrt(lambda_1) ... sqrt(lambda_d), since |Sigma| = product of eigenvalues. That is exactly the |Sigma|^{1/2} in the denominator.

48. The peak density at x = mu

Worked example

At x = mu the exponent is 0 (distance zero), so exp(0) = 1 and the density equals the bare front constant. For d = 2:

const = 1 / ((2 pi)^{d/2} |Sigma|^{1/2}) = 1 / (2 pi * sqrt(1.36))

Why: d/2 = 1, so (2 pi)^1 = 2 pi, and |Sigma|^{1/2} = sqrt(1.36). Multiply: 2 pi * 1.1662 = 7.328.

\[ p(\mu) = \frac{1}{2\pi\,\sqrt{1.36}} = \frac{1}{7.328} = 0.1365 \]

quantityvalue (verified)
det(Sigma)1.36
sqrt(det)1.1662
(2 pi)^{d/2} = 2 pi6.2832
peak p(mu)0.1365

49. Guess the shape of the answer: Evaluate the full density at x = [3, 0]

Estimation

Predict first

Combine everything: the constant 0.1365, and the exponent using the Mahalanobis d_M^2 = 2.0588 we already computed. Runnable, from scratch:

Commit before you compute: what does Evaluate the full density at x = [3, 0] come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.

Correct: p(x) = 0.1365 * exp(-0.5 * 2.0588) = 0.1365 * 0.3573 = 0.0488

Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. exp(-1.0294) = 0.3573; times the peak 0.1365 gives 0.0488.

50. Evaluate the full density at x = [3, 0]

Worked example

Combine everything: the constant 0.1365, and the exponent using the Mahalanobis d_M^2 = 2.0588 we already computed. Runnable, from scratch:

import numpy as np
mu = np.array([1., -1.])
Sigma = np.array([[2., 0.8], [0.8, 1.]])
d = len(mu)
x = np.array([3., 0.])
det = np.linalg.det(Sigma)
dM2 = (x - mu) @ np.linalg.inv(Sigma) @ (x - mu)
const = 1.0 / ((2*np.pi)**(d/2) * np.sqrt(det))
pdf = const * np.exp(-0.5 * dM2)
print(round(det, 4), round(dM2, 4))
print(round(const, 6), round(pdf, 6))

p(x) = 0.1365 * exp(-0.5 * 2.0588) = 0.1365 * 0.3573 = 0.0488

Why: exp(-1.0294) = 0.3573; times the peak 0.1365 gives 0.0488. The point sits at about 36% of the peak height - down but not deep in the tail, consistent with its modest Mahalanobis distance.

quantityvalue (verified)
det(Sigma)1.36
d_M^2 at [3,0]2.0588
const (peak)0.136474
p([3, 0])0.048751

51. Watch it run: Evaluate the full density at x = [3, 0]

Pattern

Step through it

Step through Evaluate the full density at x = [3, 0] one row at a time. What is driving the change, and what would the row after the last one be?

  1. Step 1: quantity is det(Sigma)
  2. Step 2: quantity is d_M^2 at [3,0]
  3. Step 3: quantity is const (peak)
  4. Step 4: quantity is p([3, 0])

52. Restore the missing line: Cross-check against scipy

Fill the middle

Fill in the blanks

From Cross-check against scipy — one line has had its right-hand side removed. Put it back.

import numpy as np
from scipy.stats import multivariate_normal
mu = np.array([1., -1.])
Sigma = np.array([[2., 0.8], [0.8, 1.]])
rv = multivariate_normal(mean=mu, cov=Sigma)
print(round(rv.pdf([3., 0.]), 6)) # our hand value
print(round(rv.pdf(mu), 6)) # peak, at the center

Why: Sigma is what everything below it consumes, so the wrong expression here fails later and somewhere else. Our term-by-term density is correct to 6 decimals against a battle-tested library.

53. Cross-check against scipy

Worked example

Never trust a hand PDF without an independent oracle. scipy.stats.multivariate_normal computes the same density - it must match our 0.048751.

import numpy as np
from scipy.stats import multivariate_normal
mu = np.array([1., -1.])
Sigma = np.array([[2., 0.8], [0.8, 1.]])
rv = multivariate_normal(mean=mu, cov=Sigma)
print(round(rv.pdf([3., 0.]), 6))   # our hand value
print(round(rv.pdf(mu), 6))         # peak, at the center

scipy prints 0.048751 and 0.136474 - exact match

Why: Our term-by-term density is correct to 6 decimals against a battle-tested library. The peak 0.136474 equals the front constant, confirming exp(0) = 1 at the center.

pointour formulascipy
x = [3, 0]0.0487510.048751
x = mu0.1364740.136474

54. Marginals, conditionals, independence

Section

Part 5 of 7 - closure properties

55. Gaussians are closed under slicing

Concept

A defining superpower of the Gaussian: if x is jointly Gaussian, then every marginal (drop some coordinates) is Gaussian, and every conditional (fix some coordinates) is Gaussian. You never leave the family.

This is why Gaussians dominate ML: Kalman filters, Gaussian processes, and VAEs all rely on the fact that slicing and conditioning keep you Gaussian, so the math stays closed-form.

56. Complete the line: The marginal of x_1: just read off Sigma

Fill the middle

Fill in the blanks

From The marginal of x_1: just read off Sigma — finish the line. Write what belongs on the right of the equals sign before you look.

x_1 \sim \mathcal\mathcal{N}(1,\ 2)(\mu_1,\ \Sigma____) = ___

Why: Producing the right-hand side unprompted is the difference between recognising this line and being able to use it. No conditioning, no correlation correction - the covariance term 0.8 does not enter a marginal.

57. The marginal of x_1: just read off Sigma

Worked example

The marginal of a single coordinate is astonishingly simple - you ignore the other coordinates entirely and read the corresponding mean and variance straight out of mu and Sigma:

\[ x_1 \sim \mathcal{N}(\mu_1,\ \Sigma_{11}) = \mathcal{N}(1,\ 2) \]

Marginal mean = mu_1 = 1, marginal variance = Sigma_11 = 2

Why: No conditioning, no correlation correction - the covariance term 0.8 does not enter a marginal. Confirm on the sample: x_1 has mean 1.0018, variance 2.0067.

quantitytheoryfrom 200k samples
mean of x_111.0018
var of x_122.0067

58. Conditioning is different: correlation kicks in

Intuition

The marginal ignored x_2. But the conditional x_2 | x_1 = 3 asks: given we observed a specific x_1, what do we now believe about x_2? Because the two are correlated (Sigma_12 = 0.8 > 0), learning x_1 is high pulls our estimate of x_2 up too.

Two things change versus the marginal: the mean shifts toward the correlated evidence, and the variance shrinks because we now know more. Both effects are captured by the conditioning formulas next.

59. The bivariate conditioning formulas

Worked example

For a bivariate Gaussian, conditioning x_2 on x_1 = v gives another Gaussian with a corrected mean and a reduced variance:

\[ \mathbb{E}[x_2 \mid x_1 = v] = \mu_2 + \frac{\Sigma_{21}}{\Sigma_{11}}\,(v - \mu_1) \]

\[ \operatorname{Var}[x_2 \mid x_1 = v] = \Sigma_{22} - \frac{\Sigma_{21}\,\Sigma_{12}}{\Sigma_{11}} \]

The mean gains a slope term; the variance loses a correlation term

Why: Sigma_21 / Sigma_11 is the regression slope of x_2 on x_1. The variance drop Sigma_21 Sigma_12 / Sigma_11 is the part of x_2's uncertainty explained by x_1 - it is never negative, so conditioning never increases variance.

60. Say it in words: The bivariate conditioning formulas

Translation

\( \mathbb{E}[x_2 \mid x_1 = v] = \mu_2 + \frac{\Sigma_{21}}{\Sigma_{11}}\,(v - \mu_1) \)

Draw it

Translate both ways. First write the expression above as a sentence with no symbols in it at all. Then cover it, and write your sentence back as notation. If the two versions disagree, the disagreement is the thing to fix.

61. Predict the next row: Condition x_2 on x_1 = 3, by hand then by sample

Pattern

Predict first

The table runs: conditional mean | -0.2 | -0.177 · conditional var | 0.68 | 0.684

In Condition x_2 on x_1 = 3, by hand then by sample, given the rows so far: what is the next one — the row where quantity is n in slice?

Correct: n in slice | - | 1934 samples

quantityby handempirical (x_1 approx 3)
conditional mean-0.2-0.177
conditional var0.680.684
n in slice-1934 samples

Why: The relationship between the columns, not the individual numbers, is what generates the next row. Slope 0.8/2 = 0.4; the observed x_1 = 3 is 2 above its mean 1, so x_2's estimate rises by 0.4*2 = 0.8, from -1 up to -0.2.

62. Condition x_2 on x_1 = 3, by hand then by sample

Worked example

Mean: -1 + (0.8/2)(3 - 1) = -1 + 0.42 = -0.2

Why: Slope 0.8/2 = 0.4; the observed x_1 = 3 is 2 above its mean 1, so x_2's estimate rises by 0.4*2 = 0.8, from -1 up to -0.2.

\[ \mathbb{E}[x_2 \mid x_1 = 3] = -1 + 0.4\,(3-1) = -0.2 \]

Variance: 1 - (0.8*0.8)/2 = 1 - 0.32 = 0.68

Why: Knowing x_1 removes 0.32 of x_2's original variance of 1, leaving 0.68. Correlation buys certainty.

\[ \operatorname{Var}[x_2 \mid x_1 = 3] = 1 - \frac{0.64}{2} = 0.68 \]

import numpy as np
mu = np.array([1., -1.])
Sigma = np.array([[2., 0.8], [0.8, 1.]])
x1 = 3.0
cond_mean = mu[1] + Sigma[1,0]*(1/Sigma[0,0])*(x1 - mu[0])
cond_var  = Sigma[1,1] - Sigma[1,0]*(1/Sigma[0,0])*Sigma[0,1]
print(round(cond_mean, 4), round(cond_var, 4))
L = np.linalg.cholesky(Sigma)
X = mu + np.random.default_rng(0).normal(size=(200000, 2)) @ L.T
m = np.abs(X[:, 0] - 3.0) < 0.05
print(round(X[m, 1].mean(), 3), round(X[m, 1].var(), 3))
quantityby handempirical (x_1 approx 3)
conditional mean-0.2-0.177
conditional var0.680.684
n in slice-1934 samples

63. Fill in: by hand for Condition x_2 on x_1 = 3, by hand then by…

Comparison

Comparison matrix

From Condition x_2 on x_1 = 3, by hand then by sample: refill the by hand column from what you know. The rest of the table is as it appeared.

quantityby handempirical (x_1 approx 3)
conditional mean-0.2-0.177
conditional var0.680.684
n in slice-1934 samples

64. Diagonal Sigma means independent components

Concept

Special fact for Gaussians: if Sigma is diagonal (all off-diagonal covariances zero), the components are independent - not merely uncorrelated. For a Gaussian, zero covariance really is full independence.

The engine is factorization: when Sigma is diagonal, the joint density splits into a product of 1-D Gaussians, one per coordinate - and a density that factorizes is the definition of independence. We prove it next.

65. Plan first: Prove diagonal Sigma factorizes the density

Step zero

Discussion prompt

Prove diagonal Sigma factorizes the density — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.

Hint: It starts with: Take Sigma = diag(sigma_1^2, sigma_2^2); then Sigma^{-1} =…

Answer:

  1. Take Sigma = diag(sigma_1^2, sigma_2^2); then Sigma^{-1} = diag(1/sigma_1^2, 1/sigma_2^2)
  2. The quadratic form becomes a plain sum, no cross term
  3. exp of a sum = product of exps, and the constant splits too

66. Prove diagonal Sigma factorizes the density

Worked example

Take Sigma = diag(sigma_1^2, sigma_2^2); then Sigma^{-1} = diag(1/sigma_1^2, 1/sigma_2^2)

Why: Inverting a diagonal matrix just inverts each diagonal entry. |Sigma| = sigma_1^2 sigma_2^2.

The quadratic form becomes a plain sum, no cross term

Why: With Sigma^{-1} diagonal, (x-mu)^T Sigma^{-1} (x-mu) = (x_1-mu_1)^2/sigma_1^2 + (x_2-mu_2)^2/sigma_2^2. No off-diagonal, so no x_1 x_2 coupling survives.

\[ (x-\mu)^\top \Sigma^{-1}(x-\mu) = \frac{(x_1-\mu_1)^2}{\sigma_1^2} + \frac{(x_2-\mu_2)^2}{\sigma_2^2} \]

exp of a sum = product of exps, and the constant splits too

Why: |Sigma|^{1/2} = sigma_1 sigma_2 and (2 pi)^{d/2} = (2 pi)^1 = sqrt(2 pi)^2, so the whole density separates into the two 1-D densities.

\[ p(x) = \underbrace{\frac{1}{\sqrt{2\pi}\,\sigma_1}e^{-\frac{(x_1-\mu_1)^2}{2\sigma_1^2}}}_{p(x_1)} \cdot \underbrace{\frac{1}{\sqrt{2\pi}\,\sigma_2}e^{-\frac{(x_2-\mu_2)^2}{2\sigma_2^2}}}_{p(x_2)} \]

import numpy as np
from scipy.stats import multivariate_normal, norm
Sd = np.array([[2., 0.], [0., 1.]])       # diagonal covariance
joint = multivariate_normal(mean=[0., 0.], cov=Sd).pdf([1.5, -0.5])
prod = norm.pdf(1.5, 0, np.sqrt(2.)) * norm.pdf(-0.5, 0, 1.)
print(round(joint, 8))
print(round(prod, 8))
print(np.isclose(joint, prod))
quantityvalue (verified)
joint p(1.5, -0.5)0.05658843
p(x_1) * p(x_2)0.05658843
equal?True -> independent

67. Inspect it line by line: Prove diagonal Sigma factorizes the density

Error analysis

Annotate

Walk the callouts on Prove diagonal Sigma factorizes the density. Each one is a place this is easy to get subtly wrong.

  • Inverting a diagonal matrix just inverts each diagonal entry. |Sigma| = sigma_1^2 sigma_2^2.
  • With Sigma^{-1} diagonal, (x-mu)^T Sigma^{-1} (x-mu) = (x_1-mu_1)^2/sigma_1^2 + (x_2-mu_2)^2/sigma_2^2. No off-diagonal, so no x_1 x_2 coupling survives.
  • |Sigma|^{1/2} = sigma_1 sigma_2 and (2 pi)^{d/2} = (2 pi)^1 = sqrt(2 pi)^2, so the whole density separates into the two 1-D densities.

68. Something is wrong here: uncorrelated always means independent

Anomaly

Predict first

A student writes this, and it looks reasonable:

Zero covariance means independent, for any distribution - so Cov(X, Y) = 0 always gives X independent of Y.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: Cov(X, X^2) = E[X^3] - E[X]E[X^2] = 0 - 0 = 0 (odd moments of a symmetric X vanish).

Uncorrelated implies independent only for jointly Gaussian vectors. That implication is special to the Gaussian.

Why: Cov(X, X^2) = E[X^3] - E[X]E[X^2] = 0 - 0 = 0 (odd moments of a symmetric X vanish). Verified: sampled Cov = 0.0006. Yet Y is a DETERMINISTIC function of X - knowing X fixes Y exactly. Total dependence, zero covariance.

69. Trap: uncorrelated always means independent

Trap

The trap

Zero covariance means independent, for any distribution - so Cov(X, Y) = 0 always gives X independent of Y.

Take Y = X^2 with X ~ N(0, 1) and claim independence from Cov = 0

Why: Cov(X, X^2) = E[X^3] - E[X]E[X^2] = 0 - 0 = 0 (odd moments of a symmetric X vanish). Verified: sampled Cov = 0.0006. Yet Y is a DETERMINISTIC function of X - knowing X fixes Y exactly. Total dependence, zero covariance.

The fix

Uncorrelated implies independent only for jointly Gaussian vectors. That implication is special to the Gaussian.

Diagonal Sigma => independent, but ONLY inside the Gaussian family

Why: The density factorizes (previous slide) precisely because the pair is jointly Gaussian. Outside that family, zero correlation captures only linear dependence - X and X^2 are the classic warning.

70. Sampling by Cholesky

Section

Part 6 of 7 - the reparameterization

71. How do you draw from N(mu, Sigma)?

Intuition

Your computer only knows how to draw standard normals z ~ N(0, I) - an isotropic round cloud centered at the origin, every direction identical.

We want a tilted, stretched, shifted ellipse. So we need a transform that takes the round cloud z and turns it into our Sigma-shaped cloud around mu. A single linear map does it: stretch/rotate by a matrix, then shift by mu.

72. How covariance transforms under a linear map

Concept

The one rule we need: if z has covariance C and we form x = mu + A z, then x has covariance A C A^T. Shifting by mu moves the center but never changes the spread.

\[ \operatorname{Cov}(\mu + A z) = A\,\operatorname{Cov}(z)\,A^\top \]

With z ~ N(0, I) we have Cov(z) = I, so Cov(mu + A z) = A A^T. To hit target Sigma, we just need a matrix A with A A^T = Sigma - a square root of Sigma.

73. Cholesky gives that square root

Concept

The Cholesky factorization (Lesson 13) of a PD matrix produces a lower-triangular L with Sigma = L L^T. That L is exactly the A we need.

\[ \Sigma = L L^\top \;\Longrightarrow\; x = \mu + L z,\quad z \sim \mathcal{N}(0, I) \]

And Gaussians stay Gaussian under linear maps, so x is not just any distribution with covariance Sigma - it is precisely N(mu, Sigma). Triangular L also makes the transform cheap and numerically clean.

74. Teach it back: Cholesky gives that square root

Explain it

Discussion prompt

Explain Cholesky gives that square root to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

The Cholesky factorization (Lesson 13) of a PD matrix produces a lower-triangular L with Sigma = L L^T. That L is exactly the A we need.

75. What has to happen first: Prove Cov(mu + Lz) = Sigma, one line at a time

Ranking

Put in order

Put the moves of Prove Cov(mu + Lz) = Sigma, one line at a time into the order they have to happen.

  1. Start: x = mu + L z, with Cov(z) = I
  2. Center x: x - mu = L z (the mu cancels)
  3. Cov(x) = E[(x-mu)(x-mu)^T] = E[L z (L z)^T]
  4. Pull the constants L and L^T outside the expectation
  5. L L^T = Sigma by the Cholesky definition. Done.

Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. z is standard normal, so E[z] = 0 and E[z z^T] = I.

76. Prove Cov(mu + Lz) = Sigma, one line at a time

Worked example

Start: x = mu + L z, with Cov(z) = I

Why: z is standard normal, so E[z] = 0 and E[z z^T] = I. This is the only fact we need about z.

Center x: x - mu = L z (the mu cancels)

Why: E[x] = mu + L E[z] = mu, so the centered vector is exactly L z. Covariance only sees the centered vector.

\[ x - \mu = L z \]

Cov(x) = E[(x-mu)(x-mu)^T] = E[L z (L z)^T]

Why: Definition of covariance, substituting x - mu = L z. And (L z)^T = z^T L^T.

\[ \operatorname{Cov}(x) = \mathbb{E}[\,L z\, z^\top L^\top\,] \]

Pull the constants L and L^T outside the expectation

Why: L is not random, so E[L M L^T] = L E[M] L^T. The middle expectation E[z z^T] is I.

\[ = L\,\mathbb{E}[z z^\top]\,L^\top = L\,I\,L^\top = L L^\top \]

L L^T = Sigma by the Cholesky definition. Done.

Why: So Cov(mu + L z) = Sigma exactly - the construction is not an approximation, it is an identity. Any square root of Sigma would work; Cholesky is the cheap triangular choice.

\[ \operatorname{Cov}(x) = L L^\top = \Sigma \]

77. Draw the shape of it: Prove Cov(mu + Lz) = Sigma, one line at a…

Blank canvas

Draw it

Draw what Prove Cov(mu + Lz) = Sigma, one line at a time just did — the shape of it, not the line-by-line working. One picture, labels only where you need them. Then check it against the steps: anything you could not draw is a step you followed rather than understood.

78. What has to happen first: Compute L by hand

Ranking

Put in order

Put the moves of Compute L by hand into the order they have to happen.

  1. L11 = sqrt(2) = 1.4142
  2. L21 = 0.8 / sqrt(2) = 0.5657
  3. L22 = sqrt(1 - 0.5657^2) = sqrt(1 - 0.32) = 0.8246

Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. Top-left: L11^2 = Sigma_11 = 2, so L11 = sqrt(2).

79. Compute L by hand

Worked example

Cholesky of a 2x2 [[a, b], [b, c]] gives lower-triangular L = [[L11, 0], [L21, L22]] solved top-down: L11 = sqrt(a), L21 = b/L11, L22 = sqrt(c - L21^2).

L11 = sqrt(2) = 1.4142

Why: Top-left: L11^2 = Sigma_11 = 2, so L11 = sqrt(2).

L21 = 0.8 / sqrt(2) = 0.5657

Why: Off-diagonal: L21 * L11 = Sigma_21 = 0.8, so L21 = 0.8/1.4142.

L22 = sqrt(1 - 0.5657^2) = sqrt(1 - 0.32) = 0.8246

Why: Bottom-right: L21^2 + L22^2 = Sigma_22 = 1, so L22 = sqrt(1 - 0.32) = sqrt(0.68).

\[ L = \begin{bmatrix} 1.4142 & 0 \\ 0.5657 & 0.8246 \end{bmatrix} \]

import numpy as np
Sigma = np.array([[2., 0.8], [0.8, 1.]])
L = np.linalg.cholesky(Sigma)     # lower-triangular
print(L.round(4))
print(np.allclose(L @ L.T, Sigma))
entryhand valuenp value
L111.41421.4142
L210.56570.5657
L220.82460.8246
L L^T == Sigma-True

80. Decode the notation: Compute L by hand

Notation

Annotate

From Compute L by hand — read this one piece at a time. What is each part doing?

On: \( L = \begin{bmatrix} 1.4142 & 0 \\ 0.5657 & 0.8246 \end{bmatrix} \)

  • Top-left: L11^2 = Sigma_11 = 2, so L11 = sqrt(2).
  • Off-diagonal: L21 * L11 = Sigma_21 = 0.8, so L21 = 0.8/1.4142.
  • Bottom-right: L21^2 + L22^2 = Sigma_22 = 1, so L22 = sqrt(1 - 0.32) = sqrt(0.68).

81. What has to be given first: Sample 200k points and recover Sigma

Missing information

Discussion prompt

Draw standard normals, transform with mu + z @ L.T (row-wise: each row is mu + L z), then check the empirical mean and covariance match. Seed 0 for reproducibility:

What do you need to know — or decide — before the first line can be written? List everything the problem has to hand you.

Hint: Anything you would have to invent to get started is a thing the problem must supply.

Answer:

Both recovered to ~2 decimals - the Cholesky transform reproduces mu AND Sigma, including the 0.8 correlation. With 200k draws the Monte-Carlo error is small; more samples tighten it further.

82. Sample 200k points and recover Sigma

Worked example

Draw standard normals, transform with mu + z @ L.T (row-wise: each row is mu + L z), then check the empirical mean and covariance match. Seed 0 for reproducibility:

import numpy as np
mu = np.array([1., -1.])
Sigma = np.array([[2., 0.8], [0.8, 1.]])
L = np.linalg.cholesky(Sigma)
rng = np.random.default_rng(0)
z = rng.normal(size=(200000, 2))     # standard normals, N(0, I)
X = mu + z @ L.T                     # x = mu + L z, row-wise
print(X.mean(0).round(3))
print(np.cov(X.T).round(3))

sample mean [1.002, -1.0]; sample cov [[2.007, 0.798], [0.798, 0.999]]

Why: Both recovered to ~2 decimals - the Cholesky transform reproduces mu AND Sigma, including the 0.8 correlation. With 200k draws the Monte-Carlo error is small; more samples tighten it further.

quantitytargetfrom 200k samples
mean[1, -1][1.002, -1.000]
Sigma_11 (var x_1)2.02.007
Sigma_12 (cov)0.80.798
Sigma_22 (var x_2)1.00.999

83. Something is wrong here: transforming by Sigma instead of its square root

Anomaly

Predict first

A student writes this, and it looks reasonable:

To get covariance Sigma, scale the standard normals by Sigma directly: x = mu + Sigma z.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: Then Cov(x) = Sigma I Sigma^T = Sigma^2, NOT Sigma.

Scale by the square root L with L L^T = Sigma, not by Sigma itself.

Why: Then Cov(x) = Sigma I Sigma^T = Sigma^2, NOT Sigma. You have squared the covariance. For our Sigma, Sigma^2 = [[4.64, 2.4], [2.4, 1.64]] - the wrong spread and wrong correlation entirely.

84. Trap: transforming by Sigma instead of its square root

Trap

The trap

To get covariance Sigma, scale the standard normals by Sigma directly: x = mu + Sigma z.

x = mu + Sigma z

Why: Then Cov(x) = Sigma I Sigma^T = Sigma^2, NOT Sigma. You have squared the covariance. For our Sigma, Sigma^2 = [[4.64, 2.4], [2.4, 1.64]] - the wrong spread and wrong correlation entirely.

The fix

Scale by the square root L with L L^T = Sigma, not by Sigma itself.

x = mu + L z, L = cholesky(Sigma)

Why: Cov(x) = L I L^T = L L^T = Sigma - exactly right, as we proved. The rule of thumb: to hit covariance Sigma you always need a matrix square root of Sigma, and Cholesky is the standard one.

85. Which of these survive contact with Lesson 23: The Multivariate Gaussian?

Two truths and a lie

Sort into buckets

Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.

Holds up
You already know the scalar bell curve: a random score x with mean mu and variance sigma^2 has density; In 1-D the exponent divides by sigma^2 - it rescales the squared miss into variance units. A miss of 2 is a big deal if sigma^2 = 0.1 and trivial if sigma^2 = 100.; Bigger c = further from center = lower density. So the density is a smooth hill over the plane whose contour lines are nested ellipses, tallest at mu.
Breaks
It is symmetric and has ones on the diagonal, so [[1, 2], [2, 1]] is a fine covariance matrix.; To find anomalies in correlated data, rank points by raw distance from the mean, ||x - mu||.
sound
These are stated as this lesson states them — each one survives the edge cases Lesson 23: The Multivariate Gaussian puts it through.
flawed
Each of these is lifted from a trap in this deck: reasonable-sounding, and wrong in a way that only shows up once you rely on it.

86. The VAE connection

Section

Part 7 of 7 - why this matters

87. The VAE's Gaussian latent

Concept

A variational autoencoder encodes each input into a Gaussian latent distribution: the encoder outputs a mean mu and a (usually diagonal) covariance Sigma per input, then the decoder reconstructs from a sample z of that Gaussian.

But sampling is random, and you cannot backpropagate through a raw random draw - the gradient has nowhere to flow. That is exactly the problem our Cholesky sampler solves.

88. The reparameterization trick

Concept

Rewrite the draw as z = mu + L * eps with eps ~ N(0, I). Now the ONLY random node is eps; mu and L are deterministic outputs of the network.

\[ z = \mu + L\,\epsilon, \qquad \epsilon \sim \mathcal{N}(0, I) \]

Gradients flow straight through mu and L because they sit on a plain deterministic path - the randomness is pushed into the fixed eps. This is the identical x = mu + L z you just proved, wearing a training hat. For diagonal Sigma, L is just the per-dimension standard deviations.

89. By analogy: The reparameterization trick

Analogy

Discussion prompt

Explain The reparameterization trick by analogy to something with no Machine Learning in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

Rewrite the draw as z = mu + L * eps with eps ~ N(0, I). Now the ONLY random node is eps; mu and L are deterministic outputs of the network.

90. Guess the shape of the answer: Diagonal-Sigma reparameterization (the VAE…

Estimation

Predict first

In a real VAE Sigma is diagonal, so L = diag(sigma) and the transform is an elementwise mu + sigma * eps - no matrix multiply. Demonstrate that it lands the right mean and per-dim std:

Commit before you compute: what does Diagonal-Sigma reparameterization (the VAE case) come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.

Correct: sample mean [0.502, -2.0]; sample std [1.001, 0.3]

Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. Elementwise mu + sigma * eps reproduces the target mean and standard deviations exactly - and because eps is the sole random node, autograd differentiates cleanly through mu and sigma.

91. Diagonal-Sigma reparameterization (the VAE case)

Worked example

In a real VAE Sigma is diagonal, so L = diag(sigma) and the transform is an elementwise mu + sigma * eps - no matrix multiply. Demonstrate that it lands the right mean and per-dim std:

import numpy as np
mu = np.array([0.5, -2.0])          # encoder output: latent mean
sigma = np.array([1.0, 0.3])        # encoder output: per-dim std (diagonal L)
rng = np.random.default_rng(0)
eps = rng.normal(size=(500000, 2))  # noise, the ONLY random node
z = mu + sigma * eps                # reparameterization: gradients flow to mu, sigma
print(z.mean(0).round(3))
print(z.std(0).round(3))

sample mean [0.502, -2.0]; sample std [1.001, 0.3]

Why: Elementwise mu + sigma * eps reproduces the target mean and standard deviations exactly - and because eps is the sole random node, autograd differentiates cleanly through mu and sigma. This is the whole sampler inside a VAE forward pass.

quantitytargetfrom samples
mean[0.5, -2.0][0.502, -2.000]
std dim 01.01.001
std dim 10.30.300

92. Work backwards from the answer: Diagonal-Sigma reparameterization (the VAE…

Reverse engineer

Discussion prompt

Work backwards. The example finished here:

sample mean [0.502, -2.0]; sample std [1.001, 0.3]

What was it asked to do, and what must it have been given? Reconstruct the problem from its answer.

Hint: Every quantity in the result had to enter somewhere. Account for each one.

Answer:

In a real VAE Sigma is diagonal, so L = diag(sigma) and the transform is an elementwise mu + sigma * eps - no matrix multiply. Demonstrate that it lands the right mean and per-dim std:

93. Without one step: The multivariate-Gaussian toolkit

Constraint

Discussion prompt

Run The multivariate-Gaussian toolkit with this step confiscated:

Distance: Mahalanobis (x-mu)^T Sigma^{-1} (x-mu), not Euclidean - it rescales by the spread

Is it still possible? If it is, say what takes its place and what it costs you. If it is not, say exactly what that step was providing that nothing else does.

Hint: A step you can drop for free was never load-bearing. If you cannot drop it, name the thing that goes wrong the moment it is gone.

Answer:

  1. Density: N(mu, Sigma) = front constant 1/((2 pi)^{d/2} |Sigma|^{1/2}) times exp(-1/2 * quadratic form)
  2. Constraint: Sigma symmetric PSD (PD to invert), because a^T Sigma a = Var(a^T x) >= 0
  3. Geometry: contours are ellipses; eigenvectors of Sigma = axes, sqrt(eigenvalues) = 1-sigma semi-axes
  4. Distance: Mahalanobis (x-mu)^T Sigma^{-1} (x-mu), not Euclidean - it rescales by the spread
  5. Closure: marginals read off Sigma directly; conditionals shift the mean and shrink the variance; diagonal Sigma => independent (Gaussian only)
  6. Sample: x = mu + L z, L = chol(Sigma) - proven Cov = L L^T = Sigma; the VAE reparameterization trick

94. The multivariate-Gaussian toolkit

Pattern

  1. Density: N(mu, Sigma) = front constant 1/((2 pi)^{d/2} |Sigma|^{1/2}) times exp(-1/2 * quadratic form)
  2. Constraint: Sigma symmetric PSD (PD to invert), because a^T Sigma a = Var(a^T x) >= 0
  3. Geometry: contours are ellipses; eigenvectors of Sigma = axes, sqrt(eigenvalues) = 1-sigma semi-axes
  4. Distance: Mahalanobis (x-mu)^T Sigma^{-1} (x-mu), not Euclidean - it rescales by the spread
  5. Closure: marginals read off Sigma directly; conditionals shift the mean and shrink the variance; diagonal Sigma => independent (Gaussian only)
  6. Sample: x = mu + L z, L = chol(Sigma) - proven Cov = L L^T = Sigma; the VAE reparameterization trick

95. Where does it stop working: The multivariate-Gaussian toolkit

Edge cases

Discussion prompt

The multivariate-Gaussian toolkit works on the cases you have just seen. Push it to the edge: what is the most degenerate input it still handles — empty, zero, one item, everything equal — and what is the first case where it stops being true? Name the case, not just "it breaks".

Hint: Try the smallest legal input, then the largest, then the one where two things collide. Methods are specified at their edges; the middle takes care of itself.

Answer:

  1. Density: N(mu, Sigma) = front constant 1/((2 pi)^{d/2} |Sigma|^{1/2}) times exp(-1/2 * quadratic form)
  2. Constraint: Sigma symmetric PSD (PD to invert), because a^T Sigma a = Var(a^T x) >= 0
  3. Geometry: contours are ellipses; eigenvectors of Sigma = axes, sqrt(eigenvalues) = 1-sigma semi-axes
  4. Distance: Mahalanobis (x-mu)^T Sigma^{-1} (x-mu), not Euclidean - it rescales by the spread
  5. Closure: marginals read off Sigma directly; conditionals shift the mean and shrink the variance; diagonal Sigma => independent (Gaussian only)
  6. Sample: x = mu + L z, L = chol(Sigma) - proven Cov = L L^T = Sigma; the VAE reparameterization trick

96. Rule out three: Check yourself - the covariance constraint

Elimination

Eliminate the wrong options

For N(mu, Sigma) with the standard density formula (using Sigma^{-1} and |Sigma|^{1/2}) to be well-defined and non-degenerate, Sigma must be:

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. symmetric positive definite (all eigenvalues > 0)
  • B. any symmetric matrix
  • C. diagonal
  • D. orthogonal

Survives elimination: A

Why: Sigma is a covariance so it is symmetric PSD (a^T Sigma a = Var(a^T x) >= 0). The formula uses Sigma^{-1} and |Sigma|^{1/2}, which need every eigenvalue strictly > 0 - positive DEFINITE - or the inverse and the square root of the determinant fail.

97. Check yourself - the covariance constraint

Check

What must Sigma be for the standard density to make sense?

Check your understanding

For N(mu, Sigma) with the standard density formula (using Sigma^{-1} and |Sigma|^{1/2}) to be well-defined and non-degenerate, Sigma must be:

  • A. symmetric positive definite (all eigenvalues > 0) (correct)
  • B. any symmetric matrix
  • C. diagonal
  • D. orthogonal

Answer: A

Why: Sigma is a covariance so it is symmetric PSD (a^T Sigma a = Var(a^T x) >= 0). The formula uses Sigma^{-1} and |Sigma|^{1/2}, which need every eigenvalue strictly > 0 - positive DEFINITE - or the inverse and the square root of the determinant fail.

Why B tempts people
A symmetric matrix with a negative eigenvalue (like [[1,2],[2,1]], eigenvalues -1 and 3) gives a^T Sigma a < 0 for some a - a negative variance, which no covariance can have. Symmetry alone is not enough.
Why C tempts people
Diagonal Sigma is allowed but far from required - our [[2,0.8],[0.8,1]] is a perfectly valid full covariance. Requiring diagonal would forbid all correlated Gaussians.
Why D tempts people
Orthogonal matrices satisfy Q^T Q = I and are generally not PD (they can have negative or complex eigenvalues). That property is unrelated to being a covariance.

98. Answer it before you see the options: Check yourself - Mahalanobis distance

Prediction

Predict first

For a correlated Gaussian, Mahalanobis distance is preferred over Euclidean because it:

Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.

Correct: measures distance in standard-deviation units along the covariance axes

Why: Sigma^{-1} rescales each direction by its variance and un-tilts the correlation, so Mahalanobis reports how many standard deviations away a point is along the ellipse axes - the correct notion of 'far' for correlated data. Points on the same contour ellipse are equidistant.

99. Check yourself - Mahalanobis distance

Check

Why rescale by Sigma^{-1} instead of using raw distance?

Check your understanding

For a correlated Gaussian, Mahalanobis distance is preferred over Euclidean because it:

  • A. measures distance in standard-deviation units along the covariance axes (correct)
  • B. is always smaller than the Euclidean distance
  • C. ignores the mean mu
  • D. only works when Sigma is diagonal

Answer: A

Why: Sigma^{-1} rescales each direction by its variance and un-tilts the correlation, so Mahalanobis reports how many standard deviations away a point is along the ellipse axes - the correct notion of 'far' for correlated data. Points on the same contour ellipse are equidistant.

Why B tempts people
It can be larger OR smaller than Euclidean: smaller along high-variance axes, larger along low-variance ones. For x=[3,0] it was 2.06 vs Euclidean 5.0 (smaller here), but the reverse happens across a short axis.
Why C tempts people
It centers on mu through the term (x - mu); it very much uses the mean. Dropping mu would measure distance from the origin, not the distribution's center.
Why D tempts people
It works for ANY PD Sigma - the off-diagonal correlation terms are exactly what Sigma^{-1} corrects for. Restricting to diagonal Sigma would defeat its whole purpose.

100. Answer it before you see the options: Check yourself - uncorrelated vs…

Prediction

Predict first

Let X ~ N(0, 1) and Y = X^2. Which statement is correct?

Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.

Correct: Cov(X, Y) = 0, but X and Y are NOT independent

Why: Cov(X, X^2) = E[X^3] - E[X]E[X^2] = 0 - 0 = 0 because odd moments of a symmetric X vanish (verified: sampled 0.0006). Yet Y is a deterministic function of X, so they are totally dependent. Zero covariance captures only LINEAR dependence.

101. Check yourself - uncorrelated vs independent

Check

Does zero covariance always mean independence?

Check your understanding

Let X ~ N(0, 1) and Y = X^2. Which statement is correct?

  • A. Cov(X, Y) = 0, but X and Y are NOT independent (correct)
  • B. Cov(X, Y) != 0, and X and Y are dependent
  • C. Cov(X, Y) = 0, so X and Y are independent
  • D. X and Y are jointly Gaussian

Answer: A

Why: Cov(X, X^2) = E[X^3] - E[X]E[X^2] = 0 - 0 = 0 because odd moments of a symmetric X vanish (verified: sampled 0.0006). Yet Y is a deterministic function of X, so they are totally dependent. Zero covariance captures only LINEAR dependence.

Why B tempts people
The covariance is exactly 0, not nonzero - E[X^3] = 0 for a symmetric distribution. The dependence is real but purely nonlinear, so covariance misses it entirely.
Why C tempts people
This is the classic error. Zero covariance implies independence ONLY for jointly Gaussian variables. (X, X^2) is not jointly Gaussian, so the implication fails - they are dependent.
Why D tempts people
(X, X^2) is not jointly Gaussian: Y = X^2 is nonnegative and its distribution is chi-square-like, not Gaussian. This is precisely why the uncorrelated => independent shortcut does not apply.

102. Rule out three: Check yourself - Cholesky sampling

Elimination

Eliminate the wrong options

To sample x ~ N(mu, Sigma) from standard normals z ~ N(0, I), you compute:

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. x = mu + L z, where Sigma = L L^T (Cholesky)
  • B. x = mu + Sigma z
  • C. x = mu + Sigma^{-1} z
  • D. x = mu z + Sigma

Survives elimination: A

Why: With Sigma = L L^T, Cov(mu + L z) = L Cov(z) L^T = L I L^T = L L^T = Sigma. The Cholesky factor L is a square root of Sigma that reshapes the isotropic z into the correct correlated ellipse, then mu shifts it into place.

103. Check yourself - Cholesky sampling

Check

How do you turn standard normals into correlated ones?

Check your understanding

To sample x ~ N(mu, Sigma) from standard normals z ~ N(0, I), you compute:

  • A. x = mu + L z, where Sigma = L L^T (Cholesky) (correct)
  • B. x = mu + Sigma z
  • C. x = mu + Sigma^{-1} z
  • D. x = mu z + Sigma

Answer: A

Why: With Sigma = L L^T, Cov(mu + L z) = L Cov(z) L^T = L I L^T = L L^T = Sigma. The Cholesky factor L is a square root of Sigma that reshapes the isotropic z into the correct correlated ellipse, then mu shifts it into place.

Why B tempts people
Scaling by Sigma directly gives Cov = Sigma I Sigma^T = Sigma^2, the SQUARE of the target. You need a matrix square root of Sigma (namely L), not Sigma itself.
Why C tempts people
Sigma^{-1} shrinks the high-variance directions instead of stretching them, producing Cov = Sigma^{-2} - the inverse-squared of what you want. Exactly backwards.
Why D tempts people
Multiplying mu by the random z and adding the matrix Sigma is dimensionally and statistically meaningless - it neither centers at mu nor produces covariance Sigma.

104. Your turn: sample a Gaussian

Section

Project

105. Project: Cholesky sampler from scratch

Concept

Build a function that samples N(mu, Sigma) by Cholesky and prove it recovers the mean and covariance you asked for. You have derived every piece - now assemble it.

#requirementtool
1L = Cholesky of Sigma; check L L^T = Sigmanp.linalg.cholesky
2x = mu + z @ L.T for z ~ N(0, I)rng.normal
3Verify sample mean and covariance matchnp.cov

Build rules: type every line yourself, draw enough samples (>= 10^5) for the covariance to converge, and remember np.cov expects variables in rows (so transpose with X.T).

106. Break it if you can: Project: Cholesky sampler from scratch

Counterexample

Discussion prompt

Build a function that samples N(mu, Sigma) by Cholesky and prove it recovers the mean and covariance you asked for. You have derived every piece - now assemble it.

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

Answer:

Build rules: type every line yourself, draw enough samples (>= 10^5) for the covariance to converge, and remember np.cov expects variables in rows (so transpose with X.T).

107. Milestone 1 - the Cholesky factor

Worked example

Your turn: factor Sigma = [[2, 0.8], [0.8, 1]] and confirm L L^T = Sigma. Predict whether L is lower- or upper-triangular before you print it.

Hint: np.linalg.cholesky returns a lower-triangular L. Use np.allclose(L @ L.T, Sigma) to check the reconstruction.

import numpy as np
Sigma = np.array([[2., 0.8], [0.8, 1.]])
L = np.linalg.cholesky(Sigma)
print(L.round(4))
print(np.allclose(L @ L.T, Sigma))
checkvalue
L[[1.4142, 0], [0.5657, 0.8246]]
lower-triangular?yes (top-right is 0)
L L^T == SigmaTrue

108. Milestone 2 - draw and check the mean

Worked example

Your turn: draw 200k standard normals and transform them with mu + z @ L.T. Predict the sample mean before you print (it should be mu).

Hint: z = rng.normal(size=(200000, 2)), then X = mu + z @ L.T. Use np.random.default_rng(0) so your numbers match these exactly.

import numpy as np
mu = np.array([1., -1.])
Sigma = np.array([[2., 0.8], [0.8, 1.]])
L = np.linalg.cholesky(Sigma)
rng = np.random.default_rng(0)
z = rng.normal(size=(200000, 2))
X = mu + z @ L.T
print(X.mean(0).round(3))
quantityvalue
sample mean[1.002, -1.000]
target mu[1, -1]

109. Milestone 3 - recover Sigma

Worked example

Your turn: estimate the sample covariance and compare to Sigma. Predict: will the 0.8 off-diagonal correlation come back?

Hint: np.cov(X.T) returns the 2x2 covariance (variables in rows). It should match Sigma to ~2 decimals - including the off-diagonal.

import numpy as np
mu = np.array([1., -1.])
Sigma = np.array([[2., 0.8], [0.8, 1.]])
L = np.linalg.cholesky(Sigma)
X = mu + np.random.default_rng(0).normal(size=(200000, 2)) @ L.T
print(np.cov(X.T).round(3))
print(Sigma)
value
sample cov[[2.007, 0.798], [0.798, 0.999]]
target Sigma[[2.0, 0.8], [0.8, 1.0]]

110. What each one costs: Milestone 3 - recover Sigma

Trade off

Comparison matrix

From Milestone 3 - recover Sigma: every row here is a choice with a cost. Fill the value column, then say which row you would actually pick and what you give up for it.

value
sample cov[[2.007, 0.798], [0.798, 0.999]]
target Sigma[[2.0, 0.8], [0.8, 1.0]]

111. The full program

Worked example

Wrap it in a reusable sample_gaussian, then sanity-check with the mean Mahalanobis distance - which should land near d = 2 (each sample averages about d in squared Mahalanobis units, a chi-square(d) fact).

import numpy as np

def sample_gaussian(mu, Sigma, n, seed=0):
    L = np.linalg.cholesky(Sigma)                 # Sigma = L L^T
    z = np.random.default_rng(seed).normal(size=(n, len(mu)))
    return mu + z @ L.T                           # x = mu + L z

mu = np.array([1., -1.])
Sigma = np.array([[2., 0.8], [0.8, 1.]])
X = sample_gaussian(mu, Sigma, 200000)
print('mean', X.mean(0).round(3))
print('cov', np.cov(X.T).round(3))

Sinv = np.linalg.inv(Sigma)
dM2 = np.einsum('ij,jk,ik->i', X - mu, Sinv, X - mu)
print('mean Mahalanobis^2', round(dM2.mean(), 3))
outputvalue (verified)
mean[1.002, -1.0]
cov[[2.007, 0.798], [0.798, 0.999]]
mean Mahalanobis^22.006 (approx d = 2)

If your samples recover both mu and Sigma and the mean Mahalanobis lands near 2 - you have implemented the exact sampler a VAE uses to generate its latents.

112. Fill in: value (verified) for The full program

Comparison

Comparison matrix

From The full program: refill the value (verified) column from what you know. The rest of the table is as it appeared.

outputvalue (verified)
mean[1.002, -1.0]
cov[[2.007, 0.798], [0.798, 0.999]]
mean Mahalanobis^22.006 (approx d = 2)

113. Show it off

Concept

Out loud, slides closed: explain (1) why Sigma must be PSD from a^T Sigma a = Var(a^T x), (2) what Mahalanobis distance corrects for that Euclidean misses, and (3) why x = mu + L z produces covariance exactly Sigma.

Stretch (homework): derive the marginal of x_1 by integrating out x_2, prove diagonal Sigma => independent by factorizing the density, and threshold outliers at d_M^2 = 5.991 (the 95% point of chi-square with 2 dof). This distribution powers GMMs (Week 24) and the VAE latent space (Week 44).

114. Connect it up: Lesson 23: The Multivariate Gaussian

Connect it up

Draw it

One page, no notation unless you need it: draw how these connect — From one bell curve to many · Why Sigma is symmetric PSD · The ellipse and Mahalanobis · The normalizing constant · Marginals, conditionals, independence · Sampling by Cholesky. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.

115. What you can do now

Recap

ideathe one thing to remember
Sigmacovariance: symmetric PSD, PD to invert
distanceMahalanobis (x-mu)^T Sigma^{-1} (x-mu), not Euclidean
geometryeigenvectors = ellipse axes, sqrt(eigenvalues) = semi-axes
conditioningmean shifts, variance shrinks; diagonal => independent (Gaussian only)
samplingx = mu + L z, L = chol(Sigma); Cov = L L^T = Sigma

Sources

  1. USAAIO Year-Long Master Lesson Plan, Lesson 23 (Week 8 - Multivariate Gaussian) — Barron - USAAIO Round 2 Preparation, 2026
  2. C. M. Bishop, Pattern Recognition and Machine Learning, Ch. 2.3 (The Gaussian Distribution) — Springer, 2006
  3. Kingma & Welling, Auto-Encoding Variational Bayes (the reparameterization trick)
  4. scipy.stats.multivariate_normal / numpy.linalg.cholesky
  5. Every determinant, eigenvalue, Cholesky factor, PDF value, and sample statistic produced by real execution — numpy 2.2.6 + scipy 1.16, verification run July 2026

Want this taught 1-on-1? Alexander tutors Machine Learning — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108