机器学习(八)

降维

Posted by Gavin on June 5, 2019

落花人独立

微雨燕双飞

前言

机器学习问题有时候训练实例会涉及几千或上百万个特征,这导致训练十分缓慢,也让我们更难找到合适的方案。因此可以通过减少特征的数量来转化问题难度。但是数据降维会丢失一些数据信息,因此降维虽能加速训练,也会轻微降低系统性能,也让流水线更加复杂。目前降维方法主要有两种:投影流形学习,降维技术主要有三种常用的:PCAKernal PCALLE

PS:高维超立方体中大多数点都非常接近边界,高维数据集很可能是非常稀疏的,因此纬度越高,过拟合可能性越大


数据降维的主要方法

投影


如图所示,实例在所有维度上并不是均匀分布的,许多特征几乎不变,也有许多特征是高度相关联的,因此,高维空间的所有训练实例实际上受一个低得多的低维子空间的影响。如上图,所有实例都在一个平面上,这就是3D空间的2D子空间。将每个训练实例垂直投影到这个平面上,会得到一个二维数据集,便实现了降维。

不过投影并非总是降维的最佳方法,很多情况下,子空间可能会弯曲或转动,如下图所示:

如果简单进行平面投影,会将实例的不同层压到一起(左),我们真正想要的是展开平铺的数据集(右):

流形学习

简单的说,2D流形就是一个能够在更高维空间里面弯曲和扭转的2D形状;概括地说,d维流形就是n(d<n)维空间的一部分,局部类似一个d维超平面。许多降维算法依赖于对训练实例进行流形建模来实现的,这就是流形学习
流形学习依赖于流形假设,即大多数高维数据集存在一个低维度的流形,同时还有一个隐含假设——如果能用低维空间的流形表示,手头的任务会更简单。但是这不一定总是成立,如下图所示,有时流形空间的决策边界更简单,有时更复杂,需要特定情况特定分析。


PCA

保留差异性

将训练集投影到低维超平面之前,需要选择正确的超平面。如下图所示,将一个2D数据集沿三种不同的轴(一维超平面),右边是投影的结果。实线上的投影保留了最大的差异性。

选择保留最大差异性的结果最为合理,因为其丢失的信息比其他两种少。

主成分

主成分分析可以识别出哪条轴对训练集差异性的贡献度最高,上图中便是实线轴,同时它也找出了第二条轴,对剩余差异性贡献度最高,与第一条轴垂直,定义每条轴的单位向量就是主成分。
如何找到训练集的主成分呢?使用一种叫奇异值分解(SVD)的矩阵分解技术,可以将训练集矩阵分解为三个矩阵的点积。其中有一个就包含我们所要的所有主成分:

import numpy as np

X_centered = X - X.mean(axis=0)
U, s, V = np.linalg.svd(X_centered)
c1 = V[:,0]
c2 = V[:,1]

PS:PCA假设数据集围绕原点集中,Scikit-Learn的PCA类意见内部实现了,如果自己实现PCA,不要忘记先将数据集中

低维度投影

一旦确定了主成分,就可以将数据集投影到由前d个主成分定义的超平面上,从而将数据集降维到d维,公式如下:

W2 = V.T[:,:2]
X2D = X_centered.dot(W2)

使用Scikit-Learn

pca = PCA(n_components=2)
X2D = pca.fit_transform(X)

方差解释率

表示每个主成分轴对整个数据集的方差的贡献度

pca.explained_variance_ratio_

结果:

array([ 0.92461621,  0.05301557])

第一条轴贡献92%的方差,第二条轴贡献5%的方差

选择正确数量的维度

将靠前的主成分方差解释率依次相加,直到得到足够大比例的方差(比如95%),这时的维度就是很好的选择。

pca = PCA()
pca.fit(X)
cumsum = np.cumsum(pca.explained_variance_ratio_)
d = np.argmax(cumsum >= 0.95) + 1

之后就可以设置n_components=d再次进行PCA,也可以直接设置为方差比:

pca = PCA(n_components=0.95)
X2D = pca.fit_transform(X)

另外可以绘制解释方差关于维度数量的函数,绘制cumsum即可:

PCA压缩

在PCA投影上运行投影的逆运算,可以将缩小的数据集解压缩回去,但是得到的就不是原始数据集了,而是丢失了5%(或者其他)方差的数据集。下面是解压缩代码:

X_recovered = pca.inverse_transform(X2D)

增量PCA

PCA需要整个训练集都进入内存,往往是不太现实的,因此我们使用增量PCA(IPCA)克服这一问题。将训练集分为一个个小批量,一次给IPCA喂一个。对于大型数据集来说,甚至可以在线应用PCA。

n_batches = 6
inc_pca = IncrementalPCA(n_components=2)
for X_batch in np.array_split(X, n_batches):
	inc_pca.partial_fit(X_batch)
X2D = inc_pca.transform(X)

或者可以使用Numpy的memmap类,它允许操控一个存储在磁盘二进制文件里的大型数组,这个类只有在需要时才加载内存中要的数据。

X_mm = np.memmap(filename, dtype='float32', mode='readonly', shape=(m, n))
batch_size = m
inc_pca = IncrementalPCA(n_components=need, batch_size=batch_size)
inc_pca.fit(X_mm)

随机PCA

此算法可以快速找到前d个主成分的近似值,当d远小于n时,此算法快得多。

rnd_pca = PCA(n_components=2, svd_solver='randomized')
X2D = rnd_pca.fit_transform(X)

核主成分分析

前言

核技巧是一种隐性地将实例映射到高维空间的技巧,这项技术可以应用于PCA,使得复杂的非线性投影降维成为可能。

rbf_pca = KernelPCA(n_components=2, kernel='rbf', gamma=0.04)
X2D = rbf_pca.fit_transform(X)

下图是使用不同核函数降到二维的瑞士卷:

选择核函数和调整超参数

由于kPCA是一种无监督的学习算法,因此没有明显的性能指标来选择。所以我们通常建立流水线,之后使用网格搜索来找到使任务性能最佳的核以及超参数。

clf = Pipeline([
	('kpca', KernelPCA(n_components=2)),
	('log_reg', LogisticRegression())
	])
param_grid = [{
	'kpca__gamma': np.linspace(0.03, 0.05, 10),
	'kpca__kernel': ['rbf', 'sigmoid']
}]
grid_search = GridSearchCV(clf,param_grid,cv=3)
grid_search.fit(X,y)
grid_search.best_params_

还有一种方法,就是选择重建误差最低的核与超参数:

rbf_pca = KernelPCA(n_components=2, kernel='rbf', gamma=0.04, fit_inverse_transform=True)
X2D = rbf_pca.fit_transform(X)
X_recovered = rbf_pca.inverse_transform(X2D)
mean_squared_error(X, X_recovered)

接下来使用网格搜索,寻找使重建误差最小的核与超参数。


局部线性嵌入

概念

这是一种非常强大的非线性降维技术(NLDR),不像之前的算法依赖于投影,这是一种流形学习技术。LLE首先测量每个实例如何与最近的邻居线性相关,然后为训练集寻找一个能最大程度保留这些局部关系的低维表示。这使得它特别擅长展开弯曲的流形,特别是没有太多噪声时。

lle = LocallyLinearEmbedding(n_components=2, n_neighbors=10)
X2D = lle.fit_transform(X)


如图是瑞士卷数据利用lle得到的二维数据集

LLE原理:

  1. 对局部关系线性建模:
  2. 保留关系并降维:

PS:此算法很难拓展到大型数据集


其他降维技巧

  • 多维缩放(MDS):保持实例间的距离,降低维度
  • 等度量映射(Isomap):将每个实例与最近的邻居连接起来,创建连接图形,保留实例间的测地距离,降低维度
  • t-分布随机近邻嵌入(t-SNE):降维时,让相似的实例彼此靠近;不相似的彼此远离
  • 线性判别(LDA):学习类别之间最有区别的轴,用此轴定义投影数据的超平面