1 目的
如果你有上百或者上千的特征变量,很容易就会忘记你到底有什么特征变量,而且有时候可能有几个不同的工程师团队。一队工程师可能给你200个特征变量,第二队工程师可能再给你300个特征变量,然后第三队工程师给你500个特征变量。所以你一共有1000个特征变量,这样就很难搞清哪个队给了你什么特征变量。实际上得到这样冗余的特征变量并不难。
假设我们不知道这两个特征量。其中x1是某个物体的长度,以厘米为单位;另一个x2是它以英寸为单位的长度。所以这是一个非常冗余的数据,与其用两个特征变量x1和x2,它们都是测量到的长度,或许我们应该把这个数据降到一维,只用一个长度的数据。
如果以厘米计的长度被取整到最近的厘米整数,以英寸计的长度被取整到最近的英寸整数。这就是为什么这些样本没有完美地在一条直线上。就是因为取整所造成的误差。
1.1 目的一:数据压缩
1.1.1 二维降到一维
在这个例子中降低维度的意思是:我希望找到一条线,基本所有数据映射到这条线上。这样做之后,我就可以直接测量这条线上每个样本的位置。我想把这个新特征叫做
在之前如果想要表示一个样本点,我需要一个二维向量
1.1.2 三维降到二维
降维在这里的作用,就是把所有的数据,都投影到一个二维的平面内。所以,我们要对所有的数据进行投影,使得它们落在这个平面上:
原始数据集是这样的:
可以看出来,大部分数据差不多可能都落在某个2D平面上,或者说距离某个2D平面不远。
所以我们可以把它们投影到2D平面上。下面是投影后的效果:
这就是把数据从三维降到二维的过程。
这就是降维以及如何使用它来压缩数据的过程。
1.2 目的二:可视化数据
在这里我们有大量的国家的数据,对于每个国家有50个特征。我们有这样的众多国家的数据集,为了使得我们能更好地来理解数据,我们需要对数据进行可视化展示。这里我们有50个特征,但绘制一幅50维度的图是异常困难的,因此我们需要对数据进行降维,然后再可视化。
我们用
在降维处理时,我们用
小结下:
- 数据压缩
- 减少内存或者磁盘空间的使用
- 提升学习算法的效率(k值的选择是关键)
- 数据可视化
- 将数据降维到二/三维度进行可视化展示
2 主成分分析 PCA
2.1 宏观理解
2.1.1 PCA的执行过程2D -> 1D
正式的说PCA所做的就是寻找一个低维的面(在这个例子中,其实是一条直线)数据投射在上面,使得这些蓝色小线段的平方和达到最小值。这些蓝色线段的长度被叫做投影误差。
PCA所做的就是寻找一个投影平面,对数据进行投影,使得这个能够最小化。
在应用PCA之前,通常的做法是先进行均值归一化和特征规范化,使得特征x1x1和x2x2均值为0,数值在可比较的范围之内。
我们正式一点地写出PCA的目标是这样的:
如果我们将数据从二维降到一维的话,我们需要试着寻找一个向量
所以当我把数据投影到这条向量所在的直线上时,最后我将得到非常小的重建误差。
另外需要说明的时无论PCA给出的是这个u^{(1)}是正还是负都没关系。因为无论给的是正的还是负的u^{(1)}它对应的直线都是同一条,也就是我将投影的方向。
更一般的情况是我们有n维的数据想降到k维。在这种情况下我们不仅仅只寻找单个的向量(u^{(1)})来对数据进行投影,我们要找到k个方向(u^{(k)})来对数据进行投影,从而最小化投影误差。
2.1.1 PCA的执行过程3D -> 2D
线代定义:我们将寻找一组向量u^{(1)},u^{(2)},…,u^{(k)},我们将要做的是将数据投影到这k个向量展开的线性子空间上。
PCA:寻找一组k维向量(一条直线、或者平面、或者诸如此类等等)对数据进行投影,来最小化正交投影误差。
2.2 PCA和线性回归的关系
PCA和线性回归有怎么样的关系?
- 对于线性回归,我们想做的是拟合一条直线,来最小化点和直线之间的平方误差;注意我画的这些蓝色的垂直线,这是垂直距离。
PCA要做的是最小化这些样本点与直线的最短距离(直角距离):
- 更更更一般的是,当你做线性回归的时候,有一个特别的变量y作为我们即将预测的值,线性回归所要做的就是用x的所有的值来预测y。然而在PCA中,没有这么一个特殊的变量y是我们要预测的。我们所拥有的是特征
x1,x2,…,xn ,所有的这些特征都是被同样地对待。
PCA不是线性回归。尽管有一定程度的相似性,使得它们看上去是有关联的,但它们实际上是非常不同的算法。
2.3 PCA算法 实现过程
2.3.1 数据预处理
拿到某组有m个无标签样本的训练集,一般先进行均值归一化(mean normalization)。这一步很重要。然后还可以进行特征缩放(feature scaling),这根据你的数据而定
这跟我们之前在监督学习中提到的均值归一和特征缩放是一样的。
- 数据预处理第一步:均值归一化(mean normalization)
μj=1m∑i=1mx(i)j
然后每个样本值对应的特征减去其对应的均值:
x(i)j←x(i)j−μj
将所有的特征替换为这种形式的结果。这样就保证了所有特征的均值为0。 - 数据预处理第二步:特征缩放(feature scaling)
将每个特征的取值范围都划定在同一范围内,因此对于均值化处理之后的特征值x_j^{(i)}-μ_j,我们还需要做进一步处理:
x(i)j←x(i)j−μjsj
这里sj 表示特征j度量范围,即该特征的最大值减去最小值。
2.3.1 数据预处理
PCA是在试图找到一个低维的子空间,然后把原数据投影到子空间上,并且最小化平方投影误差的值(投影误差的平方和,即下图中蓝色线段长度的平方和):
那么应该怎样来计算这个子空间呢? 实际上这个问题有完整的数学证明来解释如何找到这样的子空间,不过这个数学证明过程是非常复杂的,同时也超出了本课程的范围。但如果你推导一遍这个数学证明过程,你就会发现要找到u^{(1)}的值,也不是一件很难的事。但在这里,我不会给出证明,我只是简单描述一下实现PCA所需要进行的步骤。
假如说我们想要把数据从n维降低到k维,我们首先要做的是计算出下面这个协方差矩阵(通常用∑来表示):
相关证明
假如我们把它存为Octave中的一个名为Sigma的变量,我们需要做的是计算出Sigma矩阵的特征向量(eigenvectors)。
在Octave中,你可以使用如下命令来实现这一功能:
[U,S,V] = svd(Sigma);
顺便说一下,svd表示奇异值分解(singular value decomposition),这是某种更高级的奇异值分解,这是比较高级的线性代数的内容。你不必掌握这些,但实际上Sigma是一个协方差矩阵。有很多种方法来计算它的特征向量。
如果你线性代数学得很好,或者你之前听说过特征向量的话,那也许知道在Octave中还有另一个eig命令,可以用来计算特征向量。实际上svd命令和eig命令将得到相同的结果。但svd其实要更稳定一些,所以我一般选择用svd,不过我也有一些朋友喜欢用eig函数。
在这里对协方差矩阵Sigma使用eig和svd时,你会得到同样的答案。这是因为协方差均值总满足一个数学性质,称为对称正定(symmetric positive definite),其实你不必细究这个具体是什么意思,只要知道这种情况下,使用eig和svd结果是一样的就可以了。
小细节:
svd将输出三个矩阵,分别是U S V。你真正需要的是U矩阵。
如果我们想将数据的维度从n降低到k的话,我们只需要提取前k列向量。这样我们就得到了u^{(1)}到u^{(k)},也就是我们用来投影数据的k个方向。
我们取出U矩阵的前k列得到一个新的,由
这是一个n × k维的矩阵。
然后我们用这个
其中
这里的x可以是训练集中的样本,也可以是交叉验证集中的样本,也可以是测试集样本。
有一件事儿我没做:
u(1),u(2)…u(k) 通过将数据投影到k维的子平面上确实使得投影误差的平方和为最小值,但是我并没有证明这一点,因为这已经超出了这门课的范围。(这一部分证明后期博主会有新帖证明)
2.4 应用PCA
如果有一个这样的压缩算法,那么也应该有一种方法可以从压缩过的数据近似地回到原始高维度的数据。
假设有一个已经被压缩过的z(i)z(i)它有100个维度,怎样使它回到其最初的表示x(i)x(i)也就是压缩前的1000维的数据呢?
那么给出一个一维实数点zz我们能否,让zz重新变成原来的二维实数点xx呢?
即做到:
z∈R→x∈R2
我们知道:
如果想得到相反的情形,方程应这样变化:
为了检查维度,在这里
所以
同时根据PCA的意图,投影的平方误差不能很大。也就是说
这就是用低维度的特征数据z还原到未被压缩的特征数据的过程。我们找到一个与原始数据x近似的
2.5 选择主成分的数量k
在PCA算法中,我们把n维特征变量降维到k维特征变量。这个数字k是PCA算法的一个参数。这个数字k也被称作主成分的数量。在本节中我会给你们一些参考,告诉你们人们是怎样思考如何选择PCA的参数k的。
2.5.1 算法原理:最小化平均平方映射误差
PCA所做的是尽量最小化平均平方映射误差 (Average Squared Projection Error) 。
因此PCA就是要将下面这个量最小化:
即最小化x和其在低维表面上的映射点之间的距离的平方。这就是平均平方映射误差。
同时我们还要定义一下数据的总变差(Total Variation):
数据的总变差 (Total Variation) 是这些样本的长度的平方的均值。它的意思是 “平均来看,我的训练样本距离零向量(原点)多远?”。
当我们去选择k值的时候,我们通过平均平方映射误差除以数据的总变差来表示数据的变化有多大。我们想要这个比值能够小于1%:
大部分人在考虑,选择k的方法时,不是直接选择k值,而是这里的数字应该设置为多少:
数字0.01是人们经常用的一个值,另一个常用的值是0.05。如果选择了0.05,就意味着95%的差异性被保留了。从95到99是人们最为常用的取值范围。
你可能会惊讶的发现,对于许多数据集,即使保留了99%的差异性,可以大幅地降低数据的维度。因为大部分现实中的数据,许多特征变量都是高度相关的。所以实际上大量压缩数据是可能的,而且仍然会保留99%或95%的差异性。
2.5.2 初步具体实现
- 尝试k=1时的PCA。
- 计算出
Ureduce,z(1),z(2),…,z(m),x(1)approx,…,x(m)approx - 检查是否满足:
1m∑mi=1||x(i)−x(i)approx||21m∑mi=1||x(i)||2≤0.01
如果满足条件,我们就用k=1;但如果不满足,那么我们接下来尝试k=2,然后我们要重新走一遍这整个过程。
以此类推一直试到上面不等式成立为止。
2.5.3 一种更快的算法
可以想象,上面这种方式非常低效。每次尝试使用新的k值带入计算时,整个计算过程都需要重新执行一遍,还好我没有一种更快捷方便的计算方式。
当你调用svd来计算PCA时,你会得到三个矩阵[U,S,V]:
[U,S,V]=svd(Sigma)
除了之前提到的U矩阵之外,当你对协方差的矩阵Sigma调用svd时,我没还会得到中间的这个S矩阵。S矩阵是一个n×n的对角矩阵,它只有在对角线上的元素不为0,其余的元素都是0。并且显而易见,它是一个方阵:
可以证明的是(我不会在此证明)实际上对于一个给定的k值,可以通过这个S矩阵方便的计算出差异性那一项的值:
例如,假设差异性要满足小于0.01,那么可以得出:
即:
那么你可以从1开始,慢慢增大k的值,来计算上面这个不等式,直到满足为止即可(得到满足上面不等式的最小k值)。
通过这种方式,你只需要调用一次svd函数,通过svd给出的S矩阵你就可以通过依次增加k值的方式来求解了。这样以来就大幅的提升了计算效率。
2.5.4 高效具体实现
- 首先对协方差矩阵Sigma调用一次svd:
[U,S,V] = svd(Sigma)
- 然后使用下面的不等式求得满足条件的最小k值:
∑ki=1Sii∑ni=1Sii≥0.99
这就是一个平方投影误差的测量指标。它可以带给你对于数据压缩后是否与原始数据相似带来一种很好的直观感受。
顺便说一下,即使你想要手动挑选k值,如果你想要向别人解释你实现的PCA的性能具体如何,那么一个好方法就是算出这个值:
∑ki=1Sii∑ni=1Sii
它会告诉你百分之多少的差异性被保留了下来。
如果你把这个数值展现出来,那么熟悉PCA的人们就可以通过它来更好地理解你用来代表原始数据的压缩后的数据近似得有多好。因为有99%的差异性被保留了。
2.6 PCA的错误使用
有一个值得提醒的频繁被误用的PCA应用场景,那就是使用它来避免过拟合。
具体原因是将高维度数据降维处理后,相较于原先的数据,会更不容易出现过拟合的现象。例如我们将10000维的数据降到了1000维,那么降维后的1000维数据相较于降维前的10000维数据更不容易产生过拟合。
因此有人认为PCA是一种避免过拟合的方法,但在这里,我需要强调一下,为了解决过拟合问题而使用PCA是不适合的!并且我不建议这么做。
PCA会丢失信息:如果你仔细想想PCA的工作原理,你会发现它并不需要使用数据的标签,你只需要设定好输入数据
舍弃掉一些数据,并在你对数据标签yy值毫不知情的情况下对数据进行降维,所以这或许是一个使用PCA方法的可行之路。如果保留99%的方差,即保留绝大部分的方差,那也是舍弃了某些有用的信息。事实证明,当你在保留99%或者95%或者其它百分比的方差时,结果表明只使用正则化对于避免过拟合,会带来比较好的效果。
同时对于过拟合问题,正则化效果也会比PCA更好,因为当你使用线性回归或者逻辑回归或其他的方法配合正则化时,这个最小化问题实际就变成了y值是什么,才不至于将有用的信息舍弃掉。然而PCA不需要使用到这些标签,它更容易将有价值信息舍弃。
使用PCA的目的是加速学习算法,但不应该用它来避免过拟合。
2.7 使用PCA的两个建议
通常在一个项目的初期,有些人便直接写出这样的项目计划:
设计一个机器学习系统:
- 收集训练数据集
{(x(1),y(1)),(x(2),y(2)),…,(x(m),y(m))} - 运行PCA将数据
x(i) 降维到z(i) - 对降维后的数据训练逻辑回归算法
{(z(1),y(1)),(z(2),y(2)),…,(z(m),y(m))} - 在测试集上测试结果:将
x(i)test 映射到z(i)test 。对映射后的数据集{(z(1)test,y(1)test),…,(z(m)test,y(m)test)} 运行假设函数hθ(z)
在写下这样一个使用PCA方法的项目计划前,一个非常好的问题是:如果我们在整个项目中不使用PCA效果会怎样?
通常人们不会去思考这个问题,尤其是当人们提出一个复杂的项目并且其中使用了PCA或其它方法时,我经常建议大家在使用PCA之前,首先要想清楚你自己做的是什么,以及你想要做什么。这也是你首先需要在原始数据x^{(i)}上考虑的问题。并且根据具体情况来分析是否适合使用PCA,还是直接将原始数据带入到学习算法中。
我也建议一开始不要将PCA方法就直接放到算法里,先使用原始数据x(i) 看看效果。只有一个原因让我们相信算法出现了问题,那就是你的学习算法收敛地非常缓慢,占用内存或者硬盘空间非常大,所以你想来压缩数据。只有当你的x(i) 效果不好的时候,那么就考虑用PCA来进行压缩数据。
某些人在项目开始时便将PCA考虑进去,有时他们并没有仔细思考他们做了什么使得结果表现地好,更没有考虑在不用PCA下的情景会是什么样的效果。如果某个数据不使用PCA也可以工作的很好,但我们对于这些数据使用PCA耗费了大量时间,这是不值得的。
然而,尽管有这些需要注意的地方,PCA仍旧是一种不可思议的有用的算法。PCA的使用频率也很高,大部分时候我都用它来加快学习算法。但我认为PCA通常都是被用来压缩数据以减少内存使用或硬盘空间占用的、或者用来可视化数据的。
2.8 小结PCA
具体来讲,这种PCA所学习出的对应关系,所做的就是计算出一系列的参数。这些参数这就是特征缩放和均值归一化以及降维矩阵UreduceUreduce。但是对于降维矩阵UreduceUreduce中的数据,我们需要使我们的参数唯一地适应训练集,而不是适应交叉验证或者测试集。因此我们通过在训练集中找到了降维矩阵UreduceUreduce,我们就可以将同样的对应关系应用到其他样本中了,比如交叉验证数集样本,或者用在测试数据集中。
总结一下,当你在运行PCA的时候,只是在训练集那一部分来进行的,而不是在交叉验证的数据集或者测试集上运行。在训练集上运行PCA后,得到了从xx到zz的映射,然后你就可以将这个映射应用到交叉验证数据集,和测试数据集中。