《机器学习实战》学习笔记(十三)

第 13章 利用PCA来简化数据

引言

降维的目标就是对输入的数目进行削减,由此剔除数据中的噪声并提髙机器学习方法的性能。

通常而言,我们在应用其他机器学习算法之前,必须先识别出其相关特征。

13.1 降维技术

始终贯穿本书的一个难题就是对数据和结果的展示。

对数据进行简化还有如下一系列的原因:

  • 使得数据集更易使用;
  • 降低很多算法的计算开销;
  • 去除噪声;
  • 使得结果易懂。

主成分分析(Principal Component Analysis, PCA )

第一种降维的方法称为主成分分析(Principal Component Analysis, PCA )

在PCA中,数据从原来的坐标系转换到了新的坐标系,新坐标系的选择是由数据本身决定的。第一个新坐标轴选择的是原始数据中方差最大的方向,第二个新坐标轴的选择和第一个坐标轴正交且具有最大方差的方向。

该过程一直重复,重复次数为原始数据中特征的数目。我们会发现,大部分方差都包含在最前面的几个新坐标轴中。因此,我们可以忽略余下的坐标轴,即对数据进行了降维处理

因子分析(Factor Analysis)

另外一种降维技术是因子分析(Factor Analysis)。在因子分析中,我们假设在观察数据的生成中有一些观察不到的隐变量(latentvariable)。假设观察数据是这些隐变量和某些噪声的线性组合。那么隐变量的数据可能比观察数据的数目少,也就是说通过找到隐变量就可以实现数据的降维。

独立成分分析(Independent Component Analysis ICA)

还有一种降维技术就是独立成分分析(Independent Component Analysis ICA)。ICA假设数据是从N个数据源生成的,这一点和因子分析(因子分析是认为有隐变量)有些类似。假设数据为多个数据源的混合观察结果,这些数据源之间在统计上是相互独立的,而在PCA中只假设数据是不相关的。同因子分析一样,如果数据源的数目少于观察数据的数目,则可以实现降维过程。

13.2 PCA

主成分分析法优缺点:

  • 优 点 :降低数据的复杂性,识别最重要的多个特征。
  • 缺 点 :不一定需要, 且可能损失有用信息。
  • 适用数据类型:数值型数据。

13.2.1 移动坐标轴

第一条坐标轴旋转到覆盖数据的最大方差位置(下图中的直线B ),数据的最大方差给出了数据的最重要的信息。在选择了覆盖数据最大差异性的坐标轴之后,我们选择了第二条坐标轴。假如该坐标轴与第一条坐标轴垂直(更严谨的说法是正交(orthogonal)),它就是覆盖数据次大差异性的坐标轴。感觉这个说法有点怪。直接找一条与 第一条垂直的或者说是正交的就可以当做,覆盖数据次大差异性的坐标轴了吗??书里也没给出理论支撑啊!
如图直线C就是第二条坐标轴。
在这里插入图片描述

坐标轴的旋转并没有减少数据的维度。

在这里插入图片描述
上图中其中包含着3个不同的类别。要区分这3个类别,可以使用决策树、SVM但都比较复杂,如果通过PCA进行降维处理,由于只需要考虑一维信息,因此数据就可以通过比SVM 简单得多的很容易采用的规则进行区分。

在上图中,我们只需要一维信息即可,因为另一维信息只是对分类缺乏贡献的噪声数据。

第一个主成分就是从数据差异性最大(即方差最大)的方向提取出来的,第二个主成分则来自于数据差异性次大的方向,并且该方向与第一个主成分方向正交。通过数据集的协方差矩阵及其特征值分析,我们就可以求得这些主成分的值。

一旦得到了协方差矩阵的特征向量,我们就可以保留最大的N个值。这些特征向量也给出了N个最重要特征的真实结构。我们可以通过将数据乘上这N个特征向量而将它转换到新的空间。

13.2.2 在Numpy中实现PCA

将数据转换成前# 个主成分的伪码大致如下:

去除平均值
计算协方差矩阵
计算协方差矩阵的特征值和特征向量
将特征值从大到小排序
保留最上面的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的理论分析请参考),(关于特征值为啥是两个,看一下那个参考就知道了,简单说数据有两列,求得的协方差矩阵是二阶的,所以只有两个特征值)总之本应该有两个特征值,但是我们这边就取了一个,所以数据反构回去时就有部分被丢掉了。只余下了图中显示的红色部分。
当我们去两个特征向量时,即不会对数据进行删减(一共两个特征向量我么这里还是两个)输出结果如下:
在这里插入图片描述
可以看到红色的点将蓝色的点覆盖了起来。就是因为数据没有出现删减,所以才会出现和原始数据一致的情况。

13.3 示例:利用PCA对半导体制造数据降维

用平均值来代替缺失值,平均值根据那些非NaN(Not a Number)得到

将NaN替换成平均值的函数实现

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算怯中,最后得到约简后数据就可以在分类器中使用了。

13.4 小结

降维技术使得数据变得更易使用,并且它们往往能够去除数据中的噪声,使得其他机器学习任务更加精确。降维往往作为预处理步骤,在数据应用到其他算法之前清洗数据。

PCA可以从数据中识别其主要特征,它是通过沿着数据最大方差方向旋转坐标轴来实现的。选择方差最大的方向作为第一条坐标轴,后续坐标轴则与前面的坐标轴正交。协方差矩阵上的特征值分析可以用一系列的正交坐标轴来获取。

本章PCA降维算法,主要是通过对数据的操作转换得到的,和程序操作流程没有什么太大的关系,所以程序看起来十分简单。就是运用数学原理对数据进行操作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值