在原有的课件上面添加了自己的理解,代码等因素,还有一些自己的测试等
在降维过程中,我们会减少特征的数量,这意味着删除数据,数据量变少则表示模型可以获取的信息会变少,模型的表现可能会因此受影响。同时,在高维数据中,必然有一些特征是不带有有效的信息的(比如噪音),或者有一些特征带有的信息和其他一些特征是重复的(比如一些特征可能会线性相关)。我们希望能够找出一种办法来帮助我们衡量特征上所带的信息量,让我们在降维的过程中,能够即减少特征的数量,又保留大部分有效信息——将那些带有重复信息的特征合并,并删除那些带无效信息的特征等等——逐渐创造出能够代表原特征矩阵大部分信息的,特征更少的,新特征矩阵。
特征工程中有一种重要的特征选择方法:方差过滤,如果一个特征的方差很小,则意味着这个特征上很可能有大量取值都相同(比如90%都是1,只有10%是0,甚至100%是1),那这一个特征的取值对样本而言就没有区分度,这种特征就不带有有效信息。从方差的这种应用就可以推断出,如果一个特征的方差很大,则说明这个特征上带有大量的信息。
因此,在降维中,因此,在降维中,PCA使用的信息量衡量指标,就是样本方差(公式在下面),又称可解释性方差,方差越大,特征所带的信息量越多。
V a r = 1 n − 1 ∑ i = 1 n ( x i − x ^ ) 2 Var=\frac{1}{n-1}\sum_{i=1}^{n}(x_i-\hat{x})^2 Var=n−11i=1∑n(xi−x^)2
为什么方差计算公式中除以的是n-1?
》》》目的是为了得到样本方差的无偏估计,详细推导讲解请看样本方差公式为什么除以n-1
首先来看一个二维数据的降维过程
我们现在有一组简单的数据,有特征x1和x2,三个样本数据的坐标点分别为(1,1),(2,2),(3,3)。我们可以让x1和x2分别作为两个特征向量,这组数据现在每个特征的均值都为2,方差都为1。
每个特征的数据一模一样,因此方差也都为1,数据的方差总和表示为:
V a r = ( 1 − 2 ) 2 + ( 2 − 2 ) 2 + ( 3 − 2 ) 2 3 − 1 = 2 Var=\frac{(1-2)^2+(2-2)^2+(3-2)^2}{3-1}=2 Var=3−1(1−2)2+(2−2)2+(3−2)2=2
我们的目的是,只用一个特征向量来描述这组数据,即将二维数据降为一维数据,并且尽可能地保留信息量,即让数据的总方差尽量靠近2。
进行操作,将原本的直角坐标系逆时针旋转45°,形成了新的特征向量x1* 和 x2* 组成的新平面,在这个新平面中,三个样本数据的坐标点可以表示为 ( 2 , 0 ) , ( 2 2 , 0 ) , ( 3 2 , 0 ) (\sqrt{2},0),(2\sqrt{2},0),(3\sqrt{2},0) (2,0),(22,0),(32,0)。可以注意到, x 2 ∗ x2^* x2∗上的数值此时都变成了0,因此 x 2 ∗ x2^* x2∗ 明显不带有任何有效信息了(此时 x 2 ∗ x2^* x2∗的方差也为0了),此时,x1*特征上的数据均值是 ( 2 2 , 0 ) (2\sqrt{2},0) (22,0) 方差则可表示成:
V a r = ( 2 − 2 2 ) 2 + ( 2 2 − 2 2 ) 2 + ( 3 2 − 2 2 ) 2 3 − 1 = 2 Var=\frac{(\sqrt2-2\sqrt2)^2+(2\sqrt2-2\sqrt2)^2+(3\sqrt2-2\sqrt2)^2}{3-1}=2 Var=3−1(2−22)2+(22−22)2+(32−22)2=2
x 2 ∗ x2^* x2∗上的数据均值为0,方差也为0。
此时,我们根据信息含量的排序,取信息含量最大的一个特征,因为我们想要的是一维数据。所以我们可以将x2* 删除,同时也删除图中的x2* 特征向量,剩下的x1*就代表了曾经需要两个特征来代表的三个样本点。
通过旋转原有特征向量组成的坐标轴来找到新特征向量和新坐标平面,我们将三个样本点的信息压缩到了一条直线上,实现了二维变一维,并且尽量保留原始数据的信息。一个成功的降维,就实现了。
在步骤3当中,我们用来找出n个新特征向量,让数据能够被压缩到少数特征上并且总信息量不损失太多的技术就是矩阵分解。
PCA和SVD是两种不同的降维算法,但他们都遵从上面的过程来实现降维,只是两种算法中矩阵分解的
方法不同,信息量的衡量指标不同罢了。
PCA
PCA使用方差作为信息量的衡量指标,并且特征值分解来找出空间V。降维时,它会通过一系列数学的神秘操作(比如说,产生协方差矩阵 1 n X X T \frac{1}{n}XX^T n1XXT)将特征矩阵X分解为以下三个矩阵,其中 Q Q Q和 Q − 1 Q^{-1} Q−1是辅助的矩阵,Σ是一个对角矩阵(即除了对角线上有值,其他位置都是0的矩阵),其对角线上的元素就是方差。
X → 数 学 的 神 秘 宇 宙 → Q Σ Q − 1 X\rightarrow数学的神秘宇宙\rightarrow QΣQ^{-1} X→数学的神秘宇宙→QΣQ−1
降维完成之后,PCA找到的每个新特征向量就叫做“主成分”,而被丢弃的特征向量被认为信息量很少,这些信息很可能就是噪音。
SVD
SVD使用奇异值分解来找出空间V,其中Σ也是一个对角矩阵,不过它对角线上的元素是奇异值,这也是SVD中用来衡量特征上的信息量的指标。U和 V T V^T VT分别是左奇异矩阵和右奇异矩阵,也都是辅助矩阵。
X → 另 一 个 数 学 的 神 秘 宇 宙 → U Σ V T X\rightarrow 另一个数学的神秘宇宙 \rightarrow UΣV^T X→另一个数学的神秘宇宙→UΣVT
PCA ,SVD
在数学原理中,无论是PCA和SVD都需要遍历所有的特征和样本来计算信息量指标。并且在矩阵分解的过程之中,会产生比原来的特征矩阵更大的矩阵,比如原数据的结构是(m,n),在矩阵分解中为了找出最佳新特征空间V,可能需要产生(n,n),(m,m)大小的矩阵,还需要产生协方差矩阵去计算更多的信息。
降维算法的计算量很大,运行比较缓慢,但无论如何,依然是机器学习领域的宠儿。
PCA和特征选择的不同
特征工程中有三种方式:特征提取,特征创造和特征选择
特征选择是从已存在的特征中选取携带信息最多的,选完之后的特征依然具有可解释性,我们依然知道这个特征在原数据的哪个位置,代表着原数据上的什么含义。
而PCA,是将已存在的特征进行压缩,降维完毕后的特征不是原本的特征矩阵中的任何一个特征,而是通过某些方式组合起来的新特征。通常来说,在新的特征矩阵生成之前,我们无法知晓PCA都建立了怎样的新特征向量,新特征矩阵生成之后也不具有可读性,我们无法判断新特征矩阵的特征是从原数据中的什么特征组合而来,新特征虽然带有原始数据的信息,却已经不是原数据上代表着的含义了。以PCA为代表的降维算法因此是特征创造(feature creation,或feature construction)的一种。
PCA一般不适用于探索特征和标签之间的关系的模型(如线性回归),因为无法解释的新特征和标签之间的关系不具有意义。在线性回归模型中,我们使用特征选择。
n_components是我们降维后需要的维度,即降维后需要保留的特征数量,降维流程中第二步里需要确认的k值,一般输入**[0, min(X.shape)]范围中的整数**,如果留下的特征太多,就达不到降维的效果,如果留下的特征太少,那新特征向量可能无法容纳原始数据集中的大部分信息,因此,n_components既不能太大也不能太小。
K值类似于KNN中的K和随机森林中的n_estimators,这是一个需要我们人为去确认的超参数,并且我们设定的数字会影响到模型的表现
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris #鸢尾花数据集
from sklearn.decomposition import PCA #PCA
import pandas as pd
import numpy as np
iris = load_iris()
x = iris.data
y = iris.target
x.shape
>(150, 4)
由上可以观察出,这个数据是一个二维数组,四维的特征矩阵
转成DataFrame观察数据
pd.DataFrame(x)
pca = PCA(n_components=2)
pca = pca.fit(x)
x_dr = pca.transform(x)
#可以直接一步完成
#x_dr = pca.fit_transform(x)
查看降维之后的数据,由四维降维到了二维
x_dr
要将三种鸢尾花的数据分布显示在二维平面坐标系中,对应的两个坐标(两个特征向量)应该是三种鸢尾花降维后的x1和x2,怎样才能取出三种鸢尾花下不同的x1和x2呢?
y有三种取值,分别是0,1,2代表三种分类
可以通过判断y==0,1,2分别来找出x降维后对应的行,然后取出对应的x和y值
x_dr[y == 0,0] #第0分类中降维之后的x值
x_dr[y == 0,1] #第0分类中降维之后的y值
plt.figure()
plt.scatter(x_dr[y == 0,0], x_dr[y == 0,1], c='red', label=iris.target_names[0])
plt.scatter(x_dr[y == 1,0], x_dr[y == 1,1], c='black', label=iris.target_names[1])
plt.scatter(x_dr[y == 2,0], x_dr[y == 2,1], c='blue', label=iris.target_names[2])
plt.legend()
plt.title('PCA of IRIS dataset')
plt.show()
可视化还可以使用循环来进行画图
color = ['red','black','blue']
for i in [0,1,2]:
plt.scatter(x_dr[y == i,0]
,x_dr[y == i,1]
,c=color[i]
,alpha=.4
,label=iris.target_names[i]
)
plt.legend()
plt.title('PCA of IRIS dataset')
plt.show()
鸢尾花的分布被展现在我们眼前了,明显这是一个分簇的分布,并且每个簇之间的分布相对比较明显,也许versicolor和virginia这两种花之间会有一些分类错误,但setosa肯定不会被分错。这样的数据很容易分类,可以遇见,KNN,随机森林,神经网络,朴素贝叶斯,Adaboost这些分类器在鸢尾花数据集上,未调整的时候都可以有95%上下的准确率
#降到几维就有几个可解释性方差
pca.explained_variance_
#特征的信息量占原数据的占比
pca.explained_variance_ratio_
pca.explained_variance_ratio_.sum()
当参数n_components中不填写任何值,则默认返回min(X.shape)个特征,一般来说,样本量都会大于特征数目,所以什么都不填就相当于转换了新特征空间,但没有减少特征的个数。
一般来说,不会使用这种输入方式。但我们却可以使用这种输入方式来画出累计可解释方差贡献率曲线,以此选择最好的n_components的整数取值。
累积可解释方差贡献率曲线是一条以降维后保留的特征个数为横坐标,降维后新特征矩阵捕捉到的可解释方差贡献率为纵坐标的曲线,能够帮助我们决定n_components最好的取值。
首先说明,np.cumsum()是用来做累加的
np.cumsum([1,2,3,4])
pca_line = PCA().fit(x)
#对使用不同个数的特征所带信息量的值进行累加
plt.plot([1,2,3,4],np.cumsum(pca_line.explained_variance_ratio_))
plt.xticks([1,2,3,4])
plt.xlabel("number of components after dimension reduction")
plt.ylabel("cumulative explained variance ratio")
plt.show()
通常来说选择经过拐点的之后线就变得平滑的这个拐点,作为我们所要的特征数量
除了输入整数,n_components还可以用最大似然估计(maximum likelihoodestimation)自选超参数的方法,输入“mle”作为n_components的参数输入,就可以调用这种方法,但是这种方法如果使用的话在数据大的时候,花费的时间也是非常大的。
pca_mle = PCA(n_components='mle')
pca_mle.fit(x)
x_mle = pca_mle.transform(x)
x_mle
#所带总的信息量的占比
pca_mle.explained_variance_ratio_.sum()
输入**[0,1]之间的浮点数**,并且让参数svd_solver =‘full’,表示希望降维后的总解释性方差占比大于n_components指定的百分比,即是说,希望保留百分之多少的信息量
比如说,如果我们希望保留97%的信息量,就可以输入n_components = 0.97,PCA会自动选出能够让保留的信息量超过97%的特征数量。
pca_f = PCA(n_components=0.97,svd_solver='full')
x_f = pca_f.fit_transform(x)
pca_f.explained_variance_ratio_.sum()
在上面提到的svd_solver是奇异值分解器的意思,为什么PCA算法下面会有有关奇异值分解的参数?不是两种算法么?
PCA和SVD涉及了大量的矩阵计算,两者都是运算量很大的模型,但其实,SVD有一种惊人的数学性质,即是它可以跳过数学神秘的宇宙,不计算协方差矩阵,直接找出一个新特征向量组成的n维空间,而这个n维空间就是奇异值分解后的右矩阵 V T V^T VT
传 统 印 象 中 的 S V D : X → 数 学 的 神 秘 宇 宙 → U Σ V T 传统印象中的SVD: X \rightarrow 数学的神秘宇宙 \rightarrow UΣV^T 传统印象中的SVD:X→数学的神秘宇宙→UΣVT
其 实 的 S V D : X → 一 个 比 P C A 简 化 非 常 多 的 数 学 过 程 → V T 其实的SVD:X \rightarrow 一个比PCA简化非常多的数学过程 \rightarrow V^T 其实的SVD:X→一个比PCA简化非常多的数学过程→VT
右奇异矩阵 V T V^T VT有着如下性质:
X d r = X ∗ V [ : k ] T X_{dr}=X*V[:k]^T Xdr=X∗V[:k]T
k就是n_components,是我们降维后希望得到的维度。若X为(m,n)的特征矩阵, V T V^T VT就是结构为(n,n)的矩阵,取这个矩阵的前k行(进行切片),即将V转换为结构为(k,n)的矩阵。而 V ( k , n ) T V_{(k,n)}^T V(k,n)T与原特征矩阵X相乘,即可得到降维后的特征矩阵X_dr。
奇异值分解可以不计算协方差矩阵等等结构复杂计算冗长的矩阵,就直接求出新特征空间和降维后的特征矩阵。
简而言之,SVD在矩阵分解中的过程比PCA简单快速,虽然两个算法都走一样的分解流程,但SVD可以作弊耍赖直接算出V。但是遗憾的是,SVD的信息量衡量指标比较复杂,要理解”奇异值“远不如理解”方差“来得容易。
sklearn将降维流程拆成了两部分:
实现了用SVD的性质减少计算量,却让信息量的评估指标是方差,具体流程如下图:
sklearn将fit和transform分开的好处是,假如有两份数据,我是用一份数据进行fit,构建了一个特征向量空间,这个时候transform可以传入不同的数据,比如可以传入第二份数据而不用重新fit构建一个新的特征向量空间,大大缩短了数学的计算量
通过SVD和PCA的合作,sklearn实现了一种计算更快更简单,但效果却很好的“合作降维“。很多人理解SVD,是把SVD当作PCA的一种求解方法,其实指的就是在矩阵分解时不使用PCA本身的特征值分解,而使用奇异值分解来减少计算量。这种方法确实存在,但在sklearn中,矩阵U和Σ虽然会被计算出来(同样也是一种比起PCA来说简化非常多的数学过程,不产生协方差矩阵),但完全不会被用到,也无法调取查看或者使用,因此我们可以认为,U和Σ在fit过后就被遗弃了
奇异值分解追求的仅仅是V,只要有了V,就可以计算出降维后的特征矩阵。在transform过程之后,fit中奇异值分解的结果除了V(k,n)以外,就会被舍弃,而V(k,n)会被保存在属性components_ 当中,可以调用查看:
PCA(2).fit(X).components_
PCA(2).fit(x).components_.shape
返回的是 V ( k , n ) V(k,n) V(k,n)也就是新的特征向量空间,k就是降维后留下的特征个数,n是原数据的特征个数
这里的2代表的是需要降到的维度,4代表的是原数据特征的个数,
svd_solver
参数svd_solver是在降维过程中,用来控制矩阵分解的一些细节的参数。有四种模式可选:“auto”, “full”, “arpack”,“randomized”,默认”auto",通常就直接使用auto,计算不出来使用randomized。
"auto":
基于X.shape和n_components的默认策略来选择分解器:如果输入数据的尺寸大于500x500且要提
取的特征数小于数据最小维度min(X.shape)的80%,就启用效率更高的”randomized“方法。否则,精确完整的SVD将被计算,截断将会在矩阵被分解完成后有选择地发生
"full":
从scipy.linalg.svd中调用标准的LAPACK分解器来生成精确完整的SVD,适合数据量比较适中,计算时间充足的情况,生成的精确完整的SVD的结构为: U ( m , m ) , Σ ( m , n ) , V ( n , n ) T U_{(m,m)},Σ_{(m,n)},V_{(n,n)}^T U(m,m),Σ(m,n),V(n,n)T
"arpack":
从scipy.sparse.linalg.svds调用ARPACK分解器来运行截断奇异值分解(SVD truncated),分解时就
将特征数量降到n_components中输入的数值k,可以加快运算速度,适合特征矩阵很大的时候,但一般用于特征矩阵为稀疏矩阵的情况,此过程包含一定的随机性。截断后的SVD分解出的结构为: U ( m , k ) , Σ ( k , k ) , V ( n , n ) T U_{(m,k)},Σ_{(k,k)},V_{(n,n)}^T U(m,k),Σ(k,k),V(n,n)T
"randomized":
通过Halko等人的随机方法进行随机SVD。在"full"方法中,分解器会根据原始数据和输入的
n_components值去计算和寻找符合需求的新特征向量,但是在"randomized"方法中,分解器会先生成多个随机向量,然后一一去检测这些随机向量中是否有任何一个符合我们的分解需求,如果符合,就保留这个随机向量,并基于这个随机向量来构建后续的向量空间。这个方法已经被Halko等人证明,比"full"模式下计算快很多,并且还能够保证模型运行效果。适合特征矩阵巨大,计算量庞大的情况
random_state
在参数svd_solver的值为"arpack" or "randomized"的时候生效,可以控制这两种SVD模式中的随机模式。
通常我们就选用”auto“,不必对这个参数纠结太多。
PCA与特征选择的区别:
特征选择后的特征矩阵是可解读的,而PCA降维后的特征矩阵式不可解读的:PCA是将已存在的特征进行压缩,降维完毕后的特征不是原本的特征矩阵中的任何一个特征,而是通过某些方式组合起来的新特征
在新的特征矩阵生成之前,我们无法知晓PCA都建立了怎样的新特征向量,新特征矩阵生成之后也不具有可读性
在矩阵分解时,PCA是有目标的:
在原有特征的基础上,找出能够让信息尽量聚集的新特征向量。在sklearn使用的PCA和SVD联合的降维方法中,这些新特征向量组成的新特征空间其实就是V(k,n)。当V(k,n)是数字时,我们无法判断V(k,n)和原有的特征究竟有着怎样千丝万缕的数学联系。但是,如果原特征矩阵是图像,V(k,n)这个空间矩阵也可以被可视化的话,我们就可以通过两张图来比较,就可以看出新特征空间究竟从原始数据里提取了什么重要的信息。
from sklearn.datasets import fetch_lfw_people #人脸识别数据
from sklearn.decomposition import PCA #PCA
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
faces = fetch_lfw_people(min_faces_per_person=60)#参数是每个人取出60张脸的图
x = faces.data
faces
faces.data.shape
>(1348, 2914)
faces.images.shape
>(1348, 62, 47)
#可以观察出,data是一个二维的数组,images是一个一维的数组
#但是对于sklearn只能使用二维的数组,不可以使用三维的数组(fit,transform)
#可以看出的是2914=32*47,也就是说可以将data的每一个特征都看成是一个二维的数组,
#因为图片是用像素来表示的,很多个像素块凑到一起就是一个图片,所以images才是真正可以画图的
数据本身是图像和数据本身是数字他们使用的可视化方法是不同的,对于图像需要使用subplots来建立画布
fig, ax = plt.subplots(4,5 #几行几列
,figsize=[8,4] #每一个格长宽的占比
#用来设置不显示坐标轴
,subplot_kw={'xticks':[],'yticks':[]} #设置每一个格的横纵坐标
)
#画布
fig
#画布对象
ax
ax.shape
>(4, 5)
二维结构,可以有两种循环方式,一种是使用索引,循环一次同时生成一列上的三个图
另一种是把数据拉成一维,循环一次只生成一个图
例如,我们使用 子图对象.imshow 来将图像填充到空白画布上,而imshow要求的数据格式必须是一个(m,n)格式的矩阵,即每个数据都是一张单独的图
#将图像填充到第一个空
ax[0][0].imshow(faces.images[0,:,:])
#查看填充完的画布
fig
可以通过数组降维ravel(),flat等方法降维,由于在填充到画布的时候,需要提供数组下标,所以通过一个新的函数**enumerate()**来生成对应的下表,并和其对象合成元组
a = [1,2,3,4,5,6]
[*enumerate(a)]
ax.ravel()
[*enumerate(ax.ravel())]
对于图片的颜色选择可以到Choosing Colormaps in Matplotlib
#i是数组下标,axes是图片对象
for i ,axes in enumerate(ax.ravel()):
axes.imshow(faces.images[i,:,:] #第i个图片,后面的两个维度构成一张图片
,cmap='gray' #选取图片的颜色
)
fig
pca = PCA(n_components=0.99,svd_solver='full').fit(x)
x_f = pca.fit_transform(x)
pca.explained_variance_ratio_.sum()
>0.99004287
#查看返回的特征的数量
pca.explained_variance_ratio_.size
>413
pca = PCA(413).fit(x)
#通过components_获取V(k,n),新的特征向量空间
V = pca.components_
#压缩后的特征数,和原特征数
V.shape
>(413, 2914)
pca_1 = PCA(150).fit(x)
V_1 = pca_1.components_
fig, ax = plt.subplots(3,8
,figsize=(8,4)
,subplot_kw={'xticks':[],'yticks':[]})
#faces.images.shape[1],faces.images.shape[2]就是62,47
for i, axes in enumerate(ax.ravel()):
axes.imshow(V[i].reshape(faces.images.shape[1],faces.images.shape[2]),cmap='gray')
pca_1 = PCA(150).fit(x)
V_1 = pca_1.components_
fig, ax = plt.subplots(3,8
,figsize=(8,4)
,subplot_kw={'xticks':[],'yticks':[]})
for i, axes in enumerate(ax.ravel()):
axes.imshow(V_1[i].reshape(faces.images.shape[1],faces.images.shape[2]),cmap='gray')
可以看出,比起降维前的数据,新特征空间可视化后的人脸非常模糊,这是因为原始数据还没有被映射到特征空间中。但是可以看出,整体比较亮的图片,获取的信息较多,整体比较暗的图片,却只能看见黑漆漆的一块。在比较亮的图片中,眼睛,鼻子,嘴巴,都相对清晰,脸的轮廓,头发之类的比较糊。
这说明,新特征空间里的特征向量们,大部分是"五官"和"亮度"相关的向量,所以新特征向量上的信息肯定大部分是由原数据中和"五官"和"亮度"相关的特征中提取出来的。到这里,我们通过可视化新特征空间V,解释了一部分降维后的特征:虽然显示出来的数字看着不知所云,但画出来的图表示,这些特征是和”五官“以及”亮度“有关的。这也再次证明了,PCA能够将原始数据集中重要的数据进行聚集。
在特征工程中,我们学到了神奇的接口inverse_transform,可以将我们归一化,标准化,甚至做过哑变量的特征矩阵还原回原始数据中的特征矩阵,这几乎在向我们暗示,任何有inverse_transform这个接口的过程都是可逆的。
人脸识别是最容易的,用来探索inverse_transform功能的数据.
先调用一组人脸数据X(m,n),对人脸图像进行绘制,然后我们对人脸数据进行降维得到X_dr,之后再使用inverse_transform(X_dr)返回一个X_inverse(m,n),并对这个新矩阵中的人脸图像也进行绘制。如果PCA的降维过程是可逆的,我们应当期待X(m,n)和X_inverse(m,n)返回一模一样的图像,即携带一模一样的信息。
利用在 4.4写的代码中得到的,413个特征的pca和150个特征的pca_1
x_dr = pca.transform(x)
x_dr.shape
>(1348, 413)
x1_dr = pca_1.transform(x)
x1_dr.shape
>(1348, 150)
#413个特征逆转
x_inverse = pca.inverse_transform(x_dr)
#150个特征逆转
x1_inverse = pca_1.inverse_transform(x1_dr)
fig, ax = plt.subplots(2,10
,figsize=[10,2.5]
,subplot_kw={'xticks':[],'yticks':[]})
我们需要对子图对象进行遍历的循环,来将图像填入子图中,我们需要同时循环两份数据,即一次循环画一列上的两张图,而不是把ax拉平,因为现在我们的ax中是2行10列,第一行是原数据,第二行是inverse_transform后返回的数据
for i in range(10):
#第一行是原数据的图像
ax[0,i].imshow(faces.images[i,:,:],cmap='binary_r')
#第二行是pca降维后通过inverse逆转的图像
ax[1,i].imshow(x_inverse[i].reshape(62,47),cmap='binary_r')
for i in range(10):
#第一行是原数据的图像
ax[0,i].imshow(faces.images[i,:,:],cmap='binary_r')
#第二行是pca降维后通过inverse逆转的图像
ax[1,i].imshow(x1_inverse[i].reshape(62,47),cmap='binary_r')
fig
可以明显看出,这两组数据可视化后,由降维后再通过inverse_transform转换回原维度的数据画出的图像和原数据画的图像大致相似,但原数据的图像明显更加清晰。这说明,inverse_transform并没有实现数据的完全逆转。
在降维的时候,部分信息已经被舍弃了,X_dr中往往不会包含原数据100%的信息,所以在逆转的时
候,即便维度升高,原数据中已经被舍弃的信息也不可能再回来了。所以,降维不是完全可逆的。
Inverse_transform的功能,是基于X_dr中的数据进行升维,将数据重新映射到原数据所在的特征空间中,而并非恢复所有原有的数据。
同时可以观察出,保留99%特征信息的(413)要比特征数150的要更清楚一些
降维的目的之一就是希望抛弃掉对模型带来负面影响的特征,而我们相信,带有效信息的特征的方差应该是远大于噪音的,所以相比噪音,有效的特征所带的信息应该不会在PCA过程中被大量抛弃
inverse_transform能够在不恢复原始数据的情况下,将降维后的数据返回到原本的高维空间,即是说能够实现”保证维度,但去掉方差很小特征所带的信息“。利用inverse_transform的这个性质,我们能够实现噪音过滤。
from sklearn.datasets import load_digits #手写数字
from sklearn.decomposition import PCA
import numpy as np
import matplotlib.pyplot as plt
digits = load_digits()
digits
digits.data.shape
>(1797, 64)
digits.images.shape
>(1797, 8, 8)
#传入的data必须是二维数组,并且特征要有64个
def plot_digits(data):
fig, axes = plt.subplots(4,10
,figsize=[10,4]
,subplot_kw={'xticks':[],'yticks':[]})
for i, ax in enumerate(axes.ravel()):
ax.imshow(data[i].reshape(8,8),cmap='binary')
看原数据的图像
plot_digits(digits.data)
rng = np.random.RandomState(50)
#在指定的数据集中,随机抽取服从正态分布的数据
#两个参数,分别是指定的数据集,和抽取出来的正太分布的方差
#方差越大越凌乱
noisy = rng.normal(digits.data,2)
plot_digits(noisy)
pca = PCA(0.7,svd_solver='full').fit(noisy)
x_dr = pca.transform(noisy)
x_dr.shape
>(1797, 13)
without_noise = pca.inverse_transform(x_dr)
without_noise.shape
>(1797, 64)
plot_digits(without_noise)
重要参数:
重要属性:
接口: