CCF推荐系统项目代码解读!

news/2024/7/7 21:11:12

 Datawhale干货 

作者:阿水,北京航空航天大学,Datawhale成员

本文以CCF大数据与计算智能大赛(CCF BDCI)图书推荐系统竞赛为实践背景,使用Paddle构建用户与图书的打分模型,借助Embedding层来完成具体的匹配过程。后台回复 211208 可获取完整代码。

代码地址

https://aistudio.baidu.com/aistudio/projectdetail/2556840

afaad5e385884e32018f61e62e1e85cd.png

实践背景

赛题背景

赛事地址

https://www.datafountain.cn/competitions/542

随着新型互联网的发展,人类逐渐进入了信息爆炸时代。新型电商网络面临的问题也逐渐转为如何让用户从海量的商品中挑选到自己想要的目标。推荐系统正是在互联网快速发展之后的产物。

为帮助电商系统识别用户需求,为用户提供其更加感兴趣的信息,从而为用户提供更好的服务,需要依据真实的图书阅读数据集,利用机器学习的相关技术,建立一个图书推荐系统。用于为用户推荐其可能进行阅读的数据,从而在产生商业价值的同时,提升用户的阅读体验,帮助创建全民读书的良好社会风气。

赛题任务

依据真实世界中的用户-图书交互记录,利用机器学习相关技术,建立一个精确稳定的图书推荐系统,预测用户可能会进行阅读的书籍。

赛题数据

数据集来自公开数据集Goodbooks-10k,包含网站Goodreads中对10,000本书共约6,000,000条评分。为了预测用户下一个可能的交互对象,数据集已经处理为隐式交互数据集。该数据集广泛的应用于推荐系统中。

数据文件夹包含3个文件,依次为:

  • 训练集: train.csv 训练数据集,为用户-图书交互记录

  • 测试集: test.csv 测试数据集,只有需要进行预测用户ID

  • 提交样例: submission.csv 仅有两个字段user_id/item_id

解题思路

使用深度学习模型构建隐式推荐算法模型,并构建负样本,最终按照模型输出的评分进行排序,做出最终的推荐。具体可以分为以下几个步骤:

  • 步骤1:读取数据,对用户和图书进行编码;

  • 步骤2:利用训练集构建负样本;

  • 步骤3:使用Paddle构建打分模型;

  • 步骤4:对测试集数据进行预测;

步骤1:读取数据集

首先我们使用pandas读取数据集,并对数据的字段进行编码。这里可以手动构造编码过程,也可以使用LabelEncoder来完成。

这一步骤的操作目的是将对用户和图书编码为连续的数值,原始的取值并不是连续的,这样可以减少后续模型所需要的空间。

步骤2:构建负样本

由于原始训练集中都是记录的是用户已有的图书记录,并不存在负样本。而在预测阶段我们需要预测用户下一个图书,此时的预测空间是用户对所有图书的关系。

这里构建负样本的操作非常粗暴,直接是选择用户在训练集中没有图书。这里可以先使用协同过滤的思路来构建负样本,即将负样本是相似用户都没有记录的图书。

步骤3:Paddle搭建打分模型

这里使用Paddle构建用户与图书的打分模型,借助Embedding层来完成具体的匹配过程。这里用最简单的dot来完成匹配,没有构建复杂的模型。

2b0226a64cd32f0bb2a399ad954ae99b.png

步骤4:对测试集进行预测

首先将测试集数据转为模型需要的格式,然后一行代码完成预测即可,然后转换为提交格式。

改进思路

由于现有的代码写的比较基础,所以有很多改进的步骤:

  • 对模型精度进行改进,可以考虑构建更加复杂的模型,并对训练集负样本构造过程进行改进。

  • 对模型使用内存,可以考虑使用Numpy代替Pandas的操作。

代码实践

读取数据集

# 查看当前挂载的数据集目录, 该目录下的变更重启环境后会自动还原
# View dataset directory. 
# This directory will be recovered automatically after resetting environment. !unzip /home/aistudio/data/data114712/train_dataset.zip
Archive:  /home/aistudio/data/data114712/train_dataset.zipinflating: train_dataset.csv       !cp /home/aistudio/data/data114712/test_dataset.csv ./!head train_dataset.csv
user_id,item_idimport pandas as pd
import numpy as np
import paddle
import paddle.nn as nn
from paddle.io import Datasetdf = pd.read_csv('train_dataset.csv')
user_ids = df["user_id"].unique().tolist()user2user_encoded = {x: i for i, x in enumerate(user_ids)}
userencoded2user = {i: x for i, x in enumerate(user_ids)}book_ids = df["item_id"].unique().tolist()
book2book_encoded = {x: i for i, x in enumerate(book_ids)}
book_encoded2book = {i: x for i, x in enumerate(book_ids)}df["user"] = df["user_id"].map(user2user_encoded)
df["movie"] = df["item_id"].map(book2book_encoded)num_users = len(user2user_encoded)
num_books = len(book_encoded2book)user_book_dict = df.iloc[:].groupby(['user'])['movie'].apply(list)user_book_dict
user

构造负样本

neg_df = []
book_set = set(list(book_encoded2book.keys()))
for user_idx in user_book_dict.index:book_idx = book_set - set(list(user_book_dict.loc[user_idx]))book_idx = list(book_idx)neg_book_idx = np.random.choice(book_idx, 100)for x in neg_book_idx:neg_df.append([user_idx, x])neg_df = pd.DataFrame(neg_df, columns=['user', 'movie'])
neg_df['label'] = 0df['label'] = 1
train_df = pd.concat([df[['user', 'movie', 'label']], neg_df[['user', 'movie', 'label']]], axis=0)train_df = train_df.sample(frac=1)del df;

自定义数据集

# 自定义数据集# 映射式(map-style)数据集需要继承paddle.io.Dataset
class SelfDefinedDataset(Dataset):def __init__(self, data_x, data_y, mode = 'train'):super(SelfDefinedDataset, self).__init__()self.data_x = data_xself.data_y = data_yself.mode = modedef __getitem__(self, idx):if self.mode == 'predict':return self.data_x[idx]else:return self.data_x[idx], self.data_y[idx]def __len__(self):return len(self.data_x)from sklearn.model_selection import train_test_split
x_train, x_val, y_train, y_val = train_test_split(train_df[['user', 'movie']].values, train_df['label'].values.astype(np.float32).reshape(-1, 1))traindataset = SelfDefinedDataset(x_train, y_train)
for data, label in traindataset:print(data.shape, label.shape)print(data, label)breaktrain_loader = paddle.io.DataLoader(traindataset, batch_size = 1280*4, shuffle = True)
for batch_id, data in enumerate(train_loader):x_data = data[0]y_data = data[1]print(x_data.shape)print(y_data.shape)breakval_dataset = SelfDefinedDataset(x_val, y_val)
val_loader = paddle.io.DataLoader(val_dataset, batch_size = 1280*4, shuffle = True)        
for batch_id, data in enumerate(val_loader):x_data = data[0]y_data = data[1]print(x_data.shape)print(y_data.shape)break

定义模型

EMBEDDING_SIZE = 32class RecommenderNet(nn.Layer):def __init__(self, num_users, num_movies, embedding_size):super(RecommenderNet, self).__init__()self.num_users = num_usersself.num_movies = num_moviesself.embedding_size = embedding_sizeweight_attr_user = paddle.ParamAttr(regularizer = paddle.regularizer.L2Decay(1e-6),initializer = nn.initializer.KaimingNormal())self.user_embedding = nn.Embedding(num_users,embedding_size,weight_attr=weight_attr_user)self.user_bias = nn.Embedding(num_users, 1)weight_attr_movie = paddle.ParamAttr(regularizer = paddle.regularizer.L2Decay(1e-6),initializer = nn.initializer.KaimingNormal())self.movie_embedding = nn.Embedding(num_movies,embedding_size,weight_attr=weight_attr_movie)self.movie_bias = nn.Embedding(num_movies, 1)def forward(self, inputs):user_vector = self.user_embedding(inputs[:, 0])user_bias = self.user_bias(inputs[:, 0])movie_vector = self.movie_embedding(inputs[:, 1])movie_bias = self.movie_bias(inputs[:, 1])dot_user_movie = paddle.dot(user_vector, movie_vector)x = dot_user_movie + user_bias + movie_biasx = nn.functional.sigmoid(x)return xmodel = RecommenderNet(num_users, num_books, EMBEDDING_SIZE)model = paddle.Model(model)
optimizer = paddle.optimizer.Adam(parameters=model.parameters(), learning_rate=0.003)
loss = nn.BCELoss()
metric = paddle.metric.Precision()## 设置visualdl路径
log_dir = './visualdl'
callback = paddle.callbacks.VisualDL(log_dir=log_dir)model.prepare(optimizer, loss, metric)
model.fit(train_loader, val_loader, epochs=5, save_dir='./checkpoints', verbose=1, callbacks=callback)

预测测试集

test_df = []
with open('sub.csv', 'w') as up:up.write('user_id,item_id\n')book_set = set(list(book_encoded2book.keys()))
for idx in range(int(len(user_book_dict)/1000) +1):test_user_idx = []test_book_idx = []for user_idx in user_book_dict.index[idx*1000:(idx+1)*1000]:book_idx = book_set - set(list(user_book_dict.loc[user_idx]))book_idx = list(book_idx)test_user_idx += [user_idx] * len(book_idx)test_book_idx +=  book_idxtest_data = np.array([test_user_idx, test_book_idx]).Ttest_dataset = SelfDefinedDataset(test_data, data_y=None, mode='predict')test_loader = paddle.io.DataLoader(test_dataset, batch_size=1280, shuffle = False)        test_predict = model.predict(test_loader, batch_size=1024)test_predict = np.concatenate(test_predict[0], 0)test_data = pd.DataFrame(test_data, columns=['user', 'book'])test_data['label'] = test_predictfor gp in test_data.groupby(['user']):with open('sub.csv', 'a') as up:u = gp[0]b = gp[1]['book'].iloc[gp[1]['label'].argmax()]up.write(f'{userencoded2user[u]}, {book_encoded2book[b]}\n')del test_data, test_dataset, test_loader

1190e8fc0b67ff5d9602a3db2d6a1e8c.png

整理不易,三连


http://lihuaxi.xjx100.cn/news/256333.html

相关文章

转《两个个很形象的依赖注入的比喻》

何谓控制反转(IoC Inversion of Control),何谓依赖注入(DI Dependency Injection)?一直都半懂不懂,今天看到两个比喻,觉得比较形象。 IoC,用白话来讲,就是…

股市币市:数据分析与交易所公告(20190225)

沪深300 1. 沪深300分位数数据 2. 沪深300股指图 3. 沪深300分位数图 4. 沪深300筹码分布图 数据来源: https://finance.sina.com.cn/stock/ BTC比特币 1. 比特币分位数数据 2. 比特币交易图 3. 比特币分位数图 4. 比特币筹码分布图 数据来源: htt…

他,1年9个月获清华博士学位,一作身份发27篇SCI,组队击败NASA打破“航天奥林匹克”欧美垄断...

点击上方“视学算法”,选择加"星标"或“置顶”重磅干货,第一时间送达Pine 梦晨 发自 凹非寺量子位 | 公众号 QbitAI1年9个月拿到清华博士学位,一作发表SCI论文27篇,曾在国际大赛击败NASA……航天学霸姜宇的故事&#xf…

SQL:安装多个实例,修改实例端口号,和IP加端口号连接实例

原文:SQL:安装多个实例,修改实例端口号,和IP加端口号连接实例sql server 安装第一个实例,默认实例的端口是1433, 一个库中如果有多个实例,从第二个实例开始的端口是动态端口,需要的话,自己手工指…

Spring Boot 配置 HTTPS 的详细流程

欢迎关注方志朋的博客,回复”666“获面试宝典大家好,周末空下来,再学习一些小知识吧!准备把面试的项目上线,因为是小程序要求必须https,记录下完整流程和走过的坑第一步 申请SSL证书这里选了免费的 https:/…

好看的皮囊千篇一律,有趣的灵魂万里挑一

古语有云,学如逆水行舟,不进则退;在当下数字化大时代潮流背景之下,技术日新月异,不断更新,使得我们不得不去学习。一方面是给自己加深对技术领域的认知,另一方面也是自己涨薪的必备法宝。今天分…

我是主考官:两次弃用的变态笔试题

故事(3):两次弃用的变态笔试题电话的沟通虽然不可能对一个程序员作全面的了解,但基本上能有一个比较概括的判断,这也许就是所谓的第一印象吧!通过电话的初步沟通我对来面试的程序员已经有了初步的印象&…

GitLab 配置邮箱

设置 SMTP 发送邮件 这里以腾讯企业邮箱为例,其他邮箱可以参考 设置 SMTP 发送邮件。 SMTP 和 POP3/IMAP 协议 SMTP 负责发送邮件,POP3/IMAP 负责接收邮件。其中 IMAP 基本上替换掉了 POP3。 用户在使用客户端(例如 Foxmail)时&am…