1. 程式人生 > >機器學習(八)——SVD推薦系統

機器學習(八)——SVD推薦系統

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

案例二:

SVD介紹博文兩篇:

參考自:http://www.igvita.com/2007/01/15/svd-recommendation-system-in-ruby/ 線性代數相關知識: 任意一個M*N的矩陣A(M行*N列,M>N),可以被寫成三個矩陣的乘積: 1. U:(M行M列的列正交矩陣) 2. S:(M*N的對角線矩陣,矩陣元素非負) 3. V:(N*N的正交矩陣的倒置) 即 A=U*S*V'(注意矩陣V需要倒置)

直觀地說:

假設我們有一個矩陣,該矩陣每一列代表一個user,每一行代表一個item。

svd-recsys

如上圖,ben,tom….代表user,season n代表item

矩陣值代表評分(0代表未評分):

如 ben對season1評分為5,tom對season1 評分為5,tom對season2未評分。

機器學習和資訊檢索:

機器學習的一個最根本也是最有趣的特性是資料壓縮概念的相關性。

如果我們能夠從資料中抽取某些有意義的感念,則我們能用更少的位元位來表述這個資料。

從資訊理論的角度則是資料之間存在相關性,則有可壓縮性。

SVD就是用來將一個大的矩陣以降低維數的方式進行有損地壓縮。

降維:(相對於機器學習中的PCA)

下面我們將用一個具體的例子展示svd的具體過程。

首先是A矩陣。

A =

5 5 0 5

5 0 3 4

3 4 0 3

0 0 5 3

5 4 4 5

5 4 5 5

(代表上圖的評分矩陣)

使用matlab呼叫svd函式:

[U,S,Vtranspose]=svd(A)


U =

-0.4472 -0.5373 -0.0064 -0.5037 -0.3857 -0.3298

-0.3586 0.2461 0.8622 -0.1458 0.0780 0.2002

-0.2925 -0.4033 -0.2275 -0.1038 0.4360 0.7065

-0.2078 0.6700 -0.3951 -0.5888 0.0260 0.0667

-0.5099 0.0597 -0.1097 0.2869 0.5946 -0.5371

-0.5316 0.1887 -0.1914 0.5341 -0.5485 0.2429


S =

17.7139 0 0 0

0 6.3917 0 0

0 0 3.0980 0

0 0 0 1.3290

0 0 0 0

0 0 0 0


Vtranspose =

-0.5710 -0.2228 0.6749 0.4109

-0.4275 -0.5172 -0.6929 0.2637

-0.3846 0.8246 -0.2532 0.3286

-0.5859 0.0532 0.0140 -0.8085

分解矩陣之後我們首先需要明白S的意義。

可以看到S很特別,是個對角線矩陣。

每個元素非負,而且依次減小,從幾何意義上來說,此值和特徵向量中的特徵值的權重有關。

所以可以取S對角線上前k個元素。

當k=2時候即將S(6*4)降維成S(2*2)

同時U(6*6),Vtranspose(4*4)相應地變為 U(6*2),V(4*2)(這裡V.transpose應該為2*4)

如下圖(圖片裡的usv矩陣元素值和我自己matlab算出的usv矩陣元素值有些正負不一致,但是本質是相同的):

svd-recsys

此時我們用降維後的U,S,V來相乘得到A2

A2=U(1:6,1:2)*S(1:2,1:2)*(V(1:4,1:2))' //matlab語句
A2 =


5.2885 5.1627 0.2149 4.4591

3.2768 1.9021 3.7400 3.8058

3.5324 3.5479 -0.1332 2.8984

1.1475 -0.6417 4.9472 2.3846

5.0727 3.6640 3.7887 5.3130

5.1086 3.4019 4.6166 5.5822

此時我們可以很直觀地看出,A2和A很接近,這就是之前說的降維可以看成一種資料的有失真壓縮。

接下來我們開始分析該矩陣中資料的相關性

我們將u的第一列當成x值,第二列當成y值(即u的每一行用一個二維向量表示)

同理,v的每一行也用一個二維向量表示。

如下圖:

svd-recsys

從圖中可以看出:

Season5,Season6特別靠近。Ben和Fred也特別靠近。

同時我們仔細看一下A矩陣可以發現,A矩陣的第5行向量和第6行向量特別相似,Ben所在的列向量和Fred所在的列向量也特別相似。

所以,從直觀上我們發現,U矩陣和V矩陣可以近似來代表A矩陣,換據話說就是將A矩陣壓縮成U矩陣和V矩陣,至於壓縮比例得看當時對S矩陣取前k個數的k值是多少。

到這裡,我們已經完成了一半。

尋找相似使用者

我們假設,現在有個名字叫Bob的新使用者,並且已知這個使用者對season n的評分向量為:[5 5 0 0 0 5]。(此向量為行向量)

我們的任務是要對他做出個性化的推薦。

我們的思路首先是利用新使用者的評分向量找出該使用者的相似使用者。

svd-recsys

對圖中公式不做證明,只需要知道結論:得到一個Bob的二維向量,即知道Bob的座標。(本質上是特徵的降維轉換)

將Bob座標新增進原來的圖中:

svd-recsys

然後從圖中找出和Bob最相似的使用者。

注意,最相似並不是距離最近的使用者,這裡的相似用餘弦相似度計算,即夾角與Bob最小的使用者座標,可以計算出最相似的使用者是ben。

接下來的推薦策略就完全取決於個人選擇了。

這裡介紹一個非常簡單的推薦策略:

找出最相似的使用者,即ben。

觀察ben的評分向量為:【5 5 3 0 5 5】。

對比Bob的評分向量:【5 5 0 0 0 5】。

然後找出ben評分過而Bob未評分的item並排序,即【season 5:5,season 3:3】。

即推薦給Bob的item依次為 season5 和 season3。

最後還有一些關於整個推薦思路的可改進的地方:

1.svd本身就是時間複雜度高的計算過程,如果資料量大的情況恐怕時間消耗無法忍受。不過可以使用梯度下降等機器學習的相關方法來進行近似計算,以減少時間消耗。

2.相似度計算方法的選擇,有多種相似度計算方法,每種都有對應優缺點,對針對不同場景使用最適合的相似度計算方法。

3.推薦策略:首先是相似使用者可以多個,每個由相似度作為權重來共同影響推薦的item的評分。

最後附上一些其他博主的博文,可以加深理解:

感謝前輩們提供的知識~