查看原文
其他

【周末AI课堂】线性降维方法(代码篇)| 机器学习你会遇到的“坑”

唐僧不用海飞丝 读芯术 2019-12-26


AI课堂开讲,就差你了!


很多人说,看了再多的文章,可是没有人手把手地教授,还是很难真正地入门AI。为了将AI知识体系以最简单的方式呈现给你,芯君邀请AI专业人士开设“周末学习课堂”——每周就AI学习中的一个重点问题进行深度分析,课程会分为理论篇和代码篇,理论与实操,一个都不能少!


来,退出让你废寝忘食的游戏页面,取消只有胡吃海塞的周末聚会吧。未来你与同龄人的差异,也许就从每周末的这堂AI课开启了!


读芯术读者交流群,请加小编微信号:zhizhizhuji。等你。


全文共2128字,预计学习时长5分钟





大家好呀,本周第二节课开始。


在前几周的时间里,我们讨论的过拟合、特征选择代码篇中,利用的数据都是关于典型的回归问题。但在降维过程中,为了方便做可视化,采用的数据是分类的数据,从而可以在特征空间里,直接观察不同类别标记样本的离散程度,来定性地认识降维方法的效果。


我们会通过对特征空间做可视化来阐述降维的主要意义。降维会缩减特征空间的维数,新的特征将是原始特征的线性组合(线性降维),降维不仅使得模型的复杂度降低,还可以提高模型的可解释性。比如对数据进行分类的任务,经过降维,在新的特征空间中,分类很可能更容易进行。


我们选用两个典型的分类数据:


1.sklearn的IRIS数据,它有150个样本,4个特征(sepal length,sepal width ,petal length ,petal width ),总共分为三类(Setosa ,Versicolor,Virginica)。


2.sklearn的wine数据,它有178个样本,13个特征(Alcohol ,Malic acid ,Ash等),总共分为三类。


对于 IRIS,我们选取其中的三个特征,可以在特征空间中看到数据的分布:

from mpl_toolkits.mplot3d import Axes3D

from sklearn import datasets
data=datasets.load_iris()
X=data[
'data']
y=data[
'target']
ax = Axes3D(plt.figure())
for c,i,target_name in zip(
'rgb',[0,1,2],data.target_names):
    ax.scatter(X[y==i ,
0], X[y==i, 1], X[y==i,2], c=c, label=target_name)
ax.set_xlabel(data.feature_names[
0])
ax.set_ylabel(data.feature_names[
1])
ax.set_zlabel(data.feature_names[
2])
ax.set_title(
"Iris")
plt.legend()
plt.show()


图为在三维空间中,三类样本的分布


Versicolor和Virginica这两类交叠在一起,Setosa与这两类都离得特别远,我们在sepal length和sepal width所张成的二维特征空间可以将这组数据表示为:

for c, i, target_name in zip("rgb", [0, 1, 2], target_names):

    plt.scatter(X[y == i, 0], X[y == i, 1], c=c, label=target_name)



我们用不同的颜色表示不同的类别,可以看出,Versicolor和Virginica这两类仍然纠缠在一起,并没有区分开,这也是特征选择局限性的体现,因为特征选择不改变原始特征,只是从中挑选子集,我们会在后面看到降维的好处。同时,Setosa却仍然与这两类都离得特别远,特征空间上只需要一条直线就可以非常好的区分Setosa和其余两类,我们把这种情况叫做线性可分。这就是我们利用分类数据的好处,我们直接可以在特征空间上看到分类的效果。


如果我们对IRIS用PCA来降维,可以看到:

from sklearn.decomposition import PCA

pca = PCA(n_components=2)
X_p =pca.fit(X).transform(X)


降到三维

降到二维


可以看出,经过降维,Versicolor和Virginica两类几乎分开,我们就可以说PCA取得了较好的效果,如果我们再继续将PCA处理过的数据继续利用SVM或者贝叶斯分类器去学习,那么运算量将非常大大降低。


对于wine,我同样可以选取其中的三个特征,来得到特征空间的分布:

data=datasets.load_wine()

......
for c,i,target_name in zip('>o*',[0,1,2],target_names):
    ax.scatter(X[y==i ,
0], X[y==i, 1], X[y==i,2], marker=c, label=target_name)
......

为与IRIS区分,三类样本的数据点格式分别为圆形,三角形,雪花。

毫无疑问,我们均可以通过上述方式得到与IRIS一样的分布图,但是在对Wine数据的二维特征空间的观察,并且与做PCA之后的对比图发现,PCA几乎没起到任何效果:


降维前:


降维后:


这是为什么呢?是PCA的方法不管用了么?可能有的同学已经开始换降维方法,并且认为PCA不适用于这样的数据。


其实,当我们碰到这样的问题时,千万不要急着换降维方法,因为使用PCA的降维技术有一个重要的前提,就是数据要经过标准化。因为我们在对数据的协方差矩阵做特征值分解(奇异值分解)的时候,就默认了数据已经经过了标准化,换而言之,只有数据经过了标准化,低维空间的协方差矩阵才会是:

 


所以我们要先对数据标准化,然后才可以用PCA,直接利用PCA的后果就会很糟糕,数据标准化的处理就是将每个特征对应的数据变成标准的高斯分布:



其中,是特征均值,是特征的标准差,在程序上,我们可以对每一列做这样的处理,但在sklearn里面,我们可以通过如下方式来对数据做标准化:

from sklearn.preprocessing import StandardScaler 

X=StandardScaler().fit(X).transform(X)

我们把Wine数据做标准化处理,再利用PCA可以得到:


经过数据标准化,PCA取得了很好的效果!


接下来,我们把低维空间的维度作为超参数,通过观察测试集上的泛化误差,来看降维对于降低泛化误差的作用。对于 wine数据,我们采用k近邻(K-neighbor)作为分类器,准确率作为性能指标,5折交叉验证,得到泛化误差与维度的关系:

from sklearn.neighbors import KNeighborsClassifieras KNC

from sklearn.model_selectionimport cross_validate
......
test_mse=[]
train_mse=[]
for n in range(
1,X.shape[1]):
    pca=PCA(n_components=n)
    X_new=pca.fit(X).transform(X)
    knc=KNC()
    scorer=
'accuracy'
    knc_dict=cross_validate(knc,X_new,y,cv=5,scoring=scorer)
    test_mse.append(lr_dict[
'test_score'].mean())
    train_mse.append(lr_dict[
'train_score'].mean())
......


从图中可以看出,维度为5和2的时候,测试集的泛化误差最低(体现为高准确率),训练误差一直比测试误差小(体现为训练曲线一直在测试曲线上方),也正是拟合能力和泛化能力的比较。可以说明我们用PCA的方法降低了测试误差。


除此之外,我们还可以引入LDA这样一种有监督的降维技术,因为多利用了目标的信息,所以可以预想到它要比PCA表现的更好:

from sklearn.discriminant_analysis importLinearDiscriminantAnalysis as LDA

lda = LDA(n_components=2)
X_r =lda.fit(X,y).transform(X)


可以看出,对于IRIS数据,PCA和LDA均无明显的差别。


但对于Wine数据,LDA寻找到的低维空间非常明显的优于PCA,三类样本全部分开,且同类样本之间更为密集。

最后,我们利用wine数据,通过k近邻学习器,在维数等于2的时候,比较LDA和PCA的准确率:



可以发现,LDA因为是有监督的降维方式,利用了更多的信息,在这组数据上,用到了k近邻的方法,也取得了更好的效果。事实上,我们做降维的目的就在于,既要削减模型的变量,又要保留尽可能多的信息。即便放在整个机器学习来看,有监督的学习一般而言要更加准确。


读芯君开扒


课堂TIPS


•学习器之所以用k近邻作为分类器,因为从降维缓解了维数灾难,使得采样变得更加容易,k近邻算法恰恰就需要对周围的样本进行采样。但采样变得容易并不代表采样的效果会变好,所以我们也是用k近邻分类器来做试验。


•如果你不想用sklearn,毕竟用了sklearn实在是太简单了。那么根据PCA和LDA的算法,我们最终的步骤是对一个矩阵做特征值或者奇异值分解,那么你可以采用numpy的linalg.eig 或者linalg.svd函数。


•除了PCA和LDA,还有很多的线性降维技术,比如FA(Factor Analysis),ICA(Independent Component Analysis),对于不同的场景有着不同的优劣。比如,PCA处理的是高斯分布,而ICA处理的则是非高斯分布,而且ICA也不一一定就是线性的,这与它的估计方法有关。


留言 点赞 发个朋友圈

我们一起探讨AI落地的最后一公里


作者:唐僧不用海飞丝


如需转载,请后台留言,遵守转载规范


推荐文章阅读

【周末AI课堂 | 第五讲】线性降维方法(理论篇)

【周末AI课堂 | 第四讲】如何进行特征选择(代码篇)

【周末AI课堂 | 第三讲】如何进行特征选择(理论篇)

【周末AI课堂 | 第二讲】过拟合问题(代码篇)

【周末AI课堂 | 第一讲】过拟合问题(理论篇)


长按识别二维码可添加关注

读芯君爱你

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存