写点什么

主成分分析 PCA 与奇异值分解 SVD- 降维后的矩阵 components_ & inverse_transform

  • 2022-11-20
    山东
  • 本文字数:4565 字

    阅读完需:约 15 分钟

V(k,n)这个矩阵保存在.components_这个属性当中我们之前谈到过 PCA 与特征选择的区别,即特征选择后的特征矩阵是可解读的,而 PCA 降维后的特征矩阵式不可解读的:PCA 是将已存在的特征进行压缩,降维完毕后的特征不是原本的特征矩阵中的任何一个特征,而是通过某些方式组合起来的新特征。通常来说,在新的特征矩阵生成之前,我们无法知晓 PCA 都建立了怎样的新特征向量,新特征矩阵生成之后也不具有可读性,我们无法判断新特征矩阵的特征是从原数据中的什么特征组合而来,新特征虽然带有原始数据的信息,却已经不是原数据上代表着的含义了。但是其实,在矩阵分解时,PCA 是有目标的:在原有特征的基础上,找出能够让信息尽量聚集的新特征向量。在 sklearn 使用的 PCA 和 SVD 联合的降维方法中,这些新特征向量组成的新特征空间其实就是 V(k,n)。当 V(k,n)是数字时,我们无法判断 V(k,n)和原有的特征究竟有着怎样千丝万缕的数学联系。但是,如果原特征矩阵是图像,V(k,n)这个空间矩阵也可以被可视化的话,我们就可以通过两张图来比较,就可以看出新特征空间究竟从原始数据里提取了什么重要的信息


我们以人脸识别中属性 components_为例


from sklearn.datasets import fetch_lfw_peoplefrom sklearn.decomposition import PCAimport matplotlib.pyplot as pltimport numpy as np
faces = fetch_lfw_people(min_faces_per_person=60) # 实例化# min_faces_per_person:在数据集中每个人取出n张脸图
print(faces.DESCR) # 需要的话自己看吧
faces.data.shape# 每一行是样本,即1348个样本# 列式样本相关的所有特征,即2914个特征# 因此,可视化这一部分没有意义---(1348, 2914)
faces.images.shape # 注意是三维的# 严格来说,这个才是图像的特征矩阵# 1348是矩阵中图像的个数# 62是每个图像的特征矩阵的行,行上有62个像素# 42是每个图像的特征矩阵的列,列上有47个像素# 之前的2914=62*47,一张图就要有一行一列,可以看做一个表# 因此我们可以对62*47这一部分进行可视化---(1348, 62, 47)
X = faces.data
# 之前的plt.figure是无法画多个图的fig, axes = plt.subplots(4,5 # 子图4行5列。现在直接执行会给4*5=20个框,其他什么也没有 # 4,5表示要画20个图,在这里也就是20张脸,可以写其他数 ,figsize=(8,4) # 画布的尺寸和比例和大小,8代表行,4代表列 # figsize的对象是figure不是某一个subplot ,subplot_kw={"xticks":[],"yticks":[]} # 不显示坐标轴 )
复制代码



fig # 生成的一张纸
复制代码



axes # 4行5列,matplotlib的对象# axes中的一个对象对应fig中的一个空格---array([[<matplotlib.axes._subplots.AxesSubplot object at 0x0000011CA315B198>,        <matplotlib.axes._subplots.AxesSubplot object at 0x0000011CA52DF2B0>,        <matplotlib.axes._subplots.AxesSubplot object at 0x0000011CA530D828>,……# 4*5的array,里面都是matplotlib对象
axes.shape---(4, 5)
axes[0,0] # 指定那个对象和一般的矩阵一样
axes[0,0].imshow(faces.images[0,:,:])# 生成一个更新过的matplotlib对象# 我们只是改变了axes对象,在这里执行完这个cell显示图像# 只有再次看fig画布才看看到效果# imshow要求的数据格式必须是一个(m,n)格式的矩阵,即每个数据都是一张单独的图,我们需要遍历的是faces.images,其结构是(1277, 62, 47)fig
复制代码



我们要花个图,二维结构,可以有两种循环方式,一种是使用索引,循环一次同时生成一列上的三个图;另一种是把数据拉成一维,循环一次只生成一个图。这里我们选择后者


[*enumerate(axes.flat)]---[(0, <matplotlib.axes._subplots.AxesSubplot at 0x11ca315b198>), (1, <matplotlib.axes._subplots.AxesSubplot at 0x11ca52df2b0>), (2, <matplotlib.axes._subplots.AxesSubplot at 0x11ca530d828>), (3, <matplotlib.axes._subplots.AxesSubplot at 0x11ca5332da0>),# flat降维成一维# enumerate每个对象带序号构成一个元组,因为是惰性对象,所以在列表中可以用*打开
复制代码


填充所有子图


for i, ax in enumerate(axes.flat):    ax.imshow(faces.images[i,:,:],cmap='gray') # 选择色彩的模式,原本显示绿色,设置显示黑白
fig
复制代码


降维,并获取 components_


pca = PCA(150).fit(X) # X = faces.data,注意faces.data.shape为(1348, 2914)V = pca.components_ # 是V^T而不是VV.shape---(150, 2914)
V[0].shape # 这一行的shape,意义不大---(2914,)
V[0].reshape(62,47).shape # 这里实际上是被选中的降维所用的特征向量进行reshape---(62, 47)
fig, axes = plt.subplots(5,10,figsize=(8,4),subplot_kw = {"xticks":[],"yticks":[]})for i,ax in enumerate(axes.flat): ax.imshow(V[i,:].reshape(62,47),cmap='gray')# 这里对V进行可视化,显然画的不是图片,而是特征向量# 个人理解,这里的V是150*2914,150个特征向量也就是150个最重要的点# X*V,也就是通过X对V这150个特征组成的图像进行加权得到降维后的图像# 越靠前的V越重要,越靠后的区分度越小# 前几个特征也就是这个前几个图像主要关注了五官的位置,光照等
复制代码



可以看出,比起降维前的数据,新特征空间可视化后的人脸非常模糊,这是因为原始数据还没有被映射到特征空间中。但是可以看出,整体比较亮的图片,获取的信息较多,整体比较暗的图片,却只能看见黑漆漆的一块。在比较亮的图片中,眼睛,鼻子,嘴巴,都相对清晰,脸的轮廓,头发之类的比较模糊。这说明,新特征空间里的特征向量们,大部分是"五官"和"亮度"相关的向量,所以新特征向量上的信息肯定大部分是由原数据中和"五官"和"亮度"相关的特征中提取出来的。到这里,我们通过可视化新特征空间 V,解释了一部分降维后的特征:虽然显示出来的数字看着不知所云,但画出来的图表示,这些特征是和”五官“以及”亮度“有关的。这也再次证明了,PCA 能够将原始数据集中重要的数据进行聚集。


一片不错的文章可以看一下作者:雪球球链接:图像处理之基础---矩阵和特征向量的 几何意义 - 雪球球 - 博客园 (cnblogs.com)



这里关于白化其实在 sklearn 里就是一个参数


PCA(    ['n_components=None', 'copy=True', 'whiten=False', "svd_solver='auto'", 'tol=0.0', "iterated_power='auto'", 'random_state=None'],)# whiten:是否PCA后进行白化
复制代码


个人理解,我们 PCA 是求的,其中是特征向量组成的矩阵,白化 PCA 就是,这里就是协方差矩阵的特征值



因此我们说数据在经过 PCA 白化以后,其协方差矩阵是一个单位矩阵,各维度不线性相关,且每个维度方差都是 1


各维度不相关不需要白化,只进行 PCA 也能达到,因为新的特征向量本身就是互相垂直的


作者:王亮链接:白化变换:PCA白化、ZCA白化 - 知乎 (zhihu.com)

这里我们暂时不关心 ZCA(感觉 ZCA 的图也不太对)

inverse_transform

在特征工程课中,我们学到了接口inverse_transform,可以将我们归一化,标准化,甚至做过哑变量的特征矩阵还原回原始数据中的特征矩阵,这几乎在向我们暗示,任何有inverse_transform这个接口的过程都是可逆的。PCA 应该也是如此。在 sklearn 中,我们通过让原特征矩阵 X 右乘新特征空间矩阵来生成新特征矩阵,那理论上来说,让新特征矩阵右乘 V(k,n)的逆矩阵 $V^{-1}{((k,n))}X{dr}$还原为 X。


用上面人脸识别看 PCA 降维后的信息保存量


from sklearn.datasets import fetch_lfw_peoplefrom sklearn.decomposition import PCAimport matplotlib.pyplot as pltimport numpy as np
faces = fetch_lfw_people(min_faces_per_person=60)X = faces.dataX.shape---(1348, 2914)
pca = PCA(150)X_dr = pca.fit_transform(X)X_dr.shape---(1348, 150)
X_inverse = pca.inverse_transform(X_dr)X_inverse.shape# 期待X_inverse和原数据有相同的结构,如果相同,我们就说inverse_transform实现了降维过程的逆转# 维度相同,即使inverse_transform将降维后的数据映射回原数据所在的维度空间中,但信息已经损失了---(1348, 2914)
fig, ax = plt.subplots(2,10,figsize=(10,2.5) ,subplot_kw={'xticks':[],'yticks':[]} )for i in range(10): ax[0,i].imshow(faces.images[i,:,:],cmap='binary_r') ax[1,i].imshow(X_inverse[i].reshape(62,47),cmap='binary_r')fig# 第一行是原数据,第二行是inverse_transform后返回的数据---
复制代码


可以明显看出,这两组数据可视化后,由降维后再通过inverse_transform转换回原维度的数据画出的图像和原数据画的图像大致相似,但原数据的图像明显更加清晰。这说明,inverse_transform并没有实现数据的完全逆转。这是因为,在降维的时候,部分信息已经被舍弃了,中往往不会包含原数据 100%的信息,所以在逆转的时候,即便维度升高,原数据中已经被舍弃的信息也不可能再回来了。所以,降维不是完全可逆的。inverse_transform的功能,是基于中的数据进行升维,将数据重新映射到原数据所在的特征空间中,而并非恢复所有原有的数据。但同时,我们也可以看出,降维到 300 以后的数据,的确保留了原数据的大部分信息,所以图像看起来,才会和原数据高度相似,只是稍稍模糊罢了。


使用一下可能会更加明确

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.decomposition import PCA

gra = plt.imread(r'C:\Users\DELL\Desktop\test.png')
fig,axes = plt.subplots(4,figsize=(100, 100))

# imshow
axes[0].imshow(gra[:,:,0],cmap='binary_r')

X = range(0,gra.shape[1])
Y = range(gra.shape[0],0,-1)
XX,YY = np.meshgrid(X,Y)

# scatter
axes[1].scatter(XX,YY,c=gra[:,:,0],cmap='binary_r')
axes[1].set_aspect(1)

# PCA
dataset = gra[:,:,0]
pca = PCA(10)
dataset_dr = pca.fit_transform(dataset)
dataset_dr = pca.inverse_transform(dataset_dr) # 感觉一张图片降维没有意义,这是自己的325行进行降维

axes[2].imshow(dataset_dr,cmap='binary_r')

# 基于pca.components_实现降维以及还原
dataset = gra[:,:,0]
dataset_dr = np.dot(dataset - dataset.mean(axis=0),pca.components_.T)
dataset_dr = np.matmul(dataset_dr,pca.components_) + dataset.mean(axis=0)

axes[3].imshow(dataset_dr,cmap='binary_r')

for i in axes:
 i.set_xticks([])
 i.set_yticks([])


视频作者:菜菜TsaiTsai链接:【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibili


发布于: 刚刚阅读数: 4
用户头像

跃入人海 2022-09-14 加入

还未添加个人简介

评论

发布
暂无评论
主成分分析PCA与奇异值分解SVD-降维后的矩阵components_ & inverse_transform_Python_烧灯续昼2002_InfoQ写作社区