本文先從幾何意義上對奇異值分解SVD進行簡單介紹,然后分析了特征值分解與奇異值分解的區別與聯系,最后用python實現將SVD應用於推薦系統。
1.SVD詳解
SVD(singular value decomposition),翻譯成中文就是奇異值分解。SVD的用處有很多,比如:LSA(隱性語義分析)、推薦系統、特征壓縮(或稱數據降維)。SVD可以理解為:將一個比較復雜的矩陣用更小更簡單的3個子矩陣的相乘來表示,這3個小矩陣描述了大矩陣重要的特性。
1.1奇異值分解的幾何意義(因公式輸入比較麻煩所以采取截圖的方式)
2.SVD應用於推薦系統
數據集中行代表用戶user,列代表物品item,其中的值代表用戶對物品的打分。基於SVD的優勢在於:用戶的評分數據是稀疏矩陣,可以用SVD將原始數據映射到低維空間中,然后計算物品item之間的相似度,可以節省計算資源。
整體思路:先找到用戶沒有評分的物品,然后再經過SVD“壓縮”后的低維空間中,計算未評分物品與其他物品的相似性,得到一個預測打分,再對這些物品的評分從高到低進行排序,返回前N個物品推薦給用戶。
具體代碼如下,主要分為5部分:
第1部分:加載測試數據集;
第2部分:定義三種計算相似度的方法;
第3部分:通過計算奇異值平方和的百分比來確定將數據降到多少維才合適,返回需要降到的維度;
第4部分:在已經降維的數據中,基於SVD對用戶未打分的物品進行評分預測,返回未打分物品的預測評分值;
第5部分:產生前N個評分值高的物品,返回物品編號以及預測評分值。
優勢在於:用戶的評分數據是稀疏矩陣,可以用SVD將數據映射到低維空間,然后計算低維空間中的item之間的相似度,對用戶未評分的item進行評分預測,最后將預測評分高的item推薦給用戶。
#coding=utf-8 from numpy import * from numpy import linalg as la '''加載測試數據集''' def loadExData(): return 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], [1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]]) '''以下是三種計算相似度的算法,分別是歐式距離、皮爾遜相關系數和余弦相似度, 注意三種計算方式的參數inA和inB都是列向量''' def ecludSim(inA,inB): return 1.0/(1.0+la.norm(inA-inB)) #范數的計算方法linalg.norm(),這里的1/(1+距離)表示將相似度的范圍放在0與1之間 def pearsSim(inA,inB): if len(inA)<3: return 1.0 return 0.5+0.5*corrcoef(inA,inB,rowvar=0)[0][1] #皮爾遜相關系數的計算方法corrcoef(),參數rowvar=0表示對列求相似度,這里的0.5+0.5*corrcoef()是為了將范圍歸一化放到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) #將相似度歸一到0與1之間 '''按照前k個奇異值的平方和占總奇異值的平方和的百分比percentage來確定k的值, 后續計算SVD時需要將原始矩陣轉換到k維空間''' def sigmaPct(sigma,percentage): sigma2=sigma**2 #對sigma求平方 sumsgm2=sum(sigma2) #求所有奇異值sigma的平方和 sumsgm3=0 #sumsgm3是前k個奇異值的平方和 k=0 for i in sigma: sumsgm3+=i**2 k+=1 if sumsgm3>=sumsgm2*percentage: return k '''函數svdEst()的參數包含:數據矩陣、用戶編號、物品編號和奇異值占比的閾值, 數據矩陣的行對應用戶,列對應物品,函數的作用是基於item的相似性對用戶未評過分的物品進行預測評分''' def svdEst(dataMat,user,simMeas,item,percentage): n=shape(dataMat)[1] simTotal=0.0;ratSimTotal=0.0 u,sigma,vt=la.svd(dataMat) k=sigmaPct(sigma,percentage) #確定了k的值 sigmaK=mat(eye(k)*sigma[:k]) #構建對角矩陣 xformedItems=dataMat.T*u[:,:k]*sigmaK.I #根據k的值將原始數據轉換到k維空間(低維),xformedItems表示物品(item)在k維空間轉換后的值 for j in range(n): userRating=dataMat[user,j] if userRating==0 or j==item:continue similarity=simMeas(xformedItems[item,:].T,xformedItems[j,:].T) #計算物品item與物品j之間的相似度 simTotal+=similarity #對所有相似度求和 ratSimTotal+=similarity*userRating #用"物品item和物品j的相似度"乘以"用戶對物品j的評分",並求和 if simTotal==0:return 0 else:return ratSimTotal/simTotal #得到對物品item的預測評分 '''函數recommend()產生預測評分最高的N個推薦結果,默認返回5個; 參數包括:數據矩陣、用戶編號、相似度衡量的方法、預測評分的方法、以及奇異值占比的閾值; 數據矩陣的行對應用戶,列對應物品,函數的作用是基於item的相似性對用戶未評過分的物品進行預測評分; 相似度衡量的方法默認用余弦相似度''' def recommend(dataMat,user,N=5,simMeas=cosSim,estMethod=svdEst,percentage=0.9): unratedItems=nonzero(dataMat[user,:].A==0)[1] #建立一個用戶未評分item的列表 if len(unratedItems)==0:return 'you rated everything' #如果都已經評過分,則退出 itemScores=[] for item in unratedItems: #對於每個未評分的item,都計算其預測評分 estimatedScore=estMethod(dataMat,user,simMeas,item,percentage) itemScores.append((item,estimatedScore)) itemScores=sorted(itemScores,key=lambda x:x[1],reverse=True)#按照item的得分進行從大到小排序 return itemScores[:N] #返回前N大評分值的item名,及其預測評分值
將文件命名為svd2.py,在python提示符下輸入:
>>>import svd2 >>>testdata=svd2.loadExData() >>>svd2.recommend(testdata,1,N=3,percentage=0.8)#對編號為1的用戶推薦評分較高的3件商品
Reference:
1.Peter Harrington,《機器學習實戰》,人民郵電出版社,2013
2.http://www.ams.org/samplings/feature-column/fcarc-svd (講解SVD非常好的一篇文章,對於理解SVD非常有幫助,本文中SVD的幾何意義就是參考這篇)
3. http://blog.csdn.net/xiahouzuoxin/article/details/41118351 (講解SVD與特征值分解區別的一篇文章)