SVD简化数据

SVD简化数据

引言

餐馆可分为很多类别,不同的专家对其分类可能有不同依据。实际中,我们可以忘掉专家,从数据着手,可对记录用户关于餐馆观点的数据进行处理,并从中提取出其背后的因素。这些因素可能会与餐馆的类别、烹饪时采用的某个特定配料,或其他任意对象一致。然后,可利用这些因素来估计人们对没有去过的餐馆的看法。

基础概念

提取信息的方法:奇异值分解Singular Value Decomposition(SVD)

在很多情况下,数据中的一小段携带了数据集中的大部分信息,而其他信息要么是噪声,要么就是毫不相关的信息。矩阵分解可将原始矩阵表示成新的易于处理的形式,新形式是两个或多个矩阵的乘积。

不同的矩阵分解技术具有不同的性质,其中有些更适合于某个应用,有些则更适合于其他应用。最常见的一种矩阵分解技术就是SVD。SVD将原始的数据集矩阵Data分解成三个矩阵U、Σ、VT。如果原始矩阵Data是m行n列,则有如下等式:

Data m×n =U m×m Σ m×n VT n×n 

上述分解中会构建出一个矩阵Σ,该矩阵只有对角元素,其他元素均为0。另一个惯例就是,Σ的对角元素是从大到小排列的。这些对角元素称为奇异值(Singular Value),它们对应了原始数据集矩阵Data的奇异值。回想PCA章节,得到的是矩阵的特征值,它们告诉我们数据集中的重要特征。Σ中的奇异值也是如此。奇异值和特征值时有关系的。这里的奇异值就是矩阵Data∗DataT特征值的平方根。

矩阵Σ只有从大到小排列的对角元素。在科学和工程中,一致存在这样一个普遍事实:在某个奇异值的数目(r个)之后,其他的奇异值都置为0。这就意味着数据集中仅有r个重要特征,而其余特征则都是噪声或冗余特征。

3种相关度以及基于相似度推荐(+代码)

  • 欧氏距离,相似度=1/(1+距离),两个物品越相似距离也就越近,当完全相似时距离就近似于0了。
  • 皮尔逊相关系数,用 0.5+0.5(corrcoef()) 计算,用于度量两个变量X和Y之间的相关(线性相关),其值介于-1与1之间。在自然科学领域中,该系数广泛用于度量两个变量之间的相关程度,优势在于对量级不敏感。
  • 余弦相似度,计算两个向量之间夹角的余弦,如果夹角为90度,余弦为0,相似度为0;如果夹角为0度,余弦为1,相似度也就为1, cosθ=AB||A||||B|| , ||A|| 和||B||是向量A和B的2范数,numpy中用linalg.norn()。
 #欧氏距离相似度
 def ecludSim(inA, inB):
     return 1.0/(1.0 + linalg.norm(inA - inB))

#皮尔逊相关系数
def pearsSim(inA, inB):
    if len(inA) < 3:
        return 1.0
    return 0.5 + 0.5*corrcoef(inA, inB, rowvar= 0)[0][1]

#余弦相似度
def cosSim(inA, inB):
    num = float(inA.T * inB)
    denom = linalg.norm(inA)*linalg.norm(inB)
    return 0.5+0.5*(num/denom)

既然得到了相似度,就可以对一个菜单作出完整评价,如表格所示,0表示该顾客没有做出评价,首先要把0的位给预测出来,如何预测呢?
这里写图片描述
首先依次找出某以用户未评价的物品,求和其他物品的相似度,相似度累加,每次计算时还考虑相似度和当前用户评分的乘积,最后,通过除以所有的评分总和,对相似度评分的乘积进行归一化,可以使最后的评分在0-5之间。

def loadExData():
          return [[4,4,0,2,2],
                  [2,0,0,3,3],
                  [4,0,0,1,1],
                  [1,1,1,2,0],
                  [2,2,2,0,0],
                  [1,1,1,0,0],
                  [5,5,5,0,0]]
      def standEst(dataMat, user, simMeans, item):
          n = shape(dataMat)[1]
          simTotal = 0.0
          ratSimTotal = 0.0
          # 此顾客对所有的物品的评分
          for i in range(n):
              #此顾客对该的物品的评分
              userRating = dataMat[user, i]
              if userRating == 0:
                  continue
              #找出对此物品的评价和对待评价物品的都做评价的顾客
              overLap = nonzero(logical_and(dataMat[:, item].A>0, dataMat[:, i].A>0))[0]
              if len(overLap) == 0:
                  similarity = 0
              else:#求出两个物品的相似度
                  similarity = simMeans(dataMat[overLap, item], dataMat[overLap, i])
              print '%d and %d similarity = %f'%(i, item, similarity)
              #和所有物品的比较的相似度加和
              simTotal += similarity
              #评分*相似度求和
              ratSimTotal += similarity*userRating
          if simTotal == 0:
              return 0
          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 'None'
          itemScores = []
          for item in unratedItems:
              #预测出未评分的可能评分
              estimatedScore = estMethod(dataMat, user, simMeas, item)
              itemScores.append((item, estimatedScore))
          return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N]

      def main():
          dataMat = mat(loadExData2())
          print recommend(dataMat, 1)

      if name == 'main':
          main()

  '''结果'''

  4 and 0 similarity = 0.000000

  7 and 0 similarity = 0.990916

  9 and 0 similarity = 0.000000

  4 and 1 similarity = 0.000000

  7 and 1 similarity = 0.978429

  9 and 1 similarity = 0.000000

  4 and 2 similarity = 0.000000

  7 and 2 similarity = 0.977652

  9 and 2 similarity = 0.000000

  4 and 3 similarity = 0.000000

  7 and 3 similarity = 0.000000

  9 and 3 similarity = 1.000000

  4 and 5 similarity = 0.000000

  7 and 5 similarity = 0.000000

  9 and 5 similarity = 1.000000

  4 and 6 similarity = 1.000000

  7 and 6 similarity = 0.000000

  9 and 6 similarity = 0.692308

  4 and 8 similarity = 0.000000

  7 and 8 similarity = 0.995750

  9 and 8 similarity = 0.000000

  4 and 10 similarity = 0.000000

  7 and 10 similarity = 1.000000

  9 and 10 similarity = 1.000000

  [(3, 4.0), (5, 4.0), (6, 4.0)]

基于SVD评分估计(+代码)

实际的数据集会比用于展示recommend()函数功能的myMat矩阵稀疏得多。

可以通过loadExData2()将该矩阵加载到程序中

接下来计算该矩阵的SVD来了解其到底需要多少维特征。

前三个元素所包含的总能量符合要求,可以将一个11维的矩阵转换成一个3维矩阵。下面对转换后的三维空间构造出一个相似度计算函数。利用SVD将所有的菜肴映射到一个低维空间中去。在低维空间下,可以利用前面相同的相似度计算方法来进行推荐。构建一个类似于standEst()的函数svdEst()。

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

    def svdEst(dataMat, user, simMeas, item):
        n = shape(dataMat)[1]
        simTotal = 0.0
        ratSimTotal = 0.0
        #将原数据分解成svd
        U,Sigma,VT = linalg.svd(dataMat)
        Sig4 = mat(eye(4)*Sigma[:4])
        #利用U矩阵将物品转换到低维空间中
        xformedItems = dataMat.T * U[:,:4] * Sig4.I
        #print xformedItems
        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

    def main():
        dataMat = mat(loadExData2())
        print recommend(dataMat, 1, estMethod = svdEst)

    if __name__ == '__main__':
        main()

    '''结果'''
    the 0 and 3 similarity is: 0.490950
    the 0 and 5 similarity is: 0.484274
    the 0 and 10 similarity is: 0.512755
    the 1 and 3 similarity is: 0.491294
    the 1 and 5 similarity is: 0.481516
    the 1 and 10 similarity is: 0.509709
    the 2 and 3 similarity is: 0.491573
    the 2 and 5 similarity is: 0.482346
    the 2 and 10 similarity is: 0.510584
    the 4 and 3 similarity is: 0.450495
    the 4 and 5 similarity is: 0.506795
    the 4 and 10 similarity is: 0.512896
    the 6 and 3 similarity is: 0.743699
    the 6 and 5 similarity is: 0.468366
    the 6 and 10 similarity is: 0.439465
    the 7 and 3 similarity is: 0.482175
    the 7 and 5 similarity is: 0.494716
    the 7 and 10 similarity is: 0.524970
    the 8 and 3 similarity is: 0.491307
    the 8 and 5 similarity is: 0.491228
    the 8 and 10 similarity is: 0.520290
    the 9 and 3 similarity is: 0.522379
    the 9 and 5 similarity is: 0.496130
    the 9 and 10 similarity is: 0.493617
    [(4, 3.3447149384692278), (7, 3.3294020724526976), (9, 3.328100876390069)]

SVD应用(+代码)

SVD应用于图像压缩的例子。通过可视化的方式,该例子使我们很容易就能看到SVD对数据近似的效果。在代码库中,包含了一张手写的数字图像。该图像在第二章使用过。原始图像大小是32x32=1024像素,我们能否使用更少的像素来表示这张图呢?如果能对图像进行压缩,那么就可以节省空间或带宽开销了。

可以使用SVD来对数据降维,从而实现图像的压缩。下面就会看到利用SVD的手写数字图像的压缩过程了。下面的程序包含了数字的读入和压缩代码。要了解最后的压缩效果,对压缩后的图像进行了重构。

可知,只需两个奇异值就能相当精确地对图像实现重构。那么,到底需要多少个0-1的数字来重构图像呢?U和VT都是32x2的矩阵,两个奇异值。因此,总数字数目是64+64+2=130。和原数目1024相比,获得了几乎10倍的压缩比。

# coding=utf-8

    from numpy import *
    import numpy as np

    def printMat(inMat, thresh=0.8):
        for i in range(32):
            for k in range(32):
                if float(inMat[i,k]) > thresh:
                    print 1,
                else: print 0,
            print ''

    def imgCompress(numSV=3, thresh=0.8):
        myl = []
        for line in open('0_5.txt').readlines():
            newRow = []
            for i in range(32):
                newRow.append(int(line[i]))
            myl.append(newRow)
        myMat = mat(myl)
        print "****original matrix******"
        printMat(myMat, thresh)
        U,Sigma,VT = linalg.svd(myMat)
        SigRecon = mat(zeros((numSV, numSV)))
        for k in range(numSV):
            SigRecon[k,k] = Sigma[k]
        reconMat = U[:,:numSV]*SigRecon*VT[:numSV,:]
        print 'U:', U[:,:numSV]
        print 'SigRecon:', SigRecon
        print 'VT:', VT[:numSV,:]
        print "****reconstructed matrix using %d singular values******" % numSV
        print shape(reconMat)
        printMat(reconMat, thresh)

    imgCompress(2)

总结

  • 优点:简化数据,去除噪声,提高算法的结果。
  • 缺点:数据的转换可能难以理解。
  • 使用数据类型:数值型数据。
    SVD是一种强大的降维工具,可以利用SVD来逼近矩阵并从中提取重要特征。通过保留80%-90%的能量,就可以得到重要的特征并去掉噪声。SVD已经运用到多种应用中,其中一个成功的应用案例就是推荐引擎。

0_5.txt数据

00000000000000110000000000000000
00000000000011111100000000000000
00000000000111111110000000000000
00000000001111111111000000000000
00000000111111111111100000000000
00000001111111111111110000000000
00000000111111111111111000000000
00000000111111100001111100000000
00000001111111000001111100000000
00000011111100000000111100000000
00000011111100000000111110000000
00000011111100000000011110000000
00000011111100000000011110000000
00000001111110000000001111000000
00000011111110000000001111000000
00000011111100000000001111000000
00000001111100000000001111000000
00000011111100000000001111000000
00000001111100000000001111000000
00000001111100000000011111000000
00000000111110000000001111100000
00000000111110000000001111100000
00000000111110000000001111100000
00000000111110000000011111000000
00000000111110000000111111000000
00000000111111000001111110000000
00000000011111111111111110000000
00000000001111111111111110000000
00000000001111111111111110000000
00000000000111111111111000000000
00000000000011111111110000000000
00000000000000111111000000000000
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值