降维(二)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)主成分分析 理论依据 最大方差理论 最小平方误差理论 一.最大方差理论(白面机器学习) 对一个矩阵进行降维,我们希望降维之后的每一维数 ...
随机推荐
- MySQL—MySQL的存储引擎之InnoDB
MySQL-MySQL的存储引擎之InnoDB 存储引擎及种类 存储引擎 说明 MyISAM 高速引擎,拥有较高的插入,查询速度,但不支持事务 InnoDB 5.5版本后MySQL的默认数据库存储引擎 ...
- LabView之MQTT协议使用
一.MQTT概述 MQTT协议是一种消息列队传输协议,采用订阅.发布机制,订阅者只接收自己已经订阅的数据,非订阅数据则不接收,既保证了必要的数据的交换,又避免了无效数据造成的储存与处理.因此在工业物联 ...
- M9K内存使用教程
M9K内存使用教程 M9K内存是Altera内嵌的高密度存储阵列.现代的FPGA基本都包含类似的不同大小的内存. M9K的每个块有8192位(包含校验位实际是9216位).配置灵活.详细了解M9K可参 ...
- SpringMVC学习五(resultful风格/异常处理/注解)
resultful风格 异常处理 1.Restfule风格 Restfule风格是一种软件架构风格,而不是标准,只是提供了一种设计原则和约束条件.主要适用于客户端和服务器端交互的软件.是基于http协 ...
- Headless BI
Headless的概念最初的来源与内容管理平台有关,一般是指内容管理平台中的一些应用不提供可视化界面,只是通过API方式把内容以数据的方式给前端.前端根据不同的设备类型,可以再去进行针对性地渲染和展现 ...
- 【2023最新B站评论爬虫】用python爬取上千条哔哩哔哩评论
目录 一.爬取目标 二.展示爬取结果 三.爬虫代码 四.同步视频 五.附完整源码 您好,我是@马哥python说,一枚10年程序猿. 一.爬取目标 之前,我分享过一些B站的爬虫: [Python爬虫案 ...
- 【Vue】vuex存储和本地存储(localstorage、sessionstorage)的区别
sessionStorage sessionStorage 方法针对一个 session 进行数据存储.当用户关闭浏览器窗口后,数据会被删除. 用法: 储存: 1. 点(.)运算符 sessionSt ...
- linux用户与用户组管理
linux用户与用户组管理 目录 linux用户与用户组管理 1.linux用户管理 1.1 用户基础 1.2 /etc/passwd:用户信息文件 1.3 /etc/shadow:用户密码信息文件 ...
- 为什么下载程序的时候会提示win-amd64.exe
- Java类加载过程&&静态代码块的初始化过程
问题的引入 还是老规矩,先说说自己遇到的问题. 最近看到了一个比较有意思的Java程序,初次看到这段程序执行的结果还是挺让我意外的,话不多说先上程序,大家也可以揣摩一下(大神自行略过......) c ...