· Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow, 2nd Edition, by Aurélien Géron (O’Reilly). Copyright 2019 Aurélien Géron, 978-1-492-03264-9.
· “Machine Learning” Zhou Zhihua
· 环境:Anaconda(Python 3.8) + Pycharm
· 学习时间:2022.05.07~2022.05.08
第八章 降维
许多机器学习问题涉及每个训练实例的数千甚至数百万个特征。正如我们将看到的,所有这些特性不仅使训练变得极其缓慢,而且使寻找好的解决方案变得更加困难。这个问题通常被称为维度灾难。
幸运的是,在实际问题中,通常可以大大减少特征的数量,从而将棘手的问题转变为易于解决的问题。例如,考虑MNIST图像(在第3章中介绍):图像边界上的像素几乎都是白色,因此你可以从训练集中完全删除这些像素而不会丢失太多信息。图7-6确认了这些像素对于分类任务而言完全不重要。另外,两个相邻的像素通常是高度相关的,如果将它们合并为一个像素(例如,通过取两个像素的平均值),不会丢失太多信息。
数据降维确实会丢失一些信息(就好比将图像压缩为JPEG会降低其质量一样),所以,它虽然能够加速训练,但是也会轻微降低系统性能。同时它也让流水线更为复杂,维护难度上升。因此,如果训练太慢,你首先应该尝试的还是继续使用原始数据,然后再考虑数据降维。不过在某些情况下,降低训练数据的维度可能会滤除掉一些不必要的噪声和细节,从而导致性能更好(但通常来说不会,它只会加速训练)。
除了加快训练,降维对于数据可视化(或称DataViz)也非常有用。将维度降到两个(或三个),就可以在图形上绘制出高维训练集,通过视觉来检测模式,常常可以获得一些十分重要的洞察,比如聚类。此外,DataViz对于把你的结论传达给非数据科学家至关重要,尤其是将使用你的结果的决策者。
本章将探讨维度的诅咒,简要介绍高维空间中发生的事情。然后,我们将介绍两种主要的数据降维方法(投影和流形学习),并学习现在最流行的三种数据降维技术:PCA、Kernal PCA以及LLE。
8.1 维度的诅咒
我们已经习惯了生活在三维空间中,以至于当我们试图想象一个高维空间时,直觉思维很难成功。即使是一个基本的四维超立方体在我们的脑海中都难以想象,更不用说在千维空间中弯曲的两百维椭球了。
事实证明,在高维空间中,许多事物的行为都迥然不同。例如,如果你在一个单位平面(1×1的正方形)内随机选择一个点,那么这个点离边界的距离小于0.001的概率只有约0.4%(也就是说,一个随机的点不大可能刚好位于某个维度的“极端”)。但是,在一个10 000维的单位超立方体(1×1…×1立方体,一万个1)中,这个概率大于99.99999%。高维超立方体中大多数点都非常接近边界。
还有一个更麻烦的区别:如果你在单位平面中随机挑两个点,这两个点之间的平均距离大约为0.52。如果在三维的单位立方体中随机挑两个点,两点之间的平均距离大约为0.66。但是,如果在一个100万维的超立方体中随机挑两个点呢?不管你相信与否,平均距离大约为408.25(约等于)!这是非常违背直觉的:位于同一个单位超立方体中的两个点,怎么可能距离如此之远?这个事实说明高维数据集有很大可能是非常稀疏的:大多数训练实例可能彼此之间相距很远。当然,这也意味着新的实例很可能远离任何一个训练实例,导致跟低维度相比,预测更加不可靠,因为它们基于更大的推测。简而言之,训练集的维度越高,过拟合的风险就越大。
理论上来说,通过增大训练集,使训练实例达到足够的密度,是可以解开维度的诅咒的。然而不幸的是,实践中,要达到给定密度,所需要的训练实例数量随着维度的增加呈指数式上升。仅仅100个特征下(远小于MNIST问题),要让所有训练实例(假设在所有维度上平均分布)之间的平均距离小于0.1,你需要的训练实例数量就比可观察宇宙中的原子数量还要多。
8.2 降维的主要方法
在深入研究具体的降维算法之前,让我们看一下降维的两种主要方法:投影和流形学习。
8.2.1 投影
在大多数实际问题中,训练实例并不是均匀地分布在所有维度上。许多特征几乎是恒定不变的,而其他特征则是高度相关的(如之前针对MNIST所述)。结果,所有训练实例都位于(或接近于)高维空间的低维子空间内。这听起来很抽象,所以让我们看一个示例。在下图中,你可以看到由圆圈表示的3D数据集。
请注意,所有训练实例都位于一个平面附近:这是高维(3D)空间的低维(2D)子空间。如果我们将每个训练实例垂直投影到该子空间上(如实例连接到平面的短线所示),我们将获得如下图所示的新2D数据集——我们刚刚将数据集的维度从3D减少到2D。注意,轴对应于新特征z1和z2(平面上投影的坐标)。
然而,投影并不总是缩小尺寸的最佳方式。在很多情况下,子空间可以被扭曲和旋转,比如下图所示的著名的瑞士卷小数据集。
如下图左侧所示,简单地投影到一个平面上(例如,去掉x3维度)会将瑞士卷的不同层挤压在一起。你真正想要的是展开瑞士卷,得到下图右侧的2D数据集。
8.2.2 流形学习
瑞士卷是2D流形的一个示例。简而言之,2D流形是可以在更高维度的空间中弯曲和扭曲的2D形状。更一般而言,d维流形是n维空间(其中d 许多降维算法通过对训练实例所在的流形进行建模来工作。这称为流形学习。它依赖于流形假设(也称为流形假设),该假设认为大多数现实世界的高维数据集都近似于低维流形。通常这是基于经验观察的假设。 再次考虑一下MNIST数据集:所有手写数字图像都有一些相似之处。它们由连接的线组成,边界为白色,并且或多或少居中。如果你随机生成图像,那么其中只有一小部分看起来像手写数字。换句话说,如果你试图创建数字图像,可用的自由度大大低于允许你生成任何图像的自由度。这些约束倾向于将数据集压缩为低维流形。 流形假设通常还伴随着另一个隐式假设:如果用流形的低维空间表示,手头的任务(例如分类或回归)将更加简单。例如,在下图的上面一行中,瑞士卷分为两类:在3D空间(左侧)中,决策边界会相当复杂,而在2D展开流形空间中(右侧),决策边界是一条直线。 但是,这种隐含假设并不总是成立。例如,在上图的下面一行中,决策边界位于x1=5处。此决策边界在原始3D空间(垂直平面)中看起来非常简单,但在展开流形中看起来更加复杂(四个独立线段的集合)。 简而言之,在训练模型之前降低训练集的维数肯定可以加快训练速度,但并不总是会带来更好或更简单的解决方案,这取决于数据集。 希望现在您对维数诅咒以及降维算法如何解决它有一个很好的理解,尤其是当流形假设成立时。本章的其余部分将一一介绍一些最流行的算法。 主成分分析(PCA)是迄今为止最流行的降维算法。首先,它识别最靠近数据的超平面,然后将数据投影到其上。 将训练集投影到低维超平面之前需要选择正确的超平面。例如下图的左图代表一个简单的2D数据集,沿三条不同的轴(即一维超平面)。右图是将数据集映射到每条轴上的结果。正如你所见,在实线上的投影保留了最大的差异性,而点线上的投影只保留了非常小的差异性,虚线上的投影的差异性居中。 选择保留最大差异性的轴看起来比较合理,因为它可能比其他两种投影丢失的信息更少。要证明这一选择,还有一种方法,即比较原始数据集与其轴上的投影之间的均方距离,使这个均方距离最小的轴是最合理的选择,也就是实线代表的轴。这也正是PCA背后的简单思想。 主成分分析可以在训练集中识别出哪条轴对差异性的贡献度最高。在上图中是由实线表示的轴。同时它也找出了第二条轴,与第一条轴垂直,它对剩余差异性的贡献度最高。因为这个示例是二维的,所以除了这条点线再没有其他。如果是在更高维数据集中,PCA还会找到与前两条都正交的第三条轴,以及第四条、第五条,等等——轴的数量与数据集维度数量相同。 第i个轴称为数据的第i个主要成分(PC)。在上图中,第一个PC是向量c1所在的轴,第二个PC是向量c2所在的轴。 对于每个主要成分,PCA都找到一个指向PC方向的零中心单位向量。由于两个相对的单位向量位于同一轴上,因此PCA返回的单位向量的方向不稳定:如果稍微扰动训练集并再次运行PCA,则单位向量可能会指向原始向量的相反方向。但是,它们通常仍位于相同的轴上。在某些情况下,一对单位向量甚至可以旋转或交换(如果沿这两个轴的方差接近),但是它们定义的平面通常保持不变。 那么如何找到训练集的主要成分呢?幸运的是,有一种称为奇异值分解(SVD)的标准矩阵分解技术,该技术可以将训练集矩阵分解为三个矩阵的矩阵乘法,其中包含定义所有主要成分的单位向量。如公式所示。 以下Python代码使用NumPy的svd()函数来获取训练集的所有主要成分,然后提取定义前两个PC的两个单位向量: PCA假定数据集以原点为中心。正如我们将看到的,Scikit-Learn的PCA类负责为你居中数据。如果你自己实现PCA(如上例所示),或者使用其他库,请不要忘记首先将数据居中(标准化)。 一旦确定了所有主要成分,你就可以将数据集投影到前d个主要成分定义的超平面上,从而将数据集的维度降低到d维。选择这个超平面可确保投影将保留尽可能多的差异性。例如,在上图(8.2.1)中,将3D数据集投影到由前两个主成分定义的2D平面上,从而保留了数据集大部分的差异性。最终,2D投影看起来非常类似于原始3D数据集。 要将训练集投影到超平面上,得到维数为的简化数据集,将训练集矩阵乘以矩阵的矩阵,定义为包含的前列的矩阵,如公式 。 您现在知道如何将任何数据集的维度减少到任意数量的维度,同时尽可能多地保留方差。 就像我们在本章前面所做的那样,Scikit-Learn的PCA类使用SVD分解来实现PCA。以下代码应用PCA将数据集的维度降到二维(请注意,它会自动处理数据居中的问题): 将PCA转换器拟合到数据集后,其components_属性是的转置(例如,定义第一个主成分的单位向量等于pca.components.T[:, 0])。 完整代码如下: 另一个有用的信息是每个主成分的可解释方差比,可以通过explained_variance_ratio_变量来获得。该比率表示沿每个成分的数据集方差的比率。例如,让我们看一下图8-2中表示的3D数据集的前两个成分的可解释方差比: 此输出告诉你,数据集方差的84.2%位于第一个PC上,而14.6%位于第二个PC上。对于第三个PC,这还不到1.2%,因此可以合理地假设第三个PC携带的信息很少。 也就是用来说明组件的重要性。 与其任意选择要减小到的维度,不如选择相加足够大的方差部分(例如95%)的维度。当然,如果你是为了数据可视化而降低维度,这种情况下,需要将维度降低到2或3。 以下代码在不降低维度的情况下执行PCA,然后计算保留95%训练集方差所需的最小维度: 然后,你可以设置n_components=d并再次运行PCA。但是还有一个更好的选择:将n_components设置为0.0到1.0之间的浮点数来表示要保留的方差率,而不是指定要保留的主成分数: 另一个选择是将可解释方差绘制成维度的函数(简单地用cumsum绘制,见下图)。曲线上通常会出现一个拐点,其中可解释方差会停止快速增大。在这种情况下,你可以看到将维度降低到大约100而不会损失太多的可解释方差。 降维后,训练集占用的空间要少得多。例如,将PCA应用于MNIST数据集,同时保留其95%的方差。你会发现每个实例将具有150多个特征,而不是原始的784个特征。因此,尽管保留了大多数方差,但数据集现在不到其原始大小的20%!这是一个合理的压缩率,你可以看到这种维度减小极大地加速了分类算法(例如SVM分类器)。 通过应用PCA投影的逆变换,还可以将缩减后的数据集解压缩回784维。由于投影会丢失一些信息(在5%的方差被丢弃),因此这不会给你原始的数据,但可能会接近原始数据。原始数据与重构数据(压缩后再解压缩)之间的均方距离称为重构误差。 以下代码将MNIST数据集压缩为154个维度,然后使用inverse_transform()方法将其解压缩回784个维度: 下图显示了原始训练集(左侧)中的一些数字,以及压缩和解压缩后的相应数字。您会看到图像质量略有下降,但数字仍然基本完好。 逆变换的公式如下所示。 如果将超参数svd_solver设置为”randomized”,则Scikit-Learn将使用一种称为Randomized PCA的随机算法,该算法可以快速找到前d个主成分的近似值。它的计算复杂度为,而不是完全SVD方法的,因此,当远远小于时,它比完全的SVD快得多: 默认情况下,svd_solver实际上设置为”auto”:如果m或n大于500并且d小于m或n的80%,则Scikit-Learn自动使用随机PCA算法,否则它将使用完全的SVD方法。如果要强制Scikit-Learn使用完全的SVD,可以将svd_solver超参数设置为”full”。 前面的PCA实现的一个问题是,它们要求整个训练集都放入内存才能运行算法。幸运的是已经开发了增量PCA(IPCA)算法,它们可以使你把训练集划分为多个小批量,并一次将一个小批量送入IPCA算法。这对于大型训练集和在线(即在新实例到来时动态运行)应用PCA很有用。 以下代码将MNIST数据集拆分为100个小批量(使用NumPy的array_split()函数),并将其馈送到Scikit-Learn的IncrementalPCA类,来把MNIST数据集的维度降低到154(就像之前做的那样)。请注意,你必须在每个小批量中调用partial_fit()方法,而不是在整个训练集中调用fit()方法: 另外,你可以使用NumPy的memmap类,该类使你可以将存储在磁盘上的二进制文件中的大型数组当作完全是在内存中一样来操作,该类仅在需要时才将数据加载到内存中。由于IncrementalPCA类在任何给定时间仅使用数组的一小部分,因此内存使用情况处于受控状态。如以下代码所示,这使得调用通常的fit()方法成为可能: 在第5章中,我们讨论了内核,这是一种数学技术,它可以将实例隐式映射到一个高维空间(称为特征空间),从而可以使用支持向量机来进行非线性分类和回归。回想一下,高维特征空间中的线性决策边界对应于原始空间中的复杂非线性决策边界。 事实证明,可以将相同的技术应用于PCA,从而可以执行复杂的非线性投影来降低维度。这叫作内核PCA(kPCA)。它通常擅长在投影后保留实例的聚类,有时甚至可以展开位于扭曲流形附近的数据集。 下面的代码使用Scikit-Learn的KernelPCA类以及用RBF内核来执行kPCA(有关RBF内核和其他内核的更多详细信息,请参见第5章): 下图显示了瑞士卷,它使用线性内核(相当于简单地使用PCA类)、RBF内核和sigmoid内核减小为二维。 由于kPCA是一种无监督学习算法,因此没有明显的性能指标可以帮助你选择最好的内核和超参数值。也就是说,降维通常是有监督学习任务(例如分类)的准备步骤,因此你可以使用网格搜索来选择在该任务上能获得最佳性能的内核和超参数。以下代码创建了一个两步流水线,首先使用kPCA将维度减少到二维,然后使用逻辑回归来分类。它使用GridSearchCV来查找kPCA的最佳内核和gamma值,以便在流水线的最后得到最好的分类准确率: 然后,可以通过best_params_变量来得到最佳内核和超参数: 另一种完全无监督的方法是选择产生最低重构误差的内核和超参数。请注意,重建并不像使用线性PCA那样容易。以下是原因。 下图显示了原始的瑞士卷3D数据集(左上)和使用RBF内核应用kPCA之后得到的2D数据集(右上)。多亏了内核技术,此变换在数学上等效于使用特征图φ将训练集映射到无限维特征空间(右下),然后使用线性PCA将变换后的训练集投影到2D。 请注意,如果我们可以对一个给定的实例在缩小的空间中反转线性PCA,则重构点将位于特征空间中,而不是原始空间中(例如,如图中的X所示)。由于特征空间是无限维的,因此我们无法计算重构点,无法计算真实的重构误差。幸运的是,有可能在原始空间中找到一个点,该点将映射到重建点附近,这一点称为重建原像。一旦你有了原像,就可以测量其与原始实例的平方距离。然后可以选择内核和超参数,最大限度地减少此重构原像误差。 你可能想知道如何执行这个重构。一种解决方案是训练有监督的回归模型,其中将投影实例作为训练集,将原始实例作为目标值。如果设置fit_inverse_transform=True,Scikit-Learn会自动执行此操作,如以下代码所示: 默认情况下,fit_inverse_transform=False,并且KernelPCA没有inverse_transform()方法。仅当你设置fit_inverse_transform=True时,才会创建此方法。 计算重建的原像误差: 您现在可以使用网格搜索和交叉验证来查找可以最小化此错误的内核和超参数。 局部线性嵌入(LLE)是另一种强大的非线性降维(NLDR)技术。它是一种流形学习技术,不像以前的算法那样依赖于投影。简而言之,LLE的工作原理是首先测量每个训练实例如何与其最近的邻居(c.n.)线性相关,然后寻找可以最好地保留这些局部关系的训练集的低维表示形式(稍后会详细介绍)。这种方法特别适合于展开扭曲的流形,尤其是在没有太多噪声的情况下。 以下代码使用Scikit-Learn的LocallyLinearEmbedding类来展开瑞士卷: 生成的2D数据集下图所示。如你所见,瑞士卷已完全展开,并且实例之间的距离在局部得到了很好的保留。但是,距离并没有在更大范围内保留:展开的瑞士卷的左侧部分被拉伸,而右侧部分被挤压。尽管如此,LLE在流形建模方面做得很好。 LLE的工作方式如下:对于每个训练实例,算法都会识别出其k个最近的邻居(在前面的代码k=10中),然后尝试将重构为这些邻居的线性函数。更具体地说,它找到权重,使得与之间的平方距离尽可能小,并假设如果不是的k个最接近的邻居之一则。因此,LLE的第一步是下面公式中描述的约束优化问题,其中W是包含所有权重的权重矩阵。第二个约束条件只是简单地将每个训练实例的权重归一化。 这个想法导致了下面公式中描述的无约束优化问题。它看起来与第一步非常相似,但不是保持实例固定并找到最佳权重,而是相反:保持权重固定并在低维空间中找到实例图像的最佳位置。注意是一个包含所有的矩阵。 还有许多其他降维技术,Scikit-Learn中提供了其中几种。以下是一些很受欢迎的降维技术: 随机投影 多维缩放(MDS) 等度量映射Isomap t分布随机近邻嵌入(t-SNE) 线性判别分析(LDA) 有关练习9和10的解答,请参见https://github.com/ageron/handson-ml2上提供的Jupyternotebook。 主成分分析是一种无监督的绒性降维方法,监督线性降维方法最著名的是线性判别分析(LDA)Fisher,1936】,其核化版本KLDA 【Baudatand Anouar,2000】。通过最大化两个变量集合之间的相关性,则可得到”典型相关分析”(Canonical Correlation Analysis,简称 CCA)Hotelling,1936】 及其核化版本KCCA 【Harden et al.,2004,该方法在多视图学习(multi-view learning)中有广泛应用.在模式识别领域人们发现,直接对矩阵对象(例如一幅图像)进行降维操作会比将其拉伸为向量(例如把图像逐行拼接成一个向量)再进行降维操作有更好的性能,于是产生了2DPCA Yang et al.,204】、2DLDA 【Ye et al,2005】、(2D)PCA 【Zhang and Zhou,2005】 等方法,以及基于张量(ten3or)的方法【Kolda and Bader,2009】。 除了 Isomap和LLE,常见的流形学习方法还有拉普拉斯特征映射 (Laplcian Eigenmaps,简称LE)【【Belkin and Niyogi,2003】、局部切空间对齐(LocalTangent Space Alignment,简称 LTSA)【Zhang and Zha,2004等】。局部保持投影(Locality Preserving Projections,简称 LPP)【He and Niyogi,2004】 是基于LE的线性降维方法.对监督学习而言,根据类别信息扭曲后的低维空间常比本真低维空间更有利【Geng et al.,2005】。值得注意的是,流形学习欲有效进行邻域保持则需样本密采样、而这恰是高维情形下面临的重大障碍,因此流形学习方法在实践中的降维性能往往没有预期的好:但邻域保持的想法对机器学习的其他分支产生了重要影响,例如半监督学习中有著名的流形假设、流形正则化【Belkin et al,2006】。【Yan et al,2007】从图嵌入的角度给出了降维方法的一个统一框架。 将必连关系、勿连关系作为学习任务优化目标的约束,在半监督聚类的研究中使用得更早【Wagstaf et al,2001】。在度量学习中,由于这些约束是对所有样本同时发生作用 【Xing et al,2003】,因此相应的方法被称为全局度量学习方法。人们也尝试利用局部约束(例如邻域内的三元关系),从而产生了局部距离度量学习方法【Weinberger and Saul,2009】,甚至有一些研究试图为每个样本产生最合适的距离度量Frome et al,2007;Zhan et a,2009】。在具体的学习与优化求解方面,不同的度量学习方法往往采用了不同的技术,例如 【Yang etal.,2006】将度量学习转化为判别式概率模型框架下基于样本对的二分类问题求解,【Davis et al,2007】将度量学习转化为信息论框架下的 Bregman 优化问题,能方便地进行在线学习。8.3 主成分分析 PCA
8.3.1 保留差异性
8.3.2 主要成分
import numpy as np
# 构建数据集
np.random.seed(4)
m = 60
w1, w2 = 0.1, 0.3
noise = 0.1
angles = np.random.rand(m) * 3 * np.pi / 2 - 0.5
X = np.empty((m, 3))
X[:, 0] = np.cos(angles) + np.sin(angles)/2 + noise * np.random.randn(m) / 2
X[:, 1] = np.sin(angles) * 0.7 + noise * np.random.randn(m) / 2
X[:, 2] = X[:, 0] * w1 + X[:, 1] * w2 + noise * np.random.randn(m)
# 调用奇异值分解SVD
X_centered = X - X.mean(axis=0)
U, s, Vt = np.linalg.svd(X_centered)
c1 = Vt.T[:, 0]
c2 = Vt.T[:, 1]
8.3.3 向下投影到d维度
以下Python代码将训练集投影到由前两个主要成分定义的平面上:W2 = Vt.T[:, :2]
X2D = X_centered.dot(W2)
8.3.4 使用Scikit-Learn
from sklearn.decomposition import PCA
pca = PCA(n_components=2) # 将数据集的维度降到2维
X2D = pca.fit_transform(X)
import numpy as np
# 构建数据集
np.random.seed(4)
m = 60
w1, w2 = 0.1, 0.3
noise = 0.1
angles = np.random.rand(m) * 3 * np.pi / 2 - 0.5
X = np.empty((m, 3))
X[:, 0] = np.cos(angles) + np.sin(angles)/2 + noise * np.random.randn(m) / 2
X[:, 1] = np.sin(angles) * 0.7 + noise * np.random.randn(m) / 2
X[:, 2] = X[:, 0] * w1 + X[:, 1] * w2 + noise * np.random.randn(m)
# 调用奇异值分解SVD
X_centered = X - X.mean(axis=0)
U, s, Vt = np.linalg.svd(X_centered)
c1 = Vt.T[:, 0]
c2 = Vt.T[:, 1]
print('X: ', X)
W2 = Vt.T[:, :2]
# X2D = X_centered.dot(W2)
print('X_centered:', X_centered)
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
X2D = pca.fit_transform(X)
print('X2D: ', X2D)
8.3.5 可解释方差比
print('输出:\n', pca.explained_variance_ratio_)
# 输出:
# [0.84248607 0.14631839]
8.3.6 选择正确的维度
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
# 导入MINST数据集
mnist = fetch_openml('mnist_784', version=1)
mnist.target = mnist.target.astype(np.uint8)
# 划分训练集和测试集
X = mnist["data"]
y = mnist["target"]
X_train, X_test, y_train, y_test = train_test_split(X, y)
# 应用(在不降低维度的情况下执行PCA,然后计算保留95%训练集方差所需的最小维度)
pca.fit(X_train)
cumsum = np.cumsum(pca.explained_variance_ratio_)
d = np.argmax(cumsum >= 0.95) + 1
print(d)
pca = PCA(n_components=0.95)
X_reduced = pca.fit_transform(X_train)
8.3.7 PCA压缩
pca = PCA(n_components=154)
X_reduced = pca.fit_transform(X_train)
# 逆变换: 解压缩
X_recovered = pca.inverse_transform(X_reduced)
8.3.8 随机PCA
# 随机PCA
rnd_pca = PCA(n_components=154, svd_solver="randomized")
X_reduced = rnd_pca.fit_transform(X_train)
8.3.9 增量PCA(IPCA)
# IPCA
from sklearn.decomposition import IncrementalPCA
n_batches = 100
inc_pca = IncrementalPCA(n_components=154)
for X_batch in np.array_split(X_train, n_batches):
inc_pca.partial_fit(X_batch)
X_reduced = inc_pca.transform(X_train)
filename = "my_mnist.data"
X_mm = np.memmap(filename, dtype="float32", mode="readonly", shape=(m, n))
batch_size = m // n_batches
inc_pca = IncrementalPCA(n_components=154, batch_size=batch_size)
inc_pca.fit(X_mm)
8.4 内核PCA(kPCA)
from sklearn.decomposition import KernelPCA
rbf_pca = KernelPCA(n_components=2, kernel="rbf", gamma=0.04)
X_reduced = rbf_pca.fit_transform(X)
选择内核并调整超参数
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)
print(grid_search.best_params_)
# {'kpca__gamma': 0.043333333333333335, 'kpca__kernel': 'rbf'}
rbf_pca = KernelPCA(n_components=2, kernel="rbf", gamma=0.0433, fit_inverse_transform=True)
X_reduced = rbf_pca.fit_transform(X)
X_preimage = rbf_pca.inverse_transform(X_reduced)
from sklearn.metrics import mean_squared_error
print(mean_squared_error(X, X_preimage))
# 输出:32.786308795766132
8.5 局部线性嵌入LLE
from sklearn.manifold import LocallyLinearEmbedding
lle = LocallyLinearEmbedding(n_components=2, n_neighbors=10)
X_reduced = lle.fit_transform(X)
在此步骤之后,权重矩阵(包含权重)对训练实例之间的局部线性关系进行编码。第二步是将训练实例映射到d维空间(其中d
Scikit-Learn的LLE实现具有以下计算复杂度:用于找到k个最近的邻居,用于优化权重,用于构造低维表示。不幸的是,最后一项中的使该算法很难扩展到非常大的数据集。8.6 其他降维技术
8.7 练习题
问题
答案
8.8 阅读材料
文章出处登录后可见!