sklearn中的降维算法

转载自:【菜菜的sklearn】04 降维算法

1 概述

1.1 维度

针对每一张表,维度指的是样本的数量或特征的数量,一般无特别说明,指的都是特征的数量。除了索引之外,一个特征是一维,两个特征是二维,n个特征是n维。

降维算法中的”降维“,指的是降低特征矩阵中特征的数量

降维的目的:
让算法运算更快,效果更好,
数据可视化

1.2 sklearn中的降维算法——decomposition

2 PCA与SVD

在降维的过程中,即减少特征的数量,又保留大部分有效信息

将那些带有重复信息的特征合并,并删除那些带无效信息的特征

逐渐创造出能够代表原特征矩阵大部分信息的,特征更少的,新特征矩阵。

在降维中,PCA使用的信息量衡量指标,就是样本方差,又称可解释性方差,方差越大,特征所带的信息量越多。

主成分分析PCA是一种通过降维来简化数据结构的方法,即把原有的多个指标转化成少数几个代表性较好的综合指标,这少数几个指标能够反映原来指标的大部分信息(80%以上),并且各个指标之间保持独立,避免出现重叠信息。

2.1 降维究竟是怎样实现?


在这里插入图片描述
F1 方差最大,为第一主成分
F2即第二个线性组合。F2称为第二主成分

新指标能够反映原来指标的大部分信息(80%以上)即可


PCA和SVD是两种不同的降维算法,两种算法中矩阵分解的方法不同,信息量的衡量指标不同

PCA使用方差作为信息量的衡量指标,并且特征值分解来找出空间V。PCA找到的每个新特征向量就叫做“主成分”,而被丢弃的特征向量被认为信息量很少,这些信息很可能就是噪音。

SVD使用**奇异值**分解来找出空间V,其中Σ也是一个对角矩阵,不过它对角线上的元素是奇异值,这也是SVD中用来衡量特征上的信息量的指标。

无论是PCA和SVD 都需要遍历所有的特征和样本来计算信息量指标。并且在矩阵分解的过程之中,会产生比原来的特征矩阵更大的矩阵,

因此,降维算法的计算量很大,运行比较缓慢,但无论如何,它们的功能无可替代


PCA和特征选择技术都是特征工程的一部分,它们有什么不同?

特征工程中有三种方式:特征提取特征创造特征选择

特征选择是从已存在的特征中选取携带信息最多的,选完之后的特征依然具有可解释性,我们依然知道这个特征在原数据的哪个位置,代表着原数据上的什么含义。

而PCA,是将已存在的特征进行压缩,降维完毕后的特征不是原本的特征矩阵中的任何一个特征,而是通过某些方式组合起来的新特征。

PCA建立的新特征向量不具有可读性

新特征虽然带有原始数据的信息,却已经不是原数据上代表着的含义了。以PCA为代表的降维算法因此是特征创造的一种

可以想见,PCA一般不适用于探索特征和标签之间的关系的模型(如线性回归),因为无法解释的新特征和标签之间的关系不具有意义。在线性回归模型中,我们使用特征选择。

2.2 重要参数

sklearn.decomposition.PCA (
n_components=None, 
copy=True, 
whiten=False, 
svd_solver=’auto’, 
tol=0.0,
iterated_power=’auto’,
random_state=None)

n_components

n_components即降维后需要保留的特征数量,如果留下的特征太多,就达不到降维的效果,如果留下的特征太少,那新特征向量可能无法容纳原始数据集中的大部分信息,因
此,n_components既不能太大也不能太小。

2.2.1 案例

导包

import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA

提取数据集

iris = load_iris()
y = iris.target  # 标签
X = iris.data # 特征矩阵

四个特征,三种类别

import pandas as pd
pd.DataFrame(X)

'''
       0    1    2    3
0    5.1  3.5  1.4  0.2
1    4.9  3.0  1.4  0.2
2    4.7  3.2  1.3  0.2
3    4.6  3.1  1.5  0.2
4    5.0  3.6  1.4  0.2
..   ...  ...  ...  ...
145  6.7  3.0  5.2  2.3
146  6.3  2.5  5.0  1.9
147  6.5  3.0  5.2  2.0
148  6.2  3.4  5.4  2.3
149  5.9  3.0  5.1  1.8

[150 rows x 4 columns]
'''

iris.target_names
# array(['setosa', 'versicolor', 'virginica'], dtype='<U10')

调用PCA——降维

# 两个特征
pca = PCA(n_components=2) #实例化
pca = pca.fit(X) #拟合模型
X_dr = pca.transform(X) #获取新矩阵
X_dr
'''
array([[-2.68412563,  0.31939725],
       [-2.71414169, -0.17700123],
       ....
       [ 1.90094161,  0.11662796],
       [ 1.39018886, -0.28266094]])
'''
# 由四个特征变为两个个特征

可视化

X_dr[y == 0, 0] # 标签为0的第一个特征
X_dr[y == 0, 1] # 标签为0的第二个特征

X_dr[y == 1, 0] # 标签为1的第一个特征
X_dr[y == 2, 0] # 标签为2的第一个特征

...


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="orange", label=iris.target_names[2])
plt.legend()
plt.title('PCA of IRIS dataset')
plt.show()

在这里插入图片描述
属性explained_variance_,查看降维后每个新特征向量上所带的信息量大小(可解释性方差的大小

pca.explained_variance_
# array([4.22824171, 0.24267075]) 第一主成分(第一个新特征)具有大部分的信息

属性explained_variance_ratio,查看降维后每个新特征向量所占的信息量占原始数据总信息量的百分比

一般来说,新特征能够反映原来特征的大部分信息(80%以上)即可

pca.explained_variance_ratio_
# array([0.92461872, 0.05306648])

# 大部分信息都被有效地集中在了第一个特征上
pca.explained_variance_ratio_.sum()
# 0.977685206318795

97.77% > 80%

选择最好的n_components(特征个数):累积可解释方差贡献率曲线

当参数n_components中不填写任何值,则默认返回min(X.shape)个特征,一般来说,样本量都会大于特征数目,所以什么都不填就相当于转换了新特征空间,但没有减少特征的个数。一般来说,不会使用这种输入方式。但我们却可以使用这种输入方式来画出累计可解释方差贡献率曲线,以此选择最好的n_components的整数取值。

累积可解释方差贡献率曲线是一条以降维后保留的特征个数为横座标,降维后新特征矩阵捕捉到的可解释方差贡献率为纵座标的曲线,

累积可解释方差贡献率曲线 即选 n_components =1 时,能反映原始信息多少;n_components =2 时,能反映原始信息多少;…;n_components =k 时,能反映原始信息多少

能够帮助我们决定n_components最好的取值
在这里插入图片描述

2.2.2 用最大似然估计自选 n_components (新特征个数)

pca_mle = PCA(n_components="mle")
pca_mle = pca_mle.fit(X)
X_mle = pca_mle.transform(X)
X_mle
'''
array([[-2.68412563,  0.31939725, -0.02791483],
       [-2.71414169, -0.17700123, -0.21046427],
'''
#可以发现,mle为我们自动选择了3个特征
pca_mle.explained_variance_ratio_.sum() # 0.9947878161267247

2.2.3 按信息量占比选 n_components (新特征个数)

输入[0,1]之间的浮点数,并且让参数svd_solver =='full',表示希望降维后的总解释性方差占比大于n_components 指定的百分比,即是说,希望保留百分之多少的信息量。

pca_f = PCA(n_components=0.97,svd_solver="full")
pca_f = pca_f.fit(X)
X_f = pca_f.transform(X)
'''
array([[-2.68412563,  0.31939725],
       [-2.71414169, -0.17700123],
'''
pca_f.explained_variance_ratio_ # array([0.92461872, 0.05306648])

pca_f.explained_variance_ratio_.sum() #  0.977685206318795

2.3 PCA中的SVD

2.3.1 PCA中的SVD哪里来?

svd_solver是奇异值分解器的意思

SVD有一种惊人的数学性质,即是它可以跳过数学神秘的宇宙,不计算协方差矩阵,直接找出一个新特征向量组成的n维空间,

通过SVD和PCA的合作,sklearn实现了一种计算更快更简单,但效果却很好的“合作降维“。很多人理解SVD,是把SVD当作PCA的一种求解方法,其实指的就是在矩阵分解时不使用PCA本身的特征值分解,而使用奇异值分解来减少计算量。

在transform过程之后,fit中奇异值分解的结果除了V(k,n)以外,就会被舍弃,而V(k,n)会被保存在属性components_ 当中,可以调用查看。

PCA(2).fit(X).components_
'''
array([[ 0.36138659, -0.08452251,  0.85667061,  0.3582892 ],
       [ 0.65658877,  0.73016143, -0.17337266, -0.07548102]])
'''

PCA(2).fit(X).components_.shape  # (2, 4)

2.3.2 重要参数svd_solver 与 random_state

参数svd_solver是在降维过程中,用来控制矩阵分解的一些细节的参数。有四种模式可选:“auto”, “full”, “arpack”,“randomized”,默认”auto"。

  • auto”:基于X.shape和n_components的默认策略来选择分解器
  • full”,适合数据量比较适中,计算时间充足的情况
  • arpack”,可以加快运算速度,适合特征矩阵很大的时候,但一般用于特征矩阵为稀疏矩阵的情况
  • randomized”,适合特征矩阵巨大,计算量庞大的情况

而参数random_state在参数svd_solver的值为"arpack" or "randomized"的时候生效,可以控制这两种SVD模式中的随机模式。通常我们就选用”auto“,不必对这个参数纠结太多

2.3.3 重要属性components_

PCA与特征选择的区别,即特征选择后的特征矩阵是可解读的,而PCA降维后的特征矩阵式不可解读的:PCA是将已存在的特征进行压缩,降维完毕后的特征不是原本的特征矩阵中的任何一个特征,而是通过某些方式组合起来的新特征。通常来说,在新的特征矩阵生成之前,我们无法知晓PCA都建立了怎样的新特征向量,新特征矩阵生成之后也不具有可读性

在transform过程之后,fit中奇异值分解的结果除了V(k,n)以外,就会被舍弃,而V(k,n)会被保存在属性components_ 当中,可以调用查看。

在矩阵分解时,PCA是有目标的:在原有特征的基础上,找出能够让信息尽量聚集的新特征向量。在sklearn使用的PCA和SVD联合的降维方法中,这些新特征向量组成的新特征空间其实就是V(k,n)。当V(k,n)是数字时,我们无法判断V(k,n)和原有的特征究竟有着怎样千丝万缕的数学联系。但是,如果原特征矩阵是图像,V(k,n)这个空间矩阵也可以被可视化的话,我们就可以通过两张图来比较,就可以看出新特征空间究竟从原始数据里提取了
什么重要的信息。

2.4 重要接口inverse_transform

还原回原始数据中的特征矩阵

inverse_transform并没有实现数据的完全逆转。这是因为,在降维的时候,部分信息已经被舍弃了,X_dr中往往不会包含原数据100%的信息,所以在逆转的时候,即便维度升高,原数据中已经被舍弃的信息也不可能再回来了。所以,降维不是完全可逆的

2.4.1 用PCA做噪音过滤

降维的目的之一就是希望抛弃掉对模型带来负面影响的特征,而我们相信,带有效信息的特征的方差应该是远大于噪音的,所以相比噪音,有效的特征所带的信息应该不会在PCA过程中被大量抛弃。inverse_transform能够在不恢复原始数据的情况下,将降维后的数据返回到原本的高维空间,即是说能够实现”保证维度,但去掉方差很小特征所带的信息“。利用inverse_transform的这个性质,我们能够实现噪音过滤。

3 案例:PCA对手写数字数据集的降维

导入需要的模块和库

from sklearn.decomposition import PCA # 主成分分析
from sklearn.ensemble import RandomForestClassifier as RFC # 随机森林
from sklearn.model_selection import cross_val_score  # 交叉验证
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

导入数据,探索数据

data = pd.read_csv(r"C:\digit recognizor.csv")
X = data.iloc[:,1:] # 特征矩阵
y = data.iloc[:,0] # 标签
X.shape

画累计方差贡献率曲线,找最佳降维后维度的范围

plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号

pca_line = PCA().fit(X)
plt.figure(figsize=[20,5])
plt.plot(np.cumsum(pca_line.explained_variance_ratio_))
plt.xlabel("降维后的特征个数")
plt.ylabel("累计可解释性方差")
plt.show()

在这里插入图片描述
降维后维度的学习曲线,继续缩小最佳维度的范围

score = []
for i in range(1,101,10):
    X_dr = PCA(i).fit_transform(X) # 降维 特征个数从 1 到 100 ,查看效果如何
    # 用新的特征矩阵 (随机森林)
    # 交叉验证 5折
    once = cross_val_score(RFC(n_estimators=10,random_state=0),X_dr,y,cv=5).mean()
    score.append(once)
    
plt.figure(figsize=[20,5]) # 画布
plt.plot(range(1,101,10),score)
plt.show()

在这里插入图片描述
n_components 在20左右 模型效果最佳

细化学习曲线,找出降维后的最佳维度

score = []
for i in range(10,25):
    X_dr = PCA(i).fit_transform(X)
    once = cross_val_score(RFC(n_estimators=10,random_state=0),X_dr,y,cv=5).mean()
    score.append(once)
plt.figure(figsize=[20,5])
plt.plot(range(10,25),score)
plt.show()

在这里插入图片描述

score.index(max(score))+10   # 21

可知,但降到21个特征的时候,模型效果最佳

导入找出的最佳维度进行降维,查看模型效果

X_dr = PCA(21).fit_transform(X)
cross_val_score(RFC(n_estimators=10,random_state=0),X_dr,y,cv=5).mean()
# 0.9176904761904762

调整随机森林 n_estimators 参数

X_dr = PCA(21).fit_transform(X)
cross_val_score(RFC(n_estimators=100,random_state=0),X_dr,y,cv=5).mean()
# 0.9435952380952382

模型效果还好,跑出了94.36%的水平,但还是没有我们使用嵌入法特征选择过后的96%高,有没有什么办法能够提高模型的表现呢?

们知道KNN的效果比随机森林更好,KNN在未调参的状况下已经达到96%的准确率,而随机森林在未调参前只能达到93%,这是模型本身的限制带来的,这个数据使用KNN效果就是会更好。

from sklearn.neighbors import KNeighborsClassifier as KNN
cross_val_score(KNN(),X_dr,y,cv=5).mean() # 默认k=5
# 0.9676428571428571

效果非常好

KNN的k值学习曲线

score = []
for i in range(10):
    X_dr = PCA(21).fit_transform(X)
    once = cross_val_score(KNN(i+1),X_dr,y,cv=5).mean()
    score.append(once)
plt.figure(figsize=[10,5])
plt.plot(range(10),score)
plt.show()
cross_val_score(KNN(4),X_dr,y,cv=5).mean() # 交叉验证

可以发现,原本785列的特征被我们缩减到21列之后,用KNN跑出了目前位置这个数据集上最好的结果。再进行更细致的调整,我们也许可以将KNN的效果调整到98%以上。PCA为我们提供了无限的可能,终于不用再因为数据量太庞大而被迫选择更加复杂的模型了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章