unitgridOpacitycanvasColor

23. SVD

Review

A symmetric matrix SS can be decomposed as S=UDUTS = UDU^T where the columns of UU are its unit eigenvectors (all perpendicular to each other) and DD is a diagonal matrix of its eigenvalues. If SS is also positive semi-definite, the diagonal values of DD are all non-negative.

If you are in a field related to machine learning, this is the one thing you need to understand and take away from linear algebra in my opinion. Singular Value Decomposition (SVD) is a decomposition that works for a matrix of any shape. The inspiration of SVD comes from a simple question:

Can a matrix map a set of orthogonal vectors to a set of orthogonal vectors?

In this chapter, we will investigate this question, first for square matrices and then for matrices of any shape.

Here is the picture to keep in mind. Draw two unit input vectors, perpendicular to each other, shown in orange.

pie.clear()
pie.scalar(150, out='unit')
GRAY, ORANGE, VIOLET = '#71717a', '#ea580c', '#6d28d9'
pie.text('Perpendicular in, perpendicular out', 0, -0.3, 12, 2, -1, out='svd_tb', stroke=GRAY)
pie.la_vec2d(0.54, 0.84, out='in1', stroke=ORANGE)
pie.la_vec2d(-0.84, 0.54, out='in2', stroke=ORANGE)

Now draw the two output vectors, in violet: they have been rotated and their lengths have changed, but they remain perpendicular to each other. We want to know which matrices act like this.

pie.la_vec2d(-1.06, 1.06, out='o1', stroke=VIOLET)
pie.la_vec2d(0.59, 0.59, out='o2', stroke=VIOLET)
pie.text('v₁', 0.75, 1.15, 13, out='box_lbl_v1', stroke=ORANGE)
pie.text('v₂', -1.05, 0.70, 13, out='box_lbl_v2', stroke=ORANGE)
pie.text('o₁', -1.35, 1.35, 13, out='box_lbl_o1', stroke=VIOLET)
pie.text('o₂', 0.95, 0.40, 13, out='box_lbl_o2', stroke=VIOLET)

23.1 Let's understand the question first

23.1.1 Input

Let's start out with understanding what the input is. The input is a set of perpendicular unit vectors. In other words, the input is an nn-by-nn matrix VV where each column ii is a unit vector viv_i and any pair of two vectors is perpendicular:

  • V=[∣∣∣v1v2⋯vn∣∣∣]V = \begin{bmatrix} \vert & \vert & & \vert \\ v_1 & v_2 & \cdots & v_n \\ \vert & \vert & & \vert \end{bmatrix}
  • ∥vi∥=1\|v_i\| = 1 for all ii
  • ⟨vi,vj⟩=0\langle v_i, v_j \rangle = 0 for all i≠ji \ne j

Note that since VV is a matrix whose columns are unit vectors that are all perpendicular to each other, we have VT=V−1V^T = V^{-1}.


23.1.2 Output

We wish the output to also be a set of vectors that are all perpendicular to each other. Now, in the most general case, the norms of these vectors can be anything. Let the norm of the first output vector o1o_1 be d1d_1, the norm of the second output vector o2o_2 be d2d_2 and so on. To be more precise:

  • O=[∣∣∣o1o2⋯on∣∣∣]O = \begin{bmatrix} \vert & \vert & & \vert \\ o_1 & o_2 & \cdots & o_n \\ \vert & \vert & & \vert \end{bmatrix}
  • ∥oi∥=di\|o_i\| = d_i for all ii
  • ⟨oi,oj⟩=0\langle o_i, o_j \rangle = 0 for all i≠ji \ne j

Note that we can write each vector oio_i as oi=diuio_i = d_i u_i where did_i is the norm of oio_i and ui=oi∥oi∥=oidiu_i = \frac{o_i}{\|o_i\|} = \frac{o_i}{d_i} is a unit vector. Using this, we can rewrite OO as:

O=[∣∣∣u1u2⋯un∣∣∣]⏟directions[d1d2⋱dn]⏟norms=UDO = \underbrace{\begin{bmatrix} \vert & \vert & & \vert \\ u_1 & u_2 & \cdots & u_n \\ \vert & \vert & & \vert \end{bmatrix}}_{\text{directions}} \underbrace{\begin{bmatrix} d_1 & & & \\ & d_2 & & \\ & & \ddots & \\ & & & d_n \end{bmatrix}}_{\text{norms}} = UD
  • where UU is a matrix of perpendicular unit vectors, so just like VV it satisfies UT=U−1U^T = U^{-1}
  • and DD is a diagonal matrix.

For the picture above, the output pair is the product of a rotation and a diagonal scaling: O=[−0.71−0.710.71−0.71][1.500−0.84]O = \begin{bmatrix} -0.71 & -0.71 \\ 0.71 & -0.71 \end{bmatrix} \begin{bmatrix} 1.5 & 0 \\ 0 & -0.84 \end{bmatrix}. The left matrix UU holds the unit vectors pointing in the directions of the outputs, and the diagonal matrix DD scales them.


23.2 The complete equation

Now we would like to find a matrix MM that maps the column vectors of VV to the column vectors of O=UDO = UD. In other words, we want to find MM such that:

MV=O=UD\begin{aligned} MV &= O \\ &= UD \end{aligned}

Multiplying both sides by VTV^T on the right and remembering that VVT=IVV^T = I gives us:

MV=UDMVVT=UDVTMVVT⏟I=UDVTM=UDVT\begin{aligned} MV &= UD \\ MVV^T &= UDV^T \\ M\underbrace{VV^T}_{I} &= UDV^T \\ \\ M &= \textcolor{#be185d}{U}\textcolor{#0d9488}{D}\textcolor{#1d4ed8}{V^T} \end{aligned}

Thus, if a matrix MM maps a set of perpendicular unit vectors VV to a set of perpendicular vectors given by UDUD (you can interpret it as: a set of column vectors whose norms are the diagonal values of DD and whose directions are the unit column vectors of UU), it can be represented as M=UDVTM = UDV^T. This decomposition of the matrix MM is called its Singular Value Decomposition (or SVD).

Thus, we can re-frame the question as:

Given an nn-by-nn matrix MM, find nn-by-nn matrices U,D,VU, D, V such that M=UDVTM = UDV^T where UU and VV have unit column vectors which are perpendicular to each other, i.e. VVT=VTV=UUT=UTU=IVV^T = V^TV = UU^T = U^TU = I, and DD is a diagonal matrix.


23.3 Uncovering the solution

Note that we already saw a similar decomposition earlier for a symmetric matrix SS: S=UDUTS = UDU^T where the columns of UU are unit eigenvectors of SS and the diagonal values of the diagonal matrix DD are the corresponding eigenvalues. In other words, for a symmetric matrix SS, we have U=VU = V.

We will use the following insight to find the SVD for a general nn-by-nn matrix MM:

It is easy to calculate the SVD of a symmetric matrix SS: one just needs to calculate the eigenvectors and eigenvalues of SS.


23.3.1 Finding the matrices U,VU, V

Given a matrix M=UDVTM = UDV^T, we note that:

MMT=(UDVT)(VDTUT)=UD(VTV)⏟IDTUT=UD2UT\begin{aligned} MM^T &= (\textcolor{#be185d}{U}\textcolor{#0d9488}{D}\textcolor{#1d4ed8}{V^T})(\textcolor{#1d4ed8}{V}\textcolor{#0d9488}{D^T}\textcolor{#be185d}{U^T}) \\ &= \textcolor{#be185d}{U}\textcolor{#0d9488}{D}\underbrace{(\textcolor{#1d4ed8}{V^T}\textcolor{#1d4ed8}{V})}_{I}\textcolor{#0d9488}{D^T}\textcolor{#be185d}{U^T} \\ \\ &= \textcolor{#be185d}{U}\textcolor{#0d9488}{D^2}\textcolor{#be185d}{U^T} \end{aligned}

since DT=DD^T = D. Similarly:

MTM=(VDTUT)(UDVT)=VD(UTU)⏟IDVT=VD2VT\begin{aligned} M^TM &= (\textcolor{#1d4ed8}{V}\textcolor{#0d9488}{D^T}\textcolor{#be185d}{U^T})(\textcolor{#be185d}{U}\textcolor{#0d9488}{D}\textcolor{#1d4ed8}{V^T}) \\ &= \textcolor{#1d4ed8}{V}\textcolor{#0d9488}{D}\underbrace{(\textcolor{#be185d}{U^T}\textcolor{#be185d}{U})}_{I}\textcolor{#0d9488}{D}\textcolor{#1d4ed8}{V^T} \\ \\ &= \textcolor{#1d4ed8}{V}\textcolor{#0d9488}{D^2}\textcolor{#1d4ed8}{V^T} \end{aligned}

Note that the matrices MMTMM^T and MTMM^TM are nn-by-nn symmetric positive semi-definite. Thus they have nn non-negative eigenvalues and eigenvectors that are perpendicular to each other. Some more observations we can make here:

  • UD2UT\textcolor{#be185d}{U}\textcolor{#0d9488}{D^2}\textcolor{#be185d}{U^T} is the eigendecomposition of the symmetric PSD matrix MMTMM^T. The columns of U\textcolor{#be185d}{U} are the unit eigenvectors of MMTMM^T.
  • VD2VT\textcolor{#1d4ed8}{V}\textcolor{#0d9488}{D^2}\textcolor{#1d4ed8}{V^T} is the eigendecomposition of the symmetric PSD matrix MTMM^TM. The columns of V\textcolor{#1d4ed8}{V} are the unit eigenvectors of MTMM^TM.
  • Both matrices MMTMM^T and MTMM^TM share the same set of non-negative eigenvalues. These eigenvalues are given by the diagonal entries of the matrix D2D^2.

Question

Why do MMTMM^T and MTMM^TM have the same eigenvalues?

This is a special case of a general fact: for any two square matrices AA and BB, the products ABAB and BABA have the same eigenvalues. Indeed, if ABx=λxABx = \lambda x with λ≠0\lambda \ne 0, then multiplying by BB on the left gives BA(Bx)=λ(Bx)BA(Bx) = \lambda (Bx), so λ\lambda is also an eigenvalue of BABA (with eigenvector BxBx). Apply this with A=MA = M and B=MTB = M^T.


23.3.2 Complete solution

Thus, given an nn-by-nn matrix MM, the SVD of MM is given by M=UDVTM = UDV^T where:

  • the columns of the nn-by-nn matrix UU are the unit eigenvectors of MMTMM^T
  • the columns of the nn-by-nn matrix VV are the unit eigenvectors of MTMM^TM
  • DD is an nn-by-nn diagonal matrix whose values are the square roots of the eigenvalues of the symmetric PSD matrix MTMM^TM (or MMTMM^T).

23.4 Interpreting the solution

Now let's try to understand what happens when the matrix MM is multiplied by a vector xx. We can see that Mx=UDVTxMx = \textcolor{#be185d}{U}\textcolor{#0d9488}{D}\textcolor{#1d4ed8}{V^T}x. Thus we can interpret the matrix-vector product as a 3-step process:

  1. VTx\textcolor{#1d4ed8}{V^Tx}: since VV and VTV^T are orthonormal matrices (because VVT=IVV^T = I and V−1=VTV^{-1} = V^T), the vector xx is first rotated or reflected by VTV^T.
  2. D(VTx)\textcolor{#0d9488}{D}(\textcolor{#1d4ed8}{V^Tx}): since DD is a diagonal matrix whose entries are all non-negative, it stretches the dimensions of the vector. To be more precise, the ii-th element of the vector gets multiplied by the ii-th diagonal element of DD.
  3. U(DVTx)\textcolor{#be185d}{U}(\textcolor{#0d9488}{D}\textcolor{#1d4ed8}{V^Tx}): since UU and UTU^T are orthonormal matrices (because UUT=IUU^T = I and U−1=UTU^{-1} = U^T), the vector DVTxDV^Tx is rotated or reflected by UU.

This is the interpretation that SVD provides:

A matrix MM first rotates or reflects a vector xx, scales (or stretches) its dimensions, and rotates or reflects it again.

These transformations (rotations or reflections) are defined by the eigenvectors of the matrices MMTMM^T and MTMM^TM, and the scaling values are the square roots of the (common) eigenvalues of both these matrices.


23.5 Visualizing SVD

Let's calculate the SVD of M=[1011]M = \begin{bmatrix} 1 & 0 \\ 1 & 1 \end{bmatrix}. The steps of the calculation look like:

  • Calculate MMT=[1112]MM^T = \begin{bmatrix} 1 & 1 \\ 1 & 2 \end{bmatrix} and MTM=[2111]M^TM = \begin{bmatrix} 2 & 1 \\ 1 & 1 \end{bmatrix}.
  • Calculate the eigenvalues of MMTMM^T (or MTMM^TM): the eigenvalues are 2.622.62 and 0.380.38.
  • Create a diagonal matrix whose diagonal values are the square roots of the above eigenvalues: D=[1.618000.618]\textcolor{#0d9488}{D} = \begin{bmatrix} 1.618 & 0 \\ 0 & 0.618 \end{bmatrix}
  • Calculate the unit eigenvectors of MMTMM^T: U=[0.526−0.8510.8510.526]\textcolor{#be185d}{U} = \begin{bmatrix} 0.526 & -0.851 \\ 0.851 & 0.526 \end{bmatrix}
  • Calculate the unit eigenvectors of MTMM^TM: V=[0.851−0.5260.5260.851]\textcolor{#1d4ed8}{V} = \begin{bmatrix} 0.851 & -0.526 \\ 0.526 & 0.851 \end{bmatrix}

This gives us M=UDVTM = \textcolor{#be185d}{U}\textcolor{#0d9488}{D}\textcolor{#1d4ed8}{V^T}.

First let's see how this matrix transforms points on a circle. Draw the map of the unit circle under MM.

import numpy as np

pie.clear()
pie.scalar(70, out='unit')
GRAY = '#71717a'
M = [1, 0, 1, 1]
# Rainbow colors for the 20 lines of a fan
FAN_COLORS = [f'oklch(0.55 0.14 {29 + 18 * k})' for k in range(20)]

# 20 points on the unit circle, each mapped by the 2×2 matrix m (row-major), around (cx, 0)
def fan_points(m11, m12, m21, m22, cx, n=20):
    a = np.radians(np.arange(n) * 360 / n)
    return np.stack([cx + m11 * np.cos(a) + m12 * np.sin(a), m21 * np.cos(a) + m22 * np.sin(a)], axis=1)

fan = pie.lift(fan_points, pie.Array[pie.Point])
cx = pie.scalar(0, out='cx')
center = pie.point(cx, 0, out='p_ellipse', hidden=True)
pie.la_mat_to_ellipse([M[:2], M[2:]], center, out='ellipse', stroke=GRAY)
fan_m = pie.line(center, fan(*M, cx, hidden=True), out='fan')
pie.set_stroke(fan_m, FAN_COLORS)

The longest and shortest output vectors have lengths 1.6181.618 and 0.6180.618, the diagonal values of D\textcolor{#0d9488}{D}, and they point along the columns of U\textcolor{#be185d}{U}.

Now let's move it aside and transform the circle into this ellipse step by step.

pie.translate_scalar_to(cx, -3, animate=True)
pie.text('M', -2.5, 2, 14, out='mLabel')

Now let's watch the 3-step process act on a single vector.

  • Start with a unit circle
V_T = np.array([[0.851, 0.526], [-0.526, 0.851]])
D = np.diag([1.618, 0.618])
U = np.array([[0.526, -0.851], [0.851, 0.526]])
# The current matrix, row-major. It starts as the identity.
S = [pie.scalar(v, out=f's{k}') for k, v in enumerate([1, 0, 0, 1])]
origin = pie.point(0, 0, out='origin', hidden=True)
pie.la_mat_to_ellipse([S[:2], S[2:]], origin, out='ellipse_s', stroke=GRAY)
fan_s = pie.line(origin, fan(*S, 0, hidden=True), out='fan_s')
pie.set_stroke(fan_s, FAN_COLORS)
pie.text('I', 0, 2, 14, out='label')
  • Transform by VT\textcolor{#1d4ed8}{V^T}: it is a rotation
pie.text('Vᵀ', 0, 2, 14, out='label')
pie.translate_scalar_to(S, V_T.flatten().tolist(), animate=True)
  • Transform by D\textcolor{#0d9488}{D}: it scales the x and y-axes
pie.text('DVᵀ', 0, 2, 14, out='label')
pie.translate_scalar_to(S, (D @ V_T).flatten().tolist(), animate=True)

Connection with longest and shortest vectors

This step explains how the singular values are connected to the lengths of the longest and shortest vectors. This is the only transform in SVD that changes the lengths of the vectors. It does so simply by scaling the axes. A vector along axis i is scaled by length σi\sigma_i.

  • Transform by U\textcolor{#be185d}{U}: another rotation
GOLD = '#ca8a04'
pie.text('UDVᵀ', 0, 2, 14, out='label')
pie.translate_scalar_to(S, (U @ D @ V_T).flatten().tolist(), animate=True)
pie.text('σ₁ = 1.618', 1.15, 1.377, 12, out='box_sigma1', stroke=GOLD)
pie.text('σ₂ = 0.618', -1.55, 0.325, 12, out='box_sigma2', stroke=GOLD)
pie.text("Longest/shortest output vectors point along U's columns", -1, -2.2, 13, 4, -1, out='box_cap_sigma')

23.6 SVD of non-square matrices

Now let's say you are given an rr-by-cc matrix MM. In other words, the matrix has rr rows and cc columns.

  • This matrix takes as input a cc-dimensional vector. In other words, the input space of this matrix is cc-dimensional.
  • This matrix gives as output an rr-dimensional vector. In other words, the output space of this matrix is rr-dimensional.

In this case, the input space can contain at most cc vectors that are all perpendicular to each other. Similarly the output space can contain at most rr vectors that are all perpendicular to each other. Then the question that we asked initially for square matrices takes the form:

Can an rr-by-cc matrix MM map a set of cc unit vectors (each vector being cc-dimensional) perpendicular to each other to a set of cc vectors (each vector being rr-dimensional) which also remain perpendicular to each other?

There are two scenarios here: r>cr > c and r<cr < c. Let's address the above question in each of the scenarios.


23.6.1 When r>cr > c

Let's assume r=3r = 3 and c=2c = 2. Such a matrix looks like M=[m11m12m21m22m31m32]M = \begin{bmatrix} m_{11} & m_{12} \\ m_{21} & m_{22} \\ m_{31} & m_{32} \end{bmatrix}. Note that this matrix has rank at most 22. Thus its outputs can never span all of the 3-dimensional space.

We can show this at-most 2-dimensional output like so:

O=[∣∣∣u1u2u3∣∣∣][d100d200]O = \textcolor{#be185d}{\begin{bmatrix} \vert & \vert & \vert \\ u_1 & u_2 & u_3 \\ \vert & \vert & \vert \end{bmatrix}} \textcolor{#0d9488}{\begin{bmatrix} d_1 & 0 \\ 0 & d_2 \\ 0 & 0 \end{bmatrix}}

Note how the third column of the left matrix is always ignored because the bottom row of the right matrix is all zeros. Let's name the left matrix U\textcolor{#be185d}{U}. Again, U\textcolor{#be185d}{U} is a matrix whose columns are unit vectors all perpendicular to each other. In other words, UUT=UTU=IUU^T = U^TU = I.

The input vectors to MM are the same as before: a 22-by-22 matrix V\textcolor{#1d4ed8}{V} such that VVT=VTV=IVV^T = V^TV = I. This gives us:

MV=U[d100d200]⇒ M=U[d100d200]VT\begin{aligned} MV &= \textcolor{#be185d}{U} \textcolor{#0d9488}{\begin{bmatrix} d_1 & 0 \\ 0 & d_2 \\ 0 & 0 \end{bmatrix}} \\ \\ \Rightarrow \, M &= \textcolor{#be185d}{U} \textcolor{#0d9488}{\begin{bmatrix} d_1 & 0 \\ 0 & d_2 \\ 0 & 0 \end{bmatrix}} \textcolor{#1d4ed8}{V^T} \end{aligned}
  • Just like the previous case, we have MMT=UD2UTMM^T = \textcolor{#be185d}{U}\textcolor{#0d9488}{D^2}\textcolor{#be185d}{U^T} and MTM=VD2VTM^TM = \textcolor{#1d4ed8}{V}\textcolor{#0d9488}{D^2}\textcolor{#1d4ed8}{V^T} where D=[d100d2]\textcolor{#0d9488}{D} = \begin{bmatrix} d_1 & 0 \\ 0 & d_2 \end{bmatrix}.
  • The calculation of the matrices U,D,VU, D, V is the same as in the case of square matrices.

So the only difference compared to the case of square matrices is that when r>cr > c, r−cr-c rows full of zeros are added to the bottom of the diagonal matrix D\textcolor{#0d9488}{D}.


23.6.2 When r<cr < c

Let's assume r=2r = 2 and c=3c = 3. Such a matrix looks like M=[m11m12m13m21m22m23]M = \begin{bmatrix} m_{11} & m_{12} & m_{13} \\ m_{21} & m_{22} & m_{23} \end{bmatrix}. Note that this matrix also has rank at most 22. In this case the input is 33 unit vectors perpendicular to each other, each vector being 3-dimensional. The output is 33 vectors still perpendicular to each other, each vector being 2-dimensional. But how is this possible?

This is possible only if the third output vector is the zero vector. In other words, the output matrix looks like:

O=[p1q10p2q20]O = \begin{bmatrix} p_1 & q_1 & 0 \\ p_2 & q_2 & 0 \end{bmatrix}

where the vectors [p1p2]T\begin{bmatrix} p_1 & p_2 \end{bmatrix}^T and [q1q2]T\begin{bmatrix} q_1 & q_2 \end{bmatrix}^T are perpendicular to each other. This makes all three output vectors perpendicular to each other, because every vector is perpendicular to the vector of zeros: ⟨any vector,vector of zeros⟩=0\langle \text{any vector}, \text{vector of zeros} \rangle = 0.

We can rewrite the output matrix OO as:

O=[∣∣u1u2∣∣][d1000d20]=U[d1000d20]\begin{aligned} O &= \textcolor{#be185d}{\begin{bmatrix} \vert & \vert \\ u_1 & u_2 \\ \vert & \vert \end{bmatrix}} \textcolor{#0d9488}{\begin{bmatrix} d_1 & 0 & 0 \\ 0 & d_2 & 0 \end{bmatrix}} \\ \\ &= \textcolor{#be185d}{U} \textcolor{#0d9488}{\begin{bmatrix} d_1 & 0 & 0 \\ 0 & d_2 & 0 \end{bmatrix}} \end{aligned}

where the matrix U\textcolor{#be185d}{U} is an rr-by-rr matrix such that UTU=UUT=IU^TU = UU^T = I. Our input matrix is a cc-by-cc matrix V\textcolor{#1d4ed8}{V} such that VVT=VTV=IVV^T = V^TV = I. This gives us the SVD:

M=U[d1000d20]VTM = \textcolor{#be185d}{U} \textcolor{#0d9488}{\begin{bmatrix} d_1 & 0 & 0 \\ 0 & d_2 & 0 \end{bmatrix}} \textcolor{#1d4ed8}{V^T}
  • Just like the previous case, we have MMT=UD2UTMM^T = \textcolor{#be185d}{U}\textcolor{#0d9488}{D^2}\textcolor{#be185d}{U^T} and MTM=VD2VTM^TM = \textcolor{#1d4ed8}{V}\textcolor{#0d9488}{D^2}\textcolor{#1d4ed8}{V^T} where D=[d100d2]\textcolor{#0d9488}{D} = \begin{bmatrix} d_1 & 0 \\ 0 & d_2 \end{bmatrix}.
  • The calculation of the matrices U,D,VU, D, V is the same as in the case of square matrices.

So the only difference compared to the case of square matrices is that when r<cr < c, c−rc-r columns full of zeros are added to the right of the diagonal matrix D\textcolor{#0d9488}{D}.

SVD has applications in wide areas of mathematics and engineering. One of the more popular use cases is dimension reduction. Let's look into it next.


← 22. PSD matrices · 24. Dimension reduction →