item_topk_click = get_item_topk_click(trn_hist_click_df, k=50) # 获取近期点击最多的文章
user_item_time_dict = get_user_item_time(trn_hist_click_df) # 根据点击时间获取用户的点击文章序列 {user1: [(item1, time1), (item2, time2)..]...}
i2i_sim_cf = item_cf_sim(all_click_df, item_created_time_dict) # 文章与文章之间的基于“共有用户”的相似性矩阵计算
i2i_sim_emb = item_embdding_sim(all_click_df, item_emb_df, topk=50) # 文章与文章之间的基于“向量检索”的相似度计算
# 基于商品的召回i2i
def item_based_recommend(user_id, user_item_time_dict, i2i_sim, i2i_sim_emb, sim_item_topk, recall_item_num, item_topk_click, item_created_time_dict):
"""
基于文章协同过滤的召回
:param user_id: 用户id
:param user_item_time_dict: 字典, 根据点击时间获取用户的点击文章序列 {user1: [(item1, time1), (item2, time2)..]...}
:param i2i_sim: 字典,文章相似性矩阵
:param i2i_sim_emb: 字典,基于内容embedding算的文章相似矩阵
:param sim_item_topk: 整数, 选择与当前文章最相似的前k篇文章
:param recall_item_num: 整数, 最后的召回文章数量
:param item_topk_click: 列表,点击次数最多的文章列表,用户召回补全
:param item_created_time_dict: 文章创建时间列表
return: 召回的文章列表 [(item1, score1), (item2, score2)...]
"""
# 获取用户历史交互的文章
user_hist_items = user_item_time_dict[user_id]
user_hist_items_ = {user_id for user_id, _ in user_hist_items}
item_rank = {}
for loc, (i, click_time) in enumerate(user_hist_items):
for j, wij in sorted(i2i_sim[i].items(), key=lambda x: x[1], reverse=True)[:sim_item_topk]:
if j in user_hist_items_:
continue
# 文章创建时间差权重
created_time_weight = np.exp(0.8 ** np.abs(item_created_time_dict[i] - item_created_time_dict[j]))
# 相似文章和历史点击文章序列中历史文章所在的位置权重
loc_weight = (0.9 ** (len(user_hist_items) - loc))
content_weight = 1.0
if i2i_sim_emb.get(i, {}).get(j, None) is not None:
content_weight += i2i_sim_emb[i][j]
if i2i_sim_emb.get(j, {}).get(i, None) is not None:
content_weight += i2i_sim_emb[j][i]
item_rank.setdefault(j, 0)
item_rank[j] += created_time_weight * loc_weight * content_weight * wij
# 不足10个,用热门商品补全
if len(item_rank) < recall_item_num:
for i, item in enumerate(item_topk_click):
if item in item_rank.items(): # 填充的item应该不在原来的列表中
continue
item_rank[item] = - i - 100 # 随便给个负数就行
if len(item_rank) == recall_item_num:
break
item_rank = sorted(item_rank.items(), key=lambda x: x[1], reverse=True)[:recall_item_num]
return item_rank