Keras深度學習——構建電影推薦系統

語言: CN / TW / HK

theme: hydrogen

持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第19天,點選檢視活動詳情

前言

推薦系統在使用者發現中起主要作用。假設,我們具有數千種不同的產品,每種產品還存在不同的規格、樣式等。在這種情況下,對使用者進行有關產品的精準推薦將成為增加銷量的關鍵。在本節中,我們將以電影推薦系統為例介紹推薦系統模型構建的方法,從而為使用者推薦其真正感興趣的產品。

模型與資料集分析

在本節中,我們將學習如何根據使用者對電影的評分資料庫構建電影推薦系統,任務目的是最大限度地提高所推薦電影對使用者的相關性。在定義目標時,我們還應該考慮推薦的電影雖然相關,但使用者可能並不會立即觀看。同時,我們還應該確保所有的推薦並不都是關於同一種類型的,這對於推薦系統至關重要,例如,在零售環境中,我們並不希望一直向用戶推薦不同規格的同一種產品。 綜合以上分析,我們可以形式化地定義我們的目標和約束條件: - 目標:最大限度地提高推薦與使用者的相關性 - 約束:增加推薦的多樣性,並向用戶提供最多 12 條推薦建議

相關性的定義在不同任務中可能會有所不同,通常需要以實際業務需求為指導。在本任務中,我們狹義地定義相關性,也就是說,如果使用者採納了向用戶推薦的前 12 條建議中的任何一個,則認為推薦成功相關。

資料集分析

用於模型訓練的資料集中包含使用者資訊以及他們對其觀看過的電影評分資訊,其中第一列表示使用者編號,第二列表示電影編號,第三列表示使用者對電影的評分,最後一列表示時間戳。

模型分析

在實現電影推薦系統前,我們首先梳理構建推薦系統模型的策略流程: - 匯入資料 - 推薦使用者評價較高的電影,我們根據使用者在歷史記錄中喜歡的電影來訓練我們的模型。我們也可以根據使用者不喜歡的電影來進一步改善推薦的準確性,但為了簡單起見,本任務暫不考慮使用者不喜歡的電影 - 只保留觀看過 5 部以上電影的使用者 - 為不同使用者和電影分配不同 ID - 鑑於使用者的偏好可能會隨著時間而變化,因此我們需要考慮使用者的歷史記錄,其中歷史記錄中的不同事件具有與之相關的不同權重。因此,這是一個典型的時間序列分析問題,可以利用迴圈神經網路 (Recurrent neural networks, RNN) 來解決此問題 - 預處理資料,以便可以將其傳遞到長短時記憶網路 (Long Short Term Memory, LSTM) : - 輸入是使用者歷史觀看的 5 部電影 - 輸出是使用者觀看的第 6 部電影 - 構建模型執行以下操作: - 為輸入影片建立嵌入 - 將嵌入傳遞到 LSTM 層 - 將 LSTM 層的輸出連線到全連線層 - 在最後一層上應用 softmax 函式,以輸出要推薦的電影列表

電影推薦系統

在本節中,我們根據在上一小節中定義的模型策略實現電影推薦模型。

基於 LSTM 實現電影推薦系統

(1) 匯入使用者-電影資料集,該資料集包含使用者列表,並且具有使用者為不同電影提供的評分以及使用者提供評分時相應的時間戳: ```python import numpy as np import pandas as pd

column_names = ['User', 'Movies', 'rating', 'timestamp'] ratings = pd.read_csv('u.data', sep='\t', names=column_names) print(ratings.head()) 資料集的示例如下所示:shell User Movies rating timestamp 0 196 242 3 881250949 1 186 302 3 891717742 2 22 377 1 878887116 3 244 51 2 880606923 4 166 346 1 886397596 ```

(2) 篩選出使用者不喜歡的電影(即使用者對電影評分較低)的資料樣本或沒有足夠歷史記錄的使用者資料樣本。首先,排除使用者提供較低評分的電影資料樣本: python ratings = ratings[ratings['rating']>3] ratings = ratings.sort_values(by='timestamp') ratings.reset_index(inplace=True) ratings = ratings.drop(['index'],axis=1) 然後,我們僅保留歷史記錄中評價的電影數量超過 5 的使用者: python user_movie_count =ratings.groupby('User').agg({'Movies':'nunique'}).reset_index() user_movie_count.columns = ['User','Movie_count'] ratings2 = ratings.merge(user_movie_count,on='User',how='inner') movie_count = ratings2[ratings2['Movie_count']>5] movie_count = movie_count.sort_values('timestamp') movie_count.reset_index(inplace=True) movie_count = movie_count.drop(['index'],axis=1)

(3) 為不同使用者和電影分配不同 ID,以便後續使用: ```python ratings = movie_count users = ratings.User.unique() movies = ratings.Movies.unique() userid2idx = {o:i for i,o in enumerate(users)} moviesid2idx = {o:i for i,o in enumerate(movies)} idx2userid = {i:o for i,o in enumerate(users)} idx2moviesid = {i:o for i,o in enumerate(movies)}

ratings['Movies2'] = ratings.Movies.apply(lambda x: moviesid2idx[x]) ratings['User2'] = ratings.User.apply(lambda x: userid2idx[x]) ```

(4) 預處理資料,使用大小為 5 的滑動視窗構建輸入電影,輸出為使用者已觀看的第 6 部電影: python x = [] y = [] user_list = movie_count['User2'].unique() for i in range(len(user_list)): total_user_movies = movie_count[movie_count['User2']==user_list[i]].copy() total_user_movies.reset_index(inplace=True) total_user_movies = total_user_movies.drop(['index'],axis=1) for j in range(total_user_movies.shape[0]-6): x.append(total_user_movies.loc[j:(j+4),'Movies2'].tolist()) y.append(total_user_movies.loc[(j+5),'Movies2'].tolist())

(5) 預處理 xy 變數,以便可以將它們傳遞到模型,然後建立訓練和測試資料集: ```python from keras.utils import to_categorical

l = list(moviesid2idx.keys()) print(len(l)) set([x for x in l if l.count(x) > 1])

y2 = to_categorical(y, num_classes = max(y)+1) x_train = np.array(x[:40000]) x_test = np.array(x[40000:]) y_train = np.array(y2[:40000]) y_test = np.array(y2[40000:]) ```

(6) 構建電影推薦模型: python model = Sequential() model.add(Embedding(src_vocab, n_units, input_length=src_timesteps)) model.add((LSTM(100))) model.add(Dense(1000,activation='relu')) model.add(Dense(max(y)+1,activation='softmax')) model.summary() 該模型的簡要資訊輸出如下: ```shell Model: "sequential"


Layer (type) Output Shape Param #

embedding (Embedding) (None, 5, 32) 46304


lstm (LSTM) (None, 100) 53200


dense (Dense) (None, 1024) 103424


dense_1 (Dense) (None, 1447) 1483175

Total params: 1,686,103 Trainable params: 1,686,103 Non-trainable params: 0


```

(7) 編譯並擬合模型構建完成的模型: ```python from keras.optimizers import Adam adam = Adam(lr=0.0001) model.compile(optimizer=adam, loss='categorical_crossentropy', metrics = ['acc'])

history = model.fit(x_train, y_train, epochs=20, batch_size=64, validation_data=(x_test, y_test), verbose=1) ```

(8) 對測試資料進行預測: python pred = model.predict(x_test) 我們假設,如果使用者將要觀看的第 6 部影片在推薦概率最高的前 12 部影片中,則表示該推薦系統能夠向用戶推薦合適的影片,計算在所有測試使用者中,推薦了合適影片的比例: ```python count = 0 for i in range(x_test.shape[0]): rank = np.argmax(np.argsort(pred[i])[::-1]==np.argmax(y_test[i])) if rank<12: count+=1

print(count/x_test.shape[0])

0.1173

`` 我們所構建的電影推薦模型大約有11.73%` 的概率能夠為使用者推薦合適的影片。