ipynb文件见:https://github.com/824024445/Machine-learning-notes/blob/master/8降维.ipynb
《Sklearn与TensorFlow机器学习实用指南》学习笔记
维数灾难:很多机器学习的问题都会涉及到有着几千甚至数百万维的特征的训练实例,这不仅让训练过程变得非常缓慢,同时还很难找到一个很好的解。
幸运的是,在现实生活中我们经常可以极大的降低特征维度,将一个十分棘手的问题转变成一个可以较为容易解决的问题。例如,对于 MNIST 图片集(第 3 章中提到):图片四周边缘部分的像素几乎总是白的,因此你完全可以将这些像素从你的训练集中扔掉而不会丢失太多信息。
> 警告:降维肯定会丢失一些信息(这就好比将一个图片压缩成 JPEG 的格式会降低图像的质量),因此即使这种方法可以加快训练的速度,同时也会让你的系统表现的稍微差一点。你应该先尝试使用原始的数据来训练,如果训练速度太慢的话再考虑使用降维。
在这一章里,我们将会讨论维数灾难问题并且了解在高维空间的数据。然后,我们将会展示两种主要的降维方法:投影(projection)和流形学习(Manifold Learning),同时我们还会介绍三种流行的降维技术:主成分分析(PCA),核主成分分析(Kernel PCA)和局部线性嵌入(LLE)。
一、维数灾难
高维数据集有很大风险分布的非常稀疏:大多数训练实例可能彼此远离。当然,这也意味着一个新实例可能远离任何训练实例,这使得预测的可靠性远低于我们处理较低维度数据的预测,因为它们将基于更大的推测(extrapolations)。简而言之,训练集的维度越高,过拟合的风险就越大
理论上来说,维数爆炸的一个解决方案是增加训练集的大小从而达到拥有足够密度的训练集。不幸的是,在实践中,达到给定密度所需的训练实例的数量随着维度的数量呈指数增长。如果只有 100 个特征(比 MNIST 问题要少得多)并且假设它们均匀分布在所有维度上,那么如果想要各个临近的训练实例之间的距离在 0.1 以内,您需要比宇宙中的原子还要多的训练实例。
二、降维的主要方法
2.1 投影
在大多数现实生活的问题中,训练实例并不是在所有维度上均匀分布的。许多特征几乎是常数,而其他特征则高度相关(如前面讨论的 MNIST)结果,所有训练实例实际上位于(或接近)高维空间的低维子空间内。用图形表示的话:[外链图片转存失败(img-dNwlFK2c-1565771856276)(https://raw.githubusercontent.com/824024445/Machine-learning-notes/master/img/8-1.jpg)]
2.2 流形学习
许多降维算法通过对训练实例所在的流形进行建模从而达到降维目的;这叫做流形学习。它依赖于流形猜想(manifold assumption),也被称为流形假设(manifold hypothesis),它认为大多数现实世界的高维数据集大都靠近一个更低维的流形。这种假设经常在实践中被证实。
如图:它是一个二维空间,然后在三维空间中卷曲,这种图形叫做二维流形。左上方第一个图形,经过流形学习之后变得更加简单(通过直线可分割)。而左下方图形经过流行学习后变得更复杂(原本通过平面可分割)。总而言之,降低维度之后,模型肯定会变得更快,但并不一定就会变得很好,这取决于具体的数据集。
[外链图片转存失败(img-dnddyH2f-1565771856277)(https://raw.githubusercontent.com/824024445/Machine-learning-notes/master/img/8-2.jpg)]
三、流行的降维技术之主成分分析(PCA)
主成分分析(Principal Component Analysis)是目前为止最流行的降维算法。首先它找到接近数据集分布的超平面,然后将所有的数据都投影到这个超平面上。
3.1. 保留(最大)方差
左侧是一个简单的二维数据集,以及三个不同的轴(即一维超平面)。图右边是将数据集投影到每个轴上的结果。正如你所看到的,投影到实线上保留了最大方差(最上面那条线),而在点线上的投影只保留了非常小的方差,投影到虚线上保留的方差则处于上述两者之间。
选择保持最大方差的轴,这是就 PCA 背后的思想,相当简单。
[外链图片转存失败(img-u81SgVNL-1565771856278)(https://raw.githubusercontent.com/824024445/Machine-learning-notes/master/img/8-3.jpg)]
3.2主成分(Principle Componets)
如上图,有一个最大方差的轴c1,以及与其垂直的轴c2。如果数据是很高维度的话,还有有跟它们垂直的轴c3,c4,c5等等。
定义第i
个轴的单位矢量被称为第i
个主成分(PC)。在图 8-7 中,第一个 PC 是c1
,第二个 PC 是c2
。
下面的 Python 代码使用了 Numpy 提供的svd()
函数获得训练集的所有主成分,然后提取前两个 PC:
import numpy as np
X_centered = X - X.mean(axis=0)
U,s,V=np.linalg.svd(X_centered)
c1=V.T[:,0]
c2=V.T[:,1]
> 警告:PCA 假定数据集以原点为中心。正如我们将看到的,Scikit-Learn 的PCA
类负责为您的数据集中心化处理。但是,如果您自己实现 PCA(如前面的示例所示),或者如果您使用其他库,不要忘记首先要先对数据做中心化处理。
3.3 投影到d维空间
一旦确定了所有的主成分,你就可以通过将数据集投影到由前d
个主成分构成的超平面上,从而将数据集的维数降至d
维。选择这个超平面可以确保投影将保留尽可能多的方差。
3.4 使用sklearn
Scikit-Learn 的 PCA 类使用 SVD 分解来实现,就像我们之前做的那样。以下代码应用 PCA 将数据集的维度降至两维(请注意,它会自动处理数据的中心化):
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
X2D = pca.fit_transform(X)
3.5 方差解释率(Explained Variance Ratio)
每个主成分的方差解释率,可通过explained_variance_ratio_
变量获得。它表示位于每个主成分轴上的数据集方差的比例。
print(pca.explained_variance_ratio_)
3.6 选择正确的维度
通常我们倾向于选择加起来到方差解释率能够达到足够占比(例如 95%)的维度的数量
下面的代码在不降维的情况下进行 PCA,然后计算出保留训练集方差 95% 所需的最小维数:
pca=PCA()
pac.fit(X)
cumsum=np.cumsum(pca.explained_variance_ratio_)
d=np.argmax(cumsum>=0.95)+1
你可以设置n_components = d
并再次运行 PCA。但是,有一个更好的选择:不指定你想要保留的主成分个数,而是将n_components
设置为 0.0 到 1.0 之间的浮点数,表明您希望保留的方差比率:
pca=PCA(n_components=0.95)
X_reduced=pca.fit_transform(X)
另一种选择是画出方差解释率关于维数的函数(简单地绘制cumsum
;参见图 8-8)。曲线中通常会有一个肘部,方差解释率停止快速增长。您可以将其视为数据集的真正的维度,如下图。
[外链图片转存失败(img-NYGfgynW-1565771856279)(https://raw.githubusercontent.com/824024445/Machine-learning-notes/master/img/8-4.jpg)]
3.7 PCA压缩
显然,在降维之后,训练集占用的空间要少得多。例如,尝试将 PCA 应用于 MNIST 数据集,同时保留 95% 的方差。你应该发现每个实例只有 150 多个特征,而不是原来的 784 个特征。
通过应用 PCA 投影的逆变换,也可以将缩小的数据集解压缩回 784 维。当然这并不会返回给你最原始的数据,因为投影丢失了一些信息(在5%的方差内),但它可能非常接近原始数据。
下面的代码将 MNIST 数据集压缩到 154 维,然后使用inverse_transform()
方法将其解压缩回 784 维。
pca=PCA(n_components=154)
X_mnist_reduced=pca.fit_transform(X_mnist)
X_mnist_recovered=pca.inverse_transform(X_mnist_reduced)
[外链图片转存失败(img-i5hMYGY5-1565771856280)(https://raw.githubusercontent.com/824024445/Machine-learning-notes/master/img/8-5.jpg)]
3.8 增量 PCA(Incremental PCA)
下面的代码将 MNIST 数据集分成 100 个小批量(使用 NumPy 的array_split()
函数),并将它们提供给 Scikit-Learn 的IncrementalPCA
类,以将 MNIST 数据集的维度降低到 154 维(就像以前一样)。请注意,您必须对每个最小批次调用partial_fit()
方法,而不是对整个训练集使用fit()
方法:
from sklearn.decomposition import IncrementalPCA
n_batches=100
inc_pca=IncrementalPCA(n_components=154)
for X_batch in np.array_spplit(X_mnist,n_batches):
inc_pca.partial_fit(X_batch)
X_mnist_reduced=inc_pca.transform(X_mnist)
3.9 随机 PCA(Randomized PCA)
Scikit-Learn 提供了另一种执行 PCA 的选择,称为随机 PCA。这是一种随机算法,可以快速找到前d个主成分的近似值。它的计算复杂度是O(m × d^2) + O(d^3)
,而不是O(m × n^2) + O(n^3)
,所以当d
远小于n
时,它比之前的算法快得多。
rnd_pca=PCA(n_components=154,svd_solver='randomized')
X_reduced=rnd_pca.fit_transform(X_mnist)
3.10 核 PCA(Kernel PCA)
在支持向量机的章节中,讨论了核技巧,一种将实例隐式映射到非常高维空间(称为特征空间)的数学技术,让支持向量机可以应用于非线性分类和回归。
同样的技巧可以应用于 PCA,从而可以执行复杂的非线性投影来降低维度。(如上面的瑞士卷)
例如,下面的代码使用 Scikit-Learn 的KernelPCA
类来执行带有 RBF 核的 kPCA(有关 RBF 核和其他核的更多详细信息,请参阅支持向量机的笔记):
from sklearn.decomposition import KernelPCA
rbf_pca=KernelPCA(n_components=2,kernel='rbf',gamma=0.04)
X_reduced=rbf_pca.fit_transform(X)
选择一种核并调整超参数
由于 kPCA 是无监督学习算法,因此没有明显的性能指标可以帮助您选择最佳的核方法和超参数值。但是,降维通常是监督学习任务(例如分类)的准备步骤,因此可以简单地使用网格搜索来选择可以让该任务达到最佳表现的核方法和超参数。例如,下面的代码创建了一个两步的流水线,首先使用 kPCA 将维度降至两维,然后应用 Logistic 回归进行分类。然后它使用Grid SearchCV
为 kPCA 找到最佳的核和gamma
值,以便在最后获得最佳的分类准确性:
from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
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)
可以通过调用best_params_
变量来查看使模型效果最好的核和超参数:
print(grid_search.best_params_)
四、LLE(局部线性嵌入Locally Linear Embedding)
是另一种非常有效的非线性降维(NLDR)方法。这是一种流形学习技术
这是一种流形学习技术,不依赖于像以前算法那样的投影。简而言之,LLE 首先测量每个训练实例与其最近邻(c.n.)之间的线性关系,然后寻找能最好地保留这些局部关系的训练集的低维表示 。这使得它特别擅长展开扭曲的流形,尤其是在没有太多噪音的情况下。
例如,以下代码使用 Scikit-Learn 的LocallyLinearEmbedding
类来展开瑞士卷。得到的二维数据集如图所示。正如所看到的,瑞士卷被完全展开,实例之间的距离保存得很好。但是,距离不能在较大范围内保留的很好:展开的瑞士卷的左侧被挤压,而右侧的部分被拉长。尽管如此,LLE 在对流形建模方面做得非常好。
from sklearn.manifold import LocallyLinearEmbedding
lle=LocallyLinearEmbedding(n_components=2,n_neighbors=10)
X_reduced=lle.fit_transform(X)
[外链图片转存失败(img-BeBu2dcv-1565771856281)(https://raw.githubusercontent.com/824024445/Machine-learning-notes/master/img/8-6.jpg)]
五、其它降维方法
这里是其中最流行的:
- 多维缩放(MDS)在尝试保持实例之间距离的同时降低了维度
- Isomap 通过将每个实例连接到最近的邻居来创建图形,然后在尝试保持实例之间的测地距离时降低维度。
- t-分布随机邻域嵌入(t-Distributed Stochastic Neighbor Embedding,t-SNE)可以用于降低维度,同时试图保持相似的实例临近并将不相似的实例分开。它主要用于可视化,尤其是用于可视化高维空间中的实例(例如,可以将MNIST图像降维到 2D 可视化)。
- 线性判别分析(Linear Discriminant Analysis,LDA)实际上是一种分类算法,但在训练过程中,它会学习类之间最有区别的轴,然后使用这些轴来定义用于投影数据的超平面。LDA 的好处是投影会尽可能地保持各个类之间距离,所以在运行另一种分类算法(如 SVM 分类器)之前,LDA 是很好的降维技术。