奇异值分解SVD(Singular Value Decomposition)
来源:互联网 发布:windows 2008 r2 ad域 编辑:程序博客网 时间:2024/05/16 08:20
一、SVD概念
利用SVD实现,我们能够用小得多的数据集来表示原始数据集。这样做,实际上是去除了噪声和冗余数据。
当我们试图节省空间时,去除信息就是很有用了。但是在这里我们则是从数据中抽取信息。基于这个视角,我们就可以把SVD看成是从噪声数据中抽取相关特征。
二、SVD能够进行数据约减的原因
1、在很多情况下,数据中的一小段携带了数据集中的大部分信息,其他信息则要么是噪声,要么是毫不相关的信息。
2、SVD是一种常见的矩阵分解技术,SVD将原始的数据集矩阵Data分解成三个矩阵,如下公式所示:
由公式可以看出如果原始矩阵Data是m行n列,那么分解成的三个矩阵U,,V依次是m行m列,m行n列,n行n列。
矩阵的对角元素是从大到小排列的。这些对角元素成为奇异值(Singular Value),他们对应了原始数据集矩阵Data的奇异值。奇异值和特征值是有关系的,这里的奇异值就是矩阵Data*(Data的转置)特征值的平方根。
因为矩阵的对角元素是从大到小排列的,在科学和工程中,一直存在这样一个普遍事实:在某个奇异值的数目(r个)之后,其他的奇异值都置为0。这就意味着数据集中仅有r个重要特征,而其余特征则都是噪声或者冗余特征。
三、基于Python的SVD实现以及将数据映射到低维空间的过程
1、Python实现:
<span style="font-size:18px;">dataMat = [[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]]>>> dataMat = mat(dataMat)>>> U,Simga,VT = linalg.svd(dataMat)>>> Umatrix([[ -1.77939726e-01, -1.64228493e-02, 1.80501685e-02, 9.53086885e-01, -3.38915095e-02, 2.14510824e-01, 1.10470800e-01], [ -3.55879451e-01, -3.28456986e-02, 3.61003369e-02, -5.61842993e-02, -6.73073067e-01, -4.12278297e-01, 4.94783103e-01], [ -1.77939726e-01, -1.64228493e-02, 1.80501685e-02, -2.74354465e-01, -5.05587078e-02, 8.25142037e-01, 4.57226420e-01], [ -8.89698628e-01, -8.21142464e-02, 9.02508423e-02, -1.13272764e-01, 2.86119270e-01, -4.30192532e-02, -3.11452685e-01], [ -1.33954753e-01, 5.33527340e-01, -8.35107599e-01, 6.10622664e-16, 1.11022302e-16, 8.88178420e-16, 1.11022302e-16], [ -2.15749771e-02, 7.97677135e-01, 5.13074760e-01, -6.06319451e-03, -2.14803071e-01, 1.00648733e-01, -2.09028015e-01], [ -7.19165903e-03, 2.65892378e-01, 1.71024920e-01, 1.81895835e-02, 6.44409213e-01, -3.01946200e-01, 6.27084044e-01]])>>> Simgaarray([ 9.72140007e+00, 5.29397912e+00, 6.84226362e-01, 1.52344501e-15, 2.17780259e-16])>>> VTmatrix([[ -5.81200877e-01, -5.81200877e-01, -5.67421508e-01, -3.49564973e-02, -3.49564973e-02], [ 4.61260083e-03, 4.61260083e-03, -9.61674228e-02, 7.03814349e-01, 7.03814349e-01], [ -4.02721076e-01, -4.02721076e-01, 8.17792552e-01, 5.85098794e-02, 5.85098794e-02], [ -7.06575299e-01, 7.06575299e-01, -2.22044605e-16, 2.74107087e-02, -2.74107087e-02], [ 2.74107087e-02, -2.74107087e-02, 2.18575158e-16, 7.06575299e-01, -7.06575299e-01]])</span>
上面可以看到:Simga只有一行,这是因为,由于矩阵除了对角元素其他均为0,因此这种仅返回对角元素的方式能够节省空间,这就是由Numpy的内部机制产生的。我们所要记住的就是:一旦看到Sigma就知道他是一个矩阵。
上面可以看到:Simga中前三个数值比其他的值大多了,所以我们的原始数据集Data可以用如下结果近似:
下面看我们的近似结果(接着上面的代码):
>>> Sig3 = mat([[Simga[0],0,0],[0,Simga[1],0],[0,0,Simga[2]]])>>> Sig3matrix([[ 9.72140007, 0. , 0. ], [ 0. , 5.29397912, 0. ], [ 0. , 0. , 0.68422636]])>>> U[:,:3]*Sig3*VT[:3,:]matrix([[ 1.00000000e+00, 1.00000000e+00, 1.00000000e+00, -1.51788304e-17, -1.02999206e-17], [ 2.00000000e+00, 2.00000000e+00, 2.00000000e+00, 1.73472348e-18, 1.12757026e-17], [ 1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 7.61977287e-16, 7.66747776e-16], [ 5.00000000e+00, 5.00000000e+00, 5.00000000e+00, 6.59194921e-17, 9.02056208e-17], [ 1.00000000e+00, 1.00000000e+00, -7.21644966e-16, 2.00000000e+00, 2.00000000e+00], [ 1.66533454e-16, 1.30451205e-15, -8.88178420e-16, 3.00000000e+00, 3.00000000e+00], [ 6.24500451e-17, 4.57966998e-16, -3.33066907e-16, 1.00000000e+00, 1.00000000e+00]])>>>可以看到和原始数据没什么差别。
问题:我们是如何知道保留前三个奇异值的呢?
策略1:确保要保留的奇异值的数目有很多启发式的策略,其中一个典型的做法就是保留矩阵中90%的能量信息。为了计算总能量信息,我们将所有的奇异值求其平方和。于是可以将奇异值的平方和累加到总值的90%为止。
策略2:当数据有上万的奇异值时,那么就保留前面的2000或3000个。
四:基于协同过滤的推荐引擎
1、协同过滤(collaborative filtering):是通过用户和其他用户的数据进行对比来实现推荐的。通俗点说就是:
我们不利用用于描述物品的属性来计算物品之间的相似度,而是利用用户对他们的意见来计算相似度。
2、相似度的计算(这里介绍了三种):
种1:欧氏距离
我们希望相似度的值在0--1之间变化,并且物品越相似,他们的相似度值也就越大,我们可以用相似度=1/(1+距离)来表示,当距离为0时,相似度为1.0,当距离非常大时,相似度也就趋近于0
种2:皮尔逊相关系数(pearson correlation)
它度量的是两个向量之间的相似度。它相对于欧氏距离的一个优势是:它对用户评级的量级并不敏感。比如:一个狂躁者对所有物品的评分都是5分,而另一个忧郁着对所有物品的评分都是1分,它会认为这两个向量是相等的。在Numpy中,它的计算是由函数corrcoef进行的。可以看代码实现
种3:余弦相似度(cosine similarity)
计算的是两个向量夹角的余弦值.如果夹角为90度,则相似度为0;如果两个向量的方向相同,则相似度为1.0。我们采用两个向量的预选相似度的定义如下:
下面代码实现:
from numpy import *from numpy import linalg as la#欧氏距离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)
测试代码:
>>> dataMatmatrix([[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]])>>> import svdRec>>> svdRec.ecludSim(dataMat[:,0],dataMat[:,4])0.13367660240019172>>> svdRec.ecludSim(dataMat[:,0],dataMat[:,1])1.0其他两个测试,同理。
3、基于物品的相似度还是基于用户的相似度?
一般是倾向于使用基于物品的相似度的计算方法,因为用户的数目通常很多,这个看实际情况而定。
4、推荐引擎的评价
我们采用交叉测试的方法,具体做法是:我们将某些已知的评分值去掉,然后对他们进行测试,然后计算出预测值和真实值之间的差异。
四、在推荐系统中的应用
1、首先我们构建一个基本的推荐引擎,它能够寻找用户没有尝过的菜肴。然后,通过SVD来减少特征空间并提高推荐的效果。
思路:给定一个用户,系统会为此用户返回N个最好的推荐菜。为了实现这一点,我们需要做:
(1)寻找用户没有评级的菜肴,即在用户--物品矩阵中的0值
(2)在用户没有评级的所有物品中,对每个物品预计一个可能的评级分数。
(3)对这些物品从高到底进行排序,返回前N个物品。
代码实现:
#对每一个未评分菜肴预测得分def standEst(dataMat, user, simMeas, item): n = shape(dataMat)[1] simTotal = 0.0;ratSimTotal = 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]) #print("the %d and %d similarity is : %f" % (item, j, similarity)) simTotal += similarity ratSimTotal += similarity*userRating#归一化,是所有评分值在0--5之间 if simTotal == 0: return 0 else: return ratSimTotal/simTotal#给user推荐的菜肴函数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: estimatedScore = estMethod(dataMat, user, simMeas, item) print("item:",item,"estimatedScore:",estimatedScore) itemScores.append((item, estimatedScore)) return sorted(itemScores,key=lambda jj:jj[1],reverse=True)[:N]下面对用户2没有评分的菜肴进行评分,并将评分高的推荐
>>> myMat = mat([[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],[1,1,1,0,0],[5,5,5,0,0]])>>> svdRec.recommend(myMat,2)('item:', 1, 'estimatedScore:', 2.0243290220056256)('item:', 2, 'estimatedScore:', 2.5)[(2, 2.5), (1, 2.0243290220056256)]<span style="color:#ff6666;">#余弦相似度:对物品2的预测评分值为2.5,对物品1的预测评分值为2.0243</span>>>> svdRec.recommend(myMat,2,simMeas=svdRec.ecludSim)('item:', 1, 'estimatedScore:', 2.8266504712098603)('item:', 2, 'estimatedScore:', 3.0)[(2, 3.0), (1, 2.8266504712098603)]<span style="color: rgb(255, 102, 102); font-family: Arial, Helvetica, sans-serif;">#欧氏距离:对物品2的预测评分值为3.0,对物品1的预测评分值为2.8266</span>>>> svdRec.recommend(myMat,2,simMeas=svdRec.pearsSim)('item:', 1, 'estimatedScore:', 2.0)('item:', 2, 'estimatedScore:', 2.5)[(2, 2.5), (1, 2.0)]<span style="color: rgb(255, 102, 102); font-family: Arial, Helvetica, sans-serif;">#皮尔逊相关系数:对物品2的预测评分值为:2.5,对物品1的预测评分值为2.0</span>>>>
2、利用SVD提高推荐效果
为了更加接近实际情况,假设我们现在又有一个新的数据集,我们首先要知道保留几个奇异值,根据我们前面说的,保留前90%的。计算代码如下:
dataSet = [[2,0,0,4,4,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,5], [0,0,0,0,0,0,0,1,0,4,0], [3,3,4,0,3,0,0,2,2,0,0], [5,5,5,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,5,0,0,5,0], [4,0,4,0,0,0,0,0,0,0,5], [0,0,0,0,0,4,0,0,0,0,4], [0,0,0,0,0,0,5,0,0,5,0], [0,0,0,3,0,0,0,0,4,5,0], [1,1,2,1,1,2,1,0,4,5,0]]>>> U, Sigma, VT = la.svd(mat(dataSet))>>> Sigmaarray([ 1.34342819e+01, 1.18190832e+01, 8.20176076e+00, 6.86912480e+00, 5.29063022e+00, 3.91213561e+00, 2.94562509e+00, 2.35486137e+00, 2.08702082e+00, 7.08715931e-01, 1.15779137e-16])>>> Sig2 = Sigma**2>>> sum(Sig2)496.99999999999966>>> sum(Sig2)*0.9<span style="color:#ff0000;">#前90%是这么多</span>447.29999999999973>>> sum(Sig2[:3])387.43953785565753>>> sum(Sig2[:4])434.62441339532046>>> sum(Sig2[:5])#前3、4列的值不满足90%,前5列的值满足,所以保留前5列的值。462.61518152879387
下面我们实现的函数,standEst()函数的功能一样,实现对物品的估计评分值,只不过他用到了SVD来降维
def svdEst(dataMat, user, simMeas, item): n = shape(dataMat)[1] simTotal = 0.0;ratSimTotal = 0.0 U, Sigma, VT = la.svd(dataMat) Sig4 = mat(eye(4)*Sigma[:4]) xformedItems = dataMat.T * U[:,:4] * Sig4.I#利用U矩阵将物品转换到低维空间,道理在哪? 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
至此,所要说的就说完了,经过我自己的一些对比,我觉得用不用SVD的效果不是特别明显,可能是我数据小的问题吧,而且用不用SVD的结果会有一些不同,但是我不知道哪个好一些,也没有进行测试。有兴趣的可以测试下,按照我们推荐引擎评价中所说的办法。
- 奇异值(Singular value decomposition SVD)分解
- SVD(Singular value decomposition)奇异值分解
- 奇异值分解(SVD,Singular value decomposition)
- Singular Value Decomposition(SVD)奇异值分解
- 奇异值分解SVD(Singular Value Decomposition)
- SVD奇异值分解(Singular Value Decomposition)
- 奇异值分解 SVD(Singular Value Decomposition)
- 奇异值分解(Singular Value Decomposition)
- SVD 奇异值分解( The singular value decomposition )
- 奇异值分解(Singular Value Decomposition)
- 奇异值分解(We Recommend a Singular Value Decomposition)
- 奇异值分解(We Recommend a Singular Value Decomposition)
- 奇异值分解(We Recommend a Singular Value Decomposition)
- 奇异值分解(We Recommend a Singular Value Decomposition)
- 奇异值矩阵分解(Singular Value Decomposition)的一些感想
- 正则化奇异值分解Regularized Singular Value Decomposition (RSVD)
- 推荐系统学习笔记之三 LFM (Latent Factor Model) 隐因子模型 + SVD (singular value decomposition) 奇异值分解
- 主成分分析(Principal Component Analysis)与 奇异值分解(Singular Value Decomposition)
- hdu 1078 FatMouse and Cheese dfs 记忆化搜素
- 进制转换函数
- (无bug版)有一段文本,将文本中的所有单词,存放到一个字符指针数组中(要求每个单词内存恰好)。
- Myeclipse和tomcat模拟写服务器数据
- C# winform使用redis完成简单通信
- 奇异值分解SVD(Singular Value Decomposition)
- 跟我一起学extjs5(25--模块Form的自定义的设计[3])
- 学习笔记 Java_毕向东_流程控制语句_函数_数组 2014.7.30
- 数据库操作iOS库MagicalRecord使用说明(2)
- linux下如何用GDB调试c++程序
- 导航栏相关设置
- 代码之谜(一)- 有限与无限 从整数的绝对值说起
- (指针版,无bug版)有一段文本,将文本中的所有单词,存放到一个字符指针数组中(要求每个单词内存恰好)
- c++构造函数详解