顯示/隐式回報
類似視訊的評分、商品的評分這些為顯示回報;浏覽日志、購買日志這些為隐式回報。
使用者行為的統一表示方法
userId、itemId、behaviorType、context(上下文,時間地點等)、behaviorWeight、behaviorContent
常見資料集種類
- 無上下文的隐式回報資料集:僅記錄使用者和物品的ID
- 無上下文的顯示回報資料集:記錄物品和使用者的ID以及評分
- 有上下文的隐示回報資料集:記錄使用者和物品的ID以及物品産生行為的時間戳
- 有上下文的顯示回報資料集:記錄使用者和物品的ID以及評分和時間戳
評價名額
- 召回率:正确推薦的數目/總共喜歡的數目
- 準确率:正确推薦的數目/推薦出來的數目
- 覆寫率:推薦的種類/總類數
- 流行度:顧名思義
- AUC:正确的樣本排在錯誤樣本前面的機率
基于使用者的協同過濾算法(UserCF)
- 給使用者推薦和他興趣相似的其他使用者喜歡的物品,比較社會化。适用于時效性較強的領域,如新聞推薦。
- 步驟:(1)找到和目标使用者興趣類似的使用者集合。(2)找到這個集合中使用者喜歡的,且目标使用者還沒有聽說過的物品推薦給目标使用者。
- 使用者相似度計算:(1)Jaccard公式/餘弦相似度,适用于使用者較少的情況。(2)物品-使用者倒排表,一個二維矩陣,行列都是使用者ID,以物品為紐帶,如果兩個使用者之間有關聯則在矩陣中加一。C[u][v] += 1, W[u][v] = Cuv / math.sqrt(N[u] * N[v])
- 對推薦結果有較大影響的因素:推薦的數目。
- 算法的改進:(1)User-IIF,減少流行物品對相似度的影響。C[u][v] += 1 / math.log(1 + len(user)), W[u][v] = Cuv / math.sqrt(N[u] * N[v])
基于物品的協同過濾算法(ItemCF)
- 給使用者推薦和他之前喜歡物品類似的物品,比較個性化。适用于個性化需求較強烈的領域,如一些購物平台。
- 步驟:(1)計算物品之間的相似度。(2)根據物品的相似度和使用者的曆史行為給使用者生成推薦清單。
-
物品相似度計算:使用者-物品倒排表(對每個使用者建立一個包含他喜歡的物品清單),然後對于每個使用者将它物品清單中的物品兩兩在共現矩陣C加一。
C[i][j] += 1, W[i][j] = Cij / math.sqrt( N[i] * N[j])
-
算法優化:(1)User-IUF,活躍使用者對物品相似度的貢獻應該小于不活躍使用者。
C[i][j] += 1 / math.log(1 + len(item) * 1.0 ), W[i][j] = Cij / math.sqrt( N[i] * N[j])。
(2)User-Norm,歸一化相似度矩陣。
UserCF與ItemCF的優缺點對比
UserCF | ItemCF | |
---|---|---|
性能 | 适用于使用者較少的場合 | 适用于物品數目明顯小于使用者數的場合 |
領域 | 時效性較強,個性化興趣不太明顯 | 長尾物品豐富 |
實時性 | 使用者有新行為不一定導緻推薦結果立即變化 | 使用者有新行為一定會導緻推薦結果實時變化 |
冷啟動 | 在新使用者對很少物品産生行為後不能立即進行個性化推薦,因為使用者相似度是每隔一段時間離線計算的;新物品上線一段時間後,一旦有使用者對物品産生行為就可以将它推給其他人 | 新使用者隻要對一個物品産生行為就可以給他推薦相關物品;沒有辦法在不離線更新物品相似度表的情況下将新物品退給使用者 |
推薦理由 | 難以提供 | 可以解釋 |
核心代碼(基于ml-100k資料集)
計算相似度矩陣
def ItemCF_Norm(self):
# 建立物品-物品的共現矩陣
C = dict() # 物品-物品的共現矩陣
N = dict() # 物品被多少個不同使用者購買
for user, items in self.train.items():
for i in items.keys():
N.setdefault(i, 0)
N[i] += 1
C.setdefault(i, {})
for j in items.keys():
if i == j: continue
C[i].setdefault(j, 0)
C[i][j] += 1
#C[i][j] += 1 / math.log(1 + len(items) * 1.0) # IUF
# 計算相似度矩陣
self.W = dict()
for i, related_items in C.items():
self.W.setdefault(i, {})
for j, cij in related_items.items():
self.W[i][j] = cij / (math.sqrt(N[i] * N[j]))
# 歸一化
for item1, dic in self.W.items():
max = sorted(self.W[item1].items(), key=lambda x: x[1], reverse=True)[0][1]
for item2, sim in dic.items():
self.W[item1][item2] = self.W[item1][item2] / max
self.SaveDict('data/itemcf_norm.data', self.W)
return self.W
帶理由的推薦
def Recommend(self, user, K=10, num_recommend=10, num_recent=5):
rank = dict()
watched_item = self.train[user] # 使用者user産生過行為的item和評分
recent_item = self.getRecent(user, num_recent) # 看過的并且喜歡(4分以上)的電影按時間排序
for movie, list in recent_item:
for related_movie, w in sorted(self.W[movie].items(), key=lambda x: x[1], reverse=True)[:K]:
if related_movie in watched_item.keys():
continue
rank.setdefault(related_movie, [0, movie])
rank[related_movie][0] += list[0] * w # list[0]是評分 list[1]是時間
return dict(sorted(rank.items(), key=lambda x: x[1], reverse=True)[0:num_recommend])
召回率與準确率的計算
def Recall(self, N=30):
hit = 0
all = 0
for user_id in self.train.keys():
if user_id in self.test.keys():
tu = self.test[user_id]
rank = self.Recommend(user_id, num_recommend=N)
for item_id, pui in rank.items():
if item_id in tu.keys():
hit += 1
all += len(tu)
return hit / (all * 1.0)
def Precision(self, N=30):
hit = 0
all = 0
for user_id in self.train.keys():
if user_id in self.test.keys():
tu = self.test[user_id]
rank = self.Recommend(user_id, num_recommend=N)
for item_id, pui in rank.items():
if item_id in tu.keys():
hit += 1
all += N
return hit / (all * 1.0)