資料探勘之_SVD的python實現和分析pin
阿新 • • 發佈:2019-02-05
前言
今日實現第一個推薦演算法,在”機器學習實戰“一書中找到了SVD方法一章練習。這裡總結下筆記經驗,與大家分享 。
簡介
對於一個簡單的推薦系統,例如電影推薦,我們知道N個使用者對M個電影的評分。這時候對於一個新的使用者,我們應該如何給他推薦新的電影呢?一個最簡單的方法,根據使用者已看的電影,找出與他相似的使用者,然後推薦其他未看的高得分的電影。SVD提供了一個更加準確的解決方案。其基本思想是,降維! 對於電影,一般的推薦演算法是將每個電影的評分作為一個維度,對於Xi使用者,就有一個矩陣行 Xi = [xi1,xi2,xi3....xij]。 但是電影如此之多,每個人又不可能看過所有的電影,這將造成矩陣非常巨大,然後非常稀疏。SVD的具體思想是提取電影的引數Q1, ..., Qj。這個引數可以表達為電影的動作,搞笑,恐怖等程度的描述。因此後面的推薦演算法中,我們不需要對每個電影的口味進行分析,當給定新使用者的時候直接推薦適合他口味,即基於電影引數Qj的一個評分相符,的電影即可。實現
其中S向量只儲存了對角元素的成分,可以大大節省儲存空間。接下來我們就需要保留部分奇異值,對於保留的數量,一個典型的方法就是保留矩陣中90%的能量資訊。能量資訊為奇異值的平和總和。 看到這裡是否想起了PCA分析,同樣的分解矩陣,同樣的計算能量資訊。下面討論章節我會進行一些個人總結。 那麼演算法實現了,具體應用到推薦中該如何做呢?對於使用者沒有看過的電影,我們只需要計算與使用者看過的電影中的高評分電影的相似度,然後推薦相似度高的電影即可。你或許聽過協同過濾(collaborative filtering),沒聽過沒關係,我們要做得就是協同過濾。也就是基於使用者與其他使用者的資料進行對比來實現精確推薦。另外還有可以基於內容的推薦,不在本文考慮範圍。 計算相似度的集中方法,有: 歐式距離,資料差的的平方和 相關係數,一般為皮爾遜相關係數(Pearson correlation) 餘弦相似性,兩個向量的餘弦夾角的大小 注意,計算相似度後,最好把資料歸一化,一般歸一化到0-1的範圍。計算相似度中我們一般考慮使用基於物品的相似度的分析。原因是由實際考慮的。設想你有一個商店,商品種類可能不太會變動,但是使用者不斷進進出出,那麼計算那個方便呢! 對,由於物品的穩定性更高,計算量小,那麼就基於物品的推薦即可。import numpy as np data = [[1,2,3],[2,2,3],[3,3,3]] U,S,V = np.linalg.svd(data) >>> U array([[-0.48273945, 0.76567677, 0.42509024], [-0.54696309, 0.11548497, -0.82915294], [-0.68395468, -0.63277351, 0.36304778]]) >>> S array([ 7.51653849, 1.17761678, 0.33892171]) >>> V array([[-0.48273945, -0.54696309, -0.68395468], [-0.76567677, -0.11548497, 0.63277351], [-0.42509024, 0.82915294, -0.36304778]])
事例
user = 0
dataMat = mat(loadExData2())
# 直接實現
rec1 = recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst)
# 採用SVD方法實現
rec2 = recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=svdEst)
(Pdb) rec1
[(0, 5.0), (1, 5.0), (2, 5.0)]
(Pdb) rec2
[(7, 4.5148349067003304), (8, 4.514365463123859), (0, 4.5142831096323039)]
討論
(1)SVD全程為Singular Value Decomposition, 即奇異值分解。假設原始資料為m*n的矩陣,SVD會將矩陣分解為三個矩陣的乘積,中間的矩陣為一個對角不為零其餘元素為0的矩陣。那些部位0的對角元素即為奇異值。 (2)SVD其實是一種矩陣分解技術,不同的矩陣分解技術可能適合不同的應用。其他技術,例如PCA。同樣可以用於影象壓縮等其他應用。 (3)其他推薦演算法。 試想另外一種情形,假設一個商家想看怎麼捆綁銷售可以獲得利益最大化怎麼辦。這時候要考慮在所有銷售商品中,哪兩種或幾種商品銷售趨勢很一致。這其實也是一種推薦過程。這個過程中使用者的評價暫時忽略,我們嘗試找銷售量相似的產品。這裡提供一下大致的思路。 a, 計算兩兩產品的相關係數 b, 對某一產品,按照相關係數排序 c, 對排序結果,挑選相關係數最大的物品做捆綁銷售。 (4)SVD演算法其實會消耗大量的網路資源。因此實際操作的時候考慮到效率問題,大規模計算一般會是在離線情況下一天計算一次相似性,並且儲存相似性得分。加入網站剛開始建立,資料量不足(冷啟動問題),可以考慮用搜索問題來解決推薦。 理論上有很多推薦的演算法,按內容推薦, 協同過濾(包括item-based, user-based, SVD分解等),上下文推薦,Constraint-based推薦,圖關係挖掘等。很多比較牛的單個演算法, 就能在某個指標上取得較好效果, 例如MAE,RMSE。。。不過有自己的優點, 每種演算法也有自己的缺點, 例如按內容推薦主要推薦和使用者歷史結果相似的item,一般的item-based容易推薦熱門item(被更多人投票過)。。。。 所以在工業界,例如各網際網路公司, 都會使用多種演算法進行互相配合, 取長補短, 配合產品提升效果。而且在完整的推薦系統中,不僅有傳統的Rating推薦, 還需要輔以非常多的挖掘, Ranking來達到預期效果。圖片來自引用參考2
#!/usr/bin/env python
# -*- coding: UTF-8
from numpy import *
from numpy import linalg as la
import numpy as np
def loadExData2():
return[[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]]
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)
def standEst(dataMat, user, simMeas, item):
'''
計算相似性
資料矩陣,使用者ID, 相似度方法, 物品ID
simMeas: ecludSim, pearsSim, cosSim
'''
n = shape(dataMat)[1]
simTotal = 0.0; ratSimTotal = 0.0
# 不同的物品
for j in range(n):
userRating = dataMat[user,j]
if userRating == 0: continue
# 對兩個物品,item和j,考慮都有評價的部分
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
# 返回歸一化的結果
if simTotal == 0:
return 0
else:
return ratSimTotal/simTotal
def svdEst(dataMat, user, simMeas, item):
'''
基於SVD的相似性計算
'''
n = shape(dataMat)[1]
simTotal = 0.0; ratSimTotal = 0.0
U,Sigma,VT = la.svd(dataMat)
# 只考慮前四個元素,轉化成矩陣形式
Sig4 = mat(eye(4)*Sigma[:4]) #arrange Sig4 into a diagonal matrix
# 將資料降維轉化
xformedItems = dataMat.T * U[:,:4] * Sig4.I #create transformed items
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
def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst):
'''
針對某一使用者,user,進行推薦。返回dictionary
N : 推薦物品個數
simMeas : 相似性計算方法
estMethod : 針對某一使用者的某一物品,計算相似性的程式,返回向量。
'''
unratedItems = nonzero(dataMat[user,:].A==0)[1]#find unrated items
if len(unratedItems) == 0: return 'you rated everything'
itemScores = []
for item in unratedItems:
estimatedScore = estMethod(dataMat, user, simMeas, item)
itemScores.append((item, estimatedScore))
return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N]
def printMat(inMat, thresh=0.8):
for i in range(32):
for k in range(32):
if float(inMat[i,k]) > thresh:
print 1,
else: print 0,
print ''
def imgCompress(numSV=3, thresh=0.8):
# 用svd方法進行影象壓縮
myl = []
for line in open('0_5.txt').readlines():
newRow = []
for i in range(32):
newRow.append(int(line[i]))
myl.append(newRow)
myMat = mat(myl)
print "****original matrix******"
printMat(myMat, thresh)
U,Sigma,VT = la.svd(myMat) # svd
SigRecon = mat(zeros((numSV, numSV)))
for k in range(numSV):#construct diagonal matrix from vector
SigRecon[k,k] = Sigma[k]
# reconstruct with the first numSV feature
reconMat = U[:,:numSV]*SigRecon*VT[:numSV,:]
print "****reconstructed matrix using %d singular values******" % numSV
printMat(reconMat, thresh)
參考 機器學習實戰