写点什么

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

作者: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:456567
用户头像

发布了 376 篇内容, 共 211.0 次阅读, 收获喜欢 949 次。

关注

评论

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

怎样快速地迁移 MySQL 中的数据?

Java MySQL 数据库

入门数据分析师的最强秘籍,都在这4本书里!

博文视点Broadview

面试官:分库分表,真的有必要吗?

Java 分库分表

Linux安装ElasticSearch

Geek_7ubdnf

Java elasticsearch

Mac 使用brew install zsh 遇到 Error: Command failed with exit 128: git

24号工程师

brew zsh

2023年新广州市等保测评机构名单看这里!

行云管家

等级保护 等保测评 广州

Spring Security怎么从数据库加载我们的用户?

Java spring spring security

天翼云发布《中国医疗云建设与应用白皮书》 推动医疗数字化提质增速

极客天地

微服务 SpringBoot 整合 Redis GEO 实现附近商户功能

Java redis 微服务 Spring Boot

从实战出发,聊聊缓存数据库一致性

Java 数据库 缓存 一致性

分布式事务解决方案

Java 分布式事务 事务

记一次SpringBoot启动优化实践

Java spring Spring Boot

软件测试/测试开发 | Selenium多浏览器处理

测试人

软件测试 自动化测试 测试开发 selenium web测试

模块六作业

程序员小张

「架构实战营」

Three.js 进阶之旅:物理效果-碰撞和声音 💥

dragonir

CSS JavaScript html 前端 three.js

基于文心大模型套件ERNIEKit实现文本匹配算法,模块化方便应用落地

汀丶人工智能

自然语言处理 nlp 2月月更 2月日更 文本匹配算法

实现一个简单的Database10(译文)

GreatSQL

sqlite myslq greatsql greatsql社区

飞桨框架v2.4 API新升级!全面支持稀疏计算、图学习、语音处理等任务

飞桨PaddlePaddle

paddle API 飞桨

Redis高级数据结构Stream和HyperLogLog

Java redis stream HyperLogLog

ArkUI新能力,助力应用开发更便捷

HarmonyOS开发者

HarmonyOS

把盏言欢,款款而谈,当WorkPlus接入了ChatGPT机器人

BeeWorks

面试官:如果 MySQL 数据库中的数据丢失,有哪些补救的办法呢?

Java MySQL 数据库

面试官:你来谈一下Synchronized-轻量级锁

Java synchronized 轻量级锁

5 步带你入门 GaussDB (DWS) 的 GDS 导入导出

华为云开发者联盟

数据库 华为云 企业号 2 月 PK 榜 华为云开发者联盟

OneCode开源低代码引擎白皮书

codebee

低代码 开发工具 低代码平台 java UI

软件测试/测试开发 | web自动化测试-执行 JavaScript 脚本

测试人

软件测试 自动化测试 测试开发 Web自动化测试 selenium

安全等保二级和三级哪个高?哪个费用更高?

行云管家

网络安全 信息安全 等保 等级保护 等级

用这4招优雅的实现Spring Boot 异步线程间数据传递

小小怪下士

Java spring 程序员 springboot

ChatGPT入门案例|商务智能对话客服(一)| 社区征文

TiAmo

AI ChatGPT

明晚 8 点直播!OpenCloudOS 中的海光国密算法分析

OpenCloudOS

Linux

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