SVD简化数据

来源:互联网 发布:反贪知乎 编辑:程序博客网 时间:2024/06/08 18:22

SVD简化数据

  • SVD简化数据
    • 引言
    • 基础概念
    • 3种相关度以及基于相似度推荐代码
    • 基于SVD评分估计代码
    • SVD应用代码
    • 总结
    • 0_5txt数据

引言

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

基础概念

提取信息的方法:奇异值分解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数据

0000000000000011000000000000000000000000000011111100000000000000000000000001111111100000000000000000000000111111111100000000000000000000111111111111100000000000000000011111111111111100000000000000000011111111111111100000000000000000111111100001111100000000000000011111110000011111000000000000001111110000000011110000000000000011111100000000111110000000000000111111000000000111100000000000001111110000000001111000000000000001111110000000001111000000000000111111100000000011110000000000001111110000000000111100000000000001111100000000001111000000000000111111000000000011110000000000000111110000000000111100000000000001111100000000011111000000000000001111100000000011111000000000000011111000000000111110000000000000111110000000001111100000000000001111100000000111110000000000000011111000000011111100000000000000111111000001111110000000000000000111111111111111100000000000000000111111111111111000000000000000001111111111111110000000000000000001111111111110000000000000000000001111111111000000000000000000000000111111000000000000
原创粉丝点击