猜你喜歡”推薦演算法大賽冠軍分享
最近在整理一些以往的比賽經驗,下面這篇文章是我在DataCastle 參加“猜你喜歡”推薦演算法大賽獲得冠軍的思路分享。
我是Yes,boy! ,來自東北大學計算機學院。在猜你喜歡推薦系統競賽中,很幸運取得第一名的成績,下面我簡單介紹下我的思路。
本次比賽的賽題背景是給出了約3400萬條資料,包含一個商品網站站內顧客在某一時刻對某一個商品的打分值,分值範圍為1至5分。目的是通過對這些資料的學習和訓練,準確預測未來某時刻某個使用者對某個商品的評分。
通過背景可知這是一個關於推薦系統的研究問題。而推薦系統在預測準確度上有不同的研究方向,一種是基於TopN的研究,即主要是給使用者一個個性化的推薦列表,一般通過準確率度量推薦的優劣;一種是基於評分預測的研究, 它的度量方式一般是RMSE或者MAE 。在本次比賽就是通過RMSE來評價預測的好壞。那麼我們接下來要使用的方法就集中在優化評分預測的RMSE上。
在具體做的過程中,我覺得有幾點需要注意的:
1. 分析資料,瞭解資料的大致規律
2. 方法先嚐試簡單方法,再嘗試複雜方法;對複雜方法,要一點點的調整
基於此,我由簡及繁使用了三類模型:
1. 基於聚類的推薦
2. 基於協同過濾的推薦
3. 基於模型學習的推薦
在這三類中,每一類又包含很多方法,不能絕對的說哪一類模型最好,依照具體的資料形式、資料內容而定。
第一類的模型我大概使用到的方法:
1. 全域性均值
2. 物品均值
3. 使用者均值
4. 使用者分類-物品均值
5. 物品分類-使用者均值
6. 使用者活躍度
7. 物品活躍度
8. 改進的使用者活躍度
9. 改進的物品活躍度
…
這類模型的共同特徵是通過設計聚類方法來對使用者和物品分類,利用同類使用者對同類物品的評分均值來預測使用者對物品的評分。另外通過該模型的實現對使用者和商品的特徵有一個基本的瞭解。
下面是其中一種方法(使用者分類-物品均值)的程式碼:
import pandas as pd
import numpy as np
train = pd.read_csv('data/train.csv')
test = pd.read_csv('data/test.csv')
rate_rank = train.groupby('uid').mean().loc[:,['score']].iloc[:,-1]
rate_rank=pd.DataFrame(np.int32((rate_rank*2).values),index=rate_rank.index,columns=['group' ])
rate_rank_des = rate_rank.reset_index()
train_plus = pd.merge(train,rate_rank_des,how='left',on='uid')
test_plus = pd.merge(test,rate_rank_des,how='left',on='uid')
res = train_plus.groupby(['iid','group']).mean().reset_index().loc[:,['iid','group','score']]
result6 = pd.merge(test_plus,res,how='left' ,on=['iid','group']).filllna(3.0)
第二類的模型我主要使用的方法是基於物品的協同過濾,它的核心思想是當預測使用者對一個物品評分時,主要考慮與該物品最相似且使用者已打過分的若干物品。所以在這其中相似度的度量方法尤其重要,包括歐氏距離、皮爾遜相似度度量、餘弦相似度度量、改進的餘弦相似度度量。(之所以不使用基於使用者的協同過濾,是由於建立使用者-使用者的相似度矩陣過於巨大)
實現程式碼見 similarity.py 和Model4.py
第三類的模型使用的方法有:
1. SVD
2. NMF
3. RSVD
4. SVD++
5. SVDfeature
6. Libmf
7. Libfm
這一類模型的共同特點是矩陣分解。即對使用者-物品評分矩陣分解成若干個小矩陣,目的是分解之後的矩陣乘積接近原始矩陣,於是也實現了對原始矩陣為空的值的預測。在這些方法中,比較重要的幾個引數有:隱特徵個數,隨機梯度下降中的學習率,正則化引數,總迭代次數。具體在每個方法中這些引數的最優值也不盡相同。
具體介紹其中兩個在本賽題上表現最好的模型:svdfeature 和 libfm
Svdfeature 是一個feature-based協同過濾和排序工具,由陳天啟所在的上海交大Apex實驗室開發,大名鼎鼎的xgboost 同樣來自於他們。裡面能夠方便實現svd ,svd++ 等方法。在使用過程中,步驟如下:
1. 資料預處理:使用者和物品的id 不是連續的,需要進行重新的對映,轉換為從1至使用者/物品個數這樣的連續取值。
2. 資料格式轉換:要轉換為模型要求的格式
3. 為了儲存空間和計算速度,最好再轉換為二進位制形式
4. 設定各類引數。
5. 預測
在主要引數如下設定的情況下,線上得分能達到7.86
base_score = 3 全域性偏置
learning_rate = 0.005 學習率
wd_item ,wd_user =0.004 正則化引數
num_factor =4000 隱含特徵個數
LibFM是專門用於矩陣分解的利器,尤其是其中實現了MCMC(Markov Chain Monte Carlo)優化演算法,比常見的SGD優化方法精度要高,但運算速度要慢一些。LibFM中還實現了SGD、SGDA(Adaptive SGD)、ALS(Alternating Least Squares)等演算法。
在這裡面,也有很多引數和方法可以靈活設定,比如-dim 維度,-iter 迭代次數,-learning_rate 學習率,-method 優化方法,-task 任務型別,-validation驗證集,-regular 正則化引數
資料處理方式和上面類似,主要設定引數如下,這個方法的最優線上結果是7.88
-iter 100 –dim 1,1,64 –method MCMC –task –r
除此以外, libmf 模型效果也不錯,最優結果能達到7.85 。而基於scipy 的svd 和基於sklearn的NMF在小資料集上效果很好,資料量特別大的情況下效果不理想;也可能是我調參和優化不夠好的問題。
關於融合
一種是採用聯級融合,即使一種模型的預測結果作為下一個模型的輸入,不過要同時調整下一個模型的目標函式。另外一種方法是模型加權融合,最簡單的是線性融合,通過各個模型在驗證集的結果和超引數優化方法Hyperopt 找到最佳的融合係數,然後在線上使用這些融合係數進行融合。
關於時間
時間的因素我一直沒有使用,後來我讀到過有在svd++ 中加入時間因素的資料,預計加入後能夠提升模型效果。
由於比賽期間沒有把思路整理成文件,賽後才開始總結自己的思路,有寫的不明白的地方大家都可以提出來,然後在討論中相互啟發。