落花人独立
微雨燕双飞
前言
机器学习问题有时候训练实例会涉及几千或上百万个特征,这导致训练十分缓慢,也让我们更难找到合适的方案。因此可以通过减少特征的数量来转化问题难度。但是数据降维会丢失一些数据信息,因此降维虽能加速训练,也会轻微降低系统性能,也让流水线更加复杂。目前降维方法主要有两种:投影、流形学习,降维技术主要有三种常用的:PCA、Kernal PCA、LLE
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原理:
- 对局部关系线性建模:
- 保留关系并降维:
PS:此算法很难拓展到大型数据集
其他降维技巧
- 多维缩放(MDS):保持实例间的距离,降低维度
- 等度量映射(Isomap):将每个实例与最近的邻居连接起来,创建连接图形,保留实例间的测地距离,降低维度
- t-分布随机近邻嵌入(t-SNE):降维时,让相似的实例彼此靠近;不相似的彼此远离
- 线性判别(LDA):学习类别之间最有区别的轴,用此轴定义投影数据的超平面