1. 程式人生 > >奇異值分解SVD簡介及其在推薦系統中的簡單應用

奇異值分解SVD簡介及其在推薦系統中的簡單應用

本文先從幾何意義上對奇異值分解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