矩阵的奇异值分解与数据降维(Python 实现)

您所在的位置:网站首页 python实现特征值分解 矩阵的奇异值分解与数据降维(Python 实现)

矩阵的奇异值分解与数据降维(Python 实现)

2023-04-05 23:49| 来源: 网络整理| 查看: 265

0 分享至

用微信扫码二维码

分享至好友和朋友圈

1 回顾特征值分解的几何意义

在上一篇 chat 中,我们讲了通过特征值分解(EVD)的方法对样本的特征提取主成分,从而实现数据的降维。在介绍奇异值分解(SVD)之前,我们再着重挖掘一下特征值分解的几何意义。

1.1 分解过程回顾

我们最开始获得的是一组原始的 m×nm×n 数据样本矩阵 AA ,其中,mm 表示特征的个数, nn 表示样本的个数。通过与自身转置相乘:AATAAT 得到了样本特征的 mm 阶协方差矩阵 CC ,通过求取协方差矩阵 CC 的一组标准正交特征向量 q1,q2,...qmq1,q2,...qm 以及对应的特征值 λ1,λ2,...,λmλ1,λ2,...,λm。

我们这里处理的就是协方差矩阵 CC,对 CC 进行特征值分解,将矩阵分解成了 C=[q1q2...qm] λ1λ2...λm qT1qT2...qTm C=[q1q2...qm][λ1λ2...λm][q1Tq2T...qmT]

最终,我们选取前 kk 个特征向量构成数据压缩矩阵 PP 的各行,通过 PAPA 达到数据压缩的目的。

1.2 几何意义剖析

以上是回顾前文的内容,不难发现,为了完成矩阵的特征值分解,最最关键还是要回归到这个基本性质上来:Cqi=λiqiCqi=λiqi。

我们为什么又提这个呢?结合主成分分析的推导过程我们知道,协方差矩阵 CC 之所以能够分解,是因为在原始空间 RmRm 中,我们原本默认是用 e1,e2,...,eme1,e2,...,em 这组默认基向量来表示我们空间中的任意一个向量 aa,如果我们采用基变换,将 aa 用 q1,q2,...,qmq1,q2,...,qm 这组标准正交基来表示后,CaCa 的乘法运算就变得很简单了,只需要在各个基向量的方向上对应伸长 λiλi 倍即可,如图 1 所示:

实际上,我们之前也重点分析过,因为协方差矩阵具备对称性、正定性,保证了他可以被对角化,并且特征值一定为正,从而使得特征值分解的过程一定能够顺利完成。

因此利用特征值分解进行主成分分析,核心就是获取协方差矩阵,然后对其进行矩阵分解,获得一组特征值和其对应的方向。

2 从 Av=σuAv=σu 入手奇异值分解

但是,如果我们不进行协方差矩阵 CC 的求取,绕开它直接对原始的数据采样矩阵 AA 进行矩阵分解,从而进行降维操作,行不行?

如果继续沿用上面的办法,显然是不行的,特征值分解对矩阵的要求很严,首先得是一个方阵,其次在方阵的基础上,还得满足可对角化的要求。但是原始的 m×nm×n 数据采样矩阵 AA 连方阵这个最基本的条件都不满足,是根本无法进行特征值分解的。

找不到类似 Ap=λpAp=λp 的核心等式了,岂不是无能为力了?怎料,天无绝人之路,这里,我首先给大家介绍一个对于任意 m×nm×n 矩阵的更具普遍意义的一般性质:

对于一个 m×nm×n,秩为 rr 的矩阵 AA,这里我们暂且假设 m>nm>n,于是就有 r≤nσ3>...>σr,依序代表了“重要性”的程度,因此我们可以按照主成分贡献率的最低要求,选择前 kk 项进行叠加,用来对原始数据矩阵 AA 进行近似:Aσ1u1vT1+σ2u2vT2+σ3u3vT3+...+σkukvTkAσ1u1v1T+σ2u2v2T+σ3u3v3T+...+σkukvkT

原理示意图如图 4 所示:

这种思想和处理方式在图像压缩的应用中很有用处。

8 利用 python 进行实际操作

8.1 利用 python 进行奇异值分解

举一个 7×57×5 的矩阵 AA 为例,A= 00022000330001111100222005550011100 A=[00022000330001111100222005550011100],这是一个看上去很有规律的矩阵。

我们这里就不按照推导SVD原理的过程:先求 AATAAT, ATAATA,再接着获得 UU ,VV,ΣΣ 矩阵这样一步步计算。我们通过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]]

这样就非常简单的一次性获得了分解的结果,这里要强调一点,通过程序中获得的 sigma 变量不是一个矩阵,而是由 5 个奇异值按照从大到小顺序组成的列表,而最后一项,打印出来的不是 VV 矩阵,而是转置后的矩阵 VTVT。

8.2 行和列的数据压缩实践

下面我们利用奇异值分解的结果进行行压缩和列压缩:

我们观察这一组奇异值,我们发现前两个奇异值在数量级上占有绝对的优势,因此我们选择 k=2k=2 进行行压缩和列压缩。

依照上面介绍的知识点,我们利用 UT2×7AU2×7TA 将矩阵 AA 的行数由 77 行压缩成了 22 行。利用 VT2×5ATV2×5TAT,将矩阵 ATAT 的行由 55 行压缩成了 22 行,换句话说就是将矩阵 AA 的列由 55 列压缩成了 22 列。

代码片段:

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]]

于是我们成功的分别将矩阵 AA 的行和列进行了压缩。

8.3 利用数据压缩进行矩阵近似

最后,我们来实践一下如何对矩阵 AA 的整体进行数据压缩。同样,我们取前两个奇异值 σ1,σ2σ1,σ2,利用 σ1u1vT1+σ2u2vT2σ1u1v1T+σ2u2v2T 进行矩阵 AA 的近似。

代码片段:

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]]

从运行结果来看,我们用较少的数据量实现了不错的矩阵近似效果。

欢迎在下方评论区与我们交流~

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

/阅读下一篇/ 返回网易首页 下载网易新闻客户端


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3