50万奖金+官方证书,深圳国际金融科技大赛正式启动,点击报名 了解详情
写点什么

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

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

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

关注

评论

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

@PathVariable 和 @RequestParam 的区别

linux大本营

uniapp配置基本的tabbar和动态修改内容

格斗家不爱在外太空沉思

uni-app 三周年连更

ubuntu安装kernel-debuginfo

linux大本营

改变this指向的方法

linux大本营

指针 C语言 this指针

fmt库c++

linux大本营

C++ fmt

linux脚本执行可变参数任务

linux大本营

Linux 脚本

C++grpc 服务器接收到请求后如何处理

linux大本营

gRPC 序列化 protobuf C++

想转行计算机但不知道转哪个方面比较好?

linux大本营

软件开发 计算机

linux crash怎么分析

linux大本营

Crash Linux内核

stl中,cbegin,cend,crbegin,crend区别以及用途

linux大本营

容器 stl C++ STL

Apache derby 和sqlite进行对比

linux大本营

sqlite 数据库

ubuntu安装x11 forword,并做好配置

linux大本营

Linux ubuntu

领先企业的数智化进入2.0阶段,需要升级数智底座

用友BIP

创建和管理复杂的项目:OmniPlan Pro 4 mac中文版

真大的脸盆

Mac Mac 软件 项目管理工具 项目流程软件

C++11引入了std::atomic模板类无锁栈的实现如何判断栈空

linux大本营

多线程 线程安全 C++11

在Ubuntu的Vscode中怎么添加第三方库文件sqlite3.h

linux大本营

sqlite Linux ubuntu vscode

什么叫函数的注册,用c++举个例子

linux大本营

c++ 函数

delphi中TServerSocker使用stThreadBlocking模式的例子

linux大本营

koal_dm8_crypto.so.1.0.9.x86.b6f523 怎么安装

linux大本营

Linux linux文件

【Linux】iptables之防火墙概述及规则匹配+实例(1)

A-刘晨阳

Linux iptables 防火墙规则 三周年连更

leaflet加载聚合

linux大本营

arm m4 xpsr每一位的作用

linux大本营

寄存器 arm

linux tee命令

linux大本营

linux命令 TEE

20道mysql数据库笔试题及答案

linux大本营

MySQL 数据库

请详述c++中 lambda表达式使用

linux大本营

Lambda vector Function C++

怎么查看 .crash文件

linux大本营

Linux gdb Crash 内核 perf

C语言sqlit3创建表格怎么写

linux大本营

sqlite 数据库 C语言

设计一个大规模搜索引擎,大概有1000台服务器

linux大本营

搜索引擎 负载均衡 存储 分布式,

缓解过拟合方法

linux大本营

winDbg 提示 Unable to verify checksum for IOCommModel.exe

linux大本营

网络安全 数字证书

/sbin/kexec参数说明

linux大本营

Linux 镜像 Linux内核

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