推荐系统入门(六):新闻推荐实践1

目录

  • 推荐系统入门(六):新闻推荐实践1
    • 前言
    • 赛题简介
    • 数据概况
    • 评价方式理解
    • Baseline
    • 总结
    • 参考资料

前言

相关系列笔记:
推荐系统入门(一):概述
推荐系统入门(二):协同过滤(附代码)
推荐系统入门(三):矩阵分解MF&因子分解机FM(附代码)
推荐系统入门(四):Wide&Deep(附代码)
推荐系统入门(五):GBDT+LR(附代码)
推荐系统入门(六):新闻推荐实践1(附代码)
推荐系统入门(七):新闻推荐实践2(附代码)
推荐系统入门(八):新闻推荐实践3(附代码)
推荐系统入门(九):新闻推荐实践4(附代码)
推荐系统入门(十):新闻推荐实践5(附代码)

  移动联通互联网、人工智能等技术的迅速发展为人们的工作生活带来了很多便利,但是同时也带来了信息过载问题。搜索引擎和推荐系统是解决信息过载的代表技术传统的搜索引擎在本质上来讲是帮助用户过滤和筛选信息,这种方式满足了大多数人的需求,但没有提供个性化的服务。相对于传统搜索引擎来说,推荐系统可以兼顾个性化需求和解决信息过载问题推荐系统是信息过滤系统的一个子集,目的在于根据用户的喜好、习惯、个性化需求以及商品的特性来预测用户对商品的喜好,为用户推荐最合适的商品,帮助用户快速地做出决策,提高用户满意度。推荐系统的价值在于能够提供尽量合适的选择或者是推荐而不需要用户明确提供他们所想要的内容。随着大数据时代的到来,传统推荐系统在挖掘数据价值上存在的问题正在限制其性能发挥。知识图谱的出现为大数据环境下的推荐系统设计提供了一种有效途径。
  目前,推荐系统已经成为产业界和学术界关注、研究的热点问题,应用领域十分广泛,在电子商务、社交网络、视频音乐推荐等领域都有所应用。例如亚马逊网站、京东、淘宝网站为用户推荐商品,MovieLens推荐电影的功能等。

赛题简介

  此次比赛是新闻推荐场景下的用户行为预测挑战赛,该赛题是以新闻APP中的新闻推荐为背景,目的是要求我们根据用户历史浏览点击新闻文章的数据信息预测用户未来的点击行为,即用户的最后一次点击的新闻文章,这道赛题的设计初衷是引导大家了解推荐系统中的一些业务背景,解决实际问题。

数据概况

  该数据来自某新闻APP平台的用户交互数据,包括30万用户,近300万次点击,共36万多篇不同的新闻文章,同时每篇新闻文章有对应的embedding向量表示。为了保证比赛的公平性,从中抽取20万用户的点击日志数据作为训练集,5万用户的点击日志数据作为测试集A,5万用户的点击日志数据作为测试集B。

  数据表

   train_click_log.csv:训练集用户点击日志
   testA_click_log.csv:测试集用户点击日志
   articles.csv:新闻文章信息数据表
   articles_emb.csv:新闻文章embedding向量表示
   sample_submit.csv:提交样例文件

  字段表

Field Description
user_id 用户id
click_article_id 点击文章id
click_timestamp 点击时间戳
click_environment 点击环境
click_deviceGroup 点击设备组
click_os 点击操作系统
click_country 点击城市
click_region 点击地区
click_referrer_type 点击来源类型
article_id 文章id,与click_article_id相对应
category_id 文章类型id
created_at_ts 文章创建时间戳
words_count 文章字数
emb_1,emb_2,…,emb_249 文章embedding向量表示

评价方式理解

  理解评价方式,我们需要结合着最后的提交文件来看,根据sample_submit.csv,我们最后提交的格式是针对每个用户,我们都会给出五篇文章的推荐结果,按照点击概率从前往后排序。而真实的每个用户最后一次点击的文章只会有一篇的真实答案,所以我们就看我们推荐的这五篇里面是否有命中真实答案的。比如对于user1来说,我们的提交会是:

user1,article1,article2,article3,article4,article5.

  评价指标的公式如下:


  假如article1就是真实的用户点击文章,也就是article1命中,则s(user1,1)=1,s(user1,2-4)都是0,如果article2是用户点击的文章,则s(user,2)=1/2,s(user,1,3,4,5)都是0。也就是score(user)=命中第几条的倒数。如果都没中,则score(user1)=0。这个是合理的,因为我们希望的就是命中的结果尽量靠前,而此时分数正好比较高。

Baseline

# 导包
# import packages
import time, math, osimport collections
from tqdm import tqdm
import gc
import pickle
import random
from datetime import datetime
from operator import itemgetter
import numpy as np
import pandas as pd
import warnings
from collections import defaultdictwarnings.filterwarnings('ignore')
data_path = './data/'
save_path = './result/'# df节省内存函数
# 节约内存的一个标配函数
def reduce_mem(df):starttime = time.time()numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']start_mem = df.memory_usage().sum() / 1024 ** 2for col in df.columns:col_type = df[col].dtypesif col_type in numerics:c_min = df[col].min()c_max = df[col].max()if pd.isnull(c_min) or pd.isnull(c_max):continueif str(col_type)[:3] == 'int':if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:df[col] = df[col].astype(np.int8)elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:df[col] = df[col].astype(np.int16)elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:df[col] = df[col].astype(np.int32)elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:df[col] = df[col].astype(np.int64)else:if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:df[col] = df[col].astype(np.float16)elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:df[col] = df[col].astype(np.float32)else:df[col] = df[col].astype(np.float64)end_mem = df.memory_usage().sum() / 1024 ** 2print('-- Mem. usage decreased to {:5.2f} Mb ({:.1f}% reduction),time spend:{:2.2f} min'.format(end_mem,100 * (start_mem - end_mem) / start_mem,(time.time() - starttime) / 60))return df# 读取采样或全量数据
# debug模式:从训练集中划出一部分数据来调试代码
def get_all_click_sample(data_path, sample_nums=10000):"""训练集中采样一部分数据调试data_path: 原数据的存储路径sample_nums: 采样数目(这里由于机器的内存限制,可以采样用户做)"""all_click = pd.read_csv(data_path + 'train_click_log.csv')all_user_ids = all_click.user_id.unique()sample_user_ids = np.random.choice(all_user_ids, size=sample_nums, replace=False)all_click = all_click[all_click['user_id'].isin(sample_user_ids)]all_click = all_click.drop_duplicates((['user_id', 'click_article_id', 'click_timestamp']))return all_click# 读取点击数据,这里分成线上和线下,如果是为了获取线上提交结果应该讲测试集中的点击数据合并到总的数据中
# 如果是为了线下验证模型的有效性或者特征的有效性,可以只使用训练集
def get_all_click_df(data_path='./data/', offline=True):if offline:all_click = pd.read_csv(data_path + 'train_click_log.csv')else:trn_click = pd.read_csv(data_path + 'train_click_log.csv')tst_click = pd.read_csv(data_path + 'testA_click_log.csv')all_click = trn_click.append(tst_click)all_click = all_click.drop_duplicates((['user_id', 'click_article_id', 'click_timestamp']))return all_click# 全量训练集
all_click_df = get_all_click_df(offline=False)# 获取
# 用户 - 文章 - 点击时间字典
# 根据点击时间获取用户的点击文章序列   {user1: [(item1: time1), (item2: time2)..]...}
def get_user_item_time(click_df):click_df = click_df.sort_values('click_timestamp')def make_item_time_pair(df):return list(zip(df['click_article_id'], df['click_timestamp']))user_item_time_df = click_df.groupby('user_id')['click_article_id', 'click_timestamp'].apply(lambda x: make_item_time_pair(x)) \.reset_index().rename(columns={0: 'item_time_list'})user_item_time_dict = dict(zip(user_item_time_df['user_id'], user_item_time_df['item_time_list']))return user_item_time_dict# 获取点击最多的Topk个文章
# 获取近期点击最多的文章
def get_item_topk_click(click_df, k):topk_click = click_df['click_article_id'].value_counts().index[:k]return topk_click# itemCF的物品相似度计算
def itemcf_sim(df):"""文章与文章之间的相似性矩阵计算:param df: 数据表:item_created_time_dict:  文章创建时间的字典return : 文章与文章的相似性矩阵思路: 基于物品的协同过滤(详细请参考上一期推荐系统基础的组队学习), 在多路召回部分会加上关联规则的召回策略"""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:item_cnt[i] += 1i2i_sim.setdefault(i, {})for j, j_click_time in item_time_list:if (i == j):continuei2i_sim[i].setdefault(j, 0)i2i_sim[i][j] += 1 / math.log(len(item_time_list) + 1)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])# 将得到的相似性矩阵保存到本地pickle.dump(i2i_sim_, open(save_path + 'itemcf_i2i_sim.pkl', 'wb'))return i2i_sim_i2i_sim = itemcf_sim(all_click_df)# itemCF的文章推荐
# 基于商品的召回i2i
def item_based_recommend(user_id, user_item_time_dict, i2i_sim, sim_item_topk, recall_item_num, item_topk_click):"""基于文章协同过滤的召回:param user_id: 用户id:param user_item_time_dict: 字典, 根据点击时间获取用户的点击文章序列[(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]  # 注意,此时获取得到的是一个元组列表,需要将里面的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_:continueitem_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应该不在原来的列表中continueitem_rank[item] = - i - 100  # 随便给个负数就行if len(item_rank) == recall_item_num:breakitem_rank = sorted(item_rank.items(), key=lambda x: x[1], reverse=True)[:recall_item_num]return item_rank# 给每个用户根据物品的协同过滤推荐文章
# 定义
user_recall_items_dict = collections.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)
# 召回字典转换成df
# 将字典的形式转换成df
user_item_score_list = []for user, items in tqdm(user_recall_items_dict.items()):for item, score in items:user_item_score_list.append([user, item, score])recall_df = pd.DataFrame(user_item_score_list, columns=['user_id', 'click_article_id', 'pred_score'])# 生成提交文件
def submit(recall_df, topk=5, model_name=None):recall_df = recall_df.sort_values(by=['user_id', 'pred_score'])recall_df['rank'] = recall_df.groupby(['user_id'])['pred_score'].rank(ascending=False, method='first')# 判断是不是每个用户都有5篇文章及以上tmp = recall_df.groupby('user_id').apply(lambda x: x['rank'].max())assert tmp.min() >= topkdel recall_df['pred_score']submit = recall_df[recall_df['rank'] <= topk].set_index(['user_id', 'rank']).unstack(-1).reset_index()submit.columns = [int(col) if isinstance(col, int) else col for col in submit.columns.droplevel(0)]# 按照提交格式定义列名submit = submit.rename(columns={'': 'user_id', 1: 'article_1', 2: 'article_2',3: 'article_3', 4: 'article_4', 5: 'article_5'})save_name = save_path + model_name + '_' + datetime.today().strftime('%m-%d') + '.csv'submit.to_csv(save_name, index=False, header=True)# 获取测试集
tst_click = pd.read_csv(data_path + 'testA_click_log.csv')
tst_users = tst_click['user_id'].unique()# 从所有的召回数据中将测试集中的用户选出来
tst_recall = recall_df[recall_df['user_id'].isin(tst_users)]# 生成提交文件
submit(tst_recall, topk=5, model_name='itemcf_baseline')

  结果



  提交baseline运行的结果暂时的排名

总结

  本节内容主要包括赛题简介,数据概况,评价方式以及对该赛题进行了一个总体上的思路分析,作为竞赛前的预热,旨在帮助学习者们能够更好切入该赛题,为后面的学习内容打下一个良好的基础。最后我们给出了关于本赛题的一个简易Baseline, 帮助学习者们先了解一下新闻推荐比赛的一个整理流程, 接下来我们就对于流程中的每个步骤进行详细的介绍。

  今天的学习比较简单,下面整理一下关于赛题理解的一些经验:

赛题理解究竟是在理解什么?

  • 理解赛题:从直观上对问题进行梳理, 分析问题的目标,到底要让做什么事情, 这个非常重要
  • 理解数据:对赛题数据有一个初步了解,知道和任务相关的数据字段和数据字段的类型, 数据之间的内在关联等,大体梳理一下哪些数据会对我们解决问题非常有用,方便后面我们的数据分析和特征工程。
    理解评估指标:评估指标是检验我们提出的方法,我们给出结果好坏的标准,只有正确的理解了评估指标,我们才能进行更好的训练模型,更好的进行预测。此外,很多情况下,线上验证是有一定的时间和次数限制的,所以在比赛中构建一个合理的本地的验证集和验证的评价指标是很关键的步骤,能有效的节省很多时间。 不同的指标对于同样的预测结果是具有误差敏感的差异性的所以不同的评价指标会影响后续一些预测的侧重点。

有了赛题理解之后,我们该做什么?

  • 在对于赛题有了一定的了解后,分析清楚了问题的类型性质和对于数据理解 的这一基础上,我们可以梳理一个解决赛题的一个大题思路和框架
  • 我们至少要有一些相应的理解分析,比如这题的难点可能在哪里,关键点可能在哪里,哪些地方可以挖掘更好的特征.
  • 用什么样得线下验证方式更为稳定,出现了过拟合或者其他问题,估摸可以用什么方法去解决这些问题

参考资料

  1. 零基础入门推荐系统 - 新闻推荐
  2. 天池新闻推荐入门赛之【赛题理解+Baseline】Task01

推荐系统入门(六):新闻推荐实践1(附代码)相关推荐

  1. 推荐系统入门(七):新闻推荐实践2(附代码)

    推荐系统入门(七):新闻推荐实践2(附代码) 目录 推荐系统入门(七):新闻推荐实践2(附代码) 引言 数据收集 数据存储 数据分析 实战 总结 参考资料 引言 相关系列笔记: 推荐系统入门(一):概 ...

  2. 推荐系统入门(十):新闻推荐实践5(附代码)

    推荐系统入门(十):新闻推荐实践5(附代码) 目录 推荐系统入门(十):新闻推荐实践5(附代码) 前言 LGB模型 DIN模型 一.排序模型 1.LGB排序模型 2.LGB分类模型 3.DIN模型 二 ...

  3. 《最新开源 随插即用》SAM 自增强注意力深度解读与实践(附代码及分析)

    写在前面 大家好,我是cv君,前段时间忙碌工作,许久没更新,越发觉得对不起csdn的读者们,决定继续加油保持更新,保持一周2-3篇的高频率和高质量文章更新:论文分析.代码讲解.代码实操和训练.优化部署 ...

  4. 【干货】基于内容理解的新闻推荐.pdf(附下载链接)

    今天给大家带来SmartNews AI算法负责人,前Facebook AI工程经理周涵宁先生在2021年全球机器学习技术大会上所做的分享<基于内容理解的新闻推荐.pdf>,关注知识图谱及新 ...

  5. 基于深度学习的个性化新闻推荐.pdf(附下载链接)

    今天给大家带来微软亚洲研究院研究院吴方照先生在2020年5月10日举办的"推荐系统前沿进展"系列学术沙龙活动中所做的分享<基于深度学习的个性化新闻推荐>,在本次分享中, ...

  6. 机器学习三人行(系列七)----支持向量机实践指南(附代码)

    原文链接:阅读原文 欢迎大家关注微信公众号"智能算法",我们一起学习,共同进步. 如需前面系列文章,请在公众号回复" 机器学习 "进行查看! 通过对系列六的学习 ...

  7. 基于大数据个性化音乐推荐算法分析(附代码github地址)

    github网址:https://github.com/ciecus/music_lgb_recommend_kkbox 欢迎fork我,和我讨论呀~ 摘  要:音乐推荐算法针对当今时代信息过载的问题 ...

  8. 【NLP文本分类算法集锦】零基础入门经典文本分类项目实战(附代码+数据集)

    前言 大家好,我是阿光. 本专栏整理了<NLP文本分类算法集锦>,内包含了各种常见的中英文文本分类算法,以及常见的NLP任务:情感分析.新闻分类以及谣言检测等. 文本分类是NLP的必备入门 ...

  9. 独家 | 从零开始用python搭建推荐引擎(附代码)

    作者:Pulkit Sharma 翻译:申利彬 校对:付宇帅 本文约10300字,建议阅读10分钟. 本文介绍了各种推荐引擎算法以及使用Python构建它们的基本框架. 简介 当今社会的每个人都面临着 ...

最新文章

  1. 百度短视频推荐系统的目标设计
  2. Centos-6.7下_Oracle 11gR2静默详细安装过程及排错
  3. 删除控制_别了 Flash!Win10已全面删除:已从控制面板和安装文件中消失
  4. HarmonyOS之常用布局StackLayout的使用
  5. 响应式web(一):什么是响应式web,异步调用,callback的本质,servlet3的异步
  6. Oracle 获取本周、本月、本季、本年的第一天和最后一天
  7. 适合初学者的安卓开源项目_开源系列的初学者将从下周开始
  8. Android自定义Toast
  9. java 正方形_java-确定正方形和矩形之间的关系的算法
  10. tkinter的可视化拖拽工具_拒绝丑图表,教你用最简单的方法做最炫酷的可视化图表!附教程...
  11. 解除webservice上下传文件大小限制
  12. Java:如何通过代码判断字符是拼音还是英文单词?
  13. 网页打印服务器无法创建对象,打印问题,小编教你怎么解决automation服务器不能创建对象...
  14. Ubuntu下为Firefox安装Adobe Flash Player
  15. 小米平板2怎么显示电脑连接服务器,小米平板2有什么接口?小米平板2有HDMI接口吗?...
  16. 数据连接池contect.xml配置
  17. ios制作h5的桌面webapp的详解
  18. Python微信公众号教程基础篇——收发文本消息
  19. 计算机网路之面试常考
  20. 构建MFS+Keepalived双机高可用热备方案`

热门文章

  1. 1. Python环境搭建
  2. 苏宁门店数字化白皮书发布 行业数字化进程将再提速
  3. 使用Tand自动化您的机器学习工作流程
  4. 电大计算机专业英语形成性考试,电大资源网《管理英语1》形成性考核册作业题目和答案2018年...
  5. react实现echarts的疫情地图
  6. 营养不良和口服万古霉素对人体肠道菌群和营养吸收的影响
  7. Oracle Java Cloud系列(02)——创建Java云的应用服务器实例 及 数据库云实例
  8. python pyecharts 下载生成的render图片 下载背景为黑色
  9. 中专生计算机基础知识论文3000字,计算机中专毕业论文3000字
  10. licode的ios最新版本的接入