写点什么

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

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

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

关注

评论

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

稳若磐石的焱融 SaaS 服务平台背后,是数据生态的崛起

焱融科技

云计算 分布式 高性能 公有云 文件存储

Forrester发布首份《中国低代码平台发展报告》,有多少企业应用低代码?

J2PaaS低代码平台

敏捷开发 低代码 低代码开发 低代码平台 低代码报告

一文带你快速掌握PTN网络的路由计算

鲸品堂

Tapdata 在线研讨会:DaaS vs 大数据平台,是竞争还是共处?

tapdata

数据库

应用不停机发布的思考与初识

陈俊

高可用 技术架构 不停机发布

软件开发除了23种设计模式,还有7个开发原则需要了解

华为云开发者联盟

设计模式 软件开发 开发 对象 SOLID

学不懂Netty?看不懂源码?不存在的,手把手带你阅读Netty源码

热爱java的分享家

Java 架构 程序人生 编程语言 经验分享

又添权威认定,旺链科技通过可信区块链专项认证!

旺链科技

区块链 产业区块链 技术测评 数字化经济

模块四作业

Geek_1d37ea

架构训练营

模块四学习总结

Geek_1d37ea

架构训练营

Meetup 报名|开源分布式数据库探索和应用

OceanBase 数据库

数据库 分布式 活动 技术交流 oceanbase

一张图 GET 社交泛娱乐出海大招

融云 RongCloud

深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析

百度Geek说

Webkit

Sechunter移动应用隐私合规检测详解

华为云开发者联盟

移动应用 目标检测 隐私 Sechunter 隐私合规

100行代码,轻松搞定文本编辑器中草稿箱

Tom弹架构

Java 架构 设计模式

【可观测之路】“观测云”CSDN技术专区正式上线!

观测云

可观测 CSDN

压缩比达到7:1,TDengine助力校园智慧用电系统降本增效

TDengine

tdengine 时序数据库

web技术分享| LRU 缓存淘汰算法

anyRTC开发者

缓存 音视频 WebRTC LRU web技术分享

WICC 花城登场,揭秘时代变局下通信服务新形态

融云 RongCloud

开发者 社交 元宇宙 泛娱乐 通信云技术大会

openGauss开源自动化测试框架Yat,增强社区测试能力

openGauss

售后支持领域的服务指标

Geek_utwige

方法论 技术管理 指标体系 数据指标 客户服务

灵犀平台—API的全可视化开发

鲸品堂

开发者 工具

HarmonyOS内核技术大揭秘|HDC2021技术分论坛

HarmonyOS开发者

HarmonyOS

和 VMware、深信服、天翼云、招商云专家一起聊聊云原生边缘计算

阿里巴巴云原生

阿里云 容器 云原生 KubeMeet 线下活动

【概率】抽屉中的袜子

潮汐朝夕

概率

DevEco Testing,新增分布式测试功能|HDC2021技术分论坛

HarmonyOS开发者

HarmonyOS

0.99M,150FPS,移动端超轻量目标检测算法PP-PicoDet来了!

百度大脑

人工智能 百度

Kubernetes 已经成为云原生时代的安卓,这就够了吗?

阿里巴巴云原生

阿里云 Kubernetes 云原生 学习资料 应用管理平台

云原生时代:看 Apache APISIX 如何玩转可观测性

API7.ai 技术团队

云原生 可观测性 Skywalking API网关 Apache APISIX

[SpringBoot源码分析]SpringBoot如何启动

零点999

spring SpringBoot 2

HBase 基础及核心架构解析

五分钟学大数据

11月日更

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