基於矩陣分解算法的推薦系統實戰算法
推薦系統,能夠根據用戶的喜愛來推薦給用戶不一樣的事物。bash
推薦系統類型:網絡
設:app
U 爲全部用戶集合dom
P 爲全部物品集合函數
R 爲用戶對物品的喜愛程度學習
模型 Model(R) = U * P測試
算法核心:網站
經過用戶對不一樣物品的打分,來預測用戶對其餘物品的喜愛程度。此處並無考慮用戶和物品的屬性,如:用戶年齡,性別,學歷,工做等,物品價格,品類,外觀等。spa
經過用戶對物品的打分,能夠創建一個推薦值矩陣,以後就能夠經過運算該矩陣來預測用戶喜愛,即爲矩陣分解算法!
矩陣分解:
將推薦值矩陣 R 分解爲矩陣 U 和 矩陣 P,使得 U 和 P 的乘積獲得的新矩陣 R* 中的元素與 R 中的已知元素的值很是接近,那麼 R* 中對應於 R 中的未知元素的值就是預測值。
推薦值矩陣:
時間簡史 | 萬曆三十年 | 大秦帝國 | 紅樓夢 | 數學簡史 | |
---|---|---|---|---|---|
小明 | 1 | 4 | 1 | ||
小王 | 2 | 2 | 4 | ||
小李 | 4 | 1 | 4 | ||
小張 | 5 | 1 | 4 |
推薦值矩陣關鍵性問題:
通常能夠採起網絡爬蟲的方式,好比對於數據的評分,能夠爬取豆瓣讀書上的數據,也能夠在本身能夠控制的網站上作埋點等來收集用戶信息。
關鍵挑戰:
當用戶和物品的數量都比較大時,推薦之矩陣一般會是一個稀疏矩陣(在矩陣中,若數值爲0的元素數目遠遠多於非0元素的數目,而且非0元素分佈沒有規律時,則稱該矩陣爲稀疏矩陣),說明大多數用戶可能並無對大多數物品表達喜愛。
冷啓動問題,是每個推薦系統都須要面對的問題。
矩陣分解實例:
即:
對比最左側的元素矩陣和最右側的預測矩陣,預測矩陣中位於原始矩陣缺失數值位置的元素值,即爲預測值。
同時也能夠獲得
即:對於在 ij 位置上的物品的喜愛數據,能夠經過第 i 個用戶的畫像向量和第 j 個物品的畫像向量表明。
使用圖形表示以下:
其中 k 在數學上的意義爲矩陣分解的秩,在業務上的意義爲 影響用戶給物品評分的 k 個影響因子,當前咱們沒法直接知道 k 的值,在模型訓練時,通常採起交叉驗證的方式來尋找最優的 k 值。
咱們可使用「和方差」來做爲損失函數
這裏經過已知的{},計算「和方差」,使之達到最小,即預測值越接近真實值。以此得出的 U 和 P 的值就是咱們須要的值。
單獨取出偏差
對偏差 L 分別在 U 和 P 上求導可得
如今咱們已經知道了損失函數的梯度(導數),下面就可使用梯度降低法來求解 U 和 P 的值。
梯度降低法
隨機選取一個起始點,而後在負梯度的方向上持續訓練,直到損失函數的梯度愈來愈接近零,此時便可取得最優解。
爲了防止過擬合的發生,對損失函數加入正則化參數
λ>0
這樣,當 U 和 P 都保證比較小的狀況下,U 或者 P 的數值劇烈變化時,U 和 P 的點積也不會有太大的變化。
最終的損失函數爲:
+
最終損失函數的梯度爲:
設定梯度降低的速率 γ(學習速率)和 k 值,並隨機初始化 U 和 P,重複訓練,直到偏差滿意爲止。
n:測試集中推薦值的總數量
:真實的用戶 u 對物品 p 的推薦值
:預測的用戶 u 對物品 p 的推薦值
數據集格式以下:
1 1119 9.000000
1 167 8.000000
1 6265 8.000000
1 1440 9.000000
1 1427 9.000000
1 5404 8.000000
1 259 7.000000
1 4156 8.000000
2 419 9.000000
2 415 10.000000
2 2834 9.000000
2 228 10.000000
2 107 10.000000
2 440 9.000000
2 44 10.000000
2 455 10.000000
複製代碼
第一列爲用戶 ID,第二列爲物品 ID,第三列爲對應的打分(1-10)
整體代碼基於 surprise 庫,能夠先安裝
pip install scikit-surprise
複製代碼
下面導入相關庫和數據集
import numpy as np
import surprise
from surprise import BaselineOnly
from surprise import Dataset
from surprise import Reader
from surprise import accuracy
from surprise.model_selection import cross_validate
from surprise.model_selection import train_test_split
reader = Reader(line_format='user item rating', sep='\t', rating_scale=(1, 10))
data = Dataset.load_from_file('book_ratings.dat.txt', reader=reader)
# 將數據隨機分爲訓練和測試數據集
trainset, testset = train_test_split(data, test_size=.25)
複製代碼
根據公式,定義算法函數
class MatrixFactorization(surprise.AlgoBase):
def __init__(self, lr, n_epochs, n_factors, lmd):
self.lr = lr # 梯度降低法的學習速率
self.n_epochs = n_epochs # 梯度降低法的迭代次數
self.n_factors = n_factors # 分解的矩陣的秩,即影響用戶打分的隱藏因子
self.lmd = lmd # 正則化參數
def fit(self, trainset):
print("Fitting data...")
# 隨機初始化 u 和 p 矩陣
u = np.random.normal(0, .1, (trainset.n_users, self.n_factors)) # 均值爲0,方差爲0.1,(行數,列數)
p = np.random.normal(0, .1, (trainset.n_items, self.n_factors))
# 梯度降低法
for _ in range(self.n_epochs):
print("Round:", _)
for i, j, r_ij in trainset.all_ratings():
# 這裏就是套用上面獲得的公式
# u_old[i] = u[i]
err = r_ij - np.dot(u[i], p[j])
u[i] -= -self.lr * err * p[j] + self.lr * self.lmd * u[i]
p[j] -= -self.lr * err * u[i] + self.lr * self.lmd * p[j]
self.u, self.p = u, p
self.trainset = trainset
print("End fitting!")
def estimate(self, i, j):
if self.trainset.knows_user(i) and self.trainset.knows_item(j):
return np.dot(self.u[i], self.p[j])
else:
return self.trainset.global_mean # 返回平均值
複製代碼
最後再訓練、預測,評估
algo = MatrixFactorization(0.005, 60, 3, 0.2)
algo.fit(trainset)
predictions = algo.test(testset)
accuracy.mae(predictions)
複製代碼
能夠調整學習速率,迭代次數,隱藏因子個數和正則化參數等來訓練不一樣的模型,並評估結果,獲取滿意的模型。