(實踐操作過程中,如果數據量超大,單機會出現內存溢出報錯,無法正常運行,建議先用少量進行測試實現)
當今這個信息爆炸的社會,每個人都會面對無數的商品,無數的選擇。而推薦算法的目的幫助大家解決選擇困難症的問題,在大千世界中推薦專屬於你的商品。
推薦系統算法簡介
這裏簡單介紹下推薦系統中最爲主要的協同過濾算法,大致分爲如下幾類:
- 基於用戶的協同過濾(給用戶推薦與他相似的人購買的物品)
- 基於商品的協同過濾(給用戶推薦和他之前喜歡的物品相似的物品)
- 基於模型的協同過濾:關聯算法,聚類算法,分類算法,迴歸算法,矩陣分解,神經網絡,圖模型以及隱語義模型都屬於這個範疇。
而本次實戰使用的是矩陣分解算法。
矩陣分解其實是數學上的一個經典問題。大家從線性代數中可以知道,矩陣可以做SVD分解、Cholesky分解等,就好比任何大於1的正整數都可以分解成若干質數的乘積,矩陣分解可以認爲是一種信息壓縮。下圖是一個用戶電影評分矩陣。矩陣的每行表示一個用戶,每列表示一部電影,矩陣中每個位置的值,代表某個用戶對某個電影的評分值。
- R矩陣:用戶對電影的評分組合矩陣,用戶矩陣,
- 每一個被壓縮的行向量代表一個用戶的信息向量,
- 電影矩陣,每一個被壓縮列向量代表一個電影的信息向量。
而這樣的矩陣分解壓縮過程,使得用戶矩陣和電影矩陣都具有了一定的語義信息,必須強調的是用戶矩陣行向量的維數和電影矩陣列向量維數是相等的。所以本質上就是將每個用戶和每個電影通過已有的打分信息Embedding到同一維度的信息向量空間。
接下來我們就學習一下如何使用keras對R矩陣進行矩陣分解,獲得每個電影和每個用戶的信息向量。
推薦系統實戰
數據載入
import pandas as pd
import numpy as np
rating = pd.read_csv("./ml-latest-small/ratings.csv",sep=",")
num_user = np.max(rating["userId"])
num_movie = np.max(rating["movieId"])
print(num_user,num_movie,len(rating))
數據格式如下,第一列是index,第二列是用戶ID,第三列是電影ID,第四列示評分。(userId是排序過的數字)
userId movieId rating
0 1 1 4.0
1 1 3 4.0
2 1 6 4.0
3 1 47 5.0
......
100831 610 166534 4.0
100832 610 168248 5.0
100833 610 168250 5.0
100834 610 168252 5.0
100835 610 170875 3.0
其中num_user = 610, num_movie = 193609 len(rating)=100836。意味着我的數據中有610爲觀衆,193609 部電影,得到了100836個評分數據。從這些我們可以計算出上圖用戶電影組合的R矩陣的填充率。
100836/(610*193609)=0.008
這說明只有0.8%的用戶電影組合有評分,當然這和實際情況是相符的,畢竟一個人只會給很少部分的電影評分,所以我們發現用戶對電影的評分組合矩陣R極其稀疏。所以接下來我們要做的就是預測那些沒有評分的用戶電影組合可能的得分,填充R矩陣,這樣就可以爲用戶推薦模型預測得分較高的電影。
模型搭建
from keras import Model
import keras.backend as K
from keras.layers import Embedding,Reshape,Input,Dot
K.clear_session()
def Recmand_model(num_user,num_movie,k):
input_uer = Input(shape=[None,],dtype="int32")
model_uer = Embedding(num_user+1,k,input_length = 1)(input_uer)
model_uer = Reshape((k,))(model_uer)
input_movie = Input(shape=[None,],dtype="int32")
model_movie = Embedding(num_movie+1,k,input_length = 1)(input_movie)
model_movie = Reshape((k,))(model_movie)
out = Dot(1)([model_uer,model_movie])
model = Model(inputs=[input_uer,input_movie], outputs=out)
model.compile(loss='mse', optimizer='Adam')
model.summary()
return model
這裏就是矩陣分解的部分,模型的架構圖如下圖所示:將用戶和電影通過Eembdding層壓縮到k維度向量,然後簡單粗暴直接向量點乘,得到用戶對電影的預測評分。這裏誤差採用平方誤差MSE,優化器採用的是Adam。
model = Recmand_model(num_user,num_movie,100)
運行上方代碼就可以構建好模型,這裏筆者將用戶和電影都embedding的100維的向量空間中。
數據準備
將數據準備成( [用戶ID, 電影ID] , 用戶ID對電影ID的評分 )這種格式。接下來就可以把數據餵給模型了。
train_user = rating["userId"].values
train_movie = rating["movieId"].values
train_x = [train_user,train_movie]
train_y = rating["rating"].values
拿到輸入數據之後,設置好batch_size,epoch,就可以進行訓練了。運行下面代碼讓模型跑起來。
模型訓練
model.fit(train_x,train_y,batch_size = 100,epochs =10)
十個epoch之後loss只有0.09,這樣我們就可以不嚴謹的下結論:模型的預測誤差不超出0.1,接下來是預測部分。
模型預測
從之前讀入數據中可以得知,userId爲1的用戶,沒有對movieId爲2的電影評分。我們就用模型試試userId爲1的用戶會爲movieId爲2的電影打多少數分呢?運行下方代碼,便能知曉。
model.predict([[1],[2]])
輸出結果:array([[3.9732044]], dtype=float32)
模型預測爲3.9,而評分的總分爲5分,意味着userId爲1的用戶很有可能會喜歡movieId爲2的電影。可以考慮將movieId爲2的電影推薦給userId爲1的用戶。
結語
這裏只是採用了最簡單的方式做了一個簡單的推薦系統,而且此方式很難解決新的電影和新的用戶的推薦問題。推薦系統是門很深的學問,算法不僅需要考慮到推薦的準確率,覆蓋率,還要考慮到推薦內容的豐富性和新穎性。