这篇回答节选自我在专栏
1.行压缩数据降维
我们直接从矩阵$A$的奇异值分解:
入手,分析如何进行行压缩数据降维。
等式两侧同时乘以左奇异矩阵的转置:
,得到:
,重点是左侧的
,我们把矩阵
记作
个
维列向量并排放置的形式,我们展开来看:
。
这是我们刚讲过的基变换方法,很熟悉了吧,
原本使用的是默认的一组基向量
,我们通过上面的基变换,将其用
这一组标准正交基来表示,由于这一组标准正交基本质上也是由协方差对称矩阵
得到,因此将各列做基变换后,数据分布从行的角度来看就彼此无关了。
此时,我们可以把每一列看作是一个样本,各行是不同的特征,各行彼此无关,我们可以按照熟悉的方法,选择最大的$k$个奇异值对应的$k$个标准正交向量,形成行压缩矩阵:
。
通过
,就实现了列向量从
维到
维的数据降维,完成了主成分的提取。
2.列压缩数据降维
奇异值分解的精彩之处就在于他可以从两个维度进行数据降维,分别提取主成分,前面介绍的是对行进行压缩降维,那么下面我们来说说如何对列进行压缩降维。
还是牢牢抓住
进行启发,我们对式子两边同时乘以右奇异矩阵
,得到
,我们还是聚焦等式的左侧:
。
我们将其整体进行转置的预处理,得到
,我们把矩阵
记作
,
那么同样的道理:
。
类比上面的行压缩过程,在
中,我们从大到小取前
个特征值对应的标准正交特征向量,就构成了另一个压缩矩阵:
。很明显,通过
就能实现将
矩阵的各列由
维压缩到
维。而大家不要忘了,
的列不正是
矩阵的各行吗。
由此,各行向量的维数由
压缩到了
维,实现了列压缩的数据降维。
3.对矩阵整体进行数据压缩
这里我们不谈按行还是按列,我们从矩阵的整体视角,再谈一个数据压缩的方式,我们的思路有点类似级数的概念,将一个
的原始数据矩阵
分解成若干个同等维度矩阵乘以各自权重后相加的形式。这个如何实现?
依旧从奇异值分解的表达式入手,
,我们展开成完整的矩阵形式:
,这里
。
这里展开就得到了:
不难发现,每一个
的结果都是一个等维的
形矩阵,并且他们彼此之间正交,前面的
则是对应矩阵的权重,
,依序代表了“重要性”的程度,因此我们可以按照主成分贡献率的最低要求,选择前$k$项进行叠加,用来对原始数据矩阵
进行近似:
。
原理示意图如图1所示:
这种思想和处理方式在图像压缩的应用中很有用处。
4.利用python进行实际操作
4.1.利用python进行奇异值分解
举一个
的矩阵
为例,
,这是一个看上去很有规律的矩阵。
我们这里就不按照推导SVD原理的过程:先求
,
,再接着获得
,
,
矩阵这样一步步计算。我们通过python提供的工具,直接一次性获得奇异值分解的所有结果。
代码片段:
import numpy as np
A=[[0, 0, 0, 2, 2],
[0, 0, 0, 3, 3],
[0, 0, 0, 1, 1],
[1, 1, 1, 0, 0],
[2, 2, 2, 0, 0],
[5, 5, 5, 0, 0],
[1, 1, 1, 0, 0]]
U, sigma, VT = np.linalg.svd(A)
print(U)
print(sigma)
print(VT)
运行结果:
[[ 0.00000000e+00 5.34522484e-01 8.41650989e-01 5.59998398e-02
-5.26625636e-02 1.14654380e-17 2.77555756e-17]
[ 0.00000000e+00 8.01783726e-01 -4.76944344e-01 -2.09235996e-01
2.93065263e-01 -8.21283146e-17 -2.77555756e-17]
[ 0.00000000e+00 2.67261242e-01 -2.52468946e-01 5.15708308e-01
-7.73870662e-01 1.88060304e-16 0.00000000e+00]
[ -1.79605302e-01 1.38777878e-17 7.39748546e-03 -3.03901436e-01
-2.04933639e-01 8.94308074e-01 -1.83156768e-01]
[ -3.59210604e-01 2.77555756e-17 1.47949709e-02 -6.07802873e-01
-4.09867278e-01 -4.47451355e-01 -3.64856984e-01]
[ -8.98026510e-01 5.55111512e-17 -8.87698255e-03 3.64681724e-01
2.45920367e-01 -6.85811202e-17 1.25520829e-18]
[ -1.79605302e-01 1.38777878e-17 7.39748546e-03 -3.03901436e-01
-2.04933639e-01 5.94635264e-04 9.12870736e-01]]
[ 9.64365076e+00 5.29150262e+00 7.40623935e-16 4.05103551e-16
2.21838243e-32]
[[ -5.77350269e-01 -5.77350269e-01 -5.77350269e-01 0.00000000e+00
0.00000000e+00]
[ -2.46566547e-16 1.23283273e-16 1.23283273e-16 7.07106781e-01
7.07106781e-01]
[ -7.83779232e-01 5.90050124e-01 1.93729108e-01 -2.77555756e-16
-2.22044605e-16]
[ -2.28816045e-01 -5.64364703e-01 7.93180748e-01 1.11022302e-16
-1.11022302e-16]
[ 0.00000000e+00 0.00000000e+00 0.00000000e+00 -7.07106781e-01
7.07106781e-01]]
这样就非常简单的一次性获得了分解的结果,这里要强调一点,通过程序中获得的
变量不是一个矩阵,而是由
个奇异值按照从大到小顺序组成的列表,而最后一项,打印出来的不是
矩阵,而是转置后的矩阵
。
4.2.行和列的数据压缩实践
下面我们利用奇异值分解的结果进行行压缩和列压缩:
我们观察这一组奇异值,我们发现前两个奇异值在数量级上占有绝对的优势,因此我们选择
进行行压缩和列压缩。
依照上面介绍的知识点,我们利用
将矩阵
的行数由7行压缩成了2行。利用
,将矩阵
的行由5行压缩成了2行,换句话说就是将矩阵
的列由5列压缩成了2列。
代码片段:
import numpy as np
A=[[0, 0, 0, 2, 2],
[0, 0, 0, 3, 3],
[0, 0, 0, 1, 1],
[1, 1, 1, 0, 0],
[2, 2, 2, 0, 0],
[5, 5, 5, 0, 0],
[1, 1, 1, 0, 0]]
U, sigma, VT = np.linalg.svd(A)
U_T_2x7 = U.T[:2,:]
print(np.dot(U_T_2x7,A))
VT_2x5=VT[:2,:]
print(np.dot(VT_2x5,np.mat(A).T).T)
运行结果:
[[ -5.56776436e+00 -5.56776436e+00 -5.56776436e+00 0.00000000e+00
0.00000000e+00]
[ 3.60822483e-16 3.60822483e-16 3.60822483e-16 3.74165739e+00
3.74165739e+00]]
[[ 0.00000000e+00 2.82842712e+00]
[ 0.00000000e+00 4.24264069e+00]
[ 0.00000000e+00 1.41421356e+00]
[ -1.73205081e+00 -7.39557099e-32]
[ -3.46410162e+00 -1.47911420e-31]
[ -8.66025404e+00 -2.95822839e-31]
[ -1.73205081e+00 -7.39557099e-32]]
于是我们成功的分别将矩阵
的行和列进行了压缩。
4.3.利用数据压缩进行矩阵近似
最后,我们来实践一下如何对矩阵
的整体进行数据压缩。同样,我们取前两个奇异值
,利用
进行矩阵
的近似。
代码片段:
import numpy as np
A=[[0, 0, 0, 2, 2],
[0, 0, 0, 3, 3],
[0, 0, 0, 1, 1],
[1, 1, 1, 0, 0],
[2, 2, 2, 0, 0],
[5, 5, 5, 0, 0],
[1, 1, 1, 0, 0]]
U, sigma, VT = np.linalg.svd(A)
A_1 = sigma[0]*np.dot(np.mat(U[:, 0]).T, np.mat(VT[0, :]))
A_2 = sigma[1]*np.dot(np.mat(U[:, 1]).T, np.mat(VT[1, :]))
print(A_1+A_2)
运行结果:
[[ -6.97395509e-16 3.48697754e-16 3.48697754e-16 2.00000000e+00
2.00000000e+00]
[ -1.04609326e-15 5.23046632e-16 5.23046632e-16 3.00000000e+00
3.00000000e+00]
[ -3.48697754e-16 1.74348877e-16 1.74348877e-16 1.00000000e+00
1.00000000e+00]
[ 1.00000000e+00 1.00000000e+00 1.00000000e+00 5.19259273e-17
5.19259273e-17]
[ 2.00000000e+00 2.00000000e+00 2.00000000e+00 1.03851855e-16
1.03851855e-16]
[ 5.00000000e+00 5.00000000e+00 5.00000000e+00 2.07703709e-16
2.07703709e-16]
[ 1.00000000e+00 1.00000000e+00 1.00000000e+00 5.19259273e-17
5.19259273e-17]]
从运行结果来看,我们用较少的数据量实现了不错的矩阵近似效果。
此内容节选自我的专栏《机器学习中的数学:线性代数》,如有兴趣,欢迎订阅!机器学习中的数学:线性代数_专栏gitbook.cn
-------------------------------------------------------------------------------------------
当然还有《机器学习中的数学》系列专栏,欢迎大家阅读,配合食用,效果更佳~机器学习中的数学:概率统计_专栏gitbook.cn机器学习中的数学:微积分与最优化_专栏gitbook.cn
机器学习中的数学:概率图与随机过程_专栏gitbook.cn
有订阅的问题可咨询微信:zhangyumeng0422