SVD(隐語義模型)協同過濾
隐語義模型,數學上稱為SVD,奇異值分解。該算法最早在文本挖掘領域被提出,用于找到文章的隐含主題,也被稱為主題模型。
隐語義模型的核心思想是通過隐含特征(Latent Factor)計算使用者和物品的相似性。
SVD是将矩陣A分解成以下形式
A=U∑VT
其中U和V均為機關正交陣,UUT=E, VVT=E, U稱為左奇異矩陣,V稱為右奇異矩陣,∑僅在對角線上有值,我們稱之為奇異值,其他值均為0.
次元:U:m*m V:n*n ∑:m*n
關于相似性的計算存在一些問題
1.物品的人為分類對推薦算法造成影響。分類是人為指定的,不同的分類标準對不同的使用者帶來預測精度的問題
2.類中物品的相似度是一個消費行為的問題,需要針對不同的使用者确定不同的權重,這樣做的可能性不大
3.即使能夠建構權重和分類,也不能完全确定某個使用者對某類産品感興趣的程度。
是以,我們需要一種針對每類使用者的不同消費行為來計算對不同物品的相似度的算法。
假設有一個新使用者E,對物品的評分為[5,5,0,0,0,5].現在在現有矩陣中找出與其最相似的使用者,并把這個使用者感興趣的物品推薦給新使用者。
使用夾角餘弦求使用者之間的相似度,即選中與新使用者之間夾角最小(餘弦值)最大的那個。
from numpy import *
import numpy as np
# 夾角餘弦距離公式
def cosSim(inA, inB):
denom = linalg.norm(inA) * linalg.norm(inB)
return float(inA * inB.T) / (denom + eps)
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]])
# 其中s是對矩陣a的奇異值分解。s除了對角元素不為0,其他元素都為0,并且對角元素從大到小排列。s中有n個奇異值,一般排在後面的比較接近0,是以僅保留比較大的r個奇異值。
U, S, VT = linalg.svd(A)
print(S)
# 避免除0
eps = 1.0e-6
# 新資料
new = mat([[5,5,0,0,0,5]])
U,S,VT = linalg.svd(A.T)
V = VT.T
# 對角矩陣(diagonal matrix)是一個主對角線之外的元素皆為0的矩陣,常寫為diag
Sigma = diag(S)
r = 2 # 取前兩個奇異值
# 得到金絲猴的U\S\V值
Ur = U[:,:r]
Sr = Sigma[:r,:r]
Vr = V[:,:r]
newresult = new*Ur*linalg.inv(Sr) # 計算User E的坐标值
print(newresult)
maxv = 0 # 最大的餘弦值
maxi = 0 # 最大值的下标
indx = 0
for vi in Vr:
temp = cosSim(newresult, vi)
if temp > maxv:
maxv = temp
maxi = indx
indx += 1
print(maxv,maxi)
[[-0.37752201 -0.08020351]]
0.9867908785596432 0
稍微封裝下
from numpy import *
# 夾角餘弦距離公式
def cosSim(inA, inB):
eps = 1.0e-6
denom = linalg.norm(inA) * linalg.norm(inB)
return float(inA * inB.T) / (denom + eps)
# dataSet:訓練集
# testVect:測試集
# r:取前r個近似值
# rank:結果排序
# distCalc: 相似度計算函數
def recommand(dataSet, testVect, r=3, rank=1, distCalc=cosSim):
m,n = shape(dataSet)
limit = min(m, n)
if r > limit:
r = limit
U,S,VT = linalg.svd(dataSet.T) # SVD分解
V = VT.T
# 取前r個U\S\V值
Ur = U[:, :r]
Sr = diag(S)[:r, :r]
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] # 排序後的索引和值
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]])
# 新資料
new = mat([[5,5,0,0,0,5]])
indx, resultarray = recommand(A, new, r=2, rank=2,distCalc=cosSim)
# 索引值
print(indx)
# 相似度
print(resultarray)
[0 3]
[0.98679088 0.95536738]
計算後得知,與之相似的使用者坐标為0,即[5,5,3,0,5,5].此時可将該使用者的第三、五個物品推薦給新使用者
與第一個使用者的相似度為98.68%, 與第四個使用者的相似度為95.53%
SVD取前部分奇異值複原圖檔
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
# 讀取圖檔
# img_eg = mpimg.imread("/Users/FengZhen/Desktop/image/bing/road.jpeg")
img_eg = mpimg.imread("/Users/FengZhen/Desktop/image/timg.jpeg")
print(img_eg.shape)
# 奇異值分解
# 我們先将圖檔變成500×2250,再做奇異值分解。從svd函數中得到的奇異值sigma它是從大到小排列的。
img_temp = img_eg.reshape(500, 750 * 3)
U,Sigma,VT = np.linalg.svd(img_temp)
# 取前部分奇異值重構圖檔
# 取前60個奇異值
sval_nums = 60
img_restruct1 = (U[:, 0:sval_nums]).dot(np.diag(Sigma[0:sval_nums])).dot(VT[0:sval_nums, :])
img_restruct1 = img_restruct1.reshape(500, 750, 3)
# 取前120個奇異值
sval_nums = 120
img_restruct2 = (U[:, 0:sval_nums]).dot(np.diag(Sigma[0:sval_nums])).dot(VT[0:sval_nums, :])
img_restruct2 = img_restruct2.reshape(500, 750, 3)
# 取前180個奇異值
sval_nums = 180
img_restruct3 = (U[:, 0:sval_nums]).dot(np.diag(Sigma[0:sval_nums])).dot(VT[0:sval_nums, :])
img_restruct3 = img_restruct3.reshape(500, 750, 3)
fig, ax = plt.subplots(1, 4, figsize=(24, 32))
ax[0].imshow(img_eg)
ax[0].set(title="src")
ax[1].imshow(img_restruct1.astype(np.uint8))
ax[1].set(title="nums of sigma = 60")
ax[2].imshow(img_restruct2.astype(np.uint8))
ax[2].set(title="nums of sigma = 120")
ax[3].imshow(img_restruct3.astype(np.uint8))
ax[3].set(title="nums of sigma = 180")
原圖