Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Singular Value Decomposition

Output
/Users/lsp/.virtualenvs/kaggle/lib/python2.7/site-packages/matplotlib/font_manager.py:273: UserWarning: Matplotlib is building the font cache using fc-list. This may take a moment.
  warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')
Notebook Cell
Populating the interactive namespace from numpy and matplotlib
Notebook Cell
Loading...
Notebook Cell

Singular Value Decomposition

The last chapter was a lot of fun! This one will be a bit less heavy. We will see another way to decompose matrices: the Singular Value Decomposition or SVD. Since the beginning of this series, I emphasized the fact that you can see matrices as linear transformation in space. With the SVD, you decompose a matrix in three other matrices. You can see these new matrices as sub-transformations of the space. Instead of doing the transformation in one movement, we decompose it in three movements. As a bonus, we will apply the SVD to image processing. We will see the effect of SVD on an image of Lucy the goose (it is just a goose named Lucy...) so keep on reading!

Plot of the unit circle and its transformation

The unit circle and its transformation by a matrix.

We saw in 2.7 that the eigendecomposition can be done only for square matrices. The way to go to decompose other types of matrices that can’t be decomposed with eigendecomposition is to use Singular Value Decomposition (SVD).

We will decompose A\boldsymbol{A} into 3 matrices (instead of two with eigendecomposition):

Illustration of the singular value decomposition

The singular value decomposition

The matrices U\boldsymbol{U}, D\boldsymbol{D}, and V\boldsymbol{V} have the following properties:

  • U\boldsymbol{U} and V\boldsymbol{V} are orthogonal matrices (UT=U−1\boldsymbol{U}^\text{T}=\boldsymbol{U}^{-1} and VT=V−1\boldsymbol{V}^\text{T}=\boldsymbol{V}^{-1}; see previous sections for more details about orthogonal matrices)

  • D\boldsymbol{D} is a diagonal matrix (all 0 except the diagonal ; see previous sections). However D\boldsymbol{D} is not necessarily square.

The columns of U\boldsymbol{U} are called the left-singular vectors of A\boldsymbol{A} while the columns of V\boldsymbol{V} are the right-singular vectors of A\boldsymbol{A}. The values along the diagonal of D\boldsymbol{D} are the singular values of A\boldsymbol{A}.

Here are the dimensions of the factorization:

Dimensions of the singular value decomposition (SVD)

The dimensions of the singular value decomposition

The diagonal matrix of singular values is not square but have the shape of A\boldsymbol{A}. Look at the example provided in the Numpy doc to see that they create a matrix of zeros with the same shape as A\boldsymbol{A} and fill it with the singular values:

smat = np.zeros((9, 6), dtype=complex)
smat[:6, :6] = np.diag(s)

Intuition

I think that the intuition behind the singular value decomposition needs some explanations about the idea of matrix transformation. For that reason, here are several examples showing how the space can be transformed by 2D square matrices. Hopefully, this will lead to a better understanding of this statement: A\boldsymbol{A} is a matrix that can be seen as a linear transformation. This transformation can be decomposed in three sub-transformations: 1. rotation, 2. re-scaling, 3. rotation. These three steps correspond to the three matrices U\boldsymbol{U}, D\boldsymbol{D}, and V\boldsymbol{V}.

A\boldsymbol{A} is a matrix that can be seen as a linear transformation. This transformation can be decomposed in three sub-transformations: 1. rotation, 2. re-scaling, 3. rotation. These three steps correspond to the three matrices U\boldsymbol{U}, D\boldsymbol{D}, and V\boldsymbol{V}.

You can look at this animation from the Wikipedia article on the SVD. If you scroll down the page you will see each step.

Every matrix can be seen as a linear transformation

You can see a matrix as a specific linear transformation. When you apply this matrix to a vector or to another matrix you will apply this linear transformation to it.

Example 1.

We will modify the vector:

v=[xy]\boldsymbol{v}=\begin{bmatrix} x\\\\ y \end{bmatrix}

by applying the matrix:

[2002]\begin{bmatrix} 2 & 0\\\\ 0 & 2 \end{bmatrix}

We will have:

[x′y′]=[2002][xy]=[2x+0y0x+2y]=[2x2y]\begin{bmatrix} x'\\\\ y' \end{bmatrix}=\begin{bmatrix} 2 & 0\\\\ 0 & 2 \end{bmatrix} \begin{bmatrix} x\\\\ y \end{bmatrix}= \begin{bmatrix} 2x + 0y\\\\ 0x + 2y \end{bmatrix}= \begin{bmatrix} 2x\\\\ 2y \end{bmatrix}

We see that applying the matrix:

[2002]\begin{bmatrix} 2 & 0\\\\ 0 & 2 \end{bmatrix}

just doubled each coordinate of our vector. Here are the graphical representation of v\boldsymbol{v} and its transformation w\boldsymbol{w}:

Plot of a vector and its transformation

Applying the matrix on the vector multiplied each coordinate by two

You can look at other examples of simple transformations on vectors and unit circle in this video.

Example 2.

To represent the linear transformation associated with matrices we can also draw the unit circle and see how a matrix can transform it (see the BONUS in previous section). The unit circle represents the coordinates of every unit vectors (vector of length 1).

Representation of the unit circle

The unit circle

It is then possible to apply a matrix to all these unit vectors to see the kind of deformation it will produce.

Again, let’s apply the matrix:

[2002]\begin{bmatrix} 2 & 0\\\\ 0 & 2 \end{bmatrix}

to the unit circle:

[x′y′]=[2002][xy]=[2x2y]\begin{bmatrix} x'\\\\ y' \end{bmatrix}= \begin{bmatrix} 2 & 0\\\\ 0 & 2 \end{bmatrix} \begin{bmatrix} x\\\\ y \end{bmatrix}= \begin{bmatrix} 2x\\\\ 2y \end{bmatrix}
Representation of the unit circle and its transformation

Another representation of the effect of the matrix: each coordinate of the unit circle was multiplied by two

We can see that the matrix doubled the size of the circle. But in some transformations, the change applied to the xx coordinate is different from the change applied to the yy coordinate. Let’s see what it means graphically.

Example 3.

We will apply the matrix:

[3002]\begin{bmatrix} 3 & 0\\\\ 0 & 2 \end{bmatrix}

to the unit circle:

[x′y′]=[3002]⋅[xy]=[3x2y]\begin{bmatrix} x'\\\\ y' \end{bmatrix}= \begin{bmatrix} 3 & 0\\\\ 0 & 2 \end{bmatrix}\cdot \begin{bmatrix} x\\\\ y \end{bmatrix}= \begin{bmatrix} 3x\\\\ 2y \end{bmatrix}

This gives the following new circle:

Representation of the unit circle and its transformation

Another representation of the effect of the matrix: each coordinate of the unit circle was multiplied by two

We can check that with the equations associated with this matrix transformation. Let’s say that the coordinates of the new circle (after transformation) are x′x' and y′y'. The relation between the old coordinates (xx, yy) and the new coordinates (x′x', y′y') is:

[x′y′]=[3x2y]⇔{x=x′3y=y′2\begin{bmatrix} x'\\\\ y' \end{bmatrix}= \begin{bmatrix} 3x\\\\ 2y \end{bmatrix} \Leftrightarrow \begin{cases} x=\frac{x'}{3}\\\\ y=\frac{y'}{2} \end{cases}

We also know that the equation of the unit circle is x2+y2=1x^2+y^2=1 (the norm of the unit vectors is 1). By replacement we end up with:

(x′3)2+(y′2)2=1(y′2)2=1−(x′3)2y′2=1−(x′3)2y′=21−(x′3)2\begin{align*} \left(\frac{x'}{3}\right)^2 + \left(\frac{y'}{2}\right)^2 = 1\\\\ \left(\frac{y'}{2}\right)^2 = 1 - \left(\frac{x'}{3}\right)^2\\\\ \frac{y'}{2} = \sqrt{1 - \left(\frac{x'}{3}\right)^2}\\\\ y' = 2\sqrt{1 - \left(\frac{x'}{3}\right)^2} \end{align*}

We can check that this equation corresponds to our transformed circle. Let’s start by drawing the old circle. Its equation is:

x2+y2=1y2=1−x2y=1−x2\begin{align*} x^2+y^2=1\\\\ y^2=1-x^2\\\\ y=\sqrt{1-x^2} \end{align*}
<matplotlib.figure.Figure at 0x1059ae0d0>

So far so good!

Coding tip: You can see the trick to plot a circle here: you create the xx variable, then yy is defined from xx. This means that for each xx, the corresponding yy value is calculated (and thus yy has the same shape as xx). Since the result of the square root can be negative or positive (for instance, 4 can be the result of 22 but also of (−2)2(-2)^2) we need to plot both solutions (yy and −y-y in plt.plot). Note also that a lot of values are needed if we want the connection between the two demi-spheres. See also some discussion here.

Now let’s add the circle obtained after matrix transformation. We saw that it is defined with

y=21−(x3)2y = 2\sqrt{1 - \left(\frac{x}{3}\right)^2}
<matplotlib.figure.Figure at 0x108bb2090>

This shows that our transformation was correct.

Note that these examples used diagonal matrices (all zeros except the diagonal). The general rule is that the transformation associated with diagonal matrices imply only a rescaling of each coordinate without rotation. This is a first element to understand the SVD. Look again at the decomposition

The transformation associated with diagonal matrices imply only a rescaling of each coordinate **without rotation**

We saw that the matrix D\boldsymbol{D} is a diagonal matrix. And we saw also that it corresponds to a rescaling without rotation.

Example 4. rotation matrix

Matrices that are not diagonal can produce a rotation (see more details here). Since it is easier to think about angles when we talk about rotation, we will use a matrix of the form

R=[cos(θ)−sin(θ)sin(θ)cos(θ)]R= \begin{bmatrix} cos(\theta) & -sin(\theta)\\\\ sin(\theta) & cos(\theta) \end{bmatrix}

This matrix will rotate our vectors or matrices counterclockwise through an angle θ\theta. Our new coordinates will be

[x′y′]=[cos(θ)−sin(θ)sin(θ)cos(θ)][xy]=[xcos(θ)−ysin(θ)xsin(θ)+ycos(θ)]\begin{bmatrix} x'\\\\ y' \end{bmatrix}= \begin{bmatrix} cos(\theta) & -sin(\theta)\\\\ sin(\theta) & cos(\theta) \end{bmatrix} \begin{bmatrix} x\\\\ y \end{bmatrix}= \begin{bmatrix} xcos(\theta) - ysin(\theta)\\\\ xsin(\theta) + ycos(\theta) \end{bmatrix}

Let’s rotate some vectors through an angle of θ=45∘\theta = 45^\circ.

Let’s start with the vector u\boldsymbol{u} of coordinates x=0x=0 and y=1y=1 and the vector v\boldsymbol{v} of coordinates x=1x=1 and y=0y=0. The vectors u′\boldsymbol{u'} v′\boldsymbol{v'} are the rotated vectors.

Rotation of the unit vectors through matrix operation

Another representation of the effect of the matrix: each coordinate of the unit circle was multiplied by two

First, let’s plot u\boldsymbol{u} and v\boldsymbol{v}.

<matplotlib.figure.Figure at 0x108b66210>

They are the basis vectors of our space. We will calculate the transformation of these vectors:

{ux=0⋅cos(45)−1⋅sin(45)uy=0⋅sin(45)+1⋅cos(45)⇔{ux=−sin(45)uy=cos(45)\begin{cases} u_x = 0\cdot cos(45) - 1\cdot sin(45)\\\\ u_y = 0\cdot sin(45) + 1\cdot cos(45) \end{cases} \Leftrightarrow \begin{cases} u_x = -sin(45)\\\\ u_y = cos(45) \end{cases}
{vx=1⋅cos(45)−0⋅sin(45)vy=1⋅sin(45)+0⋅cos(45)⇔{vx=cos(45)vy=sin(45)\begin{cases} v_x = 1\cdot cos(45) - 0\cdot sin(45)\\\\ v_y = 1\cdot sin(45) + 0\cdot cos(45) \end{cases} \Leftrightarrow \begin{cases} v_x = cos(45)\\\\ v_y = sin(45) \end{cases}

We will now plot these new vectors to check that they are well our basis vectors rotated through an angle of 45∘45^\circ.

<matplotlib.figure.Figure at 0x108b66750>

Coding tip: the numpy functions sin and cos take input in radians. We can convert our angle from degrees to radians with the function np.radians().

We can also transform a circle. We will take a rescaled circle (the one from the example 3.) to be able to see the effect of the rotation.

A rescaled circle (not the same hight and width) rotated

Another representation of the effect of the matrix: each coordinate of the unit circle was multiplied by two

<matplotlib.figure.Figure at 0x108b66450>

We can see that the circle has been rotated by an angle of 45∘45^\circ. We have chosen the length of the vectors from the rescaling weight from example 3 (factor 3 and 2) to match the circle.

Summary

I hope that you got how vectors and matrices can be transformed by rotating or scaling matrices. The SVD can be seen as the decomposition of one complex transformation in 3 simpler transformations (a rotation, a scaling and another rotation).

Note that we took only square matrices. The SVD can be done even with non square matrices but it is harder to represent transformation associated with non square matrices. For instance, a 3 by 2 matrix will map a 2D space to a 3D space.

A rescaled circle (not the same hight and width) rotated

Another representation of the effect of the matrix: each coordinate of the unit circle was multiplied by two

The three transformations

Now that the link between matrices and linear transformation is clearer we can check that a transformation associated with a matrix can be decomposed with the help of the SVD.

But first let’s create a function that takes a 2D matrix as an input and draw the unit circle transformation when we apply this matrix to it. It will be useful to visualize the transformations.

We can use it to check that the three transformations given by the SVD are equivalent to the transformation done with the original matrix. We will also draw each step of the SVD to see the independant effect of the first rotation, the scaling and the second rotation.

We will use the matrix:

A=[3752]\boldsymbol{A}=\begin{bmatrix} 3 & 7\\\\ 5 & 2 \end{bmatrix}

and plot the unit circle and its transformation by A\boldsymbol{A}:

Unit circle:
<matplotlib.figure.Figure at 0x10a420a10>
Unit circle transformed by A:
<matplotlib.figure.Figure at 0x109dc92d0>

This is what we get when we apply the matrix A\boldsymbol{A} to the unit circle and the basis vectors. We can see that the two base vectors are not necessarily rotated the same way. This is related to the sign of the determinant of the matrix (see 2.11).

Let’s now compute the SVD of A\boldsymbol{A}:

array([[-0.85065081, -0.52573111], [-0.52573111, 0.85065081]])
array([ 8.71337969, 3.32821489])
array([[-0.59455781, -0.80405286], [ 0.80405286, -0.59455781]])

We can now look at the sub-transformations by looking at the effect of the matrices U\boldsymbol{U}, D\boldsymbol{D} and V\boldsymbol{V} in the reverse order. Note that it returns the right singular vector already transposed (see the doc).

Unit circle:
<matplotlib.figure.Figure at 0x10a099a10>
First rotation:
<matplotlib.figure.Figure at 0x109631bd0>
Scaling:
<matplotlib.figure.Figure at 0x10a11db10>
Second rotation:
<matplotlib.figure.Figure at 0x10a7bd3d0>

Just to be sure, you can compare this last step with the transformation by A\boldsymbol{A}. Fortunately, you will see that the result is the same:

<matplotlib.figure.Figure at 0x10a1d34d0>

Singular values interpretation

The singular values are ordered by descending order. They correspond to a new set of features (that are a linear combination of the original features) with the first feature explaining most of the variance. For instance from the last example we can visualize these new features. The major axis of the elipse will be the first left singular vector (u1u_1) and its norm will be the first singular value (σ1\sigma_1).

<matplotlib.figure.Figure at 0x108b5cfd0>

They are the major (σ1u1\sigma_1u_1) and minor (σ2u2\sigma_2u_2) axes of the elipse. We can see that the feature corresponding to this major axis is associated with more variance (the range of value on this axis is bigger than the other). See 2.12 for more details about the variance explained.

SVD and eigendecomposition

Now that we understand the kind of decomposition done with the SVD, we want to know how the sub-transformations are found.

The matrices U\boldsymbol{U}, D\boldsymbol{D} and V\boldsymbol{V} can be found by transforming A\boldsymbol{A} in a square matrix and by computing the eigenvectors of this square matrix. The square matrix can be obtain by multiplying the matrix A\boldsymbol{A} by its transpose in one way or the other:

  • U\boldsymbol{U} corresponds to the eigenvectors of AAT\boldsymbol{AA}^\text{T}

  • V\boldsymbol{V} corresponds to the eigenvectors of ATA\boldsymbol{A^\text{T}A}

  • D\boldsymbol{D} corresponds to the eigenvalues AAT\boldsymbol{AA}^\text{T} or ATA\boldsymbol{A^\text{T}A} which are the same.

Let’s take an example of a non square matrix:

A=[723453]\boldsymbol{A}=\begin{bmatrix} 7 & 2\\\\ 3 & 4\\\\ 5 & 3 \end{bmatrix}

The singular value decomposition can be done with the linalg.svd() function from Numpy (note that np.linalg.eig(A) works only on square matrices and will give an error for A).

array([[-0.69366543, 0.59343205, -0.40824829], [-0.4427092 , -0.79833696, -0.40824829], [-0.56818732, -0.10245245, 0.81649658]])
array([ 10.25142677, 2.62835484])
array([[-0.88033817, -0.47434662], [ 0.47434662, -0.88033817]])

The left-singular values

The left-singular values of A\boldsymbol{A} correspond to the eigenvectors of AAT\boldsymbol{AA}^\text{T}.

Example 5.

Note that the sign difference comes from the fact that eigenvectors are not unique. The linalg functions from Numpy return the normalized eigenvectors. Scaling by -1 doesn’t change their direction or the fact that they are unit vectors.

Left singular vectors of A:

array([[-0.69366543, 0.59343205, -0.40824829], [-0.4427092 , -0.79833696, -0.40824829], [-0.56818732, -0.10245245, 0.81649658]])

Eigenvectors of AA_transpose:

array([[-0.69366543, -0.59343205, -0.40824829], [-0.4427092 , 0.79833696, -0.40824829], [-0.56818732, 0.10245245, 0.81649658]])

The right-singular values

The right-singular values of A\boldsymbol{A} correspond to the eigenvectors of ATA\boldsymbol{A}^\text{T}\boldsymbol{A}.

Example 6.

Right singular vectors of A:

array([[-0.88033817, -0.47434662], [ 0.47434662, -0.88033817]])

Eigenvectors of A_transposeA:

array([[ 0.88033817, -0.47434662], [ 0.47434662, 0.88033817]])

The nonzero singular values

The nonzero singular values of A\boldsymbol{A} are the square roots of the eigenvalues of ATA\boldsymbol{A}^\text{T}\boldsymbol{A} and AAT\boldsymbol{AA}^\text{T}.

Example 7.

array([ 10.25142677, 2.62835484])

Eigenvalues of A_transposeA:

array([ 105.09175083, 6.90824917])

Eigenvalues of AA_transpose:

array([ 105.09175083, 6.90824917, -0. ])

Square root of the eigenvalues:

array([ 10.25142677, 2.62835484])

BONUS: Apply the SVD on images

In this example, we will use the SVD to extract the more important features from the image. It is nice to see the effect of the SVD on something very visual. The code is inspired/taken from this blog post.

Let’s start by loading an image in python and convert it to a Numpy array. We will convert it to grayscale to have one dimension per pixel. The shape of the matrix corresponds to the dimension of the image filled with intensity values: 1 cell per pixel.

<matplotlib.figure.Figure at 0x10e02c710>

We will see how to test the effect of SVD on Lucy the goose! Let’s start to extract the left singular vectors, the singular values and the right singular vectors:

Let’s check the shapes of our matrices:

(669, 1000)
(669, 669)
(669,)
(1000, 1000)

Remember that D\boldsymbol{D} are the singular values that need to be put into a diagonal matrix. Also, V\boldsymbol{V} doesn’t need to be transposed (see above).

The singular vectors and singular values are ordered with the first ones corresponding to the more variance explained. For this reason, using just the first few singular vectors and singular values will provide the reconstruction of the principal elements of the image.

We can reconstruct an image from a certain number of singular values. For instance for 2 singular values we will have:

A rescaled circle (not the same hight and width) rotated

Another representation of the effect of the matrix: each coordinate of the unit circle was multiplied by two

In this example, we have reconstructed the 669px by 1000px image from two singular values.

<matplotlib.figure.Figure at 0x10e2a8050>

It is hard to see Lucy with only two singular values and singular vectors. But we already see something!

We will now draw the reconstruction using different number of singular values.

<matplotlib.figure.Figure at 0x10e52f2d0>
<matplotlib.figure.Figure at 0x109d50f90>
<matplotlib.figure.Figure at 0x10a064e90>
<matplotlib.figure.Figure at 0x10d891e10>
<matplotlib.figure.Figure at 0x10e296b90>
<matplotlib.figure.Figure at 0x108d61f50>

Whaou! Even with 50 components, the quality of the image is not bad!

Conclusion

I like this chapter on the SVD because it uses what we have learned so far in a concrete application. The next chapter on the pseudo-inverse is quite cool as well so keep on reading! We will see how to find a near-solution of a system of equation that minimizes the error and at the end we will see an example that uses the pseudo-inverse to find the best fit line of a set of data points.

(0, 10)
<matplotlib.figure.Figure at 0x10a061ed0>