推荐系统入门(七):新闻推荐实践2(附代码)
推荐系统入门(七):新闻推荐实践2(附代码)
目录
- 推荐系统入门(七):新闻推荐实践2(附代码)
- 引言
- 数据收集
- 数据存储
- 数据分析
- 实战
- 总结
- 参考资料
引言
相关系列笔记:
推荐系统入门(一):概述
推荐系统入门(二):协同过滤(附代码)
推荐系统入门(三):矩阵分解MF&因子分解机FM(附代码)
推荐系统入门(四):Wide&Deep(附代码)
推荐系统入门(五):GBDT+LR(附代码)
推荐系统入门(六):新闻推荐实践1(附代码)
推荐系统入门(七):新闻推荐实践2(附代码)
推荐系统入门(八):新闻推荐实践3(附代码)
推荐系统入门(九):新闻推荐实践4(附代码)
推荐系统入门(十):新闻推荐实践5(附代码)
推荐系统一般有三类数据源,一类来自用户的基本信息,一类来自物品的基本信息,还有一类是用户对物品的行为数据。
- 要推荐物品或内容的元数据,例如关键字,分类标签,基因描述等;
- 系统用户的基本信息,例如性别,年龄,兴趣标签等
- 用户的行为数据,可以转化为对物品或者信息的偏好,根据应用本身的不同,可能包括用户对物品的评分,用户查看物品的记录,用户的购买记录等。这些用户的偏好信息可以分为两类:
(1)显式的用户反馈:这类是用户在网站上自然浏览或者使用网站以外,显式的提供反馈信息,例如用户对物品的评分,或者对物品的评论。
(2)隐式的用户反馈:这类是用户在使用网站是产生的数据,隐式的反应了用户对物品的喜好,例如用户购买了某物品,用户查看了某物品的信息等等。
一个典型的推荐系统,处理数据通常会经历以下四步:即数据收集、数据存储、数据分析和数据过滤。
数据收集
实现推荐系统的第一步便是收集数据。这些数据可以是显性数据,也可以是隐性数据。显性数据就是指用户主动输入的数据,例如对内容的评论、点赞、转发、下载等,隐性数据是指用户的浏览历史、阅读时长、观看记录、搜索日志等。后台会为每一个使用该产品/访问该站点的用户创建一个数据集。
用户的行为数据很容易收集,通过站点上的用户行为日志就能获取。如果用户已经在使用APP,获取用户的行为数据就不需要用户的额外操作。但这种方法有一个缺点,获取的数据分析起来很麻烦。比如说,从用户的大量行为日志中过滤出真正需要的日志非常麻烦。
由于每个用户对产品的喜好不一,因此收集到的每位用户的数据集也截然不同。随着时间的推移,收集到的用户数据也越来越多,通过一系列数据分析,推荐的结果也会越来越精准。
数据存储
我们为推荐算法提供的数据越多,推荐的效果就会越精准。这也就意味着,任何推荐问题都可以转变为大数据分析问题来解决。
用于创建推荐结果的数据类型可帮助我们确定应使用的数据存储类型。我们可以选择使用NoSQL(Not Only SQL)数据库、标准SQL数据库,甚至是某种对象存储。根据不同的存储目的如获取用户输入/行为,以及操作的难易程度、存储的数量级、与其他环境的集成以及数据的可移植性等因素,选择合适的数据存储类型。
在保存用户评级或评论时,可扩展和可托管的数据库能够最大限度地减少所需的任务量,将注意力聚焦在推荐结果上。 Cloud SQL可以满足这两种需求,还可以直接从Spark上加载数据。
数据分析
为了获取类似用户参与度之类的数据,我们需要使用不同的分析方法过滤数据。如果想在用户浏览产品时即时给出推荐结果,那么需要更加灵活的数据分析方法。以下是分析数据的一些方法:
- 实时分析
可以在创建数据的同时对其进行处理。这种类型的系统通常包含可以处理和分析事件流的工具。要想给出用户实时的推荐结果,就要创建实时的数据分析系统。
- 批量分析
要求定期处理数据。采用这种方法,意味着要有足够的数据才能分析数据之间的相关性,例如每日阅读量、关注量等。推荐结果可能会通过邮件形式发送给用户。
- 近实时分析
每隔几分钟或几秒钟刷新一次后可以快速收集数据,进行分析。近实时系统最适合在一次浏览会话期间给出推荐结果。
- 数据过滤
下一步便是过滤数据,获取为用户提供推荐所需的相关数据。我们得先从上面的算法中选择一种更合适的算法。比如说:
基于内容的过滤:推荐的产品具有与目标用户喜欢的产品类似的特征;
聚类:对目标用户进行分类,处于某一个簇中的用户会被当成一个整体来对待;
协同过滤:目标用户喜欢其他用户喜欢的某一产品,那他也可能喜欢其他用户喜欢的其他产品。
协同过滤能够使产品属性理论化,并根据用户的口味进行预测。协同过滤的假设基础是:如果一位用户喜欢另一位用户喜欢的某一个产品,那么现在或 将来他也有可能喜欢这位用户喜欢的另一个产品。
我们将产品和用户作为两个不同维度,把有关评级或互动的数据表示为一组矩阵。假设以下两个矩阵相似,如果用数字1代替现有的评级,用0补上缺少的评级,就能得到第二个矩阵。生成的矩阵为真值表,其中数字表示用户与产品之间的交互。
我们可以使用K-Nearest算法、Jaccard系数、Dijkstra算法、余弦相似性来更好地关联用户数据集,以便根据评级或产品特征进行推荐。
实战
# 导入相关包
import pandas as pd
import numpy as npimport matplotlib.pyplot as plt
import seaborn as sns
plt.rc('font', family='SimHei', size=13)import os,gc,re,warnings,sys
from sklearn.preprocessing import MinMaxScalerwarnings.filterwarnings("ignore")
# 读取数据
path = './data/'# train
trn_click = pd.read_csv(path+'train_click_log.csv')
item_df = pd.read_csv(path+'articles.csv')
item_df = item_df.rename(columns={'article_id': 'click_article_id'}) #重命名,方便后续match
item_emb_df = pd.read_csv(path+'articles_emb.csv')# test
tst_click = pd.read_csv(path+'testA_click_log.csv')
# 数据预处理
# 计算用户点击rank和点击次数
# 对每个用户的点击时间戳进行排序
trn_click['rank'] = trn_click.groupby(['user_id'])['click_timestamp'].rank(ascending=False).astype(int)
tst_click['rank'] = tst_click.groupby(['user_id'])['click_timestamp'].rank(ascending=False).astype(int)
# 计算用户点击文章的次数,并添加新的一列count
trn_click['click_cnts'] = trn_click.groupby(['user_id'])['click_timestamp'].transform('count')
tst_click['click_cnts'] = tst_click.groupby(['user_id'])['click_timestamp'].transform('count')# 用户点击日志文件_训练集
trn_click = trn_click.merge(item_df, how='left', on=['click_article_id'])
print(trn_click.head())
# 用户点击日志信息
print(trn_click.info())
print(trn_click.describe())# 训练集中的用户数量为20w
print(trn_click.user_id.nunique())
trn_click.groupby('user_id')['click_article_id'].count().min() # 训练集里面每个用户至少点击了两篇文章# 画直方图大体看一下基本的属性分布
plt.figure()
plt.figure(figsize=(15, 20))
i = 1
for col in ['click_article_id', 'click_timestamp', 'click_environment', 'click_deviceGroup', 'click_os', 'click_country','click_region', 'click_referrer_type', 'rank', 'click_cnts']:plot_envs = plt.subplot(5, 2, i)i += 1v = trn_click[col].value_counts().reset_index()[:10]fig = sns.barplot(x=v['index'], y=v[col])for item in fig.get_xticklabels():item.set_rotation(90)plt.title(col)
plt.tight_layout()
plt.show()# 测试集用户点击日志
tst_click = tst_click.merge(item_df, how='left', on=['click_article_id'])
print(tst_click.head())
print(tst_click.describe())# 测试集中的用户数量为5w
print(tst_click.user_id.nunique())
tst_click.groupby('user_id')['click_article_id'].count().min() # 注意测试集里面有只点击过一次文章的用户# 新闻文章数据集浏览
print(item_df.head().append(item_df.tail()))
print(item_df['words_count'].value_counts())
print(item_df['category_id'].nunique()) # 461个文章主题
item_df['category_id'].hist()
print(item_df.shape) # 364047篇文章# 新闻文章embedding向量表示
print(item_emb_df.head())
print(item_emb_df.shape)# 数据分析
# 用户重复点击
# merge
user_click_merge = trn_click.append(tst_click)
# 用户重复点击
user_click_count = user_click_merge.groupby(['user_id', 'click_article_id'])['click_timestamp'].agg({'count'}).reset_index()
print(user_click_count[:10])
print(user_click_count[user_click_count['count'] > 7])
print(user_click_count['count'].unique())# # 用户点击新闻次数
# print(user_click_count.loc[: 'count'].value_counts())# 用户点击环境变化分析
def plot_envs(df, cols, r, c):plt.figure()plt.figure(figsize=(10, 5))i = 1for col in cols:plt.subplot(r, c, i)i += 1v = df[col].value_counts().reset_index()fig = sns.barplot(x=v['index'], y=v[col])for item in fig.get_xticklabels():item.set_rotation(90)plt.title(col)plt.tight_layout()plt.show()
# 分析用户点击环境变化是否明显,这里随机采样10个用户分析这些用户的点击环境分布
sample_user_ids = np.random.choice(tst_click['user_id'].unique(), size=5, replace=False)
sample_users = user_click_merge[user_click_merge['user_id'].isin(sample_user_ids)]
cols = ['click_environment','click_deviceGroup', 'click_os', 'click_country', 'click_region','click_referrer_type']
for _, user_df in sample_users.groupby('user_id'):plot_envs(user_df, cols, 2, 3)# 用户点击新闻数量的分布
user_click_item_count = sorted(user_click_merge.groupby('user_id')['click_article_id'].count(), reverse=True)
plt.plot(user_click_item_count)
plt.show()
# 点击次数在前50的用户
plt.plot(user_click_item_count[:50])
plt.show()
# 点击次数排名在[25000:50000]之间
plt.plot(user_click_item_count[25000:50000])
plt.show()
# 新闻点击次数分析
item_click_count = sorted(user_click_merge.groupby('click_article_id')['user_id'].count(), reverse=True)
plt.plot(item_click_count)
plt.show()
plt.plot(item_click_count[:100])
plt.show()
plt.plot(item_click_count[:20])
plt.show()
plt.plot(item_click_count[3500:])
plt.show()# 新闻共现频次:两篇新闻连续出现的次数
tmp = user_click_merge.sort_values('click_timestamp')
tmp['next_item'] = tmp.groupby(['user_id'])['click_article_id'].transform(lambda x:x.shift(-1))
union_item = tmp.groupby(['click_article_id', 'next_item'])['click_timestamp'].agg({'count'}).reset_index().sort_values('count', ascending=False)
print(union_item[['count']].describe())
# 画个图直观地看一看
x = union_item['click_article_id']
y = union_item['count']
plt.scatter(x, y)
plt.show()
plt.plot(union_item['count'].values[40000:])
plt.show()
# 不同类型的新闻出现的次数
plt.plot(user_click_merge['category_id'].value_counts().values)
plt.show()
# 出现次数比较少的新闻类型, 有些新闻类型,基本上就出现过几次
plt.plot(user_click_merge['category_id'].value_counts().values[150:])
plt.show()
# 新闻字数的描述性统计
user_click_merge['words_count'].describe()
plt.plot(user_click_merge['words_count'].values)
plt.show()# 用户点击的新闻类型的偏好
# 此特征可以用于度量用户的兴趣是否广泛。
plt.plot(sorted(user_click_merge.groupby('user_id')['category_id'].nunique(), reverse=True))
plt.show()
print(user_click_merge.groupby('user_id')['category_id'].nunique().reset_index().describe())# 用户查看文章的长度的分布
# 通过统计不同用户点击新闻的平均字数,这个可以反映用户是对长文更感兴趣还是对短文更感兴趣。
plt.plot(sorted(user_click_merge.groupby('user_id')['words_count'].mean(), reverse=True))
plt.show()
# 挑出大多数人的区间仔细看看
plt.plot(sorted(user_click_merge.groupby('user_id')['words_count'].mean(), reverse=True)[1000:45000])
plt.show()# 更加详细的参数
print(user_click_merge.groupby('user_id')['words_count'].mean().reset_index().describe())# 为了更好的可视化,这里把时间进行归一化操作
mm = MinMaxScaler()
user_click_merge['click_timestamp'] = mm.fit_transform(user_click_merge[['click_timestamp']])
user_click_merge['created_at_ts'] = mm.fit_transform(user_click_merge[['created_at_ts']])user_click_merge = user_click_merge.sort_values('click_timestamp')
print(user_click_merge.head())def mean_diff_time_func(df, col):df = pd.DataFrame(df, columns={col})df['time_shift1'] = df[col].shift(1).fillna(0)df['diff_time'] = abs(df[col] - df['time_shift1'])return df['diff_time'].mean()
# 点击时间差的平均值
mean_diff_click_time = user_click_merge.groupby('user_id')['click_timestamp', 'created_at_ts'].apply(lambda x: mean_diff_time_func(x, 'click_timestamp'))
plt.plot(sorted(mean_diff_click_time.values, reverse=True))
plt.show()# 前后点击文章的创建时间差的平均值
mean_diff_created_time = user_click_merge.groupby('user_id')['click_timestamp', 'created_at_ts'].apply(lambda x: mean_diff_time_func(x, 'created_at_ts'))
plt.plot(sorted(mean_diff_created_time.values, reverse=True))
plt.show()# 用户前后点击文章的相似性分布
item_idx_2_rawid_dict = dict(zip(item_emb_df['article_id'], item_emb_df.index))
del item_emb_df['article_id']
item_emb_np = np.ascontiguousarray(item_emb_df.values, dtype=np.float32)
# 随机选择5个用户,查看这些用户前后查看文章的相似性
sub_user_ids = np.random.choice(user_click_merge.user_id.unique(), size=15, replace=False)
sub_user_info = user_click_merge[user_click_merge['user_id'].isin(sub_user_ids)]
print(sub_user_info.head())def get_item_sim_list(df):sim_list = []item_list = df['click_article_id'].valuesfor i in range(0, len(item_list)-1):emb1 = item_emb_np[item_idx_2_rawid_dict[item_list[i]]]emb2 = item_emb_np[item_idx_2_rawid_dict[item_list[i+1]]]sim_list.append(np.dot(emb1,emb2)/(np.linalg.norm(emb1)*(np.linalg.norm(emb2))))sim_list.append(0)return sim_list
for _, user_df in sub_user_info.groupby('user_id'):item_sim_list = get_item_sim_list(user_df)plt.plot(item_sim_list)
plt.show()
结果:
用户点击日志信息基本的属性分布:
随机采样10个用户分析点击的环境分布:
用户点击新闻数量的分布:
用户点击文章次数看用户的活跃度:
点击次数排名在[25000:50000]之间:
新闻点击次数分析:
新闻共现频次:
不同类型的新闻出现的次数:
新闻字数的描述性统计:
用户点击的新闻类型的偏好:
用户查看文章的长度分布:
点击时间差的平均值:
前后点击文章创建时间差的平均值:
用户前后点击文章的相似性分布:
总结
通过数据分析的过程, 我们目前可以得到以下几点重要的信息, 这个对于我们进行后面的特征制作和分析非常有帮助:
- 训练集和测试集的用户id没有重复,也就是测试集里面的用户模型是没有见过的
- 训练集中用户最少的点击文章数是2, 而测试集里面用户最少的点击文章数是1
- 用户对于文章存在重复点击的情况, 但这个都存在于训练集里面
- 同一用户的点击环境存在不唯一的情况,后面做这部分特征的时候可以采用统计特征
- 用户点击文章的次数有很大的区分度,后面可以根据这个制作衡量用户活跃度的特征
- 文章被用户点击的次数也有很大的区分度,后面可以根据这个制作衡量文章热度的特征
- 用户看的新闻,相关性是比较强的,所以往往我们判断用户是否对某篇文章感兴趣的时候, 在很大程度上会和他历史点击过的文章有关
- 用户点击的文章字数有比较大的区别, 这个可以反映用户对于文章字数的区别
- 用户点击过的文章主题也有很大的区别, 这个可以反映用户的主题偏好
- 不同用户点击文章的时间差也会有所区别, 这个可以反映用户对于文章时效性的偏好
所以根据上面的一些分析,可以更好的帮助我们后面做好特征工程, 充分挖掘数据的隐含信息。
参考资料
- 零基础入门推荐系统 - 新闻推荐
- 天池新闻推荐入门赛之【数据分析】Task02
- https://zhuanlan.zhihu.com/p/78841394
推荐系统入门(七):新闻推荐实践2(附代码)相关推荐
- 推荐系统入门(六):新闻推荐实践1(附代码)
推荐系统入门(六):新闻推荐实践1 目录 推荐系统入门(六):新闻推荐实践1 前言 赛题简介 数据概况 评价方式理解 Baseline 总结 参考资料 前言 相关系列笔记: 推荐系统入门(一):概述 ...
- 推荐系统入门(十):新闻推荐实践5(附代码)
推荐系统入门(十):新闻推荐实践5(附代码) 目录 推荐系统入门(十):新闻推荐实践5(附代码) 前言 LGB模型 DIN模型 一.排序模型 1.LGB排序模型 2.LGB分类模型 3.DIN模型 二 ...
- 《最新开源 随插即用》SAM 自增强注意力深度解读与实践(附代码及分析)
写在前面 大家好,我是cv君,前段时间忙碌工作,许久没更新,越发觉得对不起csdn的读者们,决定继续加油保持更新,保持一周2-3篇的高频率和高质量文章更新:论文分析.代码讲解.代码实操和训练.优化部署 ...
- 【干货】基于内容理解的新闻推荐.pdf(附下载链接)
今天给大家带来SmartNews AI算法负责人,前Facebook AI工程经理周涵宁先生在2021年全球机器学习技术大会上所做的分享<基于内容理解的新闻推荐.pdf>,关注知识图谱及新 ...
- 基于深度学习的个性化新闻推荐.pdf(附下载链接)
今天给大家带来微软亚洲研究院研究院吴方照先生在2020年5月10日举办的"推荐系统前沿进展"系列学术沙龙活动中所做的分享<基于深度学习的个性化新闻推荐>,在本次分享中, ...
- 机器学习三人行(系列七)----支持向量机实践指南(附代码)
原文链接:阅读原文 欢迎大家关注微信公众号"智能算法",我们一起学习,共同进步. 如需前面系列文章,请在公众号回复" 机器学习 "进行查看! 通过对系列六的学习 ...
- 基于大数据个性化音乐推荐算法分析(附代码github地址)
github网址:https://github.com/ciecus/music_lgb_recommend_kkbox 欢迎fork我,和我讨论呀~ 摘 要:音乐推荐算法针对当今时代信息过载的问题 ...
- 【NLP文本分类算法集锦】零基础入门经典文本分类项目实战(附代码+数据集)
前言 大家好,我是阿光. 本专栏整理了<NLP文本分类算法集锦>,内包含了各种常见的中英文文本分类算法,以及常见的NLP任务:情感分析.新闻分类以及谣言检测等. 文本分类是NLP的必备入门 ...
- 独家 | 从零开始用python搭建推荐引擎(附代码)
作者:Pulkit Sharma 翻译:申利彬 校对:付宇帅 本文约10300字,建议阅读10分钟. 本文介绍了各种推荐引擎算法以及使用Python构建它们的基本框架. 简介 当今社会的每个人都面临着 ...
最新文章
- TensorFlow tfjs 0.10.3 发布
- 如何给Docker镜像瘦身?
- 漫画:什么是公有云、私有云和混合云?
- mysql5.6怎么配置_MySQL 5.6和5.7怎么进行最优配置的方法
- python爬虫接单经历_一个Python小白5个小时爬虫经历
- python opencv 画米字形状
- python大神-Python代码怎么写,听听顶尖Python大神的建议
- c语言命令行选项处理函数getopt和getopt_long() 函数使用
- 推荐系统的发展与简单回顾
- Tensorflow安装问题解决(Anoconda)
- 无人驾驶五 使用pure pursuit实现无人车轨迹追踪(python)
- 萌豆工作室《用户许可协议》
- Audacity Mac版(音频录制编辑合成工具)中文版
- 调用阿里云语音识别接口
- 手写汉字识别数据集的预处理
- 深入浅出Yolo系列之Yolox核心基础完整讲解
- Java 飞翔的小鸟小游戏开发 完全源码 + 论文文档
- 【华为云技术分享】快速搭建网站之云速建站
- 天猫为海澜之家打造“智慧门店”;东方网力联手电子科技大学,共建人工智能联合实验室...
- 大数据在各领域应用之精准营销