为豆瓣电影实现User-based协同过滤的推荐系统

标签: 推荐系统 协同过滤,mahout 大数据 | 发表时间:2015-12-02 05:48 | 作者:
出处:http://colobu.com/

协同过滤(Collaborative Filtering),简单来说是利用某兴趣相投、拥有共同经验之群体的喜好来推荐使用者感兴趣的信息,个人透过合作的机制给予信息相当程度的反馈(如评分)并记录下来以达到过滤的目的进而帮助别人筛选信息,反馈不一定局限于特别感兴趣的,特别不感兴趣信息的纪录也相当重要,比如浏览信息,收藏,分享,点击等。

在前一篇文章 使用Spark MLlib给豆瓣用户推荐电影 中,在那篇文章我我介绍了使用Spark MLlib实现了model-based 的系统过滤之推荐系统。但是Spark并没有提供user-based, item-based这两种memory-based传统的协同过滤算法,所以接下来的这两篇文章我会介绍使用 Mahout实现user-based和item-based的电影推荐系统,数据还是基于豆瓣用户对电影的评论数据集。

Mahout是Apache的实现大规模的高性能的机器学习框架。它提供了很多的机器学习的算法和工具,以及利用Hadoop实现分布式的计算,本文将使用它的协同过滤算法(CF)实现非分布式的单机程序。

user-based协同过滤推荐算法就是通过不同用户对item的评分来评测用户之间的相似性,基于用户之间的相似性做出推荐。
下一篇文章中介绍item-based协同过滤推荐算法是通过用户对不同item的评分来评测item之间的相似性,基于item之间的相似性做出推荐。

User-based 优点

  • 能够过滤机器难以自动内容分析的信息,如艺术品,音乐等。
  • 共用其他人的经验,避免了内容分析的不完全或不精确,并且能够基于一些复杂的,难以表述的概念(如信息质量、个人品味)进行过滤。
  • 有推荐新信息的能力。可以发现内容上完全不相似的信息,使用者对推荐信息的内容事先是预料不到的。可以发现使用者潜在的但自己尚未发现的兴趣偏好。
  • 推荐个性化、自动化程度高。能够有效的利用其他相似使用者的反馈信息。加快个性化学习的速度。

缺点

  • 新使用者问题(New User Problem) 系统开始时推荐质量较差
  • 新项目问题(New Item Problem) 质量取决于历史资料集
  • 稀疏性问题(Sparsity)
  • 系统延伸性问题(Scalability)。

下面根据代码介绍具体的实现:

     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
     
public class DoubanUserBasedRecommender {
public static Map<Long, String> getMovies(String base) {
Map<Long, String> movies = new HashMap<>();
try {
File file = new File(base + "hot_movies.csv");
FileLineIterator iterator = new FileLineIterator(file, false);
String line = iterator.next();
while (!line.isEmpty()) {
String[] m = line.split(",");
movies.put(Long.parseLong(m[0]), m[2]);
line = iterator.next();
}
Closeables.close(iterator, true);
} catch (Exception ex) {
}
return movies;
}
......
}

上面一段代码是生成电影ID和名称的字典,这个文件的每一行代表一条热门电影,如 20645098,8.2,小王子。这样在我们输出结果的时候,可以方便的查看电影名,因为电影名比ID更有意义。

     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
     
public class DoubanUserBasedRecommender {
......
public static void main(String[] args) throws Exception {
String base = args[0];
File file = new File(base + "user_movies.csv");
DoubanFileDataModel model = new DoubanFileDataModel(file);
//皮尔逊相似度
UserSimilarity similarity = new PearsonCorrelationSimilarity(model);
UserNeighborhood neighborhood = new NearestNUserNeighborhood(2, similarity, model);
Recommender recommender = new GenericUserBasedRecommender(model, neighborhood, similarity);
Recommender cachingRecommender = new CachingRecommender(recommender);
//查看一些结果
Map<Long, String> movies = getMovies(base);
for (long userID = 0; userID < 100; userID++) {
String userName = model.userIDAndNameMapping.get(userID);
List<RecommendedItem> recommendations = cachingRecommender.recommend(userID, 2);
System.out.print("为用户 " + userName + " 推荐电影:");
for (RecommendedItem recommendation : recommendations) {
System.out.print(recommendation.getItemID() + "," + movies.get(recommendation.getItemID()) + " ");
}
System.out.println();
}
//输出结果到文件
PrintWriter writer = new PrintWriter(base + "result.csv", "UTF-8");
for (long userID = 0; userID < model.userIDAndNameMapping.size(); userID++) {
String userName = model.userIDAndNameMapping.get(userID);
List<RecommendedItem> recommendations = cachingRecommender.recommend(userID, 5);
if (recommendations.size() > 0) {
String line = userName + ",";
for (RecommendedItem recommendation : recommendations) {
line += recommendation.getItemID() + ":" + movies.get(recommendation.getItemID()) + ",";
}
if (line.endsWith(","))
line = line.substring(0, line.length() - 1);
writer.println(line);
}
}
writer.close();

6行读入数据模型,因为我们的数据文件中用户的id是字符串类型的,我们需要将它转换成一个Long类型的数据,所以实现了一个定制的类 DoubanFileDataModel
8行我们使用 PearsonCorrelationSimilarity计算相似度,Mahout还提供了其它的计算相似度的算法:

     
1
2
3
4
5
6
7
8
9
10
11
12
     
//曼哈顿相似度
//UserSimilarity similarity = new org.apache.mahout.cf.taste.impl.similarity.CityBlockSimilarity(model);
//欧几里德相似度
//UserSimilarity similarity = new org.apache.mahout.cf.taste.impl.similarity.EuclideanDistanceSimilarity(model);
//对数似然相似度
//UserSimilarity similarity = new org.apache.mahout.cf.taste.impl.similarity.LogLikelihoodSimilarity(model);
//斯皮尔曼相似度
//UserSimilarity similarity = new org.apache.mahout.cf.taste.impl.similarity.SpearmanCorrelationSimilarity(model);
//Tanimoto 相似度
//UserSimilarity similarity = new org.apache.mahout.cf.taste.impl.similarity.TanimotoCoefficientSimilarity(model)
//Cosine相似度
//UserSimilarity similarity = new org.apache.mahout.cf.taste.impl.similarity.UncenteredCosineSimilarity();

10生成UserBased Recommender类。
13行到第 22行我们为前100个用户生成推荐结果,并输出到终端窗口,这样我们可以先检查一下推荐的结果。
因为矩阵是很稀疏的,这种user-based算法可能没有提供给用户推荐的电影,有的用户可能不到5个推荐电影。
这段代码剩下的部分就是把所有的用户推荐结果都输出到一个文件中。

DoubanFileDataModel类的实现如下:

     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
     
public class DoubanFileDataModel extends FileDataModel {
public static Map<String,Long> userNameAndIDMapping = new HashMap<>();
public static Map<Long,String> userIDAndNameMapping = new HashMap<>();
private static long userID = 0;
public DoubanFileDataModel(File dataFile) throws IOException {
super(dataFile);
}
public DoubanFileDataModel(File dataFile, String delimiterRegex) throws IOException {
super(dataFile, delimiterRegex);
}
public DoubanFileDataModel(File dataFile, boolean transpose, long minReloadIntervalMS) throws IOException {
super(dataFile, transpose, minReloadIntervalMS);
}
public DoubanFileDataModel(File dataFile, boolean transpose, long minReloadIntervalMS, String delimiterRegex) throws IOException {
super(dataFile, transpose, minReloadIntervalMS, delimiterRegex);
}
@Override
protected long readUserIDFromString(String value) {
value = value.trim();
if (userNameAndIDMapping.containsKey(value)) {
return userNameAndIDMapping.get(value);
}
userNameAndIDMapping.put(value, userID);
userIDAndNameMapping.put(userID, value);
userID++;
return (userID -1);
}
@Override
protected long readItemIDFromString(String value) {
value = value.trim();
return super.readItemIDFromString(value);
}
}

下面是推荐结果的片段:

     
1
2
3
4
5
6
7
8
9
10
     
yuan521123,1866473:蚁人
58472013,26289144:滚蛋吧!肿瘤君,2973079:霍比特人3:五军之战,23761370:速度与激情7,25746375:我是路人甲
65532408,26021055:栀子花开
68333051,10533913:头脑特工队,10741643:我的个神啊,10827341:疯狂外星人,25823833:天将雄师,26289144:滚蛋吧!肿瘤君
80755814,11624706:小黄人大眼萌,10533913:头脑特工队,25723907:捉妖记,26289144:滚蛋吧!肿瘤君,24879839:道士下山
129734802,25908042:横冲直撞好莱坞
127252296,10741643:我的个神啊,25723907:捉妖记,2973079:霍比特人3:五军之战,6126442:一步之遥
aellr,6846893:超能查派,25779218:匆匆那年,24879839:道士下山
67656730,3338862:终结者:创世纪,6845667:秘密特工,6873042:明日世界,25752261:女间谍,25823833:天将雄师
ravinenoravine,25838463:像素大战

比如用户 52973703,我们为ta推荐:

user-based CF算法适用于:

  • 用户较少的场合,否则用户相似度矩阵计算代价很大
  • 适合时效性较强,用户个性化兴趣不太明显的领域
  • 对新用户不友好,对新物品友好,因为用户相似度矩阵不能实时计算
  • 很难提供令用户信服的推荐解释

item-based CF算法适用于:

  • 物品数明显小于用户数的场合,否则物品相似度矩阵计算代价很大
  • 适合长尾物品丰富,用户个性化需求强的领域
  • 对新用户友好,对新物品不友好,因为物品相似度矩阵不需要很强的实时性
  • 利用用户历史行为做推荐解释,比较令用户信服

推荐阅读项亮的 推荐系统实践,了解推荐系统的基础知识。

相关 [豆瓣 电影 user] 推荐:

为豆瓣电影实现User-based协同过滤的推荐系统

- - 鸟窝
协同过滤(Collaborative Filtering),简单来说是利用某兴趣相投、拥有共同经验之群体的喜好来推荐使用者感兴趣的信息,个人透过合作的机制给予信息相当程度的反馈(如评分)并记录下来以达到过滤的目的进而帮助别人筛选信息,反馈不一定局限于特别感兴趣的,特别不感兴趣信息的纪录也相当重要,比如浏览信息,收藏,分享,点击等.

user-select介绍

- - 前端观察
之前在《 CSS的未来:一些试验性CSS属性》中有提到user-select这个属性,最近整理的时候有遇到,所以详细的了解了下,这里简单的介绍下. 这是在css3 UI规范中新增的一个功能,用来控制内容的可选择性. auto——默认值,用户可以选中元素中的内容. none——用户不能选择元素中的任何内容.

阿北:豆瓣电影评分八问

- - 博客园_新闻
这是一篇豆瓣创始人&CEO 阿北(杨勃)刚刚发表的 长文,始终被认为理想主义的阿北,在文中阐明了豆瓣电影评分的原则和做法,用以保护公众对豆瓣评分的信任. 随着“豆瓣的电影评分在影视行业的影响越来越大”,阿北说豆瓣收到的威逼利诱也多起来……以下为全文. (豆瓣 CEO 阿北,图片来自 Qdaily).

Java豆瓣电影爬虫——抓取电影详情和电影短评数据

- - ImportNew
采集豆瓣电影数据包括电影详情页数据和电影的短评数据. 需要保存这些详情字段如导演、编剧、演员等还有图中右下方的标签. 需要保存的字段有短评所属的电影名称,每条评论的详细信息如评论人名称、评论内容等. 有了如上的需求,需要设计表,其实很简单,只需要一张电影详情表movie和一张电影短评表comments,另外还需要一张存储网页提取的超链接的记录表record.

NUI自然用户界面(Natural User Interface)

- - IT技术博客大学习
    NUI不是一个新名词.      只是经历近年的一些新产品,有了更贴切的感受.      特别是消费电子终端界面CLI、GUI、NUI的变化. 在桌面端,对象通常是显示固定的屏幕,设计师专注于键鼠驱动下的视觉效果. 到了移动端,拥有更多传感器与交互方式,便携使得移动设备不限时空,传统专注于固定场景与操作的设计思路不合时宜.

User Agent注入攻击及防御

- - FreeBuf.COM | 关注黑客与极客
CloudFlare公司经常会收到客户询问为什么他们的一些请求会被. CloudFlare WAF 屏蔽. 最近,一位客户就提出他不能理解为什么一个访问他主页简单的 GET 请求会被 WAF 屏蔽. 正如他说的,一个简单的请求访问 WEB 主页,乍看之下好像没什么问题. 除非你仔细查看 User-Agent 部分:.

豆瓣9.0分以上的好电影,值得收藏

- sky - 情迷好莱坞 – Mtime时光网
地球脉动 Planet Earth. 导演 : Alastair Fothergill. 主演 : David Attenborough/Sigourney Weaver. 评分:9.7  ★★★★★. 肖申克的救赎 The Shawshank Redemption. 导演 : Frank Darabont.

豆瓣电影2011年度清单总汇

- - 人在旅“图”
2011豆瓣电影年度总榜单,豆瓣红人影志整理,很全很强大. 2011是否遗漏了哪些好电影,参考比对一下吧. 所选范畴为2011年上映或发行的国内外所有影片,根据豆瓣用户评分+看过(在看)人数排序. ★  2011豆瓣电影【欧美20佳】. 勇士 Warrior 8.8 17872. 浮生一日 Life in a Day 8.8 11879.

算法工程师如何改进豆瓣电影 TOP250

- - 豆瓣blog
影迷们经常关注的电影排行榜里,一部由100人评出9.0分的电影,和一部由10000人评出8.0分的电影,谁应该排在前面呢. 这是我们算法工程师时常会面对的问题. 一些深度影迷可能会想到 imdb.com (互联网电影数据库) 所采用的贝叶斯公式[见附注],这个公式的思路就是通过每部影片的[评分人数]作为调节排序的杠杆:如果这部影片的评分人数低于一个预设值,则影片的最终得分会向全部影片的平均分拉低.