降维的目标就是对输入的数目进行削减,由此剔除数据中的噪声并提髙机器学习方法的性能。
通常而言,我们在应用其他机器学习算法之前,必须先识别出其相关特征。
始终贯穿本书的一个难题就是对数据和结果的展示。
对数据进行简化还有如下一系列的原因:
第一种降维的方法称为主成分分析(Principal Component Analysis, PCA ) 。
在PCA中,数据从原来的坐标系转换到了新的坐标系,新坐标系的选择是由数据本身决定的。第一个新坐标轴选择的是原始数据中方差最大的方向,第二个新坐标轴的选择和第一个坐标轴正交且具有最大方差的方向。
该过程一直重复,重复次数为原始数据中特征的数目。我们会发现,大部分方差都包含在最前面的几个新坐标轴中。因此,我们可以忽略余下的坐标轴,即对数据进行了降维处理。
另外一种降维技术是因子分析(Factor Analysis)。在因子分析中,我们假设在观察数据的生成中有一些观察不到的隐变量(latentvariable)。假设观察数据是这些隐变量和某些噪声的线性组合。那么隐变量的数据可能比观察数据的数目少,也就是说通过找到隐变量就可以实现数据的降维。
还有一种降维技术就是独立成分分析(Independent Component Analysis ICA)。ICA假设数据是从N个数据源生成的,这一点和因子分析(因子分析是认为有隐变量)有些类似。假设数据为多个数据源的混合观察结果,这些数据源之间在统计上是相互独立的,而在PCA中只假设数据是不相关的。同因子分析一样,如果数据源的数目少于观察数据的数目,则可以实现降维过程。
第一条坐标轴旋转到覆盖数据的最大方差位置(下图中的直线B ),数据的最大方差给出了数据的最重要的信息。在选择了覆盖数据最大差异性的坐标轴之后,我们选择了第二条坐标轴。假如该坐标轴与第一条坐标轴垂直(更严谨的说法是正交(orthogonal)),它就是覆盖数据次大差异性的坐标轴。感觉这个说法有点怪。直接找一条与 第一条垂直的或者说是正交的就可以当做,覆盖数据次大差异性的坐标轴了吗??书里也没给出理论支撑啊!
如图直线C就是第二条坐标轴。
坐标轴的旋转并没有减少数据的维度。
上图中其中包含着3个不同的类别。要区分这3个类别,可以使用决策树、SVM但都比较复杂,如果通过PCA进行降维处理,由于只需要考虑一维信息,因此数据就可以通过比SVM 简单得多的很容易采用的规则进行区分。
在上图中,我们只需要一维信息即可,因为另一维信息只是对分类缺乏贡献的噪声数据。
第一个主成分就是从数据差异性最大(即方差最大)的方向提取出来的,第二个主成分则来自于数据差异性次大的方向,并且该方向与第一个主成分方向正交。通过数据集的协方差矩阵及其特征值分析,我们就可以求得这些主成分的值。
一旦得到了协方差矩阵的特征向量,我们就可以保留最大的N个值。这些特征向量也给出了N个最重要特征的真实结构。我们可以通过将数据乘上这N个特征向量而将它转换到新的空间。
将数据转换成前# 个主成分的伪码大致如下:
去除平均值
计算协方差矩阵
计算协方差矩阵的特征值和特征向量
将特征值从大到小排序
保留最上面的N个特征向量
将数据转换到上述N个特征向量构建的新空间中
PCA算法程序实现如下:
from numpy import *
import matplotlib.pyplot as plt
def loadDataSet(fileName, delim='\t'):
""" 加载数据 """
fr = open(fileName)
#以delim作为分隔符分割数据,默认为'\t'
stringArr = [line.strip().split(delim) for line in fr.readlines()]
datArr = [list(map(float,line)) for line in stringArr]
#需要再做一次list转化
# map() 会根据提供的函数对指定序列做映射。
return mat(datArr)
def pca(dataMat, topNfeat=9999999):
""" PCA算法 """
#用mean计算出矩阵每列特征的均值,axis表示列计算
meanVals = mean(dataMat, axis=0)
#计算出与均值的差值
meanRemoved = dataMat - meanVals
#通过cov()计算出协方差矩阵
# 其中rowvar=0,说明传入的数据一行代表一个样本;若非0,说明传入的数据一列代表一个样本。
covMat = cov(meanRemoved, rowvar=0)
#通过linalg.eig()算出特征值和特征向量
# 一个正方形数组作为参数,它将返回两个值,第一个是数组的特征值,第二个是给定正方形数组的右特征向量。
eigVals,eigVects = linalg.eig(mat(covMat))
#对特征值进行排序,得出的是下标
# argsort()函数是对数组中的元素进行从小到大排序,并返回相应序列元素的数组下标。
eigValInd = argsort(eigVals)
#注意这里的写法,实际上是从倒数第一个(也就是最大的)往左总计N个值
eigValInd = eigValInd[:-(topNfeat+1):-1]
#得出经过筛选的特征向量,由最大到最小排列
redEigVects = eigVects[:,eigValInd]
#得出降维后的矩阵差值乘以筛选的特征向量
lowDDataMat = meanRemoved * redEigVects
#重构矩阵,即得出降维后矩阵在原纬度下是什么样的,便于之后绘图比较
#得到的数据在乘筛选的特征向量的转置
reconMat = (lowDDataMat * redEigVects.T) + meanVals
return lowDDataMat, reconMat
def main():
dataMat = loadDataSet(r'./Ch13/testSet.txt')
lowDMat,reconMat = pca(dataMat,1)
print ("lowDMat shape:",end='\t')
print(shape (lowDMat))
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(dataMat[:,0].flatten().A[0],dataMat[:,1].flatten().A[0],marker = '^',s = 90)
ax.scatter(reconMat[:,0].flatten().A[0],reconMat[:,1].flatten().A[0],marker = 'o',s = 50,c ='red')
plt.show()
if __name__ == '__main__':
main()
之前代码都没写主函数,看起来不太方便,所以后面就加上,便于观看。
整个程序流程很简单。顺序执行伪代码中的步骤。输出结果如下所示:
红色的点是我们降维之后数据,因为我们只取了一个特征值,而实际特征值是两个(关于PCA的理论分析请参考),(关于特征值为啥是两个,看一下那个参考就知道了,简单说数据有两列,求得的协方差矩阵是二阶的,所以只有两个特征值)总之本应该有两个特征值,但是我们这边就取了一个,所以数据反构回去时就有部分被丢掉了。只余下了图中显示的红色部分。
当我们去两个特征向量时,即不会对数据进行删减(一共两个特征向量我么这里还是两个)输出结果如下:
可以看到红色的点将蓝色的点覆盖了起来。就是因为数据没有出现删减,所以才会出现和原始数据一致的情况。
用平均值来代替缺失值,平均值根据那些非NaN(Not a Number)得到
def replaceNanWithMean():
datMat = loadDataSet(r'./Ch13/secom.data',' ')
# 取特征值数量
numFeat = shape(datMat)[1]
# 缺失的NaN值使用列平均值代替
for i in range(numFeat):
# np.isnan()函数判断一个数组各个元素是否为nan,并返回相同维度对应的bool数组
# nonzero()函数返回数组中不为False(0)的元素对应的索引
meanVal = mean(datMat[nonzero(~isnan(datMat[:,i].A))[0],i])
# 将值为NaN的替换为平均值
datMat[nonzero(isnan(datMat[:,i].A))[0],i] = meanVal
return datMat
替换过程就两行代码,短小精悍。
数据集中的数据是以空格划分开的,所以调用加载函数时,记得将分割标志符也传递过去。
需要特别强调的是,数据(data)和信息(information)之间具有巨大的差别。数据指的是接受的原始材料,其中可能包含噪声和不相关信息。信息是指数据中的相关部分。这些并非只是抽象概念,我们还可以定量地计算数据中所包含的信息并决定保留的比例。
输出数据矩阵的特征值结果:
def main():
# 这部分代码就和之前那个代码一样
dataMat = replaceNanWithMean()
meanVals = mean(dataMat,axis = 0)
meanRemoved = dataMat - meanVals
covMat = cov(meanRemoved, rowvar=0
eigVals,eigVects = linalg.eig(mat(
# 输出特征值:
print('eigVals: \n',eigVals)
return
输出特征结果如下:
观察输出结果可以知道,输出结果中有超过20%的特征值都是0。这就意味着这些特征都是其他特征的副本(线性代数的知识!!!),也就是说,它们可以通过其他特征来表示,而本身并没有提供额外的信息。
之后了解一下部分数值的数量级。最前面15个值的数量级大于105,实际上那以后的值都变得非常小。这就相当于告诉我们只有部分重要特征,重要特征的数目也很快就会下降。
由于变量自身的协方差是方差,因此在主对角线(左上角到右下角)中,我们实际上具有每个起始变量的方差。并且由于协方差是可交换的,协方差矩阵相对于主对角线是对称的,这意味着上三角形部分和下三角形部分是相等的。
上图给出了前20个主成分占总方差的百分比。可以看出,大部分方差都包含在前面的几个主成分中,舍弃后面的主成分并不会损失太多的信息。如果保留前6个主成分,则数据集可以从590个特征约简成6个特征,大概实现了100:1的压缩。
(主成分和方差的关系没注意到有提起。)
有效的主成分数目则取决于数据集和具体应用。
得到所用到的主成分数目,然后我们可以将该数目输人到PCA算怯中,最后得到约简后数据就可以在分类器中使用了。
降维技术使得数据变得更易使用,并且它们往往能够去除数据中的噪声,使得其他机器学习任务更加精确。降维往往作为预处理步骤,在数据应用到其他算法之前清洗数据。
PCA可以从数据中识别其主要特征,它是通过沿着数据最大方差方向旋转坐标轴来实现的。选择方差最大的方向作为第一条坐标轴,后续坐标轴则与前面的坐标轴正交。协方差矩阵上的特征值分析可以用一系列的正交坐标轴来获取。
本章PCA降维算法,主要是通过对数据的操作转换得到的,和程序操作流程没有什么太大的关系,所以程序看起来十分简单。就是运用数学原理对数据进行操作。