推薦算法概述
對於推薦系統(Recommend System, RS),從廣義上的理解為:為用戶(User)推薦相關的商品(Items)。常用的推薦算法主要有:
- 基於內容的推薦(Content-Based Recommendation)
- 協同過濾的推薦(Collaborative Filtering Recommendation)
- 基於關聯規則的推薦(Association Rule-Based Recommendation)
- 基於效用的推薦(Utility-Based Recommendation)
- 基於知識的推薦(Knowledge-Based Recommendation)
- 組合推薦(Hybrid Recommendation)
在推薦系統中,最重要的數據是用戶對商品的打分數據,數據形式如下所示:
其中,U1⋯U5表示的是5個不同的用戶,D1⋯D4表示的是4個不同的商品,這樣便構成了用戶-商品矩陣,在該矩陣中,有用戶對每一件商品的打分,其中“-”表示的是用戶未對該商品進行打分。
在推薦系統中有一類問題是對未打分的商品進行評分的預測。
目前推薦系統中用的最多的就是矩陣分解方法,在Netflix Prize推薦系統大賽中取得突出效果。以用戶-項目評分矩陣為例,矩陣分解就是預測出評分矩陣中的缺失值,然后根據預測值以某種方式向用戶推薦。常見的矩陣分解方法有基本矩陣分解(basic MF),正則化矩陣分解)(Regularized MF),基於概率的矩陣分解(PMF)等。今天以“用戶-項目評分矩陣R(N×M)”說明三種分解方式的原理以及應用。
-
Basic MF:
Basic MF是最基礎的分解方式,將評分矩陣R分解為用戶矩陣U和項目矩陣S, 通過不斷的迭代訓練使得U和S的乘積越來越接近真實矩陣,矩陣分解過程如圖:
預測值接近真實值就是使其差最小,這是我們的目標函數,然后采用梯度下降的方式迭代計算U和S,它們收斂時就是分解出來的矩陣。我們用損失函數來表示誤差(等價於目標函數):
公式1
公式1中R_ij是評分矩陣中已打分的值,U_i和S_j相當於未知變量。為求得公式1的最小值,相當於求關於U和S二元函數的最小值(極小值或許更貼切)。通常采用梯度下降的方法:
是學習速率,表示迭代的步長。其值為1.5時,通常以震盪形式接近極值點;若<1迭代單調趨向極值點;若>2圍繞極值逐漸發散,不會收斂到極值點。具體取什么值要根據實驗經驗。
-
Regularized MF
正則化矩陣分解是Basic MF的優化,解決MF造成的過擬合問題。其不是直接最小化損失函數,而是在損失函數基礎上增加規范化因子,將整體作為損失函數。
紅線表示正則化因子,在求解U和S時,仍然采用梯度下降法,此時迭代公式變為:(圖片截取自相關論文,S和V等價)
其中,
。
梯度下降結束條件:f(x)的真實值和預測值小於自己設定的閾值(很小的值,之前一直理解為是變量U和V的迭代值差小於閾值就行,弄了一天才懂。)
程序實現
對於上述的評分矩陣,通過矩陣分解的方法對其未打分項進行預測,最終的結果為:
程序代碼如下:
#!/bin/python ''' Date:20160411 @author: zhaozhiyong ''' from numpy import * def load_data(path): f = open(path) data = [] for line in f.readlines(): arr = [] lines = line.strip().split("\t") for x in lines: if x != "-": arr.append(float(x)) else: arr.append(float(0)) #print arr data.append(arr) #print data return data def gradAscent(data, K): dataMat = mat(data) print dataMat m, n = shape(dataMat) p = mat(random.random((m, K))) q = mat(random.random((K, n))) alpha = 0.0002 beta = 0.02 maxCycles = 10000 for step in xrange(maxCycles): for i in xrange(m): for j in xrange(n): if dataMat[i,j] > 0: #print dataMat[i,j] error = dataMat[i,j] for k in xrange(K): error = error - p[i,k]*q[k,j] for k in xrange(K): p[i,k] = p[i,k] + alpha * (2 * error * q[k,j] - beta * p[i,k]) q[k,j] = q[k,j] + alpha * (2 * error * p[i,k] - beta * q[k,j]) loss = 0.0 for i in xrange(m): for j in xrange(n): if dataMat[i,j] > 0: error = 0.0 for k in xrange(K): error = error + p[i,k]*q[k,j] loss = (dataMat[i,j] - error) * (dataMat[i,j] - error) for k in xrange(K): loss = loss + beta * (p[i,k] * p[i,k] + q[k,j] * q[k,j]) / 2 if loss < 0.001: break #print step if step % 1000 == 0: print loss return p, q if __name__ == "__main__": dataMatrix = load_data("./data") p, q = gradAscent(dataMatrix, 5) ''' p = mat(ones((4,10))) print p q = mat(ones((10,5))) ''' result = p * q #print p #print q print result
其中,利用梯度下降法進行矩陣分解的過程中的收斂曲線如下所示:
''' Date:20160411 原文作者:@author: zhaozhiyong ''' from pylab import * from numpy import * data = [] f = open("result") for line in f.readlines(): lines = line.strip() data.append(lines) n = len(data) x = range(n) plot(x, data, color='r',linewidth=3) plt.title('Convergence curve') plt.xlabel('generation') plt.ylabel('loss') show()