前言
本文针对天池有关推荐系统的赛题进行理解及大致梳理,不当之处还望指正一、赛题简介和数据概况:
以新闻APP中的新闻推荐为背景, 目的是要求我们根据用户历史浏览点击新闻文章的数据信息预测用户未来的点击行为, 即用户的最后一次点击的新闻文章;
数据包括30万用户,近300万次点击,共36万多篇不同的新闻文章,同时每篇新闻文章有对应的embedding向量表示。从中抽取20万用户的点击日志数据作为训练集,5万用户的点击日志数据作为测试集A,5万用户的点击日志数据作为测试集B。
二、对赛题的理解
1.明确目标:
根据“用户历史浏览记录”预测用户最后一次可能点击的文章
2.疑问:
那么应该怎么利用“用户——物品(新闻)”或者”物品(新闻)——用户“这样的信息进行机器学习?
我们知道,机器学习分为监督式和非监督式,它们最大的区别就是是否有标签,而根据数据概况了解到,每篇文章都有对应的embeding向量,所以这里是不是可以直接往监督学习方向思考的原因?那假设已经确定了大方向监督学习,就要往更细分的方向思考:分类 or 回归?
回归题目:预测用户未来的点击行为, 即用户的最后一次(可能)点击的新闻文章
这里我加了”可能“两个字,有一点理解想指出,我们给出的推荐是一个预测的列表,例如:[article1,article2,…,articlen],是可以指定Top-N的,那么,
(1)列表的内容就是可能点击的某些文章的集合(并且是在现有历史记录里找的)
(2)“可能”一词令我们自然而然的联想到概率,所以在监督学习中分类问题是涉及概率的,而概率大小又区分可能性的程度
(3)那推荐列表里就应该有依据可能性程度不同来排列文章的先后顺序
至此,是我理解的为什么解决该问题场景是选择监督式学习而不是非监督学习;选择分类模型而不是回归模型的思路
三、对Baseline的探索
1.分析历史数据:
查看用户点击历史数据,能够看到有user_id、click_article_id、click_timestamp、click_environment、click_deviceGroup、click_os、click_country、click_region、click_referrer_type七个特征
根据目标,有些特征是对分析没有什么大作用的所以一般剔除掉一些不需要用到的特征或者抽取出我们要的特征。baseline只涉及user_id(用户)、click_article_id(文章)、click_timestamp(时间)三个特征,基于协同过滤算法抽取特征user_id、click_article_id无异议,那为什么要特征click_timestamp呢?
分享一个可以说服我的解释:用户的兴趣爱好会随着时间而变化,用户的兴趣爱好会随着个人的成长经历及阅历发生显著的变化,例如小时候喜欢看动画片,上大学了喜欢看科幻片。时间算是一个上下文信息。
像理解一篇文章一样要基于上下文,如果能够给推荐系统更多上下文信息的学习,那推荐效果可以更好。所以我在想特征click_environment能不能也成为一个上下文信息,如果该推荐不局限于新闻,而是商品,例如用户在外旅游时,推荐系统应该给用户推荐他所在旅游地的美食或者宾馆,而非其他地点的相关推荐。同理,那用户大概也会对离自己较近地方所发生的事更感兴趣更想了解?
以上只是我个人疑问,以下Baseline基于user_id、click_article_id、click_timestamp进行
2.Baseline:采用基于物品的协同过滤
这里再提一下基于物品的协同过滤思想:ItemCF算法并不利用物品的内容属性计算物品之间的相似度, 主要通过分析用户的行为记录计算物品之间的相似度, 该算法认为, 物品a和物品c具有很大的相似度是因为喜欢物品a的用户大都喜欢物品c。
主要步骤:
(1)将关于用户、文章、时间的数据处理为{user1: {item1: time1, item2: time2…}…}
(2)利用基于物品的协同过滤算法计算相关性矩阵
(3)挑出相关性最强的N篇文章进行召回
(4)给每个用户推荐基于物品的协同过滤算法且该用户从未浏览的文章列表
计算物品相关性矩阵主要代码:
#导入需使用的包(此处省略)
user_item_time_dict = get_user_item_time(df)
# 计算物品相似度
i2i_sim = {}
item_cnt = defaultdict(int)
for user, item_time_list in tqdm(user_item_time_dict.items()):#抽取每个用户及其浏览过的文章
# 在基于商品的协同过滤优化的时候可以考虑时间因素(原因上述已阐述)
for i, i_click_time in item_time_list: #抽取浏览了XX文章,还浏览了哪些文章
item_cnt[i] += 1
i2i_sim.setdefault(i, {})
for j, j_click_time in item_time_list:
if(i == j):
continue
i2i_sim[i].setdefault(j, 0)
i2i_sim[i][j] += 1 / math.log(len(item_time_list) + 1)
#print(i2i_sim)
i2i_sim_ = i2i_sim.copy()#计算相关性
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])
召回文章主要代码:
"""
基于文章协同过滤的召回
:param user_id: 用户id
:param user_item_time_dict: 字典, 根据点击时间获取用户的点击文章序列 {user1: {item1: time1, item2: time2..}...}
:param i2i_sim: 字典,文章相似性矩阵
:param sim_item_topk: 整数, 选择与当前文章最相似的前k篇文章
:param recall_item_num: 整数, 最后的召回文章数量
:param item_topk_click: 列表,点击次数最多的文章列表,用户召回补全
return: 召回的文章列表 {item1:score1, item2: score2...}
注意: 基于物品的协同过滤(详细请参考上一期推荐系统基础的组队学习), 在多路召回部分会加上关联规则的召回策略
"""
# 获取用户历史交互的文章
user_hist_items = user_item_time_dict[user_id]
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
item_rank.setdefault(j, 0)
item_rank[j] += 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]
给用户推荐N篇新闻主要代码:
# 定义
user_recall_items_dict = defaultdict(dict)
# 获取 用户 - 文章 - 点击时间的字典
user_item_time_dict = get_user_item_time(all_click_df)
# 去取文章相似度
i2i_sim = pickle.load(open(save_path + 'itemcf_i2i_sim.pkl', 'rb'))
# 相似文章的数量
sim_item_topk = 10
# 召回文章数量
recall_item_num = 10
# 用户热度补全
item_topk_click = get_item_topk_click(all_click_df, k=50)
for user in tqdm(all_click_df['user_id'].unique()):
user_recall_items_dict[user] = item_based_recommend(user, user_item_time_dict, i2i_sim,
sim_item_topk, recall_item_num, item_topk_click)
四、总结
新闻推荐是推荐场景下一件实时性相对较强的应用,所以除了使用基于物品的协同过滤,还可以使用基于用户的协同过滤,使用方法也大同小异。但据了解,User CF应该会更适合一些,因为用户都比较趋向于关注当前流行热点的场景,而由于新闻更新很快,如果维护物品之间的相似度矩阵该矩阵需要随着新闻的更新而重新计算,代价太大。并且,如果新闻推荐能够像电商平台商用推荐系统一样利用好“上下文”(其他有价值的特征如地点、心情等),不知可否取得更好的推荐效果,有待考究。
五、参考
[1]Datawhale组队学习推荐系统基础_02协同过滤
[2]Datawhale组队学习新闻推荐_赛题理解+Baseline