阿里云-零基础入门推荐系统 【Baseline】

2024-03-11 09:04

本文主要是介绍阿里云-零基础入门推荐系统 【Baseline】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 赛题介绍
  • 评价方式理解
  • 赛题理解
  • 代码实战
    • 导包
    • df节省内存函数
    • 读取采样或全量数
    • 获取 用户 - 文章 - 点击时间字典
    • 获取点击最多的topk个文章
    • itemcf的物品相似度计算
    • itemcf 的文章推荐
    • 给每个用户根据物品的协同过滤推荐文章
    • 召回字典转换成df
    • 生成提交文件
    • 获取测试集
    • 从所有的召回数据中将测试集中的用户选出来
    • 生成提交文件
  • 学习过程


赛题介绍

该赛题是以新闻APP中的新闻推荐为背景, 目的是要求我们根据用户历史浏览点击新闻文章的数据信息预测用户未来的点击行为, 即用户的最后一次点击的新闻文章

评价方式理解

最后提交的格式是针对每个用户, 我们都会给出五篇文章的推荐结果,按照点击概率从前往后排序。 而真实的每个用户最后一次点击的文章只会有一篇的真实答案, 所以我们就看我们推荐的这五篇里面是否有命中真实答案的。比如对于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。 这个是合理的, 因为我们希望的就是命中的结果尽量靠前, 而此时分数正好比较高。

赛题理解

根据赛题简介,我们首先要明确我们此次比赛的目标: 根据用户历史浏览点击新闻的数据信息预测用户最后一次点击的新闻文章。从这个目标上看, 会发现此次比赛和我们之前遇到的普通的结构化比赛不太一样, 主要有两点:

  • 首先是目标上, 要预测最后一次点击的新闻文章,也就是我们给用户推荐的是新闻文章, 并不是像之前那种预测一个数或者预测数据哪一类那样的问题
  • 数据上, 通过给出的数据我们会发现, 这种数据也不是我们之前遇到的那种特征+标签的数据,而是基于了真实的业务场景, 拿到的用户的点击日志

所以拿到这个题目,我们的思考方向就是结合我们的目标,把该预测问题转成一个监督学习的问题(特征+标签),然后我们才能进行ML,DL等建模预测。

在这里插入图片描述

代码实战

已修改跑通代码协同过滤+热度召回

导包

import time, math, os
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 defaultdict
import collections
warnings.filterwarnings('ignore')# data_path = './data_raw/'
data_path = '/data/temp/用户行为预测数据集/' # '/home/admin/jupyter/data/' # 天池平台路径
save_path = 'results/0213/' # '/home/admin/jupyter/temp_results/'  # 天池平台路径

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_raw/', 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_clickprint("开始全量训练集")# 全量训练集
all_click_df = get_all_click_df(data_path, offline=False)print("结束全量训练集")

获取 用户 - 文章 - 点击时间字典

# 根据点击时间获取用户的点击文章序列   {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_cl

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_print("开始itemcf的物品相似度计算")i2i_sim = itemcf_sim(all_click_df)print("结束itemcf的物品相似度计算")

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: 字典, 根据点击时间获取用户的点击文章序列   {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]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)print("开始获取 用户 - 文章 - 点击时间的字典")
# 获取 用户 - 文章 - 点击时间的字典
user_item_time_dict = get_user_item_time(all_click_df)
print("结束获取 用户 - 文章 - 点击时间的字典# 去取文章相似度
i2i_sim = pickle.load(open(save_path + 'itemcf_i2i_sim.pkl', 'rb'))# 相似文章的数量
sim_item_topk = 10# 召回文章数量
recall_item_num = 10print("开始用户热度补全")
# 用户热度补全
item_topk_click = get_item_topk_click(all_click_df, k=50)
print("结束用户热度补全")print("开始itemcf 的文章推荐")
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)
print("结束itemcf 的文章推荐")

召回字典转换成df

print("开始召回字典转换成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'])print("结束召回字典转换成df")

生成提交文件

# 生成提交文件
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)

获取测试集

print("开始获取测试集")
# 获取测试集
tst_click = pd.read_csv(data_path + 'testA_click_log.csv')
tst_users = tst_click['user_id'].unique()
print("结束获取测试集")

从所有的召回数据中将测试集中的用户选出来

print("开始从所有的召回数据中将测试集中的用户选出来")
# 从所有的召回数据中将测试集中的用户选出来
tst_recall = recall_df[recall_df['user_id'].isin(tst_users)]
print("结束从所有的召回数据中将测试集中的用户选出来")

生成提交文件

print("开始生成提交文件")
# 生成提交文件
submit(tst_recall, topk=5, model_name='itemcf_baseline')
print("结束生成提交文件")

学习过程

20年当时自身功底是比较零基础(会写些基础的Python[三个科学计算包]数据分析),一开始看这块其实挺懵的,不会就去问百度或其他人,当时遇见困难挺害怕的,但22后面开始力扣题【目前已刷好几轮,博客没写力扣文章之前,力扣排名靠前已刷有5遍左右,排名靠后刷3次左右,代码功底也在一步一步提升】不断地刷、遇见代码不懂的代码,也开始去打印print去理解,到后面问其他人的问题越来越少,个人自主学习、自主解决能力也得到了进一步增强。

比赛源自:阿里云天池大赛 - 零基础入门推荐系统 - 新闻推荐

这篇关于阿里云-零基础入门推荐系统 【Baseline】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/797353

相关文章

从基础到进阶详解Pandas时间数据处理指南

《从基础到进阶详解Pandas时间数据处理指南》Pandas构建了完整的时间数据处理生态,核心由四个基础类构成,Timestamp,DatetimeIndex,Period和Timedelta,下面我... 目录1. 时间数据类型与基础操作1.1 核心时间对象体系1.2 时间数据生成技巧2. 时间索引与数据

Java SWT库详解与安装指南(最新推荐)

《JavaSWT库详解与安装指南(最新推荐)》:本文主要介绍JavaSWT库详解与安装指南,在本章中,我们介绍了如何下载、安装SWTJAR包,并详述了在Eclipse以及命令行环境中配置Java... 目录1. Java SWT类库概述2. SWT与AWT和Swing的区别2.1 历史背景与设计理念2.1.

基于Python实现一个简单的题库与在线考试系统

《基于Python实现一个简单的题库与在线考试系统》在当今信息化教育时代,在线学习与考试系统已成为教育技术领域的重要组成部分,本文就来介绍一下如何使用Python和PyQt5框架开发一个名为白泽题库系... 目录概述功能特点界面展示系统架构设计类结构图Excel题库填写格式模板题库题目填写格式表核心数据结构

Java日期类详解(最新推荐)

《Java日期类详解(最新推荐)》早期版本主要使用java.util.Date、java.util.Calendar等类,Java8及以后引入了新的日期和时间API(JSR310),包含在ja... 目录旧的日期时间API新的日期时间 API(Java 8+)获取时间戳时间计算与其他日期时间类型的转换Dur

Linux系统中的firewall-offline-cmd详解(收藏版)

《Linux系统中的firewall-offline-cmd详解(收藏版)》firewall-offline-cmd是firewalld的一个命令行工具,专门设计用于在没有运行firewalld服务的... 目录主要用途基本语法选项1. 状态管理2. 区域管理3. 服务管理4. 端口管理5. ICMP 阻断

安装centos8设置基础软件仓库时出错的解决方案

《安装centos8设置基础软件仓库时出错的解决方案》:本文主要介绍安装centos8设置基础软件仓库时出错的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录安装Centos8设置基础软件仓库时出错版本 8版本 8.2.200android4版本 javas

Windows 系统下 Nginx 的配置步骤详解

《Windows系统下Nginx的配置步骤详解》Nginx是一款功能强大的软件,在互联网领域有广泛应用,简单来说,它就像一个聪明的交通指挥员,能让网站运行得更高效、更稳定,:本文主要介绍W... 目录一、为什么要用 Nginx二、Windows 系统下 Nginx 的配置步骤1. 下载 Nginx2. 解压

Linux基础命令@grep、wc、管道符的使用详解

《Linux基础命令@grep、wc、管道符的使用详解》:本文主要介绍Linux基础命令@grep、wc、管道符的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录grep概念语法作用演示一演示二演示三,带选项 -nwc概念语法作用wc,不带选项-c,统计字节数-

如何确定哪些软件是Mac系统自带的? Mac系统内置应用查看技巧

《如何确定哪些软件是Mac系统自带的?Mac系统内置应用查看技巧》如何确定哪些软件是Mac系统自带的?mac系统中有很多自带的应用,想要看看哪些是系统自带,该怎么查看呢?下面我们就来看看Mac系统内... 在MAC电脑上,可以使用以下方法来确定哪些软件是系统自带的:1.应用程序文件夹打开应用程序文件夹

Python中OpenCV与Matplotlib的图像操作入门指南

《Python中OpenCV与Matplotlib的图像操作入门指南》:本文主要介绍Python中OpenCV与Matplotlib的图像操作指南,本文通过实例代码给大家介绍的非常详细,对大家的学... 目录一、环境准备二、图像的基本操作1. 图像读取、显示与保存 使用OpenCV操作2. 像素级操作3.