<jupyter_start><jupyter_text>Ridge and Lasso Regression[Video of Lecture](https://www.uio.no/studier/emner/matnat/fys/FYS-STK4155/h20/forelesningsvideoer/LectureSeptember10.mp4?vrtx=view-as-webpage) The singular value decompositionThe examples we have looked at so far are cases where we normally caninvert the matrix $\boldsymbol{X}^T\boldsymbol{X}$. Using a polynomial expansion as wedid both for the masses and the fitting of the equation of state,leads to row vectors of the design matrix which are essentiallyorthogonal due to the polynomial character of our model. Obtaining the inverse of the design matrix is then often done via a so-called LU, QR or Cholesky decomposition. This mayhowever not the be case in general and a standard matrix inversionalgorithm based on say LU, QR or Cholesky decomposition may lead to singularities. We will see examples of this below.There is however a way to partially circumvent this problem and also gain some insights about the ordinary least squares approach, and later shrinkage methods like Ridge and Lasso regressions. This is given by the **Singular Value Decomposition** algorithm, perhapsthe most powerful linear algebra algorithm.  Let us look at adifferent example where we may have problems with the standard matrixinversion algorithm. Thereafter we dive into the math of the SVD.One of the typical problems we encounter with linear regression, in particular when the matrix $\boldsymbol{X}$ (our so-called design matrix) is high-dimensional, are problems with near singular or singular matrices. The column vectors of $\boldsymbol{X}$ may be linearly dependent, normally referred to as super-collinearity.  This means that the matrix may be rank deficient and it is basically impossible to to model the data using linear regression. As an example, consider the matrix $$\begin{align*}\mathbf{X} & =  \left[\begin{array}{rrr}1 & -1 & 2\\1 & 0 & 1\\1 & 2  & -1\\1 & 1  & 0\end{array} \right]\end{align*}$$ The columns of $\boldsymbol{X}$ are linearly dependent. We see this easily since the the first column is the row-wise sum of the other two columns. The rank (more correct,the column rank) of a matrix is the dimension of the space spanned by thecolumn vectors. Hence, the rank of $\mathbf{X}$ is equal to the numberof linearly independent columns. In this particular case the matrix has rank 2.Super-collinearity of an $(n \times p)$-dimensional design matrix $\mathbf{X}$ impliesthat the inverse of the matrix $\boldsymbol{X}^T\boldsymbol{X}$ (the matrix we need to invert to solve the linear regression equations) is non-invertible. If we have a square matrix that does not have an inverse, we say this matrix singular. The example here demonstrates this $$\begin{align*}\boldsymbol{X} & =  \left[\begin{array}{rr}1 & -1\\1 & -1\end{array} \right].\end{align*}$$ We see easily that  $\mbox{det}(\boldsymbol{X}) = x_{11} x_{22} - x_{12} x_{21} = 1 \times (-1) - 1 \times (-1) = 0$. Hence, $\mathbf{X}$ is singular and its inverse is undefined.This is equivalent to saying that the matrix $\boldsymbol{X}$ has at least an eigenvalue which is zero.If our design matrix $\boldsymbol{X}$ which enters the linear regression problem $$\begin{equation}\boldsymbol{\beta}  =  (\boldsymbol{X}^{T} \boldsymbol{X})^{-1} \boldsymbol{X}^{T} \boldsymbol{y},\label{_auto1} \tag{1}\end{equation}$$ has linearly dependent column vectors, we will not be able to compute the inverseof $\boldsymbol{X}^T\boldsymbol{X}$ and we cannot find the parameters (estimators) $\beta_i$. The estimators are only well-defined if $(\boldsymbol{X}^{T}\boldsymbol{X})^{-1}$ exits. This is more likely to happen when the matrix $\boldsymbol{X}$ is high-dimensional. In this case it is likely to encounter a situation where the regression parameters $\beta_i$ cannot be estimated.A cheap  *ad hoc* approach is  simply to add a small diagonal component to the matrix to invert, that is we change $$\boldsymbol{X}^{T} \boldsymbol{X} \rightarrow \boldsymbol{X}^{T} \boldsymbol{X}+\lambda \boldsymbol{I},$$ where $\boldsymbol{I}$ is the identity matrix.  When we discuss **Ridge** regression this is actually what we end up evaluating. The parameter $\lambda$ is called a hyperparameter. More about this later. From standard linear algebra we know that a square matrix $\boldsymbol{X}$ can be diagonalized if and only it is a so-called [normal matrix](https://en.wikipedia.org/wiki/Normal_matrix), that is if $\boldsymbol{X}\in {\mathbb{R}}^{n\times n}$we have $\boldsymbol{X}\boldsymbol{X}^T=\boldsymbol{X}^T\boldsymbol{X}$ or if $\boldsymbol{X}\in {\mathbb{C}}^{n\times n}$ we have $\boldsymbol{X}\boldsymbol{X}^{\dagger}=\boldsymbol{X}^{\dagger}\boldsymbol{X}$.The matrix has then a set of eigenpairs $$(\lambda_1,\boldsymbol{u}_1),\dots, (\lambda_n,\boldsymbol{u}_n),$$ and the eigenvalues are given by the diagonal matrix $$\boldsymbol{\Sigma}=\mathrm{Diag}(\lambda_1, \dots,\lambda_n).$$ The matrix $\boldsymbol{X}$ can be written in terms of an orthogonal/unitary transformation $\boldsymbol{U}$ $$\boldsymbol{X} = \boldsymbol{U}\boldsymbol{\Sigma}\boldsymbol{V}^T,$$ with $\boldsymbol{U}\boldsymbol{U}^T=\boldsymbol{I}$ or $\boldsymbol{U}\boldsymbol{U}^{\dagger}=\boldsymbol{I}$.Not all square matrices are diagonalizable. A matrix like the one discussed above $$\boldsymbol{X} = \begin{bmatrix} 1&  -1 \\1& -1\\\end{bmatrix}$$ is not diagonalizable, it is a so-called [defective matrix](https://en.wikipedia.org/wiki/Defective_matrix). It is easy to see that the condition$\boldsymbol{X}\boldsymbol{X}^T=\boldsymbol{X}^T\boldsymbol{X}$ is not fulfilled.  The SVD, a Fantastic AlgorithmHowever, and this is the strength of the SVD algorithm, any generalmatrix $\boldsymbol{X}$ can be decomposed in terms of a diagonal matrix andtwo orthogonal/unitary matrices.  The [Singular Value Decompostion(SVD) theorem](https://en.wikipedia.org/wiki/Singular_value_decomposition)states that a general $m\times n$ matrix $\boldsymbol{X}$ can be written interms of a diagonal matrix $\boldsymbol{\Sigma}$ of dimensionality $m\times n$and two orthognal matrices $\boldsymbol{U}$ and $\boldsymbol{V}$, where the first hasdimensionality $m \times m$ and the last dimensionality $n\times n$.We have then $$\boldsymbol{X} = \boldsymbol{U}\boldsymbol{\Sigma}\boldsymbol{V}^T$$ As an example, the above defective matrix can be decomposed as $$\boldsymbol{X} = \frac{1}{\sqrt{2}}\begin{bmatrix}  1&  1 \\ 1& -1\\ \end{bmatrix} \begin{bmatrix}  2&  0 \\ 0& 0\\ \end{bmatrix}    \frac{1}{\sqrt{2}}\begin{bmatrix}  1&  -1 \\ 1& 1\\ \end{bmatrix}=\boldsymbol{U}\boldsymbol{\Sigma}\boldsymbol{V}^T,$$ with eigenvalues $\sigma_1=2$ and $\sigma_2=0$. The SVD exits always! The SVDdecomposition (singular values) gives eigenvalues $\sigma_i\geq\sigma_{i+1}$ for all $i$ and for dimensions larger than $i=p$, theeigenvalues (singular values) are zero.In the general case, where our design matrix $\boldsymbol{X}$ has dimension$n\times p$, the matrix is thus decomposed into an $n\times n$orthogonal matrix $\boldsymbol{U}$, a $p\times p$ orthogonal matrix $\boldsymbol{V}$and a diagonal matrix $\boldsymbol{\Sigma}$ with $r=\mathrm{min}(n,p)$singular values $\sigma_i\geq 0$ on the main diagonal and zeros fillingthe rest of the matrix.  There are at most $p$ singular valuesassuming that $n > p$. In our regression examples for the nuclearmasses and the equation of state this is indeed the case, while forthe Ising model we have $p > n$. These are often cases that lead tonear singular or singular matrices.The columns of $\boldsymbol{U}$ are called the left singular vectors while the columns of $\boldsymbol{V}$ are the right singular vectors. Economy-size SVDIf we assume that $n > p$, then our matrix $\boldsymbol{U}$ has dimension $n\times n$. The last $n-p$ columns of $\boldsymbol{U}$ become howeverirrelevant in our calculations since they are multiplied with thezeros in $\boldsymbol{\Sigma}$.The economy-size decomposition removes extra rows or columns of zerosfrom the diagonal matrix of singular values, $\boldsymbol{\Sigma}$, along with the columnsin either $\boldsymbol{U}$ or $\boldsymbol{V}$ that multiply those zeros in the expression. Removing these zeros and columns can improve execution timeand reduce storage requirements without compromising the accuracy ofthe decomposition.If $n > p$, we keep only the first $p$ columns of $\boldsymbol{U}$ and $\boldsymbol{\Sigma}$ has dimension $p\times p$. If $p > n$, then only the first $n$ columns of $\boldsymbol{V}$ are computed and $\boldsymbol{\Sigma}$ has dimension $n\times n$.The $n=p$ case is obvious, we retain the full SVD. In general the economy-size SVD leads to less FLOPS and still conserving the desired accuracy.<jupyter_code>import numpy as np
# SVD inversion
def SVDinv(A):
    ''' Takes as input a numpy matrix A and returns inv(A) based on singular value decomposition (SVD).
    SVD is numerically more stable than the inversion algorithms provided by
    numpy and scipy.linalg at the cost of being slower.
    '''
    U, s, VT = np.linalg.svd(A)
#    print('test U')
#    print( (np.transpose(U) @ U - U @np.transpose(U)))
#    print('test VT')
#    print( (np.transpose(VT) @ VT - VT @np.transpose(VT)))
    print(U)
    print(s)
    print(VT)

    D = np.zeros((len(U),len(VT)))
    for i in range(0,len(VT)):
        D[i,i]=s[i]
    UT = np.transpose(U); V = np.transpose(VT); invD = np.linalg.inv(D)
    return np.matmul(V,np.matmul(invD,UT))


X = np.array([ [1.0, -1.0, 2.0], [1.0, 0.0, 1.0], [1.0, 2.0, -1.0], [1.0, 1.0, 0.0] ])
print(X)
A = np.transpose(X) @ X
print(A)
# Brute force inversion of super-collinear matrix
#B = np.linalg.inv(A)
#print(B)
C = SVDinv(A)
print(C)<jupyter_output>[[ 1. -1.  2.]
 [ 1.  0.  1.]
 [ 1.  2. -1.]
 [ 1.  1.  0.]]
[[ 4.  2.  2.]
 [ 2.  6. -4.]
 [ 2. -4.  6.]]
[[-1.18404906e-16  8.16496581e-01 -5.77350269e-01]
 [-7.07106781e-01  4.08248290e-01  5.77350269e-01]
 [ 7.07106781e-01  4.08248290e-01  5.77350269e-01]]
[1.00000000e+01 6.00000000e+00 9.10898112e-32]
[[ 3.33066907e-17 -7.07106781e-01  7.07106781e-01]
 [ 8.16496581e-01  4.08248290e-01  4.08248290e-01]
 [ 5.77350269e-01 -5.77350269e-01 -5.77350269e-01]]
[[-3.65939208e+30  3.65939208e+30  3.65939208e+30]
 [ 3.65939208e+30 -3.65939208e+30 -3.65939208e+30]
 [ 3.65939208e+30 -3.65939208e+30 -3.65939208e+30]]<jupyter_text>The matrix $\boldsymbol{X}$ has columns that are linearly dependent. The firstcolumn is the row-wise sum of the other two columns. The rank of amatrix (the column rank) is the dimension of space spanned by thecolumn vectors. The rank of the matrix is the number of linearlyindependent columns, in this case just $2$. We see this from thesingular values when running the above code. Running the standardinversion algorithm for matrix inversion with $\boldsymbol{X}^T\boldsymbol{X}$ resultsin the program terminating due to a singular matrix.There are several interesting mathematical properties which will berelevant when we are going to discuss the differences between sayordinary least squares (OLS) and **Ridge** regression.We have from OLS that the parameters of the linear approximation are given by $$\boldsymbol{\tilde{y}} = \boldsymbol{X}\boldsymbol{\beta} = \boldsymbol{X}\left(\boldsymbol{X}^T\boldsymbol{X}\right)^{-1}\boldsymbol{X}^T\boldsymbol{y}.$$ The matrix to invert can be rewritten in terms of our SVD decomposition as $$\boldsymbol{X}^T\boldsymbol{X} = \boldsymbol{V}\boldsymbol{\Sigma}^T\boldsymbol{U}^T\boldsymbol{U}\boldsymbol{\Sigma}\boldsymbol{V}^T.$$ Using the orthogonality properties of $\boldsymbol{U}$ we have $$\boldsymbol{X}^T\boldsymbol{X} = \boldsymbol{V}\boldsymbol{\Sigma}^T\boldsymbol{\Sigma}\boldsymbol{V}^T =  \boldsymbol{V}\boldsymbol{D}\boldsymbol{V}^T,$$ with $\boldsymbol{D}$ being a diagonal matrix with values along the diagonal given by the singular values squared. This means that $$(\boldsymbol{X}^T\boldsymbol{X})\boldsymbol{V} = \boldsymbol{V}\boldsymbol{D},$$ that is the eigenvectors of $(\boldsymbol{X}^T\boldsymbol{X})$ are given by the columns of the right singular matrix of $\boldsymbol{X}$ and the eigenvalues are the squared singular values.  It is easy to show (show this) that $$(\boldsymbol{X}\boldsymbol{X}^T)\boldsymbol{U} = \boldsymbol{U}\boldsymbol{D},$$ that is, the eigenvectors of $(\boldsymbol{X}\boldsymbol{X})^T$ are the columns of the left singular matrix and the eigenvalues are the same. Going back to our OLS equation we have $$\boldsymbol{X}\boldsymbol{\beta} = \boldsymbol{X}\left(\boldsymbol{V}\boldsymbol{D}\boldsymbol{V}^T \right)^{-1}\boldsymbol{X}^T\boldsymbol{y}=\boldsymbol{U\Sigma V^T}\left(\boldsymbol{V}\boldsymbol{D}\boldsymbol{V}^T \right)^{-1}(\boldsymbol{U\Sigma V^T})^T\boldsymbol{y}=\boldsymbol{U}\boldsymbol{U}^T\boldsymbol{y}.$$ We will come back to this expression when we discuss Ridge regression. $$ \tilde{y}^{OLS}=\boldsymbol{X}\hat{\beta}^{OLS}=\sum_{j=1}^p \boldsymbol{u}_j\boldsymbol{u}_j^T\boldsymbol{y}$$ and for Ridge we have $$ \tilde{y}^{Ridge}=\boldsymbol{X}\hat{\beta}^{Ridge}=\sum_{j=1}^p \boldsymbol{u}_j\frac{\sigma_j^2}{\sigma_j^2+\lambda}\boldsymbol{u}_j^T\boldsymbol{y}$$ . It is indeed the economy-sized SVD, note the summation runs up tp $$p$$ only and not $$n$$. Here we have that $$\boldsymbol{X} = \boldsymbol{U}\boldsymbol{\Sigma}\boldsymbol{V}^T$$, with $$\Sigma$$ being an $$ n\times p$$ matrix and $$\boldsymbol{V}$$ being a $$ p\times p$$ matrix. We also have assumed here that $$ n > p$$.  Ridge and LASSO Regression[Video of Lecture](https://www.uio.no/studier/emner/matnat/fys/FYS-STK4155/h20/forelesningsvideoer/LectureSeptember11.mp4?vrtx=view-as-webpage)Let us remind ourselves about the expression for the standard Mean Squared Error (MSE) which we used to define our cost function and the equations for the ordinary least squares (OLS) method, that is our optimization problem is $${\displaystyle \min_{\boldsymbol{\beta}\in {\mathbb{R}}^{p}}}\frac{1}{n}\left\{\left(\boldsymbol{y}-\boldsymbol{X}\boldsymbol{\beta}\right)^T\left(\boldsymbol{y}-\boldsymbol{X}\boldsymbol{\beta}\right)\right\}.$$ or we can state it as $${\displaystyle \min_{\boldsymbol{\beta}\in{\mathbb{R}}^{p}}}\frac{1}{n}\sum_{i=0}^{n-1}\left(y_i-\tilde{y}_i\right)^2=\frac{1}{n}\vert\vert \boldsymbol{y}-\boldsymbol{X}\boldsymbol{\beta}\vert\vert_2^2,$$ where we have used the definition of  a norm-2 vector, that is $$\vert\vert \boldsymbol{x}\vert\vert_2 = \sqrt{\sum_i x_i^2}.$$ By minimizing the above equation with respect to the parameters$\boldsymbol{\beta}$ we could then obtain an analytical expression for theparameters $\boldsymbol{\beta}$.  We can add a regularization parameter $\lambda$ bydefining a new cost function to be optimized, that is $${\displaystyle \min_{\boldsymbol{\beta}\in{\mathbb{R}}^{p}}}\frac{1}{n}\vert\vert \boldsymbol{y}-\boldsymbol{X}\boldsymbol{\beta}\vert\vert_2^2+\lambda\vert\vert \boldsymbol{\beta}\vert\vert_2^2$$ which leads to the Ridge regression minimization problem where werequire that $\vert\vert \boldsymbol{\beta}\vert\vert_2^2\le t$, where $t$ isa finite number larger than zero. By defining $$C(\boldsymbol{X},\boldsymbol{\beta})=\frac{1}{n}\vert\vert \boldsymbol{y}-\boldsymbol{X}\boldsymbol{\beta}\vert\vert_2^2+\lambda\vert\vert \boldsymbol{\beta}\vert\vert_1,$$ we have a new optimization equation $${\displaystyle \min_{\boldsymbol{\beta}\in{\mathbb{R}}^{p}}}\frac{1}{n}\vert\vert \boldsymbol{y}-\boldsymbol{X}\boldsymbol{\beta}\vert\vert_2^2+\lambda\vert\vert \boldsymbol{\beta}\vert\vert_1$$ which leads to Lasso regression. Lasso stands for least absolute shrinkage and selection operator. Here we have defined the norm-1 as $$\vert\vert \boldsymbol{x}\vert\vert_1 = \sum_i \vert x_i\vert.$$ Using the matrix-vector expression for Ridge regression, $$C(\boldsymbol{X},\boldsymbol{\beta})=\frac{1}{n}\left\{(\boldsymbol{y}-\boldsymbol{X}\boldsymbol{\beta})^T(\boldsymbol{y}-\boldsymbol{X}\boldsymbol{\beta})\right\}+\lambda\boldsymbol{\beta}^T\boldsymbol{\beta},$$ by taking the derivatives with respect to $\boldsymbol{\beta}$ we obtain thena slightly modified matrix inversion problem which for finite valuesof $\lambda$ does not suffer from singularity problems. We obtain $$\boldsymbol{\beta}^{\mathrm{Ridge}} = \left(\boldsymbol{X}^T\boldsymbol{X}+\lambda\boldsymbol{I}\right)^{-1}\boldsymbol{X}^T\boldsymbol{y},$$ with $\boldsymbol{I}$ being a $p\times p$ identity matrix with the constraint that $$\sum_{i=0}^{p-1} \beta_i^2 \leq t,$$ with $t$ a finite positive number. We see that Ridge regression is nothing but the standardOLS with a modified diagonal term added to $\boldsymbol{X}^T\boldsymbol{X}$. Theconsequences, in particular for our discussion of the bias-variance tradeoff are rather interesting.Furthermore, if we use the result above in terms of the SVD decomposition (our analysis was done for the OLS method), we had $$(\boldsymbol{X}\boldsymbol{X}^T)\boldsymbol{U} = \boldsymbol{U}\boldsymbol{D}.$$ We can  analyse the OLS solutions in terms of the eigenvectors (the columns) of the right singular value matrix $\boldsymbol{U}$ as $$\boldsymbol{X}\boldsymbol{\beta} = \boldsymbol{X}\left(\boldsymbol{V}\boldsymbol{D}\boldsymbol{V}^T \right)^{-1}\boldsymbol{X}^T\boldsymbol{y}=\boldsymbol{U\Sigma V^T}\left(\boldsymbol{V}\boldsymbol{D}\boldsymbol{V}^T \right)^{-1}(\boldsymbol{U\Sigma V^T})^T\boldsymbol{y}=\boldsymbol{U}\boldsymbol{U}^T\boldsymbol{y}$$ For Ridge regression this becomes $$\boldsymbol{X}\boldsymbol{\beta}^{\mathrm{Ridge}} = \boldsymbol{U\Sigma V^T}\left(\boldsymbol{V}\boldsymbol{D}\boldsymbol{V}^T+\lambda\boldsymbol{I} \right)^{-1}(\boldsymbol{U\Sigma V^T})^T\boldsymbol{y}=\sum_{j=0}^{p-1}\boldsymbol{u}_j\boldsymbol{u}_j^T\frac{\sigma_j^2}{\sigma_j^2+\lambda}\boldsymbol{y},$$ with the vectors $\boldsymbol{u}_j$ being the columns of $\boldsymbol{U}$. Since $\lambda \geq 0$, it means that compared to OLS, we have $$\frac{\sigma_j^2}{\sigma_j^2+\lambda} \leq 1.$$ Ridge regression finds the coordinates of $\boldsymbol{y}$ with respect to theorthonormal basis $\boldsymbol{U}$, it then shrinks the coordinates by$\frac{\sigma_j^2}{\sigma_j^2+\lambda}$. Recall that the SVD haseigenvalues ordered in a descending way, that is $\sigma_i \geq\sigma_{i+1}$.For small eigenvalues $\sigma_i$ it means that their contributions become less important, a fact which can be used to reduce the number of degrees of freedom.Actually, calculating the variance of $\boldsymbol{X}\boldsymbol{v}_j$ shows that this quantity is equal to $\sigma_j^2/n$.With a parameter $\lambda$ we can thus shrink the role of specific parameters.  For the sake of simplicity, let us assume that the design matrix is orthonormal, that is $$\boldsymbol{X}^T\boldsymbol{X}=(\boldsymbol{X}^T\boldsymbol{X})^{-1} =\boldsymbol{I}.$$ In this case the standard OLS results in $$\boldsymbol{\beta}^{\mathrm{OLS}} = \boldsymbol{X}^T\boldsymbol{y}=\sum_{i=0}^{p-1}\boldsymbol{u}_j\boldsymbol{u}_j^T\boldsymbol{y},$$ and $$\boldsymbol{\beta}^{\mathrm{Ridge}} = \left(\boldsymbol{I}+\lambda\boldsymbol{I}\right)^{-1}\boldsymbol{X}^T\boldsymbol{y}=\left(1+\lambda\right)^{-1}\boldsymbol{\beta}^{\mathrm{OLS}},$$ that is the Ridge estimator scales the OLS estimator by the inverse of a factor $1+\lambda$, andthe Ridge estimator converges to zero when the hyperparameter goes toinfinity.We will come back to more interpreations after we have gone through some of the statistical analysis part. For more discussions of Ridge and Lasso regression, [<NAME>'s](https://arxiv.org/abs/1509.09169) article is highly recommended.Similarly, [Mehta et al's article](https://arxiv.org/abs/1803.08823) is also recommended. A better understanding of regularizationThe parameter $\lambda$ that we have introduced in the Ridge (andLasso as well) regression is often called a regularization parameteror shrinkage parameter. It is common to call it a hyperparameter. What does it mean mathemtically?Here we will first look at how to analyze the difference between thestandard OLS equations and the Ridge expressions in terms of a linearalgebra analysis using the SVD algorithm. Thereafter, we will link(see the material on the bias-variance tradeoff below) theseobservation to the statisical analysis of the results. In particularwe consider how the variance of the parameters $\boldsymbol{\beta}$ isaffected by changing the parameter $\lambda$.We have our design matrix $\boldsymbol{X}\in {\mathbb{R}}^{n\times p}$. With the SVD we decompose it as $$\boldsymbol{X} = \boldsymbol{U\Sigma V^T},$$ with $\boldsymbol{U}\in {\mathbb{R}}^{n\times n}$, $\boldsymbol{\Sigma}\in {\mathbb{R}}^{n\times p}$and $\boldsymbol{V}\in {\mathbb{R}}^{p\times p}$.The matrices $\boldsymbol{U}$ and $\boldsymbol{V}$ are unitary/orthonormal matrices, that is in case the matrices are real we have $\boldsymbol{U}^T\boldsymbol{U}=\boldsymbol{U}\boldsymbol{U}^T=\boldsymbol{I}$ and $\boldsymbol{V}^T\boldsymbol{V}=\boldsymbol{V}\boldsymbol{V}^T=\boldsymbol{I}$. Introducing the Covariance and Correlation functionsBefore we discuss the link between for example Ridge regression and the singular value decomposition, we need to remind ourselves aboutthe definition of the covariance and the correlation function. These are quantities Suppose we have defined two vectors$\hat{x}$ and $\hat{y}$ with $n$ elements each. The covariance matrix $\boldsymbol{C}$ is defined as $$\boldsymbol{C}[\boldsymbol{x},\boldsymbol{y}] = \begin{bmatrix} \mathrm{cov}[\boldsymbol{x},\boldsymbol{x}] & \mathrm{cov}[\boldsymbol{x},\boldsymbol{y}] \\                              \mathrm{cov}[\boldsymbol{y},\boldsymbol{x}] & \mathrm{cov}[\boldsymbol{y},\boldsymbol{y}] \\             \end{bmatrix},$$ where for example $$\mathrm{cov}[\boldsymbol{x},\boldsymbol{y}] =\frac{1}{n} \sum_{i=0}^{n-1}(x_i- \overline{x})(y_i- \overline{y}).$$ With this definition and recalling that the variance is defined as $$\mathrm{var}[\boldsymbol{x}]=\frac{1}{n} \sum_{i=0}^{n-1}(x_i- \overline{x})^2,$$ we can rewrite the covariance matrix as $$\boldsymbol{C}[\boldsymbol{x},\boldsymbol{y}] = \begin{bmatrix} \mathrm{var}[\boldsymbol{x}] & \mathrm{cov}[\boldsymbol{x},\boldsymbol{y}] \\                              \mathrm{cov}[\boldsymbol{x},\boldsymbol{y}] & \mathrm{var}[\boldsymbol{y}] \\             \end{bmatrix}.$$ The covariance takes values between zero and infinity and may thuslead to problems with loss of numerical precision for particularlylarge values. It is common to scale the covariance matrix byintroducing instead the correlation matrix defined via the so-calledcorrelation function $$\mathrm{corr}[\boldsymbol{x},\boldsymbol{y}]=\frac{\mathrm{cov}[\boldsymbol{x},\boldsymbol{y}]}{\sqrt{\mathrm{var}[\boldsymbol{x}] \mathrm{var}[\boldsymbol{y}]}}.$$ The correlation function is then given by values $\mathrm{corr}[\boldsymbol{x},\boldsymbol{y}]\in [-1,1]$. This avoids eventual problems with too large values. Wecan then define the correlation matrix for the two vectors $\boldsymbol{x}$and $\boldsymbol{y}$ as $$\boldsymbol{K}[\boldsymbol{x},\boldsymbol{y}] = \begin{bmatrix} 1 & \mathrm{corr}[\boldsymbol{x},\boldsymbol{y}] \\                              \mathrm{corr}[\boldsymbol{y},\boldsymbol{x}] & 1 \\             \end{bmatrix},$$ In the above example this is the function we constructed using **pandas**.In our derivation of the various regression algorithms like **Ordinary Least Squares** or **Ridge regression**we defined the design/feature matrix $\boldsymbol{X}$ as $$\boldsymbol{X}=\begin{bmatrix}x_{0,0} & x_{0,1} & x_{0,2}& \dots & \dots x_{0,p-1}\\x_{1,0} & x_{1,1} & x_{1,2}& \dots & \dots x_{1,p-1}\\x_{2,0} & x_{2,1} & x_{2,2}& \dots & \dots x_{2,p-1}\\\dots & \dots & \dots & \dots \dots & \dots \\x_{n-2,0} & x_{n-2,1} & x_{n-2,2}& \dots & \dots x_{n-2,p-1}\\x_{n-1,0} & x_{n-1,1} & x_{n-1,2}& \dots & \dots x_{n-1,p-1}\\\end{bmatrix},$$ with $\boldsymbol{X}\in {\mathbb{R}}^{n\times p}$, with the predictors/features $p$  refering to the column numbers and theentries $n$ being the row elements.We can rewrite the design/feature matrix in terms of its column vectors as $$\boldsymbol{X}=\begin{bmatrix} \boldsymbol{x}_0 & \boldsymbol{x}_1 & \boldsymbol{x}_2 & \dots & \dots & \boldsymbol{x}_{p-1}\end{bmatrix},$$ with a given vector $$\boldsymbol{x}_i^T = \begin{bmatrix}x_{0,i} & x_{1,i} & x_{2,i}& \dots & \dots x_{n-1,i}\end{bmatrix}.$$ With these definitions, we can now rewrite our $2\times 2$correaltion/covariance matrix in terms of a moe general design/featurematrix $\boldsymbol{X}\in {\mathbb{R}}^{n\times p}$. This leads to a $p\times p$covariance matrix for the vectors $\boldsymbol{x}_i$ with $i=0,1,\dots,p-1$ $$\boldsymbol{C}[\boldsymbol{x}] = \begin{bmatrix}\mathrm{var}[\boldsymbol{x}_0] & \mathrm{cov}[\boldsymbol{x}_0,\boldsymbol{x}_1]  & \mathrm{cov}[\boldsymbol{x}_0,\boldsymbol{x}_2] & \dots & \dots & \mathrm{cov}[\boldsymbol{x}_0,\boldsymbol{x}_{p-1}]\\\mathrm{cov}[\boldsymbol{x}_1,\boldsymbol{x}_0] & \mathrm{var}[\boldsymbol{x}_1]  & \mathrm{cov}[\boldsymbol{x}_1,\boldsymbol{x}_2] & \dots & \dots & \mathrm{cov}[\boldsymbol{x}_1,\boldsymbol{x}_{p-1}]\\\mathrm{cov}[\boldsymbol{x}_2,\boldsymbol{x}_0]   & \mathrm{cov}[\boldsymbol{x}_2,\boldsymbol{x}_1] & \mathrm{var}[\boldsymbol{x}_2] & \dots & \dots & \mathrm{cov}[\boldsymbol{x}_2,\boldsymbol{x}_{p-1}]\\\dots & \dots & \dots & \dots & \dots & \dots \\\dots & \dots & \dots & \dots & \dots & \dots \\\mathrm{cov}[\boldsymbol{x}_{p-1},\boldsymbol{x}_0]   & \mathrm{cov}[\boldsymbol{x}_{p-1},\boldsymbol{x}_1] & \mathrm{cov}[\boldsymbol{x}_{p-1},\boldsymbol{x}_{2}]  & \dots & \dots  & \mathrm{var}[\boldsymbol{x}_{p-1}]\\\end{bmatrix},$$ and the correlation matrix $$\boldsymbol{K}[\boldsymbol{x}] = \begin{bmatrix}1 & \mathrm{corr}[\boldsymbol{x}_0,\boldsymbol{x}_1]  & \mathrm{corr}[\boldsymbol{x}_0,\boldsymbol{x}_2] & \dots & \dots & \mathrm{corr}[\boldsymbol{x}_0,\boldsymbol{x}_{p-1}]\\\mathrm{corr}[\boldsymbol{x}_1,\boldsymbol{x}_0] & 1  & \mathrm{corr}[\boldsymbol{x}_1,\boldsymbol{x}_2] & \dots & \dots & \mathrm{corr}[\boldsymbol{x}_1,\boldsymbol{x}_{p-1}]\\\mathrm{corr}[\boldsymbol{x}_2,\boldsymbol{x}_0]   & \mathrm{corr}[\boldsymbol{x}_2,\boldsymbol{x}_1] & 1 & \dots & \dots & \mathrm{corr}[\boldsymbol{x}_2,\boldsymbol{x}_{p-1}]\\\dots & \dots & \dots & \dots & \dots & \dots \\\dots & \dots & \dots & \dots & \dots & \dots \\\mathrm{corr}[\boldsymbol{x}_{p-1},\boldsymbol{x}_0]   & \mathrm{corr}[\boldsymbol{x}_{p-1},\boldsymbol{x}_1] & \mathrm{corr}[\boldsymbol{x}_{p-1},\boldsymbol{x}_{2}]  & \dots & \dots  & 1\\\end{bmatrix},$$ The Numpy function **np.cov** calculates the covariance elements usingthe factor $1/(n-1)$ instead of $1/n$ since it assumes we do not havethe exact mean values.  The following simple function uses the**np.vstack** function which takes each vector of dimension $1\times n$and produces a $2\times n$ matrix $\boldsymbol{W}$ $$\boldsymbol{W} = \begin{bmatrix} x_0 & y_0 \\                          x_1 & y_1 \\                          x_2 & y_2\\                          \dots & \dots \\                          x_{n-2} & y_{n-2}\\                          x_{n-1} & y_{n-1} &              \end{bmatrix},$$ which in turn is converted into into the $2\times 2$ covariance matrix$\boldsymbol{C}$ via the Numpy function **np.cov()**. We note that we can also calculatethe mean value of each set of samples $\boldsymbol{x}$ etc using the Numpyfunction **np.mean(x)**. We can also extract the eigenvalues of thecovariance matrix through the **np.linalg.eig()** function.<jupyter_code># Importing various packages
import numpy as np
n = 100
x = np.random.normal(size=n)
print(np.mean(x))
y = 4+3*x+np.random.normal(size=n)
print(np.mean(y))
W = np.vstack((x, y))
C = np.cov(W)
print(C)<jupyter_output>-0.008885578722629236
3.7522047280566193
[[0.8520127  2.59480085]
 [2.59480085 8.99218987]]<jupyter_text>The previous example can be converted into the correlation matrix bysimply scaling the matrix elements with the variances.  We should alsosubtract the mean values for each column. This leads to the followingcode which sets up the correlations matrix for the previous example ina more brute force way. Here we scale the mean values for each column of the design matrix, calculate the relevant mean values and variances and then finally set up the $2\times 2$ correlation matrix (since we have only two vectors).<jupyter_code>import numpy as np
n = 100
# define two vectors                                                                                           
x = np.random.random(size=n)
y = 4+3*x+np.random.normal(size=n)
#scaling the x and y vectors                                                                                   
x = x - np.mean(x)
y = y - np.mean(y)
variance_x = np.sum(x@x)/n
variance_y = np.sum(y@y)/n
print(variance_x)
print(variance_y)
cov_xy = np.sum(x@y)/n
cov_xx = np.sum(x@x)/n
cov_yy = np.sum(y@y)/n
C = np.zeros((2,2))
C[0,0]= cov_xx/variance_x
C[1,1]= cov_yy/variance_y
C[0,1]= cov_xy/np.sqrt(variance_y*variance_x)
C[1,0]= C[0,1]
print(C)<jupyter_output>0.06619182206626131
2.1914224774238273
[[1.         0.70589906]
 [0.70589906 1.        ]]<jupyter_text>We see that the matrix elements along the diagonal are one as theyshould be and that the matrix is symmetric. Furthermore, diagonalizingthis matrix we easily see that it is a positive definite matrix.The above procedure with **numpy** can be made more compact if we use **pandas**.We whow here how we can set up the correlation matrix using **pandas**, as done in this simple code<jupyter_code>import numpy as np
import pandas as pd
n = 10
x = np.random.normal(size=n)
x = x - np.mean(x)
y = 4+3*x+np.random.normal(size=n)
y = y - np.mean(y)
X = (np.vstack((x, y))).T
print(X)
Xpd = pd.DataFrame(X)
print(Xpd)
correlation_matrix = Xpd.corr()
print(correlation_matrix)<jupyter_output>[[ 0.03067028  0.91697817]
 [-0.10806972 -2.26381865]
 [ 0.69519693  3.02968834]
 [-0.07521771 -0.93273404]
 [-0.25910749 -0.58239999]
 [ 0.25450941  0.36941772]
 [-1.65002433 -6.09672604]
 [ 0.58596975  2.19955871]
 [ 0.54121682  2.30685416]
 [-0.01514394  1.05318162]]
          0         1
0  0.030670  0.916978
1 -0.108070 -2.263819
2  0.695197  3.029688
3 -0.075218 -0.932734
4 -0.259107 -0.582400
5  0.254509  0.369418
6 -1.650024 -6.096726
7  0.585970  2.199559
8  0.541217  2.306854
9 -0.015144  1.053182
          0         1
0  1.000000  0.948729
1  0.948729  1.000000<jupyter_text>We expand this model to the Franke function discussed above.<jupyter_code># Common imports
import numpy as np
import pandas as pd


def FrankeFunction(x,y):
	term1 = 0.75*np.exp(-(0.25*(9*x-2)**2) - 0.25*((9*y-2)**2))
	term2 = 0.75*np.exp(-((9*x+1)**2)/49.0 - 0.1*(9*y+1))
	term3 = 0.5*np.exp(-(9*x-7)**2/4.0 - 0.25*((9*y-3)**2))
	term4 = -0.2*np.exp(-(9*x-4)**2 - (9*y-7)**2)
	return term1 + term2 + term3 + term4


def create_X(x, y, n ):
	if len(x.shape) > 1:
		x = np.ravel(x)
		y = np.ravel(y)

	N = len(x)
	l = int((n+1)*(n+2)/2)		# Number of elements in beta
	X = np.ones((N,l))

	for i in range(1,n+1):
		q = int((i)*(i+1)/2)
		for k in range(i+1):
			X[:,q+k] = (x**(i-k))*(y**k)

	return X


# Making meshgrid of datapoints and compute Franke's function
n = 4
N = 100
x = np.sort(np.random.uniform(0, 1, N))
y = np.sort(np.random.uniform(0, 1, N))
z = FrankeFunction(x, y)
X = create_X(x, y, n=n)    

Xpd = pd.DataFrame(X)
# subtract the mean values and set up the covariance matrix
Xpd = Xpd - Xpd.mean()
covariance_matrix = Xpd.cov()
print(covariance_matrix)<jupyter_output>0         1         2         3         4         5         6         7   \
0   0.0  0.000000  0.000000  0.000000  0.000000  0.000000  0.000000  0.000000   
1   0.0  0.082990  0.084167  0.081514  0.081402  0.081260  0.071681  0.071684   
2   0.0  0.084167  0.086116  0.083128  0.083376  0.083577  0.073602  0.073851   
3   0.0  0.081514  0.083128  0.084873  0.085285  0.085676  0.077469  0.077891   
4   0.0  0.081402  0.083376  0.085285  0.085951  0.086592  0.078311  0.078930   
5   0.0  0.081260  0.083577  0.085676  0.086592  0.087482  0.079139  0.079956   
6   0.0  0.071681  0.073602  0.077469  0.078311  0.079139  0.072620  0.073372   
7   0.0  0.071684  0.073851  0.077891  0.078930  0.079956  0.073372  0.074286   
8   0.0  0.071761  0.074170  0.078388  0.079624  0.080845  0.074191  0.075266   
9   0.0  0.071917  0.074568  0.078966  0.080398  0.081816  0.075084  0.076320   
10  0.0  0.062188  0.064304  0.069005  0.070131  0.071248  0.065994  0.066963   
11  0.0  0.062376  0.064686 [...]
{'fraction_non_alphanumeric': 0.15335985444228575, 'fraction_numerical': 0.05754463872698677, 'mean_word_length': 4.337942386831275, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 24, 'https://': 7, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 15, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '542540', 'license': 'CC0-1.0', 'n_tokens_mistral': 11293, 'n_tokens_neox': 10071, 'n_words': 3267, 'path': 'doc/LectureNotes/_build/jupyter_execute/chapter3.ipynb', 'repo_name': 'marlgryd/MachineLearning', 'size': '32441'}