根据菜菜的课程进行整理,方便记忆理解
代码位置如下:
属性components_
现在我们了解了,V(k,n)是新特征空间,是我们要将原始数据进行映射的那些新特征向量组成的矩阵。我们用它来计算新的特征矩阵,但我们希望获取的毕竟是X_dr,为什么我们要把V(k,n)这个矩阵保存在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_people
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import numpy as np
- 实例化数据集,探索数据
- 这个地方会运行很长的时间,我们可以将数据先准备好,数据准备的过程在这:juejin.cn/post/708449…
# min_faces_per_person 每个人需要的最少的照片数
faces = fetch_lfw_people(min_faces_per_person=60)
# (1277,62,47) 1277张图片,高是62,宽是47
faces.images.shape
#怎样理解这个数据的维度?
faces.data.shape
# 行是样本
# 列是样本相关的所有特征
#换成特征矩阵之后,这个矩阵是什么样?
X = faces.data
- 看看图像什么样?将原特征矩阵进行可视化
#数据本身是图像,和数据本身只是数字,使用的可视化方法不同
#创建画布和子图对象
fig, axes = plt.subplots(3,8
,figsize=(8,4)
,subplot_kw = {"xticks":[],"yticks":[]} #不要显示坐标轴
)
fig
axes
"""
array([[<AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>,
<AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>],
[<AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>,
<AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>],
[<AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>,
<AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>]],
dtype=object)
"""
#不难发现,axes中的一个对象对应fig中的一个空格
#我们希望,在每一个子图对象中填充图像(共24张图),因此我们需要写一个在子图对象中遍历的循环
axes.shape
# (3, 8)
#二维结构,可以有两种循环方式,一种是使用索引,循环一次同时生成一列上的三个图
#另一种是把数据拉成一维,循环一次只生成一个图
#在这里,究竟使用哪一种循环方式,是要看我们要画的图的信息,储存在一个怎样的结构里
#我们使用 子图对象.imshow 来将图像填充到空白画布上
#而imshow要求的数据格式必须是一个(m,n)格式的矩阵,即每个数据都是一张单独的图
#因此我们需要遍历的是faces.images,其结构是(1277, 62, 47)
#要从一个数据集中取出24个图,明显是一次性的循环切片[i,:,:]来得便利
#因此我们要把axes的结构拉成一维来循环
axes.flat
#填充图像
for i, ax in enumerate(axes.flat):
ax.imshow(faces.images[i,:,:],cmap="gray") #选择色彩的模式
fig
- 建模降维,提取新特征空间矩阵
#原本有2900维,我们现在来降到150维
pca = PCA(150).fit(X)
# V是我们的新的特征空间
V = pca.components_
V.shape
# (150, 2914)
- 将新特征空间矩阵可视化
fig, axes = plt.subplots(3,8,figsize=(8,4),subplot_kw = {"xticks":[],"yticks":[]})
for i, ax in enumerate(axes.flat):
ax.imshow(V[i,:].reshape(62,47),cmap="gray")
比起降维前的数据,新特征空间可视化后的人脸非常模糊,这是因为原始数据还没有被映射到特征空间中。但是可以看出,整体比较亮的图片,获取的信息较多,整体比较暗的图片,却只能看见黑漆漆的一块。在比较亮的图片中,眼睛,鼻子,嘴巴,都相对清晰,脸的轮廓,头发之类的比较模糊。
新特征空间里的特征向量们,大部分是"五官"和"亮度"相关的向量,所以新特征向量上的信息肯定大部分是由原数据中和"五官"和"亮度"相关的特征中提取出来的。到这里,我们通过可视化新特征空间V,解释了一部分降维后的特征:虽然显示出来的数字看着不知所云,但画出来的图表示,这些特征是和”五官“以及”亮度“有关的。这也再次证明了,PCA能够将原始数据集中重要的数据进行聚集。
重要接口inverse_transform
神奇的接口inverse_transform,可以将我们归一化,标准化,甚至做过哑变量的特征矩阵还原回原始数据中的特征矩阵,这几乎在向我们暗示,任何有inverse_transform这个接口的过程都是可逆的。PCA应该也是如此。在sklearn中,我们通过让原特征矩阵X右乘新特征空间矩阵V(k,n)来生成新特征矩阵X_dr,那理论上来说,让新特征矩阵X_dr右乘V(k,n)的逆矩阵,就可以将新特征矩阵X_dr还原为X。
用人脸识别看PCA降维后的信息保存量
人脸识别是最容易的,用来探索inverse_transform功能的数据。我们先调用一组人脸数据X(m,n),对人脸图像进行绘制,然后我们对人脸数据进行降维得到X_dr,之后再使用inverse_transform(X_dr)返回一个X_inverse(m,n),并对这个新矩阵中的人脸图像也进行绘制。如果PCA的降维过程是可逆的,我们应当期待X(m,n)和X_inverse(m,n)返回一模一样的图像,即携带一模一样的信息。
- 导入需要的库和模块
from sklearn.datasets import fetch_lfw_people
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import numpy as np
- 导入数据,探索数据
faces = fetch_lfw_people(min_faces_per_person=60)
faces.images.shape
# (1348, 62, 47)
faces.data.shape
# (1348, 2914)
X = faces.data
- 建模降维,获取降维后的特征矩阵X_dr
pca = PCA(150)
X_dr = pca.fit_transform(X)
X_dr.shape
# (1348, 150)
- 将降维后矩阵用inverse_transform返回原空间
X_inverse = pca.inverse_transform(X_dr)
X_inverse.shape
- 将特征矩阵X和X_inverse可视化
fig, ax = plt.subplots(2,10,figsize=(10,2.5)
,subplot_kw={"xticks":[],"yticks":[]}
)
#和2.3.3节中的案例一样,我们需要对子图对象进行遍历的循环,来将图像填入子图中
#那在这里,我们使用怎样的循环?
#现在我们的ax中是2行10列,第一行是原数据,第二行是inverse_transform后返回的数据
#所以我们需要同时循环两份数据,即一次循环画一列上的两张图,而不是把ax拉平
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")
这两组数据可视化后,由降维后再通过inverse_transform转换回原维度的数据画出的图像和原数据画的图像大致相似,但原数据的图像明显更加清晰。这说明,inverse_transform并没有实现数据的完全逆转。 这是因为,在降维的时候,部分信息已经被舍弃了,X_dr中往往不会包含原数据100%的信息,所以在逆转的时候,即便维度升高,原数据中已经被舍弃的信息也不可能再回来了。所以,降维不是完全可逆的。
用PCA做噪音过滤
-
降维目的
- 希望抛弃掉对模型带来负面影响的特征,带有效信息的特征的方差应该是远大于噪音的,所以相比噪音,有效的特征所带的信息应该不会在PCA过程中被大量抛弃
-
inverse_transform能够在不恢复原始数据的情况下,将降维后的数据返回到原本的高维空间,即是说能够实现”保证维度,但去掉方差很小特征所带的信息“。利用inverse_transform的这个性质,我们能够实现噪音过滤。
-
导入所需要的库和模块
from sklearn.decomposition import PCA
from sklearn.datasets import load_digits
import matplotlib.pyplot as plt
import numpy as np
- 导入数据,探索数据
digits = load_digits()
digits.data.shape
# (1797, 64)
data.head()
- 定义画图函数
# 定义绘图的函数
def plot_digits(data):
fig,axes = plt.subplots(4,10,figsize=(10,4),subplot_kw={"xticks":[],"yticks":[]})
for i,ax in enumerate(axes.flat):
ax.imshow(data[i].reshape(8,8),cmap="binary")
plot_digits(digits.data)
- 为数据加上噪音
# 为数据加上噪音
np.random.RandomState(42)
noisy = np.random.normal(digits.data,2)
plot_digits(noisy)
- 降维
pca = PCA(0.5).fit(noisy)
X_dr = pca.transform(noisy)
X_dr.shape
# (1797, 6)
- 逆转降维结果,实现降噪
# 进行降噪
without_noise = pca.inverse_transform(X_dr)
plot_digits(without_noise)
重要接口,参数和属性总结
- 参数
- n_components
- svd_solver
- random_state,
- 属性:
- components_
- explained_variance_
- explained_variance_ratio_
- 接口
- fit
- transform
- fit_transform
- inverse_transform