本章内容
体育比赛中,人们面对的原本是百万像素的数据,但只有球的三维位置才是最重要的,这就被称为降维(dimensionality reduction)。在低维下,数据更容易进行处理。通常,在应用其他机器学习之前,须先识别出其相关特征。
对数据进行简化的原因:
在已标注和未标注的数据上都有降维技术,这里主要关注未标注数据上的降维技术。
在上述降维技术中,PCA应用最为广泛,文中只关注PCA。
优点:降低数据的复杂性,识别最重要的多个特征。
缺点:不一定需要,且可能损失有用信息。
使用数据类型:数值型数据。
考虑一下图1中的大量数据点。若要求画出一条直线,这条线要尽可能覆盖这些点,三条直线中B最长。在PCA中,对数据的坐标进行旋转,该旋转的过程取决于数据的本身。第一条坐标轴旋转到覆盖数据的最大方差为止,即直线B。数据的最大方差给出了数据的最重要的信息。
在选择了覆盖数据最大差异性的坐标轴后,继续选择第二条坐标轴。假如该坐标轴与第一条坐标轴垂直,它就是覆盖数据次大差异性的坐标轴。更严谨的说法是正交(orthogonal)。在二维平面下,垂直和正交是一回事。在图1中,直线C就是第二条坐标轴。利用PCA,可将数据坐标轴旋转至数据角度上的那些最重要的方向。
实现坐标轴旋转后,可讨论降维。坐标轴的旋转并没有减少数据的维度。图2中包含三个不同的类别。要区分这三个类别,可使用决策树。决策树每次都是基于一个特征来做决策。我们会发现,在x轴上可找到一些值,这些值能够很好地将这3个类别分开。这样可得到一些规则,比如当(X<4)时,数据属于类别0。若使用SVM这样稍复杂的分类器,可得到更好的分类面和分类规则,比如当(w0*x + w1*y + b) > 0时,数据也属于类别0。SVM可能比决策树得到更好的分类间隔,但分类超平面却很难解释。
通过PCA进行降维处理,可同时获得SVM和决策树的优点:一方面,得到了和决策树一样的简单分类器,同时分类间隔和SVM一样好。考虑图2中下面的图,其中的数据来自于上面的图并经PCA转换后绘制而成。如果仅使用原始数据,那么这里的间隔会比决策树的间隔大。另外,由于只需要考虑一维信息,因此数据可通过比SVM简单得多的、且容易采用的规则进行区分。
在图2中,只需一维信息即可,因为另一维信息只是对分类缺乏贡献的噪声数据。在二维平面下,这看上去微不足道,但在高维空间则意义重大。
对PCA的基本过程简单阐述后,接下来可通过代码实现PCA过程。前面提到第一个主成分就是从数据差异性最大(即方差最大)的方向提取出来的,第二个主成分则来自于数据差异次大的方向,并且该方向与第一个主成分方向正交。通过数据集的协方差矩阵及其特征值分析,可以求得这些主成分的值。
一旦得到了协方差矩阵的特征向量,就可以保留最大的N个值。这些特征向量也给出了N个最重要的真实结构。可以通过将数据乘上这N个特征向量而将它转到新的空间。
将数据转换成前N个主成分的伪代码如下:
去除平均值
计算协方差矩阵
计算协方差矩阵的特征值和特征向量
将特征值从大到小排序
保留最上面的N个特征向量
将数据转换到上述N个特征向量构建的新空间中
建立一个名为pca.py的文件并将下列代码加入用于计算PCA。
# coding=utf-8
from numpy import *
#
def loadDataSet(fileName, delim='\t') :
fr = open(fileName)
stringArr = [line.strip().split(delim) for line in fr.readlines()]
dataArr = [map(float, line) for line in stringArr]
return mat(dataArr)
# dataMat: 用于进行PCA操作的数据集
# topNfeat: 可选参数,即应用的N个特征。
# 若不指定topNfeat的值,那么函数就会返回前9999999个特征,或者原始数据中的全部特征
def pca(dataMat, topNfeat=9999999) :
# 计算平均值
meanVals = mean(dataMat, axis=0)
# 减去原始数据的平均值
meanRemoved = dataMat - meanVals
# 计算协方差矩阵及其特征值
covMat = cov(meanRemoved, rowvar=0)
eigVals, eigVects = linalg.eig(mat(covMat))
# 利用argsort()函数对特征值进行从小到大的排序,根据特征值排序结果的逆序就可以得到
# topNfeat个最大的特征向量
eigValInd = argsort(eigVals)
eigValInd = eigValInd[:-(topNfeat+1):-1]
# 这些特征向量将构成后面对数据进行转换的矩阵,该矩阵则利用N个特征将原始数据转换到新空间中
redEigVects = eigVects[:, eigValInd]
lowDDataMat = meanRemoved * redEigVects
reconMat = (lowDDataMat * redEigVects.T) + meanVals
return lowDDataMat, reconMat
代码的运行结果:
>>> import ml.pca as pca
>>> dataMat = pca.loadDataSet('c:\python27\ml\\testSet.txt')
# 进行PCA操作,lowDMat包含了降维之后的矩阵,这里是一个一维矩阵
>>> lowDMat, reconMat = pca.pca(dataMat, 1)
>>> import numpy
>>> numpy.shape(lowDMat)
(1000, 1)
# 绘制图形
>>> import matplotlib
>>> import matplotlib.pyplot as plt
>>> fig = plt.figure()
>>> ax = fig.add_subplot(111)
>>> ax.scatter(dataMat[:,0].flatten().A[0], dataMat[:,1].flatten().A[0], marker=
'^', s=90)
0x02CEC9B0>
>>> ax.scatter(reconMat[:,0].flatten().A[0], reconMat[:,1].flatten().A[0], marke
r='o', s=50, c='red')
0x02CE5F30>
>>> plt.show()
执行上述命令,会得到和图3类似的结果。
半导体制造数据拥有590个特征,需要对这些特征进行降维处理。该数据集有很多缺失值。缺失值以NaN表示。在590个特征下,几乎所有样本都有NaN,因此,去除不完整的样本不太现实。可将NaN替换成0,但由于不知道这些值得意义,这样做是下策。若它们是开氏温度,则将它们置为0这种策略就太差劲了。接下来使用平均值来代替缺失值,平均值根据那些非NaN得到。
#
def replaceNanWithMean() :
dataMat = loadDataSet('c:\python27\ml\\secom.data', ' ')
numFeat = shape(dataMat)[1]
for i in range(numFeat) :
# 计算非NaN值得平均值
meanVal = mean(dataMat[nonzero(~isnan(dataMat[:,i].A))[0],i])
# 将所有NaN替换为平均值
dataMat[nonzero(isnan(dataMat[:,i].A))[0],i] = meanVal
return dataMat
移除所有NaN后,考虑在该数据集上应用PCA。首先确认所需特征和可以去除特征的数目。PCA会给出数据中所包含的信息量。需要强调的是,数据(data)和信息(information)之间具有巨大的差别。数据指的是接受的的原始材料,其中可能包含噪声和不相关信息。信息是指数据中的相关部分。这些并非抽象概念,还可以定量地计算数据中所包含的信息并决定保留的比例。
接下来看看实现情况。
>>> reload(pca)
'ml.pca' from 'C:\Python27\ml\pca.py'>
>>> dataMat = pca.replaceNanWithMean()
>>> from numpy import *
>>> meanVals = mean(dataMat, axis=0)
>>> meanRemoved = dataMat - meanVals
>>> covMat = cov(meanRemoved, rowvar=0)
>>> eigVals, eigVects = linalg.eig(mat(covMat))
>>> eigVals
array([ 5.34151979e+07, 2.17466719e+07, 8.24837662e+06,
2.07388086e+06, 1.31540439e+06, 4.67693557e+05,
2.90863555e+05, 2.83668601e+05, 2.37155830e+05,
2.08513836e+05, 1.96098849e+05, 1.86856549e+05,
1.52422354e+05, 1.13215032e+05, 1.08493848e+05,
1.02849533e+05, 1.00166164e+05, 8.33473762e+04,
......
0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00])
在得到的一堆值中,会发现其中超过20%的值都是0。这意味着这些特征都是其他特征的副本,也就是说,它们可通过其他特征来表示,而本身并没有提供额外的信息。
接下来,我们了解一下部分数值的数量级。最前面的15个值得数量级大于 105 ,实际上那以后的值都变得非常小。这就相当于告诉我们只有部分重要特征,重要特征的数目也很快就会下降。存在一些小的负值,它们主要源自数值误差应该四舍五入成0。
图4给出了总方差的百分比,在开始几个主成分之后,方差就会迅速下降。
表1给出了这些主成分所对应的方差百分比和累计方差百分比。查看“累计方差百分比(%)”这一列就会注意到,前六个主成分就覆盖了数据96.8%的方差,而前20个主成分覆盖了99.3%的方差。这就表明,如果保留前6个而去除后584个主成分,我们就可以实现大概100:1的压缩比。另外,由于舍弃了噪声的主成分,将后面的主成分去除便使得数据更加干净。
表1 半导体数据中前七个主成分所占的方差百分比
主成分 | 方差百分比(%) | 累积方差百分比(%) |
---|---|---|
1 | 59.2 | 59.2 |
2 | 24.1 | 83.4 |
3 | 9.2 | 92.5 |
4 | 2.3 | 94.8 |
5 | 1.5 | 96.3 |
6 | 0.5 | 96.8 |
7 | 0.3 | 97.1 |
20 | 0.08 | 99.3 |
从上,可知在数据集的前面多个主成分中所包含的信息量。可以尝试不同的截断值来检验它们的性能。有些人使用能包含90%信息量的主成分数量,而其他人使用前20个主成分。我们一般无法精确知道所需要的主成分数目,必须通过在实验中取不同的值来确定。有效的主成分数目取决于数据集和具体应用。
上述分析能够得到所用到的主成分数目,然后可以将该数目输入到PCA算法中,最后得到约简后数据就可以在分类器中使用了。
降维技术使得数据更易使用,且能够消除数据中的噪声,使得其他机器学习任务更为精确。降维往往作为预处理步骤,在数据应用到其他算法之前清洗数据。有很多技术可以用于数据降维,其中,独立成分分析、因子分析和主成分分析比较流行,其中又以主成分分析应用最为广泛。
PCA可从数据中识别其主要特征,它是通过沿着数据最大方差方向旋转坐标轴来实现的。选择方差最大的方向作为第一条坐标轴,后续坐标轴与前面的坐标轴正交。协方差矩阵的特征值分析可以用一系列的正交坐标轴来获取。
本章的PCA将所有的数据集都调入内存,如果无法做到,就需要其他方法来寻找其特征值。