4.5.1 SVD算法回顾
A = U∑VT
其中:A是N*M的矩阵,U是M*M的方阵(里面向量正交,称为左奇异向量),∑是一个M*N的矩阵,VT是一个N*N的矩阵(里面向量正交,右奇异向量)
那么奇异值是怎么对应起来的呢?首先,将矩阵A转置AT,将会得到一个方阵,这个方阵求特征值可以得到:
(AAT)v = λv
这里得到的v,就是我们上面的右奇异值,此外我们还可以得到:
δ = λ1/2=>u=Av/δ
δ就是奇异值,u就是上面说的左奇异向量。奇异值δ跟特征值类似,在矩阵∑中也是按从大到小排列。而且δ的减少特别快,在很多情况下,前10%甚至1%的奇异值的和就占了全部奇异值之和的99%以上,也就是,我们也可以用前r个奇异值来近似描述矩阵。这里定义一下部分奇异值分解:
Am×n≈ Am×r = Um×r∑r×rVTr×n
右边的三个矩阵相乘的结果将会是一个接近于A的矩阵,在这里,r越接近于n,则相乘的结果越接近于A。而这三个矩阵的面积之和(对于存储观点来说,矩阵面积越小,存储量就越小)要远远小于原始的矩阵A。我们如果想要压缩空间来表示矩
阵,只要记录U、∑、V三个矩阵即可。
例子:
#coding:utf-8 from numpy import * A = mat([[5,5,3,0,5,5], [5,0,4,0,4,4], [0,3,0,5,4,5], [5,4,3,3,5,5]]) U = A*A.T #手工分解求矩阵的svd lamda,hU = linalg.eig(U) #hU:U特征向量 VT = A.T*A ev,hVT = linalg.eig(VT)#hVT:VT特征向量 hV = hVT.T print "hU:",hU #U矩阵 print "hV:",hV #V矩阵 sigma = sqrt(lamda) #奇异值 print"sigma:",sigma
使用NumPy的Linalg中的svd函数计算如下。
sigma = np.zeros([shape(A)[0],shape(A)[1]]) U,S,VT = linalg.svd(A) print S
4.5.2 常用的距离函数
(1)欧式距离
#欧式距离 eps = 1.0e-6 def distEclud(vecA,vecB): return linalg.norm(vecA-vecB) + eps
(2)相关系数
def distCorrcoef(vecA,vecB): return corrcoef(vecA,vecB,rowvar = 0)[0][1]
(3)Jaccard距离
import scipy.spatial.distance as dist def distJaccard(vecA,vecB): temp = mat([array(vecA.tolist()[0]),array(vecB.tolist()[0])]) return dist.pdist(temp,'jaccard')
(4)余弦定理
def cosSim(vecA,vecB): return dot(vecA,vecB)/((linalg.norm(vecA)*(linalg.norm(vecB))+eps)
4.5.3 SVD数据集
SVD数据集用于计算奇异值分解测试。
#加载修正后的数据 trainset = mat([[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],]) test = mat([[1,0,0,0,0,0,0,1,2,0,0]])
4.5.4 SVD算法主函数
根据SVD的原理,我们实现了最简单的SVD算法的主函数。
#coding:utf-8 __author__ = 'wuchuanying' #dataSet 训练集 #testVect 测试集 #r = 3 取前r个近似值 #rank =1 ,结果排序 #distCalc 相似度计算函数 from numpy import * def recommand(dataSet,testVect,r = 3,rank = 1,distCalc = cosim): m,n = shape(dataSet) limit = min(m,n) if r > limit: r = limit U,S,VT = linalg.svd(dataSet.T) #svd分解 V = VT.T Ur = U[:,:r] Sr = diag(S)[:r,:r] #If v is a 1-D array, return a 2-D array with v on the k-th diagonal. Vr = V[:,:r] testresult = testVect*Ur*linalg.inv(Sr) #计算User E的坐标值 #计算测试集与训练集每个记录的相似度 resultarray = array([distCalc(testresult,vi) for vi in vr]) descindx = argsort(-resultarray)[:rank] #排序结果---降序 return descindx,resultarray[descindx] #排序后的索引和值
4.5.5 评估结果
执行Recommend_lib中的推荐方法,获取执行结果
trainset = mat([[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],]) test = mat([[1,0,0,0,0,0,0,1,2,0,0]]) indx,resultarray = recommand(A,test,r = 2,rank = 2,distCalc = cosSim)
执行结果如下:
[4,3]
[0.99995847,0.99987554]
返回最相似的两个向量,第一个相似度是99.996%,第二个相似度99.998%,可以将这两个向量的物品推荐给测试集中的用户。
资料来源及版权所有:郑捷《机器学习算法原理与编程实践》 仅供学习研究