基於矩陣分解算法的推薦系統實戰

基於矩陣分解算法的推薦系統實戰算法

推薦系統

推薦系統,能夠根據用戶的喜愛來推薦給用戶不一樣的事物。bash

推薦系統類型:網絡

  1. 純手工設置推薦內容
  2. 根據物品的銷量,曝光率等來排序物品,並推薦給用戶
  3. 根據不一樣的算法,整合不一樣維度的數據,來智能的推薦物品

簡單的推薦系統模型

設: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

推薦值矩陣關鍵性問題:

  1. 初始值獲取,數據的收集
  2. 從推薦值矩陣中已知數據預測未知數據
  3. 創建評價系統,用於檢驗推薦系統的效果

收集數據

通常能夠採起網絡爬蟲的方式,好比對於數據的評分,能夠爬取豆瓣讀書上的數據,也能夠在本身能夠控制的網站上作埋點等來收集用戶信息。

預測未知數據

關鍵挑戰:

  • 當用戶和物品的數量都比較大時,推薦之矩陣一般會是一個稀疏矩陣(在矩陣中,若數值爲0的元素數目遠遠多於非0元素的數目,而且非0元素分佈沒有規律時,則稱該矩陣爲稀疏矩陣),說明大多數用戶可能並無對大多數物品表達喜愛。

  • 冷啓動問題,是每個推薦系統都須要面對的問題。

矩陣分解實例:

\begin{pmatrix}
        1 & 3 & 5 & 4 \\
          & 2  &  & 4 \\   
        3 & 4 & 3 & &  \\
        \end{pmatrix}  
        \approx 
        \begin{pmatrix}
        -0.77 & -1.84 \\
         -0.2 & -1.85 \\   
        -1.98 & -0.54 \\
        \end{pmatrix}
        \begin{pmatrix}
        -1.46 & -1.67 & -0.88 & -0.32 \\ 
        0.04 & -0.89 & -2.3 & -2.04 &  \\
        \end{pmatrix}  = 
        \begin{pmatrix}
        1.06 & 2.93 & 4.9 & 4 \\
        0.21 & 1.97 & 4.41 & 3.84 \\   
        2.88 & 3.79 & 2.98 & 1.73 &  \\
        \end{pmatrix}

即: R \approx U * P^T = R^*

對比最左側的元素矩陣和最右側的預測矩陣,預測矩陣中位於原始矩陣缺失數值位置的元素值,即爲預測值。

同時也能夠獲得

R_{ij} \approx U_i * P_j = R^*_{ij}

即:對於在 ij 位置上的物品的喜愛數據,能夠經過第 i 個用戶的畫像向量和第 j 個物品的畫像向量表明。

使用圖形表示以下:

其中 k 在數學上的意義爲矩陣分解的秩,在業務上的意義爲 影響用戶給物品評分的 k 個影響因子,當前咱們沒法直接知道 k 的值,在模型訓練時,通常採起交叉驗證的方式來尋找最優的 k 值。

咱們可使用「和方差」來做爲損失函數

min_{U,P}\sum_{i,j}1/2(R_{ij}-U_i \cdot P_j)^2

這裏經過已知的{{(i,j),r_{ij}}},計算「和方差」,使之達到最小,即預測值越接近真實值。以此得出的 U 和 P 的值就是咱們須要的值。

損失函數的梯度

單獨取出偏差

L_{ij} = 1/2(R_{ij} - U_i \cdot P_j)^2

對偏差 L 分別在 U 和 P 上求導可得

\frac{\partial L_{ij}}{\partial U_i} = \frac{\partial 1/2(R_{ij} - U_i \cdot P_j)^2}{\partial U_i} = -P_j(R_{ij} - U_i \cdot P_j)

\frac{\partial L_{ij}}{\partial P_j} = \frac{\partial 1/2(R_{ij} - U_i \cdot P_j)^2}{\partial P_j} = -U_i(R_{ij} - U_i \cdot P_j)

如今咱們已經知道了損失函數的梯度(導數),下面就可使用梯度降低法來求解 U 和 P 的值。

梯度降低法

隨機選取一個起始點,而後在負梯度的方向上持續訓練,直到損失函數的梯度愈來愈接近零,此時便可取得最優解。

引入正則化

爲了防止過擬合的發生,對損失函數加入正則化參數

λ[\sum_{i=1}^m|U_i|^2 + \sum_{i=1}^n|P_i|^2]

λ>0

這樣,當 U 和 P 都保證比較小的狀況下,U 或者 P 的數值劇烈變化時,U 和 P 的點積也不會有太大的變化。

最終的損失函數爲:

min_{U,P}\sum_{i,j}1/2(R_{ij}-U_i \cdot P_j)^2 + λ[\sum_{i=1}^m|U_i|^2 + \sum_{i=1}^n|P_i|^2]

最終損失函數的梯度爲:

\frac{\partial L_{ij}}{\partial U_i} = \frac{\partial 1/2(R_{ij} - U_i \cdot P_j)^2}{\partial U_i} = -P_j(R_{ij} - U_i \cdot P_j) + λU_i

\frac{\partial L_{ij}}{\partial P_j} = \frac{\partial 1/2(R_{ij} - U_i \cdot P_j)^2}{\partial P_j} = -U_i(R_{ij} - U_i \cdot P_j) + λP_j

運用梯度降低法求最優解

設定梯度降低的速率 γ(學習速率)和 k 值,並隨機初始化 U 和 P,重複訓練,直到偏差滿意爲止。

U_i = U_i - γ\frac{\partial L_{ij}}{\partial U_i}

P_j = P_j - γ\frac{\partial L_{ij}}{\partial P_j}

評估推薦系統

  • 最基本的就是,經過訓練集訓練模型,經過測試集測試模型,若是模型在測試集上的表現達到咱們的預期,則該模型能夠上線部署。 通常採用平均絕對離差來驗證模型預測值的好壞 M_d = 1/n\sum|r_{up} - r^*_{up}|

n:測試集中推薦值的總數量

r_{up}:真實的用戶 u 對物品 p 的推薦值

r^*_{up}:預測的用戶 u 對物品 p 的推薦值

  • 在線的 A/B 測試

項目實戰

數據集格式以下:

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)
複製代碼

能夠調整學習速率,迭代次數,隱藏因子個數和正則化參數等來訓練不一樣的模型,並評估結果,獲取滿意的模型。

相關文章
相關標籤/搜索