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


    第 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个特征向量构建的新空间中
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    之前代码都没写主函数,看起来不太方便,所以后面就加上,便于观看。
    整个程序流程很简单。顺序执行伪代码中的步骤。输出结果如下所示:
    在这里插入图片描述
    红色的点是我们降维之后数据,因为我们只取了一个特征值,而实际特征值是两个(关于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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    替换过程就两行代码,短小精悍。
    数据集中的数据是以空格划分开的,所以调用加载函数时,记得将分割标志符也传递过去。

    需要特别强调的是,数据(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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    输出特征结果如下:
    在这里插入图片描述
    观察输出结果可以知道,输出结果中有超过20%的特征值都是0。这就意味着这些特征都是其他特征的副本(线性代数的知识!!!),也就是说,它们可以通过其他特征来表示,而本身并没有提供额外的信息。

    之后了解一下部分数值的数量级。最前面15个值的数量级大于105,实际上那以后的值都变得非常小。这就相当于告诉我们只有部分重要特征,重要特征的数目也很快就会下降。

    由于变量自身的协方差是方差,因此在主对角线(左上角到右下角)中,我们实际上具有每个起始变量的方差。并且由于协方差是可交换的,协方差矩阵相对于主对角线是对称的,这意味着上三角形部分和下三角形部分是相等的。
    在这里插入图片描述
    上图给出了前20个主成分占总方差的百分比。可以看出,大部分方差都包含在前面的几个主成分中,舍弃后面的主成分并不会损失太多的信息。如果保留前6个主成分,则数据集可以从590个特征约简成6个特征,大概实现了100:1的压缩。
    (主成分和方差的关系没注意到有提起。)

    有效的主成分数目则取决于数据集和具体应用。

    得到所用到的主成分数目,然后我们可以将该数目输人到PCA算怯中,最后得到约简后数据就可以在分类器中使用了。

    13.4 小结

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

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

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

  • 相关阅读:
    MySQL——四、SQL语句(下篇)
    openssl开发详解
    MySQL ——单行处理函数实例练习
    逆向实战31——xhs—xs算法分析
    Spring~五种存储Bean对象的类注解、方法注解(@Bean)以及Bean对象的获取
    小程序基础原理
    【推理引擎】如何在 ONNXRuntime 中添加新的算子
    [carla入门教程]-2 pythonAPI的使用
    八股文第二十天
    CUDA的应用场景
  • 原文地址:https://blog.csdn.net/qq_35021992/article/details/126819520