zoukankan      html  css  js  c++  java
  • Machine Learning in Action – PCA和SVD

    降维技术,
    首先举的例子觉得很好,因为不知不觉中天天都在做着降维的工作

    对于显示器显示一个图片是通过像素点0,1,比如对于分辨率1024×768的显示器,就需要1024×768个像素点的0,1来表示,这里每个像素点都是一维,即是个1024×768维的数据。而其实眼睛真正看到的只是一副二维的图片,这里眼睛其实在不知不觉中做了降维的工作,把1024×768维的数据降到2维

    降维的好处,显而易见,数据更易于显示和使用,去噪音,减少计算量,更容易理解数据
    主流的降维技术,包含:
    主成分分析,principal component analysis(PCA)
    因子分析,factor analysis
    独立成分分析,independent component nalysis (ICA)

    这些技术参考NG的课程笔记,这里给出PCA的python实现

    PCA的实现很简单,求协方差矩阵的特征值和特征向量,并取特征值top的那些特征向量作为新的坐标轴(原因参考NG讲义)

    from numpy import *
    def loadDataSet(fileName, delim='	'):
        fr = open(fileName)
        stringArr = [line.strip().split(delim) for line in fr.readlines()]
        datArr = [map(float,line) for line in stringArr]
        return mat(datArr)
    def pca(dataMat, topNfeat=9999999):
        meanVals = mean(dataMat, axis=0)
        meanRemoved = dataMat - meanVals  #Normalization
        covMat = cov(meanRemoved, rowvar=0) #生成协方差矩阵
        eigVals,eigVects = linalg.eig(mat(covMat)) #求解协方差矩阵的特征值,特征向量
        eigValInd = argsort(eigVals)
        eigValInd = eigValInd[:-(topNfeat+1):-1] #选取top特征值
        redEigVects = eigVects[:,eigValInd] #找到对应的特征向量
        lowDDataMat = meanRemoved * redEigVects #用特征向量生成新的数据
        reconMat = (lowDDataMat * redEigVects.T) + meanVals #为了调试恢复原始数据
        return lowDDataMat, reconMat

    至于topNfeat取值,即选取多少个top的特征值,其实看到实际数据就可以决定,因为往往从某个特征值开始会和前面的相差几个数量级

    比如底下的例子,

    image

     

    参考NG的讲义,对于高维的数据,算出协方差矩阵本身就是很耗的,所以其实可以用SVD分解来进行PCA,当然这个只是SVD的一个用法

    但是理解singular value decomposition (SVD)还是看看SVD最经典的应用latent semantic indexing (LSI)
    文档是由词组成,所以在比较文章的相似性时,比如分类或聚类时,会以词作为特征
    这样的问题是,无法处理同义词,比如learn和study,或拼错的词

    而如果此时对于m篇文章,n个词的矩阵做奇异值分解,就可以自动将词和文章进行分类

    image

    并将维数从词数降到分类树,即从n降到r

    image

    image

    听上去很神奇,其实原理和PCA是一样的,因为SVD分解后
    U是image 的特征向量(eigenvector)矩阵,而image 是词之间的协方差矩阵
    并且U是m×r而不是m×n,已经进行了降维,U是取top r的特征向量,和PCA的过程是一样的
    所以看上去是给词做了分类,其实只是将坐标轴旋转到特征向量,并取top进行降维

    同理V是DataT*Data的特征向量矩阵,而DataT*Data是文本之间的协方差矩阵

    所有SVD分解后,得到

    U,每行表示该文本和每个词类的关系
    V,每行表示该词和每个文本类的关系
    奇异矩阵,表示词类和文本类之间的关系

    个人理解,SVD其实是在自变量x和因变量y上同时进行了PCA降维
    而普通PCA只是会降低x的维度
    比如上面的例子,同时对于词和文章,进行特征向量的旋转和降维,得到词类和文章类


    现在SVD分解更多的用在推荐系统,推荐系统最经典的就是协同过滤(Collaborative filtering),item-base或user-base
    用SVD可以达到更有效的推荐的效果
    比如底下是用户对各个菜评分表,0表示没吃过

    image

    如果对这个做SVD分解,会得到两个奇异值
    你会发现可以分别对应到美食BBQ和日式食品两类食品上
    同时得到的结论,一拨人喜欢美食BBQ, 一拨人喜欢日式食品,这样推荐就很简单

    python中实现svd很简单,有现成的函数,

    输入,

    def loadExData():
        return[[1, 1, 1, 0, 0],
            [2, 2, 2, 0, 0],
            [1, 1, 1, 0, 0],
            [5, 5, 5, 0, 0],
            [1, 1, 0, 2, 2],
            [0, 0, 0, 3, 3],
            [0, 0, 0, 1, 1]]

    SVD分解,

    >>> import svdRec
    >>> Data=svdRec.loadExData()
    >>> U,Sigma,VT=linalg.svd(Data)
    >>> Sigma
    array([ 9.72140007e+00, 5.29397912e+00, 6.84226362e-01,
    7.16251492e-16, 4.85169600e-32])

    可以看到奇异值中,最后两个的量级很小,可以忽略,所以取前3个值

    所以r=3,做下面的近似

    image

    重新构造一个3×3的奇异矩阵,

    >>> Sig3=mat([[Sigma[0], 0, 0],[0, Sigma[1], 0], [0, 0, Sigma[2]]])

    截取特征值,产生新的数据,这步只是为了验证确实是能得到近似结果

    >>> U[:,:3]*Sig3*VT[:3,:]
        array([[ 1., 1., 1., 0., 0.],
            [ 2., 2., 2., -0., -0.],
            [ 1., 1., 1., -0., -0.],
            [ 5., 5., 5., 0., 0.],
            [ 1., 1., -0., 2., 2.],
            [ 0., 0., -0., 3., 3.],
            [ 0., 0., -0., 1., 1.]])

     

    下面实际看个推荐的例子,

    经典的item-based的协同过滤,

    #计算user和item之间的相似度
    #simMeas,给出相似度计算方法,比如欧几里德距离,pearson相关系数,余弦法
    def standEst(dataMat, user, simMeas, item):
        n = shape(dataMat)[1]
        simTotal = 0.0; ratSimTotal = 0.0
        for j in range(n): #遍历所有的item
            userRating = dataMat[user,j]
            if userRating == 0: continue #如果这个item,user没有评过,没有相关性
            #找出item和j都有评论的user的index
            overLap = nonzero(logical_and(dataMat[:,item].A>0, dataMat[:,j].A>0))[0]
            if len(overLap) == 0: similarity = 0 #没有交集没有相关性
            else: similarity = simMeas(dataMat[overLap,item], dataMat[overLap,j]) #算出item和j的相似度
            #print 'the %d and %d similarity is: %f' % (item, j, similarity)
            simTotal += similarity
            ratSimTotal += similarity * userRating #加权,因为每个item用户的rating是不一样的
        if simTotal == 0: return 0
        else: return ratSimTotal/simTotal
    
    def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst):
        unratedItems = nonzero(dataMat[user,:].A==0)[1]
        if len(unratedItems) == 0: return 'you rated everything'
        itemScores = []
        for item in unratedItems: #找出每个item和user的关系
            estimatedScore = estMethod(dataMat, user, simMeas, item)
            itemScores.append((item, estimatedScore))
        return sorted(itemScores,key=lambda jj: jj[1], reverse=True)[:N] #取出topN,最相关的

    其中求overLap的逻辑比较晦涩,

    其中dataMat[:,item].A,找出item列,因为是matrix,用.A转成array

    logical_and,其实就是找出最item列和j列都>0,只有都大于0才会是true

    nonzero会给出其中不为0的index,对于二维的

    >>> b2 = np.array([[True, False, True], [True, False, False]])
    >>> np.nonzero(b2)
        (array([0, 0, 1]), array([0, 2, 0]))

    所以知道b2[0,0]、b[0,2]和b2[1,0]的值不为0

    最终取[0],即取出user的index,哪些user同时评了item和j

    因为是item-based,所以比较item的时候,维度就取决于user的个数,如果user很多,计算起来就比较麻烦

    所以这里可以用SVD来降维,

    比如对如下的数据,

    image

    进行SVD分解,

    >>>from numpy import linalg as la
    >>> U,Sigma,VT=la.svd(mat(svdRec.loadExData2()))
    >>> Sigma
    array([ 1.38487021e+01, 1.15944583e+01, 1.10219767e+01,
            5.31737732e+00, 4.55477815e+00, 2.69935136e+00,
            1.53799905e+00, 6.46087828e-01, 4.45444850e-01,
            9.86019201e-02, 9.96558169e-17])

    如何决定r?有个定量的方法是看多少个奇异值可以达到90%的能量

    其实和PCA一样,由于奇异值其实是等于data×dataT特征值的平方根,所以总能量就是特征值的和

    >>> Sig2=Sigma**2
    >>> sum(Sig2)
    541.99999999999932

    而取到前4个时,发现超过90%,所以r=4

    >>> sum(Sig2[:3])
    500.50028912757909

    所以SVD分解的版本的关键不同在于,降低了user的纬度,从n变为4

    def svdEst(dataMat, user, simMeas, item):
        n = shape(dataMat)[1]
        simTotal = 0.0; ratSimTotal = 0.0
        U,Sigma,VT = la.svd(dataMat) #SVD分解
        Sig4 = mat(eye(4)*Sigma[:4]) #重构奇异矩阵
        xformedItems = dataMat.T * U[:,:4] * Sig4.I #构造item和user类的关系
        for j in range(n):
            userRating = dataMat[user,j]
            if userRating == 0 or j==item: continue
            similarity = simMeas(xformedItems[item,:].T,xformedItems[j,:].T)
            print 'the %d and %d similarity is: %f' % (item, j, similarity)
            simTotal += similarity
            ratSimTotal += similarity * userRating
        if simTotal == 0: return 0
        else: return ratSimTotal/simTotal

    其中关键一步,

    dataMat.T * U[:,:4] * Sig4.I

    将dataMat基于特征向量旋转,再用特征值缩放,所以dataMat,m×n,转换为n×4的item和user类的矩阵

     

    最后一个SVD的应用是图像压缩,

    对于32×32,1024个像素的图片,进行SVD分解,如果取top 2的特征值,最终只需要保存32×2×2 +2 一共130个byte,比1024小了10倍

  • 相关阅读:
    无法重用Linq2Entity Query
    The Joel Test
    MSBuilder directly instead of default VSComplie with keyborad shotcut 原创
    客户端缓存(Client Cache)
    关于代码重构和UT的一些想法,求砖头
    ExtJS2.0实用简明教程 应用ExtJS
    Perl information,doc,module document and FAQ.
    使用 ConTest 进行多线程单元测试 为什么并行测试很困难以及如何使用 ConTest 辅助测试
    史上最简单的Hibernate入门简介
    汽车常识全面介绍 传动系统
  • 原文地址:https://www.cnblogs.com/fxjwind/p/4014395.html
Copyright © 2011-2022 走看看