【推荐系统】手写ItemCF/UserCF代码,你会吗?
前言
之前朋友說有同學在面字節算法實習時讓復現DeepFM算法(包括訓練),然后就懵了。因此最近在整理傳統推薦算法的一些內容時,大概是這樣的:
就想到「基于鄰域的協同過濾(UserCF與ItemCF),除了了解原理、應用場景的區別外,如果現場寫實現偽代碼你會么?」 有很多文章在講協同過濾的原理,很少具體到代碼,所以這次把以前寫的CF代碼優化下進行分享。
本文約1.7k字,預計閱讀5分鐘.
概要
協同過濾是「基于用戶行為」設計的推薦算法,具體來說,是「通過群體的行為來找到某種相似性」(用戶之間的相似性或者物品之間的相似性),通過相似性來為用戶做決策和推薦。當然,協同過濾有基于鄰域的、隱語義模型等,這里我們主要指的是基于鄰域的ItemCF和UserCF。具體的原理就不多做解釋了,見下圖:
ItemCF與UserCF的代碼其實很相近,因此以ItemCF為例,兩個算法的具體代碼可以在github:https://github.com/ZiyaoGeng/SimpleCF(閱讀原文)中找到。
ItemCF
對于ItemCF算法的實踐,我采用類的方式進行建立,主要內容包括:
模型的輸入;
初始化【計算相似度矩陣】;
對單個用戶進行推薦;
對測試集的所有內容進行推薦;
因此大體框架為:
class ItemCF:def __init__(self, user_item_dict, item_hot_list, sim_item_topK, topN, i2i_sim=None):"""Item-based collaborative filtering:param user_item_dict: A dict. {user1: [(item1, score),...], user2: ...}:param item_hot_list: A list. The popular movies list.:param sim_item_topK: A scalar. Choose topK items for calculate.:param topN: A scalar. The number of recommender list.:param i2i_sim: dict. If None, the model should calculate similarity matrix."""def __get_item_sim(self):"""calculate item similarity weight matrix:return: i2i_sim"""def recommend(self, user_id):def recommend_all(self, test):輸入
user_item_dict:首先最重要的是建立user-item共現矩陣作為「輸入」,當然在實際應用中,我們采用字典的形式減少不必要的空間。對于ItemCF,需要user-item-dict字典,而對于UserCF,需要user-item-dict和item-user-dict兩個字典:
item-user-dict:
user-item-dict:
【注】元組部分score可以去除或者替換為其他內容;
item_hot_list:熱門物品的列表,主要是當推薦物品數量不夠時(冷啟動等),用熱門物品進行填充,計算也比較簡單;
sim_item_topK:選取某個物品最相似的TopK個物品,不然選擇所有物品會產生很大的計算量;
topN:推薦列表的大小;
i2i_sim:物品相似度矩陣。一般計算相似度矩陣后會在本地進行保存,因此如果之前計算過,則只需讀取,不用重復計算;
物品相似度矩陣
ItemCF算法認為「物品A和物品B具有很大的相似度是因為喜歡物品A的用戶也大多喜歡物品B」,因此需要計算物品相似度矩陣,主要分為兩步:
統計兩兩物品之間的共現次數,即「用戶同時喜歡兩個物品」;
通過Jaccard距離、余弦相似度等方式計算兩個物品的相似性;
當然對于1來說,需要對于活躍的用戶進行懲罰,通過增加IUF(Inverse User Frequence),用戶活躍度對數倒數的參數,對應代碼中:
i2i_sim[i][j]?+=?1?/?math.log(len(items)?+?1)在2中,通過余弦相似度的計算可以降低熱門物品會和很多物品相似的可能性,因為基于物品的推薦主要是挖掘長尾信息。
i2i_sim[i][j]?=?wij?/?math.sqrt(item_cnt[i]?*?item_cnt[j])?具體代碼如下:
????def?__get_item_sim(self):"""calculate?item?similarity?weight?matrix:return:?i2i_sim"""i2i_sim?=?dict()item_cnt?=?defaultdict(int)??#?Count?the?number?of?visits?to?the?itemfor?user,?items?in?tqdm(self.user_item_dict.items()):for?i,?score_i?in?items:item_cnt[i]?+=?1i2i_sim.setdefault(i,?{})for?j,?score_j?in?items:if?i?==?j:continuei2i_sim[i].setdefault(j,?0)i2i_sim[i][j]?+=?1?/?math.log(len(items)?+?1)??for?i,?related_items?in?i2i_sim.items():for?j,?wij?in?related_items.items():i2i_sim[i][j]?=?wij?/?math.sqrt(item_cnt[i]?*?item_cnt[j])??return?i2i_sim推薦
使用之前計算的相似度矩陣對每個用戶進行推薦。主要分為兩步:
獲取推薦用戶的歷史行為,在相似度矩陣中選取每個歷史物品(遍歷)最相似的topk個物品來計算每個物品(未出現在歷史行為中)的「累積權重」;
若1中所有物品數量小于推薦列表,則采用其他策略進行填充,如選擇熱門物品,但權重分數是最小的,可以為負數。然后對權重列表進行排序,選取權重分數最高的TopN個物品;
具體代碼如下:
????def?recommend(self,?user_id):"""recommend?one?user:param?user_id:?user's?ID:return:"""item_rank?=?dict()user_hist_items?=?self.user_item_dict[user_id]for?i,?score_i?in?user_hist_items:for?j,?wij?in?sorted(self.i2i_sim[i].items(),?key=lambda?x:?x[1],?reverse=True)[:self.sim_item_topK]:if?j?in?user_hist_items:continueitem_rank.setdefault(j,?0)item_rank[j]?+=?1?*?wijif?len(item_rank)?<?self.topN:for?i,?item?in?enumerate(self.item_hot_list):if?item?in?item_rank:continueitem_rank[item]?=?-?i?-?1??#?rank?score?<?0if?len(item_rank)?==?self.topN:breakitem_rank?=?sorted(item_rank.items(),?key=lambda?x:?x[1],?reverse=True)[:self.topN]return?[i?for?i,?score?in?item_rank]【注】在計算每個物品的權重時,選擇了1 * wij,并沒有用到分數和其他信息。
評估
「數據集」:ml-1m,當然也可以選擇其它。預處理主要包括:
選擇評分大于x的作為正樣本(當然可以直接將所有交互過的物品都作為正樣本,將評分融入推薦時權重分數的計算);
選取每個用戶的最后一次發生交互的物品作為測試集(所以評估的結果會比項亮《推薦系統實戰》的結果差很多);
建立user-item和item-user字典;
「評估指標」:
HR(點擊率)與NDCG,當每個用戶真實的交互物品只有一個時,HR等價于Recall;
評估代碼如下:
def?evaluate_model(model,?test):"""evaluate?model:param?model:?model?of?CF:param?test:?dict.:return:?hit?rate,?ndcg"""hit,?ndcg?=?0,?0for?user_id,?item_id?in?tqdm(test.items()):item_rank?=?model.recommend(user_id)if?item_id?in?item_rank:hit?+=?1ndcg?+=?1?/?np.log2(item_rank.index(item_id)?+?2)return?hit?/?len(test),?ndcg?/?len(test)實驗
對于ItemCF算法,超參數:
sim_item_topK = 20;
topN = 20;
使用自己的筆記本,結果如下:
數據讀取:2s;
模型建立(計算相似度矩陣):3min52s;
模型評估:19min47s,HR = 0.065232, NDCG = 0.024265;
總結
之前參考《推薦算法實踐》進行過復現,然后這次結合了Datawhale中新聞推薦的Baseline進行優化,確實對算法中如何懲罰熱門物品與活躍用戶有了更深的認識。所有代碼在github “https://github.com/ZiyaoGeng/SimpleCF”中,如果存在一些Bug或者更好的優化(評估時間太長),歡迎提出Issues。
往期精彩回顧適合初學者入門人工智能的路線及資料下載機器學習及深度學習筆記等資料打印機器學習在線手冊深度學習筆記專輯《統計學習方法》的代碼復現專輯 AI基礎下載機器學習的數學基礎專輯 本站知識星球“黃博的機器學習圈子”(92416895) 本站qq群704220115。 加入微信群請掃碼:總結
以上是生活随笔為你收集整理的【推荐系统】手写ItemCF/UserCF代码,你会吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: win7窗口颜色没有透明的开启教程
- 下一篇: Win11系统如何刷新按钮