写点什么

如何构建一个真实的推荐系统?

作者:Susan Li

  • 2019-02-28
  • 本文字数:4593 字

    阅读完需:约 15 分钟

如何构建一个真实的推荐系统?

AI 前线导读:随着互联网行业的井喷式发展,数据规模呈现爆炸式增长。大数据中蕴含了巨大的价值,但同时也来了很 “信息过载” 的问题。推荐系统作为一个广泛应用的信息过滤系统,在很多领域取得了巨大的成功。在电子商务上(Amazon,eBay,阿里巴巴),推荐系统为用户提供个性化产品,发掘用户潜在需求。那些电商的 “猜你喜欢” 其实就是推荐系统的应用。简单的说,推荐系统的目标是根据用户的偏好,为其找到并推荐可能感兴趣的项目。


当今机器学习中最有价值的应用之一就是推荐系统。Amazon 将其 35% 的收入归功于其推荐系统。


译注:关于 35% 这一数据详见《The Amazon Recommendations Secret to Selling More Online》(http://rejoiner.com/resources/amazon-recommendations-secret-selling-online/)


评估是研究和开发任何推荐系统的重要组成部分。根据你的业务和可用数据,有很多方法可以评估推荐系统。在本文中,我们会尝试一些评估方法。

评级预测

在我上一篇文章中《Building and Testing Recommender Systems With Surprise, Step-By-Step 》(https://towardsdatascience.com/building-and-testing-recommender-systems-with-surprise-step-by-step-d4ba702ef80b):使用 Surprise 构建和测试推荐系统,Surprise 以各种机器学习算法为中心来预测用户对商品条目的评级(即评级预测)。它要求用户提供明确的反馈,比如让用户在购买图书后对其进行 0~10 星的评级。然后我们用这些数据来建立用户兴趣的档案。问题是,不是每个人都愿意留下评级,因此数据往往是稀疏的,就像我们之前看到的 Book-Crossing 数据集一样:



译注:Book-Crossing 数据集可见 http://www2.informatik.uni-freiburg.de/~cziegler/BX/


大多数推荐系统是这样试图预测的:如果用户对相应的图书进行评级的话,他们会在里面放入什么内容。如果 “NaN” 太多,那么推荐系统就没有足够的数据来理解用户究竟喜欢什么。


但是,如果你能说服用户给你评级,那么明确的评级是很好的。因此,如果你拥有大量的数据和用户评级,那么评估指标应该为 RMSEMAE。让我们展示一个带有 Surprise 库的 Movielens 数据集示例。


movies = pd.read_csv('movielens_data/movies.csv')ratings = pd.read_csv('movielens_data/ratings.csv')df = pd.merge(movies, ratings, on='movieId', how='inner')reader = Reader(rating_scale=(0.5, 5))data = Dataset.load_from_df(df[['userId', 'title', 'rating']], reader)trainSet, testSet = train_test_split(data, test_size=.25, random_state=0)algo = SVD(random_state=0)algo.fit(trainSet)predictions = algo.test(testSet)
def MAE(predictions): return accuracy.mae(predictions, verbose=False)def RMSE(predictions): return accuracy.rmse(predictions, verbose=False) print("RMSE: ", RMSE(predictions))print("MAE: ", MAE(predictions))
复制代码


ratings_prediction.py


Top-N

从网上购物网站到视频门户网站,Top-N 推荐系统的身影无处不在。它们为用户提供他们可能感兴趣的 N 个项目的排名列表,以鼓励用户浏览、下单购买。


译注:Top-N 推荐系统的介绍可观看 YouTube 视频:https://www.youtube.com/watch?v=EeXBdQYs0CQ


Amazon 的推荐系统之一就是 “Top-N” 系统,它可以为个人提供顶级结果列表:



Amazon 的 “Top-N” 推荐包括 9 页,第一页有 6 项。一个好的推荐系统应该能够识别某个用户感兴趣的一组 N 个条目。因为我很少在 Amazon 上买书,因此我的 “Top-N” 就差得很远。换言之,我可能只会点击或阅读我的 “Top-N” 列表中的某本书。


下面的脚本为测试集中的每个用户生成了前 10 条推荐。


def GetTopN(predictions, n=10, minimumRating=4.0):    topN = defaultdict(list)    for userID, movieID, actualRating, estimatedRating, _ in predictions:        if (estimatedRating >= minimumRating):            topN[int(userID)].append((int(movieID), estimatedRating))
for userID, ratings in topN.items(): ratings.sort(key=lambda x: x[1], reverse=True) topN[int(userID)] = ratings[:n]
return topN LOOCV = LeaveOneOut(n_splits=1, random_state=1)
for trainSet, testSet in LOOCV.split(data): # Train model without left-out ratings algo.fit(trainSet) # Predicts ratings for left-out ratings only leftOutPredictions = algo.test(testSet) # Build predictions for all ratings not in the training set bigTestSet = trainSet.build_anti_testset() allPredictions = algo.test(bigTestSet) # Compute top 10 recs for each user topNPredicted = GetTopN(allPredictions, n=10)
复制代码


top-N.py


下面是我们预测的 userId 2 和 userId 3 的前 10 项。


命中率

让我们看看生成的前 10 项推荐究竟有多好。为评估前 10 项,我们使用命中率这一指标,也就是说,如果用户对我们推荐的前 10 项中的一个进行了评级,那么我们就认为这是一个 “命中”。


计算单个用户命中率的过程如下:


  • 在训练数据中查找此用户历史记录中的所有项。

  • 有意删除其中一项条目(使用留一法,一种交叉验证方法)。

  • 使用所有其他项目为推荐系统提供信息,并要求提供前 10 项推荐。

  • 如果删除的条目出现在前 10 项推荐中,那么它就是命中的。如果没有,那就不算命中。


def HitRate(topNPredicted, leftOutPredictions):    hits = 0    total = 0
# For each left-out rating for leftOut in leftOutPredictions: userID = leftOut[0] leftOutMovieID = leftOut[1] # Is it in the predicted top 10 for this user? hit = False for movieID, predictedRating in topNPredicted[int(userID)]: if (int(leftOutMovieID) == int(movieID)): hit = True break if (hit) : hits += 1
total += 1
# Compute overall precision return hits/totalprint("\nHit Rate: ", HitRate(topNPredicted, leftOutPredictions))
复制代码


HitRate.py



系统的总命中率是命中数除以测试用户数。它衡量的是我们推荐删除评级的频率,越高越好。


如果命中率非常低的话,这只是意味着我们没有足够的数据可供使用。就像 Amazon 对我来说,命中率就非常低,因为它没有足够的我购买图书的数据。

基于评级值的命中率

我们还可以通过预测的评级值来细分命中率。在理想情况下,我们希望预测用户喜欢的电影,因此我们关心的是高评级值而不是低评级值。


def RatingHitRate(topNPredicted, leftOutPredictions):    hits = defaultdict(float)    total = defaultdict(float)    # For each left-out rating    for userID, leftOutMovieID, actualRating, estimatedRating, _ in leftOutPredictions:        # Is it in the predicted top N for this user?        hit = False        for movieID, predictedRating in topNPredicted[int(userID)]:            if (int(leftOutMovieID) == movieID):                hit = True                break        if (hit) :            hits[actualRating] += 1        total[actualRating] += 1
# Compute overall precision for rating in sorted(hits.keys()): print(rating, hits[rating] / total[rating])print("Hit Rate by Rating value: ")RatingHitRate(topNPredicted, leftOutPredictions)
复制代码


RatingHitRate.py



我们的命中率细分正是我们所期望的,评级值为 5 的命中率远高于 4 或 3。越高越好。

累积命中率

因为我们关心更高的评级,我们可以忽略低于 4 的预测评级,来计算 > = 4 的评级命中率。


def CumulativeHitRate(topNPredicted, leftOutPredictions, ratingCutoff=0):    hits = 0    total = 0    # For each left-out rating    for userID, leftOutMovieID, actualRating, estimatedRating, _ in leftOutPredictions:        # Only look at ability to recommend things the users actually liked...        if (actualRating >= ratingCutoff):            # Is it in the predicted top 10 for this user?            hit = False            for movieID, predictedRating in topNPredicted[int(userID)]:                if (int(leftOutMovieID) == movieID):                    hit = True                    break            if (hit) :                hits += 1            total += 1
# Compute overall precision return hits/totalprint("Cumulative Hit Rate (rating >= 4): ", CumulativeHitRate(topNPredicted, leftOutPredictions, 4.0))
复制代码


CumulativeHitRate.py



越高越好。

平均对等命中排名(Average Reciprocal Hit Ranking,ARHR)

常用于 Top-N 推荐系统排名评估的指标,只考虑第一个相关结果出现的地方。我们在推荐用户排名靠前而不是靠后的产品获得了更多的好评。越高越好。


def AverageReciprocalHitRank(topNPredicted, leftOutPredictions):    summation = 0    total = 0        # For each left-out rating    for userID, leftOutMovieID, actualRating, estimatedRating, _ in leftOutPredictions:        # Is it in the predicted top N for this user?        hitRank = 0        rank = 0        for movieID, predictedRating in topNPredicted[int(userID)]:            rank = rank + 1            if (int(leftOutMovieID) == movieID):                hitRank = rank                break        if (hitRank > 0) :                summation += 1.0 / hitRank
total += 1
return summation / total
print("Average Reciprocal Hit Rank: ", AverageReciprocalHitRank(topNPredicted, leftOutPredictions))view rawAverageReciprocalHitRank.py hosted with ❤ by GitHub
复制代码


AverageReciprocalHitRank.py



你的第一个真实推荐系统可能质量很低,哪怕是成熟系统,用于新用户的表现也是一样。但是,这仍然比没有推荐系统要好多得多。推荐系统的目的之一,就是在推荐系统中了解用户 / 新用户的偏好,这样他们就可以开始从系统中接收准确的个性化推荐。


然而,如果你刚刚起步的话,那么你的网站就是全新的,这时候推荐系统并不能为任何人提供个性化的推荐,因为这时候并没有任何人的评价。然后,这就变成了一个系统引导问题。


译注:有关系统引导问题可参阅:《Learning Preferences of New Users in RecommenderSystems: An Information Theoretic Approach》(https://www.kdd.org/exploration_files/WebKDD08-Al-Rashid.pdf)


本文的 Jupyter Notebook 可以在 Github 上找到:https://github.com/susanli2016/Machine-Learning-with-Python/blob/master/Movielens Recommender Metrics.ipynb。


参考文献:Building Recommender Systems with Machine Learning and AI(《使用机器学习和人工智能构建推荐系统》https://learning.oreilly.com/videos/building-recommender-systems/9781789803273


原文链接:https://towardsdatascience.com/evaluating-a-real-life-recommender-system-error-based-and-ranking-based-84708e3285b


2019-02-28 13:456238
用户头像

发布了 375 篇内容, 共 191.0 次阅读, 收获喜欢 947 次。

关注

评论

发布
暂无评论
发现更多内容

Mysql数据库查询好慢,除了索引,还能因为什么?

C++后台开发

MySQL 数据库 中间件 后端开发 C++后台开发

【6.10-6.16】写作社区精彩技术博文回顾

InfoQ写作社区官方

优质创作周报

BI与SaaS碰撞,让数据处理更加轻松(下)

葡萄城技术团队

数据分析 SaaS BI

dp练习

工程师日月

6月月更

个推CTO趣谈元宇宙:从概念、成因到核心技术

个推

数据智能 元宇宙

系统困境与软件复杂度,为什么我们的系统会如此复杂

阿里巴巴终端技术

软件 系统设计 软件开发

Linux系统与Windows系统之间的文件上传与下载

龙空白白

Linux Windows系统

5月券商App行情刷新及交易体验评测报告,四家券商综合评级上升

博睿数据

智能运维 博睿数据 券商排行

鲲鹏云开发者分论坛:发挥鲲鹏的潜力,加速云上创新

科技热闻

java就业培训 | 面试官如何判断应聘者能力的,这一篇就够了

@零度

JAVA开发

InfoQ 极客传媒 15 周年庆征文|跨平台应用开发进阶(二十二) :uni-app Android APP上线准备工作汇总

No Silver Bullet

前端 签名 安全检测 6月月更 InfoQ极客传媒15周年庆

Open the World:第七届中国开源年会(COSCon'22)正式启动~

开源社

第七届中国开源年会 COSCon'22

个推TechDay直播预告 | 6月22日,开启大数据降本提效的破局之道!

个推

大数据 分布式计算 分布式存储 标签

图搜的应用场景

Geek_e369a5

图像检测 图像搜索 图搜的应用场景

如何编写一份简单易用的在线产品手册

小炮

产品宣传手册 产品说明手册

2022年中国Robotaxi行业发展洞察

易观分析

智能汽车

云安全是什么样子的?其工作原理是什么?

wljslmz

云安全 6月月更

智慧园区效果不满意?请收下ThingJS这份秘籍

ThingJS数字孪生引擎

智慧园区 数字孪生

JMeter集成底座项目压测心得

agileai

压力测试 集成底座 企业服务总线 统一身份管理平台 主数据管理平台

自己实现一个大文件切片上传+断点续传

转转技术团队

JavaScript 前端 文件上传

Vue3 响应性原理

转转技术团队

JavaScript Vue 前端

级联层与层叠上下文了解下?

转转技术团队

CSS JavaScript 前端

易观分析《计算机视觉市场研究,2022》研究报告正式启动

易观分析

人工智能

IntelliJ IDEA常见快捷键

龙空白白

IntelliJ IDEA

低代码如何“拯救”企业?

优秀

低代码 企业管理

一起认识下浏览器的5种观察器

转转技术团队

JavaScript 前端 浏览器

特定MPC问题包含哪些常见技术,有什么作用,什么场景需要用到?

Jessica@数牍

隐私计算 安全多方计算 特定mpc问题

【LeetCode】多个数组求交集Java题解

Albert

LeetCode 6月月更

编程技巧│浏览器 Notification 桌面推送通知

可视化 6月月更 Notification 实时通知

网络安全实战从0到1彻底掌握XXE

网络安全学海

黑客 网络安全 安全 渗透测试 WEB安全

科创人·数智未来私董会第4期:转型的实证-幸存者偏差与盲人摸象

科创人

如何构建一个真实的推荐系统?_大数据_InfoQ精选文章