低秩矩阵完备_1.1 矩阵降维之矩阵分解 PCA与SVD

本文介绍了矩阵降维与矩阵分解的区别,重点关注PCA(主成分分析)和SVD(奇异值分解)在低秩矩阵完备中的作用。PCA寻找最大化方差的主成分,而SVD是对任意矩阵的奇异值分解,适用于图像压缩等场景。两者在求解上等价,但SVD在处理非方阵时更具优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

矩阵降维与矩阵分解是两个不同的概念。矩阵降维指就是把数据从高维空间投影到一个低维空间,这个过程可以通过线性或者非线性的映射来完成。目的是挖掘出高维数据中富含原始信息的低维嵌入表示。降维显然是有代价的,它造成了原始信息的损失。所以降维算法的重点和难点在于如何在对原始数据进行数据降维的过程中还能尽可能地保持高维数据的几何结构信息或本征的有区别性的信息,并在此前提下找到高维数据的最优低维表示。对于传统的降维算法来说,它们通常的考虑角度都是找到最大可能地保持某种信息的投影方向或者低维空间。而矩阵分解是将矩阵拆解为数个矩阵的乘积,并没有改变矩阵的维数,但通过矩阵分解往往可以从复杂的数据中提取出相对重要的特征信息,如在特征向量为基的各个方向上的投影,然后通过保留较大的投影,删除较小的投影,便可实现降维的目的;例如特征分解

,进而可写成秩逼近的形式:

其中

是特征向量,列向量,
为逆矩阵对应的行向量,若是实对称阵
。故可通过删除小特征值对应方向的数据,只保留大特征值方向对应的数据来进行降维和压缩。特征值越大,说明矩阵在对应的特征向量上的方差越大,功率越大,信息量越多。因而矩阵分解是矩阵降维的一种重要方法。比较常用的矩阵分解方法有EVD和SVD,降维思想则有PCA, LDA, NMF, MDS等,下面介绍一下相关的学习笔记。

PCA

PCA即主成分分析,即设法将原来n个有一定相关的指标,重新组合成一组新的线性无关的综合指标来代替原来指标。也就是将n维特征映射到k维上,这k维是全新的正交特征也被称为主成分,是在原有n维特征的基础上重新构造出来的k维特征。

理解矩阵乘法
内积与投影
的内积等于
上的投影长度乘以
的模,若设向量
的模为1,则
的内积值等于
所在直线投影的矢量长度!即

基向量
要准确描述向量,首先要确定一组基,然后给出在基所在的各个直线上的投影值,就可以了。 例如向量
实际上表示线性组合:

基变换的矩阵表示
如果我们有
维向量,想将其变换为由
维向量表示的新空间中,那么首先将
个基按行组成矩阵
,然后将向量按列组成矩阵
,那么两矩阵的乘积
就是变换结果,其中
的第
列为
中第
列变换后的结果。
两个矩阵相乘的意义是将右边矩阵中的每一列列向量变换到左边矩阵中每一行行向量为基所表示的空间中去。

因而PCA的核心,既是要寻找一组基组成的矩阵左乘原矩阵,使得n维特征映射到k维上。那么我们应该如何选择K个基才能最大程度保留原有的信息?

一种直观的看法是:希望投影后的投影值尽可能分散。而这种分散程度,可以用数学上的方差来表述,即投影后的数值的方差

; 如对数据已经均值化了,则
。方差越大,表明保留的信息越多。

在高纬度中,首先我们希望找到一个方向使得投影后方差最大,这样就完成了第一个方向的选择,继而我们选择第二个投影方向。如果我们还是单纯只选择方差最大的方向,很明显,这个方向与第一个方向应该是“几乎重合在一起”,显然这样的维度是没有用的,因此应该有其他约束条件。从直观上说,让投影尽可能表示更多的原始信息,我们是不希望它们之间存在(线性)相关性的,因为相关性意味着两个字段不是完全独立,必然存在重复表示的信息。协方差表示其相关性,由于已经让每个变量均值为0,则:

为了让协方差为0,选择第二个基时只能在与第一个基正交的方向上选择。 以此类推,第三个基与第一和第二存在的平面正交。将一组
维向量降为
维(
大于0,小于
),其目标是选择
个单位(模为1)正交基,使得原始数据变换到这组基上后,各变量两两间协方差为0,而变量的方差则尽可能大(在正交的约束下,取最大的
个方差)。

PCA 本质:

设原始数据矩阵

,而
是一组基按行组成的矩阵,设
,则
做基变换后的数据(因为X每行的的均值为0,左乘矩阵相当于对X每行乘以一个系数,故变换后,每行均值仍为0)。此时,
的协方差矩阵为
,根据PCA选择基向量的约束条件,则要求对角线上的方差大,而不同基向量的协方差为0,则
为对角矩阵;进行代换
,因而可以看出寻找的基向量矩阵,即是为能够让原始矩阵X的协方差阵对角化的矩阵,换句话说,优化目标变成了寻找一个矩阵
,满足
是一个对角阵,并且对角元素按从大到小依次排列,那么
的前
行就是要寻找的基,用
的前
行组成的矩阵乘以
就使得
维降到了
维并满足上述优化条件,(
其中
的特征向量组成矩阵的逆矩阵,因为
为对称阵,故即特征矩阵的转置,前
行即为前
个特征向量)。

SVD

SVD 即奇异值分解,是特征分解在任意矩阵上的推广。因为特征分解,是有约束条件的,矩阵为方阵,并需存在可逆矩阵A,使得

为对角阵,同时还不能保证特征值一定是实数。因而在此基础上对
进行分解,因为
是实对称阵,必有一个正交矩阵使之对角化,不同特征值对应的特征向量一定正交,且特征值都是实数,特征向量都是实向量,因而能在任意矩阵上进行应用。

推导过程:对于任意矩阵,

阶对称矩阵,可以将之作特征分解:
;这个时候我们得到一组正交基,
其特征值为
,即
; 此时可以推导出
也是一组正交基,即
,只是没有标准化,进一步标准化,令
,将向量组
扩充为
中的标准正交基
。则:
进而推出
这就表明任意的矩阵A是可以分解成三个矩阵,
表明了原始域的标准正交基,
表示经过
变换后的标准正交基,
表示了
中的向量与
中相对应向量之间的关系。

在大部分介绍中,一般会这样求解,若

,
,则矩阵
的奇异值分解为
。其中
为左奇异向量矩阵,
为右奇异向量矩阵,奇异值则是
的特征值一定非负);
的对角阵虽然大小不一样,但特征值却是一样的,只是高纬度的可能有重根。这样求解大部分情况是合适的,但当
为对称阵,若有负特征值,会产生误解,因为
,其特征向量与
相同,但又要保证奇异值为负,所以会产生一点bug。而通过
,则将负号转移到左奇异矩阵中了。

同样的,通过SVD,矩阵也能写成秩逼近的形式,

其中,任意一个
是原矩阵的一个秩逼近,所带来的误差是剩余奇异值的平方和。进而可以通过保留前
个秩逼近,来对矩阵进行降维和压缩。如给一个很大的矩阵
需要储存
个元素,但如果用
个秩和逼近,则只需存储
个元素,即
个奇异值,
个左奇异向量元素,
个右奇异向量元素。

PCA与SVD相似之处:

  1. PCA主要是针对
    ,即
    的协方差举证进行特征分解。
  2. SVD则是针对
    进行奇异值分解,算的是
    的特征值和特征向量,缺少了系数
    。从求解方面来说SVD与PCA是等价的。

不同之处:

PCA 是寻找

的主要的特征向量作为基向量,对
进行投影,达到降维的目的,即
。而SVD则是通过保留比较大的奇异值和奇异向量,用秩逼近的形式来近似原矩阵,从而达到降维的目的,虽然殊途同归,但思想是不一样的。

SVD的优点:一般

的维度很高,
的计算量很大,方阵的特征值分解计算效率不高,SVD除了特征值分解这种求解方式外,还有更高效且更准确的迭代求解法,避免了
的计算

SVD 应用实例

对lena进行图片压缩:

c98a4d6b20acffbe63f19b4b1796c279.png

代码如下(网上搜寻),该图片实质可以看成(512,512,4)的一个3维矩阵,每一个元素为4个元素的数组,表示红,绿,蓝,灰度。

import numpy as np
import os
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib as mpl
from pprint import pprint

def restore(sigma, u, v, K):  # 奇异值、左特征向量、右特征向量
    m = u.shape[0]
    n = v.shape[0]
    a = np.zeros((m, n))

    for k in range(K):
        uk = u[:, k].reshape(m, 1)
        vk = v[k].reshape(1, n)
        a += sigma[k] * np.dot(uk, vk)  #    通过秩逼近,近似原矩阵

    # clip这个函数将将数组中的元素限制在a_min, a_max之间,
    # 大于a_max的就使得它等于 a_max,小于a_min,的就使得它等于a_min
    a = a.clip(0, 255)
    return np.rint(a).astype('uint8')


if __name__ == "__main__":
    print(os.getcwd())

    A = Image.open("./lena.jpg", 'r')
    print(A)

    output_path = './lena_output'
    if not os.path.exists(output_path):
        os.mkdir(output_path)

    a = np.array(A)
    #进行SVD分解
    u_r, sigma_r, v_r = np.linalg.svd(a[:, :, 0])
    u_g, sigma_g, v_g = np.linalg.svd(a[:, :, 1])
    u_b, sigma_b, v_b = np.linalg.svd(a[:, :, 2])

    plt.figure(figsize=(8, 8), facecolor='w')

    # matploblib 解决中文显示的问题
    mpl.rcParams['font.sans-serif'] = ['simHei']
    mpl.rcParams['axes.unicode_minus'] = False

    # 分别获取1到num个奇异值恢复图像
    num = 20
    sigma = 0
    for k in range(1, num+1):
        R = restore(sigma_r, u_r, v_r, k)
        G = restore(sigma_g, u_g, v_g, k)
        B = restore(sigma_b, u_b, v_b, k)
        sigma += sigma_r[k]
        # (512, 512)
        # 增加了一个维度,这个维度上的索引0,1,2分别存放R,G,B
        # axis从0开始索引,2表示第三个维度
        I = np.stack((R, G, B), axis=2)
        # (512,512,3)
        print(I.shape)

        # 将array保存为三通道彩色图像
        Image.fromarray(I).save('%s/svd_%d.png' % (output_path, k))

        # 按照三行四列显示前12图像
        if k <= 12:
            plt.subplot(3, 4, k)
            plt.imshow(I)
            plt.axis('off')
            plt.title('奇异值个数:%d' % k)
    print (sigma/sum(sigma_r)) 
    plt.suptitle('SVD与图像分解', fontsize=20)
    plt.tight_layout(0.3, rect=(0, 0, 1, 0.92))
    plt.show()

输出图片为:

3da721a06505f8d65977a6982910b773.png

前12个奇异向量已经能很好的保存图片的特征,在512个奇异值中,前20个奇异值(3.9%)所占比为0.29956,奇异值的贡献度减少的很快,一般前10%的奇异值就有99%以上的贡献度。

在实战过程中,想到既然是方阵,用特征值分解是什么样子,因而将代码进行了修改

u_r, sigma_r, v_r = np.linalg.svd(a[:, :, 0])
u_g, sigma_g, v_g = np.linalg.svd(a[:, :, 1])
u_b, sigma_b, v_b = np.linalg.svd(a[:, :, 2])


更改为:
sigma_r, u_r = np.linalg.eig(a[:, :, 0])
#求逆
v_r = np.matrix(u_r).I
sigma_g, u_g = np.linalg.eig(a[:, :, 1])
v_g = np.matrix(u_g).I
sigma_b, u_b = np.linalg.eig(a[:, :, 2])
v_b = np.matrix(u_b).I

但报错,因为特征值有虚数,故也可看出,特征值分解有其局限性。


下一章,学习LDA,MDS,NMF

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值