前面一篇隨筆介紹了基於協同過濾的推薦系統的基本思想及其python實現,本文是上一篇的續集。本文先介紹評價推薦系統的離線指標,稍后主要討論基於矩陣分解的LFM模型。
評價推薦系統的離線指標
1、F值得分
推薦系統的目的是為客戶提供可能喜歡(購買)的產品,但從本質上來說是一個聚類的過程(對客戶聚類或者對商品聚類)。對於一個離線的推薦系統來說,為某個客戶推薦出的產品我們可以通過某種方式知道是否為該客戶喜歡的,也就是說我們可以從該客戶處得到標准值。正因為如此,我們就可以借鑒檢驗分類模型的F值得分來做檢驗一個推薦系統的效能。
我們可以通過將單個用戶的准確率(或召回率)做累加,即可得到整個推薦系統的准確率(或召回率),如下:$$Precssion\left ( u \right )= \frac{\sum_{u\in U} R\left ( u \right )\bigcap T\left ( u \right )}{\sum _{u\in U}R\left ( u \right )}$$
其中\(R(u)\)是為用戶u做出的推薦列表,而\(T(u)\)是用戶在測試集上的真正的行為列表,通過如下公式可以計算出整個推薦系統的效能:
3、多樣性與驚喜度
多樣性指一個推薦系統根據用戶購買行為為用戶推薦的商品的種類情況,如果一個用戶購買了某種商品(青島啤酒),推薦系統老是為該用戶推薦其他廠商的啤酒,那么我們說這個推薦系統推薦的產品不具有多樣性(不是一個好的推薦系統),一個好的推薦系統可能會想到為用戶推薦購買產品相近且不同類別的產品,如可以推薦一個開瓶器等。
與多樣性相對應的另外一個指標是驚喜度,驚喜度來自於和客戶喜歡的物品不相似,但是客戶卻覺得滿意(會購買)的推薦,比如上面說的為買啤酒的客戶推薦一個尿布等。
上面幾個指標是作為推薦系統效能的離線指標,一般指標值都達到最好時該推薦系統效果最好,但是一般情況下不可能都達到最好,因此在評價多個推薦系統效能時得綜合考慮這幾個指標,選擇較好的部署。
基於隱變量的推薦(Latent Factor Model)
假設已經有n個人對m個商品進行評價,其評價矩陣\(A_{mxn}\)反應了每個人對每個商品的喜好程度(行為商品,列為用戶)。一般來說,一個人對商品或者某種事物的直觀感受會受到該商品或事物屬性值的影響,比如一個單身狗最近打算找一個對象,在眾多的女生中她選中了女生D,而他本人喜歡女生的類型具有漂亮的臉蛋、火辣的身材,白皙的皮膚等,這些特點就是他對美眉特征的一種偏好,而每個女生在臉蛋、身材、皮膚等特征上都有一定值,對於一個理性的單身狗來說,他選擇的D美眉一定是一個在他欣賞的屬性中具有較高值的女生,而在商品推薦中也是如此。雖然我們從矩陣A中只看到了每個用戶對每個商品的評價,但是我們可以認為這個評價值是用戶對商品的屬性值的偏愛程度與每個商品在這些屬性上的表現綜合結果,這里的屬性就是我們所說的隱變量。基於這樣的思想,我們可以將矩陣A分解成為“商品—商品屬性”的評價矩陣\(U_{mxk}\)與“商品屬性—客戶喜好”矩陣\(V_{kxn}\)的乘積,及:$$A_{m\times n}=U_{m\times k} V_{K\times n}$$
這里的k可以看做是分解的商品的隱屬性個數,舉個栗子,假設有Ben、Tom、John、Fred對6種商品的評價情況如下,評分越高代表對該商品越喜歡(0表示未評價)
Ben | Tom | John | Fred | |
product1 | 5 | 5 | 0 | 5 |
product2 | 5 | 0 | 3 | 4 |
product3 | 3 | 4 | 0 | 3 |
product4 | 0 | 0 | 5 | 3 |
product5 | 5 | 4 | 4 | 5 |
product6 | 5 | 4 | 5 | 5 |
現在,我們的目的是要將矩陣A分解成為兩個矩陣的乘積。在整個過程中有三個問題需要考慮。第一,采用什么樣的分解方法,怎樣使得分解后的矩陣乘積盡量逼進矩陣A;第二,用幾個因變量來分解A矩陣合適,即分解后的矩陣U的列是多少合適;第三,對於一個客戶,怎么通過矩陣分解后的矩陣為他提供推薦。
下面先來說說第一個問題,要使得矩陣A能分解成U與V的乘積,也就是說對於A矩陣中的所有元素,要與矩陣U與矩陣V的乘積對應元素差距盡可能小,這種想法我們可以借鑒一下做回歸分析時求回歸系數的訓練方法,及構造一個損失函數,對損失函數采用梯度下降的方式求得分解矩陣元素值。對於K個因變量構造一個損失函數:$$J\left ( U,V;A \right )=\sum {i=1}^{m}\sum {j=1}^{n}\left ( a{ij}-\sum {r=1}^{k}u{ir}\cdot v{rj} \right )^{2}+\lambda \left ( \sum _{i=1}^{m} \sum {r=1}^{k}u{ir}^{2}+\sum _{j=1}^{n}\sum {r=1}^{k}v{r=1}^{2}\right )$$損失函數的右邊部分是L2正則化項(對應於redge回歸的正則化),可以降低解決過擬合問題導致分解后的矩陣元素太大,對損失函數求梯度:
有了梯度,我們就可以先隨機給定“商品—商品屬性”矩陣U和“商品屬性—客戶喜好”矩陣V一些初始值,然后對損失函數通過SGD方式得到,代碼如下:
import numpy as np
import math
def lfm(a,k):
'''
參數a:表示需要分解的評價矩陣
參數k:分解的屬性(隱變量)個數
'''
assert type(a) == np.ndarray
m, n = a.shape
alpha = 0.01
lambda_ = 0.01
u = np.random.rand(m,k)
v = np.random.randn(k,n)
for t in range(1000):
for i in range(m):
for j in range(n):
if math.fabs(a[i][j]) > 1e-4:
err = a[i][j] - np.dot(u[i],v[:,j])
for r in range(k):
gu = err * v[r][j] - lambda_ * u[i][r]
gv = err * u[i][r] - lambda_ * v[r][j]
u[i][r] += alpha * gu
v[r][j] += alpha * gv
return u,v
#對前面提到的評價矩陣A作分解,先選擇三個隱變量(k=3)
A = np.array([[5,5,0,5],[5,0,3,4],[3,4,0,3],[0,0,5,3],[5,4,4,5],[5,4,5,5]])
b,c = lfm(A,3)
#查看“商品—商品屬性”矩陣b和“商品屬性—客戶喜好”矩陣c
b,c
(array([[-0.2972852 , 2.01461188, 1.0310134 ],
[-0.41028699, 0.88314041, 1.70740435],
[-0.69015017, 1.4846616 , 0.22246443],
[ 1.58422492, 1.15064457, 0.98881608],
[-0.13167643, 1.63106585, 1.39457599],
[ 0.36301204, 1.78414273, 1.44277207]]),
array([[-0.81521492, -0.8423817 , 1.25938865, -0.23881637],
[ 1.32574586, 2.24986472, 1.49232807, 1.71803 ],
[ 2.00802709, 0.18698412, 1.27586124, 1.42864871]]))
#查看b與c乘積
np.dot(b,c)
array([[ 4.98351751, 4.97581494, 3.94749428, 5.00511619],
[ 4.933806 , 2.65182221, 2.97963547, 4.054526 ],
[ 2.97761928, 3.96325495, 1.63026864, 3.03333586],
[ 2.21954795, 1.43916544, 4.97388618, 3.01117386],
[ 5.07006975, 4.0413629 , 4.047539 , 4.82602576],
[ 4.9665124 , 3.97806056, 4.96047648, 5.03973199]])
我們看到分解后的兩個矩陣乘積可以大致還原矩陣A,而且還原后的矩陣對原來用戶沒有評價的商品已經有了評價值,我們或許可以相信,該值即為用戶對該種商品的評價值的預測值(Tom對產品2的評價可能為2.7,John對產品1的評價可能為5.9),這就回答了上面提到的第三個問題。
上面的第一個問題與第三個問題已經解決,而且真的按照我們的思路將矩陣A分解成了兩個矩陣的乘積。我們再看看第二個問題,我們分解出幾個商品屬性(隱變量)合適呢?這個問題我確實沒有在文獻上的看到有較完整的說明,或許可以從實驗的角度多取幾個K值來人為地檢測其效果,比如本例,如果知道這幾個人的喜好,然后取不同的K值來檢驗結果。
LFM僅矩陣分解中的一種方式,下一篇我們再討論另一種特別有名氣的分解—SVD分解。