二、特征值分解
2.1 特征值分解定义与操作
🚩特征值分解,就是将矩阵A分解为如下式:
import numpy as np A = np.array([[7, 8, 4, 3], [2, 9, 6, 8], [1, 6, 9, 6], [7, 9, 7, 3]]) # w(Σ)表示特征值,v(Q)表示特征向量 w, v = np.linalg.eig(A) print('矩阵A的特征值和特征向量:') display(w, v) # 根据特征值分解公式可得 print('特征值和特征向量运算反推矩阵A:') display(v.dot(np.diag(w)).dot(np.linalg.inv(v)))
# 实对称矩阵 import numpy as np B = np.array([[1, 2, 3], [2, 5, 8], [3, 8, 9]]) # w(Σ)表示特征值,v(Q)表示特征向量 w,v = np.linalg.eig(B) print('矩阵A的特征值和特征向量:') display(w, v) print('特征值和特征向量运算反推矩阵A:') display(v.dot(np.diag(w)).dot(np.linalg.inv(v))) display(v.dot(np.diag(w)).dot(v.T)) print('Q是正交矩阵:') display(np.linalg.inv(v), v.T)
2.2 特征值分解意义
🚩一个矩阵其实就是一个线性变换,因为一个矩阵乘以一个向量后得到的向量,其实就相当于将这个向量进行了线性变换。
当矩阵是高维的情况下,那么这个矩阵就是高维空间下的一个线性变换,这个线性变化可能没法通过图片来表示,但是可以想象,这个变换也同样有很多的变换方向,我们通过特征值分解得到的前 N 大特征向量,那么就对应了这个矩阵最主要的 N 个变化方向。我们利用这前 N 个变化方向,就可以近似表达这个矩阵(变换)。也就是说的:提取这个矩阵最重要的特征。
总结一下,特征值分解可以得到特征值与特征向量,特征值大小表示的是这个特征到底有多重要,而特征向量表示这个特征是什么,可以将每一个特征向量理解为一个线性的子空间,我们可以利用这些线性的子空间干很多的事情。
不过,特征值分解也有很多的限制,比如说变换的矩阵必须是方阵。
三、矩阵和向量求导公式
3.1 常见矩阵求导公式
🚩有六种矩阵求导公式如下:
3.2 向量求导公式
3.3 矩阵求导公式
转置公式如下:
四、奇异值分解(SVD)
4.1 什么是奇异值分解
🚩特征值分解是一个提取矩阵特征很不错的方法,但是它只适用于方阵。而在现实的世界中,我们看到的大部分矩阵都不是方阵,比如说有 m 个学生,每个学生有 n 科成绩,这样形成的一个 m ∗ n 的矩阵就可能不是方阵,我们怎样才能像描述特征值一样描述这样一般矩阵呢的重要特征呢?奇异值分解就是用来干这个事的,奇异值分解是一个能适用于任意的矩阵的一种分解的方法。
4.2 奇异值与特征值关系
🚩特征值分解:
奇异值分解:
那么奇异值和特征值是怎么对应起来的呢?首先,我们将矩阵 A 的转置 和 A做矩阵乘法,将会得到一个方阵,我们用这个方阵求特征值可以得到:
对 AAT以及ATA做特征值分解,即可得到奇异值分解的结果。但是样分开求存在一定的问题,由于做特征值分解的时候,特征向量的正负号并不影响结果,比如:
解决方案:
五、求解奇异值分解
5.1 方式一
🚩根据上面对应公式,进行奇异值分解:
import numpy as np A = np.array([[ 3, 4, 5, 5], [ 7, 5, 3, 6], [ 6, 5, 7, 7], [ 4, 9, 8, 9], [ 5, 10, 5, 7]]) # 左奇异矩阵 sigma,U = np.linalg.eig(A.dot(A.T)) # 降序排列后,逆序输出 index = np.argsort(sigma)[::-1] # 将特征值对应的特征向量也对应排好序 sigma = sigma[index] U = U[:, index] print('左奇异矩阵如下:') display(U) # 计算奇异值矩阵 sigma = np.sqrt([s if s > 0 else 0 for s in sigma]) print('奇异值为:') display(sigma[:4]) # 计算右奇异矩阵 # 奇异值矩阵特殊,斜对角线有值,其余都是0 # 奇异值矩阵的逆矩阵 sigma_inv = np.diag([1 / s if s > 0 else 0 for s in sigma]) V = sigma_inv.dot((U.T).dot(A)) print('右奇异矩阵如下:') display(V[:4]) print('使用奇异值分解还原矩阵A:') U.dot(np.diag(sigma)).dot(V).round(0)
5.2 方式二
🚩根据,NumPy提供的方法,进行奇异值求解
import numpy as np A = np.array([[ 3, 4, 5, 5], [ 7, 5, 3, 6], [ 6, 5, 7, 7], [ 4, 9, 8, 9], [ 5, 10, 5, 7]]) u, s, v =np.linalg.svd(A) # 奇异值分解 display(u, s, v)
六、奇异值分解性质
右边的三个矩阵相乘的结果将会是一个接近于 A 的矩阵,在这儿,r 越接近于 n ,则相乘的结果越接近于 A 。而这三个矩阵的面积之和(在存储观点来说,矩阵面积越小,存储量就越小)要远远小于原始的矩阵 A ,我们如果想要压缩空间来表示原矩阵 A ,我们存下这里的三个矩阵:U 、Σ 、V 就好了。
说句大白话,称作「奇异值」可能无法顾名思义迅速理解其本质,那咱们换个说法,称作「主特征值」,你可能就迅速了然了。
对于非奇异(满秩)矩阵,对应着特征值;对于奇异矩阵,就需要进行奇异值分解,对应着奇异值。对于奇异矩阵,将A与其转置相乘ATA将会得到一个方阵,再求特征值。值得注意的是,对于非奇异矩阵进行奇异值分解(S V D ),得到的奇异值,其实就是特征值。
import numpy as np A = np.array([[ 3, 4, 5, 5], [ 7, 5, 3, 6], [ 6, 5, 7, 7], [ 4, 9, 8, 9], [ 5, 10, 5, 7]]) u, s, v =np.linalg.svd(A) m, n = A.shape Σ = np.concatenate([np.diag(s), np.full(shape = (m - n,n), fill_value = 0)]) print('通过奇异值分解还原原数据:') display(u.dot(Σ).dot(v)) # 抽取一部分奇异值,近似表示 print('用前r大的奇异值来近似描述矩阵:') r = 2 display(u[:, :r].dot(Σ[:r, :r]).dot(v[:r]).round(1))