降维(二)PCA
PCA
主成成分分析(Principal Component Analysis,PCA)在目前是非常热门的降维算法。首先它找到一个最接近数据的超平面,然后将数据投影到这个平面上。
保持方差(Preserving the Variance)
在将训练集投影到一个低维超平面之前,我们首先要选择正确的超平面。例如,下图左图是一个简单的2D数据集,伴随有3个不同的轴(也就是1D超平面)。右边是数据集在每个轴上投影后的结果。我们可以看到,在实线上的投影保有最大的方差,而投影到点状线上的结果保有非常低的方差,在虚线上的结果保有中等的方差:

看起来选择那条保有最大方差的轴会比较合理,因为它相对其他轴来说,是最可能丢失更少信息办法。另一个方法是:证明这个选择是使得投影到轴前后的点之间均方误差最小的。这也就是PCA背后的简单思想。
主成成分(Principle Components)
PCA会在训练集中找出让方差最大的那些轴,也就是上图中的那条实线。它同时也会找第二个轴,与第一个轴正交的轴。在上面的2D例子中,毫无疑问,它是那个点状线。如果它是一个更高维的数据集,PCA还会找第三个轴,与前两个轴均正交的轴,然后第4个、第5个,依此类推,直到找到与数据集维度相等的轴个数。
第ith轴称为数据的第ith主成成分(principal component)。在上图的2D例子中,第一个PC是向量c1 所在的那个轴上,第二个PC是向量c2所在的那个轴。
需要注意的是,对每个主成成分,PCA会找到一个零中心单位向量(zero-centered unit vector)指向PC的方向。由于两个相反的单位向量在同一个轴上,所以PCA返回的单位向量并不是稳定的:如果我们轻微干扰训练集然后再次运行PCA,单位向量可能与原始向量指向的方向相反。不过,它们一般会仍在同样的轴上。在某些情况下,一对单位向量甚至可能旋转或交换(如果与这两个轴对应的方差很接近的话),不过它们定义的平面一般会保持一样。
那,我们如何找到训练集的主成成分呢?幸运的是,有一个标准矩阵分解技术,奇异值分解(Singular Value Decomposition,SVD)可以将训练集X分解为3个矩阵的乘积:U Σ V⊺。V包含的是我们要找的那些定义了所有主成成分的单位向量,如下公式所示:

下面的Python 代码使用NumPy的svd() 方法,可以获取训练集的所有主成成分,然后提取出两个单位向量,它们定义了前两个PC:
X_centered = X - X.mean(axis=0)
U, s, Vt = np.linalg.svd(X_centered)
c1 = Vt.T[:, 0]
c2 = Vt.T[:, 1]
需要注意的是,PCA假设数据集是以原点为中心的。在sk-learn中的PCA类会为我们处理好数据中心化的操作。如果我们自行实现PCA的话(例如上面的例子),或者我们使用其他库的话,千万不要忘了先将数据中心化。
投影到d维
在我们找到所有主成成分后,若是我们需要将数据集的维度降到d维,则可以将它投影到前d个主成成分定义的超平面上即可。选择这个超平面可以确保投影保有尽可能大的方差(variance)。
将训练集投影到超平面,并获取数据集降到d维后的Xd-proj数据集时,需要计算训练集矩阵X与矩阵Wd的乘积,Wd代表的是V矩阵中前d列的矩阵。如下公式所示:
Xd-proj=XWd
下面的python代码将训练集投影到由两个主成成分定义的平面上:
W2 = Vt.T[:, :2]
X2D = X_centered.dot(W2)
使用sk-learn
Sk-learn的PCA类使用SVD分解实现 PCA,与我们前面提到的类似。下面的代码应用PCA对数据及降维到2维(需要注意的是,它自动处理了数据中心化):
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
X2D = pca.fit_transform(X)
在将PCA transformer 应用到数据集后,它的components_ 属性会包含的Wd转置(例如,定义了第一个主成成分的单位向量等同于pca.components_.T[:, 0])。
Explained Variance Ratio
另一个非常有用的信息是每个主成成分的解释方差比(explained variance ratio),可以通过explained_variance_ratio_ 变量获取。这个比率表示的是数据集的方差沿着每个主成成分上的占比。例如,我们以一个3D数据集为例,它的前两个主成成分的explained variance ratio 为:
pca.explained_variance_ratio_
array([0.84248607, 0.14631839])
这个结果告诉我们,84.2% 的数据集方差(variance)落在第一个PC,14.6% 落在第二个PC。这个还给第三个PC留下了少于1.2% 的方差,所以我们可以很合理地假设第3个PC可能并没有携带多少信息。
选择正确的维度数
在选择维度数时,不是随便选,而是选择使得explained variance ratio总和足够大(例如95%)的维度数。除非我们是要做数据可视化,这种情况下,我们会将维度降维到2或3。
下面的代码执行PCA,但不进行降维,然后计算最小维度数,使得它能保留训练集方差的95%以上:
pca = PCA()
pca.fit(X_train)
cumsum = np.cumsum(pca.explained_variance_ratio_)
d = np.argmax(cumsum >= 0.95) + 1
然后我们可以设置 n_components=d,并再次执行PCA。不过有一个更好的选项:不设置保留的主成成分数,而是设置n_components 为一个0.0到1.0之间的float值,表示的是我们希望保留的variance ratio量:
pca = PCA(n_components=0.95)
X_reduced = pca.fit_transform(X_train)
还有另一个选项是画出explained variance对维度数的图(直接画出cumsum即可,如下图所示)。一般它都是一个弓形图,explained variance会先快速增长,之后便停止快速增长,我们选择停止快速增长的那个点即可。在这个例子中,我们可以看到将维度降到100时,不会损失太多的explained variance。

PCA 与压缩
在执行降维后,训练集会占据更少的空间。例如,如果对MNIST数据集应用PCA,且保留95%的方差。则我们会看到每个实例将会仅有150个特征,而不是之前的784个特征。所以,在保留有大部分的方差的情况下,数据集现在仅有低于20%原有数据集的大小。这个是一个非常好的压缩比,并且我们也可以看到这种数量的减少极大地增加了分类算法的速度(例如SVM分类器)。
当然我们也可以将减少后的数据集解压缩回原来的784维,通过PCA投影逆变换即可。这个操作不会归还原有的数据,因为投影的操作损失了一小部分的信息(5% 以内的variance 被丢弃),但是它会与原数据非常近似。原数据与重建数据(也就是压缩后再解压缩)之间的均方距离称为重建误差(reconstruction error)。
下面的代码将MNIST 数据集压缩到154维,然后使用 inverse_transform() 方法将它解压回784维:
pca = PCA(n_components = 154)
X_reduced = pca.fit_transform(X_train)
X_recovered = pca.inverse_transform(X_reduced)
下图展示了原始训练集的几个手写数字(左图),以及对应压缩与解压缩后的数字。我们可以看到仅有小部分的画质丢失,但是数字仍基本完整。

逆转换的公式如下所示

随机化PCA
如果我们设置svd_solver 超参数为“randomized”,则sk-learn会使用一个随机算法,称为Randomized PCA,它会非常快速地找到前d个主成成分的近似值。它的计算复杂度为O(m × d2) + O(d3),而原始的full SVD计算复杂度为O(m × n2) + O(n3) 。所以这个方法比full SVD要快得多,特别是在d远小于n的情况下。
rnd_pca = PCA(n_components=154, svd_solver="randomized")
X_reduced = rnd_pca.fit_transform(X_train)
默认情况下,svd_solver 设置为“auto”:如果m或者n大于500,且d小于m或 n 的80%,则sk-learn会自动使用PCA算法,否则使用full SVD。如果希望强制sk-learn使用full SVD,则我们可以设置 svd_solver 超参数为“full”。
增量PCA
前面实现PCA的方法有一个问题,它需要将整个训练集放入内存,才能保证算法运行。不过幸运的是,增量PCA(Incremental PCA,IPCA)算法已被开发。它允许我们将训练集分割成多个小的batch,每次向IPCA算法输入一个batch数据集。这个在大型训练集和在线PCA(也就是一直有新数据实例过来)中非常有帮助。
下面的代码将MNIST数据集分割成100个mini-batch(使用Numpy的array_split() 方法),并将它们输入到sk-learn 的IncrementalPCA类中,将MNIST数据集的维度减少到154维。需要注意的是,我们必须要为每个mini-batch调用一次partial_fit() 方法,而不是在整个训练集上调用fit() 方法:
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)
或者,我们也可以使用NumPy的memmap类,它可以让我们在一个位于磁盘上的二进制文件中操作一个非常大的数组,看起来就像它全部在内存里一样。这个类仅载入它需要的数据到内存。由于IncrementalPCA类每次仅适用一小部分数组数据,所以内存的使用是可以控制的。这样可以让我们直接调用fit() 方法称为可能,如下代码所示:
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)
核化PCA
在SVM中我们介绍过核方法,一个数学技巧,用于将数据实例映射到一个非常高维的空间(称为特征空间),以让SVM处理非线性分类与回归问题。而在高维特征空间中的一个线性决策边界,对应的是一个在原空间里复杂非线性的决策边界。
同样的方法也可以应用在PCA中,可以让PCA执行一个复杂的非线性投影,以实现降维。这个称为核化PCA(Kernel PCA,kPCA)。它一般可以在投影后仍很好地保留原数据的簇(clusters),或者有时候甚至展开一个接近弯曲的流形数据集。
下面的代码使用sk-learn的KernelPCA类执行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)
下图展示的瑞士卷分别使用线性核(等同于仅使用PCA类),RBF核和sigmoid核进行降维到2维空间后的图:

选择核并调优超参数
由于kPCA是一个非监督学习,所以没有一个很明显的性能衡量方式来帮助我们选择一个最好的核以及超参数的值。也就是说,降维一般是监督学习任务(如分类)中的一个准备阶段,所以我们可以使用网格搜索,用于选择使得任务表现最好的核以及超参数,。
下面的代码创建了一个pipeline,包含两个步骤。第一个使用kPCA将维度降为2,然后使用逻辑回归做分类。第二个步骤使用GridSearchCV为kPCA找到最佳的核以及gama值,以使得分类准确性最高。
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_)
{'kpca__gamma': 0.043333333333333335, 'kpca__kernel': 'rbf'}
还有另一个方法,这次是完全非监督的,是选择令重建误差(reconstruction error)最小的核与超参数。需要注意的是,重建并不像线性PCA那样简单。下图展示了原始的瑞士卷3D数据集(左上图)以及在执行kPCA(使用RBF核)后的2D数据集(右上图)。归因于核方法,这个转换在数学上等同于使用特征映射φ将训练集映射到了一个无线维特征空间中(右下图),然后使用线性PCA将转换后的训练集投影到一个2D空间。

需要注意的是,对于给定一个在已降维空间里的数据实例来说,如果我们可以对它进行反转线性PCA的步骤,那这个重建后的点会位于一个特征空间中,但不是原空间中(例如图中由X表示的那个点)。因为特征空间是无限维的,我们无法计算重建后的点,并因此无法计算出真正的重建误差。不过幸运的是,我们可以在原始空间中找到一个点,它对应于重构后的点(不是完全对应,而是接近)。这个点称为重构原相(reconstruction pre-image)。一旦我们有了这个原像后,我们就可以衡量它到原始数据的平方距离。我们接着就可以选择使得重构原像误差最小的核与超参数。
那如何执行这个重建呢?一个解决方法是训练一个监督回归模型,使用投影后的实例作为训练集,原始实例为labels。sk-learn中,如果设置了fit_inverse_transform=True,则sk-learn会自动做这个步骤,如下所示:
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)
需要注意的是,默认情况下,fit_inverse_transform=False与KernalPCA没有inverse_transform()方法。这个方法仅在设置fit_inverse_transform=True后才会被创建。
接着可以计算重构原像误差:
from sklearn.metrics import mean_squared_error
mean_squared_error(X, X_preimage)
现在我们就可以使用网格搜索与交叉验证用于找到使得这个误差最小的核与超参数。
降维(二)PCA的更多相关文章
- 机器学习实战基础(二十三):sklearn中的降维算法PCA和SVD(四) PCA与SVD 之 PCA中的SVD
PCA中的SVD 1 PCA中的SVD哪里来? 细心的小伙伴可能注意到了,svd_solver是奇异值分解器的意思,为什么PCA算法下面会有有关奇异值分解的参数?不是两种算法么?我们之前曾经提到过,P ...
- 机器学习实战基础(二十二):sklearn中的降维算法PCA和SVD(三) PCA与SVD 之 重要参数n_components
重要参数n_components n_components是我们降维后需要的维度,即降维后需要保留的特征数量,降维流程中第二步里需要确认的k值,一般输入[0, min(X.shape)]范围中的整数. ...
- 机器学习实战基础(二十一):sklearn中的降维算法PCA和SVD(二) PCA与SVD 之 降维究竟是怎样实现
简述 在降维过程中,我们会减少特征的数量,这意味着删除数据,数据量变少则表示模型可以获取的信息会变少,模型的表现可能会因此受影响.同时,在高维数据中,必然有一些特征是不带有有效的信息的(比如噪音),或 ...
- 机器学习实战基础(二十七):sklearn中的降维算法PCA和SVD(八)PCA对手写数字数据集的降维
PCA对手写数字数据集的降维 1. 导入需要的模块和库 from sklearn.decomposition import PCA from sklearn.ensemble import Rando ...
- 机器学习实战基础(二十四):sklearn中的降维算法PCA和SVD(五) PCA与SVD 之 重要接口inverse_transform
重要接口inverse_transform 在上周的特征工程课中,我们学到了神奇的接口inverse_transform,可以将我们归一化,标准化,甚至做过哑变量的特征矩阵还原回原始数据中的特征矩阵 ...
- 机器学习实战基础(二十):sklearn中的降维算法PCA和SVD(一) 之 概述
概述 1 从什么叫“维度”说开来 我们不断提到一些语言,比如说:随机森林是通过随机抽取特征来建树,以避免高维计算:再比如说,sklearn中导入特征矩阵,必须是至少二维:上周我们讲解特征工程,还特地提 ...
- 机器学习实战基础(二十五):sklearn中的降维算法PCA和SVD(六) 重要接口,参数和属性总结
到现在,我们已经完成了对PCA的讲解.我们讲解了重要参数参数n_components,svd_solver,random_state,讲解了三个重要属性:components_, explained_ ...
- 机器学习实战基础(二十六):sklearn中的降维算法PCA和SVD(七) 附录
- 机器学习基础与实践(三)----数据降维之PCA
写在前面:本来这篇应该是上周四更新,但是上周四写了一篇深度学习的反向传播法的过程,就推迟更新了.本来想参考PRML来写,但是发现里面涉及到比较多的数学知识,写出来可能不好理解,我决定还是用最通俗的方法 ...
- 降维【PCA & SVD】
PCA(principle component analysis)主成分分析 理论依据 最大方差理论 最小平方误差理论 一.最大方差理论(白面机器学习) 对一个矩阵进行降维,我们希望降维之后的每一维数 ...
随机推荐
- 【Python爬虫案例】用python爬1000条哔哩哔哩搜索结果
目录 一.爬取目标 二.讲解代码 三.同步讲解视频 四.完整源码 一.爬取目标 大家好,我是 @马哥python说 ,一名10年程序猿. 今天分享一期爬虫的案例,用python爬哔哩哔哩的搜索结果,也 ...
- PHP游戏线下线上陪玩平台APP小程序H5源码开发多少钱?可用于家政,陪诊,陪伴服务等
做陪玩app项目,不少创业者们都比较头疼该如何去选择软件系统!目前软件市场上,陪玩app平台的软件系统五花八门,价位也是参差不齐.创业者们都比较纠结是定制开发,还是选择开元源码二次开 发? 前两天成都 ...
- rails byebug
Gemfile里添加 gem 'byebug' bundle install 在要打断点的地方写 byebug byebug -h #帮助 c 放行,入下走 n 单行调适 q 退出进行 启动异步任务推 ...
- Could not find mimemagic-0.3.2 in any of the sources
rails s报如下错误 Could not find mimemagic-0.3.2 in any of the sources Run `bundle install` to install mi ...
- golang 中 strings 包的 Replace 用法介绍笔记
函数申明: func Replace(s, old, new string, n int) string 官方描述为: 返回将s中前n个不重叠old子串都替换为new的新字符串,如果n<0会替换 ...
- vue3编译优化之“静态提升”
前言 在上一篇 vue3早已具备抛弃虚拟DOM的能力了文章中讲了对于动态节点,vue做的优化是将这些动态节点收集起来,然后当响应式变量修改后进行靶向更新.那么vue对静态节点有没有做什么优化呢?答案是 ...
- IDEA 2020 版配置VUE
找到IDE工具栏,就是启动项目的run那里 点击下拉框,找到Eidt Confiuration,选择 选择小加号 选取npm 设置npm页,完成后,点击apply run npm ,如图选择run或者 ...
- BigDecimal类的基本使用
概述 java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算.双精度浮点型变量double可以处理16位有效数.在实际应用中,需要对更大或者更小的 ...
- iPhoneX 适配总结
一.iPhoneX适配第一步,根据iPhoneX的屏幕像素大小,引入对应的启动图,告诉系统,app兼容iPhoneX 需要在launchimage中引入一张 1125*2436的png,app将默认展 ...
- iOS开发环境theos开发环境搭建与介绍
iOS开发环境theos开发环境搭建与介绍 标签(空格分隔): 越狱开发-第一篇 1. 环境准备 一台Mac,本人的机器是MacBook Air (13-inch, Mid 2013),系统是10.1 ...