阿里云-零基础入门推荐系统 【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**2
    for col in df.columns:
        col_type = df[col].dtypes
        if col_type in numerics:
            c_min = df[col].min()
            c_max = df[col].max()
            if pd.isnull(c_min) or pd.isnull(c_max):
                continue
            if 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**2
    print('-- 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_click


print("开始全量训练集")

# 全量训练集
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] += 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)
                
    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_:
                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]
        
    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 = 10

print("开始用户热度补全")
# 用户热度补全
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() >= topk
    
    del 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去理解,到后面问其他人的问题越来越少,个人自主学习、自主解决能力也得到了进一步增强。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/447264.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

202003 青少年软件编程(Scratch)等级考试试卷(一级)

202003 青少年软件编程&#xff08;Scratch&#xff09;等级考试试卷&#xff08;一级&#xff09; 第1题&#xff1a;【 单选题】 在Scratch中&#xff0c;以下哪个区域可以展示编程效果&#xff1f; A:代码区 B:舞台区 C:角色区 D:积木区 【正确答案】: B 【试题解析】…

《C缺陷和陷阱》-笔记(4)

目录 一、边界计算与不对称边界 1.栏杆错误 2.程序简化 3.编写程序 4.移动字符 5.打印元素 二、求值顺序 一、边界计算与不对称边界 在C语言中&#xff0c;这个数组的下标范围是从0到9。一个拥有10个元素的数组中&#xff0c;它的元素的下标范围是从0到n-1。 例如&…

2.4 为赌博而生的期望值理论

期望值理论 人们在风险决策时&#xff0c;会把数学期望值最大的可能选项作为自己的最终选择。 期望值&#xff1a;指无数次相同的风险决策的最终平均值或加权平均数它往往以货币或财产的数量为表现形式。又称期望货币值。 期望值的计算 圣彼得堡论&#xff1a;为什么人们不愿…

FPGA高端项目:FPGA基于GS2971的SDI视频接收+HLS多路视频融合叠加,提供1套工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本方案的SDI接收转HDMI输出应用本方案的SDI接收图像缩放应用本方案的SDI接收纯verilog图像缩放纯verilog多路视频拼接应用本方案的SDI接收HLS图像缩放Video Mixer多路视频拼接应用本方案的SDI接收OSD动态字符叠加…

《AI歌手:音乐产业的未来之音?》

引言 随着人工智能技术的快速发展,AI歌手作为一种新兴的演艺模式逐渐走进了人们的视野。AI歌手以其独特的魅力和无限的潜力引发了人们对于音乐产业未来的思考。本文将围绕AI歌手的音乐呈现、市场认可、替代性以及其他类似AI应用等方面展开讨论,探究AI歌手是否有望成为音乐产…

R语言读取大型NetCDF文件

失踪人口回归&#xff0c;本篇来介绍下R语言读取大型NetCDF文件的一些实践。 1 NetCDF数据简介 先给一段Wiki上关于NetCDF的定义。 NetCDF (Network Common Data Form) is a set of software libraries and self-describing, machine-independent data formats that support…

【强化学习的数学原理-赵世钰】课程笔记(七)时序差分方法

目录 一.内容概述 二.激励性实例&#xff08;Motivating examples&#xff09;-随机问题&#xff08;stochastic problems&#xff09; 三.估计 state value 的 TD 算法&#xff08;TD learning of state values&#xff09; 四.估计 action value 的 TD 算法&#xff08;TD…

【c语言 】 函数入门

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;C语言 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步&…

Spring循环依赖的成因与破局

一、Spring注入类型 Spring 核心功能之一依赖注入&#xff0c;依赖注入是使用 Spring 框架的基本手段&#xff0c;通过他获取各种类型的 bean&#xff0c;但使用不同的依赖注入类型时经常会遇到循环依赖的问题。Spring 依赖注入类型&#xff1a; 字段注入&#xff0c;这是最常…

酷开科技发力研发酷开系统,让家庭娱乐生活更加丰富多彩

在这个快节奏的社会&#xff0c;家庭娱乐已成为我们日常生活中不可或缺的一部分&#xff0c;为了给家庭带来更多欢笑与感动&#xff0c;酷开科技发力研发出拥有丰富内容和技术的智能电视操作系统——酷开系统&#xff0c;它集合了电影、电视剧、综艺、游戏、音乐等海量内容&…

01-分析同步通讯/异步通讯的特点及其应用

同步通讯/异步通讯 微服务间通讯有同步和异步两种方式 同步通讯: 类似打电话场景需要实时响应(时效性强可以立即得到结果方便使用),而且通话期间不能响应其他的电话(不支持多线操作)异步通讯: 类似发邮件场景不需要马上回复并且可以多线操作(适合高并发场景)但是时效性弱响应…

Unmanaged PowerShell

简介 在渗透测试当中经常会使用到PowerShell来执行脚本, 但是直接使用PowerShell.exe是一个非常敏感的行为, EDR等产品对PowerShell.exe进程的创建监控的很密切, 并且随着PowerShell的渗透测试工具的普及, 越来越多的EDR会利用微软提供的AMSI接口对PS脚本进行扫描, 但是对于低…

[短文]不同空白字符导致程序执行失败问题

屏幕显示的一个空白字符&#xff0c;对于编程者来说&#xff0c;并无差异&#xff0c;但底层截然不同的表示方法&#xff0c;极大可能导致程序执行失败&#xff01; 今天博主就遇到一个空格字符的问题&#xff0c;大概情况是前端编写SQL传入&#xff0c;后端有时可以执行&…

Uninty 鼠标点击(摄像机发出射线-检测位置)

平面来触发碰撞&#xff0c;胶囊用红色材质方便观察。 脚本挂载到胶囊上方便操作。 目前实现的功能&#xff0c;鼠标左键点击&#xff0c;胶囊就移动到那个位置上。 using System.Collections; using System.Collections.Generic; using UnityEngine;public class c6 : MonoBe…

DNS服务

简介 DNS&#xff1a;domain name service 域名服务 作用&#xff1a;为客户机提供域名解析 dns 监听端口&#xff1a;53端口 分为 udp&#xff08;只负责向外发&#xff09; 和 tcp&#xff08;确保正常发送至对端&#xff0c;对端发送数据包表示已经收到&#xff09; 搭建…

解密QQ盗号诈骗APP:逆向溯源,探寻幕后黑色操作

逆向溯源qq盗号诈骗app 起因 专注于web漏洞挖掘、内网渗透、免杀和代码审计&#xff0c;感谢各位师傅的关注&#xff01;网安之路漫长&#xff0c;与君共勉&#xff01; 分析该app是源于朋友被盗号了&#xff0c;对方发了个app想盗号&#xff0c;这怎么能惯着他&#xff1f;这不…

day16_购物车(添加购物车,购物车列表查询,删除购物车商品,更新选中商品状态,完成购物车商品的全选,清空购物车)

文章目录 购物车模块1 需求说明2 环境搭建3 添加购物车3.1 需求说明3.2 远程调用接口开发3.2.1 ProductController3.2.2 ProductService 3.3 openFeign接口定义3.3.1 环境搭建3.3.2 接口定义3.3.3 降级类定义 3.4 业务后端接口开发3.4.1 添加依赖3.4.2 修改启动类3.4.3 CartInf…

MyBatisPlus理解

MyBatisPlus是mybatis的增强&#xff0c;mybatis是数据库持久化的框架&#xff0c;但mybatisplus并不是替代mybatis&#xff0c;而是相辅相成的关系 MyBatisPlus不会对以前使用mybatis开发的项目进行影响&#xff0c;引入后仍然正常运行。 使用方法&#xff1a; 1.在引入了对…

[力扣 Hot100]Day48 路径总和 III

题目描述 给定一个二叉树的根节点 root &#xff0c;和一个整数 targetSum &#xff0c;求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始&#xff0c;也不需要在叶子节点结束&#xff0c;但是路径方向必须是向下的&#xff08;只能从父节点到…

再谈 Cookie 和 Session

文章目录 1. 核心方法&#xff1a;HttpServletRequest 类中HttpServletResponse 类中HttpSession类中Cookie类中 2.实现登录界面 Cookie 是浏览器在本地持久化存储数据的一种机制。 Cookie 的数据从哪里来&#xff1f; 是服务器返回给浏览器的。 Cookie 的数据长什么啥样&#…