PCA原理与实践

  在对数据进行预处理时,我们经常会遇到数据的维数非常之大,如果不进行相应的特征处理,那么算法的资源开销会很大,这在很多场景下是我们不能接受的。而对于数据的若干维度之间往往会存在较大的相关性,如果能将数据的维度之间进行相应的处理,使它们在保留最大数据信息的同时降低维度之间的相关性,就可以达到降维的效果。PCA(主成分分析)便是利用这样的概念将数据映射到新的维度空间中,选择最重要的几个成分作为新空间向量的基,这样在新的坐标空间中,数据既可以保留大部分的数据信息又可以达到降维的效果。在机器学习实战中对于PCA的描述太过简单,只是利用代码可能并不能为我们详细理解其背后的算法原理,通过学习一些博客,其中有一篇博客http://blog.codinglabs.org/articles/pca-tutorial.html,这篇博客通俗易懂让我从中学习到了很多,现在将学习到的知识总结如下: 

总体思路:

  数据最初所在空间的维度数目是由数据的特征数量决定的,我们想要把它映射到新的空间中,这个空间的维度数目是由数据最重要的特征所决定并且当数据映射后能够尽可能的保留数据信息,而且数据的分离程度较明显。这里就用到了数学上方差和协方差的概念,PCA算法的主要理论依据便是基于这些概念进行一步步求解优化的。

算法原理:

  因为在http://blog.codinglabs.org/articles/pca-tutorial.html这篇博客中原理解释的非常棒,所以以下是基于这篇这博客的原理讲解,感谢博主!

向量内积以及矩阵相乘的几何意义:

  两个向量内积从公式上可以看出最终得到了一个实数,也就是说内积运算是将两个向量映射为一个实数。

  那么内积的几何意义是什么呢?其实它是将向量A投影到了B上的矢量长度。假设A和B是两个n维向量,我们知道n维向量可以等价表示为n维空间中的一条从原点发射的有向线段,为了简单起见我们假设A和B均为二维向量,则A=(x1,y1),B=(x2,y2)。则在二维平面上A和B可以用两条发自原点的有向线段表示,见下图:

现在我们从A点向B所在直线引一条垂线。我们知道垂线与B的交点叫做A在B上的投影,再设A与B的夹角是a,则投影的矢量长度为|A|cos(a),其中

是向量A的模,也就是A线段的标量长度。注意这里我们专门区分了矢量长度和标量长度,标量长度总是大于等于0,值就是线段的长度;而矢量长度可能为负,其绝对值是线段长度,而符号取决于其方向与标准方向相同或相反。同时向量内积还有一个通用的公式:

A?B=|A||B|cos(a)

如果我们假设B的模为1,即让|B|=1,那么就变成了:A?B=|A|cos(a),也就是说设向量B的模为1,则A与B的内积值等于A向B所在直线投影的矢量长度

矩阵运算的几何意义:

如果我们确定了N维空间中的一组基,就可以利用矩阵相乘将向量在该空间中进行线性变换。现在我们有一个向量,要确定它在由N个基向量确定的空间中的具体位置,就可以利用该向量在各个基向量上的投影来确定。通常基向量都是单位正交基,这样我们可以抽取出一个方向上的基向量与该向量做内积运算,利用上一节的内积几何意义不就得到了向量在基向量上的投影,因此可以利用矩阵形式来做向量的基变换。用一个例子来说明:

在二维空间中,一般情况下我们是以(0,1)和(1,0)作为一组基的,但同时(1,1)和(-1,1)也可以成为一组基。一般来说,我们希望基的模是1,因为从内积的意义可以看到,如果基的模是1,那么就可以方便的用向量点乘基而直接获得其在新基上的坐标了!因此将其单位化上面的基可以变为和。现在,我们想获得(3,2)在新基上的坐标,即在两个方向上的投影矢量值,那么根据内积的几何意义,我们只要分别计算(3,2)和两个基的内积,不难得到新的坐标为。下图给出了新的基以及(3,2)在新基上坐标值的示意图:

矩阵形式:

将右边矩阵中的每一列列向量变换到左边矩阵中每一行行向量为基所表示的空间中去

PCA算法原理:

  我们在做PCA时希望能够得到数据在哪个方向上的分离程度最大并且能够保留数据的信息尽可能多。在数学上方差能够很好的表征数据的离散程度,方差越大数据越分离,我们越容易辨认。而协方差能够很好的表征数据在不同维度上的相关性。因此我们在处理数据时先对数据去均值化,将均值变为0,然后将数据矩阵乘以矩阵的转置得到了我们需要的协方差矩阵。在该协方差矩阵中,主对角线上的元素为方差,其余元素为协方差。

  现在所要做的事情就是要选择出方差最大的向量方向作为基向量,以此为基础在选择与第一个基向量正交并且方差第二大的向量作为第二维度的基向量,以此类推。通常前r个方向上的基向量就能够最大化的近似原始数据,这里r远远小于n,从而达到降维的效果。因此整个逻辑就是优化协方差矩阵,将其对角化操作。由于协方差矩阵是个实对称矩阵故而其必然能够对角化,而且它的不同特征值所对应的特征向量都正交。由线性代数的知识我们可以很容易的得到协方差矩阵的特征值和特征向量,特征值表示特征向量的重要程度,特征向量则表示了数据方差变化的方向。对特征值按降序排列后,将其所对应的特征向量构成了新的坐标空间中的基向量。这样将原始数据重构于新的坐标空间,根据矩阵相乘的意义就可以做出映射后的数据。

  这里证明一下为什么我们需要的空间向量基就是协方差矩阵的特征向量。设原始数据矩阵X对应的协方差矩阵为C,而P是一组基按行组成的矩阵,设Y=PX,则Y为X对P做基变换后的数据。设Y的协方差矩阵为D,我们推导一下D与C的关系:

  我们要找的P不是别的,而是能让原始协方差矩阵对角化的P,即就是原始数据协方差的特征向量。换句话说,优化目标变成了寻找一个矩阵P,满足PCPT是一个对角矩阵,并且对角元素按从大到小依次排列,那么P的前r行就是要寻找的基,用P的前r行组成的矩阵乘以X就使得X从N维降到了r维并满足上述优化条件。

机器学习实战应用实例:

  1. 将数据转换成前N个主成分

    1 def loadData(path):
    2     fr = open(path)
    3     dataSet= []
    4     for line in fr.readlines():
    5         currentData = line.strip().split(‘ ‘)
    6         dataSet.append(currentData)
    7     dataArr = [map(float,lineData) for lineData in dataSet]
    8     return mat(dataArr
 #PCA 1 def PCA(dataMat,featureNum):
 2     dataMean = np.mean(dataMat , axis = 0)            #求均值
 3     dataMat = dataMat -dataMean                       #数据去均值化
 4     covDataMat = np.cov(dataMat,rowvar=0)             #求协方差
 5     featVal,featVec = linalg.eig(covDataMat)          #求特征值和特征向量
 6     featIndex = argsort(featVal)                      #对特征值排序后取前k个特征向量
 7     featIndex=list(reversed(list(featIndex)))
 8     featIndex = featIndex[:featureNum:1]
 9     featVec = mat(featVec[:,featIndex])
10     newDataMat = dataMat * featVec                    #降维
11     originMat = (newDataMat*featVec.T)+dataMean       #重构原始数据
12     return newDataMat,originMat       
#画图1 def plotData(dataMat,originMat):
2     fig =plt.figure()
3     ax = fig.add_subplot(111)
4     ax.scatter(dataMat[:,0].flatten().A[0],dataMat[:,1].flatten().A[0],marker=‘^‘,s=90)
5     ax.scatter(originMat[:,0].flatten().A[0],dataMat[:,1].flatten().A[0],s=50,c=‘red‘)
6     plt.show()

  2.针对机器学习实战提供的有关半导体数据,含有590个特征。我们想要做的是,这些特征中有多少主特征,把这些主特征提取出来。由于数据中含有很多缺失值是以NaN标识的,所以第一步要用均值来代替这些数据。

1 def replaceNan(dataMat):
2     m = shape(dataMat)[1]
3     for i in range(m):
4         dataOfMean = mean(dataMat[nonzero(~isnan(dataMat[:,i]))[0],i])
5         dataMat[nonzero(isnan(dataMat[:,i]))[0],i] = dataOfMean
6     return dataMat

稍微改动下PCA程序在里面加入判别,如果前k个主成分的累积方差占总方差的百分比大于98%就停止,选择这K个特征向量:

def PCA(dataMat,featureNum):
    dataMean = np.mean(dataMat , axis = 0)
    dataMat = dataMat -dataMean
    covDataMat = np.cov(dataMat,rowvar=0)
    featVal,featVec = linalg.eig(covDataMat)
    featIndex = argsort(featVal)
    featIndex=list(reversed(list(featIndex)))
    featIndex = featIndex[:featureNum:1]
    sumOfFeatVal = sum(featVal)
    addtotal = 0.0
    for k in range(featureNum):
        addtotal +=featVal[featIndex[k]]
        percent= addtotal / sumOfFeatVal
        if percent>0.98:
            print ‘the number of %d has occupied %f‘ % (k, percent)
            print ‘the best number of feature is ‘,k
            break
        print ‘the number of %d has occupied %f‘%(k,percent)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    percentFeat = featVal[featIndex][0:20] / sumOfFeatVal
    x = arange(20)
    ax.plot(x, percentFeat, ‘o-‘, c=‘r‘)
    plt.xlabel(‘the number of Princple feature‘)
    plt.ylabel(‘var percent(%)‘)
    plt.grid()
    plt.show()
    featVec = mat(featVec[:,featIndex])
    newDataMat = dataMat * featVec
    originMat = (newDataMat*featVec.T)+dataMean
    return newDataMat,originMat,featVal

最后的统计结果:

the number of 0 has occupied 0.592541
the number of 1 has occupied 0.833779
the number of 2 has occupied 0.925279
the number of 3 has occupied 0.948285
the number of 4 has occupied 0.962877
the number of 5 has occupied 0.968065
the number of 6 has occupied 0.971291
the number of 7 has occupied 0.974438
the number of 8 has occupied 0.977069
the number of 9 has occupied 0.979382
the number of 10 has occupied 0.981557
the best number of feature is  10

参考:

1.博客http://blog.codinglabs.org/articles/pca-tutorial.html

2.《机器学习实战》

时间: 2024-10-25 17:45:12

PCA原理与实践的相关文章

分布式开放消息系统(RocketMQ)的原理与实践

分布式消息系统作为实现分布式系统可扩展.可伸缩性的关键组件,需要具有高吞吐量.高可用等特点.而谈到消息系统的设计,就回避不了两个问题: 消息的顺序问题 消息的重复问题 RocketMQ作为阿里开源的一款高性能.高吞吐量的消息中间件,它是怎样来解决这两个问题的?RocketMQ 有哪些关键特性?其实现原理是怎样的? 关键特性以及其实现原理 一.顺序消息 消息有序指的是可以按照消息的发送顺序来消费.例如:一笔订单产生了 3 条消息,分别是订单创建.订单付款.订单完成.消费时,要按照顺序依次消费才有意

Atitit 表达式原理 语法分析 原理与实践 解析java的dsl  递归下降是现阶段主流的语法分析方法

Atitit 表达式原理 语法分析 原理与实践 解析java的dsl  递归下降是现阶段主流的语法分析方法 于是我们可以把上面的语法改写成如下形式:1 合并前缀1 语法分析有自上而下和自下而上两种分析方法2 递归下降是现阶段主流的语法分析方法,2 于是我们可以把上面的语法改写成如下形式: 1)       Operator="+" | "-" | "*" | "/" 2)       Expression=<数字>

《从PAXOS到ZOOKEEPER分布式一致性原理与实践》pdf

下载地址:网盘下载 内容简介  · · · · · · <Paxos到Zookeeper:分布式一致性原理与实践>从分布式一致性的理论出发,向读者简要介绍几种典型的分布式一致性协议,以及解决分布式一致性问题的思路,其中重点讲解了Paxos和ZAB协议.同时,本书深入介绍了分布式一致性问题的工业解决方案--ZooKeeper,并着重向读者展示这一分布式协调框架的使用方法.内部实现及运维技巧,旨在帮助读者全面了解ZooKeeper,并更好地使用和运维ZooKeeper.全书共8章,分为五部分:第一

嵌入式实时操作系统μCOS原理与实践任务控制与时间的解析

/***********************************************************************************************************                                                uC/OS-II*                                          The Real-Time Kernel  RTOS* ***************

《数字图像处理原理与实践(MATLAB版)》一书之代码Part9

本文系<数字图像处理原理与实践(MATLAB版)>一书之代码系列的Part9,辑录该书第431至第438页之代码,供有需要读者下载研究使用.至此全书代码发布已经接近尾声,希望这些源码能够对有需要的读者有所帮助.代码执行结果请参见原书配图,建议下载代码前阅读下文: 关于<数字图像处理原理与实践(MATLAB版)>一书代码发布的说明 http://blog.csdn.net/baimafujinji/article/details/40987807 首先给出的是原书P438所列之程序源

《数字图像处理原理与实践(MATLAB版)》一书之代码Part8

本文系<数字图像处理原理与实践(MATLAB版)>一书之代码系列的Part8,辑录该书第375至第415页之代码,供有需要读者下载研究使用.至此全书代码发布已经接近尾声,希望这些源码能够对有需要的读者有所帮助.代码执行结果请参见原书配图,建议下载代码前阅读下文: 关于<数字图像处理原理与实践(MATLAB版)>一书代码发布的说明 http://blog.csdn.net/baimafujinji/article/details/40987807 P385-1 function y

20145309《网络对抗技术》免杀原理与实践

20145309<网络对抗技术>免杀原理与实践 1.基础问题回答 (1)杀软是如何检测出恶意代码的? 根据特征来检测:对已存在的流行代码特征的提取与比对根据行为来检测:是否有更改注册表行为.是否有设置自启动.是否有修改权限等等 (2)免杀是做什么? 使用一些方法使得恶意程序不被杀软和防火墙发现,避免被查杀. (3)免杀的基本方法有哪些? 加壳:就是相当于把你的后门代码封装起来,但是现在大部分公开的壳都能被杀毒软件查出来,所以加这些壳还不如不加:加花指令:就是加一段垃圾代码,但是并不影响程序的正

20145331魏澍琛《网络对抗》——免杀原理与实践

20145331魏澍琛<网络对抗>--免杀原理与实践 问题回答 1.杀软是如何检测出恶意代码的? 一个是基于特征码的检测,第二个是启发式恶意软件检测,最后是基于行为的恶意软件检测 2.免杀是做什么? 让病毒不被杀毒软件kill掉 3.免杀的基本方法有哪些? a)可以用这次实验所涉及的改变特征码 b)加壳:就是相当于把你的后门代码封装起来,但是现在大部分公开的壳都能被杀毒软件查出来,效果其实不好 实践过程 一.使用msf生成后门程序的检测 1.用上节课所讲的msf生成一个后门 2.到相应网站上查

20145239杜文超《网络对抗》- 免杀原理与实践

<网络对抗>- 免杀原理与实践 基础问题回答 (1)杀软是如何检测出恶意代码的? 根据搜集来的最全的.最新的特征码库,检测程序有没有异常或者可疑的行为. (2)免杀是做什么? 利用一些手段,让你的的后门不被AV软件发现. (3)免杀的基本方法有哪些? 加壳.用其他语言进行重写再编译. 使用反弹式连接. 自己手工编一个. 实践总结与体会 这次的免杀实验做得非常顺利,每一个步骤基本上都一次成功,虽然这正是我所希望的但也有不好就是万一下次遇到问题可能不会解决,毕竟遇到问题.解决问题的过程才能让学习的