logo资料库

自己动手搭建一个电影推荐系统.docx

第1页 / 共6页
第2页 / 共6页
第3页 / 共6页
第4页 / 共6页
第5页 / 共6页
第6页 / 共6页
资料共6页,全文预览结束
动手构建一个推荐系统(Recommendation System)
1. 什么是推荐系统(Recommendation System)
2. 推荐系统相关理论
3. 动手构建一个推荐系统
(1)数据集
(2)核心:推荐函数
(3)实验结果
相关日志
动手构建一个推荐系统(Recommendation System) 2010 年 7 月 11 日 小武哥 发表评论 阅读评论 写在前面:本文通过构建一个电影推荐系统,深入浅出的介绍推荐系统相关的概念、算法,让读者朋友能够在对推荐系统有比较全面的认识 的基础之上,能够轻松地构建出自己的推荐系统。 1. 什么是推荐系统(Recommendation System) 推荐系统是指根据一个群体的偏好,来为群体中的成员提供推荐的系统。现实生活中这样的例子很多,比如豆瓣(Douban.com)读书中的“豆 瓣猜”功能,它根据你看过的一些书和相关评价,与整个豆瓣社区其它会员看过的书与评价经过一系列的计算,就能给你推荐一些你没有读过 的,但有可能感兴趣的书(如下图所示): 这是我读过的或者正在读的书: 这是豆瓣给我推荐的书: 通过上面的例子,相信大家对推荐系统都有了一个初步的认识。其实生活中还有许许多多的这样的例子,像在线购物中的商品推荐、在线视 频播放网站的视频推荐等。 2. 推荐系统相关理论 (1)推荐系统通常可以分为两类:一类是基于人的推荐系统,它利用人与人之间的相似度来进行推荐;一类是基于物品的推荐,它利用物品 之间的相似度来进行推荐。通俗地讲,基于人的推荐,是通过分析人与人喜欢的物品,计算出人与人之间的相似度,然后做推荐;而基于物 品的推荐,是通过分析某人喜欢的物品与其它物品的相似度,然后来为其做推荐。 (2)推荐系统中比较关键的算法就是相似度的计算,有人与人之间的相似度计算,也有物品与物品之间的相似度计算。相似度计算函数要满
足如下特点:拥有同样的函数签名,以一个浮点数做为返回值,其数值越大代表相似度越大。下面介绍几个算法: a. 欧几里德距离(Euclidean Distance),我们知道两个人的喜好越相似,他们的欧几里德距离值越小,所以需要将欧几里德距离转化一 下,这里介绍一个简单的转化:1/(1 + dist), 这样就能保证越相似取值越大,而且取值范围在(0, 1] b. 皮求逊相关系数(Pearson Correlation Coefficient), 取值范围[-1, 1], 越相似值越大,满足条件 c. Tanimoto 系统 (Tanimoto Coefficient),最值范围[0, 1], 越相似值越大,满足条件 (3)在推荐系统里,需要注意: a. 没有对某物品进行评价的人不能对该物品的推荐打分产生影响 b. 不能因为某人的偏执喜好(打很高或者很低的分)对推荐打分产生明显的影响 为了避免上面的问题,通常采用加权平均的方法来计算某物品的推荐打分(详见第三部分算法实现) 3. 动手构建一个推荐系统 本部分通过构建一个真实的电影推荐系统,来介绍构建一个推荐的基本步骤与方法。 (1)数据集 本系统采用的数据是来自 http://www.grouplens.org/node/73 的数据,本次实验采用的是如下图所示的第三份数据,总共有 6040 个用 户和 3952 部电影,以及 1000209 条相关评价。 (2)核心:推荐函数 typedef double (*ScoreFunc)(const double *, const double *, size_t); void GetRecommendation( const double ** allCritics, size_t personNum, size_t size, size_t myIndex, ScoreFunc scorer, size_t recNum, int * recItems, double * recScores ) double * allRels = new double[personNum]; ::memset(allRels, 0, sizeof(double)*personNum); ///计算所有的相关度 //[in] 所有人的打分表 //[in] 所有人的个数 //[in] 打分表大小 //[in] 我在打分表中下标 //[in] 打分函数 //[out] 被推荐项个数 //[out] 被推荐项列表 //[out] 被推荐项得分 {
for (size_t idx = 0; idx < personNum; ++ idx) { if (idx == myIndex) { } allRels[idx] = scorer(allCritics[myIndex], allCritics[idx], size); continue; //it's me, just continue } double * rels = new double[personNum]; double * critics = new double[personNum]; std::multimap mapScores; for (size_t itemIdx = 0; itemIdx < size; ++ itemIdx) { ::memset(rels, 0, sizeof(double)*personNum); ::memset(critics, 0, sizeof(double)*personNum); ///获取有效的相关度和对应的评分 for (size_t personIdx = 0; personIdx < personNum; ++ personIdx) { if (allCritics[personIdx][itemIdx] <= 0)//invalid score { rels[personIdx] = 0; critics[personIdx] = 0; } else { } rels[personIdx] = allRels[personIdx]; critics[personIdx] = allCritics[personIdx][itemIdx]; } ///计算加权打分 double score = GetWeightedMead(critics, critics + personNum, rels, rels + mapScores.insert(std::make_pair(score, itemIdx)); personNum); } ///获取最终的推荐列表和对应的打分 std::multimap::reverse_iterator oIt = mapScores.rbegin(); for (size_t count = 0; count < recNum; ++ count) { = oIt->second; recScores[count] = oIt->first; ++ oIt; } delete [] critics; delete [] rels; delete [] allRels; } recItems[count] 上面的函数,输入所有人的评分表,以及需要提供推荐的人的相关信息,输出提供的推荐列表,以及相应的打分。这里需要说明的一点是打 分函数 scorer, 它是一个 ScoreFunc 类型的函数指针,在实际调用的时候,可以通过传入不同的打分函数,获得在该打分函数下的推荐列 表。需要注意打分函数的签名在这里必须与 ScoreFunc 的定义一致。下面是我定义的打分函数: return coef; return (1 / (1 + dist)); double GetEuclideanScore(double dist) { } double GetPearsonScore(double coef) { } double GetTanimotoScore(double coef) { } double GetEuclideanScore(const double * myCritics, const double * hisCritics, size_t size) { return coef; double dist = GetEuclideanDistance(myCritics, myCritics + size, hisCritics, hisCritics + size); return GetEuclideanScore(dist); } double GetPearsonScore(const double * myCritics, const double * hisCritics, size_t size)
{ double coef = GetPearsonCorrelationCoefficient(myCritics, myCritics + size, hisCritics, hisCritics + size); return GetPearsonScore(coef); } double GetTanimotoScore(const double * myCritics, const double * hisCritics, size_t size) { double coef = GetTanimotoCoefficient(myCritics, myCritics + size, hisCritics, hisCritics + size); return GetTanimotoScore(coef); } 上面的六个函数,前面三个是对结果进行规范化的函数,使结果满足越相关越大。后面三个函数是真正的评价打分函数。在上面的实现中用 到了 GetEuclideanDistance, GetPearsonCorrelationCoefficient, GetTanimotoCoefficient, GetWeightedMead 这四个函数, 它们分别是计算欧几里德距离,Pearson 相关系数,Tanimoto 系数和加权平均值的函数,在这里我就不给出具体的实现了。 (3)实验结果 我们用三种不同的打分函数,为第 1000 个用户,推荐 20 部电影,来对比一下推荐的结果: a. Euclidean 结果: b. Pearson 结果:
c. Tanimoto 结果: 从上面的结果中,可以得出如下结果: a. 采用 Euclidean 打分推荐和采用 Pearson 打分推荐的结果中,有 16 个是相同的 b. 采用 Pearson 打分推荐和采用 Tanimoto 打分推荐的结果中,有 15 个是相同的 c. 采用 Tanimoto 打分推荐和采用 Euclidean 打分推荐的结果中,有 16 个是相同的 从上面的数据可以看出,虽说采用不同的打分函数进行推荐的结果存在一定的差异,但是整体上是一致的,不同的结果的相互覆盖率都超过 了 75%, 这说明我们的打分函数都还是比较有效的。 相关日志
     “小武哥的开源项目”正式发布 漫话 C++0x(六)—- variadic templates JSON 学习小结 UML 中类之间的关系小结 HttpTunnel 技术介绍 原创文章,转载请注明出处:小武哥的博客 本文固定链接:http://www.wuzesheng.com/?p=1277 Zemanta
分享到:
收藏