SVDchapter14 机器学习之利用SVD简化数据

餐馆可划分为很多类别,比如美式、中式、日式、牛排馆、素食店,等等。你是否想过这些
类别够用吗?或许人们喜欢这些的混合类别,或者类似中式素食店那样的子类别。如何才能知道
到底有多少类餐馆呢?我们也许可以问问专家?但是倘若某个专家说应该按照调料分类,而另一
个专家则认为应该按照配料分类,那该怎么办呢?忘了专家,我们还是从数据着手吧。我们可以
对记录用户关于餐馆观点的数据进行处理,并且从中提取出其背后的因素。
    这些因素可能会与餐馆的类别、烹饪时所用的某个特定配料,或其他任意对象一致。然后,
我们就可以利用这些因素来估计人们对没有去过的餐馆的看法。提取这些信息的方法称为奇异值分解(Singular Value Decomposition, SVD)。从生物信息学到金融学等在内的很多应用中,SVD都是提取信息的强大工具。
    本章将介绍SVD的概念及其能够进行数据约简的原因。然后,我们将会介绍基于Python的
SVD实现以及将数据映射到低维空间的过程。再接下来,我们就将学习推荐引擎的概念和它们的
实际运行过程。为了提高SVD的精度,我们将会把其应用到推荐系统中去,该推荐系统将会帮助
人们寻找到合适的餐馆。最后,我们讲述一个SVD在图像压缩中的应用例子。


对于上述进行SVD处理会得到两个奇异值。因此就会仿佛有两个概念或主题与此数据集相关联
    我们可以把奇异值想象成一个新空间。与图14-1中的矩阵给出的五维或者七维不同,我们最
终的矩阵只有二维。那么这二维分别是什么呢?创门能告诉我们数据的什么信息?这二维分别对
应图中给出的两个组,右图中已经标示出了其中的一个组。我们可以基于每个组的共同特征来命
名这二维,比如我们得到的美式BBQ和日式食品这二维。
    如何才能将原始数据变换到上述新空间中呢?下一节我们将会进一步详细地介绍SVD,届时
将会了解到SVD是如何得到U和VT两个矩阵的。VT矩阵会将用户映射到BBQ/日式食品空间去。类
似地,u矩阵会将餐馆的菜映射到BBQ/日式食品空间去。真实的数据通常不会像图14-1中的矩阵
那样稠密或整齐.这里如此只是为了便于说明问题。

矩阵分解:

    在很多情况下,数据中的一小段携带了数据集中的大部分信息,其他信息则要么是噪声,要
么就是毫不相关的信息。在线性代数中还有很多矩阵分解技术。矩阵分解可以将原始矩阵表示成
新的易于处理的形式,这种新形式是两个或多个矩阵的乘积。我们可以将这种分解过程想象成代
数中的因子分解。如何将12分解成两个数的乘积?(1,12), (2,6)和((3,4)都是合理的答案。
    不同的矩阵分解技术具有不同的性质,其中有些更适合于某个应用,有些则更适合于其他应
用。最常见的一种矩阵分解技术就是SVD。 SVD将原始的数据集矩阵Data分解成三个矩阵U, E
和VT。如果原始矩阵Data是m行n列,那么U, E和VT就分别是m行m列、m行n列和n行n列。

 

 

源码如下:

 

 

 

[html] view plain copy print?

  1. <pre name="code" class="html">#coding=utf-8  
  2. '''  
  3. Created on Mar 8, 2011  
  4.   
  5. @author: Peter  
  6. 利用SVD简化数据  
  7.   
  8. 奇异值分解  
  9. 优点:简化数据,去除噪声,提高算法的结果。  
  10. 缺点:数据的转换可能难以理解。  
  11. 适用数据类型:数值型数据。  
  12.   
  13. 总结:  
  14. 推荐引擎将物品推荐给用户,协同过滤则是一种基于用户喜好或行为数据的推荐的实现方  
  15. 法。协同过滤的核心是相似度计算方法;有很多相似度计算方法都可以用于计算物品或用户之间  
  16. 的相似度。通过在低维空间下计算相似度,SVD提高了推荐系引擎的效果。  
  17.     在大规模数据集上,SVD的计算和推荐可能是一个很困难的工程问题。通过离线方式来进行  
  18. SVD分解和相似度计算,是一种减少冗余计算和推荐所需时间的办法。  
  19.   
  20.   
  21. 信息检索:  
  22. SVD最早的应用是信息检索,我们称利用SVD的方法为隐性语义索引(LSI)或者隐性语义分析(LSA)  
  23. LSI中一个矩阵是由文档和词语组成的,当我们在矩阵上应用SVD时,会构建出多个奇异值,这些奇异值代表了文档中  
  24. 的概念或主题这一特点可以用于更高效的文档搜索。在词语拼写错误时只基于词语存在与否的简单搜索方法会遇到问题  
  25. 简单搜索的另一个问题就是同义词的使用。当查找一个词时,其同义词所在的文档可能并不会匹配上  
  26.   
  27. 推荐系统:  
  28. SVD的另一个应用就是推荐系统。简单版本的推荐系统能够计算项或者人之间的相似度,更先进的方法则  
  29. 先利用SVD从数据中构建一个主题空间,然后再在该空间下计算其相似度。  
  30.   
  31. >>> from numpy import linalg as la  
  32. >>> U,Sigma,VT = la.svd(mat(svdRec.loadExData2()))  
  33. >>> Sigma  
  34. array([ 15.77075346,  11.40670395,  11.03044558,   4.84639758,  
  35.          3.09292055,   2.58097379,   1.00413543,   0.72817072,  
  36.          0.43800353,   0.22082113,   0.07367823])  
  37. >>> Sig2 = Sigma**2  
  38. >>> sum(Sig2)  
  39. 541.99999999999955  
  40. >>> sum(Sig2)*0.9  
  41. 487.79999999999961  
  42. >>> sum(Sig2[:2])  
  43. 378.8295595113579  
  44. >>> Sig3  = mat([[Sigma[0],0,0],[0,Sigma[1],0],[0,0,Sigma[2]]])  
  45. >>> U[:,:3]*Sig3*VT[:3,:]  
  46.   
  47.   
  48.   
  49. 基于协同过滤的推荐引擎  
  50.   
  51. 相似度的计算  
  52. 不利用专家给出的重要属性来描述物品从而计算它们之间的相似度,而是利用用户对他们的意见来计算相似度,这就是协同过滤中所使用的方法  
  53. 不关心物品的描述属性,而是严格地按照许多用户的观点来计算相似度  
  54.   
  55.   
  56. 构建推荐引擎面临的挑战  
  57. 我们不必每次估计评分时都做svd分解,对于上述数据集。snd分解在效率上没有太大的区别,  
  58. 在更大规模的数据集上,svd分解会降低程序的速度。svd分解可以在程序调入时运行一次。  
  59. 在大型系统中,svd每天运行一次或者其运行频率并不高,而且要离线运行  
  60.   
  61. 推荐引擎中还存在其他更多规模扩展性的挑战问题,比如矩阵的表示方法。在系统中存在很多0,  
  62. 可以通过只存储非零元素来节省内存和计算开销  
  63. 另一个潜在的计算资源浪费来自于相似度得分。在程序中,每次需要一个推荐的分时,都要计算多个物品的相似度得分  
  64. 这些得分记录的是物品间的相似度。因此在需要时这些记录可以被另一个用户重复使用。在实际中,另一个普遍的做法就是  
  65. 离线计算并保存相似度得分  
  66.   
  67. 推荐引擎面临的另一个问题就是如何在缺乏数据时给出好的推荐,称为冷启动问题,处理起来十分困难。  
  68. 该问题的另一种说法是,用户不喜欢一个无效的物品,而用户不喜欢的物品又无效。  
  69. 如果推荐只是一个可有可无的功能,那么上述问题不是很大。如果应用的成功与否和推荐的成功与否密切相连,那么问题非常严重  
  70. '''  
  71. from numpy import *  
  72. from numpy import linalg as la  
  73.   
  74. def loadExData():  
  75.     return[[0, 0, 0, 2, 2],  
  76.            [0, 0, 0, 3, 3],  
  77.            [0, 0, 0, 1, 1],  
  78.            [1, 1, 1, 0, 0],  
  79.            [2, 2, 2, 0, 0],  
  80.            [5, 5, 5, 0, 0],  
  81.            [1, 1, 1, 0, 0]]  
  82.       
  83. def loadExData2():  
  84.     return[[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5],  
  85.            [0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3],  
  86.            [0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0],  
  87.            [3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0],  
  88.            [5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0],  
  89.            [0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0],  
  90.            [4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1],  
  91.            [0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4],  
  92.            [0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2],  
  93.            [0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0],  
  94.            [1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]]  
  95. '''  
  96. 2范数  
  97. 相似度计算  
  98.   
  99.     程序中的3个函数就是上面提到的几种相似度的计算方法。为了便于理解,NumPy的线性代  
  100. 数工具箱linalg被作为la导人,函数中假定inA和inB都是列向量。perasSim()函数会检查是否  
  101. 存在3个或更多的点。如果不存在,该函数返回1.0,这是因为此时两个向量完全相关。  
  102.   
  103. >>> reload(svdRec)  
  104. <module 'svdRec' from 'C:\Users\kernel\Documents\python\ch14\svdRec.py'>  
  105. >>> myMat = mat(svdRec.loadExData())  
  106. >>> svdRec.ecludSim(myMat[:,0],myMat[:,4])  
  107. 0.12973190755680383  
  108. >>> svdRec.ecludSim(myMat[:,0],myMat[:,0])  
  109. 1.0  
  110. >>> svdRec.cosSim(myMat[:,0],myMat[:,4])  
  111. 0.5  
  112. >>> svdRec.cosSim(myMat[:,0],myMat[:,0])  
  113. 1.0  
  114. >>> svdRec.pearsSim(myMat[:,0],myMat[:,4])  
  115. 0.20596538173840329  
  116. >>> svdRec.pearsSim(myMat[:,0],myMat[:,0])  
  117. 1.0  
  118.   
  119. '''      
  120. def ecludSim(inA,inB):  
  121.     return 1.0/(1.0 + la.norm(inA - inB))  
  122.   
  123. def pearsSim(inA,inB):  
  124.     if len(inA) < 3 : return 1.0  
  125.     return 0.5+0.5*corrcoef(inA, inB, rowvar = 0)[0][1]  
  126.   
  127. def cosSim(inA,inB):  
  128.     num = float(inA.T*inB)  
  129.     denom = la.norm(inA)*la.norm(inB)  
  130.     return 0.5+0.5*(num/denom)  
  131. '''  
  132. 推荐未尝过的菜肴  
  133.     推荐系统的工作过程是:给定一个用户,系统会为此用户返回N个最好的推荐菜。为了实现  
  134. 这一点,则需要我们做到:  
  135.     (1)寻找用户没有评级的菜肴,即在用户-物品矩阵中的0值;  
  136.     (2)在用户没有评级的所有物品中,对每个物品预计一个可能的评级分数。这就是说,我们  
  137. 认为用户可能会对物品的打分(这就是相似度计算的初衷);  
  138.     (3)对这些物品的评分从高到低进行排序,返回前N个物品。  
  139.   
  140.     上述程序包含了两个函数。第一个函数是standEst(),用来计算在给定相似度计算方法的  
  141. 条件下,用户对物品的估计评分值。第二个函数是recommend ( ),也就是推荐引擎.,它会调用  
  142. standESt()函数。我们先讨论。tandEst()函数,然后讨论recommend()函数。  
  143.     函数s七andEst()的参数包括数据矩阵、用户编号、物品编号和相似度计算方法。假设这里  
  144. 的数据矩阵为图14-1和图14-2的形式,即行对应用户、列对应物品。那么,我们首先会得到数据  
  145. 集中的物品数目,然后对两个后面用于计算估计评分值的变量进行初始化。接着,我们遍历行中  
  146. 的每个物品。如果某个物品评分值为0,就意味着用户没有对该物品评分,跳过了这个物品。该  
  147. 循环大体上是对用户评过分的每个物品进行遍历,并将它和其他物品进行比较。变量。verLap  
  148.   
  149. 给出的是两个物品当中已经被评分的那个元素0。如果两者没有任何重合元素,则相似度为。且  
  150. 中止本次循环。但是如果存在重合的物品,则基于这些重合物品计算相似度。随后,相似度会不  
  151. 断累加,每次计算时还考虑相似度和当前用户评分的乘积。最后,通过除以所有的评分总和,对  
  152. 上述相似度评分的乘积进行归一化。这就可以使得最后的评分值在。到5之间,而这些评分值则用  
  153. 于对预测值进行排序。  
  154.     函数recommend)产生了最高的N个推荐结果。如果不指定N的大小,则默认值为3。该函  
  155. 数另外的参数还包括相似度计算方法和估计方法。我们可以使用程序清单14-1中的任意一种相似  
  156. 度计算方法。此时我们能采用的估计方法只有一种选择,但是在下一小节中会增加另外一种选择。  
  157. 该函数的第一件事就是对给定的用户建立一个未评分的物品列表O。如果不存在未评分物品,那  
  158. 么就退出函数;否则,在所有的未评分物品上进行循环。对每个未评分物品,则通过调用  
  159. 。七andEst()来产生该物品的预测得分。该物品的编号和估计得分值会放在一个元素列表  
  160. i七emScores中。最后按照估计得分,对该列表进行排序并返回O。该列表是从大到小逆序排列  
  161. 的,因此其第一个值就是最大值。  
  162.   
  163.   
  164.   
  165. 基于物品相似度的推荐引擎  
  166. 基于物品的相似度还是基于用户的相似度  
  167. 计算两个餐馆菜肴之间的距离,基于物品的相似度  
  168. 计算用户距离的方法称作基于用户的相似度  
  169. 行与行之间标胶是基于物品的相似度,列与列之间的比较是基于物品的相似度  
  170. 基于物品相似度计算会随着物品数量的增加而增加,基于用户的相似度的计算时间  
  171. 会随着用户数量的增加而增加。如果用户数目很多,我们可能倾向于使用基于物品相似度的计算方法  
  172.   
  173. 推荐引擎的评价:  
  174. 如何对推荐引擎进行评价,此时既没有预测的目标值,也没有用户来调查他们对预测的满意程度  
  175. 使用交叉测试的方法,具体做法如下:将某些已知的评分值去掉,然后对他们进行预测,最后计算  
  176. 预测值与真实值之间的差异  
  177.   
  178. 推荐引擎评价的指标是称为最小均方根误差的指标,首先计算均方误差的平均值然后取其平方根。如果评级在1~5星范围内  
  179. 我们得到的RMSE为1.0,那么意味着我们的预测值和用户给出的真实评价相差了一个星级  
  180.   
  181.   
  182. 餐馆菜肴推荐引擎  
  183. 构建一个基本的推荐引擎,能够找到用户没有尝过额菜肴,然后通过SVD减少特征空间并提高推荐的效果。之后,将程序打包  
  184. 并通过可读的人际界面提供给人们使用  
  185.   
  186.   
  187. '''  
  188. #计算在给定相似度计算方法的条件下,用户对物品的估计评分值  
  189. def standEst(dataMat, user, simMeas, item):   #数据矩阵,用户编号,物品编号和相似度计算方法  
  190.     n = shape(dataMat)[1]   #数据集物品数目  
  191.     simTotal = 0.0; ratSimTotal = 0.0  #对计算估计评分值的变量进行初始化  
  192. #如果某个物品的评分值为0,那么意味着用户没有对该物品评分,跳过该物品  
  193. #寻找两个用户都评级的物品  对用户评过分的每个物品进行遍历,并将它与其他物品进行比较  
  194.     for j in range(n):   #对行中每个物品做遍历  
  195.         userRating = dataMat[user,j]  
  196.         if userRating == 0: continue  
  197.         overLap = nonzero(logical_and(dataMat[:,item].A>0, \  
  198.                                       dataMat[:,j].A>0))[0]  
  199.         if len(overLap) == 0: similarity = 0    
  200.         else: similarity = simMeas(dataMat[overLap,item], \  
  201.                                    dataMat[overLap,j])  
  202.   
  203.         #如果两者没有任何重合元素,则相似度为0,且终止本次循环  如果存在重合的物品,则基于这些重合物品进行相似度计算  
  204.   
  205.         print 'the %d and %d similarity is: %f' % (item, j, similarity)  
  206.         simTotal += similarity  #对相似度进行累加,每次计算还考虑相似度和当前用户评分的乘积  
  207.         ratSimTotal += similarity * userRating  
  208.     if simTotal == 0: return 0  
  209.     else: return ratSimTotal/simTotal  #除以所有的评分总和,对上述相似度评分的乘积进行归一化。使得最后的评分值在0~5之间,这些评分值则用于对预测值进行排序  
  210. '''  
  211. 基于SVD的评分估计  
  212.   
  213.     上述程序中包含有一个函数svdEst()。在recommend)中,这个函数用于替换对。tand-  
  214. Est()的调用,该函数对给定用户给定物品构建了一个评分估计值。如果将该函数与程序清单  
  215. 14-2中的standEst()函数进行比较,就会发现很多行代码都很相似。该函数的不同之处就在于  
  216. 它在第3行对数据集进行了SVD分解。在SVD分解之后,我们只利用包含了90%能量值的奇异值,  
  217. 这些奇异值会以NumPy}C组的形式得以保存。因此如果要进行矩阵运算,那么就必须要用这些奇  
  218. 异值构建出一个对角矩阵.。然后,利用u矩阵将物品转换到低维空间中)o  
  219.     对于给定的用户,for循环在用户对应行的所有元素上进行遍历。这和。tandEst()函数中  
  220. 的for循环的目的一样,只不过这里的相似度计算是在低维空间下进行的。相似度的计算方法也  
  221. 会作为一个参数传递给该函数。然后,我们对相似度求和,同时对相似度及对应评分值的乘积求  
  222.   
  223.   
  224. >>> import svdRec  
  225. >>> myMat = mat(svdRec.loadExData())  
  226. >>> myMat[0,1] = myMat[0,0] = myMat[1,0] = myMat[2,0] = 4  
  227. >>> myMat[3,3] = 2  
  228. >>> myMat  
  229. matrix([[4, 4, 0, 2, 2],  
  230.         [4, 0, 0, 3, 3],  
  231.         [4, 0, 0, 1, 1],  
  232.         [1, 1, 1, 2, 0],  
  233.         [2, 2, 2, 0, 0],  
  234.         [5, 5, 5, 0, 0],  
  235.         [1, 1, 1, 0, 0]])  
  236. >>> svdRec.recommend(myMat,2)  
  237. the 1 and 0 similarity is: 1.000000  
  238. the 1 and 3 similarity is: 0.928746  
  239. the 1 and 4 similarity is: 1.000000  
  240. the 2 and 0 similarity is: 1.000000  
  241. the 2 and 3 similarity is: 1.000000  
  242. the 2 and 4 similarity is: 0.000000  
  243. [(2, 2.5), (1, 2.0243290220056256)]  
  244.   
  245. >>> svdRec.recommend(myMat,2,simMeas = svdRec.ecludSim)  
  246. the 1 and 0 similarity is: 1.000000  
  247. the 1 and 3 similarity is: 0.309017  
  248. the 1 and 4 similarity is: 0.333333  
  249. the 2 and 0 similarity is: 1.000000  
  250. the 2 and 3 similarity is: 0.500000  
  251. the 2 and 4 similarity is: 0.000000  
  252. [(2, 3.0), (1, 2.8266504712098603)]  
  253. >>> svdRec.recommend(myMat,2,simMeas = svdRec.pearsSim)  
  254. the 1 and 0 similarity is: 1.000000  
  255. the 1 and 3 similarity is: 1.000000  
  256. the 1 and 4 similarity is: 1.000000  
  257. the 2 and 0 similarity is: 1.000000  
  258. the 2 and 3 similarity is: 1.000000  
  259. the 2 and 4 similarity is: 0.000000  
  260. [(2, 2.5), (1, 2.0)]  
  261. '''         
  262. def svdEst(dataMat, user, simMeas, item):  
  263.     n = shape(dataMat)[1]  
  264.     simTotal = 0.0; ratSimTotal = 0.0  
  265.     U,Sigma,VT = la.svd(dataMat)  #进行svd分解  分解之后只利用包含了90%能量值的奇异值,这些奇异值以numpy数组的形式得以保存  
  266.     Sig4 = mat(eye(4)*Sigma[:4]) #arrange Sig4 into a diagonal matrix  用奇异值 建立对角矩阵  
  267.     xformedItems = dataMat.T * U[:,:4] * Sig4.I  #create transformed items  用U矩阵将物品转换成低维空间  构建转换后的物品  
  268.     for j in range(n):  #for循环相似度计算是在低维空间下进行的  相似度的计算作为一个参数传递给该函数  
  269.         userRating = dataMat[user,j]  
  270.         if userRating == 0 or j==item: continue  
  271.         similarity = simMeas(xformedItems[item,:].T,\  
  272.                              xformedItems[j,:].T)  
  273.         print 'the %d and %d similarity is: %f' % (item, j, similarity)  
  274.         simTotal += similarity   #对相似度求和,同时对相似度及对应评分值的乘积求和  
  275.         ratSimTotal += similarity * userRating  
  276.     if simTotal == 0: return 0  
  277.     else: return ratSimTotal/simTotal  
  278. #推荐引擎,调用standEst函数  函数recommend产生了最高的n个推荐结果,如果不指定N的大小,则默认值为3  参数还包括相似度计算方法和估计方法  
  279. def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst):  
  280.     unratedItems = nonzero(dataMat[user,:].A==0)[1]#find unrated items 建立一个未评分的物品列表  
  281.     if len(unratedItems) == 0: return 'you rated everything'   #寻找未评级的物品 如果不存在未评分的物品,那么退出函数  否则在未评分的物品上进行循环  
  282.     itemScores = []  
  283.     for item in unratedItems:  
  284.         estimatedScore = estMethod(dataMat, user, simMeas, item)  #对每个未评分的物品,通过调用standEst产生该物品的预测得分,该物品的编号和估计得分值放在一个元素列表itemscores中  
  285.         itemScores.append((item, estimatedScore))  
  286.     return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N]    #寻找前n个未评级的物品  按照估计得分,对该列表进行排序并返回,该列表从大到小逆序排序,第一个值就是最大值  
  287. '''  
  288. 基于SVD的图像压缩  
  289.   
  290.     上述程序中第一个函数printMat()的作用是打印矩阵。由于矩阵包含了浮点数,因此必须  
  291. 定义浅色和深色。这里通过一个阑值来界定,后面也可以调节该值。该函数遍历所有的矩阵元素,  
  292. 当元素大于阑值时打印1,否则打印。。  
  293. '''  
  294. def printMat(inMat, thresh=0.8):   #打印矩阵,矩阵包含了浮点数,因此必须定义浅色和深色 thresh做阈值界定,遍历所有的矩阵元素,当元素大于阈值时打印1,反之打印0   
  295.     for i in range(32):  
  296.         for k in range(32):  
  297.             if float(inMat[i,k]) > thresh:  
  298.                 print 1,  
  299.             else: print 0,  
  300.         print ''  
  301.   
  302.   
  303. '''  
  304. 实验图像的压缩  允许基于任意给定的奇异值 数目来重构图像,该函数构建了一个列表,然后打开文本文件,从文件中以数值方式读入字符  
  305. 在矩阵调入之后,就可以在屏幕上输出该矩阵了。接下来就开始对原始图像进行SVD分解并重构图像。在程序中,  
  306. 通过将sigma重新否成sigrecon来实现这一点,digma是一个对角矩阵。因此需要建立一个全0矩阵  
  307. 然后将前面的那些奇异值填充到对角线上。最后,通过阶段的U和V^2矩阵,用sigrecon得到重构后的矩阵,该矩阵通过printmat函数输出  
  308. '''        
  309. def imgCompress(numSV=3, thresh=0.8):  
  310.     myl = []  
  311.     for line in open('0_5.txt').readlines():  
  312.         newRow = []  
  313.         for i in range(32):  
  314.             newRow.append(int(line[i]))  
  315.         myl.append(newRow)  
  316.     myMat = mat(myl)  
  317.     print "****original matrix******"  
  318.     printMat(myMat, thresh)  
  319.     U,Sigma,VT = la.svd(myMat)  
  320.     SigRecon = mat(zeros((numSV, numSV)))  
  321.     for k in range(numSV):#construct diagonal matrix from vector  
  322.         SigRecon[k,k] = Sigma[k]  
  323.     reconMat = U[:,:numSV]*SigRecon*VT[:numSV,:]  
  324.     print "****reconstructed matrix using %d singular values******" % numSV  
  325.     printMat(reconMat, thresh)  

<pre name="code" class="html">#coding=utf-8
'''
Created on Mar 8, 2011

@author: Peter
利用SVD简化数据

奇异值分解
优点:简化数据,去除噪声,提高算法的结果。
缺点:数据的转换可能难以理解。
适用数据类型:数值型数据。

总结:
推荐引擎将物品推荐给用户,协同过滤则是一种基于用户喜好或行为数据的推荐的实现方
法。协同过滤的核心是相似度计算方法;有很多相似度计算方法都可以用于计算物品或用户之间
的相似度。通过在低维空间下计算相似度,SVD提高了推荐系引擎的效果。
    在大规模数据集上,SVD的计算和推荐可能是一个很困难的工程问题。通过离线方式来进行
SVD分解和相似度计算,是一种减少冗余计算和推荐所需时间的办法。


信息检索:
SVD最早的应用是信息检索,我们称利用SVD的方法为隐性语义索引(LSI)或者隐性语义分析(LSA)
LSI中一个矩阵是由文档和词语组成的,当我们在矩阵上应用SVD时,会构建出多个奇异值,这些奇异值代表了文档中
的概念或主题这一特点可以用于更高效的文档搜索。在词语拼写错误时只基于词语存在与否的简单搜索方法会遇到问题
简单搜索的另一个问题就是同义词的使用。当查找一个词时,其同义词所在的文档可能并不会匹配上

推荐系统:
SVD的另一个应用就是推荐系统。简单版本的推荐系统能够计算项或者人之间的相似度,更先进的方法则
先利用SVD从数据中构建一个主题空间,然后再在该空间下计算其相似度。

>>> from numpy import linalg as la
>>> U,Sigma,VT = la.svd(mat(svdRec.loadExData2()))
>>> Sigma
array([ 15.77075346,  11.40670395,  11.03044558,   4.84639758,
         3.09292055,   2.58097379,   1.00413543,   0.72817072,
         0.43800353,   0.22082113,   0.07367823])
>>> Sig2 = Sigma**2
>>> sum(Sig2)
541.99999999999955
>>> sum(Sig2)*0.9
487.79999999999961
>>> sum(Sig2[:2])
378.8295595113579
>>> Sig3  = mat([[Sigma[0],0,0],[0,Sigma[1],0],[0,0,Sigma[2]]])
>>> U[:,:3]*Sig3*VT[:3,:]



基于协同过滤的推荐引擎

相似度的计算
不利用专家给出的重要属性来描述物品从而计算它们之间的相似度,而是利用用户对他们的意见来计算相似度,这就是协同过滤中所使用的方法
不关心物品的描述属性,而是严格地按照许多用户的观点来计算相似度


构建推荐引擎面临的挑战
我们不必每次估计评分时都做svd分解,对于上述数据集。snd分解在效率上没有太大的区别,
在更大规模的数据集上,svd分解会降低程序的速度。svd分解可以在程序调入时运行一次。
在大型系统中,svd每天运行一次或者其运行频率并不高,而且要离线运行

推荐引擎中还存在其他更多规模扩展性的挑战问题,比如矩阵的表示方法。在系统中存在很多0,
可以通过只存储非零元素来节省内存和计算开销
另一个潜在的计算资源浪费来自于相似度得分。在程序中,每次需要一个推荐的分时,都要计算多个物品的相似度得分
这些得分记录的是物品间的相似度。因此在需要时这些记录可以被另一个用户重复使用。在实际中,另一个普遍的做法就是
离线计算并保存相似度得分

推荐引擎面临的另一个问题就是如何在缺乏数据时给出好的推荐,称为冷启动问题,处理起来十分困难。
该问题的另一种说法是,用户不喜欢一个无效的物品,而用户不喜欢的物品又无效。
如果推荐只是一个可有可无的功能,那么上述问题不是很大。如果应用的成功与否和推荐的成功与否密切相连,那么问题非常严重
'''
from numpy import *
from numpy import linalg as la

def loadExData():
    return[[0, 0, 0, 2, 2],
           [0, 0, 0, 3, 3],
           [0, 0, 0, 1, 1],
           [1, 1, 1, 0, 0],
           [2, 2, 2, 0, 0],
           [5, 5, 5, 0, 0],
           [1, 1, 1, 0, 0]]
    
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]]
'''
2范数
相似度计算

    程序中的3个函数就是上面提到的几种相似度的计算方法。为了便于理解,NumPy的线性代
数工具箱linalg被作为la导人,函数中假定inA和inB都是列向量。perasSim()函数会检查是否
存在3个或更多的点。如果不存在,该函数返回1.0,这是因为此时两个向量完全相关。

>>> reload(svdRec)
<module 'svdRec' from 'C:\Users\kernel\Documents\python\ch14\svdRec.py'>
>>> myMat = mat(svdRec.loadExData())
>>> svdRec.ecludSim(myMat[:,0],myMat[:,4])
0.12973190755680383
>>> svdRec.ecludSim(myMat[:,0],myMat[:,0])
1.0
>>> svdRec.cosSim(myMat[:,0],myMat[:,4])
0.5
>>> svdRec.cosSim(myMat[:,0],myMat[:,0])
1.0
>>> svdRec.pearsSim(myMat[:,0],myMat[:,4])
0.20596538173840329
>>> svdRec.pearsSim(myMat[:,0],myMat[:,0])
1.0

'''    
def ecludSim(inA,inB):
    return 1.0/(1.0 + la.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 = la.norm(inA)*la.norm(inB)
    return 0.5+0.5*(num/denom)
'''
推荐未尝过的菜肴
    推荐系统的工作过程是:给定一个用户,系统会为此用户返回N个最好的推荐菜。为了实现
这一点,则需要我们做到:
    (1)寻找用户没有评级的菜肴,即在用户-物品矩阵中的0值;
    (2)在用户没有评级的所有物品中,对每个物品预计一个可能的评级分数。这就是说,我们
认为用户可能会对物品的打分(这就是相似度计算的初衷);
    (3)对这些物品的评分从高到低进行排序,返回前N个物品。

    上述程序包含了两个函数。第一个函数是standEst(),用来计算在给定相似度计算方法的
条件下,用户对物品的估计评分值。第二个函数是recommend ( ),也就是推荐引擎.,它会调用
standESt()函数。我们先讨论。tandEst()函数,然后讨论recommend()函数。
    函数s七andEst()的参数包括数据矩阵、用户编号、物品编号和相似度计算方法。假设这里
的数据矩阵为图14-1和图14-2的形式,即行对应用户、列对应物品。那么,我们首先会得到数据
集中的物品数目,然后对两个后面用于计算估计评分值的变量进行初始化。接着,我们遍历行中
的每个物品。如果某个物品评分值为0,就意味着用户没有对该物品评分,跳过了这个物品。该
循环大体上是对用户评过分的每个物品进行遍历,并将它和其他物品进行比较。变量。verLap

给出的是两个物品当中已经被评分的那个元素0。如果两者没有任何重合元素,则相似度为。且
中止本次循环。但是如果存在重合的物品,则基于这些重合物品计算相似度。随后,相似度会不
断累加,每次计算时还考虑相似度和当前用户评分的乘积。最后,通过除以所有的评分总和,对
上述相似度评分的乘积进行归一化。这就可以使得最后的评分值在。到5之间,而这些评分值则用
于对预测值进行排序。
    函数recommend)产生了最高的N个推荐结果。如果不指定N的大小,则默认值为3。该函
数另外的参数还包括相似度计算方法和估计方法。我们可以使用程序清单14-1中的任意一种相似
度计算方法。此时我们能采用的估计方法只有一种选择,但是在下一小节中会增加另外一种选择。
该函数的第一件事就是对给定的用户建立一个未评分的物品列表O。如果不存在未评分物品,那
么就退出函数;否则,在所有的未评分物品上进行循环。对每个未评分物品,则通过调用
。七andEst()来产生该物品的预测得分。该物品的编号和估计得分值会放在一个元素列表
i七emScores中。最后按照估计得分,对该列表进行排序并返回O。该列表是从大到小逆序排列
的,因此其第一个值就是最大值。



基于物品相似度的推荐引擎
基于物品的相似度还是基于用户的相似度
计算两个餐馆菜肴之间的距离,基于物品的相似度
计算用户距离的方法称作基于用户的相似度
行与行之间标胶是基于物品的相似度,列与列之间的比较是基于物品的相似度
基于物品相似度计算会随着物品数量的增加而增加,基于用户的相似度的计算时间
会随着用户数量的增加而增加。如果用户数目很多,我们可能倾向于使用基于物品相似度的计算方法

推荐引擎的评价:
如何对推荐引擎进行评价,此时既没有预测的目标值,也没有用户来调查他们对预测的满意程度
使用交叉测试的方法,具体做法如下:将某些已知的评分值去掉,然后对他们进行预测,最后计算
预测值与真实值之间的差异

推荐引擎评价的指标是称为最小均方根误差的指标,首先计算均方误差的平均值然后取其平方根。如果评级在1~5星范围内
我们得到的RMSE为1.0,那么意味着我们的预测值和用户给出的真实评价相差了一个星级


餐馆菜肴推荐引擎
构建一个基本的推荐引擎,能够找到用户没有尝过额菜肴,然后通过SVD减少特征空间并提高推荐的效果。之后,将程序打包
并通过可读的人际界面提供给人们使用


'''
#计算在给定相似度计算方法的条件下,用户对物品的估计评分值
def standEst(dataMat, user, simMeas, item):   #数据矩阵,用户编号,物品编号和相似度计算方法
    n = shape(dataMat)[1]   #数据集物品数目
    simTotal = 0.0; ratSimTotal = 0.0  #对计算估计评分值的变量进行初始化
#如果某个物品的评分值为0,那么意味着用户没有对该物品评分,跳过该物品
#寻找两个用户都评级的物品  对用户评过分的每个物品进行遍历,并将它与其他物品进行比较
    for j in range(n):   #对行中每个物品做遍历
        userRating = dataMat[user,j]
        if userRating == 0: continue
        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])

        #如果两者没有任何重合元素,则相似度为0,且终止本次循环  如果存在重合的物品,则基于这些重合物品进行相似度计算

        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  #除以所有的评分总和,对上述相似度评分的乘积进行归一化。使得最后的评分值在0~5之间,这些评分值则用于对预测值进行排序
'''
基于SVD的评分估计

    上述程序中包含有一个函数svdEst()。在recommend)中,这个函数用于替换对。tand-
Est()的调用,该函数对给定用户给定物品构建了一个评分估计值。如果将该函数与程序清单
14-2中的standEst()函数进行比较,就会发现很多行代码都很相似。该函数的不同之处就在于
它在第3行对数据集进行了SVD分解。在SVD分解之后,我们只利用包含了90%能量值的奇异值,
这些奇异值会以NumPy}C组的形式得以保存。因此如果要进行矩阵运算,那么就必须要用这些奇
异值构建出一个对角矩阵.。然后,利用u矩阵将物品转换到低维空间中)o
    对于给定的用户,for循环在用户对应行的所有元素上进行遍历。这和。tandEst()函数中
的for循环的目的一样,只不过这里的相似度计算是在低维空间下进行的。相似度的计算方法也
会作为一个参数传递给该函数。然后,我们对相似度求和,同时对相似度及对应评分值的乘积求


>>> import svdRec
>>> myMat = mat(svdRec.loadExData())
>>> myMat[0,1] = myMat[0,0] = myMat[1,0] = myMat[2,0] = 4
>>> myMat[3,3] = 2
>>> myMat
matrix([[4, 4, 0, 2, 2],
        [4, 0, 0, 3, 3],
        [4, 0, 0, 1, 1],
        [1, 1, 1, 2, 0],
        [2, 2, 2, 0, 0],
        [5, 5, 5, 0, 0],
        [1, 1, 1, 0, 0]])
>>> svdRec.recommend(myMat,2)
the 1 and 0 similarity is: 1.000000
the 1 and 3 similarity is: 0.928746
the 1 and 4 similarity is: 1.000000
the 2 and 0 similarity is: 1.000000
the 2 and 3 similarity is: 1.000000
the 2 and 4 similarity is: 0.000000
[(2, 2.5), (1, 2.0243290220056256)]

>>> svdRec.recommend(myMat,2,simMeas = svdRec.ecludSim)
the 1 and 0 similarity is: 1.000000
the 1 and 3 similarity is: 0.309017
the 1 and 4 similarity is: 0.333333
the 2 and 0 similarity is: 1.000000
the 2 and 3 similarity is: 0.500000
the 2 and 4 similarity is: 0.000000
[(2, 3.0), (1, 2.8266504712098603)]
>>> svdRec.recommend(myMat,2,simMeas = svdRec.pearsSim)
the 1 and 0 similarity is: 1.000000
the 1 and 3 similarity is: 1.000000
the 1 and 4 similarity is: 1.000000
the 2 and 0 similarity is: 1.000000
the 2 and 3 similarity is: 1.000000
the 2 and 4 similarity is: 0.000000
[(2, 2.5), (1, 2.0)]
'''       
def svdEst(dataMat, user, simMeas, item):
    n = shape(dataMat)[1]
    simTotal = 0.0; ratSimTotal = 0.0
    U,Sigma,VT = la.svd(dataMat)  #进行svd分解  分解之后只利用包含了90%能量值的奇异值,这些奇异值以numpy数组的形式得以保存
    Sig4 = mat(eye(4)*Sigma[:4]) #arrange Sig4 into a diagonal matrix  用奇异值 建立对角矩阵
    xformedItems = dataMat.T * U[:,:4] * Sig4.I  #create transformed items  用U矩阵将物品转换成低维空间  构建转换后的物品
    for j in range(n):  #for循环相似度计算是在低维空间下进行的  相似度的计算作为一个参数传递给该函数
        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
#推荐引擎,调用standEst函数  函数recommend产生了最高的n个推荐结果,如果不指定N的大小,则默认值为3  参数还包括相似度计算方法和估计方法
def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst):
    unratedItems = nonzero(dataMat[user,:].A==0)[1]#find unrated items 建立一个未评分的物品列表
    if len(unratedItems) == 0: return 'you rated everything'   #寻找未评级的物品 如果不存在未评分的物品,那么退出函数  否则在未评分的物品上进行循环
    itemScores = []
    for item in unratedItems:
        estimatedScore = estMethod(dataMat, user, simMeas, item)  #对每个未评分的物品,通过调用standEst产生该物品的预测得分,该物品的编号和估计得分值放在一个元素列表itemscores中
        itemScores.append((item, estimatedScore))
    return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N]    #寻找前n个未评级的物品  按照估计得分,对该列表进行排序并返回,该列表从大到小逆序排序,第一个值就是最大值
'''
基于SVD的图像压缩

    上述程序中第一个函数printMat()的作用是打印矩阵。由于矩阵包含了浮点数,因此必须
定义浅色和深色。这里通过一个阑值来界定,后面也可以调节该值。该函数遍历所有的矩阵元素,
当元素大于阑值时打印1,否则打印。。
'''
def printMat(inMat, thresh=0.8):   #打印矩阵,矩阵包含了浮点数,因此必须定义浅色和深色 thresh做阈值界定,遍历所有的矩阵元素,当元素大于阈值时打印1,反之打印0 
    for i in range(32):
        for k in range(32):
            if float(inMat[i,k]) > thresh:
                print 1,
            else: print 0,
        print ''


'''
实验图像的压缩  允许基于任意给定的奇异值 数目来重构图像,该函数构建了一个列表,然后打开文本文件,从文件中以数值方式读入字符
在矩阵调入之后,就可以在屏幕上输出该矩阵了。接下来就开始对原始图像进行SVD分解并重构图像。在程序中,
通过将sigma重新否成sigrecon来实现这一点,digma是一个对角矩阵。因此需要建立一个全0矩阵
然后将前面的那些奇异值填充到对角线上。最后,通过阶段的U和V^2矩阵,用sigrecon得到重构后的矩阵,该矩阵通过printmat函数输出
'''      
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 = la.svd(myMat)
    SigRecon = mat(zeros((numSV, numSV)))
    for k in range(numSV):#construct diagonal matrix from vector
        SigRecon[k,k] = Sigma[k]
    reconMat = U[:,:numSV]*SigRecon*VT[:numSV,:]
    print "****reconstructed matrix using %d singular values******" % numSV
    printMat(reconMat, thresh)

 

 

推荐引擎将物品推荐给用户,协同过滤则是一种基于用户喜好或行为数据的推荐的实现方
法。协同过滤的核心是相似度计算方法;有很多相似度计算方法都可以用于计算物品或用户之间
的相似度。通过在低维空间下计算相似度,SVD提高了推荐系引擎的效果。
    在大规模数据集上,SVD的计算和推荐可能是一个很困难的工程问题。通过离线方式来进行
SVD分解和相似度计算,是一种减少冗余计算和推荐所需时间的办法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值