今天是机器学习专题的第27文章,我们一起来聊聊数据处理领域的降维(dimensionality reduction)算法。
我们都知道,图片格式当中有一种叫做svg,这种格式的图片无论我们将它放大多少倍,也不会失真更不会出现边缘模糊的情况。原因也很简单,因为这种图片是矢量图,一般的图片存储的是每一个像素点的颜色值,而在矢量图当中,我们存储的是矢量,也就是起点终点以及颜色。由于矢量图只记录起点终点,所以无论我们如何放大,图片都不会失真,而传统的图片就做不到这一点。
其实svg就相当于图片的降维,我们将上百万的像素点简化成了若干个矢量完成了图片的存储,大大减少了数据的规模。机器学习领域中的降维算法其实也是差不多的原理。
背景与原理
现在降维算法这个词已经越来越少听到了,在面试当中也很少被提及,这是有时代因素的。因为现在的计算资源以及存储资源越来越廉价了,在以前很难承担的计算量,现在变得越来越轻松。所以相对而言,降维算法没有之前热门了,也越来越少在面试当中出现。
从现状倒推回从前,我们大概可以猜到,在若干年以前,当我们面临海量无法承担的数据的时候,降维算法是多么的重要。因为,我们都知道,机器学习训练的速度和它使用的数据量有这非常密切的关系,使用10维特征和使用100维特征的模型的收敛速度至少是10倍以上的差距。那么,自然而然地我们就会想到,如果有某种方法可以将100维的数据”压缩“成10维,该有多好?
但问题来了,数据不是实体,我们真的可以随意压缩吗,这其中的原理是什么呢?
最根本的原理是既然特征可以用来训练模型,那么特征的分布和label的分布必然是有一定的内在联系的。也就是说数据并不是随意分散的,而是彼此之间有联系的。我们各种各样的压缩算法,本质上都是利用了数据之间的关联。
举个不是非常恰当,但是很直观的例子。假设说我们现在有三个特征,分别是一个人的考试成绩、智商以及努力程度。我们会很明显地发现,考试成绩和智商以及努力程度这两个特征高度相关。如果我们能够找到它们之间的关联,我们完全可以去掉考试成绩这个特征,而通过智商、努力程度和它的这种关联来推算出这个值来。当然既然是推算出来的,显然会和原本的值有一定的误差,这也是不可避免的。
从这个例子当中,我们可以明确两点,首先,压缩数据是利用的数据分布的关联或者是特性,如果是完全随机的数据是无法降维压缩的。其次,降维压缩必然会带来信息损失,也就是误差,这是不可避免的。
降维算法
降维压缩的算法有好几种,常见的有PCA、ICA和FA,下面我们来简单介绍一下。
首先是PCA,PCA的英文全称是Principal Component Analysis即主成分分析。这种方法的主要原理是对数据进行坐标变换,即将数据从原来的坐标系更换到新的坐标系。新的坐标轴是通过最大方差理论推导得到的,即新的坐标轴包含了原始数据中大部分的方差,这里的方差可以理解成信息。
ICA的英文是Independent Component Analysis即独立成分分析,在这个算法当中它假设数据是通过N个数据源生成的。假设数据是这N个数据源数据混合观察的结果。这些数据源在统计上是互相独立的,如果数据源的数目少于原始特征的数目,也可以完成降维。
最后是FA即Factor Analysis即因子分析。在因子分析当中,我们假设样本当中存在一些隐变量,我们假设样本是这些隐变量和一些噪音的线性组合。那么只要这些隐变量的数量少于原始特征的数量,我们就可以用这些隐变量来作为新的数据从而实现降维。
这三种降维算法虽然各不相同,但是核心的思路都是一致的。都是假设数据的分布满足某一种特性,通过利用这一种特性来对数据进行压缩。这其中使用范围最广的是PCA,所以我们着重来了解一下PCA的原理以及实现。
理论推导
关于PCA算法有两种通俗的解释,一种是最大方差理论,另外一种是最小化降维损失,这两个思路推导出的结果是一样的。相比之下,最大方差理论更加容易理解一些,所以我们就选择最大方差理论来做个简单的解释。
在信号系统当中,我们普遍认为信号具有较大的方差,而噪音拥有较小的方差。信噪比就是信号与噪声的方差比,这个比值越大越好,越大说明噪音越小,信号的质量越高。比如下图当中的这个数据分布,我们可以在原始数据当中找到两个正交轴,根据方差最大理论,我们会把方差大的那个轴看成是信号,方差小的看成是噪音。
根据这个思路,最好的k维特征是将n维的样本转换成k维坐标之后,拥有最大方差的k个。
协方差
到这里,我们虽然知道了要获取方差最大的方向作为新的坐标轴,但是如果我们直接去计算的话是会有问题的。最大的问题在于我们没办法选出K个来,如果只是选择类似的K个方向,这K个轴的信息都差不多,会丢失大量的信息。所以我们不仅要选择K个轴,而且要保证这K个轴尽可能线性无关。
要做到线性无关,也就是说这K个轴应该是彼此正交的。如果两个轴正交,可以进一步得到这两个轴的协方差为零。为了简化运算,我们可以先让原始数据全部减去各自特征的均值。在去除均值之后,两个特征的协方差可以表示为:
两个特征正交等价于它们的协方差为0,我们假设去除了均值之后的矩阵为X,我们来写出它的协方差矩阵。
协方差矩阵
对于去除了均值的矩阵X而言,有一个性质是它的协方差矩阵
X_cov=1/m X X^T。我们可以来简单证明一下,假设矩阵当中只有两个特征a和b,那么我们将它按行写成矩阵:
我们假设X的协方差矩阵为C,那么C是一个对称矩阵,它的对角线上的元素表示各个特征的方差,其他的元素则表示特征之间的协方差。我们的目标是希望能够得到一个类似形式的对角矩阵,也就是说除了对角线之外的其余元素全为0,这样这些特征之间就是正交矩阵,我们根据对角线上的值挑选出方差最大的K个特征即可。
我们的目的和方向已经很明确了,距离终点只有一步之遥,但是这一步怎么迈过去呢?
对角化
这里我们采用逆向思维来思考,假设我们已经找到了矩阵P,通过P对X进行线性变换的结果是Y,那么Y=PX,我们假设Y的协方差矩阵为D,那么根据刚才我们推导的结论可以得到:
我们希望D是一个对角矩阵,所以我们要寻找的就是P,P找到之后一切都迎刃而解。因为D是一个对角矩阵,我们将它对角的元素从大到小排列之后,对应P的行组成的矩阵就是我们寻找的基。我们用P的前K行组成的新矩阵对原始数据X进行线性变换,就将它从n维降低到了K维。
所以问题就只剩下了一个,这个P矩阵要怎么求呢?我们干想是很困难的,其实数据家们已经给了我们答案,就是C矩阵的特征向量。
由于C是对称矩阵,根据线性代数的原理,它有如下两条性质:
- 对称矩阵不同的特征值对应的特征向量必然正交
- 特征值是实数,K重特征值对应的线性无关的特征向量刚好有K个
根据这两条性质,我们可以得到,对于n*n的矩阵C来说,我们可以找到n个特征向量 e_1, e_2, ... , e_n。我们将它们按列组成矩阵:
我们通过E可以将C对角化:
我们对Lambda中的特征值从大到小排列,选出前K个特征值对应的特征向量组成矩阵即得到了最终的结果P。
最后,我们整理一下上述的整个过程。
- 每一维特征减去平均值
- 计算协方差矩阵
- 求解协方差矩阵的特征值和特征向量
- 对特征值降序排序,选择其中最大的K个,然后将对应的K个特征向量作为行向量组成特征向量P
- 转换之后的结果X_t = PX
我们把这个逻辑整理一下,写成代码:
import numpy as npdef pca(df, k): mean = np.mean(df, axis=0) new_df = df - mean # 计算协方差矩阵,也可以用公式自己算 cov = np.cov(new_df, rowvar=0) # 求解矩阵特征值和特征向量 eigVals, eigVects = np.linalg.eig(np.mat(cov)) # 对特征值排序,选最大的K个,由于是从小到大排,所以我们取反 eigValIndice = np.argsort(-eigVals) # 构建变换矩阵 n_eigValIndice = eigValIndice[:k] n_eigVect = eigVects[:, n_eigValIndice] data_ret = new_df.dot(n_eigVect) return data_ret
实战验证
为了验证程序效果,我们找了一份经典的机器学习数据:http://archive.ics.uci.edu/ml/datasets/SECOM。
我们把它下载下来之后,用pandas读入进来:
可以看到它的特征有590维,展开看的话会发现特征当中有许多空值:
我们对它进行一个简单地预处理,将空值替换成特征均值,并且再读入label的值:
为了验证PCA降维的效果,我们用同样一份数据,用同样的模型,比较一下做PCA之前和之后模型的效果。
这里我选择的是随机森林,其实不管用什么模型都大同小异。我们将数据拆分成训练数据与测试数据,并且调用skelarn库当中的随机森林完成训练和预测,最后计算模型在测试集当中的表现。说起来挺复杂,但是由于sklearn替我们完成了大量的工作,所以用到的代码并不多:
我们可以看到,在PCA之前,随机森林在测试集上的表现是92.3%的准确率。
接下来,我们用同样的数据和模型来验证PCA之后对于模型性能的影响。为了保证数据集的完全一致,我们把测试集的随机种子也设置成一样。
可以看到模型在测试集上的准确率完全一样,说明PCA并没有过多降低模型的性能,和我们的预期一致。
总结
在今天的文章当中,我们详细介绍并推导了PCA背后的原理,并采取实际数据集验证了PCA算法的效果。从最后的结果上来看,虽然我们将590维的特征缩减到了10维,但是模型的效果却几乎没有多大影响,可见PCA的威力。
当然,这背后的因素很多,除了PCA本身的原理之外,和数据的分布以及训练测试样本的数量也有关系。在极端场景下,可能特征的数量非常多,含有大量的噪音,如果我们不做降维直接训练的话,很有可能导致模型很难收敛。在这种情况下,使用降维算法是必要的,而且会带来正向的提升。如果特征数量不多,模型能够收敛,使用降维算法可能没什么助益,而且会稍稍降低模型的效果。但在一般的情况下,数据集特征的分布也符合二八定律,即20%的特征带来80%以上的贡献,大部分特征效果不明显,或者噪音很多。在这种情况下,使用PCA进行降维,几乎是一定起到正向作用的。
当然在实际的应用场景当中,降维算法用的越来越少,除了计算能力提升之外,另外一个很重要的原因是深度学习的兴起。深度神经网络本身就带有特征筛选的效果,它自己会选择合适的特征组合达到最好的效果,所以很多特征处理和降维等操作显得不是特别有必要了。虽然如此,但是算法本身的思想还是很有借鉴作用,PCA算法在Kaggle比赛当中使用频率也很高,对它进行详细地了解和学习还是很有必要的。
今天的文章就到这里,如果喜欢本文,可以的话,请点个赞和关注吧,给我一点鼓励,也方便获取更多文章。
本文始发于公众号:TechFlow