四、降维算法
- 特征矩阵/DataFrame结构永远只是一张表(就有行和列),其中行是样本,列是特征。针对每一张表,维度指的是样本的数量或特征的数量*,一般无特别说明,指的都是特征的数量;
- 对于图像而言,维度就是图像中特征向量的数量(也就是坐标轴的数量);
- 降维算法中的”降维“,指的是降低特征矩阵中特征的数量——>目的:让算法运算更快,效果更好;
- 数据可视化:三维以上的看不懂,所以得降维之后才有助于理解;
-
sklearn中的降维算法:
主成分分析中PCA和TruncatedSVD是混合在一起的;现在比较受欢迎的是高级矩阵算法
SVD和PCA都是通过分解特征矩阵来进行降维
4.1 PCA与SVD
- 在降维过程中,我们会减少特征的数量,这意味着删除数据,数据量变少——>模型可以获取的信息会变少,模型的表现可能会因此受影响;
- 目标:找到一个能够即减少特征的数量,又保留大部分有效信息的方法——>创造出能够代表原特征矩阵大部分信息的,特征更少的,新特征矩阵(将那些带有重复信息的特征合并,并删除那些带无效信息的特征等等);
- PCA使用的信息量衡量指标:样本方差,又称为可解释性方差,方差越大,特征所带的信息量越多:
Var代表一个特征的方差,n代表样本量,xi代表一个特征中的每个样本取值,xhat代表这一列样本的均值。
n-1是因为得了得到无偏估计
4.1.1 降维究竟是怎样实现?
class sklearn.decomposition.PCA (n_components=None, copy=True, whiten=False, svd_solver=’auto’, tol=0.0,iterated_power=’auto’, random_state=None)
-
降维例子:
我们现在有一组简单的数据,有特征x1和x2,三个样本数据的坐标点分别为(1,1),(2,2),(3,3)。那么其对应方差就是:
每个特征的数据一模一样,因此方差也都为1,数据的方差总和是2
- 目标:只用一个特征向量来描述这组数据,即将二维数据降为一维数据,并且尽可能地保留信息量,即让数据的总方差尽量靠近2;
- 做法:把坐标轴转一下,然后得到了各个点对应的新坐标,此时的x1均值为2√2,而方差为:
应该是x1*这里打错了
x2*上的数值此时都变成了0,因此x2*明显不带有任何有效信息了(此时x2*的方差也为0了)那么他就可以删掉了,这样就使得表只剩下一列/一个特征,这样一来就成功的降维了;
-
步骤:
在步骤3当中,我们用来找出n个新特征向量,让数据能够被压缩到少数特征上并且总信息量不损失太多的技术就是矩阵分解
- PCA和SVD是两种不同的降维算法,但他们都遵从上面的过程来实现降维,只是两种算法中矩阵分解的方法不同,信息量的衡量指标不同罢了(PCA能干的事,SVD也能干,反之亦然)
(1)PCA使用方差作为信息量的衡量指标,并且特征值分解来找出空间V。降维时,它会通过一系列数学的神秘操作(比如说,产生协方差矩阵1/nXXT)将特征矩阵X分解为以下三个矩阵:
Q和Q-1是两个辅助矩阵,而∑则是个对角矩阵,其中对角线上的元素就是方差
降维完成之后,PCA找到的每个新特征向量就叫做“主成分”,而被丢弃的特征向量被认为信息量很少,这些信息很可能就是噪音;
(2)而SVD使用奇异值分解来找出空间V:
其中Σ也是一个对角矩阵,不过它对角线上的元素是奇异值,这也是SVD中用来衡量特征上的信息量的指标。U和VT分别是左奇异矩阵和右奇异矩阵,也都是辅助矩阵
4.1.2 重要参数n_components
- 含义:是我们降维后需要的维度,即降维后需要保留的特征数量,降维流程中第二步里需要确认的k值,一般输入[0, min(X.shape)]范围中的整数;
- 确定方法:可以先从我们的降维目标说起:如果我们希望可视化一组数据来观察数据分布,我们往往将数据降到三维以下,很多时候是二维,即n_components的取值为2;
- 案例:高维数据可视化:
- 调用库和模块
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
2.提取数据集
iris = load_iris()
y = iris.target
X = iris.data
#作为数组,X是几维?
X.shape
#(150, 4)
#作为数据表或特征矩阵,X是几维?
import pandas as pd
pd.DataFrame(X)
说明X是四维的
- 建模
#调用PCA
#实例化
pca = PCA(n_components=2)
#拟合模型
pca = pca.fit(X)
#获取新矩阵
X_dr = pca.transform(X)
X_dr
X_dr.shape
#(150, 2)
#也可以fit_transform一步到位
#X_dr = PCA(2).fit_transform(X)
- 可视化
#要将三种鸢尾花的数据分布显示在二维平面坐标系中,对应的两个坐标(两个特征向量)应该是三种鸢尾花降维后的x1和x2,怎样才能取出三种鸢尾花下不同的x1和x2呢?
#可以直接拿X_dr画点图,但是这样不知道哪个点对应哪种花
#所以要根据花的类别来把三种不同的花标记出来
#这里是布尔索引,看出来了么?这里就是取出标签为0的花的第一个特征
X_dr[y == 0, 0]
#要展示三中分类的分布,需要对三种鸢尾花分别绘图
#可以写成三行代码,也可以写成for循环
"""
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()
"""
colors = ['red', 'black', 'orange']
iris.target_names
plt.figure()
for i in [0, 1, 2]:
plt.scatter(X_dr[y == i, 0]
,X_dr[y == i, 1]
,alpha=.7 #设置点的透明度
,c=colors[i]
,label=iris.target_names[i]
)
plt.legend()
plt.title('PCA of IRIS dataset')
plt.show()
可以看出每种花都是挨在一起的(分簇的分布)并且每个簇之间的分布相对比较明显,这种分布正式聚类模型擅长的。也许versicolor和virginia这两种花之间会有一些分类错误,但setosa肯定不会被分错。KNN,随机森林,神经网络,朴素贝叶斯,Adaboost这些分类器在鸢尾花数据集上,未调整的时候都可以有95%上下的准确率
- 探索降维后的数据
#属性explained_variance_,查看降维后每个新特征向量上所带的信息量大小(可解释性方差的大小)
pca.explained_variance_
#array([4.22824171, 0.24267075]) 说明大多数信息都在第一个轴上
#属性explained_variance_ratio,查看降维后每个新特征向量所占的信息量占原始数据总信息量的百分比
#又叫做可解释方差贡献率
pca.explained_variance_ratio_
#array([0.92461872, 0.05306648])
#大部分信息都被有效地集中在了第一个特征上
pca.explained_variance_ratio_.sum()
#0.977685206318795 因为降维后的信息量一定有损失
- 选择最好的n_components:累积可解释方差贡献率曲线
当参数n_components中不填写任何值,则默认返回min(X.shape)个特征,一般来说,样本量都会大于特征数目——>什么都不填就相当于转换了新特征空间,但没有减少特征的个数
可以使用这种输入方式来画出累计可解释方差贡献率曲线,以此选择最好的n_components的整数取值;
- 累积可解释方差贡献率曲线是一条以降维后保留的特征个数为横坐标,降维后新特征矩阵捕捉到的可解释方差贡献率为纵坐标的曲线——>能够帮助我们决定n_components最好的取值:
import numpy as np
pca_line = PCA().fit(X)
pca_line.explained_variance_ratio_
#array([0.92461872, 0.05306648, 0.01710261, 0.00521218])
np.cumsum(pca_line.explained_variance_ratio_)
#array([0.92461872, 0.97768521, 0.99478782, 1. ]) 分别是前2/3/4个数值相加的结果
#画图
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()
在这里,2/3都是可以考虑的选择。当特征数很大时,这个曲线就会比较平滑:我们就找在曲率变小之前的那一段来判断特征个数
- 最大似然估计自选超参数:让PCA用最大似然估计(maximum likelihoodestimation)自选超参数的方法,输入“mle”作为n_components的参数输入,就可以调用这种方法,但是计算量会很大:
pca_mle = PCA(n_components="mle")
pca_mle = pca_mle.fit(X)
X_mle = pca_mle.transform(X)
X_mle
#可以发现,mle为我们自动选择了3个特征
pca_mle.explained_variance_ratio_.sum()
#得到了比设定2个特征时更高的信息含量,对于鸢尾花这个很小的数据集来说,3个特征对应这么高的信息含量,并不需要去纠结于只保留2个特征,毕竟三个特征也可以可视化
#0.9947878161267247 (牛蛙牛蛙)
- 按信息量占比选超参数:输入[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)
#自动返回了一个二维的
pca_f.explained_variance_ratio_
#array([0.92461872, 0.05306648])
这种方法能够让我们知道:需要多少的信息量才能让模型保持准确度,可以在一开始的时候拿来用,一般都是80~90%;
4.1.3 PCA中的SVD
(1) PCA中的SVD哪里来
SVD可以不计算协方差矩阵,直接找出一个新特征向量组成的n维空间,而这个n维空间就是奇异值分解后的右矩阵VT,它有着如下的性质:
k就是n_components,是我们降维后希望得到的维度。若X为(m,n)的特征矩阵,VT就是结构为(n,n)的矩阵,取这个矩阵的前k行(进行切片),即将V转换为结构为(k,n)的矩阵。而V(k,n)T与原特征矩阵X相乘,即可得到降维后的特征矩阵Xdr
这是说,奇异值分解可以不计算协方差矩阵等等结构复杂计算冗长的矩阵,就直接求出新特征空间和降维后的特征矩阵;
但是奇异值也挺难理解的,sklearn将降维流程拆成了两部分:一部分是 计算特征空间V,由奇异值分解完成,另一部分是 映射数据和求解新特征矩阵,由主成分分析完成,实现了用SVD的性质减少计算量,却让信息量的评估指标是方差。具体流程如下:
在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]])
#返回新特征空间V(k,n)
PCA(2).fit(X).components_.shape
#(2, 4)
(2)重要参数svd_solver 与 random_state
参数svd_solver是在降维过程中,用来控制矩阵分解的一些细节的参数。有四种模式可选:"auto", "full", "arpack","randomized",默认”auto":
- auto::如果输入数据的尺寸大于500x500且要提取的特征数小于数据最小维度min(X.shape)的80%,就启用效率更高的”randomized“方法。否则,精确完整的SVD(完成分解后的U,Σ,V都会被完整转置)将被计算,截断将会在矩阵被分解完成后有选择地发生;
- full:从scipy.linalg.svd中调用标准的LAPACK分解器来生成精确完整的SVD,适合数据量比较适中,计算时间充足的情况(因为要生成的矩阵很大),生成的精确完整的SVD的结构为:
- arpack:从scipy.sparse.linalg.svds调用ARPACK分解器来运行截断奇异值分解(SVD truncated),分解时就将特征数量降到n_components中输入的数值k,可以加快运算速度,适合特征矩阵很大的时候,但一般用于特征矩阵为稀疏矩阵的情况,此过程包含一定的随机性。截断后的SVD分解出的结构为:
- randomized:分解器会先生成多个随机向量,然后一一去检测这些随机向量中是否有任何一个符合我们的分解需求,如果符合,就保留这个随机向量,并基于这个随机向量来构建后续的向量空间。已经被证实比full好使,适合特征矩阵巨大,计算量庞大的情况;
而参数random_state在参数svd_solver的值为"arpack" or "randomized"的时候生效,可以控制这两种SVD模式中的随机模式。通常我们就选用”auto“,不必对这个参数纠结太多。
(3) 重要属性components
虽然要的是Xdr但是保存V(k,n)是为了后面把它可视化然后找出所提取的重要的信息;
PCA与特征选择的区别:即特征选择后的特征矩阵是可解读的,而PCA降维后的特征矩阵式不可解读的:PCA是将已存在的特征进行压缩,降维完毕后的特征不是原本的特征矩阵中的任何一个特征,而是通过某些方式组合起来的新特征;
- 人脸识别中属性components_的运用[]
- 导入需要的库和模块
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)
#怎样理解这个数据的维度?
#严格来说,这个才是特征矩阵,虽然是三维的
#1348是图像个数,62/47是每个图像的特征矩阵的行/列
#行是样本
#列是样本相关的特征
faces.data.shape
#(1348, 2914)
#换成特征矩阵之后,这个矩阵是什么样?
X = faces.data
6247=2914,说明这里的特征是由两个相互垂直的特征向量构成的;
虽然2914不能图像化,但是6247是可以的,代表的是总像素的数字
- 看看图像什么样?将原特征
#数据本身是图像,和数据本身只是数字,使用的可视化方法不同
#创建画布和子图对象
#4,5代表画4行,每行有5张图;figsize代表画布大小;subplot_kw代表
fig, axes = plt.subplots(4,5
,figsize=(8,4)
,subplot_kw = {"xticks":[],"yticks":[]} #不要显示坐标轴
)
#fig就是上面的画布
fig
#每个画布的对象
axes
#不难发现,axes中的一个对象对应fig中的一个空格
#我们希望,在每一个子图对象中填充图像(共20张图),因此我们需要写一个在子图对象中遍历的循环
axes.shape
#(4, 5)
#二维结构,可以有两种循环方式,一种是使用索引,循环一次同时生成一列上的三个图
#另一种是把数据拉成一维,循环一次只生成一个图
#在这里,究竟使用哪一种循环方式,是要看我们要画的图的信息,储存在一个怎样的结构里
#我们使用 子图对象.imshow 来将图像填充到空白画布上
#而imshow要求的数据格式必须是一个(m,n)格式的矩阵,即每个数据都是一张单独的图
#因此我们需要遍历的是faces.images,其结构是(1277, 62, 47)
#要从一个数据集中取出24个图,明显是一次性的循环切片[i,:,:]来得便利
#因此我们要把axes的结构拉成一维来循环
axes[0][0].imshow(faces.images[0,:,:])
#<matplotlib.image.AxesImage at 0x25cea82f880>,实际上就已经画出一个人脸图了
#降维成一维数组
axes.flat
#给每个对象加个索引
enumerate(axes.flat)
#填充图像,i就是0-19;ax就是每个画图对象
for i, ax in enumerate(axes.flat):
ax.imshow(faces.images[i,:,:]
,cmap="gray" #选择色彩的模式
)
#上面这个for要放到建画布那里去
#https://matplotlib.org/tutorials/colors/colormaps.html
- 建模降维,提取新特征空间矩阵
#原本有2900维,我们现在来降到150维
pca = PCA(150).fit(X)
V = pca.components_
V.shape
#(150, 2914)
- 将新特征空间矩阵可视化
fig, axes = plt.subplots(3,8,figsize=(8,4),subplot_kw = {"xticks":[],"yticks":[]})
#对V进行循环,把其中每个2914都变成62*47的结构,这样就可以画图了
for i, ax in enumerate(axes.flat):
ax.imshow(V[i,:].reshape(62,47),cmap="gray")
大抵还是能看出眼鼻嘴的,这说明这仨是降维过程中所提取的主要特征信息
4.1.4 重要接口inverse_transform
- 作用:可以将我们归一化,标准化,甚至做过哑变量的特征矩阵还原回原始数据中的特征矩阵;
(1) 迷你案例:用人脸识别看PCA降维后的信息保存量
我们先调用一组人脸数据X(m,n),对人脸图像进行绘制,然后我们对人脸数据进行降维得到X_dr,之后再使用inverse_transform(X_dr)返回一个X_inverse(m,n),并对这个新矩阵中的人脸图像也进行绘制。如果PCA的降维过程是可逆的,我们应当期待X(m,n)和X_inverse(m,n)返回一模一样的图像,即携带一模一样的信息:
- 导入需要的库和模块(与2.3.3节中步骤一致)
from sklearn.datasets import fetch_lfw_people
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import numpy as np
- 导入数据,探索数据(与2.3.3节中步骤一致)
faces = fetch_lfw_people(min_faces_per_person=60)
faces.images.shape
#怎样理解这个数据的维度?
faces.data.shape
#换成特征矩阵之后,这个矩阵是什么样?
X = faces.dat
- 建模降维,获取降维后的特征矩阵X_dr
#实例化
pca = PCA(150)
#拟合+提取结果
X_dr = pca.fit_transform(X)
X_dr.shape
#(1348, 150)
- 将降维后矩阵用inverse_transform返回原空间
#希望降维后的X_inverse和原来的X是一样的;如果相同就说明inverse_transform完成了降维的逆转
X_inverse = pca.inverse_transform(X_dr)
X_inverse.shape
(1348, 2914)
- 将特征矩阵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中的数据进行升维,将数据重新映射到原数据所在的特征空间中,而并非恢复所有原有的数据。但是也说明原有数据在经过降维后仍然能够保留大部分信息;
(2) 迷你案例:用PCA做噪音过滤
降维的目的之一就是希望抛弃掉对模型带来负面影响的特征,所以相比噪音,有效的特征所带的信息应该不会在PCA过程中被大量抛弃,inverse_transform说能够实现”保证维度,但去掉方差很小特征所带的信息“——>噪音过滤:
- 导入需要的库和模块
from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import numpy as np
2.导入数据,探索数据
digits = load_digits()
digits.data.shape
#(1797, 64)
#该数据集很干净,没有噪音
#set是个去重函数
set(digits.target)
#{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
3.定义画图函数
def plot_digits(data):
#data的结构必须是m*n,且n要能够被分为(8,8)这样的结构
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)
- 为数据加上噪音
import numpy as np
#对于图像,使用正态分布的随机抽取函数
rng = np.random.RandomState(42)
#在指定的数据集中,随机抽取服从正态分布的数据
#两个参数,分别是指定的数据集,和抽取出来的正太分布的方差
noisy = rng.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)
可以看到虽然还有一些噪音,但是比起之前的版本已经好很多了;
4.1.5 重要接口,参数和属性总结
- 重要参数:n_components(高维数据可视化),svd_solver(解释了SVD相关参数),random_state(随机矩阵分解模式);
- 重要属性:components_(调用V(k,n), explained_variance_(衡量降维后每个特征的信息量)以及explained_variance_ratio_(帮助选择最佳的n_components的取值);
- 重要接口:fit,transform(调取结果),fit_transform(前两个的结合),inverse_transform;
4.2 案例:PCA对手写数字数据集的降维
我们使用了各种技术对手写数据集进行特征选择,最后使用嵌入法SelectFromModel选出了324个特征,将随机森林的效果也调到了96%以上。但是,因为数据量依然巨大,还是有300多个特征。今天,我们就来试着用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"E:\jupyter\sklearn\4 Decomposition\digit recognizor.csv")
#X是所有行数据第1列开始取,直到最后
X = data.iloc[:,1:]
#y是所有行的第0列
y = data.iloc[:,0]
X.shape
#(42000, 784)
- 画累计方差贡献率曲线,找最佳降维后维度的范围
pca_line = PCA().fit(X)
plt.figure(figsize=[20,5])
plt.plot(np.cumsum(pca_line.explained_variance_ratio_))
plt.xlabel("number of components after dimension reduction")
plt.ylabel("cumulative explained variance ratio")
plt.show()
0-200之间必然有个值是想要的
- 降维后维度的学习曲线,继续缩小最佳维度的范围
#======【TIME WARNING:2mins 30s】======#
score = []
#就是之前调参的原理
for i in range(1,101,10):
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(1,101,10),score)
plt.show()
- 细化学习曲线,找出降维后的最佳维度
#======【TIME WARNING:2mins 30s】======#
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()
可得出: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.9180952380952381
#======【TIME WARNING:1mins 30s】======#
cross_val_score(RFC(n_estimators=100,random_state=0),X_dr,y,cv=5).mean()
#0.9434047619047619
- 突发奇想,特征数量已经不足原来的3%,换模型怎么样?
from sklearn.neighbors import KNeighborsClassifier as KNN
cross_val_score(KNN(),X_dr,y,cv=5).mean()
之所以能用KNN,是因为我们已经通过降维来去掉了很多特征,从而加快了KNN的运行速度
- KNN的k值学习曲线
#======【TIME WARNING: 】======#
score = []
for i in range(10):
X_dr = PCA(23).fit_transform(X)
once = cross_val_score(KNN(i+1),X_dr,y,cv=5).mean()
score.append(once)
plt.figure(figsize=[20,5])
plt.plot(range(10),score)
plt.show()
- 定下超参数后,模型效果如何,模型运行时间如何?
cross_val_score(KNN(3),X_dr,y,cv=5).mean()
#0.9680714285714286
#下面的代码要另起一个格子
%%timeit
#表示对这个块中的所有代码都执行一遍,求其运行时间
#=======【TIME WARNING: 3mins】======#
cross_val_score(KNN(3),X_dr,y,cv=5).mean()
#1.5 s ± 47.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
可以发现,原本785列的特征被我们缩减到21列之后,用KNN跑出了目前位置这个数据集上最好的结果。再进行更细致的调整,我们也许可以将KNN的效果调整到96%以上。
PCA拿来减少特征数,从而提高KNN的运行速度
——>能用PCA一般就不用特征选择了,不能用PCA才去选择
拖更了,得亏还知道回来